home *** CD-ROM | disk | FTP | other *** search
- ;==============================================================================
- ; TSRDEMO2.ASM
- ; Thomas Brandenborg
- ; 87.02.11
- ;
- ; Sample demonstration of a safe approach for writing TSR programmes.
- ;
- ;------------------------------------------------------------------------------
- ; Copyright 1987 by Thomas Brandenborg. All Rights Reserved
- ; Written for uploading to Compuserve Forums by
- ;
- ; Thomas Brandenborg
- ; Lundbyesgade 11
- ; DK-8000 Aarhus C
- ; DENMARK
- ;
- ; This code is intended as a reference to users on Compuserve Forums
- ; on how to write Terminate-And-Stay resident programmes for Personal
- ; Cumputers running under DOS versions 2.00 and newer.
- ;
- ; The code is not part of any proprietary product, but is rather a
- ; demonstration of such techniques that may be used to write safe TSR
- ; programmes.
- ;
- ; All or part of it may, however, be used in other software products
- ; or otherwise distributed assuming the copyright holders Name & Address
- ; as listed above are included clearly and visibly in the documentation
- ; for such product.
- ;
- ; The copyright holder offers no warranty with this code whatsoever,
- ; including its fitness for any particular purpose. Neither shall he
- ; be liable for damages of any kind that may arise from its use.
- ;
- ;
- ; IF YOU THINK THIS CODE IS USEFUL:
- ;
- ; If you think this code has had some value to you, and in particular
- ; if you consider using all or parts of it in your own product, you
- ; may want to consider a smaller or larger donation to the author
- ; (Name & Address above) who spend his late night hours putting
- ; it together.
- ;
- ; As to the size of a such donation this is entirely up to your own
- ; judgement. It is suggested that you simply consider the value this
- ; code has had to you, the time you saved not having to write it your
- ; self... that should help you determine the right amount.
- ;
- ; Please notice that such donations are an entirely voluntary contribution.
- ; This holds true whatever your purpose is for using this code, and whatever
- ; the type of product and distribution you work with. The author has nothing
- ; against commercial software distribution and does not have any reason
- ; to restrict developers of commercial products in their use of this code.
- ;------------------------------------------------------------------------------
- ; COMPILING: masm tsrdemo2;
- ; LINKING: link tsrdemo2;
- ; exe2bin tsrdemo2 tsrdemo2.com
- ;------------------------------------------------------------------------------
- ; Revisions
- ; Brandenborg 87.02.14 Added copyright notice & checked comments
- ; Brandenborg 87.02.17 Added full AX value in Set Ext Err call
- ; Brandenborg 87.02.25 Went through to optimize things
- ; Brandenborg 87.02.28 Added auto INT28 invocation in INT16 handler
- ; Brandenborg 87.03.01 Added INT21 filter for recursion onto console stack
- ; Brandenborg 87.03.02 Final cleanup of comments etc.
- ;==============================================================================
-
- ;==============================================================================
- ; DEFINE BIOS DATA SEGMENT OFFSETS
- ;==============================================================================
-
- BiosData segment at 40h
- org 17h
- KbFlag label byte ;current shift status bits
- org 18h
- KbFlag1 label byte ;current key status of toggle keys
- BiosData ends
-
- ;==============================================================================
- ; DEFINE OFFSETS WITHIN BIOS EXTRA DATA SEGMENT
- ;==============================================================================
-
- BiosXX segment at 50h
- org 0
- StatusByte label byte ;PrtSc status
- BiosXX ends
-
- ErrPrtSc equ -1 ;err during last PrtSc
- InPrtSc equ 1 ;PrtSc in progress
-
- ;==============================================================================
- ; DEFINE OFFSETS WITHIN OUR PSP
- ;==============================================================================
-
- Cseg segment byte public
- org 2
- TopSeg label word ;last seg in alloc block
- org 2ch
- EnvSeg label word ;seg of our environment copy
- Cseg ends
-
- ;==============================================================================
- ; DOS COM-FILE ENTRY POINT
- ;==============================================================================
-
- Cseg segment public byte
- assume cs:Cseg, ds:nothing, es:nothing, ss:nothing
- org 100h
- ComEntry: jmp Init ;JMP to init at bottom of seg
-
- ;==============================================================================
- ; IDENTIFICATION CODES FOR THIS TSR (MUST BE UNIQUE FOR EACH CO-EXISTING TSR)
- ; HIGH BYTE OF GetId MUST NOT MATCH ANY AH REQUEST CODES FOR INT16H.
- ;==============================================================================
-
- GetId equ 'tc' ;INT16h AX val to get MyId
- MyId equ 'TC' ;ID of this TSR
-
- ;==============================================================================
- ; FLAGS AND PTRS FOR RESIDENT HANDLING
- ;==============================================================================
-
- TsrMode db 0 ;bits for various modes
- InInt08 equ 1 SHL 0 ;timer0 tick handler
- InInt09 equ 1 SHL 1 ;keyboard handler
- InInt13 equ 1 SHL 2 ;BIOS disk I/O
- InInt28 equ 1 SHL 3 ;INT28 handler
- In28Call equ 1 SHL 4 ;we have issued INT28
- InPopup equ 1 SHL 5 ;popup routine activated
- NewDos equ 1 SHL 6 ;DOS 2.x in use
- InDosClr equ 1 SHL 7 ;InDos=0 at popup time
-
- KeyMode db 0 ;bits for hotkey status
- HotIsShift equ 1 SHL 0 ;hotkey is shift state
- InHotMatch equ 1 SHL 1 ;so far keys match hotkey seq
- HotKeyOn equ 1 SHL 2 ;full hotkey pressed
-
- InDosPtr label dword ;seg:off of InDos flag
- InDosOff dw 0
- InDosSeg dw 0
-
- CritErrPtr label dword ;seg:off of CritErr flag
- CritErrOff dw 0
- CritErrSeg dw 0
-
- ;==============================================================================
- ; DATA FOR INT09H HANDLER TO CHECK FOR HOTKEY COMBINATION
- ;==============================================================================
-
- ; ------------ EQU'S FOR BIT SHIFTS WITHIN KEYBOARD FLAGS
-
- InsState equ 80h
- CapsState equ 40h
- NumState equ 20h
- ScrollState equ 10h
- AltShift equ 08h
- CtlShift equ 04h
- LeftShift equ 02h
- RightShift equ 01h
-
- InsShift equ 80h
- CapsShift equ 40h
- NumShift equ 20h
- ScrollShift equ 10h
- HoldState equ 08h
-
- ; ------------ SCAN CODES FOR VARIOUS SHIFT KEYS
-
- LeftDown equ 42 ;scan code of left shift key
- LeftUp equ LeftDown OR 80h
- RightDown equ 54 ;scan code of right shift key
- RightUp equ RightDown OR 80h
- AltDown equ 56 ;scan code of alt key
- AltUp equ AltDown OR 80h
- CtlDown equ 29 ;scan code of ctrl key
- CtlUp equ CtlDown OR 80h
-
- ; ------------ MISC KEYBOARD DATA
-
- KbData equ 60h ;keyboard data input
-
- ;==============================================================================
- ; TO USE A SHIFT KEY COMBINATION AS HOT KEY:
- ; - SET THE FLAG HotIsShift IN KeyMode
- ; - DEFINE THE SHIFT STATUS BITS IN THE VARIABLE HotKeyShift
- ;
- ; TO USE A SERIES OF SCAN CODES AS HOT KEY:
- ; CLEAR THE FLAG HotIsShift IN KeyMode
- ; - INSERT THE MAKE AND BREAK SCAN CODES IN THE HotKeySeq STRING
- ; NOTE: WITH THIS DEMO IMPLEMENTATION YOU SHOULD NOT USE A HOT KEY
- ; SEQUENCE WHICH PRODUCES A KEY IN THE BIOS KEYBOARD QUEUE,
- ; SINCE THE KEY IS NOT REMOVED BEFORE CALLING THE POPUP ROUTINE.
- ;
- ; NOTE: HOTKEY TYPE AND CONTENTS OF HOTKEY VARIABLES MAY BE CHANGED AT RUN TIME
- ;==============================================================================
-
- HotKeyShift db LeftShift OR RightShift ;shift state IF HotIsShift=FF
-
- HotKeySeq db LeftDown,LeftUp,LeftDown,LeftUp
- HotKeyLen equ $-HotKeySeq
- HotIndex db 0 ;# key in seq to compare next
- BetweenKeys db 0 ;timeout count between keys
- KeyTimeOut equ 10 ;more ticks means not a hotkey
-
- ;==============================================================================
- ; DATA FOR INT08H HANDLER TO CHECK FOR POPUP
- ;==============================================================================
-
- SafeWait db 0 ;count-down for safe popup
- MaxWait equ 8 ;wait no more 8/18 sec
-
- ;==============================================================================
- ; PROCESS & SYSTEM DATA
- ;==============================================================================
-
- OurSS dw 0 ;stack for popup routine
- OurSP dw 0
- StackSize equ 512 ;bytes to reserve for stack
-
- OldSS dw 0 ;old stack seg
- OldSP dw 0 ;old stack off
-
- OurPSP dw 0 ;our PSP seg
- OldPSP dw 0 ;old PSP seg
-
- OldDTA label dword ;seg:off of old DTA area
- OldDTAOff dw 0
- OldDTASeg dw 0
-
- OurDTA label dword ;seg:off of our DTA
- OurDTAOff dw 0
- OurDTASeg dw 0
-
- OldBreak db 0 ;old ctrl-break state
- OldExtErr dw 3 dup (0) ;AX,BX,CX of ext err
-
- ;==============================================================================
- ; LOCATIONS FOR SAVED INTERRUPT VECTORS
- ;==============================================================================
-
- OldInt08 label dword ;Timer0 loaded before this
- OldInt08Off dw 0
- OldInt08Seg dw 0
-
- OldInt09 label dword ;Kb handler loadde before this
- OldInt09Off dw 0
- OldInt09Seg dw 0
-
- OldInt13 label dword ;BIOS diskette I/O
- OldInt13Off dw 0
- OldInt13Seg dw 0
-
- OldInt16 label dword ;BIOS kb Q-handler
- OldInt16Off dw 0
- OldInt16Seg dw 0
-
- OldInt1B label dword ;^break of process we steal
- OldInt1BOff dw 0
- OldInt1BSeg dw 0
-
- OldInt1C label dword ;timer tick of process we steal
- OldInt1COff dw 0
- OldInt1CSeg dw 0
-
- OldInt21 label dword ;DOS function dispatcher
- OldInt21Off dw 0
- OldInt21Seg dw 0
-
- OldInt23 label dword ;^C of process we steal
- OldInt23Off dw 0
- OldInt23Seg dw 0
-
- OldInt24 label dword ;crit err of process we steal
- OldInt24Off dw 0
- OldInt24Seg dw 0
-
- OldInt28 label dword ;DOS idles loaded before this
- OldInt28Off dw 0
- OldInt28Seg dw 0
-
- ;==============================================================================
- ; SPEAKER/TONE GENERATION DATA
- ;==============================================================================
-
- PB0port equ 61h ;port for speaker bit
- ErrLen1 equ 10 ;# outer err beep cycles
- ErrLen2 equ 80 ;# inner err beep cycles
- ErrLow equ 100 ;low tone wait in err beep
- ErrHi equ 40 ;hi tone wait in err beep
-
- ;==============================================================================
- ; ErrBeep - PRODUCE ERROR-INDICATING SOUND ON SPEAKER
- ;==============================================================================
-
- ErrBeep proc near
- assume ds:nothing, es:nothing, ss:nothing
-
- push ax ;save regs used
- push bx
- push cx
- push dx
-
- mov cx,ErrLen1 ;# mix-cycles for beep
-
- ErrBeep1: mov dx,ErrLow ;wait time for half-cycle
- mov bx,ErrLen2 ;len of one tone
- call DoTone ;output low err tone
- mov dx,ErrHi ;wait time for half-cycle
- mov bx,ErrLen2 ;len of one tone
- call DoTone ;output low err tone
-
- loop ErrBeep1 ;loop for some time
-
- pop dx
- pop cx ;restore regs
- pop bx
- pop ax
- ret
- ErrBeep endp
-
- ;==============================================================================
- ; DoTone - OUTPUT ONE TONE ON THE SPEAKER
- ;
- ; INPUT: DX: LOOP WAIT TIME FOR HALF CYCLE IN TONE
- ; BX: NUMBER OF CYCLES FOR TONE DURATION
- ; OUTPUT: NONE
- ; REGS: ALL PRESERVED
- ;==============================================================================
-
- DoTone proc near
- assume ds:nothing, es:nothing, ss:nothing
-
- push ax ;save regs used
- push bx
- push cx
- in al,PB0port ;get PB0 reg pattern
- mov ah,al ;save it
-
- DoTone1: and al,0fch ;mask off speaker bit
- out PB0port,al ;pull!
- mov cx,dx ;half cycle in counter
- DoTone2: loop DoTone2 ;leave there for half a cycle
- or al,2 ;turn on speaker bit
- out PB0port,al ;push!
- mov cx,dx ;half cycle in counter
- DoTone3: loop DoTone3 ;leave there for half a cycle
-
- dec bx ;count down tone duration
- jnz DoTone1 ;go through full tone
-
- mov al,ah ;AL=original PB0 reg value
- out PB0port,al ;restore
-
- pop cx ;restore regs
- pop bx
- pop ax
- ret
- DoTone endp
-
- ;==============================================================================
- ; TestSafe - CHECK IF THIS IS A SAFE TIME TO DO A POP UP
- ;
- ; RETURN CLC IF SAFE TO POP UP, CY IF NOT SAFE.
- ;
- ; CHECK IF ANY INTs ARE IN CRITICAL AREAS (InInt09 & InInt13)
- ; CHECK IF WE ARE IN AN OUR OWN INT28 CALL (In28Call)
- ; CHECK 8259A PIC ISR REGISTER FOR MISSING EOIs
- ; CHECK IF DOS IS STABLE FOR POP UP
- ; CHECK IF A PRINT SCREEN IS IN PROGRESS
- ;==============================================================================
-
- TestSafe proc near
- assume ds:nothing, es:nothing
-
- push ax ;save regs used
- push bx
- push ds
-
- ; ------------ CHECK INTs TO SEE IF THEY WERE INTERRUPTED AT BAD TIMES
-
- test TsrMode,InInt09 OR InInt13 OR In28Call
- jnz NotSafe ;jump if any INTs are chopped
-
- ; ------------ CHECK THE 8259A PIC ISR REGISTER FOR NON-EOIed HW INTs
-
- mov al,00001011b ;tell 8259A we want the ISR
- out 20h,al ;8259A command reg
- nop
- nop
- nop ;now, ISR should be ready
- in al,20h ;AL=mask of active INTs
- or al,al ;test all (IRQ0 *did* EOI)
- jnz NotSafe ;jump if active INTs
-
- ; ------------ NOW, ENSURE THAT DOS WAS NOT INTERRUPTED
-
- assume ds:nothing
-
- lds bx,InDosPtr ;now, DS:BX=InDos
- mov al,byte ptr [bx] ;get InDos to AL
- lds bx,CritErrPtr ;now, DS:BX=CritErr
- or al,byte ptr [bx] ;both flags zero?
- jz DosSafe ;YES - DOS is really idle
- test TsrMode,InInt28 ;is this an INT28h
- jz NotSafe ;NO - not safe, should be idle
- cmp al,1 ;YES - one InDos entry only?
- ja NotSafe ;NO - jump if more than one
- DosSafe:
-
- ; ------------ CHECK TO SEE IF A PRINT SCREEN IS IN PROGRESS
-
- mov ax,BiosXX
- mov ds,ax ;move DS to BIOS extra data seg
- assume ds:BiosXX
-
- cmp StatusByte,InPrtSc ;print screen in progress?
- je NotSafe ;YES - jump if prtsc
-
- ; ------------ SEEMS TO BE A SAFE TIME FOR POPUP
-
- IsSafe: clc ;CLC=safe to popup
- jmp short ExitSafe ;end this then
-
- ; ------------ APPARENTLY THIS IS JUST NOT THE TIME TO DO A POPUP
-
- NotSafe: stc ;CY=don't popup now
-
- ; ------------ RETURN TO CALLER WITH CARRY SET/CLEAR
-
- ExitSafe: pop ds ;restore regs
- pop bx
- pop ax
- ret
- TestSafe endp
-
- ;==============================================================================
- ; OurInt08 - TSR INT08H HANDLER TO WATCH FOR HOTKEY AND SAFE POPUP TIMES
- ;
- ; CALL OldInt08
- ; CHECK FOR RE-ENTRANCE INTO CRITICAL INT08 CODE
- ; SET InInt08 FLAG
- ; CHECK FOR TIMEOUT BETWEEN KEYS IN HOTKEY SEQUENCE
- ; CHECK IF HOTKEY WAS PRESSED
- ; CHECK IF ALREADY InPopup OR InInt28
- ; CHECK IF SAFE TIME FOR SYSTEM TO POPUP
- ; UPDATE FLAGS AND CALL POPUP IF SAFE
- ; GIVE ERROR BEEP IF POPUP WAS UNSAFE FOR A LONG TIME
- ; RESET InInt08 FLAG
- ; DO IRET
- ;==============================================================================
-
- ; ------------ NEAR JUMP DESTINATION FOR FAST IRET'S
-
- Exit08: iret ;IRET (!)
-
- ; ------------ ACTUAL INT08 ENTRY POINT
-
- OurInt08 proc far
- assume ds:nothing, es:nothing, ss:nothing
-
- pushf ;simulate INT08
- cli ;in case others forgot it
- call OldInt08 ;call TSRs loaded before us
-
- ; ------------ ENSURE NO RECURSION INTO CRITICAL INT08 CODE
-
- sti ;we'll manage INTs
-
- test TsrMode,InInt08 ;already in here somewhere?
- jnz Exit08 ;YES - don't re-enter
- or TsrMode,InInt08 ;tell people we are here
-
- push ax ;need a few regs in this code
-
- ; ------------ COUNT DOWN TIME-OUT BETWEEN KEYS IN HOTKEY SEQUENCE
-
- test KeyMode,InHotMatch ;are we in a key match?
- jz TestHot08 ;NO - don't care then
- dec BetweenKeys ;count down timeout val
- jnz TestHot08 ;jump if no timeout yet
- mov HotIndex,0 ;start match from beginning
- and KeyMode,not InHotMatch ;just so we know it next time
-
- ; ------------ CHECK FOR POSSIBLE POPUP ACTIONS
-
- TestHot08: test KeyMode,HotKeyOn ;has hotkey been pressed?
- jz ExitInt08 ;NO - jump if no fun here
-
- test TsrMode,InInt28 OR InPopup
- jnz ExitInt08 ;jmp if not alr in business
-
- ; ------------ HOTKEY PRESSED, CHECK TO SEE IF IT IS SAFE TO POPUP
-
- cmp SafeWait,0 ;first time we find hotkey?
- ja TestSafe08 ;NO - wait has alr been set
- mov SafeWait,MaxWait ;# ticks to wait at most
-
- TestSafe08: call TestSafe ;now, CY clear if popup is safe
- jc NotSafe08 ;jump if popup is bad idea
-
- ; ------------ SEEMS SAFE TO POPUP AT THIS TIME, SO DO!
-
- xor al,al ;fast zero
- mov SafeWait,al ;don't count any more
- and KeyMode,not HotKeyOn ;clear hotkey status
- or TsrMode,InPopup ;tell'em we enter popup routine
- and TsrMode,not InInt08 ;OK to enter critical INT08
- call InitPopup ;do actual popup
- or TsrMode,InInt08 ;back in INT08 code here
- and TsrMode,not InPopup ;not in popup code any more
- mov SafeWait,al ;in case of hotkey during popup
- and KeyMode,not HotKeyOn ;clear hotkey status
-
- jmp short ExitInt08 ;finally done
-
- ; ------------ UNSAFE POPUP TIME, COUNT DOWN SafeWait
-
- NotSafe08: dec SafeWait ;count down waiter
- jnz ExitInt08 ;jump if still no timeout
-
- ; ------------ NO SAFE TIMES FOUND FOR QUITE SOME TIME, ERROR
-
- and KeyMode,not HotKeyOn ;might as well clear hotkey
- call ErrBeep ;do an error beep
-
- ; ------------ NORMAL INT08H EXIT, RESET InInt08
-
- ExitInt08: pop ax ;restore regs used
- and TsrMode,not InInt08 ;clear that flag
- iret ;straight back
- OurInt08 endp
-
- ;==============================================================================
- ; OurInt09 - TSR INT09H HANDLER TO WATCH FOR HOTKEY
- ;
- ; SAVE SCAN CODE
- ; CALL OldInt09
- ; CHECK FOR RECURSION INTO CRITICAL INT09 CODE
- ; SET InInt09 FLAG
- ; CHECK IF HOTKEY ALREADY SET
- ; DETERMINE HOTKEY TYPE (SHIFT STATE OR KEY SEQENCE)
- ; CHECK SHIFT STATE IF HotIsShift
- ; COMPARE FOR KEY MATCH IF (NOT HotIsShift)
- ; SET HotKeyOn IF HOTKEY PRESSED
- ; RESET InInt09 FLAG
- ; DO IRET
- ;==============================================================================
-
- ; ------------ NEAR JUMP DESTINATION FOR EARLY EXITS
-
- Exit09: pop bx ;restore regs
- pop ax
- iret ;flags restored from stack
-
- ; ------------ ACTUAL INT09 ENTRY POINT
-
- OurInt09 proc far
- assume ds:nothing, es:nothing, ss:nothing
-
- push ax ;save regs used
- push bx
-
- ; ------------ READ SCAN CODE, IN CASE SEQUENCE MATCHING SELECTED
-
- in al,KbData ;Al=key, preserved by BIOS
-
- ; ------------ CALL BIOS TO PERFORM IT'S DUTIES
-
- pushf ;simulate INT (CLI alr set)
- cli ;in case others forgot it
- call OldInt09 ;call BIOS/earlier TSRs
-
- ; ------------ ENSURE NO RECURSION INTO CRITICAL INT09 CODE
-
- sti ;we'll manage INTs
-
- test TsrMode,InInt09 ;alr in business?
- jnz Exit09 ;YES - skip test till clear
- or TsrMode,InInt09 ;tell them we arrived here
-
- ; ------------ DETERMINE HOT KEY TYPE SELECTED
-
- test KeyMode,HotKeyOn ;already hotkey there?
- jnz ExitInt09 ;YES - no double hotkeys here
-
- test KeyMode,HotIsShift ;shift state type hotkey?
- jz CompSeq09 ;NO - go compare sequence
-
- ; ------------ COMPARE CURRENT SHIFT STATUS AGAINST HOTKEY
-
- push ds ;save current ds
- mov ax,BiosData ;move DS to BIOS data seg
- mov ds,ax ;DS can now access keyb vars
- assume ds:BiosData ;tell MASM about our DS
- mov al,KbFlag ;get BIOS shift state bits
- pop ds ;restore
- assume ds:nothing ;last thing we know about him
-
- and al,HotKeyShift ;isolate relevant bits
- cmp al,HotKeyShift ;our shift state in effect?
- jne ExitInt09 ;NO - not that shift state
- or KeyMode,HotKeyOn ;YES - flag hotkey
- jmp short ExitInt09 ;now we can be proud to leave
-
- ; ------------ MATCH KEY IN SCAN CODE SEQUENCE
-
- CompSeq09: mov bl,HotIndex ;next scan code to match
- xor bh,bh ;must be word
- cmp al,HotKeySeq[bx] ;does key match?
- je HotMatch09 ;YES - jump if match
- mov HotIndex,bh ;search from start next time
- and KeyMode,not InHotMatch ;current no match
- jmp short ExitInt09 ;now end this
-
- ; ------------ KEY MACTHED NEXT SCAN CODE IN HotKeySeq
-
- HotMatch09: inc bl ;new code at next pass
- cmp bl,HotKeyLen ;did we match whole sequence?
- jae HotHit09 ;YES - jump if full sequence
- mov HotIndex,bl ;NO - save new count
- mov BetweenKeys,KeyTimeOut ;reset counter between keys
- or KeyMode,InHotMatch ;we are in a match now
- jmp short ExitInt09 ;time to end this
-
- ; ------------ KEY MATCHED ALL SCAN CODES IN HOTKEY SEQUENCE
-
- HotHit09: or KeyMode,HotKeyOn ;say hotkey was pressed
- mov HotIndex,bh ;match 1st code next time
- and KeyMode,not InHotMatch ;that's the end of a match
-
- ; ------------ EXIT FROM INT09H, RESET InInt09 FLAG
-
- ExitInt09: and TsrMode,not InInt09 ;tell'em we left this code
- pop bx ;restore regs
- pop ax
- iret ;flags restored from stack
- OurInt09 endp
-
- ;==============================================================================
- ; OurInt13 - SET InInt13 FLAG TO SAY THAT WE ARE IN AN INT13H
- ;==============================================================================
-
- OurInt13 proc far
- assume ds:nothing, es:nothing, ss:nothing
-
- pushf ;save flags we use
- or TsrMode,InInt13 ;remember we are in BIOS now
- popf ;restore flags
-
- pushf ;simulate INT13
- cli ;just in case others forgot
- call OldInt13 ;let BIOS handle it all
-
- pushf ;BIOS uses flag return
- and TsrMode, not InInt13 ;tell people we left INT13h
- popf
-
- ret 2 ;throw flags off stack
- OurInt13 endp
-
- ;==============================================================================
- ; OurInt16 - TSR INT16H HANDLER, INT28 CHAIN INTERFACE
- ;
- ; INPUT: AX = GetId
- ; OUTPUT: AX = MyId
- ; REGS: AX LOST, ALL OTHERS PRESERVED
- ; DESCRIPTION: DETERMINE IF TSR WITH THIS ID IS ALREADY IN MEMORY
- ;
- ; INPUT: AH = 00
- ; OUTPUT: AX = NEXT KEY FROM BUFFER
- ; REGS; AX LOST, ALL OTHERS PRESERVED
- ; DESCRIPTION: RETURN A KEY FROM KEYBOARD BUFFER, WAIT TILL KEY IS PRESSED
- ;
- ; INPUT: AH = 02
- ; OUTPUT: AX = KEY FROM BUFFER IN ANY
- ; ZF = NO KEYS IN BUFFER (AX PRESERVED)
- ; NZ = KEY IN BUFFER (RETURNED IN AX, KEY STILL IN BUFFER)
- ; DESCRIPTION: CHECK BUFFER FOR ANY PENDING KEYS, RETURN KEY IF ANY
- ;
- ; NOTE: ALL OTHER AX REQUEST CODES ARE PASSED ON TO BIOS INT16H HANDLER.
- ;
- ; NOTE: DURING INT28 POPUP (InPopup AND NOT InDosClr) FUNCTIONS AH=0 AND
- ; AH=1 WILL ISSUE INT28, UNLESS InDos HAS FROM VALUE AT POPUP OR
- ; CritErr HAS BEEN SET.
- ;==============================================================================
-
- OurInt16 proc far
- assume ds:nothing, es:nothing, ss:nothing
-
- sti ;we'll manage INTs
- pushf ;save callers flags
- cmp ax,GetId ;return ID request?
- jne NotId16 ;NO - jump if not
-
- ; ------------ TSR DIAGNOSTIC REQUEST, RETURN SPECIAL VALUE TO SAY WE ARE HERE
-
- mov ax,MyId ;ID val returned in AX
- popf ;restore flags
- iret ;return to caller
-
- ; ------------ PASS CONTROL TO BIOS, FLAGS ON STACK
-
- GoBios16: popf ;restore flags at INT time
- jmp OldInt16 ;continue in the woods
-
- ; ------------ REGULAR BIOS INT16 REQUEST, CHECK FOR ANY FANCY ACTIONS
-
- NotId16: test TsrMode,InPopup ;are we in a popup?
- jz GoBios16 ;NO - leave rest with BIOS
- test TsrMode,InDosClr ;InDos clear at popup?
- jnz GoBios16 ;YES - no need to signal INT28
-
- popf ;restore original flags
- push bx ;we need a few regs here
- push cx
- push si
- push ds
- pushf ;original flags back on stack
-
- ; ------------ GET REQUEST CODE TO BH ENHANCED BIT TO BL
-
- mov bh,ah ;BH=function request code
- and bh,not 10h ;zap enhanced kybd bit
- cmp bh,1 ;any function above 1?
- ja ExitBios16 ;YES - leave rest with BIOS
-
- mov bl,ah ;BL used for enhanced bit
- and bl,10h ;BL=value of enhanced bit
-
- ; ------------ GET InDos To CL, CritErr to CH, SETUP REGS
-
- assume ds:nothing
-
- lds si,InDosPtr ;DS:[SI]=InDos
- mov cl,byte ptr [si] ;CL=InDos value
- lds si,CritErrPtr ;ES:[SI]=CritErr
- mov ch,byte ptr [si] ;CH=CritErr value
-
- mov si,ax ;save AX call value
-
- mov ax,cs ;move DS here, now we got it
- mov ds,ax
- assume ds:Cseg ;everybody should know
-
- ; ------------ CHECK KEYBOARD BUFFER, ORIGINAL FLAGS ON STACK
-
- Wait16: mov ah,1 ;AH=1=test buffer status
- or ah,bl ;maintain enhanced bit value
-
- popf ;restore original flags
- pushf ;simulate INT
- cli ;in case others forgot
- call OldInt16 ;now, ZF set if no keys
- pushf ;save result flags
- jnz TestSkip16 ;jump if a key was found
-
- ; ------------ NO KEY FOUND, CALL INT28 IF DOS InDos ALLOWS
-
- cmp cx,0001h ;CritErr=0, InDos=1 ?
- jne NextKey16 ;NO - wait for next key
- or TsrMode,In28Call ;tell people we called this INT
- int 28h ;now take your chance
- and TsrMode,not In28Call ;end of that call
-
- ; ------------ TEST BUFFER AGAIN IF INT16.00, IRET IF INT16.01
-
- NextKey16: or bh,bh ;is this a wait for key?
- jz Wait16 ;YES - then go wait for it!
- mov ax,si ;restore original AX contents
- jmp short Exit16 ;NO - exit with status we got
-
- ; ------------ KEY IN BUFFER, IF CTRL-C WE MAY HAVE TO SKIP IT, FLAGS ON STACK
-
- TestSkip16: cmp al,3 ;is this Ctrl-C?
- jne TestExit16 ;NO - determine exit method
- test cx,not 0001h ;anything but InDos=1?
- jz TestExit16 ;NO - determine exit method
-
- ; ------------ SKIP CTRL-C IN KEYBOARD BUFFER
-
- mov ah,bl ;AH=0 + enhanced bit
- popf ;restore original INTs
- pushf ;save again
- pushf ;simulate INT
- cli ;simulate properly!
- call OldInt16 ;now, key should be gone
- jmp short Wait16 ;do as if nothing had happened
-
- ; ------------ KEY IN AX, IRET IF INT16.01, LEAVE WITH BIOS IF INT16.00
-
- TestExit16: or bh,bh ;is this a wait for key?
- jnz Exit16 ;NO - do fast return
- mov ax,si ;YES - restore AX code
-
- ; ------------ PASS CONTROL TO BIOS, FLAGS & REGS ON STACK
-
- assume ds:nothing
-
- ExitBios16: popf ;restore work flags
- pop ds ;restore regs
- pop si
- pop cx
- pop bx
- cli ;should look like an INT
- jmp OldInt16 ;leave rest with BIOS
-
- ; ------------ RETURN FROM INT16, FLAGS & REGS ON STACK
-
- assume ds:nothing
-
- Exit16: popf ;restore proper flags
- pop ds ;restore regs
- pop si
- pop cx
- pop bx
- ret 2 ;IRET, without flags restore
-
- OurInt16 endp
-
- ;==============================================================================
- ; OurInt21 - INT21 FILTER TO THROW DANGEROUS DOS CALLS ON CRITICAL STACK
- ;
- ; CHECK IF InPopup AND InDosClr
- ; CHECK FUNCTION USES CONSOLE STACK
- ; SET CritErr IN DOS IF CONSOLE STACK USED
- ; CALL OldInt21
- ; RESTORE CritErr IF CRITICAL STACK USED
- ;==============================================================================
-
- OurInt21 proc far
- assume ds:nothing, es:nothing
-
- pushf ;save calling flags
- sti
-
- test TsrMode,InPopup ;are we in a popup?
- jz GoDos21 ;NO - don't worry then
- test TsrMode,InDosClr ;console stack idle?
- jnz GoDos21 ;YES - nothing fancy then
-
- ; ------------ THIS IS 2ND CALL INTO DOS, SEE IF USING CONSOLE STACK
-
- cmp ah,0ch ;any function 00-0C?
- jbe UseCrit21 ;YES - use critical stack
- test TsrMode,NewDos ;NO - is this DOS 3.x?
- jnz GoDos21 ;YES - no other to worry about
- cmp ah,50h ;set PSP function?
- je UseCrit21 ;YES - use critical stack
- cmp ah,51h ;get PSP function?
- jne GoDos21 ;NO - leave it with DOS
-
- ; ------------ FORCE USE OF CRITICAL STACK FOR THIS CALL
-
- UseCrit21: assume ds:nothing ;nothing to say about DS
-
- push si ;save regs
- push ds
- lds si,CritErrPtr ;now, DS:[SI]=InDos
- mov byte ptr [si],-1 ;FF=use crit stack now
- pop ds ;restore regs
- pop si
-
- popf ;retsore flags setting
- pushf ;simulate INT
- cli ;in case others forgot
- call OldInt21 ;flags already on stack
-
- push si ;save regs
- push ds
- lds si,CritErrPtr ;now, DS:[SI]=InDos
- mov byte ptr [si],0 ;0=back to default stack
- pop ds ;restore regs
- pop si
-
- ret 2 ;IRET throw old flags
-
- ; ------------ PASS CONTROL TO DOS, FLAGS ON STACK
-
- GoDos21: popf ;restore original flags
- cli ;just in case someone forgot
- jmp OldInt21 ;let DOS handle the rest
- OurInt21 endp
-
- ;==============================================================================
- ; OurInt24 - SAFE DOS CRITICAL ERROR HANDLER
- ;
- ; IF DOS 3.X, FAIL THE SYSTEM CALL
- ; IF NOT DOS 3.X, IGNORE ERROR
- ;==============================================================================
-
- OurInt24 proc far
- assume ds:nothing, es:nothing, ss:nothing
- mov al,3 ;AL=3=fail system call
- test TsrMode,NewDos ;are we using DOS 3.x?
- jnz Exit24 ;YES - OK to use AL=3
- xor al,al ;NO - have to ignore err then
- Exit24: iret ;return to DOS
- OurInt24 endp
-
- ;==============================================================================
- ; OurInt28 - TSR INT28H HANDLER, ALLOWS POPUP DURING DOS IDLE CALLS
- ;
- ; CALL OldInt28
- ; CHECK FOR RECURSION INTO CRITICAL INT28 CODE (& OTHER INTs AS WELL)
- ; SET InInt28 FLAG
- ; CHECK FOR HOTKEY
- ; CHECK IF SAFE TO POPUP
- ; DO POPUP IF SAFE AT THIS TIME
- ; RESET InInt28 FLAG
- ; DO IRET
- ;==============================================================================
-
- ; ------------ NEAR JUMP DESTINATION FOR FAST IRET'S
-
- Exit28: iret ;IRET (!)
-
- ; ------------ ACTUAL INT28 ENTRY POINT
-
- OurInt28 proc far
- assume ds:nothing, es:nothing, ss:nothing
-
- pushf
- cli ;in case others forgot it
- call OldInt28 ;call TSRs loaded before this
-
- ; ------------ ENSURE NO RECURSION ON CRITICAL INT28 CODE
-
- sti ;we'll manage INT's after this
- test TsrMode,InInt08 OR InInt28 OR In28Call OR InPopup
- jnz Exit28 ;exit fast if already going
- or TsrMode,InInt28 ;tell'em we are here
-
- ; ------------ CHECK FOR POSSIBLE POPUP ACTIONS
-
- test KeyMode,HotKeyOn ;any hotkeys pressed?
- jz ExitInt28 ;NO - don't check any more then
-
- ; ------------ HOTKEY WAS PRESSED, ENSURE IT'S SAFE TO DO POPUP
-
- call TestSafe ;now, CY clear if popup is OK
- jc ExitInt28 ;jump if not to popup
-
- ; ------------ SEEMS OK TO DO POPUP, SO DO!
-
- and KeyMode,not HotKeyOn ;clear hotkey status
- or TsrMode,InPopup ;tell'em we enter popup routine
- and TsrMode,not InInt28 ;OK to enter critical INT28
- call InitPopup ;then do popup
- or TsrMode,InInt28 ;back in INT28 code here
- and TsrMode,not InPopup ;not in popup code any more
- and KeyMode,not HotKeyOn ;clear hotkeys during popup
-
- ; ------------ NORMAL INT28H EXIT, RESET InInt28 FLAG
-
- ExitInt28: and TsrMode,not InInt28 ;tell'em we left this code
- iret ;we have nothing more to say
- OurInt28 endp
-
- ;==============================================================================
- ; NopInt - DUMMY IRET INSTRUCTION USED BY EMPTY INT HANDLERS
- ;==============================================================================
-
- NopInt: iret ;immediate return
-
- ;==============================================================================
- ; InitPopup - PREPARES SYSTEM FOR POPUP, THEN CALLS Popup, THEN RESTORES
- ;
- ; ESTABLISH INTERNAL WORK STACK
- ; SAVE CPU REGS
- ; UPDATE InDosClr FLAG WITH CURRENT VALUE OF InDos
- ; SAVE PROCESS RELATED SYSTEM INFO
- ; SAVE USER INTERRUPT VECTORS
- ; INSERT SAFE USER INTERRUPT VECTORS
- ; CALL POPUP ROUTINE
- ; RESTORE USER INTERRUPT VECTORS
- ; RESTORE PROCESS AND SYSTEM INFO
- ; CLEAR InDosClr FLAG TO PREVENT UNSAFE INT28 CALLs
- ; RESTORE CPU REGS
- ;==============================================================================
-
- InitPopup proc near
- assume ds:nothing, es:nothing, ss:nothing
-
- ; ------------ SWITCH TO PSP INTERNAL STACK
-
- mov OldSS,ss ;save current stack frame
- mov OldSP,sp
-
- cli ;always CLI for the old chips
- mov ss,OurSS ;move SS here
- mov sp,OurSP ;move SP into position
- sti ;OK guys
-
- ; ------------ SAVE ALL REGS
-
- push ax
- push bx
- push cx
- push dx
- push bp
- push si
- push di
- push ds
- push es
-
- mov ax,cs
- mov ds,ax ;mov DS here
- assume ds:Cseg ;tell MASM that
-
- ; ------------ TAG VALUE OF InDos FLAG AT TIME OF POPUP
-
- or TsrMode,InDosClr ;assume InDos=0
- les si,InDosPtr ;now, ES:[SI]=InDos
- cmp byte ptr es:[si],1 ;InDos set? (>2 impossible)
- jb InDosSaved ;NO - jump if all clear DOS
- and TsrMode,not InDosClr ;clear flag for popup InDos
- InDosSaved:
-
- ; ------------ SAVE DOS 3.X EXTENDED ERROR INFO
-
- test TsrMode,NewDos ;really DOS 3.x?
- jz Dos3Saved ;NO - jump if not 3.x
-
- mov ah,59h ;to get err info from DOS
- xor bx,bx ;BX must be zero
- push ds ;save DS (killed by DOS)
- int 21h ;ext err info in AX,BX,CX
- pop ds ;restore
- mov OldExtErr[0],ax ;save
- mov OldExtErr[2],bx
- mov OldExtErr[4],cx
-
- Dos3Saved:
-
- ; ------------ SAVE CURRENT BREAK STATE, RELAX BREAK CHECKING
-
- mov ax,3302h ;to swap DL with BREAK value
- xor dl,dl ;DL=0=relax checking
- int 21h ;current level in DL
- mov OldBreak,dl ;save current level
-
- ; ------------ SAVE CURRENT USER INT VECTORS
-
- mov ax,351bh ;BIOS ctrl-break int
- int 21h ;ES:BX=vector
- mov OldInt1BOff,bx ;save it
- mov OldInt1BSeg,es
-
- mov ax,351ch ;BIOS timer tick
- int 21h ;ES:BX=vector
- mov OldInt1COff,bx ;save it
- mov OldInt1CSeg,es
-
- mov ax,3523h ;DOS ctrl-C
- int 21h ;ES:BX=vector
- mov OldInt23Off,bx ;save it
- mov OldInt23Seg,es
-
- mov ax,3524h ;DOS crit err handler
- int 21h ;ES:BX=vector
- mov OldInt24Off,bx ;save it
- mov OldInt24Seg,es
-
- ; ------------ INSERT DUMMY IRET INTO DANGEROUS VECTORS
-
- mov dx,offset NopInt ;now, DS:DX=dunny iret
- mov ax,251bh ;BIOS ctrlk-break handler
- int 21h ;set to IRET
- mov ax,251ch ;BIOS timer tick
- int 21h ;set to IRET
- mov ax,2523h ;DOS ctrl-C handler
- int 21h ;set to IRET
-
- ; ------------ ESTABLISH SAFE CRITICAL ERROR HANDLER
-
- mov dx,offset OurInt24 ;now, DS:DX=safe crit err
- mov ax,2524h ;to set crit err handler
- int 21h
-
- ; ------------ SAVE CURRENT DTA AREA, SET OUR DEFAULT DTA
-
- mov ah,2fh ;to obtain current DTA from DOS
- int 21h ;DTA addr now in ES:BX
- mov OldDTAOff,bx ;save it
- mov OldDTASeg,es
-
- push ds ;save DS for a while
- lds dx,OurDTA ;DS:DX=our DTA addr
- mov ah,1ah ;to set DTA via DOS
- int 21h ;set that addr
- pop ds ;restore DS
-
- ; ------------ SAVE CURRENT PSP, ESTABLISH OURS INSTEAD
-
- mov ax,5100h ;to get PSP from DOS
- int 21h ;current PSP now in BX
- mov OldPSP,bx ;save it
- mov bx,OurPSP ;het our PSP instead
- mov ax,5000h ;to set our PSP
- int 21h
-
- ; ------------ CALL USER POPUP ROUTINE
-
- call Popup ;finally!
-
- ; ------------ RESTORE TO SAVED CURRENT PROCESS
-
- mov bx,OldPSP ;new current process in BX
- mov ax,5000h ;to set PSP via DOS
- int 21h ;restore original PSP
-
- ; ------------ RESTORE SAVED DTA
-
- push ds ;save DS for a while
- lds dx,OldDTA ;DS:DX=our DTA addr
- mov ah,1ah ;to set DTA via DOS
- int 21h ;set that addr
- pop ds ;restore DS
-
- ; ------------ RESTORE SAVED INTERRUPT VECTORS
-
- push ds ;save for a while
- assume ds:nothing ;be careful about MASM
-
- lds dx,OldInt1B ;BIOS ctrl-break handler
- mov ax,251bh
- int 21h
-
- lds dx,OldInt1C ;BIOS timer tick
- mov ax,251ch
- int 21h
-
- lds dx,OldInt23 ;DOS ctrl-C
- mov ax,2523h
- int 21h
-
- lds dx,OldInt24 ;DOS crit err handler
- mov ax,2524h
- int 21h
-
- pop ds ;restore data seg DS
- assume ds:Cseg
-
- ; ------------ RESTORE SAVED BREAK CHECKING LEVEL
-
- mov ax,3301h ;to set break check level
- mov dl,OldBreak ;get saved break state
- int 21h
-
- ; ------------ RESTORE DOS 3.X SPECIFIC SYSTEM INFO
-
- test TsrMode,NewDos ;using DOS 3.x
- jz Dos3Restored ;NO - jump if old DOS 2
- mov dx,offset OldExtErr ;DS:DX=3 words of ext err
- mov ax,5d0ah ;to set ext err info
- int 21h
- Dos3Restored:
-
- ; ------------ RESET InDosSet FLAG VALUE TO PREVENT UNSAFE INT28
-
- or TsrMode,InDosClr ;now we only care that InDos=0
-
- ; ------------ RESTORE USER REGS
-
- pop es
- pop ds
- pop di
- pop si
- pop bp
- pop dx
- pop cx
- pop bx
- pop ax
- assume ds:nothing
-
- ; ------------ RETURN TO USER STACK
-
- cli ;always CLI for the old chips
- mov ss,OldSS ;restore SS
- mov sp,OldSP ;restore SP
- sti ;OK guys
-
- ret
- InitPopup endp
-
- ;==============================================================================
- ; DATA FOR POPUP ROUTINE
- ;==============================================================================
-
- DosReadMsg db 13,10,'Reading DOS CON (press <Enter> to terminate)',13,10
- DosReadLen equ $-DosReadMsg
-
- BiosReadMsg db 'Reading BIOS keyboard (press any key... )',8,8
- BiosReadLen equ $-BiosReadMsg
-
- DoneMsg db ' key pressed, exit from TSR DEMO)',13,10
- DoneLen equ $-DoneMsg
-
- Scratch db 80 dup (?)
-
- ;==============================================================================
- ; Popup - POPUP USER ROUTINE
- ;
- ; ALL REGISTERS EXCEPT SS:SP AND DS MAY BE CHANGED.
- ; DS IS PRESET TO THE TSR DATA SEGMENT.
- ;
- ; NOTE: UPON ENTRY TO THIS ROUTINE ALL DOS FUNCTIONS MAY BE CALLED.
- ; IF POPUP WAS DONE ON INT28, WITH CritErr==1, ALL DOS FUNCTIONS
- ; THAT WOULD NORMALLY USE THE CONSOLE STACK, WILL GO TO THE CRITICAL
- ; STACK, HENCE PREVENTING FURTHER POPUP DURING THE DOS CALL.
- ; (HOWEVER, MOST TSRs WOULD NOT POPUP ANYWAY, SINCE InDos==2).
- ;
- ; ADDRESSES OF THE InDos AND CritErr ARE STORED IN THE DOUBLE WORDS
- ; InDosPtr AND CritErrPtr.
- ;
- ; AT ENTRY CritErr FLAG IS 0 (ZERO), InDos NO GREATER THAN 1 (ONE).
- ;==============================================================================
-
- Popup proc near
- assume ds:Cseg, es:nothing, ss:nothing
-
- mov ah,40h ;DOS write handle
- mov bx,1 ;standard output handle
- mov dx,offset DosReadMsg ;DS:DX=str to write
- mov cx,DosReadLen ;CX=# chars to write
- int 21h ;output that string
-
- mov ah,3fh ;DOS read handle
- xor bx,bx ;standard input handle
- mov dx,offset Scratch ;scratch buf for key
- mov cx,80 ;read till CR hit
- int 21h
-
- mov ah,40h ;read from BIOS msg
- mov bx,1
- mov dx,offset BiosReadMsg
- mov cx,BiosReadLen
- int 21h
-
- xor ah,ah ;to let BIOS wait for key
- int 16h ;now, key was pressed
-
- mov ah,40h ;write confirm msg
- mov bx,1
- mov dx,offset DoneMsg
- mov cx,DoneLen
- int 21h
-
- ret
- Popup endp
-
- ;==============================================================================
- ; TSR IRON CURTAIN - HE WHO CROSSES THIS CURTAIN WILL BE GONE AFTER TSR!
- ;==============================================================================
-
- TsrCurtain: ;TSR memory break
-
- ;==============================================================================
- ; NON-RESIDENT MESSAGES FOR INIT
- ;==============================================================================
-
- BannerMsg label byte
- db 13,10
- db '<<<<<< TSR DEMO >>>>>>',13,10
- db ' Thomas Brandenborg',13,10
- db ' Version 2.00',13,10,10
- db '$'
-
- FirstMsg label byte
- db 'Pop up routine installed resident.',13,10
- db '$'
-
- SecondMsg label byte
- db 'TSR DEMO already loaded.',13,10
- db '$'
-
- HotKeyMsg label byte
- db 'Hit <Left Shift> twice to pop up!',13,10,10
- db '$'
-
- Dos1Msg label byte
- db 'OOPS!',7,13,10
- db 'Must use DOS release 2.00 or later!',13,10,10
- db '$'
-
- BadDosMsg label byte
- db 'OOPS!',7,13,10
- db 'Did not recognize DOS version!',13,10,10
- db '$'
-
- ; ------------ DOS ERROR LEVEL EXIT CODES
-
- xOk equ 0 ;normal, OK exit
- xSecond equ 1 ;TSR already loaded
- xBadDos equ 2 ;CritErr flag not found
-
- ;==============================================================================
- ; Init - INITIALIZE TSR APPLICATION, ENTERED UPON DOS LOAD
- ;
- ; DISPLAY BANNER, INITIALIZE SYSTEM DATA, CHECK IF ALREADY LOADED,
- ; HOOK INTO INTERRUPT CHAIN, TERMINATE, BUT STAY RESIDENT.
- ;==============================================================================
-
- Init proc near
- assume ds:Cseg, es:nothing, ss:nothing
-
- mov dx,offset BannerMsg
- mov ah,9
- int 21h ;display programme banner
-
- ; ------------ USE INT16H DIAGNOSTIC TO SEE IF TSR ALREADY INSTALLED
-
- mov ax,GetId ;INT16h diagnostic request
- int 16h ;now, AX=MyId if installed
- cmp ax,MyId ;TSR already installed?
- jne CheckDos ;NO - jump if not installed
-
- ; ------------ TSR ALREADY INSTALLED, DISPLAY MSG, EXIT
-
- mov dx,offset SecondMsg
- mov ah,9
- int 21h ;display alr installed msg
- mov dx,offset HotKeyMsg
- mov ah,9
- int 21h ;be kind & disp hot key
- mov ax,4c00h + xSecond ;error level in AL
- int 21h ;abot now
-
- ; ------------ IDIOT IS RUNNING DOS 1, LEAVE THE OLD FASHION WAY!
-
- Dos1: mov dx,offset Dos1Msg
- mov ah,9
- int 21h ;display msg about DOS 1
- int 20h ;no err level for DOS 1
-
- ; ------------ ENSURE DOS VERSION IS NEWER THAN 2.00
-
- CheckDos: or TsrMode,NewDos ;assume suing DOS 3.x
- mov ah,30h ;to get DOS version number
- int 21h ;version is AL.AH
- cmp al,2 ;release 2 or newer?
- jb Dos1 ;NO - jump if DOS 1 in use
- ja DosFlags ;jump if DOS 3.x
- and TsrMode,not NewDos ;now, say we use DOS 2.x
-
- ; ------------ INITIALIZE PTRS TO DOS FLAGS - 1ST InDos
-
- DosFlags: mov ax,3400h ;to get InDos ptr
- int 21h ;ES:BX=seg:off of InDos
- mov InDosOff,bx ;save ptr
- mov InDosSeg,es
-
- ; ------------ WE NEED CritErr TO USE PSP FUNCTIONS IN DOS 2.X (CHIPs WAY)
-
- xor dl,dl ;DL=0=this is 1st scan
- mov CritErrSeg,es ;DOS seg still in ES
- CritScan: mov di,bx ;start search at InDos
- mov cx,2000h ;search max 1000h words
- mov ax,3e80h ;opcode CMP BYTE PTR [CritErr]
- cld ;better serach forward
-
- CritScan2: repne scasw ;search till found or end
- jne NoCritFound ;jump if CMP not found
- ;ES:[DI-2] at:
- ; CMP BYTE PTR [CritErr]
- ; JNZ ...
- ; MOV SP,stack addr
- cmp byte ptr es:[di][5],0bch ;really CMP SP there?
- jne CritScan2 ;NO - scan again if not
- mov ax,word ptr es:[di] ;now, AX=CritErr offset
- mov CritErrOff,ax ;save it
- jmp short InitData ;OK to end this now
-
- NoCritFound: or dl,dl ;was this1 st scan?
- jnz BadDos ;NO - CritErr not founbd at all
- inc dl ;DL=1=this is 2nd scan
- inc bx ;try scan at odd/even offset
- jmp CritScan ;scan again
-
- ; ------------ COULD NOT LOCATE DOS CritErr FLAG - THAT'S AN ERROR
-
- BadDos: mov dx,offset BadDosMsg
- mov ah,9
- int 21h ;display msg about that
- mov ax,4c00h + xBadDos ;err level in AL
- int 21h ;OK to use 4C (DOS >= 2)
-
- ; ------------ INITIALIZE SYSTEM DATA VARIABLES
-
- InitData: ;store position for stack
- mov OurSP,TsrCurtain - ComEntry + 100h + StackSize
- mov OurSS,cs ;stack seg is code seg
-
- mov ax,5100h ;to get current PSP from DOS
- int 21h ;PSP now in BX
- mov OurPSP,bx ;save our PSP
-
- mov ah,2fh ;to get current DTA from DOS
- int 21h ;now, ES:BX=current DTA
- mov OurDTAOff,bx ;save it
- mov OurDTASeg,es
-
- and KeyMode,not HotIsShift ;hotkey is not shift state
- or TsrMode,InDosClr ;will prevent unsafe INT28s
-
- ; ------------ SAVE VECTORS FOR OUR MONITOR INTERRUPTS
-
- mov ax,3508h ;BIOS timer0 tick handler
- int 21h ;ES:BX=vector
- mov OldInt08Off,bx
- mov OldInt08Seg,es
-
- mov ax,3509h ;BIOS kb HW handler
- int 21h ;ES:BX=vector
- mov OldInt09Off,bx
- mov OldInt09Seg,es
-
- mov ax,3513h ;BIOS disk I/O service
- int 21h ;ES:BX=vector
- mov OldInt13Off,bx
- mov OldInt13Seg,es
-
- mov ax,3516h ;BIOS kb read
- int 21h ;ES:BX=vector
- mov OldInt16Off,bx
- mov OldInt16Seg,es
-
- mov ax,3521h ;DOS functions dispatcher
- int 21h ;ES:BX=vector
- mov OldInt21Off,bx
- mov OldInt21Seg,es
-
- mov ax,3528h ;DOS idle hook
- int 21h ;ES:BX=vector
- mov OldInt28Off,bx
- mov OldInt28Seg,es
-
- ; ------------ ESTABLISH IRET INT23 TO PREVENT BREAK DURING VECTOR FIX
-
- mov dx,offset NopInt ;DS:DX=dummy vector to set
- mov ax,2523h ;to set ^C handler through DOS
- int 21h ;now, no break will occur
-
- ; ------------ SAVE VECTORS FOR OUR MONITOR INTERRUPTS
-
- mov ax,2508h ;to set our INT08h handler
- mov dx,offset OurInt08 ;DS:DX=new vector
- int 21h ;let DOS set vector
-
- mov ax,2509h ;to set our INT09h handler
- mov dx,offset OurInt09 ;DS:DX=new vector
- int 21h ;let DOS set vector
-
- mov ax,2513h ;to set our INT13h handler
- mov dx,offset OurInt13 ;DS:DX=new vector
- int 21h ;let DOS set vector
-
- mov ax,2516h ;to set our INT16h handler
- mov dx,offset OurInt16 ;DS:DX=new vector
- int 21h ;let DOS set vector
-
- mov ax,2521h ;to set our INT21h handler
- mov dx,offset OurInt21 ;DS:DX=new vector
- int 21h ;let DOS set vector
-
- mov ax,2528h ;to set our INT28h handler
- mov dx,offset OurInt28 ;DS:DX=new vector
- int 21h ;let DOS set vector
-
- ; ------------ DISLAY MSG ABOUT HOW WELL THIS IS ALL RUNNING
-
- mov dx,offset FirstMsg
- mov ah,9
- int 21h ; display confirm msg
- mov dx,offset HotKeyMsg
- mov ah,9
- int 21h ;disp hot key
-
- ; ------------ EXIT, SAY GOOD BYE TO FRIENDS BEHIND CURTAIN!
-
- mov es,EnvSeg ;ES=our environment copy
- mov ah,49h ;to let DOS free block
- int 21h ;environment copy freed
-
- mov dx,(TsrCurtain-ComEntry+100h+StackSize+15) SHR 4
- mov ax,3100h + xOk ;TSR, AL=err level
- int 21h
- Init endp
-
- ;==============================================================================
-
- Cseg ends
- end ComEntry
-
-