home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / msdos / asmutl / tsrdemo2.arc / TSRDEMO2.ASM next >
Assembly Source File  |  1987-03-10  |  46KB  |  1,503 lines

  1. page    60,132
  2. ;==============================================================================
  3. ; TSRDEMO2.ASM
  4. ; Thomas Brandenborg
  5. ; 87.02.11
  6. ;
  7. ; Sample demonstration of a safe approach for writing TSR programmes.
  8. ;
  9. ;------------------------------------------------------------------------------
  10. ; Copyright 1987 by Thomas Brandenborg. All Rights Reserved
  11. ; Written for uploading to Compuserve Forums by
  12. ;    Thomas Brandenborg
  13. ;    Lundbyesgade 11
  14. ;    DK-8000 Aarhus C
  15. ;    DENMARK
  16. ;
  17. ; This code is intended as a reference to users on Compuserve Forums
  18. ; on how to write Terminate-And-Stay resident programmes for Personal
  19. ; Cumputers running under DOS versions 2.00 and newer.
  20. ;
  21. ; The code is not part of any proprietary product, but is rather a
  22. ; demonstration of such techniques that may be used to write safe TSR
  23. ; programmes.
  24. ;
  25. ; All or part of it may, however, be used in other software products
  26. ; or otherwise distributed assuming the copyright holders Name & Address
  27. ; as listed above are included clearly and visibly in the documentation
  28. ; for such product.
  29. ;
  30. ; The copyright holder offers no warranty with this code whatsoever,
  31. ; including its fitness for any particular purpose. Neither shall he
  32. ; be liable for damages of any kind that may arise from its use.
  33. ;
  34. ;
  35. ; IF YOU THINK THIS CODE IS USEFUL:
  36. ;
  37. ; If you think this code has had some value to you, and in particular
  38. ; if you consider using all or parts of it in your own product, you
  39. ; may want to consider a smaller or larger donation to the author
  40. ; (Name & Address above) who spend his late night hours putting
  41. ; it together.
  42. ;
  43. ; As to the size of a such donation this is entirely up to your own
  44. ; judgement. It is suggested that you simply consider the value this
  45. ; code has had to you, the time you saved not having to write it your
  46. ; self... that should help you determine the right amount.
  47. ;
  48. ; Please notice that such donations are an entirely voluntary contribution.
  49. ; This holds true whatever your purpose is for using this code, and whatever
  50. ; the type of product and distribution you work with. The author has nothing
  51. ; against commercial software distribution and does not have any reason
  52. ; to restrict developers of commercial products in their use of this code.
  53. ;------------------------------------------------------------------------------
  54. ; COMPILING:    masm tsrdemo2;
  55. ; LINKING:    link tsrdemo2;
  56. ;         exe2bin tsrdemo2 tsrdemo2.com
  57. ;------------------------------------------------------------------------------
  58. ; Revisions
  59. ; Brandenborg 87.02.14    Added copyright notice & checked comments
  60. ; Brandenborg 87.02.17    Added full AX value in Set Ext Err call
  61. ; Brandenborg 87.02.25    Went through to optimize things
  62. ; Brandenborg 87.02.28    Added auto INT28 invocation in INT16 handler
  63. ; Brandenborg 87.03.01    Added INT21 filter for recursion onto console stack
  64. ; Brandenborg 87.03.02    Final cleanup of comments etc.
  65. ; Brandenborg 87.03.10    INT28 handler chains AFTER popup actions
  66. ;            InitPopup: INT21.33 (AL=2) not allowed for DOS 2.x
  67. ;            InitPopup: Set ext err info uses all regs
  68. ;==============================================================================
  69.  
  70. ;==============================================================================
  71. ; DEFINE BIOS DATA SEGMENT OFFSETS
  72. ;==============================================================================
  73.  
  74. BiosData    segment at 40h
  75.         org    17h
  76. KbFlag        label    byte        ;current shift status bits
  77.         org    18h
  78. KbFlag1        label    byte        ;current key status of toggle keys
  79. BiosData    ends
  80.  
  81. ;==============================================================================
  82. ; DEFINE OFFSETS WITHIN BIOS EXTRA DATA SEGMENT
  83. ;==============================================================================
  84.  
  85. BiosXX        segment at 50h
  86.         org    0
  87. StatusByte    label    byte        ;PrtSc status
  88. BiosXX        ends
  89.  
  90. ErrPrtSc    equ    -1        ;err during last PrtSc
  91. InPrtSc        equ    1        ;PrtSc in progress
  92.  
  93. ;==============================================================================
  94. ; DEFINE OFFSETS WITHIN OUR PSP
  95. ;==============================================================================
  96.  
  97. Cseg        segment byte public
  98.         org    2
  99. TopSeg        label    word        ;last seg in alloc block
  100.         org    2ch
  101. EnvSeg        label    word        ;seg of our environment copy
  102. Cseg        ends
  103.  
  104. ;==============================================================================
  105. ; DOS COM-FILE ENTRY POINT
  106. ;==============================================================================
  107.  
  108. Cseg        segment public byte
  109.         assume    cs:Cseg, ds:nothing, es:nothing, ss:nothing
  110.         org    100h
  111. ComEntry:    jmp    Init        ;JMP to init at bottom of seg
  112.  
  113. ;==============================================================================
  114. ; IDENTIFICATION CODES FOR THIS TSR (MUST BE UNIQUE FOR EACH CO-EXISTING TSR)
  115. ; HIGH BYTE OF GetId MUST NOT MATCH ANY AH REQUEST CODES FOR INT16H.
  116. ;==============================================================================
  117.  
  118. GetId        equ    'tc'            ;INT16h AX val to get MyId
  119. MyId        equ    'TC'            ;ID of this TSR
  120.  
  121. ;==============================================================================
  122. ; FLAGS AND PTRS FOR RESIDENT HANDLING
  123. ;==============================================================================
  124.  
  125. TsrMode        db    0            ;bits for various modes
  126. InInt08        equ    1 SHL 0            ;timer0 tick handler
  127. InInt09        equ    1 SHL 1            ;keyboard handler
  128. InInt13        equ    1 SHL 2            ;BIOS disk I/O
  129. InInt28        equ    1 SHL 3            ;INT28 handler
  130. In28Call    equ    1 SHL 4            ;we have issued INT28
  131. InPopup        equ    1 SHL 5            ;popup routine activated
  132. NewDos        equ    1 SHL 6            ;DOS 2.x in use
  133. InDosClr    equ    1 SHL 7            ;InDos=0 at popup time
  134.  
  135. KeyMode        db    0            ;bits for hotkey status
  136. HotIsShift    equ    1 SHL 0            ;hotkey is shift state
  137. InHotMatch    equ    1 SHL 1            ;so far keys match hotkey seq
  138. HotKeyOn    equ    1 SHL 2            ;full hotkey pressed
  139.  
  140. InDosPtr    label    dword            ;seg:off of InDos flag
  141. InDosOff    dw    0
  142. InDosSeg    dw    0
  143.  
  144. CritErrPtr    label    dword            ;seg:off of CritErr flag
  145. CritErrOff    dw    0
  146. CritErrSeg    dw    0
  147.  
  148. ;==============================================================================
  149. ; DATA FOR INT09H HANDLER TO CHECK FOR HOTKEY COMBINATION
  150. ;==============================================================================
  151.  
  152. ; ------------    EQU'S FOR BIT SHIFTS WITHIN KEYBOARD FLAGS
  153.  
  154. InsState    equ    80h
  155. CapsState    equ    40h
  156. NumState    equ    20h
  157. ScrollState    equ    10h
  158. AltShift    equ    08h
  159. CtlShift    equ    04h
  160. LeftShift    equ    02h
  161. RightShift    equ    01h
  162.  
  163. InsShift    equ    80h
  164. CapsShift    equ    40h
  165. NumShift    equ    20h
  166. ScrollShift    equ    10h
  167. HoldState    equ    08h
  168.  
  169. ; ------------    SCAN CODES FOR VARIOUS SHIFT KEYS
  170.  
  171. LeftDown    equ    42            ;scan code of left shift key
  172. LeftUp        equ    LeftDown OR 80h
  173. RightDown    equ    54            ;scan code of right shift key
  174. RightUp        equ    RightDown OR 80h
  175. AltDown        equ    56            ;scan code of alt key
  176. AltUp        equ    AltDown OR 80h
  177. CtlDown        equ    29            ;scan code of ctrl key
  178. CtlUp        equ    CtlDown OR 80h
  179.  
  180. ; ------------    MISC KEYBOARD DATA
  181.  
  182. KbData        equ    60h            ;keyboard data input
  183.  
  184. ;==============================================================================
  185. ; TO USE A SHIFT KEY COMBINATION AS HOT KEY:
  186. ;  -    SET THE FLAG HotIsShift IN KeyMode
  187. ;  -    DEFINE THE SHIFT STATUS BITS IN THE VARIABLE HotKeyShift
  188. ;
  189. ; TO USE A SERIES OF SCAN CODES AS HOT KEY:
  190. ;    CLEAR THE FLAG HotIsShift IN KeyMode
  191. ;  -    INSERT THE MAKE AND BREAK SCAN CODES IN THE HotKeySeq STRING
  192. ;    NOTE:    WITH THIS DEMO IMPLEMENTATION YOU SHOULD NOT USE A HOT KEY
  193. ;        SEQUENCE WHICH PRODUCES A KEY IN THE BIOS KEYBOARD QUEUE,
  194. ;        SINCE THE KEY IS NOT REMOVED BEFORE CALLING THE POPUP ROUTINE.
  195. ;
  196. ; NOTE:    HOTKEY TYPE AND CONTENTS OF HOTKEY VARIABLES MAY BE CHANGED AT RUN TIME
  197. ;==============================================================================
  198.  
  199. HotKeyShift    db    LeftShift OR RightShift    ;shift state IF HotIsShift=FF
  200.  
  201. HotKeySeq    db    LeftDown,LeftUp,LeftDown,LeftUp
  202. HotKeyLen    equ    $-HotKeySeq
  203. HotIndex    db    0            ;# key in seq to compare next
  204. BetweenKeys    db    0            ;timeout count between keys
  205. KeyTimeOut    equ    10            ;more ticks means not a hotkey
  206.  
  207. ;==============================================================================
  208. ; DATA FOR INT08H HANDLER TO CHECK FOR POPUP
  209. ;==============================================================================
  210.  
  211. SafeWait    db    0            ;count-down for safe popup
  212. MaxWait        equ    8            ;wait no more 8/18 sec
  213.  
  214. ;==============================================================================
  215. ; PROCESS & SYSTEM DATA
  216. ;==============================================================================
  217.  
  218. OurSS        dw    0            ;stack for popup routine
  219. OurSP        dw    0
  220. StackSize    equ    512            ;bytes to reserve for stack
  221.  
  222. OldSS        dw    0            ;old stack seg
  223. OldSP        dw    0            ;old stack off
  224.  
  225. OurPSP        dw    0            ;our PSP seg
  226. OldPSP        dw    0            ;old PSP seg
  227.  
  228. OldDTA        label    dword            ;seg:off of old DTA area
  229. OldDTAOff    dw    0
  230. OldDTASeg    dw    0
  231.  
  232. OurDTA        label    dword            ;seg:off of our DTA
  233. OurDTAOff    dw    0
  234. OurDTASeg    dw    0
  235.  
  236. OldBreak    db    0            ;old ctrl-break state
  237. OldExtErr    dw    8 dup (0)        ;all regs of ext err info
  238.  
  239. ;==============================================================================
  240. ; LOCATIONS FOR SAVED INTERRUPT VECTORS
  241. ;==============================================================================
  242.  
  243. OldInt08    label    dword            ;Timer0 loaded before this
  244. OldInt08Off    dw    0
  245. OldInt08Seg    dw    0
  246.  
  247. OldInt09    label    dword            ;Kb handler loadde before this
  248. OldInt09Off    dw    0
  249. OldInt09Seg    dw    0
  250.  
  251. OldInt13    label    dword            ;BIOS diskette I/O
  252. OldInt13Off    dw    0
  253. OldInt13Seg    dw    0
  254.  
  255. OldInt16    label    dword            ;BIOS kb Q-handler
  256. OldInt16Off    dw    0
  257. OldInt16Seg    dw    0
  258.  
  259. OldInt1B    label    dword            ;^break of process we steal
  260. OldInt1BOff    dw    0
  261. OldInt1BSeg    dw    0
  262.  
  263. OldInt1C    label    dword            ;timer tick of process we steal
  264. OldInt1COff    dw    0
  265. OldInt1CSeg    dw    0
  266.  
  267. OldInt21    label    dword            ;DOS function dispatcher
  268. OldInt21Off    dw    0
  269. OldInt21Seg    dw    0
  270.  
  271. OldInt23    label    dword            ;^C of process we steal
  272. OldInt23Off    dw    0
  273. OldInt23Seg    dw    0
  274.  
  275. OldInt24    label    dword            ;crit err of process we steal
  276. OldInt24Off    dw    0
  277. OldInt24Seg    dw    0
  278.  
  279. OldInt28    label    dword            ;DOS idles loaded before this
  280. OldInt28Off    dw    0
  281. OldInt28Seg    dw    0
  282.  
  283. ;==============================================================================
  284. ; SPEAKER/TONE GENERATION DATA
  285. ;==============================================================================
  286.  
  287. PB0port        equ    61h            ;port for speaker bit
  288. ErrLen1        equ    10            ;# outer err beep cycles
  289. ErrLen2        equ    80            ;# inner err beep cycles
  290. ErrLow        equ    100            ;low tone wait in err beep
  291. ErrHi        equ    40            ;hi tone wait in err beep
  292.  
  293. ;==============================================================================
  294. ; ErrBeep - PRODUCE ERROR-INDICATING SOUND ON SPEAKER
  295. ;==============================================================================
  296.  
  297. ErrBeep        proc    near
  298.         assume    ds:nothing, es:nothing, ss:nothing
  299.  
  300.         push    ax            ;save regs used
  301.         push    bx
  302.         push    cx
  303.         push    dx
  304.  
  305.         mov    cx,ErrLen1        ;# mix-cycles for beep
  306.  
  307. ErrBeep1:    mov    dx,ErrLow        ;wait time for half-cycle
  308.         mov    bx,ErrLen2        ;len of one tone
  309.         call    DoTone            ;output low err tone
  310.         mov    dx,ErrHi        ;wait time for half-cycle
  311.         mov    bx,ErrLen2        ;len of one tone
  312.         call    DoTone            ;output low err tone
  313.  
  314.         loop    ErrBeep1        ;loop for some time
  315.  
  316.         pop    dx
  317.         pop    cx            ;restore regs
  318.         pop    bx
  319.         pop    ax
  320.         ret
  321. ErrBeep        endp
  322.  
  323. ;==============================================================================
  324. ; DoTone - OUTPUT ONE TONE ON THE SPEAKER
  325. ;
  326. ; INPUT:    DX:    LOOP WAIT TIME FOR HALF CYCLE IN TONE
  327. ;        BX:    NUMBER OF CYCLES FOR TONE DURATION
  328. ; OUTPUT:    NONE
  329. ; REGS:        ALL PRESERVED
  330. ;==============================================================================
  331.  
  332. DoTone        proc    near
  333.         assume    ds:nothing, es:nothing, ss:nothing
  334.  
  335.         push    ax            ;save regs used
  336.         push    bx
  337.         push    cx
  338.         in    al,PB0port        ;get PB0 reg pattern
  339.         mov    ah,al            ;save it
  340.  
  341. DoTone1:    and    al,0fch            ;mask off speaker bit
  342.         out    PB0port,al        ;pull!
  343.         mov    cx,dx            ;half cycle in counter
  344. DoTone2:    loop    DoTone2            ;leave there for half a cycle
  345.         or    al,2            ;turn on speaker bit
  346.         out    PB0port,al        ;push!
  347.         mov    cx,dx            ;half cycle in counter
  348. DoTone3:    loop    DoTone3            ;leave there for half a cycle
  349.  
  350.         dec    bx            ;count down tone duration
  351.         jnz    DoTone1            ;go through full tone
  352.  
  353.         mov    al,ah            ;AL=original PB0 reg value
  354.         out    PB0port,al        ;restore
  355.  
  356.         pop    cx            ;restore regs
  357.         pop    bx
  358.         pop    ax
  359.         ret
  360. DoTone        endp
  361.  
  362. ;==============================================================================
  363. ; TestSafe - CHECK IF THIS IS A SAFE TIME TO DO A POP UP
  364. ; RETURN CLC IF SAFE TO POP UP, CY IF NOT SAFE.
  365. ;
  366. ; CHECK IF ANY INTs ARE IN CRITICAL AREAS (InInt09 & InInt13)
  367. ; CHECK IF WE ARE IN AN OUR OWN INT28 CALL (In28Call)
  368. ; CHECK 8259A PIC ISR REGISTER FOR MISSING EOIs
  369. ; CHECK IF DOS IS STABLE FOR POP UP
  370. ; CHECK IF A PRINT SCREEN IS IN PROGRESS
  371. ;==============================================================================
  372.  
  373. TestSafe    proc    near
  374.         assume    ds:nothing, es:nothing
  375.  
  376.         push    ax            ;save regs used
  377.         push    bx
  378.         push    ds
  379.  
  380. ; ------------    CHECK INTs TO SEE IF THEY WERE INTERRUPTED AT BAD TIMES
  381.  
  382.         test    TsrMode,InInt09 OR InInt13 OR In28Call
  383.         jnz    NotSafe            ;jump if any INTs are chopped
  384.  
  385. ; ------------    CHECK THE 8259A PIC ISR REGISTER FOR NON-EOIed HW INTs
  386.  
  387.         mov    al,00001011b        ;tell 8259A we want the ISR
  388.         out    20h,al            ;8259A command reg
  389.         nop
  390.         nop
  391.         nop                ;now, ISR should be ready
  392.         in    al,20h            ;AL=mask of active INTs
  393.         or    al,al            ;test all (IRQ0 *did* EOI)
  394.         jnz    NotSafe            ;jump if active INTs
  395.  
  396. ; ------------    NOW, ENSURE THAT DOS WAS NOT INTERRUPTED
  397.  
  398.         assume    ds:nothing
  399.  
  400.         lds    bx,InDosPtr        ;now, DS:BX=InDos
  401.         mov    al,byte ptr [bx]    ;get InDos to AL
  402.         lds    bx,CritErrPtr        ;now, DS:BX=CritErr
  403.         or    al,byte ptr [bx]    ;both flags zero?
  404.         jz    DosSafe            ;YES - DOS is really idle
  405.         test    TsrMode,InInt28        ;is this an INT28h
  406.         jz    NotSafe            ;NO - not safe, should be idle
  407.         cmp    al,1            ;YES - one InDos entry only?
  408.         ja    NotSafe            ;NO - jump if more than one
  409. DosSafe:
  410.  
  411. ; ------------    CHECK TO SEE IF A PRINT SCREEN IS IN PROGRESS
  412.  
  413.         mov    ax,BiosXX
  414.         mov    ds,ax            ;move DS to BIOS extra data seg
  415.         assume    ds:BiosXX
  416.  
  417.         cmp    StatusByte,InPrtSc    ;print screen in progress?
  418.         je    NotSafe            ;YES - jump if prtsc
  419.  
  420. ; ------------    SEEMS TO BE A SAFE TIME FOR POPUP
  421.  
  422. IsSafe:        clc                ;CLC=safe to popup
  423.         jmp    short ExitSafe        ;end this then
  424.  
  425. ; ------------    APPARENTLY THIS IS JUST NOT THE TIME TO DO A POPUP
  426.  
  427. NotSafe:    stc                ;CY=don't popup now
  428.  
  429. ; ------------    RETURN TO CALLER WITH CARRY SET/CLEAR
  430.  
  431. ExitSafe:    pop    ds            ;restore regs
  432.         pop    bx
  433.         pop    ax
  434.         ret
  435. TestSafe    endp
  436.  
  437. ;==============================================================================
  438. ; OurInt08 - TSR INT08H HANDLER TO WATCH FOR HOTKEY AND SAFE POPUP TIMES
  439. ;
  440. ; CALL OldInt08
  441. ; CHECK FOR RE-ENTRANCE INTO CRITICAL INT08 CODE
  442. ; SET InInt08 FLAG
  443. ; CHECK FOR TIMEOUT BETWEEN KEYS IN HOTKEY SEQUENCE
  444. ; CHECK IF HOTKEY WAS PRESSED
  445. ; CHECK IF ALREADY InPopup OR InInt28
  446. ; CHECK IF SAFE TIME FOR SYSTEM TO POPUP
  447. ; UPDATE FLAGS AND CALL POPUP IF SAFE
  448. ; GIVE ERROR BEEP IF POPUP WAS UNSAFE FOR A LONG TIME
  449. ; RESET InInt08 FLAG
  450. ; DO IRET
  451. ;==============================================================================
  452.  
  453. ; ------------    NEAR JUMP DESTINATION FOR FAST IRET'S
  454.  
  455. Exit08:        iret                ;IRET (!)
  456.  
  457. ; ------------    ACTUAL INT08 ENTRY POINT
  458.  
  459. OurInt08    proc    far
  460.         assume    ds:nothing, es:nothing, ss:nothing
  461.  
  462.         pushf                ;simulate INT08
  463.         cli                ;in case others forgot it
  464.         call    OldInt08        ;call TSRs loaded before us
  465.  
  466. ; ------------    ENSURE NO RECURSION INTO CRITICAL INT08 CODE
  467.  
  468.         sti                ;we'll manage INTs
  469.  
  470.         test    TsrMode,InInt08        ;already in here somewhere?
  471.         jnz    Exit08            ;YES - don't re-enter
  472.         or    TsrMode,InInt08        ;tell people we are here
  473.  
  474.         push    ax            ;need a few regs in this code
  475.  
  476. ; ------------    COUNT DOWN TIME-OUT BETWEEN KEYS IN HOTKEY SEQUENCE
  477.  
  478.         test    KeyMode,InHotMatch    ;are we in a key match?
  479.         jz    TestHot08        ;NO - don't care then
  480.         dec    BetweenKeys        ;count down timeout val
  481.         jnz    TestHot08        ;jump if no timeout yet
  482.         mov    HotIndex,0        ;start match from beginning
  483.         and    KeyMode,not InHotMatch    ;just so we know it next time
  484.  
  485. ; ------------    CHECK FOR POSSIBLE POPUP ACTIONS
  486.  
  487. TestHot08:    test    KeyMode,HotKeyOn    ;has hotkey been pressed?
  488.         jz    ExitInt08        ;NO - jump if no fun here
  489.  
  490.         test    TsrMode,InInt28 OR InPopup
  491.         jnz    ExitInt08        ;jmp if not alr in business
  492.  
  493. ; ------------    HOTKEY PRESSED, CHECK TO SEE IF IT IS SAFE TO POPUP
  494.  
  495.         cmp    SafeWait,0        ;first time we find hotkey?
  496.         ja    TestSafe08        ;NO - wait has alr been set
  497.         mov    SafeWait,MaxWait    ;# ticks to wait at most
  498.  
  499. TestSafe08:    call    TestSafe        ;now, CY clear if popup is safe
  500.         jc    NotSafe08        ;jump if popup is bad idea
  501.  
  502. ; ------------    SEEMS SAFE TO POPUP AT THIS TIME, SO DO!
  503.  
  504.         xor    al,al            ;fast zero
  505.         mov    SafeWait,al        ;don't count any more
  506.         and    KeyMode,not HotKeyOn    ;clear hotkey status
  507.         or    TsrMode,InPopup        ;tell'em we enter popup routine
  508.         and    TsrMode,not InInt08    ;OK to enter critical INT08
  509.         call    InitPopup        ;do actual popup
  510.         or    TsrMode,InInt08        ;back in INT08 code here
  511.         and    TsrMode,not InPopup    ;not in popup code any more
  512.         mov    SafeWait,al        ;in case of hotkey during popup
  513.         and    KeyMode,not HotKeyOn    ;clear hotkey status
  514.  
  515.         jmp    short ExitInt08        ;finally done
  516.  
  517. ; ------------    UNSAFE POPUP TIME, COUNT DOWN SafeWait
  518.  
  519. NotSafe08:    dec    SafeWait        ;count down waiter
  520.         jnz    ExitInt08         ;jump if still no timeout
  521.  
  522. ; ------------    NO SAFE TIMES FOUND FOR QUITE SOME TIME, ERROR
  523.  
  524.         and    KeyMode,not HotKeyOn    ;might as well clear hotkey
  525.         call    ErrBeep            ;do an error beep
  526.  
  527. ; ------------    NORMAL INT08H EXIT, RESET InInt08
  528.  
  529. ExitInt08:    pop    ax            ;restore regs used
  530.         and    TsrMode,not InInt08    ;clear that flag
  531.         iret                ;straight back
  532. OurInt08    endp
  533.  
  534. ;==============================================================================
  535. ; OurInt09 - TSR INT09H HANDLER TO WATCH FOR HOTKEY
  536. ;
  537. ; SAVE SCAN CODE
  538. ; CALL OldInt09
  539. ; CHECK FOR RECURSION INTO CRITICAL INT09 CODE
  540. ; SET InInt09 FLAG
  541. ; CHECK IF HOTKEY ALREADY SET
  542. ; DETERMINE HOTKEY TYPE (SHIFT STATE OR KEY SEQENCE)
  543. ; CHECK SHIFT STATE IF HotIsShift
  544. ; COMPARE FOR KEY MATCH IF (NOT HotIsShift)
  545. ; SET HotKeyOn IF HOTKEY PRESSED
  546. ; RESET InInt09 FLAG
  547. ; DO IRET
  548. ;==============================================================================
  549.  
  550. ; ------------    NEAR JUMP DESTINATION FOR EARLY EXITS
  551.  
  552. Exit09:        pop    bx            ;restore regs
  553.         pop    ax
  554.         iret                ;flags restored from stack
  555.  
  556. ; ------------    ACTUAL INT09 ENTRY POINT
  557.  
  558. OurInt09    proc    far
  559.         assume    ds:nothing, es:nothing, ss:nothing
  560.  
  561.         push    ax            ;save regs used
  562.         push    bx
  563.  
  564. ; ------------    READ SCAN CODE, IN CASE SEQUENCE MATCHING SELECTED
  565.  
  566.         in    al,KbData        ;Al=key, preserved by BIOS
  567.  
  568. ; ------------    CALL BIOS TO PERFORM IT'S DUTIES
  569.  
  570.         pushf                ;simulate INT (CLI alr set)
  571.         cli                ;in case others forgot it
  572.         call    OldInt09        ;call BIOS/earlier TSRs
  573.  
  574. ; ------------    ENSURE NO RECURSION INTO CRITICAL INT09 CODE
  575.  
  576.         sti                ;we'll manage INTs
  577.  
  578.         test    TsrMode,InInt09        ;alr in business?
  579.         jnz    Exit09            ;YES - skip test till clear
  580.         or    TsrMode,InInt09        ;tell them we arrived here
  581.  
  582. ; ------------    DETERMINE HOT KEY TYPE SELECTED
  583.  
  584.         test    KeyMode,HotKeyOn    ;already hotkey there?
  585.         jnz    ExitInt09        ;YES - no double hotkeys here
  586.  
  587.         test    KeyMode,HotIsShift    ;shift state type hotkey?
  588.         jz    CompSeq09        ;NO - go compare sequence
  589.  
  590. ; ------------    COMPARE CURRENT SHIFT STATUS AGAINST HOTKEY
  591.  
  592.         push    ds            ;save current ds
  593.         mov    ax,BiosData        ;move DS to BIOS data seg
  594.         mov    ds,ax            ;DS can now access keyb vars
  595.         assume    ds:BiosData        ;tell MASM about our DS
  596.         mov    al,KbFlag        ;get BIOS shift state bits
  597.         pop    ds            ;restore
  598.         assume    ds:nothing        ;last thing we know about him
  599.  
  600.         and    al,HotKeyShift        ;isolate relevant bits
  601.         cmp    al,HotKeyShift        ;our shift state in effect?
  602.         jne    ExitInt09        ;NO - not that shift state
  603.         or    KeyMode,HotKeyOn    ;YES - flag hotkey
  604.         jmp    short ExitInt09        ;now we can be proud to leave
  605.  
  606. ; ------------    MATCH KEY IN SCAN CODE SEQUENCE
  607.  
  608. CompSeq09:    mov    bl,HotIndex        ;next scan code to match
  609.         xor    bh,bh            ;must be word
  610.         cmp    al,HotKeySeq[bx]    ;does key match?
  611.         je    HotMatch09        ;YES - jump if match
  612.         mov    HotIndex,bh        ;search from start next time
  613.         and    KeyMode,not InHotMatch    ;current no match
  614.         jmp    short ExitInt09        ;now end this
  615.  
  616. ; ------------    KEY MACTHED NEXT SCAN CODE IN HotKeySeq
  617.  
  618. HotMatch09:    inc    bl            ;new code at next pass
  619.         cmp    bl,HotKeyLen        ;did we match whole sequence?
  620.         jae    HotHit09         ;YES - jump if full sequence
  621.         mov    HotIndex,bl        ;NO - save new count
  622.         mov    BetweenKeys,KeyTimeOut    ;reset counter between keys
  623.         or    KeyMode,InHotMatch    ;we are in a match now
  624.         jmp    short ExitInt09        ;time to end this
  625.  
  626. ; ------------    KEY MATCHED ALL SCAN CODES IN HOTKEY SEQUENCE
  627.  
  628. HotHit09:    or    KeyMode,HotKeyOn    ;say hotkey was pressed
  629.         mov    HotIndex,bh        ;match 1st code next time
  630.         and    KeyMode,not InHotMatch    ;that's the end of a match
  631.  
  632. ; ------------    EXIT FROM INT09H, RESET InInt09 FLAG
  633.  
  634. ExitInt09:    and    TsrMode,not InInt09    ;tell'em we left this code
  635.         pop    bx            ;restore regs
  636.         pop    ax
  637.         iret                ;flags restored from stack
  638. OurInt09    endp
  639.  
  640. ;==============================================================================
  641. ; OurInt13 - SET InInt13 FLAG TO SAY THAT WE ARE IN AN INT13H
  642. ;==============================================================================
  643.  
  644. OurInt13    proc    far
  645.         assume    ds:nothing, es:nothing, ss:nothing
  646.  
  647.         pushf                ;save flags we use
  648.         or    TsrMode,InInt13        ;remember we are in BIOS now
  649.         popf                ;restore flags
  650.  
  651.         pushf                ;simulate INT13
  652.         cli                ;just in case others forgot
  653.         call    OldInt13        ;let BIOS handle it all
  654.  
  655.         pushf                ;BIOS uses flag return
  656.         and    TsrMode, not InInt13    ;tell people we left INT13h
  657.         popf
  658.  
  659.         ret    2            ;throw flags off stack
  660. OurInt13    endp
  661.  
  662. ;==============================================================================
  663. ; OurInt16 - TSR INT16H HANDLER, INT28 CHAIN INTERFACE
  664. ;
  665. ; INPUT:    AX = GetId
  666. ; OUTPUT:    AX = MyId
  667. ; REGS:        AX LOST, ALL OTHERS PRESERVED
  668. ; DESCRIPTION:    DETERMINE IF TSR WITH THIS ID IS ALREADY IN MEMORY
  669. ;
  670. ; INPUT:    AH = 00
  671. ; OUTPUT:    AX = NEXT KEY FROM BUFFER
  672. ; REGS;        AX LOST, ALL OTHERS PRESERVED
  673. ; DESCRIPTION:    RETURN A KEY FROM KEYBOARD BUFFER, WAIT TILL KEY IS PRESSED
  674. ;
  675. ; INPUT:    AH = 01
  676. ; OUTPUT:    AX = KEY FROM BUFFER IN ANY
  677. ;        ZF = NO KEYS IN BUFFER (AX PRESERVED)
  678. ;        NZ = KEY IN BUFFER (RETURNED IN AX, KEY STILL IN BUFFER)
  679. ; DESCRIPTION:    CHECK BUFFER FOR ANY PENDING KEYS, RETURN KEY IF ANY
  680. ;
  681. ; NOTE:    ALL OTHER AX REQUEST CODES ARE PASSED ON TO BIOS INT16H HANDLER.
  682. ;
  683. ; NOTE:    DURING INT28 POPUP (InPopup AND NOT InDosClr) FUNCTIONS AH=0 AND
  684. ;    AH=1 WILL ISSUE INT28, UNLESS InDos HAS FROM VALUE AT POPUP OR
  685. ;    CritErr HAS BEEN SET.
  686. ;==============================================================================
  687.  
  688. OurInt16    proc    far
  689.         assume    ds:nothing, es:nothing, ss:nothing
  690.  
  691.         sti                ;we'll manage INTs
  692.         pushf                ;save callers flags
  693.         cmp    ax,GetId        ;return ID request?
  694.         jne    NotId16            ;NO - jump if not
  695.  
  696. ; ------------    TSR DIAGNOSTIC REQUEST, RETURN SPECIAL VALUE TO SAY WE ARE HERE
  697.  
  698.         mov    ax,MyId            ;ID val returned in AX
  699.         popf                ;restore flags
  700.         iret                ;return to caller
  701.  
  702. ; ------------    PASS CONTROL TO BIOS, FLAGS ON STACK
  703.  
  704. GoBios16:    popf                ;restore flags at INT time
  705.         jmp    OldInt16        ;continue in the woods
  706.  
  707. ; ------------    REGULAR BIOS INT16 REQUEST, CHECK FOR ANY FANCY ACTIONS
  708.  
  709. NotId16:    test    TsrMode,InPopup        ;are we in a popup?
  710.         jz    GoBios16        ;NO - leave rest with BIOS
  711.         test    TsrMode,InDosClr    ;InDos clear at popup?
  712.         jnz    GoBios16        ;YES - no need to signal INT28
  713.  
  714.         popf                ;restore original flags
  715.         push    bx            ;we need a few regs here
  716.         push    cx
  717.         push    si
  718.         push    ds
  719.         pushf                ;original flags back on stack
  720.  
  721. ; ------------    GET REQUEST CODE TO BH ENHANCED BIT TO BL
  722.  
  723.         mov    bh,ah            ;BH=function request code
  724.         and    bh,not 10h        ;zap enhanced kybd bit
  725.         cmp    bh,1            ;any function above 1?
  726.         ja    ExitBios16        ;YES - leave rest with BIOS
  727.  
  728.         mov    bl,ah            ;BL used for enhanced bit
  729.         and    bl,10h            ;BL=value of enhanced bit
  730.  
  731. ; ------------    GET InDos To CL, CritErr to CH, SETUP REGS
  732.  
  733.         assume    ds:nothing
  734.  
  735.         lds    si,InDosPtr        ;DS:[SI]=InDos
  736.         mov    cl,byte ptr [si]    ;CL=InDos value
  737.         lds    si,CritErrPtr        ;ES:[SI]=CritErr
  738.         mov    ch,byte ptr [si]    ;CH=CritErr value
  739.  
  740.         mov    si,ax            ;save AX call value
  741.  
  742.         mov    ax,cs            ;move DS here, now we got it
  743.         mov    ds,ax
  744.         assume    ds:Cseg            ;everybody should know
  745.  
  746. ; ------------    CHECK KEYBOARD BUFFER, ORIGINAL FLAGS ON STACK
  747.  
  748. Wait16:        mov    ah,1            ;AH=1=test buffer status
  749.         or    ah,bl            ;maintain enhanced bit value
  750.  
  751.         popf                ;restore original flags
  752.         pushf                ;simulate INT
  753.         cli                ;in case others forgot
  754.         call    OldInt16        ;now, ZF set if no keys
  755.         pushf                ;save result flags
  756.         jnz    TestSkip16        ;jump if a key was found
  757.  
  758. ; ------------    NO KEY FOUND, CALL INT28 IF DOS InDos ALLOWS
  759.  
  760.         cmp    cx,0001h        ;CritErr=0, InDos=1 ?
  761.         jne    NextKey16        ;NO - wait for next key
  762.         or    TsrMode,In28Call    ;tell people we called this INT
  763.         int    28h            ;now take your chance
  764.         and    TsrMode,not In28Call    ;end of that call
  765.  
  766. ; ------------    TEST BUFFER AGAIN IF INT16.00, IRET IF INT16.01
  767.  
  768. NextKey16:    or    bh,bh            ;is this a wait for key?
  769.         jz    Wait16            ;YES - then go wait for it!
  770.         mov    ax,si            ;restore original AX contents
  771.         jmp    short Exit16        ;NO - exit with status we got
  772.  
  773. ; ------------    KEY IN BUFFER, IF CTRL-C WE MAY HAVE TO SKIP IT, FLAGS ON STACK
  774.  
  775. TestSkip16:    cmp    al,3            ;is this Ctrl-C?
  776.         jne    TestExit16        ;NO - determine exit method
  777.         test    cx,not 0001h        ;anything but InDos=1?
  778.         jz    TestExit16        ;NO - determine exit method
  779.  
  780. ; ------------    SKIP CTRL-C IN KEYBOARD BUFFER
  781.  
  782.         mov    ah,bl            ;AH=0 + enhanced bit
  783.         popf                ;restore original INTs
  784.         pushf                ;save again
  785.         pushf                ;simulate INT
  786.         cli                ;simulate properly!
  787.         call    OldInt16        ;now, key should be gone
  788.         jmp    short Wait16        ;do as if nothing had happened
  789.  
  790. ; ------------    KEY IN AX, IRET IF INT16.01, LEAVE WITH BIOS IF INT16.00
  791.  
  792. TestExit16:    or    bh,bh            ;is this a wait for key?
  793.         jnz    Exit16            ;NO - do fast return
  794.         mov    ax,si            ;YES - restore AX code
  795.  
  796. ; ------------    PASS CONTROL TO BIOS, FLAGS & REGS ON STACK
  797.  
  798.         assume    ds:nothing
  799.  
  800. ExitBios16:    popf                ;restore work flags
  801.         pop    ds            ;restore regs
  802.         pop    si
  803.         pop    cx
  804.         pop    bx
  805.         cli                ;should look like an INT
  806.         jmp    OldInt16        ;leave rest with BIOS
  807.  
  808. ; ------------    RETURN FROM INT16, FLAGS & REGS ON STACK
  809.  
  810.         assume    ds:nothing
  811.  
  812. Exit16:        popf                ;restore proper flags
  813.         pop    ds            ;restore regs
  814.         pop    si
  815.         pop    cx
  816.         pop    bx
  817.         ret    2            ;IRET, without flags restore
  818.  
  819. OurInt16    endp
  820.  
  821. ;==============================================================================
  822. ; OurInt21 - INT21 FILTER TO THROW DANGEROUS DOS CALLS ON CRITICAL STACK
  823. ;
  824. ; CHECK IF InPopup AND InDosClr
  825. ; CHECK FUNCTION USES CONSOLE STACK
  826. ; SET CritErr IN DOS IF CONSOLE STACK USED
  827. ; CALL OldInt21
  828. ; RESTORE CritErr IF CRITICAL STACK USED
  829. ;==============================================================================
  830.  
  831. OurInt21    proc    far
  832.         assume    ds:nothing, es:nothing
  833.  
  834.         pushf                ;save calling flags
  835.         sti
  836.  
  837.         test    TsrMode,InPopup        ;are we in a popup?
  838.         jz    GoDos21            ;NO - don't worry then
  839.         test    TsrMode,InDosClr    ;console stack idle?
  840.         jnz    GoDos21            ;YES - nothing fancy then
  841.  
  842. ; ------------    THIS IS 2ND CALL INTO DOS, SEE IF USING CONSOLE STACK
  843.  
  844.         cmp    ah,0ch            ;any function 00-0C?
  845.         jbe    UseCrit21        ;YES - use critical stack
  846.         test    TsrMode,NewDos        ;NO - is this DOS 3.x?
  847.         jnz    GoDos21            ;YES - no other to worry about
  848.         cmp    ah,50h            ;set PSP function?
  849.         je    UseCrit21        ;YES - use critical stack
  850.         cmp    ah,51h            ;get PSP function?
  851.         jne    GoDos21            ;NO - leave it with DOS
  852.  
  853. ; ------------    FORCE USE OF CRITICAL STACK FOR THIS CALL
  854.  
  855. UseCrit21:    assume    ds:nothing        ;nothing to say about DS
  856.  
  857.         push    si            ;save regs
  858.         push    ds
  859.         lds    si,CritErrPtr        ;now, DS:[SI]=InDos
  860.         mov    byte ptr [si],-1    ;FF=use crit stack now
  861.         pop    ds            ;restore regs
  862.         pop    si
  863.  
  864.         popf                ;retsore flags setting
  865.         pushf                ;simulate INT
  866.         cli                ;in case others forgot
  867.         call    OldInt21        ;flags already on stack
  868.  
  869.         push    si            ;save regs
  870.         push    ds
  871.         lds    si,CritErrPtr        ;now, DS:[SI]=InDos
  872.         mov    byte ptr [si],0        ;0=back to default stack
  873.         pop    ds            ;restore regs
  874.         pop    si
  875.  
  876.         ret    2            ;IRET throw old flags
  877.  
  878. ; ------------    PASS CONTROL TO DOS, FLAGS ON STACK
  879.  
  880. GoDos21:    popf                ;restore original flags
  881.         cli                ;just in case someone forgot
  882.         jmp    OldInt21        ;let DOS handle the rest
  883. OurInt21    endp
  884.  
  885. ;==============================================================================
  886. ; OurInt24 - SAFE DOS CRITICAL ERROR HANDLER
  887. ; IF DOS 3.X, FAIL THE SYSTEM CALL
  888. ; IF NOT DOS 3.X, IGNORE ERROR
  889. ;==============================================================================
  890.  
  891. OurInt24    proc    far
  892.         assume    ds:nothing, es:nothing, ss:nothing
  893.         mov    al,3            ;AL=3=fail system call
  894.         test    TsrMode,NewDos        ;are we using DOS 3.x?
  895.         jnz    Exit24            ;YES - OK to use AL=3
  896.         xor    al,al            ;NO - have to ignore err then
  897. Exit24:        iret                ;return to DOS
  898. OurInt24    endp
  899.  
  900. ;==============================================================================
  901. ; OurInt28 - TSR INT28H HANDLER, ALLOWS POPUP DURING DOS IDLE CALLS
  902. ;
  903. ; CALL OldInt28
  904. ; CHECK FOR RECURSION INTO CRITICAL INT28 CODE (& OTHER INTs AS WELL)
  905. ; SET InInt28 FLAG
  906. ; CHECK FOR HOTKEY
  907. ; CHECK IF SAFE TO POPUP
  908. ; DO POPUP IF SAFE AT THIS TIME
  909. ; RESET InInt28 FLAG
  910. ; DO IRET
  911. ;==============================================================================
  912.  
  913. OurInt28    proc    far
  914.         assume    ds:nothing, es:nothing, ss:nothing
  915.  
  916. ; ------------    ENSURE NO RECURSION ON CRITICAL INT28 CODE
  917.  
  918.         pushf                ;save original flags
  919.         sti                ;we'll manage INT's after this
  920.         test    TsrMode,InInt08 OR InInt28 OR In28Call OR InPopup
  921.         jz    NowIn28            ;continue if not recursion
  922.  
  923. ; ------------    PASS CONTROL TO OLD BIOS28, ORIGINAL FLAGS ON STACK
  924.  
  925. GoOld28:    popf                ;restore original flags
  926.         cli                ;in case others forgot
  927.         jmp    OldInt28        ;call TSRs loaded before this
  928.  
  929. ; ------------    ENTER CRITICAL INT28 CODE, ORIGINAL FLAGS ON STACK
  930.  
  931. NowIn28:    or    TsrMode,InInt28        ;tell'em we are here
  932.  
  933. ; ------------    CHECK FOR POSSIBLE POPUP ACTIONS
  934.  
  935.         test    KeyMode,HotKeyOn    ;any hotkeys pressed?
  936.         jz    ExitInt28        ;NO - don't check any more then
  937.  
  938. ; ------------    HOTKEY WAS PRESSED, ENSURE IT'S SAFE TO DO POPUP
  939.  
  940.         call    TestSafe        ;now, CY clear if popup is OK
  941.         jc    ExitInt28        ;jump if not to popup
  942.  
  943. ; ------------    SEEMS OK TO DO POPUP, SO DO!
  944.  
  945.         and    KeyMode,not HotKeyOn    ;clear hotkey status
  946.         or    TsrMode,InPopup        ;tell'em we enter popup routine
  947.         and    TsrMode,not InInt28    ;OK to enter critical INT28
  948.         call    InitPopup        ;then do popup
  949.         or    TsrMode,InInt28        ;back in INT28 code here
  950.         and    TsrMode,not InPopup    ;not in popup code any more
  951.         and    KeyMode,not HotKeyOn    ;clear hotkeys during popup
  952.  
  953. ; ------------    NORMAL INT28H EXIT, RESET InInt28 FLAG
  954.  
  955. ExitInt28:    and    TsrMode,not InInt28    ;tell'em we left this code
  956.         jmp    short GoOld28        ;we have nothing more to say
  957. OurInt28    endp
  958.  
  959. ;==============================================================================
  960. ; NopInt - DUMMY IRET INSTRUCTION USED BY EMPTY INT HANDLERS
  961. ;==============================================================================
  962.  
  963. NopInt:        iret                ;immediate return
  964.  
  965. ;==============================================================================
  966. ; InitPopup - PREPARES SYSTEM FOR POPUP, THEN CALLS Popup, THEN RESTORES
  967. ;
  968. ; ESTABLISH INTERNAL WORK STACK
  969. ; SAVE CPU REGS
  970. ; UPDATE InDosClr FLAG WITH CURRENT VALUE OF InDos
  971. ; SAVE PROCESS RELATED SYSTEM INFO
  972. ; SAVE USER INTERRUPT VECTORS
  973. ; INSERT SAFE USER INTERRUPT VECTORS
  974. ; CALL POPUP ROUTINE
  975. ; RESTORE USER INTERRUPT VECTORS
  976. ; RESTORE PROCESS AND SYSTEM INFO
  977. ; CLEAR InDosClr FLAG TO PREVENT UNSAFE INT28 CALLs
  978. ; RESTORE CPU REGS
  979. ;==============================================================================
  980.  
  981. InitPopup    proc    near
  982.         assume    ds:nothing, es:nothing, ss:nothing
  983.  
  984. ; ------------    SWITCH TO PSP INTERNAL STACK
  985.  
  986.         mov    OldSS,ss        ;save current stack frame
  987.         mov    OldSP,sp
  988.  
  989.         cli                ;always CLI for the old chips
  990.         mov    ss,OurSS        ;move SS here
  991.         mov    sp,OurSP        ;move SP into position
  992.         sti                ;OK guys
  993.  
  994. ; ------------    SAVE ALL REGS
  995.  
  996.         push    ax
  997.         push    bx
  998.         push    cx
  999.         push    dx
  1000.         push    bp
  1001.         push    si
  1002.         push    di
  1003.         push    ds
  1004.         push    es
  1005.  
  1006.         mov    ax,cs
  1007.         mov    ds,ax            ;mov DS here
  1008.         assume    ds:Cseg            ;tell MASM that
  1009.  
  1010. ; ------------    TAG VALUE OF InDos FLAG AT TIME OF POPUP
  1011.  
  1012.         or    TsrMode,InDosClr    ;assume InDos=0
  1013.         les    si,InDosPtr        ;now, ES:[SI]=InDos
  1014.         cmp    byte ptr es:[si],1    ;InDos set? (>2 impossible)
  1015.         jb    InDosSaved        ;NO - jump if all clear DOS
  1016.         and    TsrMode,not InDosClr    ;clear flag for popup InDos
  1017. InDosSaved:
  1018.  
  1019. ; ------------    SAVE DOS 3.X EXTENDED ERROR INFO
  1020.  
  1021.         test    TsrMode,NewDos        ;really DOS 3.x?
  1022.         jz    Dos3Saved        ;NO - jump if not 3.x
  1023.  
  1024.         mov    ah,59h            ;to get err info from DOS
  1025.         xor    bx,bx            ;BX must be zero
  1026.         push    ds            ;save DS (killed by DOS)
  1027.         int    21h            ;ext err info in AX,BX,CX
  1028.         pop    ds            ;restore
  1029.         mov    OldExtErr[0],ax        ;save
  1030.         mov    OldExtErr[2],bx
  1031.         mov    OldExtErr[4],cx
  1032.         mov    OldExtErr[6],dx
  1033.         mov    OldExtErr[8],si
  1034.         mov    OldExtErr[10],di
  1035.         mov    OldExtErr[12],ds
  1036.         mov    OldExtErr[14],es
  1037. Dos3Saved:
  1038.  
  1039. ; ------------    SAVE CURRENT BREAK STATE, RELAX BREAK CHECKING
  1040.  
  1041.         mov    ax,3300h        ;to get current BREAK value
  1042.         int    21h            ;now, DL=current BREAK
  1043.         mov    OldBreak,dl        ;save current level
  1044.         mov    ax,3301h        ;to set BREAK from DL
  1045.         xor    dl,dl            ;DL=0=relax checking
  1046.         int    21h            ;current level in DL
  1047.  
  1048. ; ------------    SAVE CURRENT USER INT VECTORS
  1049.  
  1050.         mov    ax,351bh        ;BIOS ctrl-break int
  1051.         int    21h            ;ES:BX=vector
  1052.         mov    OldInt1BOff,bx        ;save it
  1053.         mov    OldInt1BSeg,es
  1054.  
  1055.         mov    ax,351ch        ;BIOS timer tick
  1056.         int    21h            ;ES:BX=vector
  1057.         mov    OldInt1COff,bx        ;save it
  1058.         mov    OldInt1CSeg,es
  1059.  
  1060.         mov    ax,3523h        ;DOS ctrl-C
  1061.         int    21h            ;ES:BX=vector
  1062.         mov    OldInt23Off,bx        ;save it
  1063.         mov    OldInt23Seg,es
  1064.  
  1065.         mov    ax,3524h        ;DOS crit err handler
  1066.         int    21h            ;ES:BX=vector
  1067.         mov    OldInt24Off,bx        ;save it
  1068.         mov    OldInt24Seg,es
  1069.  
  1070. ; ------------    INSERT DUMMY IRET INTO DANGEROUS VECTORS
  1071.  
  1072.         mov    dx,offset NopInt    ;now, DS:DX=dunny iret
  1073.         mov    ax,251bh        ;BIOS ctrlk-break handler
  1074.         int    21h            ;set to IRET
  1075.         mov    ax,251ch        ;BIOS timer tick
  1076.         int    21h            ;set to IRET
  1077.         mov    ax,2523h        ;DOS ctrl-C handler
  1078.         int    21h            ;set to IRET
  1079.  
  1080. ; ------------    ESTABLISH SAFE CRITICAL ERROR HANDLER
  1081.  
  1082.         mov    dx,offset OurInt24    ;now, DS:DX=safe crit err
  1083.         mov    ax,2524h        ;to set crit err handler
  1084.         int    21h
  1085.  
  1086. ; ------------    SAVE CURRENT DTA AREA, SET OUR DEFAULT DTA
  1087.  
  1088.         mov    ah,2fh            ;to obtain current DTA from DOS
  1089.         int    21h            ;DTA addr now in ES:BX
  1090.         mov    OldDTAOff,bx        ;save it
  1091.         mov    OldDTASeg,es
  1092.  
  1093.         push    ds            ;save DS for a while
  1094.         lds    dx,OurDTA        ;DS:DX=our DTA addr
  1095.         mov    ah,1ah            ;to set DTA via DOS
  1096.         int    21h            ;set that addr
  1097.         pop    ds            ;restore DS
  1098.  
  1099. ; ------------    SAVE CURRENT PSP, ESTABLISH OURS INSTEAD
  1100.  
  1101.         mov    ax,5100h        ;to get PSP from DOS
  1102.         int    21h            ;current PSP now in BX
  1103.         mov    OldPSP,bx        ;save it
  1104.         mov    bx,OurPSP        ;het our PSP instead
  1105.         mov    ax,5000h        ;to set our PSP
  1106.         int    21h
  1107.  
  1108. ; ------------    CALL USER POPUP ROUTINE
  1109.  
  1110.         call    Popup            ;finally!
  1111.  
  1112. ; ------------    RESTORE TO SAVED CURRENT PROCESS
  1113.  
  1114.         mov    bx,OldPSP        ;new current process in BX
  1115.         mov    ax,5000h        ;to set PSP via DOS
  1116.         int    21h            ;restore original PSP
  1117.  
  1118. ; ------------    RESTORE SAVED DTA
  1119.  
  1120.         push    ds            ;save DS for a while
  1121.         lds    dx,OldDTA        ;DS:DX=our DTA addr
  1122.         mov    ah,1ah            ;to set DTA via DOS
  1123.         int    21h            ;set that addr
  1124.         pop    ds            ;restore DS
  1125.  
  1126. ; ------------    RESTORE SAVED INTERRUPT VECTORS
  1127.  
  1128.         push    ds            ;save for a while
  1129.         assume    ds:nothing        ;be careful about MASM
  1130.  
  1131.         lds    dx,OldInt1B        ;BIOS ctrl-break handler
  1132.         mov    ax,251bh
  1133.         int    21h
  1134.  
  1135.         lds    dx,OldInt1C        ;BIOS timer tick
  1136.         mov    ax,251ch
  1137.         int    21h
  1138.  
  1139.         lds    dx,OldInt23        ;DOS ctrl-C
  1140.         mov    ax,2523h
  1141.         int    21h
  1142.  
  1143.         lds    dx,OldInt24        ;DOS crit err handler
  1144.         mov    ax,2524h
  1145.         int    21h
  1146.  
  1147.         pop    ds            ;restore data seg DS
  1148.         assume    ds:Cseg
  1149.  
  1150. ; ------------    RESTORE SAVED BREAK CHECKING LEVEL
  1151.  
  1152.         mov    ax,3301h        ;to set break check level
  1153.         mov    dl,OldBreak        ;get saved break state
  1154.         int    21h
  1155.  
  1156. ; ------------    RESTORE DOS 3.X SPECIFIC SYSTEM INFO
  1157.  
  1158.         test    TsrMode,NewDos        ;using DOS 3.x
  1159.         jz    Dos3Restored        ;NO - jump if old DOS 2
  1160.         mov    dx,offset OldExtErr    ;DS:DX=3 words of ext err
  1161.         mov    ax,5d0ah        ;to set ext err info
  1162.         int    21h
  1163. Dos3Restored:
  1164.  
  1165. ; ------------    RESET InDosSet FLAG VALUE TO PREVENT UNSAFE INT28
  1166.  
  1167.         or    TsrMode,InDosClr    ;now we only care that InDos=0
  1168.  
  1169. ; ------------    RESTORE USER REGS
  1170.  
  1171.         pop    es
  1172.         pop    ds
  1173.         pop    di
  1174.         pop    si
  1175.         pop    bp
  1176.         pop    dx
  1177.         pop    cx
  1178.         pop    bx
  1179.         pop    ax
  1180.         assume    ds:nothing
  1181.  
  1182. ; ------------    RETURN TO USER STACK
  1183.  
  1184.         cli                ;always CLI for the old chips
  1185.         mov    ss,OldSS        ;restore SS
  1186.         mov    sp,OldSP        ;restore SP
  1187.         sti                ;OK guys
  1188.  
  1189.         ret
  1190. InitPopup    endp
  1191.  
  1192. ;==============================================================================
  1193. ; DATA FOR POPUP ROUTINE
  1194. ;==============================================================================
  1195.  
  1196. DosReadMsg    db    13,10,'Reading DOS CON (press <Enter> to terminate)',13,10
  1197. DosReadLen    equ    $-DosReadMsg
  1198.  
  1199. BiosReadMsg    db    'Reading BIOS keyboard (press any key... )',8,8
  1200. BiosReadLen    equ    $-BiosReadMsg
  1201.  
  1202. DoneMsg        db    ' key pressed, exit from TSR DEMO)',13,10
  1203. DoneLen        equ    $-DoneMsg
  1204.  
  1205. Scratch        db    80 dup (?)
  1206.  
  1207. ;==============================================================================
  1208. ; Popup - POPUP USER ROUTINE
  1209. ;
  1210. ; ALL REGISTERS EXCEPT SS:SP AND DS MAY BE CHANGED.
  1211. ; DS IS PRESET TO THE TSR DATA SEGMENT.
  1212. ;
  1213. ; NOTE:    UPON ENTRY TO THIS ROUTINE ALL DOS FUNCTIONS MAY BE CALLED.
  1214. ;    IF POPUP WAS DONE ON INT28, WITH CritErr==1, ALL DOS FUNCTIONS
  1215. ;    THAT WOULD NORMALLY USE THE CONSOLE STACK, WILL GO TO THE CRITICAL
  1216. ;    STACK, HENCE PREVENTING FURTHER POPUP DURING THE DOS CALL.
  1217. ;    (HOWEVER, MOST TSRs WOULD NOT POPUP ANYWAY, SINCE InDos==2).
  1218. ;
  1219. ;    ADDRESSES OF THE InDos AND CritErr ARE STORED IN THE DOUBLE WORDS
  1220. ;    InDosPtr AND CritErrPtr.
  1221. ;
  1222. ;    AT ENTRY CritErr FLAG IS 0 (ZERO), InDos NO GREATER THAN 1 (ONE).
  1223. ;==============================================================================
  1224.  
  1225. Popup        proc    near
  1226.         assume    ds:Cseg, es:nothing, ss:nothing
  1227.  
  1228.         mov    ah,40h            ;DOS write handle
  1229.         mov    bx,1            ;standard output handle
  1230.         mov    dx,offset DosReadMsg    ;DS:DX=str to write
  1231.         mov    cx,DosReadLen        ;CX=# chars to write
  1232.         int    21h            ;output that string
  1233.  
  1234.         mov    ah,3fh            ;DOS read handle
  1235.         xor    bx,bx            ;standard input handle
  1236.         mov    dx,offset Scratch    ;scratch buf for key
  1237.         mov    cx,80            ;read till CR hit
  1238.         int    21h
  1239.  
  1240.         mov    ah,40h            ;read from BIOS msg
  1241.         mov    bx,1
  1242.         mov    dx,offset BiosReadMsg
  1243.         mov    cx,BiosReadLen
  1244.         int    21h
  1245.  
  1246.         xor    ah,ah            ;to let BIOS wait for key
  1247.         int    16h            ;now, key was pressed
  1248.  
  1249.         mov    ah,40h            ;write confirm msg
  1250.         mov    bx,1
  1251.         mov    dx,offset DoneMsg
  1252.         mov    cx,DoneLen
  1253.         int    21h
  1254.  
  1255.         ret
  1256. Popup        endp
  1257.  
  1258. ;==============================================================================
  1259. ; TSR IRON CURTAIN - HE WHO CROSSES THIS CURTAIN WILL BE GONE AFTER TSR!
  1260. ;==============================================================================
  1261.  
  1262. TsrCurtain:                    ;TSR memory break
  1263.  
  1264. ;==============================================================================
  1265. ; NON-RESIDENT MESSAGES FOR INIT
  1266. ;==============================================================================
  1267.  
  1268. BannerMsg    label    byte
  1269. db    13,10
  1270. db    '<<<<<<  TSR DEMO  >>>>>>',13,10
  1271. db    '   Thomas Brandenborg',13,10
  1272. db    '      Version 2.01',13,10,10
  1273. db    '$'
  1274.  
  1275. FirstMsg    label    byte
  1276. db    'Pop up routine installed resident.',13,10
  1277. db    '$'
  1278.  
  1279. SecondMsg    label    byte
  1280. db    'TSR DEMO already loaded.',13,10
  1281. db    '$'
  1282.  
  1283. HotKeyMsg    label    byte
  1284. db    'Hit <Left Shift> twice to pop up!',13,10,10
  1285. db    '$'
  1286.  
  1287. Dos1Msg    label    byte
  1288. db    'OOPS!',7,13,10
  1289. db    'Must use DOS release 2.00 or later!',13,10,10
  1290. db    '$'
  1291.  
  1292. BadDosMsg    label    byte
  1293. db    'OOPS!',7,13,10
  1294. db    'Did not recognize DOS version!',13,10,10
  1295. db    '$'
  1296.  
  1297. ; ------------    DOS ERROR LEVEL EXIT CODES
  1298.  
  1299. xOk        equ    0            ;normal, OK exit
  1300. xSecond        equ    1            ;TSR already loaded
  1301. xBadDos        equ    2            ;CritErr flag not found
  1302.  
  1303. ;==============================================================================
  1304. ; Init - INITIALIZE TSR APPLICATION, ENTERED UPON DOS LOAD
  1305. ; DISPLAY BANNER, INITIALIZE SYSTEM DATA, CHECK IF ALREADY LOADED,
  1306. ; HOOK INTO INTERRUPT CHAIN, TERMINATE, BUT STAY RESIDENT.
  1307. ;==============================================================================
  1308.  
  1309. Init        proc    near
  1310.         assume    ds:Cseg, es:nothing, ss:nothing
  1311.  
  1312.         mov    dx,offset BannerMsg
  1313.         mov    ah,9
  1314.         int    21h            ;display programme banner
  1315.  
  1316. ; ------------    USE INT16H DIAGNOSTIC TO SEE IF TSR ALREADY INSTALLED
  1317.  
  1318.         mov    ax,GetId        ;INT16h diagnostic request
  1319.         int    16h            ;now, AX=MyId if installed
  1320.         cmp    ax,MyId            ;TSR already installed?
  1321.         jne    CheckDos        ;NO - jump if not installed
  1322.  
  1323. ; ------------    TSR ALREADY INSTALLED, DISPLAY MSG, EXIT
  1324.  
  1325.         mov    dx,offset SecondMsg
  1326.         mov    ah,9
  1327.         int    21h                ;display alr installed msg
  1328.         mov    dx,offset HotKeyMsg
  1329.         mov    ah,9
  1330.         int    21h            ;be kind & disp hot key
  1331.         mov    ax,4c00h + xSecond    ;error level in AL
  1332.         int    21h            ;abot now
  1333.  
  1334. ; ------------    IDIOT IS RUNNING DOS 1, LEAVE THE OLD FASHION WAY!
  1335.  
  1336. Dos1:        mov    dx,offset Dos1Msg
  1337.         mov    ah,9
  1338.         int    21h            ;display msg about DOS 1
  1339.         int    20h            ;no err level for DOS 1
  1340.  
  1341. ; ------------    ENSURE DOS VERSION IS NEWER THAN 2.00
  1342.  
  1343. CheckDos:    or    TsrMode,NewDos        ;assume suing DOS 3.x
  1344.         mov    ah,30h            ;to get DOS version number
  1345.         int    21h            ;version is AL.AH
  1346.         cmp    al,2            ;release 2 or newer?
  1347.         jb    Dos1            ;NO - jump if DOS 1 in use
  1348.         ja    DosFlags        ;jump if DOS 3.x
  1349.         and    TsrMode,not NewDos    ;now, say we use DOS 2.x
  1350.  
  1351. ; ------------    INITIALIZE PTRS TO DOS FLAGS - 1ST InDos
  1352.  
  1353. DosFlags:    mov    ax,3400h        ;to get InDos ptr
  1354.         int    21h            ;ES:BX=seg:off of InDos
  1355.         mov    InDosOff,bx        ;save ptr
  1356.         mov    InDosSeg,es
  1357.  
  1358. ; ------------    WE NEED CritErr TO USE PSP FUNCTIONS IN DOS 2.X (CHIPs WAY)
  1359.  
  1360.         xor    dl,dl            ;DL=0=this is 1st scan
  1361.         mov    CritErrSeg,es        ;DOS seg still in ES
  1362. CritScan:    mov    di,bx            ;start search at InDos
  1363.         mov    cx,2000h        ;search max 1000h words
  1364.         mov    ax,3e80h        ;opcode CMP BYTE PTR [CritErr]
  1365.         cld                ;better serach forward
  1366.  
  1367. CritScan2:    repne    scasw            ;search till found or end
  1368.         jne    NoCritFound        ;jump if CMP not found
  1369.                         ;ES:[DI-2] at:
  1370.                         ;    CMP BYTE PTR [CritErr]
  1371.                         ;    JNZ ...
  1372.                         ;    MOV SP,stack addr
  1373.         cmp    byte ptr es:[di][5],0bch ;really CMP SP there?
  1374.         jne    CritScan2        ;NO - scan again if not
  1375.         mov    ax,word ptr es:[di]    ;now, AX=CritErr offset
  1376.         mov    CritErrOff,ax        ;save it
  1377.         jmp    short InitData        ;OK to end this now
  1378.  
  1379. NoCritFound:    or    dl,dl            ;was this1 st scan?
  1380.         jnz    BadDos            ;NO - CritErr not founbd at all
  1381.         inc    dl            ;DL=1=this is 2nd scan
  1382.         inc    bx            ;try scan at odd/even offset
  1383.         jmp    CritScan        ;scan again
  1384.         
  1385. ; ------------    COULD NOT LOCATE DOS CritErr FLAG - THAT'S AN ERROR
  1386.  
  1387. BadDos:        mov    dx,offset BadDosMsg
  1388.         mov    ah,9
  1389.         int    21h            ;display msg about that
  1390.         mov    ax,4c00h + xBadDos    ;err level in AL
  1391.         int    21h            ;OK to use 4C (DOS >= 2)
  1392.  
  1393. ; ------------    INITIALIZE SYSTEM DATA VARIABLES
  1394.  
  1395. InitData:                    ;store position for stack
  1396.         mov    OurSP,TsrCurtain - ComEntry + 100h + StackSize
  1397.         mov    OurSS,cs        ;stack seg is code seg
  1398.  
  1399.         mov    ax,5100h        ;to get current PSP from DOS
  1400.         int    21h            ;PSP now in BX
  1401.         mov    OurPSP,bx        ;save our PSP
  1402.  
  1403.         mov    ah,2fh            ;to get current DTA from DOS
  1404.         int    21h            ;now, ES:BX=current DTA
  1405.         mov    OurDTAOff,bx        ;save it
  1406.         mov    OurDTASeg,es
  1407.  
  1408.         and    KeyMode,not HotIsShift    ;hotkey is not shift state
  1409.         or    TsrMode,InDosClr    ;will prevent unsafe INT28s
  1410.  
  1411. ; ------------    SAVE VECTORS FOR OUR MONITOR INTERRUPTS
  1412.  
  1413.         mov    ax,3508h        ;BIOS timer0 tick handler
  1414.         int    21h            ;ES:BX=vector
  1415.         mov    OldInt08Off,bx
  1416.         mov    OldInt08Seg,es
  1417.  
  1418.         mov    ax,3509h        ;BIOS kb HW handler
  1419.         int    21h            ;ES:BX=vector
  1420.         mov    OldInt09Off,bx
  1421.         mov    OldInt09Seg,es
  1422.  
  1423.         mov    ax,3513h        ;BIOS disk I/O service
  1424.         int    21h            ;ES:BX=vector
  1425.         mov    OldInt13Off,bx
  1426.         mov    OldInt13Seg,es
  1427.  
  1428.         mov    ax,3516h        ;BIOS kb read
  1429.         int    21h            ;ES:BX=vector
  1430.         mov    OldInt16Off,bx
  1431.         mov    OldInt16Seg,es
  1432.  
  1433.         mov    ax,3521h        ;DOS functions dispatcher
  1434.         int    21h            ;ES:BX=vector
  1435.         mov    OldInt21Off,bx
  1436.         mov    OldInt21Seg,es
  1437.  
  1438.         mov    ax,3528h        ;DOS idle hook
  1439.         int    21h            ;ES:BX=vector
  1440.         mov    OldInt28Off,bx
  1441.         mov    OldInt28Seg,es
  1442.  
  1443. ; ------------    ESTABLISH IRET INT23 TO PREVENT BREAK DURING VECTOR FIX
  1444.  
  1445.         mov    dx,offset NopInt    ;DS:DX=dummy vector to set
  1446.         mov    ax,2523h        ;to set ^C handler through DOS
  1447.         int    21h            ;now, no break will occur
  1448.  
  1449. ; ------------    SAVE VECTORS FOR OUR MONITOR INTERRUPTS
  1450.  
  1451.         mov    ax,2508h        ;to set our INT08h handler
  1452.         mov    dx,offset OurInt08    ;DS:DX=new vector
  1453.         int    21h            ;let DOS set vector
  1454.  
  1455.         mov    ax,2509h        ;to set our INT09h handler
  1456.         mov    dx,offset OurInt09    ;DS:DX=new vector
  1457.         int    21h            ;let DOS set vector
  1458.  
  1459.         mov    ax,2513h        ;to set our INT13h handler
  1460.         mov    dx,offset OurInt13    ;DS:DX=new vector
  1461.         int    21h            ;let DOS set vector
  1462.  
  1463.         mov    ax,2516h        ;to set our INT16h handler
  1464.         mov    dx,offset OurInt16    ;DS:DX=new vector
  1465.         int    21h            ;let DOS set vector
  1466.  
  1467.         mov    ax,2521h        ;to set our INT21h handler
  1468.         mov    dx,offset OurInt21    ;DS:DX=new vector
  1469.         int    21h            ;let DOS set vector
  1470.  
  1471.         mov    ax,2528h        ;to set our INT28h handler
  1472.         mov    dx,offset OurInt28    ;DS:DX=new vector
  1473.         int    21h            ;let DOS set vector
  1474.  
  1475. ; ------------    DISLAY MSG ABOUT HOW WELL THIS IS ALL RUNNING
  1476.  
  1477.         mov    dx,offset FirstMsg
  1478.         mov    ah,9
  1479.         int    21h            ; display confirm msg
  1480.         mov    dx,offset HotKeyMsg
  1481.         mov    ah,9
  1482.         int    21h            ;disp hot key
  1483.  
  1484. ; ------------    EXIT, SAY GOOD BYE TO FRIENDS BEHIND CURTAIN!
  1485.  
  1486.         mov    es,EnvSeg        ;ES=our environment copy
  1487.         mov    ah,49h            ;to let DOS free block
  1488.         int    21h            ;environment copy freed
  1489.  
  1490.         mov    dx,(TsrCurtain-ComEntry+100h+StackSize+15) SHR 4
  1491.         mov    ax,3100h + xOk        ;TSR, AL=err level
  1492.         int    21h
  1493. Init        endp
  1494.  
  1495. ;==============================================================================
  1496.  
  1497. Cseg        ends
  1498.         end    ComEntry
  1499.