home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / assemblr / library / sampler0 / pcmap.asm < prev    next >
Assembly Source File  |  1989-04-22  |  39KB  |  1,105 lines

  1.  
  2. page 58,132
  3.  
  4. ;----------------------------------------------------------------
  5. ; PCMAP 2.0 - Jeff Hasty (CompuServe 71121,2352) - April, 1989
  6. ;         Documentation in PCMAP2.DOC
  7. ;----------------------------------------------------------------
  8.  
  9. ;----------------------------------------------------------------
  10. ; EQUATES
  11. ;----------------------------------------------------------------
  12. MAX_BLK     EQU    23        ;Number of spaces in table
  13. INT9_BUSY    EQU    1        ;Mask for BUSY byte
  14. INT10_BUSY    EQU    2        ;Mask for BUSY byte
  15. SHIFT_MASK    EQU    8        ;Mask for hot key (8=Alt)
  16. HOTKEY        EQU    19H        ;Scan code (19h=P)
  17.  
  18. CR        EQU    0DH        ; ASCII carriage return
  19. LF        EQU    0AH        ; ASCII line feed
  20. TAB        EQU    09h        ; ASCII tab
  21. BLANK        EQU    20h        ; ASCII space character
  22.  
  23. ;----------------------------------------------------------------
  24. ; START - entry point for command-line mode
  25. ;----------------------------------------------------------------
  26. _TEXT    SEGMENT PARA    PUBLIC    'CODE'    ;set up for .COM file
  27.     ASSUME    CS:_TEXT,DS:_TEXT
  28.         ORG 100H
  29. START:
  30.         JMP    RES        ;jump to installation routines
  31.  
  32. ;----------------------------------------------------------------
  33. ; RESIDENT DATA AREA
  34. ;----------------------------------------------------------------
  35. ID        DB    "PCMAP 2.0 - Jeff Hasty (CompuServe 71121,2352)"
  36.         DB    " - April, 1989",1Ah
  37. HEADING_MSG    DB    CR,LF
  38.                 DB      "Segment               Size                 Program"
  39.                 DB      CR,LF
  40.                 DB      "Address    Owner      (para)     Type      Name"
  41.         DB    CR,LF
  42.         DB    "0000",18 DUP(' '),0
  43. BLOCK1_MSG    DB    10 DUP (' '),"DOS + Drivers",CR,LF,0
  44. CR_LF_MSG    DB    CR,LF,0
  45. COM_MSG         DB      "COMMAND.COM"
  46. PSP_MSG         DB      "PSP"
  47. ENV_MSG         DB      "ENV"
  48. UNK_MSG         DB      "(Unknown)"
  49. FREE_MSG        DB      "(Free)"
  50. SPACE3_MSG      DB      "   "
  51. SPACE_MSG    DB    7 DUP(' '),0
  52. TABLE_FULL_MSG    DB    "Out of space     ",0
  53. PROGRAM_ID    DB    "PCMAP 2.0",0
  54. HIT_ANY_KEY    DB    "  -  Hit any key to return...",0
  55.  
  56. DISABLE     DB    0    ;flag to disable if cannot uninstall
  57. TSR_MODE    DB    1    ;=0 if command line mode
  58. VER3        DB    0    ;=1 if Version >= 3.0
  59. LAST_BLOCK    DB    0    ;=1 if last MCB
  60. TABLE_FULL    DB    0    ;=1 if table full
  61.  
  62. CURSOR_POS    DW    0    ;to store cursor position
  63. BIOS_SEG    DW    40H    ;address of bios data area
  64. DIFF        DW    0    ;# of chars on a line > 80
  65. N_BLK        DB    0    ;Count table entries
  66.  
  67. OUR_SS        DW    0    ;  used for stack swap
  68. OUR_SP        DW    0
  69. THEIR_SS    DW    0
  70. THEIR_SP    DW    0
  71. RETADDR     DW    0
  72.  
  73. ADDR_INT9H    DD    0    ;to save original vectors
  74. ADDR_INT10H    DD    0
  75. BUSY        DB    0    ;to store status of int 9 and int 10h
  76.  
  77. ;----------------------------------------------------------------------
  78. ; INT9H - entry point for memory-resident mode.
  79. ; pressing any key causes entry here.
  80. ;----------------------------------------------------------------------
  81. INT9H PROC FAR
  82.         STI                ;interrupts on
  83.         PUSH    AX            ;save working register
  84.         CMP    CS:DISABLE,-1        ;if disabled, do nothing
  85.         JE    NOT_US
  86.         IN    AL,60H            ;get key from keyboard port
  87.         CMP    AL,HOTKEY        ;is it our hotkey?
  88.         JNE    NOT_US            ;if not, exit
  89.         MOV    AH,2            ;otherwise
  90.         INT    16H            ;get shift status
  91.         AND    AL,0FH
  92.         CMP    AL,SHIFT_MASK        ;test the shift status
  93.         JNE    NOT_US            ;if not shift combo, exit
  94.         IN    AL,61H            ;These instructions reset
  95.         MOV    AH,AL            ; the keyboard.
  96.         OR    AL,80H
  97.         OUT    61H,AL
  98.         MOV    AL,AH
  99.         JMP    SHORT $+2        ;I/O delay for fast AT's
  100.         OUT    61H,AL
  101.         CLI                ;Disable interrupts and
  102.         MOV    AL,20H            ;reset the int controller
  103.         OUT    20H,AL
  104.         STI
  105.         CMP    CS:BUSY,0        ;recursion protection
  106.         JNE    WE_ARE_BUSY        ;dont allow re-entrancy
  107.         OR    CS:BUSY,INT9_BUSY    ;set flag for protection
  108.         CALL    ADJUST_FOR_VIDEO_MODE
  109.         JC    CANT_POP_UP        ;exit if inappropriate mode
  110.         CALL    MAIN            ;call our program
  111. CANT_POP_UP:
  112.         CLI                ;disable kbd momentarily
  113.         AND    CS:BUSY,NOT(INT9_BUSY)    ;reset protection
  114. WE_ARE_BUSY:
  115.         POP    AX            ;restore working register
  116.         STI
  117.         IRET                ;return to foreground
  118. NOT_US:
  119.         POP    AX            ;restore working register
  120.         CLI                ;interrupts off
  121.         JMP    CS:ADDR_INT9H        ;jump to original int 9
  122. INT9H ENDP
  123.  
  124.  
  125. ;-----------------------------------------------------------------
  126. ; ADJUST_FOR_VIDEO_MODE
  127. ; check for text modes and set offset for lines > than 80 characters
  128. ; in length.  sets carry flag if inappropriate mode for pop-up.
  129. ;-----------------------------------------------------------------
  130. ADJUST_FOR_VIDEO_MODE PROC NEAR
  131.  
  132.         PUSH    BX            ;save register
  133.         MOV    AH,15            ;get present mode
  134.         INT    10H
  135.  
  136.         CMP    AH,80
  137.         JB    BAD_MODE        ;less than 80 chars per line
  138.  
  139.         MOV    CS:BYTE PTR DIFF,AH    ;calc the # of chars > 80
  140.         SUB    CS:BYTE PTR DIFF,80    ;on the line & save in diff
  141.         CMP    AL,7            ;7 is mono, good mode
  142.         JNE    TRY_COLOR
  143. MODE_OK:
  144.         CLC                ;clear carry flag
  145.         POP    BX            ;restore register
  146.         RET
  147. TRY_COLOR:
  148.         CMP    AL,3            ;3 is color 80x25,
  149.         JBE    MODE_OK         ;  2 is B&W 80x25
  150. BAD_MODE:
  151.         STC                ;not good mode, set carry flag
  152.         POP    BX            ;restore register
  153.         RET
  154.  
  155. ADJUST_FOR_VIDEO_MODE ENDP
  156.  
  157. ;-----------------------------------------------------------------
  158. ; MAIN - main routine called by pressing hot key
  159. ;-----------------------------------------------------------------
  160. MAIN PROC NEAR
  161.         CLD                ;strings forward
  162.         CALL    SWAPIN            ;new stack
  163.         MOV    AX,CS            ;our data segment is
  164.         MOV    DS,AX            ;  same as CS
  165.         CALL    GETPOS            ;save cursor position
  166.         CALL    CURSOR_HOME        ;cursor to 0,0
  167.         CALL    SAVE_SCREEN        ;save screen
  168.         CALL    CLEAR_SCREEN        ;clear screen
  169.         CALL    PROGRAM         ;construct & display memory map
  170.         MOV    TABLE_FULL,0        ;reset flag
  171.         CALL    RESTORE_SCREEN        ;put screen back
  172.         CALL    RESTORE_CURSOR        ;cursor to original position
  173.         CALL    SWAPOUT            ;put stack back
  174.         RET                ;that's all
  175. MAIN ENDP
  176.  
  177.  
  178. ;-----------------------------------------------------------------
  179. ; SWAPIN, SWAPOUT - stack routines
  180. ;-----------------------------------------------------------------
  181. SWAPIN PROC NEAR
  182.         POP    CS:RETADDR        ;save callers address
  183.         MOV    CS:THEIR_SS,SS        ;save their stack
  184.         MOV    CS:THEIR_SP,SP
  185.         MOV    SS,CS:OUR_SS        ;switch to our stack
  186.         MOV    SP,CS:OUR_SP
  187.         PUSH    AX            ;save all registers
  188.         PUSH    BX
  189.         PUSH    CX
  190.         PUSH    DX
  191.         PUSH    SI
  192.         PUSH    DI
  193.         PUSH    ES
  194.         PUSH    DS
  195.         PUSH    BP
  196.         JMP    CS:RETADDR        ;return to caller
  197. SWAPIN ENDP
  198. ;-----------------------------------------------------------------
  199. SWAPOUT PROC NEAR
  200.         POP    CS:RETADDR        ;save callers address
  201.         POP    BP            ;restore all registers
  202.         POP    DS
  203.         POP    ES
  204.         POP    DI
  205.         POP    SI
  206.         POP    DX
  207.         POP    CX
  208.         POP    BX
  209.         POP    AX
  210.         MOV    SS,CS:THEIR_SS        ;restore callers stack
  211.         MOV    SP,CS:THEIR_SP
  212.         JMP    CS:RETADDR        ;return to caller
  213. SWAPOUT ENDP
  214.  
  215. ;-----------------------------------------------------------------
  216. ; GETPOS, CURSOR_HOME, RESTORE_CURSOR, SETPOS - cursor routines
  217. ;-----------------------------------------------------------------
  218. GETPOS PROC NEAR
  219.         MOV    AH,3            ;get cursor position
  220.         XOR    BH,BH            ;active page
  221.         INT    10H            ;get cursor position in dx
  222.         MOV    CURSOR_POS,DX        ;  and save
  223.         RET
  224. GETPOS ENDP
  225. ;----------------------------------------------------------------------
  226. CURSOR_HOME    PROC    NEAR
  227.         XOR    DX,DX            ;position 0,0
  228.         CALL    SETPOS            ;set cursor position
  229.         RET
  230. CURSOR_HOME    ENDP
  231.  
  232. ;----------------------------------------------------------------------
  233. RESTORE_CURSOR PROC NEAR
  234.         MOV    DX,CURSOR_POS        ;saved position
  235.         CALL    SETPOS            ;set
  236.         RET
  237. RESTORE_CURSOR ENDP
  238. ;----------------------------------------------------------------------
  239. SETPOS PROC NEAR
  240.         MOV    AH,2            ;set cursor position
  241.         XOR    BH,BH            ;active page
  242.         INT    10H            ;set cursor position to dx
  243.         RET
  244. SETPOS ENDP
  245.  
  246. ;-----------------------------------------------------------------
  247. ; SAVE_SCREEN, CLEAR_SCREEN, RESTORE_SCREEN - screen routines
  248. ;-----------------------------------------------------------------
  249. SAVE_SCREEN PROC NEAR
  250.         PUSH    DS            ;save data segment
  251.         XOR    AX,AX
  252.         MOV    BX,AX
  253.         CALL    CALC_SCRN_ADDR        ;address of (0,0)
  254.         MOV    SI,OFFSET SCREEN    ;buffer is past end of table
  255.         PUSH    DS            ;exchange
  256.         PUSH    ES            ;ds and es
  257.         POP    DS
  258.         POP    ES
  259.         XCHG    DI,SI            ;exchange source,destination
  260.         MOV    BX,25            ;save 25 lines
  261. SAVE_NEXT_LINE:
  262.         MOV    CX,80            ;save 80 words per line
  263.         REP    MOVSW            ;save line
  264.         ADD    SI,CS:DIFF        ;add extra characters and
  265.         ADD    SI,CS:DIFF        ;  attributes to SI
  266.         DEC    BX            ;decrement line counter
  267.         JNZ    SAVE_NEXT_LINE
  268.         POP    DS            ;restore data segment
  269.         RET
  270. SAVE_SCREEN ENDP
  271. ;-----------------------------------------------------------------
  272. CLEAR_SCREEN PROC NEAR
  273.         XOR    AX,AX
  274.         MOV    BX,AX
  275.         CALL    CALC_SCRN_ADDR        ;address of (0,0)
  276.         MOV    AX,0720H        ;space with normal attribute
  277.         MOV    BX,25            ;clear 25 lines
  278. CLEAR_NEXT_LINE:
  279.         MOV    CX,80            ;clear 80 words per line
  280.         REP    STOSW            ;clear line
  281.         ADD    DI,CS:DIFF        ;add extra characters and
  282.         ADD    DI,CS:DIFF        ;  attributes to SI
  283.         DEC    BX            ;decrement line counter
  284.         JNZ    CLEAR_NEXT_LINE
  285.         RET
  286. CLEAR_SCREEN ENDP
  287. ;-----------------------------------------------------------------
  288. RESTORE_SCREEN PROC NEAR
  289.         XOR    AX,AX
  290.         MOV    BX,AX
  291.         CALL    CALC_SCRN_ADDR        ;address of (0,0)
  292.         MOV    SI,OFFSET SCREEN    ;buffer past end of table
  293.         MOV    BX,25            ;restore 25 lines
  294. RESTORE_NEXT_LINE:
  295.         MOV    CX,80            ;restore 80 words per line
  296.         REP    MOVSW            ;restore line
  297.         ADD    DI,CS:DIFF        ;add extra characters and
  298.         ADD    DI,CS:DIFF        ;  attributes to SI
  299.         DEC    BX            ;decrement line counter
  300.         JNZ    RESTORE_NEXT_LINE    ;25 lines
  301.         RET
  302. RESTORE_SCREEN ENDP
  303.  
  304. ;------------------------------------------------------------------
  305. ; CALC_SCRN_ADDR
  306. ; ax = row bx= col, returns es:di pointing to the screen address
  307. ;------------------------------------------------------------------
  308. CALC_SCRN_ADDR PROC NEAR
  309.         PUSH    CX            ;save CX
  310.         PUSH    DX            ;save DX (mul destroys DX)
  311.         PUSH    AX            ;save AX for later
  312.         MOV    ES,CS:BIOS_SEG        ;bios data segment into ES
  313.         MOV    AX,0B800H        ;scrn seg = b800h (assume color)
  314.         CMP    ES:BYTE PTR[49H],7    ;40:49 = 7?
  315.         JNZ    COLOR            ;  no, color
  316.         MOV    AH,0B0H         ;  yes, mono, scrn seg = b000h
  317. COLOR:
  318.         MOV    ES,AX            ;scrn seg into es
  319.         POP    CX            ;get row
  320.         MOV    AX,160            ;160 bytes to a row of text
  321.         ADD    AX,CS:DIFF        ;# characters > 80
  322.         ADD    AX,CS:DIFF        ;# of attribute bytes > 80
  323.         MUL    CX            ;row * 160 + diff*2
  324.         ADD    AX,BX            ;+col
  325.         ADD    AX,BX            ;row*160 + col*2
  326.         MOV    DI,AX            ;es:di points to right address
  327.         POP    DX            ;restore registers
  328.         POP    CX
  329.         RET                ;that's all
  330. CALC_SCRN_ADDR ENDP
  331.  
  332. ;----------------------------------------------------------------------
  333. ; PROGRAM
  334. ; Find the first MCB by scanning through memory.  All MCBs have the
  335. ; character "M" as the first byte (except the last MCB, which has "Z"
  336. ; as the first byte.  The second and third bytes give the segment
  337. ; address of the PSP block of the "owner" (the program which allocated
  338. ; the memory block).  The fourth and fifth bytes give the length of
  339. ; the block in paragraphs.  The first block is COMMAND.COM, which
  340. ; follows such things as the operating system and device drivers.
  341. ; On entry, AX=Memory block Address, BX=ES, CX=Owner.
  342. ; On exit, ES points to the first valid MCB.
  343. ;----------------------------------------------------------------------
  344. PROGRAM PROC NEAR
  345.         XOR    BX,BX            ;Zero BX
  346. SRCH_MEM:
  347.                 MOV     ES,BX                   ;Point ES to next paragraph
  348.  
  349.                 CMP     BYTE PTR ES:[0],'M'     ;Is this a MCB?
  350.                 JE      CHECK_MCB               ; might be
  351. CRAWL:
  352.                 INC     BX                      ;Point to next paragraph
  353.         JMP    SRCH_MEM        ; continue search
  354. CHECK_MCB:
  355.                 MOV     AX,BX                   ;Point AX to next paragraph
  356.                 INC     AX                      ; (possible 1st memory block)
  357.                 MOV     CX,WORD PTR ES:[1]      ;If first block is COMMAND.COM,
  358.                 CMP     AX,CX                   ; it will "own" itself
  359.         JNE    CRAWL            ;If not, continue search
  360. FOUND_FIRST:
  361.                 MOV     DI,OFFSET TABLE         ;Table offset in DI
  362.  
  363. ;  Add an entry to the table.
  364. ;       If the owner=0, then this block is unallocated (free).
  365. ;    AX=Mem Address, BX=ES=MCB address, CX=Owner.
  366. ;       DI points to 1st empty spot in table.
  367.  
  368. CREATE_ENTRY:
  369.                 INC     N_BLK                   ;Adding new entry
  370.                 MOV     WORD PTR [DI],AX        ;Put block addr in table
  371.                 MOV     WORD PTR [DI+2],CX      ;Put owner in table
  372.                 MOV     SI,WORD PTR ES:[3]      ;Put block length in
  373.                 MOV     WORD PTR [DI+4],SI      ; table via SI
  374.                 MOV     SI,OFFSET SPACE3_MSG    ;Blanks in type column
  375.                 MOV     CX,3                    ;String length
  376.                 ADD     DI,6                    ;Address in table
  377.                 CALL    COPY_NAME               ;Move blanks to table
  378.                 MOV     CX,WORD PTR ES:[1]      ;Owner segment back into CX
  379.                 OR      CX,CX                   ;If owner <> 0, determine 
  380.                 JNZ     HAVE_OWNER              ; type and find owner name.
  381.                 MOV     CX,6                    ; Else set string length,
  382.                 MOV     SI,OFFSET FREE_MSG      ; point SI to "(Free)",
  383.                 CALL    COPY_NAME               ; and copy to table
  384.                 JMP     FIND_NEXT               ;Next memory block
  385.  
  386. ;  Is this block a PSP (program) block?
  387.  
  388. HAVE_OWNER:
  389.         CMP    AX,CX            ;Is mem = owner?
  390.                 JNE     FIND_NAME               ;No, not PSP block, jump
  391.                 SUB     DI,4                    ;Yes, set table destination
  392.                 MOV     SI,OFFSET PSP_MSG       ;Point SI to "PSP" string
  393.                 MOV     CX,3                    ;String length
  394.                 CALL    COPY_NAME               ;Move string to table
  395.  
  396. ;  If this is first block, it is COMMAND.COM.
  397.  
  398. FIND_NAME:
  399.         CMP    N_BLK,1         ;If not first block
  400.                 JNE     FIND_ENV                ; look for environment
  401.                 MOV     SI,OFFSET COM_MSG       ;Point to "COMMAND.COM" string
  402.                 MOV     CX,11                   ;String length
  403.                 CALL    COPY_NAME               ;Put name in table
  404.                 JMP     FIND_NEXT               ;Next memory block
  405.  
  406. ;  Word at offset 2Ch into the owner's PSP block contains the 
  407. ;  environment segment address.
  408.  
  409. FIND_ENV:
  410.                 MOV     CX,WORD PTR ES:[1]      ;Owner segment into CX
  411.                 MOV     ES,CX                   ; and ES
  412.                 CMP     CX,WORD PTR DS:[TABLE]  ;Is owner COMMAND.COM?
  413.                 JNE     NOT_COMMAND             ;No, jump
  414.         CMP    N_BLK,2            ;2nd block?
  415.         JNE    NOT_ENV            ;No, jump
  416.         MOV    SI,OFFSET ENV_MSG    ;Yes, is system environment
  417.         MOV    CX,3            ;String length
  418.         SUB    DI,4            ;Restore destination
  419.         CALL    COPY_NAME        ;Move string to table
  420. NOT_ENV:
  421.                 MOV     SI,OFFSET COM_MSG       ;"COMMAND.COM" to table
  422.                 MOV     CX,11
  423.                 CALL    COPY_NAME
  424.                 JMP     FIND_NEXT        ;Next memory block
  425.  
  426. ;  Is this block an ENV (environment) block?
  427.  
  428. NOT_COMMAND:
  429.         MOV    SI,WORD PTR ES:[2Ch]    ;Get owner's env segment
  430.                 CMP     AX,SI                   ;Is this block owner's env?
  431.                 JNZ     EXTR_NAME               ;No, jump
  432.                 PUSH    SI                      ;Save SI (env segment)
  433.                 MOV     SI,OFFSET ENV_MSG       ;Point SI to "ENV" string
  434.                 MOV     CX,3                    ;String length
  435.                 SUB     DI,4                    ;Restore destination
  436.                 CALL    COPY_NAME               ;Move string to table
  437.                 POP     SI                      ;Restore SI
  438.  
  439. ;  Get name from environment (if DOS 3.x or later)
  440.  
  441. EXTR_NAME:
  442.         CMP    VER3,0            ;If not 3.x
  443.                 JE      NO_ENV                  ; skip this section
  444.  
  445. ;  Is env still allocated to owner of current block?
  446.  
  447.                 MOV     CX,ES                   ;Owner segment to CX
  448.                 DEC     SI                      ;Point to env MCB
  449.                 PUSH    SI                      ;and put in DS
  450.                 POP     DS
  451.                 CMP     CX,WORD PTR DS:[1]      ;Compare owners
  452.                 JNZ     NO_ENV                  ;Not our property
  453.  
  454. ;  The environment block terminates with two zero bytes.  In DOS 3.0 
  455. ;  and later, the double zero is followed by a string count (two bytes)
  456. ;  and the fully qualified file name of the owner program, terminated
  457. ;  by a zero byte.
  458. ;  Point DS:SI to the environment and scan for the double zero entry.
  459.  
  460.         INC    SI            ;Point SI to environment
  461.                 PUSH    SI                      ; and put in DS
  462.                 POP     DS
  463.                 XOR     SI,SI                   ;DS:SI = ENV:0
  464.                 INC     SI
  465. SCAN_ENV:
  466.                 DEC     SI                      ;Backup one byte, SI=SI-1
  467.                 LODSW                           ;Look at word, SI=SI+2
  468.                 OR      AX,AX                   ;If not double 0 byte
  469.                 JNZ     SCAN_ENV                ;Continue to look
  470.  
  471. ;  Find the end of the program pathname.
  472.  
  473.         LODSW                ;Skip a word (string count)
  474.                 MOV     BP,SI                   ;SI points to 1st char
  475.                 DEC     BP             ;BP points before 1st char
  476. SCAN_PATH:
  477.                 LODSB                           ;Read char at SI
  478.                 OR      AL,AL                   ;If 0, end of string
  479.                 JNZ     SCAN_PATH               ; else continue reading
  480.  
  481. ;  SI points past the terminating 0.  Scan backwards for the \.
  482.  
  483.                 DEC     SI                      ;Point SI and CX to the
  484.                 MOV     CX,SI                   ; zero byte past last char
  485. SCAN_NAME:
  486.                 DEC     SI                      ;Point to char
  487.                 CMP     SI,BP                   ;Is it before 1st char?
  488.                 JE      STRING_START
  489.                 CMP     BYTE PTR [SI],'\'       ;It is backslash?
  490.                 JNE     SCAN_NAME               ; no, continue
  491. STRING_START:
  492.                 INC     SI                      ;Point to start of string
  493.                 SUB     CX,SI                   ;Length of string
  494.                 CALL    COPY_NAME               ;Transfer to table
  495.                 JMP     FIND_NEXT        ;Next memory block
  496. NO_ENV:
  497.                 PUSH    CS                      ;Restore DS
  498.                 POP     DS
  499.                 MOV     CX,9                    ;Number of chars
  500.                 MOV     SI,OFFSET UNK_MSG       ;Point to "Unknown" string
  501.                 CALL    COPY_NAME               ;Transfer to table
  502.  
  503. ;  Point ES to next MCB and continue search.  Stop at top of memory.
  504.  
  505. FIND_NEXT:
  506.                 PUSH    CS                      ;Restore DS
  507.                 POP     DS
  508.         CMP    LAST_BLOCK,1        ;was this last block?
  509.         JE    NO_MORE         ;  yes, done
  510.         CMP    N_BLK,MAX_BLK        ;out of space?
  511.         JE    OUT_OF_SPACE        ;  yes, jump
  512.                 MOV     ES,BX                   ;ES to current MCB
  513.                 ADD     BX,WORD PTR ES:[3]      ;BX to next MCB
  514.                 INC     BX
  515.                 MOV     ES,BX                   ;ES too
  516.         CMP    BYTE PTR ES:[0],'Z'    ;is this last block?
  517.         JNE    MORE_BLOCKS        ;  no, jump
  518.         INC    LAST_BLOCK        ;  yes, set flag
  519. MORE_BLOCKS:
  520.         MOV    AX,BX            ;Put address of block
  521.                 INC     AX                      ; in AX
  522.                 MOV     DI,OFFSET TABLE         ;Find
  523.                 MOV     CL,N_BLK                ; address
  524.                 XOR     CH,CH                   ; of
  525. ADDEM:                                          ; next
  526.         ADD    DI,23            ; table
  527.                 LOOP    ADDEM                   ; entry
  528.                 MOV     CX,WORD PTR ES:[1]      ;Block length in CX
  529.         JMP    CREATE_ENTRY        ;Continue with next entry
  530. OUT_OF_SPACE:
  531.         INC    TABLE_FULL        ;set flag for out of space msg
  532.  
  533. ;  Display the resulting table on the screen.
  534.  
  535. NO_MORE:
  536.         MOV    SI,OFFSET HEADING_MSG    ;Display the heading
  537.         CALL    DISPLAY_STRING
  538.         MOV    SI,OFFSET TABLE     ;Table location
  539.         CALL    PRINT_WORD        ;1st table entry is address
  540.                         ;of COMMAND.COM = size of
  541.                         ;initial memory block
  542.         MOV    SI,OFFSET BLOCK1_MSG    ;Display 1st block description
  543.         CALL    DISPLAY_STRING
  544.  
  545.                 MOV     SI,OFFSET TABLE         ;Table location
  546.                 MOV     CL,N_BLK                ;Number of entries
  547.                 XOR     CH,CH                   ; as a word
  548. PRINT_TABLE:
  549.                 CALL    PRINT_WORD              ;Print address
  550.                 CALL    PRINT_WORD              ; and owner
  551.         CALL    PRINT_WORD        ; and size
  552.         CALL    DISPLAY_STRING        ;Print type
  553.         PUSH    SI            ;save SI
  554.         MOV    SI,OFFSET SPACE_MSG    ;Space over
  555.         CALL    DISPLAY_STRING
  556.         POP    SI            ;restore SI
  557.         ADD    SI,4            ;Point to owner name
  558.         CALL    DISPLAY_STRING        ;and print
  559.         PUSH    SI            ;save SI
  560.         MOV    SI,OFFSET CR_LF_MSG    ;Newline
  561.         CALL    DISPLAY_STRING
  562.         POP    SI            ;restore SI
  563.         ADD    SI,13            ;point to start of next entry
  564.                         ;pause if CTRL-S pressed
  565.         MOV    AH,1            ;keystroke waiting?
  566.         INT    16H
  567.         JZ    LOOP_NOW        ;no, proceed
  568.         MOV    AH,0            ;get keystroke scan code
  569.         INT    16H
  570.         CMP    AH,31            ;is it S?
  571.         JNE    LOOP_NOW        ;no, proceed
  572.         MOV    AH,2            ;get shift status
  573.         INT    16H
  574.         AND    AL,0FH            ;mask off status of toggles
  575.         CMP    AL,4            ;CTRL depressed?
  576.         JNE    LOOP_NOW        ;no, proceed
  577.         MOV    AH,0            ;yes, wait for next keystroke
  578.         INT    16H
  579. LOOP_NOW:
  580.         LOOP    PRINT_TABLE        ;print next entry
  581.         CMP    TABLE_FULL,0        ;is table full?
  582.         JE    DONE              ; no, jump
  583.         MOV    SI,OFFSET TABLE_FULL_MSG  ; yes, print out of space msg
  584.         CALL    DISPLAY_STRING
  585. DONE:
  586.         MOV    N_BLK,0         ;reset counter
  587.         MOV    LAST_BLOCK,0        ;and flag
  588.         MOV    SI,OFFSET PROGRAM_ID    ;print progam ID
  589.         CALL    DISPLAY_STRING
  590.         CMP    TSR_MODE,1        ;TSR mode?
  591.         JNE    PROGRAM_EXIT        ;  no, exit now
  592.         MOV    SI,OFFSET HIT_ANY_KEY    ;  yes, print message
  593.         CALL    DISPLAY_STRING
  594.         XOR    AH,AH            ;wait for keystroke
  595.         INT    16H
  596. PROGRAM_EXIT:
  597.         RET
  598. PROGRAM ENDP
  599.  
  600. ;----------------------------------------------------------------------
  601. ; COPY_NAME
  602. ; Move string at DS:SI to CS:DI, string length in CX, add 0 at end
  603. ;----------------------------------------------------------------------
  604. COPY_NAME       PROC    NEAR
  605.                 PUSH    AX                      ;Save AX
  606.                 PUSH    ES                      ;Save ES
  607.                 PUSH    CS                      ;Point ES to
  608.                 POP     ES                      ; this segment
  609.                 REP     MOVSB                   ;Put name in table
  610.         MOV    AL,0            ;string terminator
  611.                 STOSB                           ;Store it in table
  612.                 POP     ES                      ;Restore ES
  613.                 POP     AX                      ;Restore AX
  614.                 RET
  615. COPY_NAME       ENDP
  616.  
  617. ;----------------------------------------------------------------------
  618. ; PRINT_WORD - Print hex value of word at DS:SI, followed by spaces
  619. ;----------------------------------------------------------------------
  620. PRINT_WORD      PROC    NEAR
  621.  
  622.                 LODSW                           ;Get value
  623.         CALL    HEX4            ;Write 4 digits
  624.         PUSH    SI            ;save SI
  625.         MOV    SI,OFFSET SPACE_MSG    ;space over
  626.         CALL    DISPLAY_STRING
  627.         POP    SI            ;restore SI
  628.                 RET
  629.  
  630. PRINT_WORD      ENDP
  631.  
  632. ;----------------------------------------------------------------------
  633. ;  HEX4 - Write AX as 4 hex digits to console
  634. ;  HEX2 - Write AL as 2 hex digits to console
  635. ;-----------------------------------------------------------------------------
  636. HEX4            PROC    NEAR
  637.  
  638.                 PUSH    AX                      ;Save register
  639.                 MOV     AL,AH                   ;Show high digits first
  640.                 CALL    HEX2                    ;Display AL
  641.                 POP     AX                      ;Restore low digits in AL
  642.  
  643. HEX2            PROC    NEAR                    ;Display AL
  644.  
  645.                 PUSH    AX                      ;Save register
  646.                 PUSH    CX                      ;Save CX during shift
  647.                 MOV     CL,4
  648.                 SHR     AL,CL                   ;Get high 4 bits
  649.                 POP     CX                      ;Restore CX
  650.  
  651.                 CALL    H2C                     ;Display upper AL digit
  652.                 POP     AX                      ;Restore lower
  653.                 AND     AL,0FH                  ;Mask and display
  654. H2C:
  655.                 ADD     AL,90H                  ;Convert AL to ASCII
  656.                 DAA
  657.                 ADC     AL,40H
  658.                 DAA
  659.  
  660.         MOV    AH,0EH            ;Display character
  661.         XOR    BH,BH
  662.         INT    10H
  663.  
  664.                 RET
  665.  
  666. HEX2            ENDP
  667. HEX4            ENDP
  668.  
  669. ;------------------------------------------------------------------
  670. ; DISPLAY_STRING - displays string at ds:si
  671. ;------------------------------------------------------------------
  672. DISPLAY_STRING    PROC    NEAR
  673.         PUSH    SI            ;save registers
  674.         PUSH    AX
  675.         PUSH    BX
  676. NEXT_CHAR:
  677.         LODSB                ;get character
  678.         OR    AL,AL            ;is it zero?
  679.         JZ    LAST_CHAR        ;  yes, done
  680.         MOV    AH,0EH            ;print character
  681.         XOR    BH,BH            ;page 0
  682.         INT    10H
  683.         JMP    NEXT_CHAR
  684. LAST_CHAR:
  685.         POP    BX            ;restore registers
  686.         POP    AX
  687.         POP    SI
  688.         RET
  689. DISPLAY_STRING    ENDP
  690.  
  691. ;----------------------------------------------------------------------
  692. ; INT10H
  693. ; this routine sets bit to prevent popping up while int 10h is active
  694. ;----------------------------------------------------------------------
  695. INT10H PROC FAR
  696.         OR    CS:BUSY,INT10_BUSY    ;set bit
  697.         PUSHF                ;push flags to simulate INT
  698.         CALL    CS:ADDR_INT10H        ;call original int 10h
  699.         PUSHF                ;save flags
  700.         AND    CS:BUSY,NOT(INT10_BUSY) ;clear bit
  701.         POPF                ;restore flags
  702.         RET    2            ;return from int 10h
  703. INT10H ENDP
  704.  
  705. ;----------------------------------------------------------------------
  706. ; end of resident code, and start of memory used for table, screen save
  707. ; buffer, and stack.  each table entry has structure:
  708. ;    address dw    ?
  709. ;    owner    dw    ?
  710. ;    size    dw    ?
  711. ;    type    db    "XXX",0
  712. ;    name    db    "FILENAME.EXT",0
  713. ; (total 23 bytes per entry)
  714. ;----------------------------------------------------------------------
  715. TABLE DB (MAX_BLK*23) DUP (20H)     ;reserve space for table
  716. SCREEN    LABEL    BYTE            ;marks start of memory used
  717.  
  718. ;----------------------------------------------------------------------
  719. ; TRANSIENT DATA AREA
  720. ;----------------------------------------------------------------------
  721. INSTALLED        DB        "PCMAP installed"
  722.             DB        CR,LF,"Hotkey is Alt-P",CR,LF,"$"
  723. UNINSTALLED        DB        "PCMAP Uninstalled",CR,LF,"$"
  724. DISABLED        DB        CR,LF,"PCMAP is disabled",CR,LF,"$"
  725. ENABLED         DB        CR,LF,"PCMAP is re-enabled",CR,LF,"$"
  726.  
  727. INSTALLED_SEGMENT   DW        0            ;addr of resident copy
  728.  
  729. START_OFFSET        DW        0            ;used by search_mem
  730. START_SEGMENT        DW        0
  731. END_OFFSET        DW        0
  732. END_SEGMENT        DW        0
  733. SEARCH_PARAS        DW        0
  734. SEARCH_BYTES        DW        0
  735.  
  736. ;-----------------------------------------------------------------
  737. ; RES - code relating to residency
  738. ;-----------------------------------------------------------------
  739. RES        PROC    NEAR
  740.         CLD                ;strings forward
  741.         CALL    CHECK_VER        ;See if DOS vers >=3.0
  742.         MOV    BX,80H            ;ES:BX=command tail
  743.         CALL    ARGV            ;Get 1st argument
  744.         CMP    AX,2            ;If argument length<>2,
  745.         JNE    NO_RES            ;  proceed with program
  746.         MOV    AX,ES:[BX]        ;Get argument (bytes reversed)
  747.         AND    AH,0DFH         ;Convert to upper case
  748.         CMP    AX,'R/'         ;If not '/R',
  749.         JNE    NO_RES            ;  proceed with program,
  750.         CALL    PROGRAM_ALREADY_IN    ;  else see if already installed
  751.         JNZ    NOT_IN            ;if not in, it's ok to install
  752.         CALL    UNINSTALL        ;else, try to uninstall
  753.         MOV    AX,4C00H        ;terminate with error code=0
  754.         INT    21H
  755. NO_RES:
  756.         DEC    TSR_MODE        ;command line mode
  757.         CALL    PROGRAM         ;Display memory map
  758.         MOV    AX,4C00H        ;terminate, assume error code=0
  759.         CMP    TABLE_FULL,1        ;out of space error?
  760.         JNE    RES_EXIT        ;  no, jump
  761.         MOV    AL,01            ;  yes, error code=1
  762. RES_EXIT:
  763.         INT    21H
  764. NOT_IN:
  765.         MOV    OUR_SS,CS        ;set stack seg
  766.         MOV    OUR_SP,OFFSET TABLE+(MAX_BLK*23)+4000+256  ;and pointer
  767.                 ;(256 byte stack follows table and scrn buffer)
  768.         CALL    INSTALL
  769.         MOV    DX,OFFSET INSTALLED    ;confirm installation
  770.         MOV    AH,9
  771.         INT    21H
  772.          ;program, table, scrn buf, stack, round up, cnvrt to paras
  773.          MOV  DX,(OFFSET TABLE-OFFSET _TEXT+(MAX_BLK*23)+4000+256+15) SHR 4
  774.         MOV    AX,3100H        ;stay resident, error code=0
  775.         INT    21H
  776. RES ENDP
  777.  
  778. ;--------------------------------------------------------------------
  779. ; CHECK_VER - check DOS version
  780. ;--------------------------------------------------------------------
  781. CHECK_VER    PROC    NEAR
  782.  
  783.                 MOV     AH,30H                  ;Check DOS version
  784.                 INT     21H                     ; Thru DOS
  785.                 CMP     AL,3                    ;If not 3.x or later
  786.                 JB      NOT_3                   ; don't turn on flag
  787.                 INC     VER3                    ; else, indicate
  788. NOT_3:
  789.         RET
  790. CHECK_VER    ENDP
  791.  
  792. ;--------------------------------------------------------------------
  793. ; ARGV
  794. ; Call with:  ES:BX = command line address
  795. ;              (implicit: ES=PSP segment, BX=80h)
  796. ;
  797. ; Returns:    ES:BX = argument address (first argument)
  798. ;             AX    = argument length 
  799. ;                     (0=argument not found)
  800. ;             Other registers preserved.
  801. ;--------------------------------------------------------------------
  802. ARGV    PROC    NEAR        ; get address & length of
  803.                                 ; command tail argument
  804.  
  805.     PUSH    CX        ; save original CX and DI
  806.     PUSH    DI
  807.  
  808. ARGV1:
  809. ARGV2:    INC    BX        ; point to next character
  810.     CMP    BYTE PTR ES:[BX],CR
  811.     JE    ARGV7        ; exit if carriage return
  812.     CMP    BYTE PTR ES:[BX],BLANK
  813.     JE    ARGV1        ; outside argument if ASCII blank
  814.     CMP    BYTE PTR ES:[BX],TAB
  815.     JE    ARGV1        ; outside argument if ASCII tab
  816.  
  817. ARGV4:                ; found desired argument, now
  818.                                 ; determine its length...
  819.     MOV    AX,BX        ; save param. starting address
  820.  
  821. ARGV5:    INC    BX        ; point to next character
  822.     CMP    BYTE PTR ES:[BX],CR
  823.     JE    ARGV6        ; found end if carriage return
  824.     CMP    BYTE PTR ES:[BX],BLANK
  825.     JE    ARGV6        ; found end if ASCII blank
  826.     CMP    BYTE PTR ES:[BX],TAB
  827.     JNE    ARGV5        ; found end if ASCII tab
  828.  
  829. ARGV6:    XCHG    BX,AX        ; set ES:BX = argument address
  830.     SUB    AX,BX        ; and AX = argument length
  831.     JMP    ARGVX        ; return to caller
  832.  
  833. ARGV7:    XOR    AX,AX        ; set AX = 0, argument not found
  834.     JMP    ARGVX        ; return to caller
  835.  
  836. ARGVX:                ; common exit point
  837.     POP    DI        ; restore original CX and DI
  838.     POP    CX
  839.     RET            ; return to caller
  840.  
  841. ARGV    ENDP
  842.  
  843. ;------------------------------------------------------------------
  844. ; PROGRAM_ALREADY_IN - determine if program is already installed.
  845. ; returns zero flag = 1 if installed.
  846. ;------------------------------------------------------------------
  847. PROGRAM_ALREADY_IN  PROC NEAR
  848.         NOT    WORD PTR START        ;mark this program as active
  849.         MOV    START_SEGMENT,60H    ;start after dos
  850.         MOV    START_OFFSET,0        ;
  851.         MOV    END_SEGMENT,CS        ;stop looking before you
  852.         MOV    END_OFFSET,0        ; get to this program
  853.         MOV    SI,OFFSET START     ;start compare at modified byte
  854.                         ;  (a previously installed copy
  855.                         ;  will also have modified byte)
  856.         MOV    CX,25            ;compare 25 bytes
  857.         CALL    SEARCH_MEM        ;search
  858.         PUSHF                ;save zr flag
  859.         MOV    AX,START_SEGMENT    ;get address of find
  860.         MOV    INSTALLED_SEGMENT,AX    ;save in installed address
  861.         MOV    AX,START_OFFSET
  862.         MOV    CL,4
  863.         SHR    AX,CL
  864.         SUB    AX,10H            ;adjust for psp
  865.         ADD    INSTALLED_SEGMENT,AX
  866.         POPF                ;restore flgs from search
  867.         RET
  868. PROGRAM_ALREADY_IN  ENDP
  869.  
  870. ;-----------------------------------------------------------------
  871. ; SEARCH_MEM
  872. ; DS:SI = search string  CX = string_size
  873. ; search for match of string beginning at START_SEGMENT:START_OFFSET,
  874. ; and ending at END_SEGMENT:END_OFFSET.  if found, zero flag set,
  875. ; START_SEGMENT:START_OFFSET points to find.
  876. ;-----------------------------------------------------------------
  877. SEARCH_MEM  PROC NEAR
  878.         MOV    DI,CX            ;save string size
  879.         CALL    END_MINUS_START        ;calculate search length
  880. LOOK_AGAIN:
  881.         CMP    SEARCH_PARAS,1000H    ;more than or equal 64k?
  882.         JAE    MORE_THAN_ENOUGH    ;if so, search 64k
  883.         MOV    AX,SEARCH_PARAS     ;otherwise, get what's left
  884.         MOV    CL,4            ;
  885.         SHL    AX,CL            ;segs*16 = bytes to search
  886.         ADD    AX,SEARCH_BYTES        ;add in the last few bytes
  887.         JMP    SHORT LOOK        ;and go look
  888. MORE_THAN_ENOUGH:
  889.         MOV    AX,0FFFFh        ;64K-1 bytes to search
  890. LOOK:                       
  891.         SUB    AX,BX            ;subtract initial offset
  892.         JB    SEARCH_NOT_FOUND    ;offset < search size?
  893.         CMP    AX,DI            ;compare to string size
  894.         JB    SEARCH_NOT_FOUND    ;less than search size?
  895.         MOV    DX,AX            ;dx gets search size
  896.         MOV    CL,4            ;
  897.         SHR    DX,CL            ;number of segments to search
  898.         SUB    SEARCH_PARAS,DX     ;decrease the amount to search
  899.                         ;si = search string  di = size
  900.                         ;es:bx=start addr
  901.         CALL    SEARCH            ;ax=bytes to search
  902.         JZ    SEARCH_FOUND        ;if zero flag, string is found
  903.         ADD    AX,1            ;next character after fail
  904.         MOV    BX,AX            ;into es:bx
  905.         JNC    NOWR            ;if offset rolls over
  906.         MOV    AX,ES            ;add 64k
  907.         ADD    AX,1000H        ;to the
  908.         MOV    ES,AX            ;offset
  909. NOWR:
  910.         CALL    NORMALIZE        ;change ES:BX so that
  911.                         ;  10h > BX >= 0
  912.         JMP    LOOK_AGAIN
  913. SEARCH_NOT_FOUND:
  914.         XOR    AX,AX            ;start over
  915.         MOV    ES,AX
  916.         CMP    AL,1            ;clear zero flag
  917. SEARCH_FOUND:
  918.         MOV    START_SEGMENT,ES    ;set address of found string
  919.         MOV    START_OFFSET,AX
  920.         RET
  921. SEARCH_MEM ENDP
  922.  
  923. ;-----------------------------------------------------------------
  924. ; END_MINUS_START
  925. ; using START_OFFSET, START_SEGMENT, END_OFFSET, and END_SEGMENT,
  926. ; return SEARCH_BYTES, SEARCH_PARAS, and normalized pointer to
  927. ; starting address in ES:BX
  928. ;-----------------------------------------------------------------
  929. END_MINUS_START PROC NEAR
  930.         LES    BX,DWORD PTR START_OFFSET    ;start addr in ES:BX
  931.         CALL    NORMALIZE        ;change es:bx so 10h > bx >=0
  932.         MOV    AX,ES            ;save normalized result
  933.         MOV    CX,BX            ;for later use
  934.         LES    BX,DWORD PTR END_OFFSET    ;get end address
  935.         CALL    NORMALIZE        ;change es:bx so 10h > bx >=0
  936.         MOV    DX,ES            ;get end segment
  937.         SUB    DX,AX            ;calculate paragraphs to search
  938.         MOV    SEARCH_PARAS,DX     ;and save
  939.         MOV    SEARCH_BYTES,BX     ;# bytes after final paragraph
  940.         MOV    ES,AX            ;set es:bx to
  941.         MOV    BX,CX            ;  start address
  942.         RET                ;that's all
  943. END_MINUS_START ENDP
  944.  
  945. ;-----------------------------------------------------------------
  946. ; NORMALIZE
  947. ; make 20 bit pointer in es:bx from segment:offset in es:bx,
  948. ; i.e. adjust ES:BX to point to same absolute address, but with
  949. ; 10h > BX >= 0
  950. ;-----------------------------------------------------------------
  951. NORMALIZE PROC NEAR
  952.         PUSH    AX            ;save registers
  953.         PUSH    CX
  954.         PUSH    DX
  955.         MOV    AX,BX            ;get the offset
  956.         MOV    CL,4            ;make into
  957.         SHR    AX,CL            ;number of paragraphs
  958.         MOV    DX,ES            ;get segment
  959.         ADD    DX,AX            ;add in number of paragraphs
  960.         MOV    ES,DX            ;back into segment
  961.         SHL    AX,CL            ;calc offset into segment
  962.         SUB    BX,AX            ;  (BX mod 16)
  963.         POP    DX            ;restore registers
  964.         POP    CX
  965.         POP    AX
  966.         RET
  967. NORMALIZE ENDP
  968.  
  969. ;-----------------------------------------------------------------
  970. ; SEARCH
  971. ; si = search string di = string size es:bx = pointer to buffer to search
  972. ; ax = number of bytes in buffer to search.  If found, zero flag set, and
  973. ; es:bx points to found string.  If not found, zero flag cleared, and es:bx
  974. ; points to last first byte checked.
  975. ;-----------------------------------------------------------------
  976. SEARCH PROC NEAR
  977.         PUSH    BX
  978.         PUSH    DI
  979.         PUSH    SI
  980.         XCHG    BX,DI        ;bx=string size, es:di=ptr to data area
  981.         MOV    CX,AX        ;# chars in segment to search
  982. BYTE_ADD:
  983.         LODSB            ;char for first part of search
  984. NEXT_SRCH:
  985.         REPNZ    SCASB        ;is first char in string in buffer
  986.         JNZ    NOT_FOUND    ;if not, no match
  987.         PUSH    DI        ;save against cmpsb
  988.         PUSH    SI
  989.         PUSH    CX
  990.         LEA    CX,[BX-1]    ;# chars in string - 1 (CX=BX-1)
  991.         JCXZ    ONE_CHAR    ;if one char search, we have found it
  992.         REP    CMPSB        ;otherwise compare rest of string
  993. ONE_CHAR:
  994.         POP    CX        ;restore for next cmpsb
  995.         POP    SI
  996.         POP    DI
  997.         JNZ    NEXT_SRCH    ;if zr = 0 then string not found
  998. NOT_FOUND:
  999.          LEA    AX,[DI-1]    ;ES:AX=ptr to last first character found
  1000.                     ;  (AX=DI-1)
  1001.          POP    SI        ;restore registers
  1002.          POP    DI
  1003.          POP    BX
  1004.          RET
  1005. SEARCH ENDP
  1006.  
  1007. ;------------------------------------------------------------------
  1008. ; UNINSTALL - removes resident program from memory if possible.  if not,
  1009. ; toggles the disable flag
  1010. ;------------------------------------------------------------------
  1011. UNINSTALL PROC NEAR
  1012.         CALL    HOOKED_VECTORS_SAME?    ;if all vectors still hooked
  1013.         JZ    UNINSTALL_OK        ;go ahead and uninstall
  1014.  
  1015.         MOV    ES,INSTALLED_SEGMENT    ;else, change the disable flag
  1016.         NOT    ES:DISABLE        ;in the installed program
  1017.         MOV    DX,OFFSET ENABLED    ;get the message corresponding
  1018.         CMP    ES:DISABLE,-1        ;to the action that causes
  1019.         JNZ    ITS_DISABLED        ;
  1020.         MOV    DX,OFFSET DISABLED    ;
  1021. ITS_DISABLED:                    ;
  1022.         MOV    AH,9            ;and display that message
  1023.         INT    21H
  1024.         JMP    SHORT UNINSTALL_EXIT    ;all done here.
  1025. UNINSTALL_OK:
  1026.         MOV    ES,INSTALLED_SEGMENT    ;get resident prog's psp
  1027.         NOT    ES:WORD PTR START    ;mark resident program inactive
  1028.         MOV    DX,ES:WORD PTR ADDR_INT9H    ;restore int 9 vector
  1029.         MOV    DS,ES:WORD PTR ADDR_INT9H+2
  1030.         MOV    AH,25H
  1031.         MOV    AL,9
  1032.         INT    21H
  1033.         MOV    DX,ES:WORD PTR ADDR_INT10H    ;restore int 10h vector
  1034.         MOV    DS,ES:WORD PTR ADDR_INT10H+2
  1035.         MOV    AH,25H
  1036.         MOV    AL,10H
  1037.         INT    21H
  1038.         PUSH    ES
  1039.         MOV    ES,ES:[2CH]        ;get segment of environment
  1040.         MOV    AH,49H            ;belonging to resident program
  1041.         INT    21H            ;free it
  1042.         POP    ES
  1043.         MOV    AH,49H            ;free memory block of program
  1044.         INT    21H
  1045.         PUSH    CS
  1046.         POP    DS            ;get back our data segment
  1047.         MOV    DX,OFFSET UNINSTALLED    ;display message
  1048.         MOV    AH,9
  1049.         INT    21H
  1050. UNINSTALL_EXIT:
  1051.         RET
  1052. UNINSTALL ENDP
  1053.  
  1054. ;------------------------------------------------------------------
  1055. ; HOOKED_VECTORS_SAME?
  1056. ; determine if vectors have changed since program was installed.
  1057. ; if changed, zero flag cleared; if not changed, zero flag set.
  1058. ;------------------------------------------------------------------
  1059. HOOKED_VECTORS_SAME? PROC NEAR
  1060.         MOV    CX,INSTALLED_SEGMENT    ;get executing segment
  1061.         XOR    AX,AX            ;interrupt table segment
  1062.         MOV    ES,AX            ;into the extra segment
  1063.         CMP    CX,ES:[10H*4+2]        ;see if int 10h points at us
  1064.         JNZ    VECTOR_CHANGED
  1065.         CMP    CX,ES:[9*4+2]        ;see if int 9 points at us
  1066. VECTOR_CHANGED:
  1067.         RET
  1068. HOOKED_VECTORS_SAME? ENDP
  1069.  
  1070. ;----------------------------------------------------------------------
  1071. ; INSTALL - links vectors 9h and 10h to our code
  1072. ;----------------------------------------------------------------------
  1073. INSTALL PROC NEAR
  1074.         MOV    CL,9            ;link vector 9
  1075.         MOV    SI,OFFSET ADDR_INT9H
  1076.         MOV    DI,OFFSET INT9H
  1077.         CALL    INSTALL_VECTOR
  1078.         MOV    CL,10H            ;link vector 10h
  1079.         MOV    SI,OFFSET ADDR_INT10H
  1080.         MOV    DI,OFFSET INT10H
  1081.         CALL    INSTALL_VECTOR
  1082.         RET
  1083. INSTALL ENDP
  1084.  
  1085. ;----------------------------------------------------------------------
  1086. ; INSTALL_VECTOR - generic vector-linking routine
  1087. ;----------------------------------------------------------------------
  1088. INSTALL_VECTOR PROC NEAR
  1089.  
  1090.         MOV    AL,CL            ;get vector number
  1091.         MOV    AH,35H            ;get interrupt vector
  1092.         INT    21H            ;
  1093.         MOV    [SI],BX            ;save interrupt vector
  1094.         MOV    [SI+2],ES        ;
  1095.         MOV    DX,DI            ;get replacement address
  1096.         MOV    AH,25H            ;set vector address
  1097.         MOV    AL,CL            ;for vector
  1098.         INT    21H
  1099.         RET
  1100.  
  1101. INSTALL_VECTOR ENDP
  1102.  
  1103. _TEXT ENDS
  1104.         END    START
  1105.