home *** CD-ROM | disk | FTP | other *** search
/ Boldly Go Collection / version40.iso / TS / 21B / PRN2FL3A.ZIP / PRN2FILE.ASM next >
Assembly Source File  |  1992-02-24  |  36KB  |  897 lines

  1. ;----------------------------------------------------------------------
  2. ; PRN2FILE.ASM - A resident program which redirects printer output.
  3. ; SYNTAX: PRN2FILE d:path:filename.ext [/Pn] [/Bn] [/U]
  4. ;  1)  Run PRN2FILE with the desired filename to activate it.
  5. ;  2)  Run it again with no filename to turn off redirection.
  6. ;  3)  Run it with a differant filename to change destination file.
  7. ;  4)  Use /P to designate the printer number (defaults to 1)
  8. ;  5)  Use /B to enter buffer size in K bytes (defaults to 4)
  9. ;  6)  Use /F to print just to file and not to printer (default is both)
  10. ;  6)  Use /A to append to file (default is to create new file)
  11. ;  7)  Use /U to uninstall the program
  12. ;----------------------------------------------------------------------
  13. CSEG            SEGMENT
  14.         ASSUME  CS:CSEG,DS:NOTHING
  15.  
  16.         ORG     100H    ;Beginning for .COM programs
  17. START:          JMP INITIALIZE  ;Initialization code is at end
  18.  
  19. ;----------------------------------------------------------------------
  20. ; Data area used by this program
  21. ;----------------------------------------------------------------------
  22. COPYRIGHT       DB      " PRN2FILE 1.1 (c) 1987 Ziff Communications Co.",0DH,0AH
  23.         DB      "Modifications (c) 1991 Automated Answers$",1AH
  24. PROGRAMMERS     DB      "Tom Kihlken"
  25.         DB      "Russell Cummings"
  26. REDIRECT_MESS   DB      "LPT"
  27. PRN_NUM         DB      "1 Redirected to: $"
  28. BAD_FILENAME    DB      "Invalid filename.$"
  29. BAD_PARAM       DB      "Usage: PRN2FILE [path][filename][/Pn][/Bnn][/F][/A][/U]$"
  30. BAD_ALLOC       DB      "Memory Allocation Error.$"
  31. BAD_UNINSTALL   DB      "Cannot Uninstall.$"
  32. PRN_TXT         DB      "PRN$"
  33. CRLF            DB      13,10,"$"
  34. ERR_MESSAGE     DB      13,10,"*Buffer Overflow*",13,10
  35. MESS_LENGTH     EQU     $ - OFFSET ERR_MESSAGE
  36. freq1           dw      400     ;Frequency of first sound
  37. freq2           dw      1300    ;Frequency of second sound
  38. lgth1           dw      100     ;Duration of first sound
  39. lgth2           dw      40      ;Duration between sounds
  40. lgth3           dw      100     ;Duration of second sound
  41. bell_flag       db      1       ;Indicates whether sound to be made
  42. OLDINT08        DD      ?       ;Old timer tick interrupt vector
  43. OLDINT17        DD      ?       ;Old printer output vector
  44. OLDINT21        DD      ?       ;Old dos function interrupt vector
  45. OLDINT28        DD      ?       ;Old dos waiting interrupt vector
  46. DOS_FLAG        DD      ?       ;Dos busy flag
  47. JUST_FILE       DB      0       ;Just file flag
  48. APPEND_FILE     DB      0       ;Append file flag
  49. SWITCH          DB      0       ;On/off switch for redirecting printer
  50. TIMEOUT         DW      0       ;Holds timeout counter to flush buffer
  51. INSTALLED_SEG   DW      0       ;Segment location of installed copy
  52. WRITE_FLAG      DB      0       ;Indicates buffer should be written
  53. PRINTER_NUM     DW      0       ;Default to first parallel printer
  54. BUFF_POINTER    DW      0       ;Pointer to next space in buffer
  55. BUFF_SIZE       DW      4       ;Size of buffer
  56. BUFF_SEGMENT    DW      0       ;Segment address of buffer
  57. TIME_TO_WRITE   EQU     400H    ;Flush buffer when this full
  58.  
  59. ;-----------------------------------------------------------------------
  60. ; Interrupt 17 routine. (BIOS printer output)
  61. ; If output is to the selected printer and switch is on then redirect
  62. ; the character into a file.
  63. ;-----------------------------------------------------------------------
  64. NEWINT17        PROC    FAR
  65.         ASSUME  DS:NOTHING, ES:NOTHING
  66.  
  67.         CMP     DX,CS:PRINTER_NUM ;Is this the selected printer?
  68.         JNE     IGNORE          ;If not, let bios handle it
  69.         CMP     CS:SWITCH,1     ;Is redirection turned on?
  70.         JE      REDIRECT_IT     ;If on, take jump
  71. IGNORE:
  72.         JMP     CS:OLDINT17     ;Jump to the bios routine
  73. REDIRECT_IT:
  74.                 CMP  CS:JUST_FILE,1     ;Just file turned on?
  75.                 JE   NO_PUSHES          ;If on, take jump
  76.  
  77.         PUSH ES                 ;Save registers, to be popped
  78.         PUSH DS                 ;off later to go to old int 17
  79.         PUSH SI
  80.         PUSH DI
  81.         PUSH DX
  82.         PUSH CX
  83.         PUSH BX
  84.         PUSH AX
  85. NO_PUSHES:
  86.         STI                     ;Get interrupts back on
  87.  
  88. ;*****************************************************************************
  89. ;* Below is the key modification by Mel Brown to the original PRN2FILE.      *
  90. ;* It adds a call to a sound-making routine when appropriate.  The sound     *
  91. ;* routines are from the book "Bluebook Of Assembly Routines For The IBM PC" *
  92. ;* by Christopher L. Morgan.                                                 *
  93. ;* Also replaced some erroneous JCXZ instructions to allow the program to    *
  94. ;* accept single-character filenames.                                        *
  95. ;*****************************************************************************
  96.  
  97. ;Check BELL_FLAG to determine whether 5 seconds have elapsed since most
  98. ;recent write to file.  If so, make a beep-beep sound to remind user that
  99. ;PRN2FILE is intercepting printer traffic and redirecting it to a disk file.
  100.  
  101.                 cmp     cs:bell_flag,1  ;Check flag
  102.                 jne     no_beep_beep    ;If not set, bypass next
  103.                 call    beep_beep       ;Do the sound
  104.                 mov     cs:bell_flag,0  ;Reset the flag
  105. no_beep_beep:
  106.                 MOV     CS:TIMEOUT,91   ;Reset timeout counter
  107.         PUSH    SI              ;Si will be used for a pointer
  108.         CMP     AH,1            ;Initializing the printer?
  109.         JE      WRITE_BUFF      ;If yes, then flush the buffer
  110.         OR      AH,AH           ;Printing a character?
  111.         JNZ     PRINT_RET       ;If not, take jump to return
  112.         MOV     SI,CS:BUFF_POINTER      ;Get pointer to the buffer
  113.         CMP     SI,CS:BUFF_SIZE ;Is buffer filled up yet?
  114.         JE      PRINT_RET       ;If full just return.
  115.  
  116.         PUSH    DS              ;Save the data segment
  117.         MOV     DS,CS:BUFF_SEGMENT      ;Load DS with the buffer seg
  118.         MOV     DS:[SI],AL      ;Store the character in buffer
  119.         POP     DS              ;Restore data segment
  120.         INC     SI              ;And point to next position
  121.         MOV     CS:BUFF_POINTER,SI      ;Save the new pointer
  122.  
  123.         CMP     SI,TIME_TO_WRITE ;Is buffer filling up yet?
  124.         JL      PRINT_RET        ;If not, just return
  125. WRITE_BUFF:
  126.         MOV     CS:WRITE_FLAG,1 ;Signal buffer needs emptying
  127.         PUSH    DS
  128.         PUSH    BX
  129.         LDS     BX,CS:DOS_FLAG  ;Get location of dos flag
  130.         CMP     BYTE PTR [BX],0 ;Is dos busy flag set?
  131.         POP     BX
  132.         POP     DS
  133.         JNE     PRINT_RET       ;If busy, do nothing
  134.         CALL    WRITE_TO_FILE   ;This empties the buffer
  135. PRINT_RET:
  136.         POP     SI
  137.         MOV     AH,10010000B    ;Return printer status good
  138.  
  139.                 CMP  CS:JUST_FILE,1     ;Just file turned on?
  140.                 JE   NO_POPS            ;If on, take jump
  141.         
  142.         POP AX                
  143.         POP BX
  144.         POP CX
  145.         POP DX
  146.         POP DI
  147.         POP SI
  148.         POP DS
  149.         POP ES                  ;Restore registers and
  150.  
  151.         JMP CS:OLDINT17         ;print onto printer
  152. NO_POPS:  
  153.                 IRET                    ;Return from interrupt        
  154. NEWINT17        ENDP                    ;Without going to printer
  155.  
  156. ;----------------------------------------------------------------------
  157. ; New interrupt 08h (timer tick) decrement the timeout counter. Set
  158. ; the flush flag when counter reaches zero.
  159. ;----------------------------------------------------------------------
  160. NEWINT08        PROC    FAR
  161.         ASSUME  DS:NOTHING, ES:NOTHING
  162.  
  163.         PUSHF                   ;Simulate an interrupt
  164.         CALL    CS:OLDINT08     ;Do normal timer routine
  165.         DEC     CS:TIMEOUT      ;Count down the flush time count
  166.         JNZ     STILL_TIME      ;Count until it gets to zero
  167.                 mov     cs:bell_flag,1  ;Set trigger to make sound
  168.                 CMP     CS:BUFF_POINTER,0 ;Anything in buffer?
  169.         JE      STILL_TIME      ;If not, just continue
  170.         MOV     CS:WRITE_FLAG,1 ;Set flush trigger
  171. STILL_TIME:
  172.         IRET                    ;Return from timer interrupt
  173.  
  174. NEWINT08        ENDP
  175.  
  176. ;----------------------------------------------------------------------
  177. ; Interrupt 21 routine.  (DOS function calls) intercept function 40h
  178. ; when it writes to the printer.  Also check to see if WRITE_FLAG is
  179. ; set to one.  If it is then flush the buffer.
  180. ;----------------------------------------------------------------------
  181. NEWINT21        PROC    FAR
  182.         ASSUME  DS:NOTHING, ES:NOTHING
  183.  
  184.         PUSHF                   ;Save the callers flags
  185.         CMP     CS:WRITE_FLAG,1 ;Buffer need to be written?
  186.         JNE     DONT_WRITE      ;If not, then just return
  187.         PUSH    DS
  188.         PUSH    BX
  189.         LDS     BX,CS:DOS_FLAG  ;Get location of DOS flag
  190.         CMP     BYTE PTR [BX],0 ;Is DOS busy flag set?
  191.         POP     BX
  192.         POP     DS
  193.         JNE     DONT_WRITE      ;If busy, do nothing
  194.         CALL    WRITE_TO_FILE   ;Empty the buffer now
  195. DONT_WRITE:
  196.         OR      AH,AH           ;Doing function zero?
  197.         JNE     NOT_ZERO
  198.         MOV     AX,4C00H        ;If yes, change it to 4Ch
  199. NOT_ZERO:
  200.         CMP     AH,40H          ;Writing to a device?
  201.         JNE     NOT_PRINTER     ;If not, just continue
  202.         CMP     BX,4            ;Writing to the printer handle?
  203.         JNE     NOT_PRINTER     ;If not, just continue
  204.         CMP     CS:SWITCH,1     ;Is redirection on?
  205.         JE      PRINT_IT        ;If yes, then redirect it
  206. NOT_PRINTER:
  207.         POPF                    ;Recover flags from stack
  208.         CLI
  209.         JMP     CS:OLDINT21     ;Do the DOS function
  210.  
  211. ; Emulate print string function by involking INT 17h
  212.  
  213. PRINT_IT:
  214.         STI                     ;Reenable interrupts
  215.         CLD                     ;String moves forward
  216.  
  217.         PUSH    CX              ;Save these registers
  218.         PUSH    DX
  219.         PUSH    SI
  220.  
  221.         MOV     SI,DX           ;Get pointer to string
  222.         MOV     DX,PRINTER_NUM  ;Selected printer ID in DX
  223.         JCXZ    END_LOOP        ;Skip loop if count is zero
  224. PRINT_LOOP:
  225.         LODSB                   ;Load next character from string
  226.         MOV     AH,00           ;Print character function
  227.         INT     17H             ;BIOS print
  228.         LOOP    PRINT_LOOP      ;Loop through whole string
  229. END_LOOP:
  230.         POP     SI
  231.         POP     DX
  232.         POP     CX
  233.  
  234.         MOV     AX,CX           ;All bytes were output
  235.         POPF                    ;Restore the callers flags
  236.  
  237.         CLC                     ;Return success status
  238.         STI                     ;Reenable interrupts
  239.         RET     2               ;Return with current flags
  240.  
  241. NEWINT21        ENDP
  242.  
  243. ;----------------------------------------------------------------------
  244. ; This copies the buffer contents to a file. It should only be called
  245. ; when dos is in a reentrant condition.  All registers are preserved
  246. ;----------------------------------------------------------------------
  247. WRITE_TO_FILE   PROC    NEAR
  248.         ASSUME  DS:NOTHING, ES:NOTHING
  249.  
  250.         PUSH    AX              ;Save registers we need to use
  251.         PUSH    BX
  252.         PUSH    CX
  253.         PUSH    DX
  254.         PUSH    DS
  255.         PUSH    ES
  256.  
  257.         PUSH    CS
  258.         POP     DS              ;Set DS to code segment
  259.         ASSUME  DS:CSEG         ;Tell assembler DS is CSEG
  260.         MOV     WRITE_FLAG,0    ;Clear write request flag
  261.         MOV     AX,3524H        ;Get dos critical error vector
  262.         CALL    DOS_FUNCTION    ;Do the dos function
  263.         PUSH    BX              ;Save old vector on stack
  264.         PUSH    ES
  265.  
  266. ; Replace the dos severe error interrupt with our own routine.
  267.  
  268.         MOV     DX,OFFSET NEWINT24
  269.         MOV     AX,2524H        ;Setup to change int 24h vector
  270.         CALL    DOS_FUNCTION    ;Do the dos function
  271.  
  272. ; First try to open the file.  If dos returns with the carry flag set,
  273. ; the file didn't exist and we must create it.  Once the file is opened,
  274. ; advance the file pointer to the end of file to append.
  275.  
  276.         CMP     BUFF_POINTER,0  ;Anything in the buffer?
  277.         JE      REP_VECTOR      ;If not, no nothing
  278.         MOV     DX,OFFSET FILENAME ;Point to filename
  279.         MOV     AX,3D02H        ;Dos function to open file
  280.         CALL    DOS_FUNCTION    ;Do the dos function
  281.         JC      FILE_NOT_FOUND  ;Set if file doesn't exist.
  282.         MOV     BX,AX           ;Keep handle in BX also
  283.         XOR     CX,CX           ;Move dos file pointer to the
  284.         XOR     DX,DX           ;End of the file. this lets us
  285.         MOV     AX,4202H        ;Append this to an existing file
  286.         CALL    DOS_FUNCTION    ;Do the dos function
  287.         JC      CLOSE_FILE      ;On any error, take jump
  288.         JMP     SHORT WRITE_FILE
  289. FILE_NOT_FOUND:
  290.         CMP     AX,2            ;Was it file not found error?
  291.         JNE     REP_VECTOR      ;If not, just quit
  292.         MOV     CX,0020H        ;Attribute for new file
  293.         MOV     AH,3CH          ;Create file for writing
  294.         CALL    DOS_FUNCTION    ;Do the dos function
  295.         JC      CLOSE_FILE      ;On any error, take jump
  296.  
  297.         MOV     BX,AX           ;Save handle in BX also
  298. WRITE_FILE:     MOV     DX,0            ;Point to buffer
  299.         MOV     CX,BUFF_POINTER ;Number of chars in buffer
  300.         MOV     AH,40H          ;Dos write to a device function
  301.         PUSH    DS
  302.         MOV     DS,BUFF_SEGMENT ;Point to buffer segment
  303.         CALL    DOS_FUNCTION    ;Do the dos function
  304.         POP     DS
  305.         JC      CLOSE_FILE      ;On any error, take jump
  306.         CMP     CX,AX           ;Was everything written
  307.         JNE     CLOSE_FILE      ;If not, it was an error
  308.         CMP     CX,BUFF_SIZE    ;Was buffer full?
  309.         JNE     CLOSE_FILE      ;If not everything is OK
  310.  
  311.         MOV     DX,OFFSET ERR_MESSAGE ;Insert the error message
  312.         MOV     CX,MESS_LENGTH
  313.         MOV     AH,40H          ;Dos write to file function
  314.         CALL    DOS_FUNCTION    ;Do the dos function
  315. CLOSE_FILE:
  316.         MOV     AH,3EH          ;Dos function to close the file
  317.         CALL    DOS_FUNCTION    ;Do the dos function
  318. REP_VECTOR:
  319.         MOV     BUFF_POINTER,0  ;Indicate buffer is empty
  320.         POP     DS              ;Recover int 24h vector from stack
  321.         POP     DX
  322.         MOV     AX,2524H        ;Restore critical error vector
  323.         CALL    DOS_FUNCTION    ;Do the dos function
  324.         ASSUME  DS:NOTHING
  325.         POP     ES              ;Restore all registers
  326.         POP     DS
  327.         POP     DX
  328.         POP     CX
  329.         POP     BX
  330.         POP     AX
  331.         RET                     ;Finished with writing to disk
  332.  
  333. WRITE_TO_FILE   ENDP
  334.  
  335. ;----------------------------------------------------------------------
  336. ; Make a sound indicating that PRN2FILE is active and redirecting
  337. ; printer output to a disk file
  338. ;----------------------------------------------------------------------
  339. beep_beep       proc    near
  340.                 push    ax
  341.                 push    cx
  342.                 mov     cx,cs:freq1     ;Get first frequency
  343.                 call    freq            ;Convert from frequency to period
  344.                 call    toneset         ;Set the sound frequency
  345.                 call    toneon          ;Start the first sound
  346.                 mov     cx,cs:lgth1     ;Get length of first sound
  347.                 call    delay
  348.                 call    toneoff         ;End the first sound
  349.                 mov     cx,cs:lgth2     ;Get delay between sounds
  350.                 call    delay
  351.                 mov     cx,cs:freq2     ;Get second frequency
  352.                 call    freq            ;Convert from frequency to period
  353.                 call    toneset         ;Set the sound frequency
  354.                 call    toneon          ;Start the second sound
  355.                 mov     cx,cs:lgth3     ;Get length of second sound
  356.                 call    delay
  357.                 call    toneoff         ;End the second sound
  358.                 pop     cx
  359.                 pop     ax
  360.                 ret
  361.  
  362. beep_beep       endp
  363.  
  364. ;----------------------------------------------------------------------
  365. ; Convert from frequency to period
  366. ;----------------------------------------------------------------------
  367. freq            proc    near
  368.                 push    dx
  369.                 push    ax
  370.                 mov     dx,12h          ;Upper part of numerator
  371.                 mov     ax,34deh        ;Lower part of numerator
  372.                 div     cx              ;Divide by frequency
  373.                 mov     cx,ax           ;The quotient is the output
  374.                 pop     ax
  375.                 pop     dx
  376.                 ret
  377.  
  378. freq            endp
  379.  
  380. ;----------------------------------------------------------------------
  381. ;Set the sound frequency into the timer 
  382. ;----------------------------------------------------------------------
  383. toneset         proc    near
  384.                 mov     al,cl           ;Send lower byte
  385.                 out     42h,al          ;to timer
  386.                 mov     al,ch           ;Send upper byte
  387.                 out     42h,al          ;to timer
  388.                 ret
  389.  
  390. toneset         endp
  391.  
  392. ;----------------------------------------------------------------------
  393. ; Turn the sound on
  394. ;----------------------------------------------------------------------
  395. toneon          proc    near
  396.                 push    ax
  397.                 in      al,61h          ;Get contents of port B
  398.                 or      al,3            ;Turn speaker and timer on
  399.                 out     61h,al          ;Send out new values to port B
  400.                 pop     ax
  401.                 ret
  402.  
  403. toneon          endp
  404.  
  405. ;----------------------------------------------------------------------
  406. ; Turn the sound off
  407. ;----------------------------------------------------------------------
  408. toneoff         proc    near
  409.                 push    ax
  410.                 in      al,61h          ;Get contents of port B
  411.                 and     al,0fch         ;Turn speaker and timer off
  412.                 out     61h,al          ;Send out new values to port B
  413.                 pop     ax
  414.                 ret
  415.  
  416. toneoff         endp
  417.  
  418. ;----------------------------------------------------------------------
  419. ; Delay for a specified number of milliseconds
  420. ;----------------------------------------------------------------------
  421. delay           proc    near
  422.                 push    cx
  423. delay1:
  424.                 push    cx              ;Save counter
  425.                 mov     cx,260          ;Timing constant
  426. delay2:
  427.                 loop    delay2          ;Small loop
  428.                 pop     cx              ;Restore counter
  429.                 loop    delay1          ;Loop to count milliseconds
  430.                 pop     cx
  431.                 ret
  432.  
  433. delay           endp
  434.  
  435.  
  436. ;----------------------------------------------------------------------
  437. ; This routine emulates an INT 21 by calling the dos interrupt address
  438. ;----------------------------------------------------------------------
  439. DOS_FUNCTION    PROC    NEAR
  440.         ASSUME  DS:NOTHING, ES:NOTHING
  441.  
  442.         PUSHF                   ;Save the processor flags
  443.         CLI                     ;Clear interrupt enable bit
  444.         CALL    CS:OLDINT21     ;Execute the interupt procedure
  445.         STI                     ;Enable further interrupts
  446.         RET                     ;And return to calling routine
  447.  
  448. DOS_FUNCTION    ENDP
  449.  
  450. ;----------------------------------------------------------------------
  451. ; New interrupt 24h (critical dos error).  This interrupt is only in
  452. ; effect when writing to the disk.  It is required to suppress the
  453. ; 'Abort, Retry, Ignore' message.  All fatal disk errors are ignored.
  454. ;----------------------------------------------------------------------
  455. NEWINT24        PROC    FAR
  456.         ASSUME  DS:NOTHING, ES:NOTHING
  457.  
  458.         STI                     ;Turn interrupts back on
  459.         XOR     AL,AL           ;Tells dos to ignore the error
  460.         MOV     CS:SWITCH,AL    ;Turn off logging of output
  461.         IRET                    ;And return to dos
  462.  
  463. NEWINT24        ENDP
  464.  
  465. ;----------------------------------------------------------------------
  466. ; New interrupt 28h (DOS idle).  Check to see if write_flag is set to
  467. ; one. If it is, then flush the buffer
  468. ;----------------------------------------------------------------------
  469. NEWINT28        PROC    FAR
  470.         ASSUME  DS:NOTHING, ES:NOTHING
  471.  
  472.         STI
  473.         CMP     CS:WRITE_FLAG,0 ;Buffer need to be written?
  474.         JE      DO_NOTHING      ;If not, just continue
  475.         CALL    WRITE_TO_FILE   ;Empty the buffer
  476. DO_NOTHING:
  477.         JMP     CS:OLDINT28     ;Continue with old interrupt
  478.  
  479. NEWINT28        ENDP
  480.  
  481. ;----------------------------------------------------------------------
  482. ; Here is the code used to initialize prn2file.com.  First determine
  483. ; if prn2file is already installed.  If it is, just copy new parameters
  484. ; into the resident programs data area, otherwise save old vectors
  485. ; and replace with new ones.  The output buffer will later overlay
  486. ; this code to conserve memory.
  487. ;----------------------------------------------------------------------
  488.         ASSUME  CS:CSEG, DS:CSEG, ES:NOTHING
  489. INITIALIZE:
  490.         MOV     DX,OFFSET COPYRIGHT
  491.         CALL    STRING_CRLF     ;Display the string
  492.  
  493. ; Search for a previously installed copy of prn2file
  494.  
  495.         NOT     WORD PTR START  ;Modify to avoid false match
  496.         XOR     BX,BX           ;Start search at segment zero
  497.         MOV     AX,CS           ;Compare to this code segment
  498. NEXT_SEGMENT:
  499.         INC     BX              ;Look at next segment
  500.         CMP     AX,BX           ;Until reaching this code seg
  501.         MOV     ES,BX
  502.         JE      NOT_INSTALLED
  503.         MOV     SI,OFFSET START ;Setup to compare strings
  504.         MOV     DI,SI
  505.         MOV     CX,16           ;16 bytes must match
  506.         REP     CMPSB           ;Compare DS:SI to ES:DI
  507.         OR      CX,CX
  508.         JNZ     NEXT_SEGMENT    ;If no match, try next segment
  509.         MOV     ES:SWITCH,1     ;Turn redirection on
  510.         MOV     DX,ES:PRINTER_NUM ;Retrieve old printer number
  511.         MOV     DS:PRINTER_NUM,DX ;Save it here
  512.         MOV     AH,1            ;Initialize the resident copy
  513.         INT     17H             ;To flush it's buffer
  514.         ADD     DL,31H          ;Convert printer num to ascii
  515.         MOV     PRN_NUM,DL      ;Put it into the message area
  516. NOT_INSTALLED:
  517.         MOV     INSTALLED_SEG,ES
  518.         PUSH    CS
  519.         POP     ES              ;Set ES to this segment
  520.         ASSUME  ES:CSEG
  521.         CMP     BYTE PTR DS:[0080],0 ;Anything entered?
  522.         JE      NO_PARAMS       ;If not, take jump
  523. PARSE:
  524.         MOV     AL,"/"          ;Look for a slash
  525.         CALL    LOAD_PARAMS
  526.         REPNE   SCASB           ;Scan for slashes
  527.         JCXZ    ALMOST_PARSE    ;Quit when no more slashes
  528.         MOV     AL,[DI]         ;Get the parameter
  529.         MOV WORD PTR [DI-1],2020H;Erase the slash and letter
  530.         OR      AL,32           ;Convert to lower case
  531.         CMP     AL,"p"          ;Is it the "p" parameter
  532.         JE      SLASH_P
  533.         CMP     AL,"b"          ;Is it the "b" parameter
  534.         JE      SLASH_B
  535.         CMP     AL,"f"          ;Is it the "f" parameter
  536.         JE      SLASH_F
  537.         CMP     AL,"a"          ;Is it the "a" parameter
  538.         JE      SLASH_A
  539.         CMP     AL,"u"          ;Is it the "u" parameter
  540.         JE      SLASH_U
  541. INVALID_PARAM:
  542.         MOV     DX,OFFSET BAD_PARAM ;Point to error message
  543.         JMP     ERR_EXIT
  544. SLASH_U:
  545.         JMP     UNINSTALL       ;Slash "u" means uninstall it
  546. SLASH_B:
  547.         MOV     BUFF_SIZE,0     ;Zero buff size for accumulator
  548. NEXT_DIGIT:
  549.         MOV     AX,BUFF_SIZE    ;Get current buff size
  550.         MOV     BL,10
  551.         MUL     BL              ;Times 10 for next digit
  552.         INC     DI              ;Point to next digit
  553.         MOV     BL,[DI]         ;And get the next one
  554.         SUB     BL,30H          ;Convert it to binary
  555.         JC      PARSE           ;If not a digit, keep parsing
  556.         CMP     BL,9
  557.         JA      PARSE           ;If not a digit, keep parsing
  558.         MOV     BYTE PTR [DI]," ";Erase character from command
  559.         XOR     BH,BH
  560.         ADD     AX,BX           ;Add in this digit
  561.         MOV     BUFF_SIZE,AX    ;And save the new total
  562.         JMP     NEXT_DIGIT
  563. SLASH_P:
  564.         INC     DI              ;Point to the printer number
  565.         MOV     AL,[DI]
  566.         MOV     BYTE PTR [DI]," ";Erase this char from command
  567.         MOV     PRN_NUM,AL      ;Put it in the message area
  568.         SUB     AL,31H          ;Convert it to printer number
  569.         XOR     AH,AH           ;Make it a word
  570.         CMP     AL,3            ;Printer id must be less than 3
  571.         JAE     INVALID_PARAM   ;If it isn't, take jump
  572.         MOV     PRINTER_NUM,AX  ;Store the parameter
  573.         JMP     PARSE           ;Look for more parameters
  574. SLASH_F:
  575.             MOV     AL,1            ;Set just file flag
  576.             MOV     JUST_FILE,AL    ;Put it in the message area
  577.         JMP     PARSE           ;Look for more parameters
  578. SLASH_A:
  579.             MOV     AL,1            ;Set append file flag
  580.             MOV     APPEND_FILE,AL  ;Put it in the message area
  581.         JMP     PARSE           ;Look for more parameters
  582. ALMOST_PARSE:
  583.                 JMP     PARSE_DONE
  584. NO_PARAMS:
  585.         MOV     DX,OFFSET REDIRECT_MESS ;Point to message
  586.         MOV     AH,9            ;Display the string of text
  587.         INT     21H             ;Using DOS display function
  588.         MOV     DX,OFFSET PRN_TXT ;Point to "PRN"
  589.         CALL    STRING_CRLF     ;Display the string
  590.         MOV     AL,0            ;Turn off redirection switch
  591.         JMP     CHECK_FOR_INSTALL
  592. PARSE_DONE:
  593.         CMP     BUFF_SIZE,1     ;Buff must be at least 1K
  594.         JB      INVALID_PARAM   ;If not, exit with error
  595.         CMP     BUFF_SIZE,64    ;Check for maximum buff size
  596.         JA      INVALID_PARAM   ;If above, exit with error
  597.         MOV     AL," "          ;Look for spaces
  598.         CALL    LOAD_PARAMS
  599.         REPE    SCASB           ;Scan for non-space character
  600.                 JZ      NO_PARAMS       ;Any letters found?
  601.  
  602.         CMP     BYTE PTR [DI],":" ;Was a drive specified?
  603.         JNE     GET_DEF_DRIVE   ;If not, get the default drive
  604.         DEC     DI              ;Now DI points to first letter
  605.         MOV     AL,[DI]         ;Get drive letter in AL
  606.         MOV     WORD PTR [DI],2020H;Erase the drive and colon
  607.         JMP     STORE_DRIVE
  608. GET_DEF_DRIVE:
  609.         MOV     AH,19H          ;Get default drive
  610.         INT     21H
  611.         ADD     AL,65           ;Convert integer drive to ascii
  612. STORE_DRIVE:
  613.         MOV     AH,":"          ;AL has drive, AH has colon
  614.         MOV     WORD PTR FILENAME,AX ;Store drive and colon
  615.         MOV     AL,"\"          ;Look for a backslash
  616.         MOV     FILENAME+2,AL   ;Add a backslash to filename
  617.         CALL    LOAD_PARAMS
  618.         REPNE   SCASB           ;Scan for a backslash
  619.                 JnZ     GET_DEF_PATH    ;If no path, use current path
  620.         MOV     DI,OFFSET FILENAME+2 ;Location to store path
  621.         JMP     STORE_PATH
  622. GET_DEF_PATH:
  623.         MOV     DL,FILENAME     ;Selected drive letter
  624.         AND     DL,11011111B    ;Convert it to upper case
  625.         SUB     DL,64           ;Convert it to integer
  626.         MOV     SI,OFFSET FILENAME + 3 ;Put current path at SI
  627.         MOV     DI,SI           ;Save this for search later
  628.         MOV     AH,47H          ;DOS get current directory
  629.         INT     21H
  630.         JC      BAD_NAME_EXIT   ;Exit if invalid drive
  631.         MOV     AL,0            ;Look for end of path
  632.         CMP     [DI],AL         ;Was there any path?
  633.         JE      STORE_PATH      ;If not, don't scan it
  634.         MOV     CX,64           ;Maximum number of bytes in path
  635.         REPNE   SCASB           ;Scan for end of path string
  636.         MOV     BYTE PTR [DI-1],"\" ;Add the trailing backslash
  637. STORE_PATH:
  638.         PUSH    DI              ;Save location to append path
  639.         MOV     AL," "          ;Look for blank spaces
  640.         CALL    LOAD_PARAMS
  641.         REPE    SCASB           ;Scan for non-blank character
  642.         MOV     SI,DI
  643.         DEC     SI              ;This is first letter of path
  644.         POP     DI              ;Get back location to append
  645. COPY_PATH:
  646.         LODSB                   ;Get next char of path
  647.         CMP     AL," "          ;Is it a blank?
  648.         JE      VERIFY_NAME     ;If yes, its the last char
  649.         CMP     AL,13           ;Is it a carriage return?
  650.         JE      VERIFY_NAME     ;If yes, its the last char
  651.         STOSB                   ;Store this letter
  652.         JMP     COPY_PATH       ;Copy until end of path found
  653. VERIFY_NAME:
  654.         PUSH    DI              ;Save end of string location
  655.         MOV     BYTE PTR [DI],"$" ;Mark eos for dos display
  656.         MOV     DX,OFFSET REDIRECT_MESS ;Point to message
  657.         MOV     AH,9            ;Display the string of text
  658.         INT     21H             ;Using dos display function
  659.         MOV     DX,OFFSET FILENAME ;Point to filename for display
  660.         CALL    STRING_CRLF     ;Display the string
  661.         POP     DI
  662.         MOV     BYTE PTR [DI],0 ;Now make it an ascii string
  663.         MOV     DX,OFFSET FILENAME ;Dx points to the filename
  664.  
  665.                 CMP  CS:APPEND_FILE,1   ;Append file turned on?
  666.                 JNE  OPEN_ERR           ;If not, take jump
  667.  
  668.         MOV     AX,3D00H        ;Open this file for reading
  669.         INT     21H
  670.         JC      OPEN_ERR        ;Error may indicate not found
  671. CLOSE_IT:
  672.         MOV     BX,AX           ;Get the handle into BX
  673.         MOV     AH,3EH          ;Close the file
  674.         INT     21H
  675.         JMP     FILENAME_OK
  676. OPEN_ERR:
  677.         MOV     CX,0020H        ;Attribute for new file
  678.         MOV     AH,3CH          ;Create file for writing
  679.         INT     21H             ;Dos function to create file
  680.         JNC     CLOSE_IT        ;If no error, just close it
  681. BAD_NAME_EXIT:
  682.         MOV     DX,OFFSET BAD_FILENAME
  683. ERR_EXIT:
  684.         CALL    STRING_CRLF     ;Display the string
  685.         INT     20H             ;Just exit to dos
  686. FILENAME_OK:
  687.         MOV     ES,INSTALLED_SEG;Point to installed program
  688.         PUSH    DS:PRINTER_NUM  ;This moves the new printer
  689.         POP     ES:PRINTER_NUM  ;number to the resident copy
  690.         MOV     DI,OFFSET FILENAME ;Setup to copy the filename
  691.         MOV     SI,DI
  692.         MOV     CX,128          ;Copy entire file specification
  693.         REP     MOVSB           ;String move instruction
  694.         MOV     AL,1            ;Turn redirection on
  695. CHECK_FOR_INSTALL:
  696.         MOV     CX,CS
  697.         CMP     CX,INSTALLED_SEG
  698.         MOV     ES,INSTALLED_SEG
  699.         MOV     ES:SWITCH,AL    ;Store the new on/off switch
  700.         JE      INSTALL         ;If not installed yet, do it now
  701.         INT     20H             ;Otherwise terminate
  702.  
  703. ;----------------------------------------------------------------------
  704. ; This subroutine displays a string followed by a CR and LF
  705. ;----------------------------------------------------------------------
  706. STRING_CRLF     PROC    NEAR
  707.  
  708.         MOV     AH,9            ;Display the string of text
  709.         INT     21H             ;Using dos display function
  710.         MOV     DX,OFFSET CRLF  ;Now point to CR/LF characters
  711.         MOV     AH,9            ;Send the CR and LF
  712.         INT     21H
  713.         RET
  714.  
  715. STRING_CRLF     ENDP
  716.  
  717. ;----------------------------------------------------------------------
  718. ; This subroutine sets DI to the command line and CX to the byte count
  719. ;----------------------------------------------------------------------
  720. LOAD_PARAMS     PROC    NEAR
  721.  
  722.         MOV     DI,80H          ;Point to parameter area
  723.         MOV     CL,CS:[DI]      ;Get number of chars into CL
  724.         XOR     CH,CH           ;Make it a word
  725.         INC     DI              ;Point to first character
  726.         CLD                     ;String search forward
  727.         RET
  728.  
  729. LOAD_PARAMS     ENDP
  730.  
  731. ;----------------------------------------------------------------------
  732. ; This code does the actual installation by storing the existing
  733. ; interrupt vectors and replacing them with the new ones.
  734. ; Then allocate memory for the buffer.  Exit and remain resident.
  735. ;----------------------------------------------------------------------
  736.         ASSUME  DS:CSEG, ES:CSEG
  737. INSTALL:
  738.         MOV     BX,OFFSET END_OF_CODE   ;Get end of resident code
  739.         ADD     BX,15
  740.         MOV     CL,4            ;Shift by 4 to divide by 16
  741.         SHR     BX,CL           ;This converts to paragraphs
  742.         MOV     AH,4AH          ;Modify memory block
  743.         INT     21H             ;Dos setblock function call
  744.         JNC     ALLOCATE_BUFFER ;If it worked ok, then continue
  745. ALLOC_ERROR:
  746.         MOV     DX,OFFSET BAD_ALLOC ;Err message for bad allocation
  747.         JMP     ERR_EXIT        ;Display message and exit
  748. ALLOCATE_BUFFER:
  749.         MOV     BX,BUFF_SIZE    ;Buffer size in K bytes
  750.         MOV     CL,6            ;Shift by 6 to get paragraphs
  751.         SHL     BX,CL           ;Buffersize is in paragraphs
  752.         MOV     AH,48H
  753.         INT     21H             ;Dos allocate memory
  754.         JC      ALLOC_ERROR     ;If allocation error, take jump
  755.         MOV     BUFF_SEGMENT,AX ;Save the segment for the buffer
  756.  
  757.         MOV     AX,BUFF_SIZE    ;Buffer size in K bytes
  758.         MOV     CL,10           ;Shift by 10 to get bytes
  759.         SHL     AX,CL
  760.         OR      AX,AX           ;Is buff_size=0 (64K)?
  761.         JNZ     SIZE_OK
  762.         DEC     AX              ;If yes, make it FFFFh
  763. SIZE_OK:
  764.         MOV     BUFF_SIZE,AX    ;Now buff_size is in bytes
  765.  
  766.         ASSUME  ES:NOTHING
  767.  
  768.         MOV     AH,34H          ;Get dos busy flag location
  769.         INT     21H
  770.         MOV     WORD PTR [DOS_FLAG]  ,BX ;Store flag address
  771.         MOV     WORD PTR [DOS_FLAG+2],ES
  772.  
  773.         MOV     AX,3508H        ;Get timer interrupt vector
  774.         INT     21H
  775.         MOV     WORD PTR [OLDINT08]  ,BX
  776.         MOV     WORD PTR [OLDINT08+2],ES
  777.         MOV     DX, OFFSET NEWINT08
  778.         MOV     AX, 2508H
  779.         INT     21H             ;Dos function to change vector
  780.  
  781.         MOV     AX,3517H        ;Get printer interrupt vector
  782.         INT     21H
  783.         MOV     WORD PTR [OLDINT17]  ,BX
  784.         MOV     WORD PTR [OLDINT17+2],ES
  785.         MOV     DX, OFFSET NEWINT17
  786.         MOV     AX, 2517H
  787.         INT     21H             ;Dos function to change vector
  788.  
  789.         MOV     AX,3521H        ;Get dos function vector
  790.         INT     21H
  791.         MOV     WORD PTR [OLDINT21]  ,BX
  792.         MOV     WORD PTR [OLDINT21+2],ES
  793.         MOV     DX, OFFSET NEWINT21
  794.         MOV     AX, 2521H
  795.         INT     21H             ;Dos function to change vector
  796.  
  797.         MOV     AX,3528H        ;Get dos waiting vector
  798.         INT     21H
  799.         MOV     WORD PTR [OLDINT28]  ,BX
  800.         MOV     WORD PTR [OLDINT28+2],ES
  801.         MOV     DX, OFFSET NEWINT28
  802.         MOV     AX, 2528H
  803.         INT     21H             ;Dos function to change vector
  804.  
  805. ;----------------------------------------------------------------------
  806. ; Deallocate our copy of the enviornment.  Exit using interrupt 27h
  807. ; (TSR). This leaves code and space for buffer resident.
  808. ;----------------------------------------------------------------------
  809.  
  810.         MOV     AX,DS:[002CH]   ;Get segment of enviornment
  811.         MOV     ES,AX           ;Put it into ES
  812.         MOV     AH,49H          ;Release allocated memory
  813.         INT     21H
  814.         MOV     DX,(OFFSET END_OF_CODE - OFFSET CSEG + 15)SHR 4
  815.         MOV     AX,3100H
  816.         INT     21H             ;Terminate and stay resident
  817.  
  818. ;----------------------------------------------------------------------
  819. ; This procedure removes PRN2FILE from memory by replacing the vectors
  820. ; and releasing the memory used for the code and buffer.
  821. ;----------------------------------------------------------------------
  822.         ASSUME  DS:CSEG, ES:NOTHING
  823. UNINSTALL:
  824.         MOV     AL,08H          ;Check the timer interrupt
  825.         CALL    CHECK_SEG       ;If changed, can't uninstall
  826.         JNE     CANT_UNINSTALL
  827.  
  828.         MOV     AL,17H          ;Check the printer interrupt
  829.         CALL    CHECK_SEG       ;If changed, can't uninstall
  830.         JNE     CANT_UNINSTALL
  831.  
  832.         MOV     AL,21H          ;Check dos interrupt
  833.         CALL    CHECK_SEG       ;If changed, can't uninstall
  834.         JNE     CANT_UNINSTALL
  835.  
  836.         MOV     AL,28H          ;Check dos idle interrupt
  837.         CALL    CHECK_SEG       ;If changed, can't uninstall
  838.         JNE     CANT_UNINSTALL
  839.  
  840.         MOV     ES,INSTALLED_SEG
  841.         ASSUME  DS:NOTHING, ES:NOTHING
  842.  
  843.         LDS     DX,ES:OLDINT08  ;Get original vector
  844.         MOV     AX,2508H
  845.         INT     21H             ;Dos function to change vector
  846.  
  847.         LDS     DX,ES:OLDINT17  ;Get original vector
  848.         MOV     AX,2517H
  849.         INT     21H             ;Dos function to change vector
  850.  
  851.         LDS     DX,ES:OLDINT21  ;Get original vector
  852.         MOV     AX,2521H
  853.         INT     21H             ;Dos function to change vector
  854.  
  855.         LDS     DX,ES:OLDINT28  ;Get original vector
  856.         MOV     AX,2528H
  857.         INT     21H             ;Dos function to change vector
  858.  
  859.         MOV     ES,ES:BUFF_SEGMENT;Get segment of buffer
  860.         MOV     AH,49H          ;Free its allocated memory
  861.         INT     21H
  862.         JC      RELEASE_ERR     ;If error, take jump
  863.  
  864.         MOV     ES,INSTALLED_SEG;The resident program segment
  865.         NOT     WORD PTR ES:START
  866.         MOV     AH,49H          ;Free its allocated memory
  867.         INT     21H
  868.         JC      RELEASE_ERR     ;If error, take jump
  869.         MOV     AX,4C00H
  870.         INT     21H             ;Exit to dos
  871. RELEASE_ERR:
  872.         MOV     DX,OFFSET BAD_ALLOC ;Memory allocation error
  873.         JMP     ERR_EXIT        ;Exit with error message
  874. CANT_UNINSTALL:
  875.         MOV     DX,OFFSET BAD_UNINSTALL ;Point to error message
  876.         JMP     ERR_EXIT        ;Exit with error message
  877.  
  878. ;----------------------------------------------------------------------
  879. ; This subroutine checks to see if an interrupt vector points to the
  880. ; installed program segment. Returns with ZF=1 if it does.
  881. ;----------------------------------------------------------------------
  882. CHECK_SEG       PROC    NEAR
  883.  
  884.         MOV     AH,35H          ;Get the vector
  885.         INT     21H             ;Dos function to get the vector
  886.         MOV     AX,ES
  887.         CMP     AX,INSTALLED_SEG;Is it the installed segment?
  888.         RET
  889.  
  890. CHECK_SEG       ENDP
  891. ;----------------------------------------------------------------------
  892. FILENAME        LABEL   BYTE            ;File name will go here
  893. END_OF_CODE     =       $ + 128         ;Allow 128 bytes for it
  894.  
  895. CSEG            ENDS
  896.         END     START
  897.