home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0010 - 0019 / ibm0010-0019 / ibm0010.tar / ibm0010 / LITES.ZIP / LITES.ASM next >
Encoding:
Assembly Source File  |  1989-02-22  |  15.8 KB  |  567 lines

  1. ;=============================================================================
  2. ; LITES displays the realtime status of the UART's DTR, RTS, CTS, DSR, DCD,
  3. ; and RI lines as well as its Baud rate and data format settings.  Syntax is:
  4. ;    LITES [comport] [U]
  5. ; where comport is a number indicating which COM port is to be monitored,
  6. ; and U uninstalls the program from memory.
  7. ;=============================================================================
  8.  
  9. ;    Bug Fix:    02/19/88    -    Missing OUT instruction
  10. ;                        in the keyboard RESET routine
  11. ;                        RMG
  12.  
  13.  
  14.  
  15.  
  16. CODE        SEGMENT    PARA PUBLIC 'CODE'
  17.         ASSUME    CS:CODE
  18.         ORG    100H
  19. BEGIN:        JMP    INITIALIZE
  20.  
  21. PROGRAM        DB    "LITES 1.1 (c) 1989 Ziff Communications Co.",13,10
  22. AUTHOR        DB    "PC Magazine ",254," Jeff Prosise",13,10
  23. HOTKEY        DB    "Hotkey is Alt-L",32,32,32,13,10,"$"
  24.  
  25. ;-----------------------------------------------------------------------------
  26. ; Patch points to modify the program's operation.
  27. ;-----------------------------------------------------------------------------
  28. KEYCODE        DB    26H            ;scan code for "L" key
  29. VIDEO_ATTR    DB    70H            ;status line video attribute
  30. ROWNUM        DB    0            ;row number of status line
  31. COL_OFFSET    DB    0            ;offset from right margin
  32. TICKS        DB    2            ;ticks between refreshes
  33. COMPORT        DW    0            ;COM port designator (COM1)
  34.  
  35. ;-----------------------------------------------------------------------------
  36. ; Other program variables.
  37. ;-----------------------------------------------------------------------------
  38. INT09H        DD    ?            ;interrupt 9h vector
  39. INT08H        DD    ?            ;interrupt 8h vector
  40. COUNTER        DB    0FFH            ;refresh counter
  41.  
  42. PINS        DB    ?            ;bit 0 = DTR, 1 = RTS
  43.                         ;    2 = CTS, 3 = DSR
  44.                         ;    4 = RI, 5 = DCD
  45.  
  46. BAUDRATE    DW    ?            ;Baud rate
  47. PARITY        DB    ?            ;parity setting
  48. DATABITS    DB    ?            ;number of data bits
  49. STOPBITS    DB    ?            ;number of stop bits
  50.  
  51. VIDEOSEG    DW    ?            ;video segment address
  52. VIDEOADDR    DW    ?            ;video offset address
  53. SYNCFLAG    DB    ?            ;0 = no video sync, 1 = sync
  54.  
  55. PINTEXT        DB    "DTRRTSCTSDSRRI",32,"DCD"
  56.  
  57. ;=============================================================================
  58. ; KBINT intercepts and handles the keyboard interrupt 9h.
  59. ;=============================================================================
  60. KBINT        PROC    FAR
  61.  
  62.         PUSHF                ;save flags
  63.         PUSH    AX            ;save AX
  64.  
  65.         STI                ;interrupts on
  66.         IN    AL,60H            ;get scan code from keyboard
  67.         CMP    AL,CS:[KEYCODE]        ;exit to BIOS if it's not
  68.         JNE    NOT_HOT            ;  the hotkey
  69.         MOV    AH,2            ;get keyboard shift state
  70.         INT    16H
  71.         AND    AL,0FH            ;mask off the upper 4 bits
  72.         CMP    AL,8            ;exit if Alt isn't held down
  73.         JNE    NOT_HOT
  74.         MOV    AH,0FH            ;get current video mode
  75.         INT    10H
  76.         CMP    AL,7            ;abort if not in text mode
  77.         JE    RESET
  78.         CMP    AL,4
  79.         JB    RESET
  80. NOT_HOT:    POP    AX            ;exit to BIOS int 9 handler
  81.         POPF
  82.         JMP    CS:INT09H
  83. ;
  84. ;Reset the keyboard controller and interrupt controller.
  85. ;
  86. RESET:        CLI                ;interrupts off
  87.         IN    AL,61H            ;reset the keyboard controller
  88.         MOV    AH,AL
  89.         OR    AL,80H
  90.         OUT    61H,AL
  91.         XCHG    AH,AL
  92.         OUT    61H,AL            ; This instruction was
  93.                         ; missing in the orig version
  94.                         ; RMG
  95.         MOV    AL,20H            ;signal EOI to interrupt
  96.         OUT    20H,AL            ;  controller
  97.         STI                ;interrupts back on
  98.         PUSH    BX            ;save registers
  99.         PUSH    CX
  100.         PUSH    DX
  101.         PUSH    SI
  102.         PUSH    DI
  103.         PUSH    DS
  104.         PUSH    ES
  105.         PUSH    BP
  106. ;
  107. ;Deactivate the status line if it is currently displayed.
  108. ;
  109.         CMP    CS:[COUNTER],0FFH    ;branch if status line is
  110.         JE    SETVIDEO        ;  dormant
  111.         MOV    CS:[COUNTER],0FFH    ;disable counter
  112.         MOV    AX,CS            ;point DS:SI to buffer
  113.         MOV    DS,AX
  114.         MOV    SI,OFFSET INITIALIZE
  115.         MOV    ES,CS:[VIDEOSEG]    ;point ES:DI to video memory
  116.         MOV    DI,CS:[VIDEOADDR]
  117.         MOV    CX,32            
  118.         CALL    VIDEO_XFER        ;erase the status line
  119.         JMP    SHORT KB_EXIT
  120. ;
  121. ;Initialize video parameters.
  122. ;
  123. SETVIDEO:    MOV    CS:[VIDEOSEG],0B800H    ;set default video parms
  124.         MOV    CS:[SYNCFLAG],0
  125.         MOV    AX,40H            ;point ES to BIOS data
  126.         MOV    ES,AX            ;  area
  127.         TEST    BYTE PTR ES:[63H],20H    ;set video segment value
  128.         JZ    COLOR            ;  (monochrome or color)
  129.         MOV    CS:[VIDEOSEG],0B000H
  130.         JMP    SHORT NOSYNC
  131. COLOR:        MOV    AH,12H            ;if a color adapter is
  132.         MOV    BL,10H            ;  detected, determine
  133.         INT    10H            ;  whether or not it's
  134.         CMP    BL,10H            ;  a CGA and set SYNCFLAG
  135.         JNE    NOSYNC            ;  if it is
  136.         MOV    CS:[SYNCFLAG],1
  137. NOSYNC:        MOV    AX,ES:[4AH]        ;calculate offset address
  138.         SHL    AX,1            ;  within video segment
  139.         MOV    BL,CS:[ROWNUM]        ;multiply number of columns
  140.         MUL    BL            ;  by row number
  141.         MOV    BX,ES:[4AH]        ;add column offset
  142.         SUB    BL,32
  143.         SUB    BL,CS:[COL_OFFSET]
  144.         SHL    BL,1
  145.         ADD    AX,BX
  146.         ADD    AX,ES:[4EH]        ;add page address
  147.         MOV    CS:[VIDEOADDR],AX    ;store it
  148. ;
  149. ;Save the portion of the screen that will be overwritten by the status line.
  150. ;
  151.         MOV    DS,CS:[VIDEOSEG]    ;point DS:SI to video memory
  152.         MOV    SI,CS:[VIDEOADDR]
  153.         MOV    AX,CS            ;point ES:DI to save buffer
  154.         MOV    ES,AX
  155.         MOV    DI,OFFSET INITIALIZE
  156.         MOV    CX,32
  157.         CALL    VIDEO_XFER        ;block move
  158. ;
  159. ;Activate the status line, restore registers, and exit.
  160. ;
  161.         MOV    AL,CS:[TICKS]        ;enable counter
  162.         MOV    CS:[COUNTER],AL
  163. KB_EXIT:    POP    BP            ;restore registers
  164.         POP    ES
  165.         POP    DS
  166.         POP    DI
  167.         POP    SI
  168.         POP    DX
  169.         POP    CX
  170.         POP    BX
  171.         POP    AX            ;restore AX and flags
  172.         POPF
  173.         IRET
  174. KBINT        ENDP
  175.  
  176. ;=============================================================================
  177. ; TIMERINT intercepts and handles the timer tick interrupt 8h.
  178. ;=============================================================================
  179.  
  180. TIMERINT    PROC    FAR
  181.         PUSHF                ;push flags for int call
  182.         CALL    CS:INT08H            ;call old interrupt handler
  183.  
  184.         CMP    CS:[COUNTER],0FFH    ;exit if status line is
  185.         JE    TIMER_EXIT        ;  dormant
  186.         DEC    CS:[COUNTER]        ;decrement timer
  187.         JNZ    TIMER_EXIT        ;exit if nonzero
  188.         PUSH    AX
  189.         MOV    AL,CS:[TICKS]        ;reset counter
  190.         MOV    CS:[COUNTER],AL
  191.         PUSH    BX            ;save remaining registers
  192.         PUSH    CX
  193.         PUSH    DX
  194.         PUSH    SI
  195.         PUSH    DI
  196.         PUSH    DS
  197.         PUSH    ES
  198.         STI                ;enable interrupts
  199.         CALL    SETPARMS        ;set output variables
  200.         CALL    REFRESH            ;refresh the status display
  201.         POP    ES            ;restore registers and exit
  202.         POP    DS
  203.         POP    DI
  204.         POP    SI
  205.         POP    DX
  206.         POP    CX
  207.         POP    BX
  208.         POP    AX
  209. TIMER_EXIT:
  210.         IRET                ;exit
  211. TIMERINT    ENDP
  212.  
  213. ;-----------------------------------------------------------------------------
  214. ; SETPARMS is called by TIMERINT to set UART parameter values.
  215. ;-----------------------------------------------------------------------------
  216. PSETTINGS    DB    "NONE"
  217. DLAB_REG    DB    ?
  218.  
  219. SETPARMS    PROC    NEAR
  220.         MOV    DX,CS:[COMPORT]        ;get UART base address
  221.         ADD    DX,3            ;point DX to Data Format
  222.         PUSH    DX
  223.         IN    AL,DX            ;read Data Format byte
  224.         MOV    CS:[DLAB_REG],AL    ;save it
  225.         OR    AL,80H            ;set DLAB
  226.         OUT    DX,AL
  227.         SUB    DX,3            ;read divisor LSB
  228.         IN    AL,DX
  229.         MOV    BL,AL
  230.         INC    DX
  231.         IN    AL,DX            ;read divisor MSB
  232.         MOV    BH,AL
  233.         OR    BX,BX            ;don't divide if BX = 0
  234.         JNZ    SP1
  235.         MOV    WORD PTR CS:[BAUDRATE],0
  236.         JMP    SHORT SP2
  237. SP1:        MOV    AX,0C200H        ;calculate Baud rate from
  238.         MOV    DX,1            ;  clock and divisor
  239.         DIV    BX
  240.         MOV    CS:[BAUDRATE],AX    ;save it
  241. SP2:        POP    DX            ;restore Data Format register
  242.         MOV    AL,CS:[DLAB_REG]
  243.         OUT    DX,AL
  244. ;
  245. ;Determine what the parity, data bits, and stop bits settings are.
  246. ;
  247.         MOV    AL,CS:[DLAB_REG]    ;data bits in bits 0 and 1
  248.         AND    AL,3
  249.         ADD    AL,5
  250.         MOV    CS:[DATABITS],AL
  251.         MOV    AL,CS:[DLAB_REG]    ;stop bits in bit 2
  252.         SHR    AL,1
  253.         SHR    AL,1
  254.         AND    AL,1
  255.         ADD    AL,1
  256.         MOV    CS:[STOPBITS],AL
  257.         MOV    BL,CS:[DLAB_REG]    ;parity in bits 3 and 4
  258.         MOV    CL,3
  259.         SHR    BL,CL
  260.         AND    BL,3
  261.         XOR    BH,BH
  262.         MOV    AL,BYTE PTR CS:[PSETTINGS][BX]
  263.         MOV    CS:[PARITY],AL
  264. ;
  265. ;Determine the states of the CTS, DSR, DCD, and RI input pins.
  266. ;
  267.         MOV    DX,CS:[COMPORT]        ;point DX to Modem Status
  268.         ADD    DX,6
  269.         IN    AL,DX            ;read register
  270.         AND    AL,0F0H            ;mask off the lower 4 bits
  271.         SHR    AL,1            ;then save them
  272.         SHR    AL,1
  273.         MOV    CS:[PINS],AL
  274. ;
  275. ;Determine the states of the RTS and DTR output pins.
  276. ;
  277.         SUB    DX,2            ;point DX to Modem Control
  278.         IN    AL,DX            ;read register
  279.         AND    AL,3            ;strip the upper 6 bits
  280.         OR    CS:[PINS],AL        ;store in PINS byte
  281.         RET
  282. SETPARMS    ENDP
  283.  
  284. ;-----------------------------------------------------------------------------
  285. ; REFRESH is called by TIMERINT to display the status line.
  286. ;-----------------------------------------------------------------------------
  287. REFRESH        PROC    NEAR
  288.         MOV    AX,CS            ;copy the underlying line
  289.         MOV    DS,AX            ;  into the status buffer
  290.         MOV    ES,AX
  291.         MOV    SI,OFFSET INITIALIZE
  292.         MOV    DI,OFFSET INITIALIZE + 64
  293.         MOV    CX,32
  294.         CLD
  295.         REP    MOVSW
  296. ;
  297. ;Construct the portion of the string pertaining to the 6 pins.
  298. ;
  299.         MOV    SI,OFFSET PINTEXT        ;point SI to text
  300.         MOV    DI,OFFSET INITIALIZE + 64    ;point DI to buffer
  301.         MOV    BL,1
  302.         MOV    AH,CS:[VIDEO_ATTR]
  303.         MOV    CX,6            ;6 pins to test
  304. RLOOP1:        PUSH    CX
  305.         PUSH    SI
  306.         PUSH    DI
  307.         TEST    CS:[PINS],BL        ;copy pin name to buffer if
  308.         JZ    PINLOW            ;  corresponding bit is set
  309.         MOV    CX,3
  310. RLOOP2:        LODSB
  311.         STOSW
  312.         LOOP    RLOOP2
  313. PINLOW:        POP    DI
  314.         POP    SI
  315.         SHL    BL,1
  316.         ADD    SI,3
  317.         ADD    DI,8
  318.         POP    CX
  319.         LOOP    RLOOP1            ;loop unitl all 6 are done
  320. ;
  321. ;Add Baud rate, parity, data bits, and stop bits indicators to the string.
  322. ;
  323.         MOV    AX,CS:[BAUDRATE]    ;Baud rate
  324.         CALL    BIN2ASC
  325.         MOV    DI,OFFSET INITIALIZE + 64 + 58
  326.         MOV    AL,CS:[PARITY]        ;parity
  327.         MOV    AH,CS:[VIDEO_ATTR]
  328.         STOSW
  329.         MOV    AL,CS:[DATABITS]    ;data bits
  330.         ADD    AL,30H
  331.         STOSW
  332.         MOV    AL,CS:[STOPBITS]    ;stop bits
  333.         ADD    AL,30H
  334.         STOSW
  335. ;
  336. ;Display the status string.
  337. ;
  338.         MOV    SI,OFFSET INITIALIZE + 64
  339.         MOV    ES,CS:[VIDEOSEG]
  340.         MOV    DI,CS:[VIDEOADDR]
  341.         MOV    CX,32
  342.         CALL    VIDEO_XFER
  343.         RET
  344. REFRESH        ENDP
  345.  
  346. ;-----------------------------------------------------------------------------
  347. ; VIDEO_XFER transfers a block of data to or from the video buffer.
  348. ;   Entry:  DS:SI - source
  349. ;           ES:DI - destination
  350. ;           CX    - number of words
  351. ;-----------------------------------------------------------------------------
  352. VIDEO_XFER    PROC    NEAR
  353.         CLD                ;clear DF
  354.         TEST    CS:[SYNCFLAG],1        ;don't wait if this isn't
  355.         JZ    NOWAIT            ;  a CGA
  356.         MOV    DX,3DAH            ;wait if it is
  357. MWAIT1:        IN    AL,DX            ;wait for non-retrace period
  358.         TEST    AL,8
  359.         JNZ    MWAIT1
  360. MWAIT2:        IN    AL,DX            ;wait for the next vertical
  361.         TEST    AL,8            ;  retrace
  362.         JZ    MWAIT2
  363. NOWAIT:        CLI                ;interrupts off
  364.         REP    MOVSW            ;block move
  365.         STI                ;interrupts back on
  366.         RET
  367. VIDEO_XFER    ENDP
  368.  
  369. ;-----------------------------------------------------------------------------
  370. ; BIN2ASC converts a single binary word value into its ASCII decimal
  371. ; equivalent and writes it to the designated output buffer.
  372. ;   Entry:  AX    - binary value
  373. ;           ES:DI - destination
  374. ;-----------------------------------------------------------------------------
  375. BASE        DW    10            ;base 10 divisor
  376.  
  377. BIN2ASC        PROC    NEAR
  378.         XOR    CX,CX            ;zero digit counter
  379. BALOOP1:    INC    CX            ;increment counter
  380.         XOR    DX,DX
  381.         DIV    CS:[BASE]        ;divide by base
  382.         PUSH    DX            ;save remainder on stack
  383.         OR    AX,AX            ;loop until quotient is zero
  384.         JNZ    BALOOP1
  385. BALOOP2:    POP    AX            ;pull digits back off the
  386.         ADD    AL,30H            ;  stack and output them
  387.         MOV    AH,CS:[VIDEO_ATTR]
  388.         STOSW
  389.         LOOP    BALOOP2
  390.         RET
  391. BIN2ASC        ENDP
  392.  
  393. ;=============================================================================
  394. ; INITIALIZE installs or uninstalls the program.
  395. ;=============================================================================
  396. ERRMSG1        DB    "Usage: LITES [comport] [U]$"
  397. ERRMSG2        DB    "Not Installed$"
  398. ERRMSG3        DB    "Cannot Uninstall$"
  399. ERRMSG4        DB    "Already Installed$"
  400. ERRMSG5        DB    "Invalid COM Port$"
  401. OUTTEXT        DB    "Uninstalled$"
  402. INSTALLED    DB    0
  403.  
  404. INITIALIZE    PROC    NEAR
  405.         ASSUME    CS:CODE, DS:CODE
  406. ;
  407. ;See if a copy of LITES is already resident in memory.
  408. ;
  409.         CLD                ;clear DF for string ops
  410.         MOV    WORD PTR [BEGIN],0    ;initialize fingerprint
  411.         XOR    BX,BX            ;zero BX for start
  412.         MOV    AX,CS            ;keep CS value in AX
  413. INIT1:        INC    BX            ;increment search segment value
  414.         MOV    ES,BX
  415.         CMP    AX,BX            ;not installed if current
  416.         JE    PARSE1            ;  segment is reached
  417.         MOV    SI,OFFSET BEGIN        ;search this segment for ASCII
  418.         MOV    DI,SI            ;  fingerprint
  419.         MOV    CX,16
  420.         REPE    CMPSB
  421.         JNE    INIT1            ;loop back if not found
  422.         MOV    INSTALLED,1        ;set installed flag
  423. ;
  424. ;Parse the command line for entries.
  425. ;
  426. PARSE1:        MOV    SI,81H            ;point SI to command line
  427. PARSE2:        LODSB                ;get a character
  428.         CMP    AL,20H            ;skip it if it's a space
  429.         JE    PARSE2
  430.         CMP    AL,0DH            ;exit loop when a carriage
  431.         JE    INSTALL            ;  return is encountered
  432.         CMP    AL,"0"            ;branch if numeral is found
  433.         JB    ERROR1
  434.         CMP    AL,"9"
  435.         JBE    CHECK_COM
  436.         AND    AL,0DFH            ;capitalize the character
  437.         CMP    AL,"U"            ;branch to uninstall code if
  438.         JE    UNINSTALL        ;  character is a "U"
  439. ;
  440. ;An error was encountered in parsing.  Display error message and exit.
  441. ;
  442. ERROR1:        MOV    DX,OFFSET ERRMSG1    ;load message address
  443. ERROR2:        MOV    AH,9            ;display error message
  444.         INT    21H
  445.         MOV    AX,4C01H        ;exit with ERRORLEVEL = 1
  446.         INT    21H
  447. ;
  448. ;Make sure the COM port entered on the command line exists.
  449. ;
  450. CHECK_COM:    SUB    AL,"1"            ;normalize the entry
  451.         MOV    BL,AL            ;save it
  452.         INT    11H            ;determine number of COM
  453.         SHR    AH,1            ;  ports installed
  454.         AND    AH,07H
  455.         MOV    DX,OFFSET ERRMSG5
  456.         CMP    AH,BL            ;exit on error if COM port
  457.         JNA    ERROR2            ;  number is invalid
  458.         MOV    BYTE PTR COMPORT,BL    ;save port designator
  459.         JMP    SHORT INSTALL        ;go to install routine
  460. ;
  461. ;Uninstall the program.
  462. ;
  463. UNINSTALL:    MOV    DX,OFFSET ERRMSG2    ;error if program isn't
  464.         CMP    INSTALLED,0        ;  installed
  465.         JE    ERROR2
  466.         CALL    REMOVE            ;call uninstall routine
  467.         MOV    DX,OFFSET ERRMSG3    ;error if uninstall failed
  468.         JC    ERROR2
  469.         MOV    DX,OFFSET OUTTEXT    ;display "Uninstalled"
  470.         MOV    AH,9            ;  message
  471.         INT    21H
  472.         MOV    AX,4C00H        ;exit with ERRORLEVEL = 0
  473.         INT    21H
  474. ;
  475. ;Make sure LITES isn't already installed, then get the UART base address.
  476. ;
  477. INSTALL:    MOV    DX,OFFSET ERRMSG4    ;exit on error if program
  478.         CMP    INSTALLED,0        ;  is already installed
  479.         JNE    ERROR2
  480.         MOV    AX,40H            ;get UART address from BIOS
  481.         MOV    ES,AX            ;  data area using the port
  482.         XOR    DI,DI            ;  number as an index into
  483.         MOV    BX,COMPORT        ;  the table
  484.         SHL    BX,1
  485.         MOV    AX,WORD PTR ES:[DI][BX]
  486.         MOV    COMPORT,AX
  487. ;
  488. ;Hook into interrupts 9h and 8h and deallocate the environment block.
  489. ;
  490.         MOV    AX,3509H        ;save old interrupt 9h vector
  491.         INT    21H
  492.         MOV    WORD PTR INT09H,BX
  493.         MOV    WORD PTR INT09H[2],ES
  494.         MOV    AX,2509H        ;then set the new 9h vector
  495.         MOV    DX,OFFSET KBINT
  496.         INT    21H
  497.         MOV    AX,3508H        ;save old interrupt 8h vector
  498.         INT    21H
  499.         MOV    WORD PTR INT08H,BX
  500.         MOV    WORD PTR INT08H[2],ES
  501.         MOV    AX,2508H        ;then set the new 8h vector
  502.         MOV    DX,OFFSET TIMERINT
  503.         INT    21H
  504.         MOV    AX,DS:[2CH]        ;deallocate the program's
  505.         MOV    ES,AX            ;  environment block
  506.         MOV    AH,49H
  507.         INT    21H
  508. ;
  509. ;Display copyright notice, then terminate and remain resident in memory.
  510. ;
  511.         MOV    AH,9            ;display copyright and hotkey
  512.         MOV    DX,OFFSET PROGRAM    ;  information
  513.         INT    21H
  514.         MOV    AX,3100H
  515.         MOV    DX,(OFFSET INITIALIZE - OFFSET CODE + 15 + 128) SHR 4
  516.         INT    21H
  517. INITIALIZE    ENDP
  518.  
  519. ;-----------------------------------------------------------------------------
  520. ; REMOVE deallocates the memory block addressed by ES and restores the
  521. ; interrupt vectors displaced on installation.
  522. ;   Entry:  ES - segment to release
  523. ;   Exit:   CF clear - program uninstalled
  524. ;        CF set   - can't uninstall
  525. ;-----------------------------------------------------------------------------
  526. REMOVE          PROC    NEAR
  527.         MOV    CX,ES            ;abort if either interrupt
  528.         MOV    AX,3509H        ;  vector has been altered
  529.         INT    21H            ;  since installation
  530.         MOV    AX,ES
  531.         CMP    AX,CX
  532.         JNE    REMOVE_ERROR
  533.         MOV    AX,3508H
  534.         INT    21H
  535.         MOV    AX,ES
  536.         CMP    AX,CX
  537.         JNE    REMOVE_ERROR
  538. ;
  539. ;Free memory given to the original program block.
  540. ;
  541.         MOV    ES,CX            ;Ask DOS to free it
  542.         MOV     AH,49H
  543.         INT     21H
  544.         JC    REMOVE_ERROR        ;exit if call failed
  545. ;
  546. ;Restore the interrupt 9h and 8h vectors to their installation values.
  547. ;
  548.         PUSH    DS
  549.         ASSUME  DS:NOTHING
  550.         MOV    AX,2509H        ;restore interrupt 9h vector
  551.         LDS    DX,ES:[INT09H]
  552.         INT    21H
  553.         MOV    AX,2508H        ;restore interrupt 8h vector
  554.         LDS    DX,ES:[INT08H]
  555.         INT    21H
  556.         POP     DS
  557.         ASSUME  DS:CODE
  558.         NOT     WORD PTR ES:[BEGIN]     ;destroy ASCII fingerprint
  559.         CLC                ;clear CF for exit
  560.         RET
  561. REMOVE_ERROR:    STC                ;set CF to indicate program
  562.         RET                ;  couldn't be uninstalled
  563. REMOVE          ENDP
  564.  
  565. CODE        ENDS
  566.         END    BEGIN
  567.