home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / dos_ency / 11 / snap.asm < prev   
Encoding:
Assembly Source File  |  1988-08-11  |  38.6 KB  |  1,080 lines

  1. ;
  2. ; Name:         snap
  3. ;
  4. ; Description:  This RAM-resident (terminate-and-stay-resident) utility
  5. ;               produces a video "snapshot" by copying the contents of the
  6. ;               video regeneration buffer to a disk file.  It may be used
  7. ;               in 80-column alphanumeric video modes on IBM PCs and PS/2s.
  8. ;
  9. ; Comments:     Assemble and link to create SNAP.EXE.
  10. ;
  11. ;               Execute SNAP.EXE to make resident.
  12. ;
  13. ;               Press Alt-Enter to dump current contents of video buffer
  14. ;               to a disk file.
  15. ;
  16.  
  17. MultiplexID     EQU     0CAh            ; unique INT 2FH ID value
  18.  
  19. TSRStackSize    EQU     100h            ; resident stack size in bytes
  20.  
  21. KB_FLAG         EQU     17h             ; offset of shift-key status flag in
  22.                                         ; ROM BIOS keyboard data area
  23.  
  24. KBIns           EQU     80h             ; bit masks for KB_FLAG
  25. KBCaps          EQU     40h
  26. KBNum           EQU     20h
  27. KBScroll        EQU     10h
  28. KBAlt           EQU     8
  29. KBCtl           EQU     4
  30. KBLeft          EQU     2
  31. KBRight         EQU     1
  32.  
  33. SCEnter         EQU     1Ch
  34.  
  35. CR              EQU     0Dh
  36. LF              EQU     0Ah
  37. TRUE            EQU     -1
  38. FALSE           EQU     0
  39.  
  40.                 PAGE
  41. ;------------------------------------------------------------------------------
  42. ;
  43. ; RAM-resident routines
  44. ;
  45. ;------------------------------------------------------------------------------
  46.  
  47. RESIDENT_GROUP  GROUP   RESIDENT_TEXT,RESIDENT_DATA,RESIDENT_STACK
  48.  
  49. RESIDENT_TEXT   SEGMENT byte public 'CODE'
  50.                 ASSUME  cs:RESIDENT_GROUP,ds:RESIDENT_GROUP
  51.  
  52. ;------------------------------------------------------------------------------
  53. ; System verification routines
  54. ;------------------------------------------------------------------------------
  55.  
  56. VerifyDOSState  PROC    near            ; Returns:    carry flag set if MS-DOS
  57.                                         ;             is busy
  58.  
  59.                 push    ds              ; preserve these registers
  60.                 push    bx
  61.                 push    ax
  62.  
  63.                 lds     bx,cs:ErrorModeAddr
  64.                 mov     ah,[bx]         ; AH = ErrorMode flag
  65.  
  66.                 lds     bx,cs:InDOSAddr
  67.                 mov     al,[bx]         ; AL = InDOS flag
  68.  
  69.                 xor     bx,bx           ; BH = 00H, BL = 00H
  70.                 cmp     bl,cs:InISR28   ; carry flag set if INT 28H handler
  71.                                         ; is running
  72.                 rcl     bl,01h          ; BL = 01H if INT 28H handler is running
  73.  
  74.                 cmp     bx,ax           ; carry flag zero if AH = 00H
  75.                                         ; and AL <= BL
  76.  
  77.                 pop     ax              ; restore registers
  78.                 pop     bx
  79.                 pop     ds
  80.                 ret
  81.  
  82. VerifyDOSState  ENDP
  83.  
  84.  
  85. VerifyIntState  PROC    near            ; Returns:    carry flag set if hardware
  86.                                         ;             or ROM BIOS unstable
  87.  
  88.                 push    ax              ; preserve AX
  89.  
  90. ; Verify hardware interrupt status by interrogating Intel 8259A Programmable
  91. ;  Interrupt Controller
  92.  
  93.                 mov     ax,00001011b    ; AH = 0
  94.                                         ; AL = 0CW3 for Intel 8259A (RR = 1,
  95.                                         ; RIS = 1)
  96.                 out     20h,al          ; request 8259A's in-service register
  97.                 jmp     short L10       ; wait a few cycles
  98. L10:            in      al,20h          ; AL = hardware interrupts currently
  99.                                         ; being serviced (bit = 1 if in-service)
  100.                 cmp     ah,al
  101.                 jc      L11             ; exit if any hardware interrupts still
  102.                                         ; being serviced
  103.  
  104. ; Verify status of ROM BIOS interrupt handlers
  105.  
  106.                 xor     al,al           ; AL = 00H
  107.  
  108.                 cmp     al,cs:InISR5
  109.                 jc      L11             ; exit if currently in INT 05H handler
  110.  
  111.                 cmp     al,cs:InISR9
  112.                 jc      L11             ; exit if currently in INT 09H handler
  113.  
  114.                 cmp     al,cs:InISR10
  115.                 jc      L11             ; exit if currently in INT 10H handler
  116.  
  117.                 cmp     al,cs:InISR13   ; set carry flag if currently in
  118.                                         ; INT 13H handler
  119.  
  120. L11:            pop     ax              ; restore AX and return
  121.                 ret
  122.  
  123. VerifyIntState  ENDP
  124.  
  125.  
  126. VerifyTSRState  PROC    near            ; Returns: carry flag set if TSR
  127.                                         ;          inactive
  128.  
  129.                 rol     cs:HotFlag,1    ; carry flag set if (HotFlag = TRUE)
  130.                 cmc                     ; carry flag set if (HotFlag = FALSE)
  131.                 jc      L20             ; exit if no hot key
  132.  
  133.                 ror     cs:ActiveTSR,1  ; carry flag set if (ActiveTSR = TRUE)
  134.                 jc      L20             ; exit if already active
  135.  
  136.                 call    VerifyDOSState
  137.                 jc      L20             ; exit if MS-DOS unstable
  138.  
  139.                 call    VerifyIntState  ; set carry flag if hardware or BIOS
  140.                                         ; unstable
  141.  
  142. L20:            ret
  143.  
  144. VerifyTSRState  ENDP
  145.  
  146.                 PAGE
  147. ;------------------------------------------------------------------------------
  148. ; System monitor routines
  149. ;------------------------------------------------------------------------------
  150.  
  151. ISR5            PROC    far             ; INT 05H handler
  152.                                         ; (ROM BIOS print screen)
  153.  
  154.                 inc     cs:InISR5       ; increment status flag
  155.  
  156.                 pushf
  157.                 cli
  158.                 call    cs:PrevISR5     ; chain to previous INT 05H handler
  159.  
  160.                 dec     cs:InISR5       ; decrement status flag
  161.                 iret
  162.  
  163. ISR5            ENDP
  164.  
  165.  
  166. ISR8            PROC    far             ; INT 08H handler (timer tick, IRQ0)
  167.  
  168.                 pushf
  169.                 cli
  170.                 call    cs:PrevISR8     ; chain to previous handler
  171.  
  172.                 cmp     cs:InISR8,0
  173.                 jne     L31             ; exit if already in this handler
  174.  
  175.                 inc     cs:InISR8       ; increment status flag
  176.  
  177.                 sti                     ; interrupts are ok
  178.                 call    VerifyTSRState
  179.                 jc      L30             ; jump if TSR is inactive
  180.  
  181.                 mov     byte ptr cs:ActiveTSR,TRUE
  182.                 call    TSRapp
  183.                 mov     byte ptr cs:ActiveTSR,FALSE
  184.  
  185. L30:            dec     cs:InISR8
  186.  
  187. L31:            iret
  188.  
  189. ISR8            ENDP
  190.  
  191.  
  192. ISR9            PROC    far             ; INT 09H handler
  193.                                         ; (keyboard interrupt IRQ1)
  194.  
  195.                 push    ds              ; preserve these registers
  196.                 push    ax
  197.                 push    bx
  198.  
  199.                 push    cs
  200.                 pop     ds              ; DS -> RESIDENT_GROUP
  201.  
  202.                 in      al,60h          ; AL = current scan code
  203.  
  204.                 pushf                   ; simulate an INT
  205.                 cli
  206.                 call    ds:PrevISR9     ; let previous handler execute
  207.  
  208.                 mov     ah,ds:InISR9    ; if already in this handler ..
  209.                 or      ah,ds:HotFlag   ; .. or currently processing hot key ..
  210.                 jnz     L43             ; .. jump to exit
  211.  
  212.                 inc     ds:InISR9       ; increment status flag
  213.                 sti                     ; now interrupts are ok
  214.  
  215. ; Check scan code sequence
  216.  
  217.                 cmp     ds:HotSeqLen,0
  218.                 je      L40             ; jump if no hot sequence to match
  219.  
  220.                 mov     bx,ds:HotIndex
  221.                 cmp     al,[bx+HotSequence]     ; test scan code sequence
  222.                 jne     L41             ; jump if no match
  223.  
  224.                 inc     bx
  225.                 cmp     bx,ds:HotSeqLen
  226.                 jb      L42             ; jump if not last scan code to match
  227.  
  228. ; Check shift-key state
  229.  
  230. L40:            push    ds
  231.                 mov     ax,40h
  232.                 mov     ds,ax           ; DS -> ROM BIOS data area
  233.                 mov     al,ds:[KB_FLAG] ; AH = ROM BIOS shift-key flags
  234.                 pop     ds
  235.  
  236.                 and     al,ds:HotKBMask ; AL = flags AND "don't care" mask
  237.                 cmp     al,ds:HotKBFlag
  238.                 jne     L42             ; jump if shift state does not match
  239.  
  240. ; Set flag when hot key is found
  241.  
  242.                 mov     byte ptr ds:HotFlag,TRUE
  243.  
  244. L41:            xor     bx,bx           ; reinitialize index
  245.  
  246. L42:            mov     ds:HotIndex,bx  ; update index into sequence
  247.                 dec     ds:InISR9       ; decrement status flag
  248.  
  249. L43:            pop     bx              ; restore registers and exit
  250.                 pop     ax
  251.                 pop     ds
  252.                 iret
  253.  
  254. ISR9            ENDP
  255.  
  256.  
  257. ISR10           PROC    far             ; INT 10H handler (ROM BIOS video I/O)
  258.  
  259.                 inc     cs:InISR10      ; increment status flag
  260.  
  261.                 pushf
  262.                 cli
  263.                 call    cs:PrevISR10    ; chain to previous INT 10H handler
  264.  
  265.                 dec     cs:InISR10      ; decrement status flag
  266.                 iret
  267.  
  268. ISR10           ENDP
  269.  
  270.  
  271. ISR13           PROC    far             ; INT 13H handler
  272.                                         ; (ROM BIOS fixed disk I/O)
  273.  
  274.                 inc     cs:InISR13      ; increment status flag
  275.  
  276.                 pushf                
  277.                 cli
  278.                 call    cs:PrevISR13    ; chain to previous INT 13H handler
  279.  
  280.                 pushf                   ; preserve returned flags
  281.                 dec     cs:InISR13      ; decrement status flag
  282.                 popf                    ; restore flags register
  283.  
  284.                 sti                     ; enable interrupts
  285.                 ret     2               ; simulate IRET without popping flags
  286.  
  287. ISR13           ENDP
  288.  
  289.  
  290. ISR1B           PROC    far             ; INT 1BH trap (ROM BIOS Ctrl-Break)
  291.  
  292.                 mov     byte ptr cs:Trap1B,TRUE
  293.                 iret
  294.  
  295. ISR1B           ENDP
  296.  
  297.  
  298. ISR23           PROC    far             ; INT 23H trap (MS-DOS Ctrl-C)
  299.  
  300.                 mov     byte ptr cs:Trap23,TRUE
  301.                 iret
  302.  
  303. ISR23           ENDP
  304.  
  305.  
  306. ISR24           PROC    far             ; INT 24H trap (MS-DOS critical error)
  307.  
  308.                 mov     byte ptr cs:Trap24,TRUE
  309.  
  310.                 xor     al,al           ; AL = 00H (MS-DOS 2.x):
  311.                                         ; ignore the error
  312.                 cmp     cs:MajorVersion,2
  313.                 je      L50
  314.  
  315.                 mov     al,3            ; AL = 03H (MS-DOS 3.x):
  316.                                         ; fail the MS-DOS call in which
  317.                                         ; the critical error occurred
  318. L50:            iret
  319.  
  320. ISR24           ENDP
  321.  
  322.  
  323. ISR28           PROC    far             ; INT 28H handler
  324.                                         ; (MS-DOS idle interrupt)
  325.  
  326.                 pushf
  327.                 cli
  328.                 call    cs:PrevISR28    ; chain to previous INT 28H handler
  329.  
  330.                 cmp     cs:InISR28,0
  331.                 jne     L61             ; exit if already inside this handler
  332.  
  333.                 inc     cs:InISR28      ; increment status flag
  334.  
  335.                 call    VerifyTSRState
  336.                 jc      L60             ; jump if TSR is inactive
  337.  
  338.                 mov     byte ptr cs:ActiveTSR,TRUE
  339.                 call    TSRapp
  340.                 mov     byte ptr cs:ActiveTSR,FALSE
  341.  
  342. L60:            dec     cs:InISR28      ; decrement status flag
  343.  
  344. L61:            iret
  345.  
  346. ISR28           ENDP
  347.  
  348.  
  349. ISR2F           PROC    far             ; INT 2FH handler
  350.                                         ; (MS-DOS multiplex interrupt)
  351.                                         ; Caller:  AH = handler ID
  352.                                         ;          AL = function number
  353.                                         ; Returns for function 0:  AL = 0FFH
  354.                                         ; for all other functions:  nothing
  355.  
  356.                 cmp     ah,MultiplexID
  357.                 je      L70             ; jump if this handler is requested
  358.  
  359.                 jmp     cs:PrevISR2F    ; chain to previous INT 2FH handler
  360.  
  361. L70:            test    al,al
  362.                 jnz     MultiplexIRET   ; jump if reserved or undefined function
  363.  
  364. ; Function 0:  get installed state
  365.  
  366.                 mov     al,0FFh         ; AL = 0FFh (this handler is installed)
  367.  
  368. MultiplexIRET:  iret                    ; return from interrupt                
  369.  
  370. ISR2F           ENDP
  371.  
  372.                 PAGE
  373. ;
  374. ;
  375. ; AuxInt21--sets ErrorMode while executing INT 21h to force use of the
  376. ;       AuxStack instead of the IOStack.
  377. ;
  378. ;
  379.  
  380. AuxInt21        PROC    near            ; Caller:     registers for INT 21H
  381.                                         ; Returns:    registers from INT 21H
  382.                 
  383.                 push    ds
  384.                 push    bx
  385.                 lds     bx,ErrorModeAddr
  386.                 inc     byte ptr [bx]   ; ErrorMode is now nonzero
  387.                 pop     bx
  388.                 pop     ds
  389.  
  390.                 int     21h             ; perform MS-DOS function
  391.  
  392.                 push    ds
  393.                 push    bx
  394.                 lds     bx,ErrorModeAddr
  395.                 dec     byte ptr [bx]   ; restore ErrorMode
  396.                 pop     bx
  397.                 pop     ds
  398.                 ret
  399.  
  400. AuxInt21        ENDP
  401.  
  402.  
  403. Int21v          PROC    near            ; perform INT 21H or AuxInt21,
  404.                                         ; depending on MS-DOS version
  405.  
  406.                 cmp     DOSVersion,30Ah
  407.                 jb      L80             ; jump if earlier than 3.1
  408.  
  409.                 int     21h             ; version 3.1 and later
  410.                 ret
  411.  
  412. L80:            call    AuxInt21        ; versions earlier than 3.1
  413.                 ret
  414.  
  415. Int21v          ENDP
  416.  
  417.                 PAGE
  418. ;------------------------------------------------------------------------------
  419. ; RAM-resident application
  420. ;------------------------------------------------------------------------------
  421.  
  422. TSRapp          PROC    near
  423.  
  424. ; Set up a safe stack
  425.  
  426.                 push    ds              ; save previous DS on previous stack
  427.  
  428.                 push    cs
  429.                 pop     ds              ; DS -> RESIDENT_GROUP
  430.  
  431.                 mov     PrevSP,sp       ; save previous SS:SP
  432.                 mov     PrevSS,ss
  433.  
  434.                 mov     ss,TSRSS        ; SS:SP -> RESIDENT_STACK
  435.                 mov     sp,TSRSP
  436.  
  437.                 push    es              ; preserve remaining registers
  438.                 push    ax
  439.                 push    bx
  440.                 push    cx
  441.                 push    dx
  442.                 push    si
  443.                 push    di
  444.                 push    bp
  445.  
  446.                 cld                     ; clear direction flag
  447.  
  448. ; Set break and critical error traps
  449.  
  450.                 mov     cx,NTrap
  451.                 mov     si,offset RESIDENT_GROUP:StartTrapList
  452.  
  453. L90:            lodsb                   ; AL = interrupt number
  454.                                         ; DS:SI -> byte past interrupt number
  455.  
  456.                 mov     byte ptr [si],FALSE     ; zero the trap flag
  457.  
  458.                 push    ax              ; preserve AX
  459.                 mov     ah,35h          ; INT 21H function 35H
  460.                                         ; (get interrupt vector)
  461.                 int     21h             ; ES:BX = previous interrupt vector
  462.                 mov     [si+1],bx       ; save offset and segment ..
  463.                 mov     [si+3],es       ;  .. of previous handler
  464.  
  465.                 pop     ax              ; AL = interrupt number
  466.                 mov     dx,[si+5]       ; DS:DX -> this TSR's trap
  467.                 mov     ah,25h          ; INT 21H function 25H
  468.                 int     21h             ; (set interrupt vector)
  469.                 add     si,7            ; DS:SI -> next in list
  470.  
  471.                 loop    L90
  472.  
  473. ; Disable MS-DOS break checking during disk I/O
  474.  
  475.                 mov     ax,3300h        ; AH = INT 21H function number
  476.                                         ; AL = 00H (request current break state)
  477.                 int     21h             ; DL = current break state
  478.                 mov     PrevBreak,dl    ; preserve current state
  479.  
  480.                 xor     dl,dl           ; DL = 00H (disable disk I/O break
  481.                                         ; checking)
  482.                 mov     ax,3301h        ; AL = 01H (set break state)
  483.                 int     21h
  484.  
  485. ; Preserve previous extended error information
  486.  
  487.                 cmp     DOSVersion,30Ah
  488.                 jb      L91             ; jump if MS-DOS version earlier 
  489.                                         ; than 3.1
  490.                 push    ds              ; preserve DS
  491.                 xor     bx,bx           ; BX = 00H (required for function 59H)
  492.                 mov     ah,59h          ; INT 21H function 59H
  493.                 call    Int21v          ; (get extended error info)
  494.  
  495.                 mov     cs:PrevExtErrDS,ds
  496.                 pop     ds
  497.                 mov     PrevExtErrAX,ax ; preserve error information
  498.                 mov     PrevExtErrBX,bx ; in data structure
  499.                 mov     PrevExtErrCX,cx
  500.                 mov     PrevExtErrDX,dx
  501.                 mov     PrevExtErrSI,si
  502.                 mov     PrevExtErrDI,di
  503.                 mov     PrevExtErrES,es
  504.  
  505. ; Inform MS-DOS about current PSP
  506.  
  507. L91:            mov     ah,51h          ; INT 21H function 51H (get PSP address)
  508.                 call    Int21v          ; BX = foreground PSP
  509.  
  510.                 mov     PrevPSP,bx      ; preserve previous PSP
  511.  
  512.                 mov     bx,TSRPSP       ; BX = resident PSP
  513.                 mov     ah,50h          ; INT 21H function 50H (set PSP address)
  514.                 call    Int21v
  515.  
  516. ; Inform MS-DOS about current DTA (not really necessary in this application
  517. ; because DTA is not used)
  518.  
  519.                 mov     ah,2Fh          ; INT 21H function 2FH
  520.                 int     21h             ; (get DTA address) into ES:BX
  521.                 mov     PrevDTAoffs,bx
  522.                 mov     PrevDTAseg,es
  523.  
  524.                 push    ds              ; preserve DS
  525.                 mov     ds,TSRPSP
  526.                 mov     dx,80h          ; DS:DX -> default DTA at PSP:0080
  527.                 mov     ah,1Ah          ; INT 21H function 1AH
  528.                 int     21h             ; (set DTA address)
  529.                 pop     ds              ; restore DS
  530.  
  531. ; Open a file, write to it, and close it
  532.  
  533.                 mov     ax,0E07h        ; AH = INT 10H function number
  534.                                         ; (Write Teletype)
  535.                                         ; AL = 07H (bell character)
  536.                 int     10h             ; emit a beep
  537.  
  538.                 mov     dx,offset RESIDENT_GROUP:SnapFile
  539.                 mov     ah,3Ch          ; INT 21H function 3CH
  540.                                         ; (create file handle)
  541.                 mov     cx,0            ; file attribute
  542.                 int     21h
  543.                 jc      L94             ; jump if file not opened
  544.  
  545.                 push    ax              ; push file handle
  546.                 mov     ah,0Fh          ; INT 10H function 0FH
  547.                                         ; (get video status)
  548.                 int     10h             ; AL = video mode number
  549.                                         ; AH = number of character columns
  550.                 pop     bx              ; BX = file handle 
  551.  
  552.                 cmp     ah,80
  553.                 jne     L93             ; jump if not 80-column mode
  554.  
  555.                 mov     dx,0B800h       ; DX = color video buffer segment
  556.                 cmp     al,3
  557.                 jbe     L92             ; jump if color alphanumeric mode
  558.  
  559.                 cmp     al,7
  560.                 jne     L93             ; jump if not monochrome mode
  561.  
  562.                 mov     dx,0B000h       ; DX = monochrome video buffer segment
  563.  
  564. L92:            push    ds
  565.                 mov     ds,dx
  566.                 xor     dx,dx           ; DS:DX -> start of video buffer
  567.                 mov     cx,80*25*2      ; CX = number of bytes to write
  568.                 mov     ah,40h          ; INT 21H function 40H (write file)
  569.                 int     21h
  570.                 pop     ds
  571.  
  572. L93:            mov     ah,3Eh          ; INT 21H function 3EH (close file)
  573.                 int     21h
  574.  
  575.                 mov     ax,0E07h        ; emit another beep
  576.                 int     10h
  577.  
  578. ; Restore previous DTA
  579.  
  580. L94:            push    ds              ; preserve DS
  581.                 lds     dx,PrevDTA      ; DS:DX -> previous DTA
  582.                 mov     ah,1Ah          ; INT 21H function 1AH (set DTA address)
  583.                 int     21h
  584.                 pop     ds
  585.  
  586. ; Restore previous PSP
  587.  
  588.                 mov     bx,PrevPSP      ; BX = previous PSP
  589.                 mov     ah,50h          ; INT 21H function 50H
  590.                 call    Int21v          ; (set PSP address)
  591.  
  592. ; Restore previous extended error information
  593.  
  594.                 mov     ax,DOSVersion
  595.                 cmp     ax,30AH
  596.                 jb      L95             ; jump if MS-DOS version earlier than 3.1
  597.                 cmp     ax,0A00h
  598.                 jae     L95             ; jump if MS OS/2-DOS 3.x box
  599.  
  600.                 mov     dx,offset RESIDENT_GROUP:PrevExtErrInfo
  601.                 mov     ax,5D0Ah
  602.                 int     21h             ; (restore extended error information)
  603.  
  604. ; Restore previous MS-DOS break checking
  605.  
  606. L95:            mov     dl,PrevBreak    ; DL = previous state
  607.                 mov     ax,3301h
  608.                 int     21h
  609.  
  610. ; Restore previous break and critical error traps
  611.  
  612.                 mov     cx,NTrap
  613.                 mov     si,offset RESIDENT_GROUP:StartTrapList
  614.                 push    ds              ; preserve DS
  615.  
  616. L96:            lods    byte ptr cs:[si] ; AL = interrupt number
  617.                                         ; ES:SI -> byte past interrupt number
  618.  
  619.                 lds     dx,cs:[si+1]    ; DS:DX -> previous handler
  620.                 mov     ah,25h          ; INT 21H function 25H
  621.                 int     21h             ; (set interrupt vector)
  622.                 add     si,7            ; DS:SI -> next in list
  623.                 loop    L96
  624.  
  625.                 pop     ds              ; restore DS
  626.  
  627. ; Restore all registers
  628.  
  629.                 pop     bp
  630.                 pop     di
  631.                 pop     si
  632.                 pop     dx
  633.                 pop     cx
  634.                 pop     bx
  635.                 pop     ax
  636.                 pop     es
  637.  
  638.                 mov     ss,PrevSS       ; SS:SP -> previous stack
  639.                 mov     sp,PrevSP
  640.                 pop     ds              ; restore previous DS
  641.  
  642. ; Finally, reset status flag and return
  643.  
  644.                 mov     byte ptr cs:HotFlag,FALSE
  645.                 ret
  646.  
  647. TSRapp          ENDP
  648.  
  649.  
  650. RESIDENT_TEXT   ENDS
  651.  
  652.  
  653. RESIDENT_DATA   SEGMENT word public 'DATA'
  654.  
  655. ErrorModeAddr   DD      ?               ; address of MS-DOS ErrorMode flag
  656. InDOSAddr       DD      ?               ; address of MS-DOS InDOS flag
  657.  
  658. NISR            DW      (EndISRList-StartISRList)/8 ; number of installed ISRs
  659.  
  660. StartISRList    DB      05h             ; INT number
  661. InISR5          DB      FALSE           ; flag
  662. PrevISR5        DD      ?               ; address of previous handler
  663.                 DW      offset RESIDENT_GROUP:ISR5
  664.  
  665.                 DB      08h
  666. InISR8          DB      FALSE
  667. PrevISR8        DD      ?
  668.                 DW      offset RESIDENT_GROUP:ISR8
  669.  
  670.                 DB      09h
  671. InISR9          DB      FALSE
  672. PrevISR9        DD      ?
  673.                 DW      offset RESIDENT_GROUP:ISR9
  674.  
  675.                 DB      10h
  676. InISR10         DB      FALSE
  677. PrevISR10       DD      ?
  678.                 DW      offset RESIDENT_GROUP:ISR10
  679.  
  680.                 DB      13h
  681. InISR13         DB      FALSE
  682. PrevISR13       DD      ?
  683.                 DW      offset RESIDENT_GROUP:ISR13
  684.  
  685.                 DB      28h
  686. InISR28         DB      FALSE
  687. PrevISR28       DD      ?
  688.                 DW      offset RESIDENT_GROUP:ISR28
  689.  
  690.                 DB      2Fh
  691. InISR2F         DB      FALSE
  692. PrevISR2F       DD      ?
  693.                 DW      offset RESIDENT_GROUP:ISR2F
  694.  
  695. EndISRList      LABEL   BYTE
  696.  
  697. TSRPSP          DW      ?               ; resident PSP
  698. TSRSP           DW      TSRStackSize    ; resident SS:SP
  699. TSRSS           DW      seg RESIDENT_STACK
  700. PrevPSP         DW      ?               ; previous PSP
  701. PrevSP          DW      ?               ; previous SS:SP
  702. PrevSS          DW      ?
  703.  
  704. HotIndex        DW      0               ; index of next scan code in sequence
  705. HotSeqLen       DW      EndHotSeq-HotSequence   ; length of hot-key sequence
  706.  
  707. HotSequence     DB      SCEnter         ; hot sequence of scan codes
  708. EndHotSeq       LABEL   BYTE
  709.  
  710. HotKBFlag       DB      KBAlt           ; hot value of ROM BIOS KB_FLAG
  711. HotKBMask       DB      (KBIns OR KBCaps OR KBNum OR KBScroll) XOR 0FFh
  712. HotFlag         DB      FALSE
  713.  
  714. ActiveTSR       DB      FALSE
  715.  
  716. DOSVersion      LABEL   WORD
  717.                 DB      ?               ; minor version number
  718. MajorVersion    DB      ?               ; major version number
  719.  
  720. ; The following data is used by the TSR application:
  721.  
  722. NTrap           DW      (EndTrapList-StartTrapList)/8   ; number of traps
  723.  
  724. StartTrapList   DB      1Bh
  725. Trap1B          DB      FALSE
  726. PrevISR1B       DD      ?
  727.                 DW      offset RESIDENT_GROUP:ISR1B
  728.  
  729.                 DB      23h
  730. Trap23          DB      FALSE
  731. PrevISR23       DD      ?
  732.                 DW      offset RESIDENT_GROUP:ISR23
  733.  
  734.                 DB      24h
  735. Trap24          DB      FALSE
  736. PrevISR24       DD      ?
  737.                 DW      offset RESIDENT_GROUP:ISR24
  738.  
  739. EndTrapList     LABEL   BYTE
  740.  
  741. PrevBreak       DB      ?               ; previous break-checking flag
  742.  
  743. PrevDTA         LABEL   DWORD           ; previous DTA address
  744. PrevDTAoffs     DW      ?
  745. PrevDTAseg      DW      ?
  746.  
  747. PrevExtErrInfo  LABEL   BYTE            ; previous extended error information
  748. PrevExtErrAX    DW      ?
  749. PrevExtErrBX    DW      ?
  750. PrevExtErrCX    DW      ?
  751. PrevExtErrDX    DW      ?
  752. PrevExtErrSI    DW      ?
  753. PrevExtErrDI    DW      ?
  754. PrevExtErrDS    DW      ?
  755. PrevExtErrES    DW      ?
  756.                 DW      3 dup(0)
  757.  
  758. SnapFile        DB      '\snap.img'     ; output filename in root directory
  759.  
  760. RESIDENT_DATA   ENDS
  761.  
  762. RESIDENT_STACK  SEGMENT word stack 'STACK'
  763.  
  764.                 DB      TSRStackSize dup(?)
  765.  
  766. RESIDENT_STACK  ENDS
  767.  
  768.                 PAGE
  769. ;------------------------------------------------------------------------------
  770. ;
  771. ; Transient installation routines
  772. ;
  773. ;------------------------------------------------------------------------------
  774.  
  775. TRANSIENT_TEXT  SEGMENT para public 'TCODE'
  776.                 ASSUME  cs:TRANSIENT_TEXT,ds:RESIDENT_DATA,ss:RESIDENT_STACK
  777.  
  778. InstallSnapTSR  PROC    far             ; At entry:  CS:IP -> InstallSnapTSR
  779.                                         ;            SS:SP -> stack
  780.                                         ;            DS,ES -> PSP
  781. ; Save PSP segment
  782.  
  783.                 mov     ax,seg RESIDENT_DATA
  784.                 mov     ds,ax           ; DS  -> RESIDENT_DATA
  785.  
  786.                 mov     TSRPSP,es       ; save PSP segment
  787.  
  788. ; Check the MS-DOS version
  789.                 
  790.                 call    GetDOSVersion   ; AH = major version number
  791.                                         ; AL = minor version number
  792.  
  793. ; Verify that this TSR is not already installed
  794. ;
  795. ;       Before executing INT 2Fh in MS-DOS versions 2.x, test whether INT 2FH
  796. ;       vector is in use.  If so, abort if PRINT.COM is using it.
  797. ;
  798. ;       (Thus, in MS-DOS 2.x, if both this program and PRINT.COM are used,
  799. ;       this program should be made resident before PRINT.COM.)
  800.  
  801.                 cmp     ah,2
  802.                 ja      L101            ; jump if version 3.0 or later
  803.  
  804.                 mov     ax,352Fh        ; AH = INT 21H function number
  805.                                         ; AL = interrupt number
  806.                 int     21h             ; ES:BX = INT 2FH vector
  807.  
  808.                 mov     ax,es
  809.                 or      ax,bx           ; jump if current INT 2FH vector ..
  810.                 jnz     L100            ; .. is nonzero
  811.  
  812.                 push    ds
  813.                 mov     ax,252Fh        ; AH = INT 21H function number
  814.                                         ; AL = interrupt number
  815.                 mov     dx,seg RESIDENT_GROUP
  816.                 mov     ds,dx
  817.                 mov     dx,offset RESIDENT_GROUP:MultiplexIRET
  818.                 
  819.                 int     21h             ; point INT 2FH vector to IRET
  820.                 pop     ds
  821.                 jmp     short L103      ; jump to install this TSR
  822.  
  823. L100:           mov     ax,0FF00h       ; look for PRINT.COM:
  824.                 int     2Fh             ; if resident, AH = print queue length;
  825.                                         ; otherwise, AH is unchanged 
  826.  
  827.                 cmp     ah,0FFh         ; if PRINT.COM is not resident ..
  828.                 je      L101            ; .. use multiplex interrupt
  829.  
  830.                 mov     al,1
  831.                 call    FatalError      ; abort if PRINT.COM already installed
  832.  
  833. L101:           mov     ah,MultiplexID  ; AH = multiplex interrupt ID value
  834.                 xor     al,al           ; AL = 00H
  835.                 int     2Fh             ; multiplex interrupt
  836.  
  837.                 test    al,al
  838.                 jz      L103            ; jump if ok to install
  839.  
  840.                 cmp     al,0FFh
  841.                 jne     L102            ; jump if not already installed
  842.  
  843.                 mov     al,2
  844.                 call    FatalError      ; already installed
  845.  
  846. L102:           mov     al,3
  847.                 call    FatalError      ; can't install
  848.  
  849. ; Get addresses of InDOS and ErrorMode flags
  850.  
  851. L103:           call    GetDOSFlags
  852.  
  853. ; Install this TSR's interrupt handlers
  854.  
  855.                 push    es              ; preserve PSP segment
  856.  
  857.                 mov     cx,NISR
  858.                 mov     si,offset StartISRList
  859.  
  860. L104:           lodsb                   ; AL = interrupt number
  861.                                         ; DS:SI -> byte past interrupt number
  862.                 push    ax              ; preserve AX
  863.                 mov     ah,35h          ; INT 21H function 35H
  864.                 int     21h             ; ES:BX = previous interrupt vector
  865.                 mov     [si+1],bx       ; save offset and segment ..
  866.                 mov     [si+3],es       ; .. of previous handler
  867.  
  868.                 pop     ax              ; AL = interrupt number
  869.                 push    ds              ; preserve DS
  870.                 mov     dx,[si+5]
  871.                 mov     bx,seg RESIDENT_GROUP
  872.                 mov     ds,bx           ; DS:DX -> this TSR's handler
  873.                 mov     ah,25h          ; INT 21H function 25H
  874.                 int     21h             ; (set interrupt vector)
  875.                 pop     ds              ; restore DS
  876.                 add     si,7            ; DS:SI -> next in list
  877.                 loop    L104
  878.  
  879. ; Free the environment
  880.  
  881.                 pop     es              ; ES = PSP segment
  882.                 push    es              ; preserve PSP segment
  883.                 mov     es,es:[2Ch]     ; ES = segment of environment
  884.                 mov     ah,49h          ; INT 21H function 49H
  885.                 int     21h             ; (free memory block)
  886.  
  887. ; Terminate and stay resident
  888.  
  889.                 pop     ax              ; AX = PSP segment
  890.                 mov     dx,cs           ; DX = paragraph address of start of
  891.                                         ; transient portion (end of resident
  892.                                         ; portion)
  893.                 sub     dx,ax           ; DX = size of resident portion
  894.  
  895.                 mov     ax,3100h        ; AH = INT 21H function number
  896.                                         ; AL = 00H (return code)
  897.                 int     21h
  898.  
  899. InstallSnapTSR  ENDP
  900.  
  901.  
  902. GetDOSVersion   PROC    near            ; Caller:   DS = seg RESIDENT_DATA
  903.                                         ;           ES = PSP
  904.                                         ; Returns:  AH = major version
  905.                                         ;           AL = minor version
  906.                 ASSUME  ds:RESIDENT_DATA
  907.  
  908.                 mov     ax,30h          ; INT 21h function 30H:
  909.                                         ; (get MS-DOS version)
  910.                 int     21h
  911.                 cmp     al,2
  912.                 jb      L110            ; jump if version 1.x
  913.  
  914.                 xchg    ah,al           ; AH = major version
  915.                                         ; AL = minor version
  916.                 mov     DOSVersion,ax   ; save with major version in
  917.                                         ; high-order byte
  918.                 ret
  919.  
  920. L110:           mov     al,00h
  921.                 call    FatalError      ; abort if version 1.x
  922.  
  923. GetDOSVersion   ENDP
  924. GetDOSFlags     PROC    near            ; Caller:     DS = seg RESIDENT_DATA
  925.                                         ; Returns:    InDOSAddr -> InDOS
  926.                                         ;             ErrorModeAddr -> ErrorMode
  927.                                         ; Destroys:   AX,BX,CX,DI
  928.                 ASSUME  ds:RESIDENT_DATA
  929.  
  930. ; Get InDOS address from MS-DOS
  931.  
  932.                 push    es
  933.  
  934.                 mov     ah,34h          ; INT 21H function number
  935.                 int     21h             ; ES:BX -> InDOS
  936.                 mov     word ptr InDOSAddr,bx
  937.                 mov     word ptr InDOSAddr+2,es
  938.  
  939. ; Determine ErrorMode address
  940.  
  941.                 mov     word ptr ErrorModeAddr+2,es     ; assume ErrorMode is
  942.                                                         ; in the same segment
  943.                                                         ; as InDOS
  944.  
  945.                 mov     ax,DOSVersion
  946.                 cmp     ax,30Ah
  947.                 jb      L120            ; jump if MS-DOS version earlier
  948.                                         ; than 3.1 ..
  949.                 cmp     ax,0A00h
  950.                 jae     L120            ; .. or MS OS/2-DOS 3.x box
  951.  
  952.                 dec     bx              ; in MS-DOS 3.1 and later, ErrorMode
  953.                 mov     word ptr ErrorModeAddr,bx       ; is just before InDOS
  954.                 jmp     short L125
  955.  
  956. L120:                                   ; scan MS-DOS segment for ErrorMode
  957.  
  958.                 mov     cx,0FFFFh       ; CX = maximum number of bytes to scan
  959.                 xor     di,di           ; ES:DI -> start of MS-DOS segment
  960.  
  961. L121:           mov     ax,word ptr cs:LF2  ; AX = opcode for INT 28H
  962.  
  963. L122:           repne   scasb           ; scan for first byte of fragment
  964.                 jne     L126            ; jump if not found
  965.  
  966.                 cmp     ah,es:[di]              ; inspect second byte of opcode
  967.                 jne     L122                    ; jump if not INT 28H
  968.  
  969.                 mov     ax,word ptr cs:LF1 + 1  ; AX = opcode for CMP
  970.                 cmp     ax,es:[di][LF1-LF2]
  971.                 jne     L123                    ; jump if opcode not CMP
  972.  
  973.                 mov     ax,es:[di][(LF1-LF2)+2] ; AX = offset of ErrorMode
  974.                 jmp     short L124              ; in DOS segment
  975.  
  976. L123:           mov     ax,word ptr cs:LF3 + 1  ; AX = opcode for TEST
  977.                 cmp     ax,es:[di][LF3-LF4]
  978.                 jne     L121                    ; jump if opcode not TEST
  979.  
  980.                 mov     ax,es:[di][(LF3-LF4)+2] ; AX = offset of ErrorMode
  981.  
  982. L124:           mov     word ptr ErrorModeAddr,ax
  983.  
  984. L125:           pop     es
  985.                 ret
  986.  
  987. ; Come here if address of ErrorMode not found
  988.  
  989. L126:           mov     al,04H
  990.                 call    FatalError
  991.  
  992.  
  993. ; Code fragments for scanning for ErrorMode flag
  994.  
  995. LFnear          LABEL   near            ; dummy labels for addressing
  996. LFbyte          LABEL   byte
  997. LFword          LABEL   word
  998.                                         ; MS-DOS versions earlier than 3.1
  999. LF1:            cmp     ss:LFbyte,0     ; CMP ErrorMode,0
  1000.                 jne     LFnear
  1001. LF2:            int     28h
  1002.                                         ; MS-DOS versions 3.1 and later
  1003.  
  1004. LF3:            test    ss:LFbyte,0FFh  ; TEST ErrorMode,0FFH
  1005.                 jne     LFnear
  1006.                 push    ss:LFword
  1007. LF4:            int     28h
  1008.  
  1009. GetDOSFlags     ENDP
  1010.  
  1011. FatalError      PROC    near            ; Caller:   AL = message number
  1012.                                         ;           ES = PSP
  1013.                 ASSUME  ds:TRANSIENT_DATA
  1014.  
  1015.                 push    ax              ; save message number on stack
  1016.  
  1017.                 mov     bx,seg TRANSIENT_DATA
  1018.                 mov     ds,bx
  1019.  
  1020. ; Display the requested message
  1021.  
  1022.                 mov     bx,offset MessageTable
  1023.                 xor     ah,ah           ; AX = message number
  1024.                 shl     ax,1            ; AX = offset into MessageTable
  1025.                 add     bx,ax           ; DS:BX -> address of message
  1026.                 mov     dx,[bx]         ; DS:BX -> message
  1027.                 mov     ah,09h          ; INT 21H function 09H (display string)
  1028.                 int     21h             ; display error message
  1029.  
  1030.                 pop     ax              ; AL = message number
  1031.                 or      al,al
  1032.                 jz      L130            ; jump if message number is zero
  1033.                                         ; (MS-DOS versions 1.x)
  1034.  
  1035. ; Terminate (MS-DOS 2.x and later)
  1036.  
  1037.                 mov     ah,4Ch          ; INT 21H function 4CH
  1038.                 int     21h             ; (terminate process with return code)
  1039.  
  1040. ; Terminate (MS-DOS 1.x)
  1041.  
  1042. L130            PROC    far
  1043.  
  1044.                 push    es              ; push PSP:0000H
  1045.                 xor     ax,ax
  1046.                 push    ax
  1047.                 ret                     ; far return (jump to PSP:0000H)
  1048.  
  1049. L130            ENDP
  1050.  
  1051. FatalError      ENDP
  1052.  
  1053.  
  1054. TRANSIENT_TEXT  ENDS
  1055.  
  1056.                 PAGE
  1057. ;
  1058. ;
  1059. ; Transient data segment
  1060. ;
  1061. ;
  1062.  
  1063. TRANSIENT_DATA  SEGMENT word public 'DATA'
  1064.  
  1065. MessageTable    DW   Message0           ; MS-DOS version error
  1066.                 DW   Message1           ; PRINT.COM found in MS-DOS 2.x
  1067.                 DW   Message2           ; already installed
  1068.                 DW   Message3           ; can't install
  1069.                 DW   Message4           ; can't find flag
  1070.  
  1071. Message0        DB   CR,LF,'TSR requires MS-DOS 2.0 or later version',CR,LF,'$'
  1072. Message1        DB   CR,LF,'Can''t install TSR:  PRINT.COM active',CR,LF,'$'
  1073. Message2        DB   CR,LF,'This TSR is already installed',CR,LF,'$'
  1074. Message3        DB   CR,LF,'Can''t install this TSR',CR,LF,'$'
  1075. Message4        DB   CR,LF,'Unable to locate MS-DOS ErrorMode flag',CR,LF,'$'
  1076.  
  1077. TRANSIENT_DATA  ENDS
  1078.  
  1079.                 END     InstallSnapTSR
  1080.