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 + -
显示快捷键?