COMMENT %
Here sent is a TSR which does it all... Activated at TIMERBASE, KEYBASE and
MSDOSBASE .. The program entered in the TSR saves all needed, like front
PSP, extended error info, only activates when it's really safe.... Just
delete the parts not needed, and keep track of the programming style..
It's pretty solid....
Remember my name, and give me some credits or a copy when your program
is finished.. I worked hard on this "completely MSDOS-like TSR" ..
.o
------------ oOO ------------------------------------------------
oOOo jbodde@euronet.nl
. oOOOoOO o http://www.euronet.nl/~jbodde
oO. .OOOOOOOooOOO
oOOo .o oOOOOOOOo A! JW2 YK++ WK+++^i DT++" P&B+
OOOO oo oOOOo S&S+ GDF+++PS++ HIP--- GP+++^
.OOOo .oo OOo HN+++! MM++ MS-- CO+++ P+ $+
oOOOOOOo.OO o E15a Ee45a Eee61a Ay72 M
oOOOOOOOOOOO
COMMENT ENDS %
.286
.MODEL LARGE
LOCALS
JUMPS
;==============================================================================
;
; TOTAL.ASM
; Total fixed TSR program (28/02/96)
; Version 1.0
;
; Made by: Jorgen Bodde (c) '96
;
; *** Revision history ***
;
; V1.0 : 280296\JB
; Created
;
; FUTURE: checkINTS also needed to check for ISR08 and DOS..
; change callAPP to callREAL.. Make IFNDEF's for
; timer and hotkey preservations.
;
;==============================================================================
; *******************************************************************
; *************************** EQUATES *******************************
; *******************************************************************
; console constants
CR equ 0dh
LF equ 0ah
TAB equ 09h
BLANK equ 20h
BELL equ 07h
EOS equ '$'
; condition constants
FALSE equ 0
TRUE equ -1
; assembly equates
B equ byte ptr
W equ word ptr
D equ dword ptr
N equ near ptr
F equ far ptr
S equ seg
O equ offset
HIGHsign equ 8740h ; these strings are used
LOWsign equ 03e5h ; for tsr identification
PRG_name EQU 'TOTAL' ; leave them..
PRG_ver EQU '1.1'
PRG_date EQU '28/02/96'
argMIN equ 1d
STACKsize equ 100h
TSR_INSTALL equ 00h
TSR_UNHOOK equ 01h
TSR_FREEZE equ 02h
TSR_ACTIVATE equ 03h
TSR_RUNAPP equ 04h
KBins equ 80h
KBcaps equ 40h
KBnum equ 20h
KBscroll equ 10h
KBalt equ 8h
KBctrl equ 4h
KBshleft equ 2h
KBshright equ 1h
KBmask equ KBshright + KBshleft ; Mask for key to activate
timerSTART equ 125H ; Timer countdown value
ARGCASEINS equ TRUE ; remove when CASESENSITIVE arguments
TIMERBASE equ TRUE ; remove when no TIMER activations
HOTKEYBASE equ TRUE ; remove when no HOTKEY activations
;MSDOSBASE equ TRUE ; remove when no DOS activations
; *******************************************************************
; *************************** MACRO'S *******************************
; *******************************************************************
DOSB macro DOSByte
mov ah,DOSByte
int 21h
endm
DOSW macro DOSWord
mov ax,DOSWord
int 21h
endm
ADDINT macro name, INTnr
name&amount = name&amount + 1
db INTnr&h
inISR&INTnr db FALSE
prevISR&INTnr dd 00000000h
dw O mytsr:my&name&INTnr
endm
; *******************************************************************
; ************************** TSR DATA *******************************
; *******************************************************************
mytsr SEGMENT para public
ASSUME cs:mytsr, ds:mytsr, es:mytsr
ISRamount = 0 ; is increased every new ISR
TRAPamount = 0 ; is increased every new TRAP
ISRstart LABEL BYTE
ADDINT ISR,08 ; timer int
ADDINT ISR,09 ; keyboard int
ADDINT ISR,10 ; video int
ADDINT ISR,13 ; diskdrive int
IFDEF MSDOSBASE
ADDINT ISR,21 ; msdos function INT
ENDIF
ADDINT ISR,28 ; dos IDLE int
TRAPstart LABEL BYTE
ADDINT TRAP,1b ; rom BIOS cntrl-break interrupt
ADDINT TRAP,23 ; ctrl-break interrupt
ADDINT TRAP,24 ; critical ERROR handler
ERRMODEaddr dd ? ; Errormode adres
INDOSaddr dd ? ; InDos flag
IObreak db ? ; IObreak flag
DTAaddr dd ? ; Original DTA address
EEinfo label byte ; Extended error info
EEax dw ? ; ax : Extended Error Code
EEbx dw ? ; bx : Error Class
EEcx dw ? ; cl : Suggested Action, ch : Error Src
EEdx dw ? ; dx : undefined
EEsi dw ? ; si : undefined
EEdi dw ? ; di : undefined
EEds dw ? ; ds : undefined
EEes dw ? ; es : undefined
dw 3 dup (0)
PSPtsr dw ? ; PSP tsr segment
PSPfront dw ? ; PSP front app segment
frontSS dw ? ; STACK-SEG front app
frontSP dw ? ; STACK-OFF front app
tsrSS dw S mytsr ; New STACK-SEG and STACK-OFF
tsrSP dw O mytsr:TSRstack + (STACKsize - 1)
; Multiplex jumps (INT2F)
MUXJumps LABEL BYTE
dw O MUXinstall ; Check TSR installation (00)
dw O MUXunhook ; Request TSR unhook (01)
dw O MUXfreeze ; Request freeze TSR (02)
dw O MUXactive ; Request activate TSR (03)
dw O MUXrunapp ; Force running TSR (04)
MUXAmount EQU ($ - MUXJumps) / 2
hotAPP db FALSE ; Toggle want to start APP
inAPP db FALSE ; Toggle in APP
residentAPP db FALSE ; Toggle resident
freezeTSR db FALSE ; Toggle APP freeze
APPstatus db TRUE ; Status of executed APP
TSRname db PRG_name,PRG_ver,PRG_date,0d
TSRstack db STACKsize dup ('?')
IFDEF HOTKEYBASE
scanKEY db ? ; last pressed key
ENDIF
IFDEF TIMERBASE
TIMERcount dw timerSTART ; countdown timer
ENDIF
IFDEF MSDOSBASE
DOSfunc db 0h ; last activated DOS function
ENDIF
; *******************************************************************
; ************************ CUSTOM DATA ******************************
; *******************************************************************
; *******************************************************************
; ************************** TSR CODE *******************************
; *******************************************************************
; safeDOS
; This procedure checks if DOS is ready to be entered. When the INDOS-flag
; is unequal to 0, it can't be done. Except when ISR28 is executed, because
; DOS is then only waiting for a key, and it's safe to enter it.
;
; - returns -
; CARRY = 0 : everything is ok
; CARRY = 1 : not safe to enter DOS
;
safeDOS proc near
push ds
push bx
push ax
lds bx,cs:ERRMODEaddr ; check errormode
mov ah,ds:[bx]
lds bx,cs:INDOSaddr ; check indos
mov al,ds:[bx]
xor bx,bx
cmp bl,cs:inISR28
rcl bl,01h ; bl = 1 when in INT28
cmp bx,ax ; when carry=0, all ok
pop ax
pop bx
pop ds
ret
safeDOS endp
;------------------------------------------------------------------------------
; checkINTS
; Checks if one of the hooked interrupts isn't already accessed. If one of
; them is accessed, it's not safe to enter the application if it uses one
; or more of the interrupts.
;
; - returns -
; CARRY = 0 : When it's ok to enter the app
; CARRY = 1 : When it's not safe to do so
;
checkINTS proc near
push ax
mov ax,00001011b ; check 8259A pic for any int
out 20h,al ; request
jmp short @@waitcycle
@@waitcycle: in al,20h ; get int-mask
cmp ah,al ; when some set, not safe
jc @@intactive
xor al,al ; already in kbint ?
cmp al,cs:inISR09
jc @@intactive
cmp al,cs:inISR10 ; already in videoint ?
jc @@intactive
cmp al,cs:inISR13 ; already in diskdrive-int ?
@@intactive: pop ax ; when CARRY=1, not safe
ret
checkINTS endp
;------------------------------------------------------------------------------
; TSRcheck
; Checks if it is safe to run the application. Also when the application is
; already executed, it's reported.
;
; - returns -
; CARRY = 0 : It's safe to run the application
; CARRY = 1 : It's not safe to run the application
;
TSRcheck proc near
rol B cs:freezeTSR,1h ; when app is freezed, no exec
jc @@noexec
ror cs:inAPP,1h ; carry=1 when appl
jc @@noexec ; already active
IFDEF TIMERBASE
cmp w cs:TIMERcount,0h ; when timer is zero
jg @@check_hot ; I want to activate
mov cs:hotAPP,TRUE ; application
ENDIF
@@check_hot: rol B cs:hotAPP,1h ; when carry = 1 then
cmc ; not want app to start
jc @@noexec
call safeDOS ; carry = 1 when dos
jc @@noexec ; busy
call checkINTS ; carry = 1, ints active
@@noexec: ret
TSRcheck endp
;------------------------------------------------------------------------------
; TSRunhook
; This procedure unhooks the TSR and frees the memory used.
;
; WARNING: Only safe when TSRcheck returns a CARRY = 0.
;
; - returns -
; Nothing
;
TSRunhook proc near
mov cx,ISRamount ; number of ISR to unhook
mov si,O mytsr:ISRstart
call TSRfreeints ; unhook the vectors
mov W es,cs:PSPtsr ; es = tsrpsp
DOSB 049h ; free pspseg
mov W es,cs:PSPtsr ; ax = tsrpsp
mov es,es:[02ch] ; get program segment
DOSB 049h ; release program
ret
TSRunhook endp
;------------------------------------------------------------------------------
; myISR08
; This procedure checks if the application must be executed, and if it is
; safe to execute. When it does, the application is executed.
;
; - returns -
; Nothing
;
myISR08 proc far
pushf
cli
call cs:prevISR08 ; call old
cmp B cs:inISR08,0h ; already in 08 ?? quit!
jne @@quit08
inc B cs:inISR08 ; now were in
IFDEF TIMERBASE
sub w cs:TIMERcount,1h ; decrease timer
jnc @@no_reset
mov w cs:TIMERcount,timerStart
ENDIF
@@no_reset: sti
call TSRcheck ; when not safe, no
jc @@leave08 ; executing possible
@@startapp: call callAPP ; the twilight zone !
@@leave08: dec B cs:inISR08
@@quit08: iret
myISR08 endp
;------------------------------------------------------------------------------
; myISR09
; This procedure checks if the right key-sequence is pressed, and sets a
; flag so that the TSR is executed when it's safe in myISR08 or myISR28.
;
; - returns -
; Nothing
;
myISR09 proc far
push ds
push ax
push bx
push cs
pop ds
IFDEF HOTKEYBASE
in al,60h ; get current scan-code
mov cs:scanKEY,al
ENDIF
pushf
cli
call cs:prevISR09 ; execute old kb-int
cmp B cs:inISR09,0h ; if already in kb-int
jne @@quit09
ror B cs:hotAPP,1h ; or already succeeded executing
jc @@quit09
rol B cs:freezeTSR,1h ; or freezed application
jc @@quit09 ; stop further executing
inc B cs:inISR09 ; now were in INT09
sti
IFDEF HOTKEYBASE
push ds
mov ax,40h ; 040:017 is KB-FLAG pos
mov ds,ax
mov al,ds:[017h] ; get shift-scancode
pop ds
and al,KBmask
cmp al,KBmask ; check if desired bits are ok
jne @@leave09
mov B cs:hotAPP,TRUE ; i want to get in APP
ENDIF
@@leave09: dec B cs:inISR09 ; no more in ISR09
@@quit09: pop bx
pop ax
pop ds
iret
myISR09 endp
;------------------------------------------------------------------------------
; myISR10
; Sets a flag when ISR10 is accessed. So TSRcheck knows when it's not safe to
; enter the application who uses this ISR.
;
myISR10 proc far ; new video int
inc B cs:inISR10 ; were now in INT10
pushf
cli
call cs:prevISR10
dec B cs:inISR10
iret
myISR10 endp
;------------------------------------------------------------------------------
; myISR13
; Sets a flag when ISR13 is accessed. So TSRcheck knows when it's not safe to
; enter the application who uses this ISR.
;
; - returns -
; Nothing
;
myISR13 proc far ; new diskdrive int
inc B cs:inISR13 ; were now in INT13
pushf
cli
call cs:prevISR13
pushf
dec B cs:inISR13
popf
sti
ret 2
myISR13 endp
;------------------------------------------------------------------------------
; myTRAP1b
; When an error occured, the original flags from the original INT aren't
; disturbed by the application.
;
; - returns -
; Nothing
;
myTRAP1b proc far ; rom BIOS cntrl-break interrupt
mov B cs:inISR1b,TRUE
iret
myTRAP1b endp
;------------------------------------------------------------------------------
; myTRAP23
; When an error occured, the original flags from the original INT aren't
; disturbed by the application.
;
; - returns -
; Nothing
;
myTRAP23 proc far ; cntrl-BREAK interrupt
mov B cs:inISR23,TRUE
iret
myTRAP23 endp
;------------------------------------------------------------------------------
; myTRAP24
; When an error occured, the original flags from the original INT aren't
; disturbed by the application.
;
; - returns -
; Nothing
;
myTRAP24 proc far ; critical Error interrupt
mov B cs:inISR24,TRUE
mov al,3h ; fail MSDOS-call
iret
myTRAP24 endp
;------------------------------------------------------------------------------
; myISR28
; This procedure is called when DOS waits for a key or something. It's pretty
; safe to enter the application from this point. So it's done when the rest
; of DOS isn't accessed.
;
; To access this TSR:
; dx:bx - Unique value only known by front TSR installer
; ds:si - Pointing to TSR name
; cx - Sum of dx and bx
; ax - Function number
;
; - returns -
; Nothing
;
myISR28 proc far
cmp bx,LOWsign ; check if LOW and HIGH
jne @@failed_pass1 ; sign are OK
cmp dx,HIGHsign
jne @@failed_pass1
push ax ; and if CX is sum of
mov ax,bx ; both signs ...
add ax,dx
cmp cx,ax
jne @@failed_pass2
push si ; and ds:si contains
push di ; identical name ...
push es
mov di,O mytsr:TSRname
mov ax,S mytsr
mov es,ax
cld
@@pass3_loop: lodsb ; compare string and
cmp al,b es:[di] ; when OK, we're in !!
jne @@failed_pass3
inc di
or al,al
jnz @@pass3_loop
pop es
pop di
pop si
pop ax
call FRONTinterface ; talk to FRONT app
jmp @@quit28 ; and remember altered regs
@@failed_pass3: pop es
pop di
pop si
@@failed_pass2: pop ax
@@failed_pass1: pushf
cli
call cs:prevISR28 ; do previous IDLE int
cmp B cs:inISR28,0h
jne @@quit28
inc B cs:inISR28
call TSRcheck ; check it's safe to exec
jc @@leave28
call callAPP ; do APP
@@leave28: dec B cs:inISR28
@@quit28: iret
myISR28 endp
;------------------------------------------------------------------------------
; FRONTinterface
; This routine is used to talk to the TSR using a front application.
; A complex identification procedure makes sure it's our front APP
; which wants to communicate with us..
;
; - assumes -
; AX - Function number
;
; - returns -
; AX - Contains the value TRUE
; BX - Returned as internal status (TRUE = OK, FALSE = ERROR)
; CL - Returned as status
; TSR_UNHOOK gives FALSE when can't be unhooked
; TSR_RUNAPP gives FALSE when error inside APP
; DS:SI - Contains the address of the TSR real app
; DS:DX - Contains the data area of the TSR
;
; Functions are:
; TSR_INSTALL Request installation check (dummy)
; TSR_UNHOOK Request for unhook
; TSR_FREEZE Request for freezing application
; TSR_ACTIVATE Request activating again
; TSR_RUNAPP Force running application
;
FRONTinterface proc near
cmp ax,0h ; when wrong number
jl @@front_err ; exit
cmp ax,MUXamount
jl @@front_jump
@@front_err: mov bx,FALSE ; give error
mov ax,TRUE ; and exit
@@prepare_ptrs: push cs
pop ds
mov si,O mytsr:realAPP
mov dx,O mytsr:ISRstart
ret
@@front_jump: mov cx,TRUE ; cx = status ok (assume)
mov bx,O mytsr:MUXjumps ; jump to label
shl ax,1d
add bx,ax
jmp w cs:[bx]
MUXinstall: jmp @@front_ok ; just send some info
MUXunhook: call originalINT ; check if can be unhooked
jnc @@unhook_tsr
mov cl,FALSE ; status: can't unhook!
jmp @@front_ok
@@unhook_tsr: mov b cs:freezeTSR,TRUE ; freeze and
call TSRunhook ; unhook TSR !
mov cl,TRUE
jmp @@front_ok
MUXfreeze: mov b cs:freezeTSR,TRUE ; try to freeze TSR
jmp @@front_ok
MUXactive: mov b cs:freezeTSR,FALSE ; try to activate TSR
jmp @@front_ok
MUXrunapp: call callAPP ; run application
mov cl,b cs:APPstatus ; and return status
@@front_ok: mov ax,TRUE ; all went ok
mov bx,TRUE
jmp @@prepare_ptrs
FRONTinterface endp
;------------------------------------------------------------------------------
; originalINT
; This procedure checks if the TSR-vectors in the table match the vectors
; in the vector-table. If not, it's not safe to unhook, because one or
; more TSR's might loose their hooked vectors if the original vectors out
; of the TSR's table are reset.
;
; - returns -
; CARRY = 0 : If it's safe to do
; CARRY = 1 : If it's not safe
;
originalINT proc near
push cx
push si
push es
cld
mov cx,ISRamount ; number of ISR to inst
mov si,O mytsr:ISRstart
push cs ; ds = MYTSR
pop ds
@@check_next_isr:
lods B cs:[si] ; get int-nr
DOSB 35h ; get int-nr vector
cmp cs:[si+5],bx ; compare offset with old one
jne @@nope_unhook
push cs
pop bx
push es
pop ax
xor ax,bx ; compare segment with old one
jnz @@nope_unhook
add si,7h ; next isr-list
loop @@check_next_isr
clc
@@unhook_leave: pop es
pop si
pop cx
ret
@@nope_unhook: stc
jc @@unhook_leave
originalINT endp
;------------------------------------------------------------------------------
; myISR21
;
IFDEF MSDOSBASE
myISR21 proc far
mov b cs:DOSfunc,ah ; remember DOS function
pushf ; push flags already !
cli ; call the old DOS
call cs:prevISR21
pushf
cmp b cs:DOSfunc,3dh
jne @@normalend
mov b cs:hotAPP,TRUE ; always activate to check
sti
call TSRcheck ; when not safe, no
jc @@normalend ; executing possible
call callAPP ; the twilight zone !
@@normalend: popf
sti
ret 2
myISR21 endp
ENDIF
;------------------------------------------------------------------------------
; callAPP
; This procedure is called when the application must be called.
; All necressary things are done to preserve everything needed,
; and trap the ISR's which could be irritating when accessed.
; When all preserved, realAPP is called, and after that all is
; restored in it's original state.
;
; - returns -
; cs:APPstatus : TRUE when all went OK
; FALSE when something went wrong
;
callAPP proc near
rol b cs:residentAPP,1h ; is the app already resident?
jnc @@start ; if not, skip inAPP check
cmp b cs:inAPP,TRUE ; already in APP ?
jne @@start ; when so, stop further exec
ret
@@start: mov b cs:inAPP,TRUE
mov cs:frontSP,sp ; remember old stack
mov cs:frontSS,ss
mov ss,cs:tsrSS ; set new ss:sp value
mov sp,cs:tsrSP
push ds
push es
pusha ; 286 and higher.. I am sorry!
push cs ; ds = mytsr
pop ds
cld ; clear direction-flag
mov cx,TRAPamount ; get number of trap-ISR's
mov si,O mytsr:TRAPstart
@@set_traps: lods B cs:[si] ; AL = int-number
mov B cs:[si],FALSE ; reset the trap-flag
push ax
DOSB 35h ; get interrupt-vector
mov cs:[si+1],bx ; store old handler in list
mov cs:[si+3],es
pop ax ; restore int-number
mov dx,cs:[si+5] ; offset of this TSR's trap
DOSB 25h ; set interrupt-vector
add si,7h ; do next trap
loop @@set_traps
DOSW 03300h ; get previous extd-break state
mov B cs:IObreak,dl
xor dl,dl
inc al ; set break state (assume ah=33)
DOSB 033h
xor bx,bx
DOSB 059h ; get extended error-info
mov cs:EEds,ds ; store all extended error regs
mov cs:EEax,ax
mov cs:EEbx,bx
mov cs:EEcx,cx
mov cs:EEdx,dx
mov cs:EEsi,si
mov cs:EEdi,di
mov cs:EEes,es
DOSB 51h ; get current PSP-seg
mov cs:PSPfront,bx ; remember it
mov bx,cs:PSPtsr ; set my TSR's psp
DOSB 50h
DOSB 02fh ; get DTA-address -> ES:BX
mov si,O mytsr:DTAaddr
mov cs:[si],bx
mov cs:[si+2],es ; store it
mov ax,cs:PSPtsr ; at PSP:080h
mov ds,ax
mov dx,80h
DOSB 01ah
push cs ; seg = mytsr
pop ds
call realAPP ; start real application
lds dx,cs:DTAaddr ; restore previous DTA
DOSB 01ah ; DS:DX previous DTA
mov bx,cs:PSPfront ; restore PSPseg
DOSB 050h
push cs
pop ds
mov dx,O mytsr:EEinfo ; restore extended error info
DOSW 05d0ah
mov dl,cs:IObreak ; restore IO-break
DOSW 3301h ; set it
mov cx,TRAPamount ; reset TRAP-vectors
mov si,O mytsr:TRAPstart
call TSRfreeints
@@leaveAPP: popa ; restore all registers
pop es
pop ds
mov ss,cs:frontSS ; restore original stack
mov sp,cs:frontSP
IFDEF TIMERBASE
mov W cs:TIMERcount,timerSTART
ENDIF
mov B cs:hotAPP,FALSE ; application finished!
mov B cs:inAPP,FALSE ; outside APP
rol b cs:residentAPP,1h ; is app resident ?
jc @@leavecall ; when so, return FAR
retf
@@leavecall: ret
callAPP endp
;------------------------------------------------------------------------------
TSRfreeints proc near
push dx
push bx
push ds
cld ; clear direction-flag
@@unhnextisr: lods B cs:[si] ; get int-nr
mov dx,cs:[si+1] ; get old offset
mov bx,cs:[si+3] ; get old segment
mov ds,bx
DOSB 25h
add si,7h ; next interrupt
loop @@unhnextisr
pop ds
pop bx
pop dx
ret
TSRfreeints endp
;------------------------------------------------------------------------------
; REALapp
; This is your application. Do with it what you like.
;
; Set the application-status flag when you exit, to indicate
; something went wrong, or all went great!
;
; - returns -
; cs:APPstatus : TRUE when all went OK
; FALSE when something went wrong
;
REALapp proc near
mov ax,0b800h ; display funky
mov es,ax ; screenshit
mov ax,es:[0000h]
xor ax,0ffffh
mov es:[0000h],ax
mov es:[0002h],ax
mov b cs:APPstatus,TRUE
ret
REALapp endp
;------------------------------------------------------------------------------
mytsr ends
myinstall SEGMENT para public
assume cs:myinstall,ds:myinstall,es:myinstall
; *******************************************************************
; ************************ INSTALL DATA *****************************
; *******************************************************************
startsign db cr,lf,'- ',PRG_name,' (c) Jorgen Bodde, V',PRG_ver,' (',PRG_date,') -',cr,lf,cr,lf,eos
err_presign db 'ERROR: ',bell,eos
err_wrongdosver db 'Wrong MSDOS version (must be 3.1 or higher) !!',cr,lf,eos
err_installed db PRG_name,' already installed !!',cr,lf,eos
err_notinstall db PRG_name,' not yet installed !!',cr,lf,eos
err_unhook db 'One or more installed TSR''s altered the original vectors.',cr,lf
db PRG_name,' can''t unhook right now, try later !',cr,lf,eos
err_badarg db 'Unknown argument (Use option ''-?'' for HELP) !',cr,lf,eos
err_invalidargc db 'Invalid amount of arguments (Use option ''-?'' for HELP)',cr,lf,eos
err_insidetsr db 'Illegal interface operation between TSR and INSTALLER !',cr,lf,eos
err_runapp db 'While executing (resident) application !',cr,lf,eos
ok_unhooked db 'Unhook-request for ',PRG_name,' passed !!',cr,lf,eos
ok_installed db 'Resident part of ',PRG_name,' installed (use option ''-?'' for HELP).',cr,lf,eos
ok_freeze db PRG_name,' is now temporarely frozen !!',cr,lf,eos
ok_active db PRG_name,' is now active again !!',cr,lf,eos
ok_runapp db 'Execution of (resident) application went OK !',cr,lf,eos
ok_usage db 'USAGE: ',PRG_name,' [option]',cr,lf,cr,lf
db ' Options are : -? -h : This HELP',cr,lf
db ' -u : Unhook installed TSR',cr,lf
db ' -f : Temporarely freeze active TSR',cr,lf
db ' -a : Making frozen TSR active again',cr,lf
db ' -r : Run (resident) application without install',cr,lf,cr,lf,eos
ARGlist db '?hufar',0 ; LOWERCASE argument list to execute
ARGcalls dw O ARGhelp ; call-table for args
dw O ARGhelp
dw O ARGunhook
dw O ARGfreeze
dw O ARGreactivate
dw O ARGrunapp
APPname db PRG_name,PRG_ver,PRG_date,0
TSRinstalled db FALSE
;******************************************************************************
;****************************** INSTALL CODE **********************************
;******************************************************************************
main proc far
mov ax,cs ; display invitation
mov ds,ax
mov dx,O myinstall:startsign
DOSB 09h
assume ds:mytsr
mov ax,S mytsr ; ds = mytsr
mov ds,ax
mov w ds:PSPtsr,es ; save pspseg
DOSW 3000h ; get DOS-version
cmp al,02h ; jump when > 2
ja @@good_dosver
stc
mov dx,O myinstall:err_wrongdosver
call exitAPP ; display wrong version of DOS
@@good_dosver: push ds
xor ax,ax ; check if our TSR is here
call TSRinterface
pop ds
rol ax,1h
jnc @@check_arguments
mov B cs:TSRinstalled,TRUE ; our TSR is installed
@@check_arguments:
mov ax,w ds:PSPtsr ; es:bx = pointer arglist
mov es,ax
mov bx,080h
call argc ; minimum amount of arguments
mov cx,ax ; cx = amount of arguments
cmp ax,argMIN ; when none, try install
jge @@ok_arg
stc
mov dx,O myinstall:err_invalidargc
call exitAPP ; report invalid amount
@@ok_arg: cmp ax,1h ; when no arguments
je @@install_tsr ; try installing TSR
mov ax,1h ; get argv[1] (argument)
call argv
cmp b es:[bx],'-' ; check if option
je @@search_arg ; if not, it's a custom
cmp b es:[bx],'/' ; argument
je @@search_arg
@@do_custom: push bx
push cx
push es
call ARGcustom ; do CUSTOM argument
pop es
pop cx
pop bx
jmp @@install_tsr
@@search_arg: push cs ; check which argument
pop ds ; is the correct one
assume ds:myinstall
cld
mov di,O ARGcalls
mov si,O ARGlist
IFDEF ARGCASEINS ; when defined case insens
cmp b es:[bx+1],65d ; adjust to LOWERCASE
jl @@arg_loop
cmp b es:[bx+1],90d
jg @@arg_loop
add b es:[bx+1],32d
ENDIF
@@arg_loop: lodsb ; get argument case
cmp b es:[bx+1d],al ; when same, make call
je @@make_call
add di,2h ; next call in list
or al,al ; when no zero, check
jnz @@arg_loop ; next
stc ; unknown argument
mov dx,O myinstall:err_badarg
call exitAPP
@@make_call: push bx
push cx
push es
call W ds:[di] ; make argument call
pop es
pop cx
pop bx
cmp cx,2h ; when more than one
jg @@do_custom
@@install_tsr: rol cs:TSRinstalled,1h ; when TSR already
jnc @@try_install ; installed
mov dx,O myinstall:err_installed ; CARRY SET
call exitAPP ; report already installed
@@try_install: assume ds:mytsr ; set correct pointer
mov ax,S mytsr
mov ds,ax
DOSB 34h ; get indosflag
mov W ds:INDOSaddr,bx ; store address
mov W ds:INDOSaddr+2,es
mov W ds:ERRMODEaddr+2,es ; assume errormode = same seg
dec bx
mov W ds:ERRMODEaddr,bx ; and one byte before indos
cld
mov cx,ISRamount ; number of ISR to install
mov si,O mytsr:ISRstart
@@donextisr: lodsb ; get int-nr
push ax
DOSB 35h ; get int-nr vector
mov [si+1],bx ; store old one
mov [si+3],es
pop ax
push ds
mov dx,[si+5] ; get new off
mov bx,S mytsr ; get new seg
mov ds,bx
DOSB 25h
pop ds
add si,7h ; next interrupt
loop @@donextisr
clc
push cs ; display OK!
pop ds
mov dx,O myinstall:ok_installed
DOSB 09h
mov ax,S mytsr ; my tsr again!
mov ds,ax
mov b ds:residentAPP,TRUE ; tsr is resident !
mov es,ds:PSPtsr ; get PSPseg back
push es
mov es,es:[02ch]
DOSB 049h ; free segment environment
pop ax ; ax = pspseg
mov dx,cs ; para start of inst-part
sub dx,ax ; DX = size of resident
DOSW 3100h ; exitAPP and stay res
main endp
;------------------------------------------------------------------------------
; ARG_____ procedures called with ES:BX pointing to argumentlist
; You can use ARGC and ARGV to get the necressary argument info
;
; ES:BX = pointer to arguments
; CX = amount of arguments
ARGcustom proc near
;
; Place your custom code here, when you want to
; check for a filename or more than one argument in
; the TSR..
;
; ARGcustom is called when the number of arguments
; is greater then one, or when the first argument
; isn't an option.
;
ret
ARGcustom endp
;------------------------------------------------------------------------------
ARGhelp proc near
clc
mov dx,O myinstall:ok_usage
call exitAPP
ARGhelp endp
;------------------------------------------------------------------------------
ARGunhook proc near
call CheckInstall
mov al,TSR_UNHOOK ; --> unhook yourself!
call TSRinterface
rol cl,1h ; if ah = ff, other tsr above
jc @@ok_unhook ; our tsr
cmc
mov dx,O myinstall:err_unhook
call exitAPP ; report TSR above ours
@@ok_unhook: cmc
mov dx,O myinstall:ok_unhooked
call exitAPP ; report unhook-request
ARGunhook endp
;------------------------------------------------------------------------------
ARGfreeze proc near
call CheckInstall
mov al,TSR_FREEZE ; freeze TSR
call TSRinterface
clc ; freeze sign
mov dx,O myinstall:ok_freeze
call exitAPP
ARGfreeze endp
;------------------------------------------------------------------------------
ARGreactivate proc near
call CheckInstall
mov al,TSR_ACTIVATE ; reactivate TSR
call TSRinterface
clc ; active sign
mov dx,O myinstall:ok_active
call exitAPP
ARGreactivate endp
;------------------------------------------------------------------------------
ARGrunapp proc near
rol cs:TSRinstalled,1h ; when installed
jnc @@no_tsr ; execute already resident
mov al,TSR_RUNAPP ; try to run TSR
call TSRinterface
mov dx,O myinstall:ok_runapp
rol cl,1h ; display status of app
jc @@display ; runned
jmp @@err_tsr
@@no_tsr: call F mytsr:callAPP ; call APP and start
mov dx,O myinstall:ok_runapp
mov ax,S mytsr ; display status
mov ds,ax ; afterwards
rol b ds:APPstatus,1h
jc @@display
@@err_tsr: mov dx,O myinstall:err_runapp
@@display: cmc
call exitAPP
ret
ARGrunapp endp
;------------------------------------------------------------------------------
ARGforceexec proc near
ARGforceexec endp
;------------------------------------------------------------------------------
; CheckInstall
; This routine checks if the tsr is installed, and
; gives an error when not.
;
; - assumes -
; cs:TSRinstalled : TRUE when installed (OK)
; FALSE when not (ERROR)
;
; - returns -
; Only when TSR is installed
;
CheckInstall proc near
rol cs:TSRinstalled,1h ; when installed
jnc @@no_tsr ; return normally
ret
@@no_tsr: cmc
mov dx,O myinstall:err_notinstall
call exitAPP
CheckInstall endp
;------------------------------------------------------------------------------
; exitAPP
; This procedure prints a text pointed to by ds:dx and exitAPPs the program
; with an error (carry = 1), or normally (carry = 0)
;
; - assumes -
; CARRY = 1 : Error occured
; CARRY = 0 : exitAPP normally
; DX : offset for message
;
; - returns -
; To dos with errorlevel = 1 if carry = 1
; else to dos with errorlevel = 0.
;
exitAPP proc near
push cs ; prepare pointer
pop ds
mov al,0h ; assume error without
jnc @@good ; disturbing CARRY
inc al
push ax ; push error
push dx
mov dx,O err_presign
DOSB 09h ; print error-presign
pop dx
pop ax
@@good: push ax
DOSB 09h ; exitAPP exit with returncode
pop ax
DOSB 4ch
exitAPP endp
;------------------------------------------------------------------------------
; TSRinterface
; This function tries to talk to our TSR.
;
; - assumes -
; AX = Function value (See function FRONTinterface for details)
;
; - returns -
; (See function FRONTinterface for details)
;
TSRinterface proc near
mov dx,HIGHsign ; prepare KEY for TSR
mov bx,LOWsign ; to get through
mov cx,bx
add cx,dx
push cs
pop ds
mov si,O myinstall:APPname
int 28h ; talk to our TSR
cmp ax,TRUE ; is our TSR present ?
jne @@no_tsr
or bx,bx ; when FALSE, error from TSR
jnz @@no_tsr
stc
mov dx,O myinstall:err_insidetsr
call exitAPP ; illegal error inside TSR
@@no_tsr: ret
TSRinterface endp
;------------------------------------------------------------------------------
; Call with: ES:BX = command line
;
; Returns: AX = argument count (always >=1)
; Other registers preserved
argc proc near
push bx
push cx
mov ax,1
@@argc1: mov cx,-1
@@argc2: inc bx
cmp byte ptr es:[bx],cr
je @@argc3
cmp byte ptr es:[bx],blank
je @@argc1
cmp byte ptr es:[bx],tab
je @@argc1
jcxz @@argc2
inc ax
not cx
jmp @@argc2
@@argc3: pop cx
pop bx
ret
argc endp
;------------------------------------------------------------------------------
;
; ARGV.ASM: return address and length of specified
; command line argument or fully qualified program
; name. Treats blanks and tabs as whitespace, carriage
; return as terminator.
;
; (C) 1987 Ray Duncan
;
; Call with: ES:BX = command line address
; (implicit: ES=PSP segment)
; AX = argument number (0 based)
;
; Returns: ES:BX = argument address
; AX = argument length
; (0=argument not found)
; Other registers preserved.
;
; If called with AX=0 (argv[0]) and running under
; MS-DOS version 3.0 or later, returns ES:BX pointing
; to program name in environment block and AX=length,
; otherwise returns ES:BX unchanged and AX=0.
;
argv proc near
push cx
push di
or ax,ax
jz @@argv8
xor ah,ah
@@argv1: mov cx,-1
@@argv2: inc bx
cmp byte ptr es:[bx],cr
je @@argv7
cmp byte ptr es:[bx],blank
je @@argv1
cmp byte ptr es:[bx],tab
je @@argv1
jcxz @@argv2
inc ah
cmp ah,al
je @@argv4
not cx
jmp @@argv2
@@argv4: mov ax,bx
@@argv5: inc bx
cmp byte ptr es:[bx],cr
je @@argv6
cmp byte ptr es:[bx],blank
je @@argv6
cmp byte ptr es:[bx],tab
jne @@argv5
@@argv6: xchg bx,ax
sub ax,bx
jmp @@argvx
@@argv7: xor ax,ax
jmp @@argvx
@@argv8: mov ax,3000h
int 21h
cmp al,3
jb @@argv7
mov es,es:[2ch]
xor di,di
xor al,al
mov cx,-1
cld
@@argv9: repne scasb
scasb
jne @@argv9
add di,2
mov bx,di
mov cx,-1
repne scasb
not cx
dec cx
mov ax,cx
@@argvx: pop di
pop cx
ret
argv endp
;------------------------------------------------------------------------------
myinstall ends
mystack SEGMENT WORD STACK 'stack'
; *******************************************************************
; ************************ INSTALL STACK ****************************
; *******************************************************************
db 256 dup ('?')
mystack ENDS
END main