handlers.asm

来自「这是一些例程」· 汇编 代码 · 共 1,284 行 · 第 1/4 页

ASM
1,284
字号
        pop     ax                      ; Return address of resident PSP
        jmp     exit                    ;  to signal success
e_exit:
        mov     ax, CANT_DEINSTALL
exit:
        ret

Deinstall ENDP


;* GetVersion - Gets the DOS version and stores it in a global variable as
;* well as returning it in AX.
;*
;* Uses:   Version
;*
;* Params: DS = Resident code segment
;*
;* Return: AH = Major version
;*         AL = Minor version

GetVersion PROC NEAR

        mov     ax, 3000h               ; Request DOS Function 30h
        int     21h                     ; Get MS-DOS version number
        .IF     al < 2                  ; If Version 1.x:
        mov     ax, WRONG_DOS           ; Abort with WRONG_DOS as error code
        .ELSE
        xchg    ah, al                  ; AH = major, AL = minor version
        mov     Version, ax             ; Save in global
        .ENDIF
        ret

GetVersion ENDP


;* GetDosFlags - Gets pointers to the DOS InDos and Critical Error flags.
;*
;* Params: DS = Resident code segment
;*
;* Return: 0 if successful, or the following error code:
;*         FLAGS_NOT_FOUND

GetDosFlags PROC NEAR

; Get InDOS address from MS-DOS

        mov     ah, 34h                         ; Request DOS Function 34h
        int     21h                             ; Get Address of InDos flag
        mov     WORD PTR InDosAddr[0], bx       ; Store address (ES:BX)
        mov     WORD PTR InDosAddr[2], es       ;   for later access

; Determine address of Critical Error Flag

        mov     ax, Version             ; AX = DOS version number

; If DOS 3.1 or greater and not OS/2 compatibility mode, Critical Error
; flag is in byte preceding InDOS flag 
        .IF     (ah < 10) && ((ah > 3) || ((ah == 3) && (al >= 10)))
        dec     bx                      ; BX points to byte before InDos flag

        .ELSE
; For earlier versions, the only reliable method is to scan through
; DOS to find an INT 28h instruction in a specific context.

        mov     cx, 0FFFFh              ; Maximum bytes to scan
        sub     di, di                  ; ES:DI = start of DOS segment

INT_28  EQU     028CDh

        .REPEAT
        mov     ax, INT_28              ; Load opcode for INT 28h

        .REPEAT
        repne   scasb                   ; Scan for first byte of opcode

        .IF     !zero?
        mov     ax, FLAGS_NOT_FOUND     ; Return error if not found
        jmp     exit
        .ENDIF
        .UNTIL  ah == es:[di]           ; For each matching first byte,
                                        ;   check the second byte until match

; See if INT 28h is in this context:
;                                       ;     (-7)    (-5)
;       CMP     ss:[CritErrFlag], 0     ;  36, 80, 3E,  ?,  ?,  0
;       JNE     NearLabel               ;  75,  ?
        int     28h                     ;  CD, 28
;                                       ;  (0) (1)
CMP_SS    EQU   3E80h
P_CMP_SS  EQU   8
P_CMP_OP  EQU   6

        mov     ax, CMP_SS              ; Load and compare opcode to CMP
        .IF     ax == es:[di-P_CMP_SS]  ; If match:
        mov     bx, es:[di-P_CMP_OP]    ; BX = offset of
        jmp     exit                    ;   Critical Error flag
        .ENDIF

; See if INT 28h is in this context:
;                                       ;     (-12)   (-10)
;       TEST    ?s:[CritErr], 0FFh      ;  ?6  F6, 06,  ?,  ?, FF
;       JNE     NearLabel               ;  75, ?
;       PUSH    ss:[CritErrFlag]        ;  36, FF, 36,  ?,  ?
        int     28h                     ;  CD, 28
;                                       ;  (0) (1)
TEST_SS   EQU   06F6h
P_TEST_SS EQU   13
P_TEST_OP EQU   11

        mov     ax, TEST_SS             ; Load AX = opcode for TEST
        .UNTIL  ax == es:[di-P_TEST_SS] ; If not TEST, continue scan

        mov     bx, es:[di-P_TEST_OP]   ; Else load BX with offset of
        .ENDIF                          ;   Critical Error flag
exit:
        mov     WORD PTR CritErrAddr[0], bx     ; Store address of
        mov     WORD PTR CritErrAddr[2], es     ;   Critical Error flag
        sub     ax, ax                          ; Clear error code
        ret

GetDosFlags ENDP


;* GetTimeToElapse - Determines number of 5-second intervals that
;* must elapse between specified hour:minute and current time.
;*
;* Params: AH = Hour
;*         AL = Minute
;*
;* Return: AX = Number of 5-second intervals

GetTimeToElapse PROC NEAR 

        push    ax                      ; Save hour:minute
        mov     ah, 2Ch                 ; Request DOS Function 2Ch
        int     21h                     ; Get Time (CH:CL = hour:minute)
        pop     bx                      ; Recover hour:minute
        mov     dl, dh
        sub     dh, dh
        push    dx                      ; Save DX = current seconds

        mov     al, 60                  ; 60 minutes/hour
        mul     bh                      ; Multiply by specified hour
        sub     bh, bh
        add     bx, ax                  ; BX = minutes from midnight
                                        ;   to activation time
        mov     al, 60                  ; 60 minutes/hour
        mul     ch                      ; Multiply by current hour
        sub     ch, ch
        add     ax, cx                  ; AX = minutes from midnight
                                        ;   to current time
        sub     bx, ax                  ; BX = minutes to elapse before
        .IF     carry?                  ; If activation is tomorrow,
        add     bx, 24 * 60             ;   add number of minutes per day
        .ENDIF

        mov     ax, 60
        mul     bx                      ; DX:AX = minutes-to-elapse-times-60
        pop     bx                      ; Recover current seconds
        sub     ax, bx                  ; DX:AX = seconds to elapse before
        sbb     dx, 0                   ;   activation
        .IF     carry?                  ; If negative,
        mov     ax, 5                   ;   assume 5 seconds
        cwd
        .ENDIF

        mov     bx, 5                   ; Divide result by 5 seconds
        div     bx                      ; AX = number of 5-second intervals
        ret

GetTimeToElapse ENDP


;* CallMultiplex - Calls the Multiplex Interrupt (Interrupt 2Fh).
;*
;* Uses:   IDstring
;*
;* Params: AL = Function number for multiplex handler
;*
;* Return: AX    = One of the following return codes:
;*                 NOT_INSTALLED      IS_INSTALLED       NO_IDNUM
;*         ES:DI = Resident code segment:identifier string (function 0)
;*         ES:DI = Resident PSP segment address (function 1)
;*         ES:DI = Far address of shared memory (function 2)

CallMultiplex PROC FAR USES ds

        push    ax                      ; Save function number
        mov     ax, @code
        mov     ds, ax                  ; Point DS to code segment

; First, check 2Fh vector. DOS Version 2.x may leave the vector null
; if PRINT.COM is not installed. If vector is null, point it to IRET
; instruction at LABEL NoMultiplex. This allows the new multiplex
; handler to pass control, if necessary, to a proper existing routine.

        mov     ax, 352Fh               ; Request DOS Function 35h
        int     21h                     ; Get interrupt vector in ES:BX
        mov     ax, es
        or      ax, bx
        .IF     zero?                   ; If Null vector,
        mov     dx, OFFSET NoMultiplex  ;   set vector to IRET instruction
        mov     ax, 252Fh               ;   at LABEL NoMultiplex
        int     21h                     ; Set interrupt vector
        .ENDIF

; Second, call Interrupt 2Fh with function 0 (presence request). Cycle
; through allowable identity numbers (192 to 255) until TSR's multiplex
; handler returns ES:DI = IDstring to verify its presence or until call
; returns AL = 0, indicating the TSR is not installed.

        mov     dh, 192                 ; Start with identity number = 192

        .REPEAT
        mov     ah, dh                  ; Call Multiplex with AH = trial ID
        sub     al, al                  ;   and AL = function 0
        push    dx                      ; Save DH and DS in case call
        push    ds                      ;   destroys them
        int     2Fh                     ; Multiplex
        pop     ds                      ; Recover DS and
        pop     dx                      ;   current ID number in DH
        or      al, al                  ; Does a handler claim this ID number?
        jz      no                      ; If not, stop search

        .IF     al == 0FFh              ; If handler ready to process calls,
        mov     si, OFFSET IDstring     ;   point DS:SI to ID string, compare
        mov     cx, IDstrlen            ;   with string at ES:DI returned
        repe    cmpsb                   ;   by multiplex handler
        je      yes                     ; If equal, TSR's handler is found
        .ENDIF

        inc     dh                      ; This handler is not the one
        .UNTIL  zero?                   ; Try next identity number up to 255

        mov     ax, NO_IDNUM            ; In the unlikely event that numbers
        jmp     e_exit                  ;   192-255 are all taken, quit

; Third, assuming handler is found and verified, process the multiplex
; call with the requested function number.

yes:
        pop     ax                      ; AL = original function number
        mov     ah, dh                  ; AH = identity number
        int     2Fh                     ; Multiplex
        mov     ax, IS_INSTALLED        ; Signal that handler has been found
        jmp     exit                    ;   and quit

; Reaching this section means multiplex handler (and TSR) not installed.
; Since the value in DH is not claimed by any handler, it will be used as
; the resident TSR's identity number.  Save the number in resident code
; segment so multiplex handler can find it.

no:
        mov     IDnumber, dh            ; Save multiplex identity number
        mov     ax, NOT_INSTALLED       ; Signal handler is not installed
e_exit:
        pop     bx                      ; Remove function number from stack
exit:
        ret

CallMultiplex ENDP


;* InitTsr - Initializes DOS version variables and multiplex data with
;* following parameters. This procedure must execute before calling
;* either the Install, Deinstall, or CallMultiplex procedures. Callable
;* from a high-level language.
;*
;* Uses:   IDstring
;*
;* Params: PspParam - Segment address of PSP
;*         StrParam - Far address of TSR's identifier string
;*         ShrParam - Far address of shared memory
;*
;* Return: AX = WRONG_DOS if not DOS Version 2.0 or higher

InitTsr PROC    FAR USES ds es si di,
        PspParam:WORD, StrParam:FPVOID, ShrParam:FPVOID

        mov     ax, @code
        mov     ds, ax                          ; Point DS and ES
        mov     es, ax                          ;   to code segment

; Get and store parameters passed from main program module

        mov     ax, PspParam
        mov     TsrPspSeg, ax                   ; Store PSP segment address

        mov     ax, WORD PTR ShrParam[0]
        mov     bx, WORD PTR ShrParam[2]
        mov     WORD PTR ShareAddr[0], ax       ; Store far address of
        mov     WORD PTR ShareAddr[2], bx       ;   shared memory

        push    ds
        mov     si, WORD PTR StrParam[0]        ; DS:SI points to multiplex
        mov     ax, WORD PTR StrParam[2]        ;   identifier string
        mov     ds, ax
        mov     di, OFFSET IDstring             ; Copy string to IDstring
        mov     cx, STR_LEN                     ;   at ES:DI so multiplex
                                                ;   handler has a copy
        .REPEAT
        lodsb                                   ; Copy STR_LEN characters
        .BREAK .IF al == 0                      ;   or until null terminator
        stosb                                   ;   found
        .UNTILCXZ

        pop     ds                              ; Recover DS = code segment
        mov     ax, STR_LEN
        sub     ax, cx
        mov     IDstrlen, ax                    ; Store string length

        INVOKE  GetVersion                      ; Return AX = version number
        ret                                     ;   or WRONG_DOS

InitTsr ENDP


INSTALLCODE ENDS

        END

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?