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