home *** CD-ROM | disk | FTP | other *** search
/ Phoenix CD 2.0 / Phoenix_CD.cdr / 15a / lptx700.zip / LPTX.ASM next >
Assembly Source File  |  1987-10-19  |  106KB  |  2,542 lines

  1. title LPTx : Line PrinTer Output Capture Routine
  2. page    60,132
  3. ;------------------------------------------------------------
  4. ;
  5. ;       MAIN PROGRAM    Version 7.00
  6. ;
  7. ;  (C)  Copyright 1987 by Mark DiVecchio, All Rights Reserved
  8. ;  (C)  Copyright 1987 by Kepa Zubeldia,  All Rights Reserved
  9. ;
  10. .xlist
  11. ;
  12. ;This program is released for use in non-commercial environments. I
  13. ;ask commercial users to register the program with a $25 copyright fee for
  14. ;each site (any number of users and computers) at which the program is used.
  15. ;
  16. ; DISCLAMER : this program tries to perform a function which is
  17. ; not supported by DOS. It will work sometime and will not work
  18. ; other times. That kind of explains why you don't see this type
  19. ; of program on the market.
  20. ; I have tested it under DOS 2.1 and DOS 3.1. Some users have
  21. ; reported trouble when running under DOS 3.x and for other users
  22. ; it works fine. USE AT YOUR OWN RISK.
  23. ;
  24. ; Mark C. DiVecchio
  25. ; 10435 Mountain Glen Terrace
  26. ; San Diego, CA 92131
  27. ; 619-566-6810
  28. ;------------------------------------------------------------
  29. ; Updates for Version 7.00               6 Oct 87
  30. ;
  31. ; Kepa Zubeldia
  32. ; Micro Consulting, Inc.
  33. ; 1650 Citizens Tower Bldg.
  34. ; Oklahoma City, OK 73106
  35. ; (405) 528-8133
  36. ;
  37. ; -Added a pop-up window that allows the control of the redirection from within
  38. ;  an application program.
  39. ; -Added a switch for monochrome screens combined with color cards, to cancel
  40. ;  the color generation and make the window more visible.
  41. ; -Deleted the Control-Z that was appended on the last close to a file.
  42. ; -Allow for file append when the program is called with the name of a file
  43. ;  that already exists. The user still has the option of overwriting the file.
  44. ; -Added a switch to perform the append automatically.
  45. ; -Corrected the -? switch so it works.
  46. ; -Assembled with Microsoft MASM V5.0
  47. ;
  48. ; The major change is the addition of the pop-up window. The code for this was
  49. ; taken from PC Magazine, October 13, 1987, page 401, a productivity utility
  50. ; written by Jeff Prosise. It works with CGA, MGA, and EGA. With the EGA it
  51. ; will also work in 120 cols. and/or 35 or 43 line modes. Exiting the pop-up
  52. ; with an EGA leaves the cursor in underline. This is not a bug, but a feature.
  53. ;
  54. ; The pop-up window lets you turn redirection on and off from within a
  55. ; running program. You first must start LPTx from the command line as
  56. ; usual :
  57. ;
  58. ; lptx output.fil
  59. ;
  60. ; Then you can press Alt-PrtSc and a pop-up window will appear. When it
  61. ; does, the up and down arrow keys select a printer and the right and
  62. ; left arrow keys select redirection. The pop-up will state "to file"
  63. ; or "to printer" indicating the state of the redirection.
  64. ;
  65. ; Press <Enter> or <Esc> to close the window.
  66. ;
  67. ; Remember that you must start LPTx from the command line before the
  68. ; pop-up will let you turn redirection on or off.
  69. ;
  70. ;------------------------------------------------------------
  71. ; Updates for Version 6.00              18 Mar 87
  72. ;
  73. ; Added use of Timer Interrupt and Idle interrupt to permit
  74. ; writing to disk
  75. ;
  76. ; Added a switch to inhibit the output of linefeed characters when
  77. ; capturing a file. Program strips linefeed character at the
  78. ; end of the line if you turn on this switch. The switch is -l on the
  79. ; command line when you open a capture file.
  80. ;
  81. ; This version does not use the PSP swapping of previous versions.
  82. ;
  83. ;------------------------------------------------------------
  84. ; Updates for Version 5.02              19 Nov 86
  85. ;
  86. ; Added -i inactivate option. Must be only option on command line :
  87. ;               lptx -i
  88. ;
  89. ; This version adds a check for DOS interuupt 21h
  90. ; function 40h for standard printer device = 0004.
  91. ;
  92. ; This addition was suggested by Dale Letterman of Seattle.
  93. ;
  94. ; Assembled using MicroSoft MASM v 4.0
  95. ;
  96. ; Program is called and used in the same way as version 3.00
  97. ;
  98. ; I now enter DOS with interrupts disabled.
  99. ;
  100. ; Added a switch to inhibit the checking of the Critical Section Flag
  101. ; Add -x to the command line the first time that you run LPTx.
  102. ;
  103. ;------------------------------------------------------------
  104. ; Updates for Version 5.00              13 May 86
  105. ;
  106. ; This version also takes over the DOS interuupt 21h and specifically
  107. ; checks for function 5.
  108. ; If that is the call, LPTx captures the character if LPTx has been
  109. ; activated. If it is a DOS call, LPTx assumes that DOS wants LPT1 since
  110. ; there is no way for the DOS call to specify a line printer number.
  111. ;
  112. ; Uses undocumented DOS int 21h calls 50h and 51h.
  113. ;       50h     Set new current Program Segment Prefix(PSP) from
  114. ;               segment number in BX
  115. ;       51h     Get current PSP into BX.
  116. ; These calls are used before any file is opened by the resident portion
  117. ; of LPTx. There is some concern that DOS puts information about open files
  118. ; into the current PSP. Before we open our spooler file, we want to set
  119. ; the current PSP to our PSP and then restore it after the file I/O
  120. ; is complete. This idea was expressed in PC Magazine May 13, 1986 on page
  121. ; 314 in an article by Charles Petzold.
  122. ;
  123. ;
  124. ; This version 5.0 does not obsolete versions 4.0 and 3.0. Those versions
  125. ; may work under some conditions where this one does not and vica versa.
  126. ;------------------------------------------------------------
  127. ; Updates for Version 4.01              5 May 86
  128. ;
  129. ; Had an error in the way LPTx detected if it was already in memory.
  130. ; This error existed from back in version 3.00 and may have been
  131. ; the cause of this program locking up the system the very first
  132. ; time it was called.
  133. ;------------------------------------------------------------
  134. ; Updates for Version 4.0               25 April 86
  135. ;
  136. ; Assembled using MicroSoft MASM v 4.0
  137. ;
  138. ; Program is called and used in the same way as version 3.00
  139. ;
  140. ; Modified the code to check if DOS was running when the print interrupt
  141. ; occurs. If so the print request is routed back to the regular line
  142. ; printer. This will limit the use of this capture program to user
  143. ; programs which do their own output without going to DOS.
  144. ;
  145. ; In turn, this guarantees that we do re-enter DOS.
  146. ;
  147. ; The trick of saving the DOS stack was dropped in this version and
  148. ; I have resorted to another trick which I garnered from the
  149. ; following message found on info-ibmpc. I use method number 2.
  150. ;
  151. ; This version 4.0 does not obsolete version 3.0. That version may
  152. ; work under some conditions where this one does not and vica versa.
  153. ; This one worked fine for me using DOS 2.1 and 123 version 1.A.
  154. ; Will not work with Shift PrtSc.
  155. ;
  156. comment *
  157. Date: Thu, 30 Jan 86 08:47:51 est
  158. Subject: File I/O from resident programs
  159. To: allegra!seismo!usc-isib.arpa!info-ibmpc
  160.  
  161. Regarding opening up a file when you are terminate-and-stay-resident:
  162.  
  163. Be very careful when you attempt this.  Many an FAT has been eaten for
  164. lunch when I first tried doing it.  Two ways that work like a charm:
  165.  
  166. 1)      Take over interrupt 0x28.  This interrupt gets called by DOS
  167.         while its waiting for a key to be hit. Whenever it does get
  168.         called (your program should not be time critical, btw, as
  169.         this routine is never called from CPU intensive tasks), it
  170.         is safe to do with DOS what you will. (Except for certain
  171.         interruptions, such as Search First and Search Next, which
  172.         either you'll screw-up for the foreground task, or they'll
  173.         screw-up for you.)
  174.  
  175. 2)      Get the Critical Section Flag by issuing an int 21, with ah=0x34.
  176.         This returns a pointer to a flag in ES:BX. When this flag is
  177.         NULL, and interrupts are on, it is safe to play DOS games.
  178.         Unless you are the last program to take over the interrupt,
  179.         don't trust the flag word: many "fine" programs like SideKick
  180.         do not give you a true copy of the flag word on the stack, but
  181.         rather give a simple "pushf" after interrupts are turned off.
  182. * ;end of comment
  183. ;
  184. ; More information from a message posted on USENIX:
  185. ;
  186. comment *
  187. From sdcsvax!ihnp4!timeinc!greenber Mon Jul  1 05:12:16 1985
  188. Date: 30 Jun 85 17:12:37 CDT (Sun)
  189. -----------------------------------------------------------
  190. INT 21 - Internal - Return CritSectFlag Pointer (MSDOS generic)
  191.           REG AH = 34H
  192.           On Return:
  193.                     ES:BX points to DOS "Critical Section Flag"
  194.           When byte pointed to is zero, DOS is supposed to be
  195.           safe to interrupt. NOT RELIABLE according to Chris
  196.           Dunford.
  197.                     Examination of DOS 2.10 code in this area
  198.           indicates that the byte immediately FOLLOWING this
  199.           "Critical Section Flag" must be 00 to permit the
  200.           PRINT.COM interrupt to be called. This suggests that
  201.           checking the WORD pointed to, rather than the BYTE,
  202.           might increase reliability of the test greatly.
  203. -----------------------------------------------------------
  204. INT 28 - Internal routine for MSDOS
  205.           This interrupt is called from inside the "get input
  206. from keyboard" routine in DOS, if and only if it is safe to use
  207. INT 21 to access the disk at that time. It is used primarily by
  208. the PRINT.COM routines, but any number of other routines could
  209. be chained to it by saving the original vector, and calling it
  210. with a FAR call (or just JMPing to it) at the end of the new
  211. routine.
  212.           Until PRINT.COM installs its own routine, this
  213. interrupt vector simply points to an IRET opcode.
  214. -----------------------------------------------------------
  215. * ;end of comment
  216. ;
  217. ;------------------------------------------------------------
  218. ; Updates for Version 3.0
  219. ;
  220. ; This version is fully compatible with IBM's PRINT command and
  221. ; hopefully most other print spoolers. I changed the method by which
  222. ; LPTx determines if a resident copy of itself is already in memory.
  223. ;
  224. ;------------------------------------------------------------
  225. ; This program intercepts the BIOS interrupt 17, the line printer
  226. ; interrupt. It will redirect the output of LPT1, LPT2, or LPT3 to a disk
  227. ; file. All three redirects may be active at the same time.
  228. ;
  229. ;
  230. ; Background:
  231. ;
  232. ; The basic problem with this type of program is that PC-DOS as written
  233. ; by Microsoft is not re-entrant. That means that if DOS is in control when
  234. ; the print interrupt occurs, you can not call DOS again to do some other
  235. ; function. Therefore, LPTx can not call DOS to write the captured print
  236. ; data to disk. Version 3.00 of LPTx tries to get around this by making
  237. ; PC-DOS re-entrant. Version 4.00 of LPTx gets around this by not ever
  238. ; trying to re-enter DOS.
  239. ;
  240. ;*******This Program Must be Converted to a .COM file before running ******
  241. ; Assemble with :
  242. ;       masm    lptx;
  243. ;       link    lptx;
  244. ;       exe2bin lptx,lptx.com
  245. ;       erase lptx.obj
  246. ;       erase lptx.exe
  247. ;
  248. ;-----------------------------------------------------------------
  249. .list
  250. if1
  251.         %out Pass 1 - Including Macros - v6.00
  252. ;
  253. .xlist
  254. ;-----------------------------------------------------------------
  255. ;
  256. ; Macros
  257. ;
  258. display macro   msg
  259.         mov     DX,offset msg
  260.         mov     AH,DISPLAY_OUTPUT
  261.         int     DOS_CALL
  262.         endm
  263. ;
  264. ; For the PC-AT:
  265. ; POPF macro described in the IBM Personal Computer
  266. ; Seminar Proceedings Volume 2, Number 4 September 1984
  267. ; QUOTE
  268. ; "If the system microprocessor executes a POPF instruction in either
  269. ; the real or the virtual address mode with CPL <= IOPL, then a 
  270. ; pending maskable interrupt (the INTR pin active) may be improperly
  271. ; recognized after executing the POPF instruction even if maskable
  272. ; interrupts were disabled before the POPF instruction and the value 
  273. ; popped had IF=0. If the interrupt is improperly recognized, the
  274. ; interrupt is still correctly executed. This errata has no effect
  275. ; when interrupts are enabled in either real or virtual address mode.
  276. ; The errata has no effect in the virtual address mode when
  277. ; CPL > IOPL."
  278. ;
  279. popff   macro                           ;use POPFF instead of POPF
  280.         local   popem,skip
  281.                                         ;simulate popping flags using IRET
  282.         jmp     short skip              ;jump around iret
  283. popem:
  284.         iret                            ;pop CS,IP,flags
  285. skip:
  286.         push    CS
  287.         call    popem                   ;call within segment
  288.                                         ;program will continue here
  289.         endm
  290.  
  291. ;
  292. call_dos        macro                   ;Enter DOS with interrupts disabled
  293.         cli
  294.         int     DOS_CALL
  295.         sti
  296.         endm
  297. ;
  298. ;
  299. .list
  300. else
  301.         %out Pass 2
  302. endif
  303. ;-----------------------------------------------------------------
  304. NULL            equ     0
  305. OFF             equ     0
  306. ON              equ     1
  307. EMPTY           equ     0
  308. BEL             equ     7
  309. CR              equ     13
  310. LF              equ     10
  311. DOLLAR          equ     '$'
  312. COLON           equ     ':'
  313. BACKSLASH       equ     '\'
  314. BLANK           equ     ' '
  315. DASH            equ     '-'
  316. DOS_CALL        equ     21h
  317. ;
  318. REQ             equ     00B91h          ;LPTx request flag
  319. ACK             equ     0ABCBh          ;LPTx acknowledge flag
  320. ;
  321. BUFSIZE         equ     4096            ;size of DMA buffer
  322. DISPLAY_OUTPUT  equ     9               ;for DOS call
  323. DEF_DRIVE       equ     19h
  324. CREATE_FILE     equ     3Ch
  325. OPEN_FILE       equ     3Dh
  326. CLOSE_FILE      equ     3Eh
  327. WRITE_FILE      equ     40h
  328. DELETE_FILE     equ     41h
  329. LSEEK_FILE      equ     42h
  330. DEF_PATH        equ     47h
  331. FIND_FILE       equ     4Eh
  332. ;-----------------------------------------------------------------
  333. ;
  334. p_block struc
  335. ;
  336. ; data structure - these variables are used only in the
  337. ; memory resident copy of LPTx. BX is set to point to the offset of the
  338. ; allocation of this structure for the selected LPT
  339. ; NOTE : all of the labels in this structure are required as place
  340. ; holders even if not referenced. Used by the initialization calls
  341. ; later on.
  342. ;
  343. active  db      OFF                     ;ON = this LPTx is on, OFF = off
  344. handle  dw      NULL                    ;handle of disk file used by this LPT
  345. filen   db      'a:\lptx'               ;space for redirection disk file name
  346. pnum    db      '0'                     ;default to '0' just in case
  347.         db      '.lst',NULL
  348.         db      '                          '
  349.         db      '                        '
  350. bufcntr dw      EMPTY                   ;bytes used in DMA buffer for this LPT
  351. request db      OFF                     ;ON indicates a write request is active
  352.                                         ;for this LPT
  353. prt_status      db      10h             ;printer status for this LPT
  354. linefeed        db      ON              ;ON = output a linefeed
  355.                                         ;OFF = don't output a linefeed
  356. buffer  db      BUFSIZE dup(0)          ;data buffer for this LPT
  357. ;
  358. p_block ends
  359. ;
  360. ;-----------------------------------------------------------------
  361. ;
  362. bios_data     segment at 40h
  363.               org 4Ah
  364. crt_cols      dw ?                          ;number of display columns
  365.               org 4Eh
  366. crt_start     dw ?                          ;video page offset address
  367.               org 63h
  368. addr_6845     dw ?                          ;CRTC base address
  369.               org 87h
  370. infobyte      label word
  371. ega_info      db ?                          ;EGA info byte
  372. bios_data     ends
  373. ;
  374. ;-----------------------------------------------------------------
  375. ;
  376. subttl  Main Code
  377. page
  378. %out Assembling CODE Segment
  379. cseg    segment para public 'CODE'
  380.         assume  CS:cseg,DS:nothing,SS:nothing
  381.         org     100h
  382. lptx:   jmp     lptx_start
  383. ;
  384. ; What follows are three allocations of the Structure p_block
  385. ; One for each line printer that we can support.
  386. ; With this, all three line printers have DMA buffers and flag
  387. ; variables.
  388. ; BX is used to point to the offset of the allocation currently in use
  389. ; Line printer 1
  390. lpt1            p_block <,,,'1'>
  391. ; Line printer 2
  392. lpt2            p_block <,,,'2'>
  393. ; Line printer 3
  394. lpt3            p_block <,,,'3'>
  395. ;
  396. crit_flag       db      OFF             ;set to ON if critical error occured
  397. off_crit        dw      0               ;save old critical error address
  398. seg_crit        dw      0
  399. ;
  400. csect_off       dw      0               ;pointer to Critical Section flag
  401. csect_seg       dw      0
  402. cerrf_off       dw      0               ;pointer to Critical Error flag
  403. cerrf_seg       dw      0
  404. ; cs_switch can be cleared by the transient copy of LPTx
  405. cs_switch       db      ON              ;enable check of Critical Section flag
  406. P_NORMAL        equ     90h             ;Printed selected and ready
  407. P_TIMEOUT       equ     01h             ;Time out
  408. save_psp        dw      0               ;Save area for User's PSP Segment Address
  409. lptx_psp        dw      0               ;Our PSP Segment Address
  410. byte_count      dw      0               ;to save DOS byte count
  411. busy            db      OFF             ;ON indicates write is taking place
  412. sound           db      OFF             ;ON uses speaker to indicate progress of LPTx
  413. ;
  414. flag_10h           db   0                   ;status of interrupt 10h
  415. flag_13h           db   0                   ;status of interrupt 13h
  416. request_popup      db   0                   ;status of processing request
  417. adapter            db   2                   ;0=MDA, 1=CGA, 2=EGA
  418. ;The following 5 lines MUST not be changed. They are dynamically set at Run Time
  419. video_segment      dw   0B800h              ;video segment address
  420. border_attr        db   ?                   ;window border attribute
  421. text_attr          db   ?                   ;window text attribute
  422. menu_attr          db   ?                   ;menu line attribute
  423. video_page         db   ?                   ;current video page
  424. cursor_mode        dw   ?                   ;cursor shape
  425. cursor_pos         dw   ?                   ;cursor position
  426. cursor_addr        dw   ?                   ;cursor CRTC address
  427. new_cursor         dw   0607h               ;Popup cursor shape
  428. nocolors           db   0                   ;monochrome only
  429. menu_choice        db   0                   ;popup cursor line (0,1,2)
  430. screen_buffer      db   48    dup('SCREEN BUFFER   ') ;10 * 34 * 2 bytes
  431. color_attr         db   0,0B8h,10h,07h,3fh  ;black/blue,white/black,hi-white/blue
  432. nocolor_attr       db   0,0B8h,70h,07h,70h  ;mono
  433. mono_attr          db   0,0B0h,70h,07h,70h
  434. enable_values      db   2Ch,28h,2Dh,29h,2Ah,2Eh,1Eh
  435. ;
  436. ; Old interrupt vector addresses
  437. old_08h dd      0                       ;address of old int 08h routine
  438. old_09h dd      0                       ;address of old int 09h routine
  439. old_10h dd      0                       ;address of old int 10h routine
  440. old_13h dd      0                       ;address of old int 13h routine
  441. old_17h dd      0                       ;address of old int 17h routine
  442. old_21h dd      0                       ;address of old int 21h routine
  443. old_28h dd      0                       ;address of old int 28h routine
  444. ;
  445. old1Bh_segment     dw ?                     ;old interrupt 1Bh segment
  446. old1Bh_offset      dw ?                     ;old interrupt 1Bh offset
  447. old23h_segment     dw ?                     ;old interrupt 23h segment
  448. old23h_offset      dw ?                     ;old interrupt 23h offset
  449. old24h_segment     dw ?                     ;old interrupt 24h segment
  450. old24h_offset      dw ?                     ;old interrupt 24h offset
  451. ;
  452. ; New Stack for Interrupt 17h
  453. stk1_save       dd      0               ;caller's stack EA
  454.                 db      128 dup('STACK   ')
  455. stk1            equ     this byte - 4
  456. ; New Stack for Interrupts 08h and 28h
  457. stk2_save       dd      0               ;caller's stack EA
  458.                 db      128 dup('STACK   ')
  459. stk2            equ     this byte - 4
  460. ; New Stack for Popup window process
  461. stk3_ss         dw      0               ;caller's stack EA
  462. stk3_sp         dw      0
  463.                 db      128 dup('STACKP  ')
  464. stk3            equ     this byte - 4
  465. ;
  466. ;------------------------------------------------------------------------------
  467. ; Window text
  468. ;------------------------------------------------------------------------------
  469. ;
  470. ;You will need an editor capable of handling graphics to edit the window.
  471. ;I used PC-Write, but old edlin will also work. 
  472. ;Do not change the width or the depth of the window !!!
  473. ;
  474. window          db "┌──────────────LPTX──────────────┐"
  475.                 db "│                                │"
  476. menuline1       db "│       lpt1:  to  PRINTER       │"
  477. menuline2       db "│       lpt2:  to  PRINTER       │"
  478. menuline3       db "│       lpt3:  to  PRINTER       │"
  479.                 db "│                                │"
  480.                 db "├────────────────────────────────┤"
  481.                 db "│ Press:  to move,  to toggle,│"
  482.                 db "│       <Enter> to close window. │"
  483.                 db "└────────────────────────────────┘"
  484. menufile1       db "│       lpt1:  to  FILE          │"
  485. menufile2       db "│       lpt2:  to  FILE          │"
  486. menufile3       db "│       lpt3:  to  FILE          │"
  487. ;------------------------------------------------------------------------------
  488. ; Interrupt handler for interrupt 09h (keyboard)
  489. ;------------------------------------------------------------------------------
  490. ;
  491. keyboard      proc near
  492.               push    ax
  493.               cmp     CS:busy,OFF           ;program already active?
  494.               jne     kb_regular            ; yes, skip processing this key.
  495.               in      al,60h                ;look at the hardware register
  496.               cmp     al,55                 ;is this PrtSc ?
  497.               jne     kb_regular            ; no, process it the old way.
  498.               sti                           ;set interrupt enable flag
  499.               mov     ah,2                  ;get keyboard shift status    
  500.               int     16h
  501.               test    al,8                  ;Alt key also pressed?
  502.               je      kb_regular            ; no, process old way.
  503.               mov     CS:request_popup,18   ;set request flag for 1 second.
  504.                                             ;now, call keyboard handling routine
  505.                                             ; to reset keyboard controller
  506.                                             ; and as courtesy to other TSRs.
  507. kb_regular:   pop     ax
  508.               jmp     dword ptr CS:old_09h
  509. keyboard      endp
  510. ;
  511. ;------------------------------------------------------------------------------
  512. ;Interrupt 10h handling routine.
  513. ;------------------------------------------------------------------------------
  514. video         proc near
  515.               pushf                         ;push flags onto stack
  516.               inc flag_10h                  ;increment flag
  517.               call dword ptr CS:old_10h     ;call BIOS routine
  518.               dec flag_10h                  ;decrement flag
  519.               iret
  520. video         endp
  521. ;
  522. ;------------------------------------------------------------------------------
  523. ;Interrupt 13h handling routine.
  524. ;------------------------------------------------------------------------------
  525. bdisk         proc far
  526.               pushf                         ;push flags onto stack
  527.               inc flag_13h                  ;set 'busy' flag
  528.               call dword ptr CS:old_13h     ;call BIOS routine
  529.               pushf                         ;save output flags
  530.               dec flag_13h                  ;clear flag
  531.               popff                         ;restore output flags
  532.               ret 2                         ;exit without destroying flags
  533. bdisk         endp
  534. ;
  535. ;
  536. ;-----------------------------------------------------------------
  537. ;
  538. ; Interrupt handler for interrupt 17h
  539. ;
  540. int_17h proc    far
  541.         sti                             ;interrupts on
  542.         cmp     AH,3                    ;AH=3 for LPTx Function
  543.         jne     reg_call                ;This is a regular print call
  544.         jmp     ret_ack                 ;This is an LPTx Call
  545. reg_call:
  546.         push    BX
  547. ; set up BX to point to the data area for the requested printer
  548.         cmp     DX,0                    ;lpt1?
  549.         jne     chk_lpt2                ;no
  550.         mov     BX,offset lpt1          ;offset to LPT1
  551.         jmp     short bx_set
  552. chk_lpt2:
  553.         cmp     DX,1                    ;lpt2?
  554.         jne     chk_lpt3                ;no
  555.         mov     BX,offset lpt2          ;offset to LPT2
  556.         jmp     short bx_set
  557. chk_lpt3:
  558.         cmp     DX,2                    ;lpt3?
  559.         jne     ill_ptr                 ;no - bad printer number
  560.         mov     BX,offset lpt3          ;offset to LPT3
  561. bx_set: 
  562.         cmp     CS:[BX].active,OFF      ;are we active?
  563.         je      sleep17                 ;no
  564.         mov     CS:[BX].prt_status,P_NORMAL     ;signal ready status
  565.         cmp     AH,1                    ;initialize call?
  566.         je      do_nix                  ;yes    
  567.         cmp     AH,2                    ;status call?
  568.         je      do_nix                  ;yes
  569.         cmp     AH,0                    ;print call?
  570.         jne     do_nix                  ;no
  571.  
  572.         jmp     prt_17                  ;we are active
  573. do_nix: mov     AH,CS:[BX].prt_status   ;return print status
  574. rtn:    pop     BX
  575.         iret
  576. ;
  577. ill_ptr:mov     CS:[BX].prt_status,P_TIMEOUT    ;time out status
  578.         jmp     do_nix
  579. ;
  580. ret_ack:                                ;return acknowledgement that I'm here
  581. ; note : Change REQ & ACK from version to version so two versions
  582. ; of LPTx don't get intermixed.
  583.         cmp     DX,REQ                  ;my flag to detect that LPTx is
  584.                                         ;already loaded and alive.
  585.         jne     ret_nak                 ;return a NAK
  586.         mov     DX,ACK                  ;Memory resident LPTx answers with ACK
  587.         push    CS                      ;now set up ES to point to the resident
  588.         pop     ES                      ; data area
  589. ret_nak:
  590.         iret                            ;return to calling program
  591. ;
  592. sleep17:pop     BX                      ;restore BX before we go to sleep
  593.         jmp     dword ptr CS:old_17h    ;jump immediate to original handler
  594. ;
  595. prt_17:
  596.         push    AX                      ; Start the print process.
  597.         push    BX                      ; Character is in AL.
  598.         push    CX
  599.         push    DX
  600.         push    DS
  601.         push    ES
  602.         push    SI
  603.         push    DI
  604.         push    BP
  605. ;
  606.         push    CS                      ; DS is used as the segment register
  607.         pop     DS                      ; for all data during the interrupt
  608. ;
  609.         pushf
  610.         cli
  611.         mov     SI,SS
  612.         mov     word ptr DS:stk1_save+2,SI      ;save caller's stack pointer
  613.         mov     SI,SP
  614.         mov     word ptr DS:stk1_save,SI
  615.         mov     SI,CS
  616.         mov     SS,SI                   ;give me new bigger stack
  617.         mov     SI,offset stk1
  618.         mov     SP,SI
  619.         popff
  620.         mov     DS:[BX].prt_status,P_NORMAL     ;signal ready status
  621.                                         ;prt_status is set before
  622.                                         ;the call to prnt so that prnt
  623.                                         ;can change it if the print to
  624.                                         ;disk fails
  625.         call    prnt                    ;print the character
  626. ;
  627.         pushf
  628.         cli
  629.         mov     SI,word ptr DS:stk1_save
  630.         mov     SP,SI                   ;restore caller's stack pointer
  631.         mov     SI,word ptr DS:stk1_save+2
  632.         mov     SS,SI
  633.         popff
  634.  
  635.         pop     BP
  636.         pop     DI
  637.         pop     SI
  638.         pop     ES
  639.         pop     DS
  640.         pop     DX
  641.         pop     CX
  642.         pop     BX
  643.         pop     AX
  644.         jmp     do_nix
  645. int_17h endp
  646. ;-----------------------------------------------------------------
  647. ;
  648. ; Interrupt handler for interrupt 21h
  649. ;
  650. int_21h proc    far
  651.         sti                             ;interrupts on
  652.         push    BX
  653.         cmp     AH,5                    ;is this a DOS printer call?
  654.         je      int_21h_5               ;yes
  655.         cmp     AH,40h                  ;is this a DOS write call?
  656.         je      int_21h_40              ;yes
  657.         jmp     sleep21                 ;no - go on to real DOS
  658. ; DOS Function 40h - Write to File or Device. DS:DX contains the address
  659. ;of data to write. CX contains the byte count. Return AX = byte count.
  660. int_21h_40:                             
  661.         cmp     BX,0004                 ;Standard Printer?
  662.         jne     sleep21                 ;no - go on to real DOS
  663. ; set up BX to point to the data area for the requested printer
  664. ; Since we don't know what the standard printer device is, we
  665. ; use LPT1
  666.         mov     BX,offset lpt1          ;offset to LPT1
  667.         cmp     CS:[BX].active,OFF      ;are we active?
  668.         je      sleep21                 ;no
  669.         push    AX                      ;now do it
  670.         push    BX
  671.         push    CX
  672.         push    DX
  673.         mov     CS:byte_count,CX        ;save byte count
  674.         mov     BX,DX
  675. ;DS:BX points to buffer, CX has byte count
  676.         cmp     CX,EMPTY                ;check for zero byte count
  677.         je      prt_21_done
  678. loop21_40:      
  679.         mov     AL,[BX]                 ;get a character
  680.         mov     AH,0                    ;set up for call to interrupt 17h
  681.         int     17h                     ;note : int17h returns a printer status
  682.                                         ;in AH but DOS does not define a way
  683.                                         ;to return that status
  684.         inc     BX
  685.         loop    loop21_40
  686. prt_21_done:
  687.         pop     DX
  688.         pop     CX
  689.         pop     BX
  690.         pop     AX
  691.         mov     AX,CS:byte_count        ;DOS returns byte count
  692.         jmp     exit21                  ;to return it to the user.
  693. ;
  694. int_21h_5:
  695. ; DOS Function 5 Printer Output. The Character in DL is output to the
  696. ; standard printer device.
  697. ;
  698. ; set up BX to point to the data area for the requested printer
  699. ; Since we don't know what the standard printer device is, we
  700. ; use LPT1
  701.         mov     BX,offset lpt1          ;offset to LPT1
  702.         cmp     CS:[BX].active,OFF      ;are we active?
  703.         je      sleep21                 ;no
  704.  
  705.         push    AX
  706.         mov     AH,0                    ;set up for call to interrupt 17h
  707.         mov     AL,DL                   ;the character
  708.         int     17h
  709.         pop     AX
  710.                                         ;note : int17h returns a printer status
  711.                                         ;in AH but DOS does not define a way
  712.                                         ;to return it to the user.
  713. exit21: pop     BX
  714.         popff                           ;restore flags
  715.         clc                             ;never an error from us
  716.         ret                             ;return to caller (regular FAR return)
  717. ;
  718. sleep21:pop     BX                      ;restore BX before we go to sleep
  719.         jmp     dword ptr CS:old_21h
  720. int_21h endp
  721. ;
  722. ;-----------------------------------------------------------------
  723. ;
  724. ; PRNT - Print a character in AL
  725. ;
  726. prnt    proc    near
  727.         push    DS
  728.         cmp     DS:[BX].active,OFF
  729.         je      prtext                  ;nothing there?
  730.         push    AX
  731.         cmp     DS:[BX].bufcntr,BUFSIZE/2       ;buffer half full?
  732.         jne     intadd                  ;no
  733. ;
  734. ; set write request but don't actually write anything out.
  735. ; we hope that the write can take place before the buffer fills up
  736. ;
  737.         mov     DS:[BX].request,ON
  738.         cmp     DS:sound,OFF            ;sound on?
  739.         je      intadd                  ;no
  740.         call    horn                    ;sound horn to indicate buffer
  741.                                         ; write request has been made
  742. ;
  743. intadd: pop     AX
  744.         cmp     AL,LF                   ;is it a linefeed?
  745.         jne     intnolf                 ;no
  746.         cmp     DS:[BX].linefeed,OFF    ;we are stripping linefeeds?
  747.         je      prtext                  ;yes
  748. intnolf:
  749.         mov     DI,BX                   ;offset of this printer's allocation
  750.         add     DI,offset lpt1.buffer   ;add in offset of buffer
  751.         add     DI,DS:[BX].bufcntr      ;add in current byte count
  752.         mov     DS:[DI],AL              ;stuff it
  753.         inc     DS:[BX].bufcntr
  754.         cmp     DS:[BX].bufcntr,BUFSIZE ;buffer overflow?
  755.         jne     prtext                  ;no
  756.         mov     DS:[BX].active,OFF      ;yes, nothing to do but deactivate
  757.                                         ; LPTx
  758.         cmp     DS:sound,OFF            ;sound on?
  759.         je      prtext                  ;no
  760.         call    beep                    ;sound beep twice to indicate that
  761.         call    beep                    ;we have been deactivated
  762. prtext: pop     DS
  763.         ret                             ;done
  764. prnt    endp
  765. ;-----------------------------------------------------------------
  766. ;
  767. ; Critical Error Handler
  768. ;
  769. crit_int        proc    far             ;got critical error
  770.         mov     CS:crit_flag,ON         ; set flag
  771.         mov     AL,0                    ;tells DOS to ignore the
  772.         iret                            ;error
  773. crit_int        endp
  774. ;-----------------------------------------------------------------
  775. ;
  776. ; Interrupt handler for interrupt 08h - clock ticks
  777. ;
  778. ; This function is installed as a handler for hardware interrupt
  779. ; type 8. It first calls the previous int 08h handler to service
  780. ; the INTEL 8259 Programmable Interrupt Controller. Then it checks
  781. ; to see if any write requests are pending. If so, it calls do_save
  782. ; and flush to write the buffer to the disk. Note that int_08h
  783. ; checks the DOS critical section flag : do_save is called only if
  784. ; DOS is available.
  785. ;
  786. ; This function protects itself against secondary invocations by
  787. ; means of the global busy flag.
  788. ;
  789. int_08h proc    far
  790. ; call original int 8h handler - ALWAYS
  791.         pushf
  792.         call    dword ptr CS:old_08h
  793. ; now we can process if possible
  794.         pushf
  795.         cli
  796.         cmp     CS:busy,OFF             ;can we process this?
  797.         jne     i08_exit                ;no
  798.         cmp     CS:flag_10h,0           ;video flag set?
  799.         jne     dectime                 ;yes, then exit
  800.         cmp     CS:flag_13h,0           ;disk flag set?
  801.         jne     dectime                 ;yes, then exit
  802. ; if DOS is in its critical section, we skip the write for now and
  803. ; hope that we can write before the buffer fills
  804.         push    DS                      ; check the critical section flag
  805.         push    SI
  806.         push    BX
  807.         cmp     CS:cs_switch,OFF        ;is checking off?
  808.         je      no_cs                   ;yes, don't check the flag
  809.         lds     SI,dword ptr CS:csect_off
  810.         cmp     byte ptr [SI],OFF
  811.         jne     poptime                 ;DOS in critical section
  812.                                         ;this indicates that we cannot
  813.                                         ;do any disk operations at this
  814.                                         ;time
  815.         lds     SI,dword ptr CS:cerrf_off
  816.         cmp     byte ptr [SI],OFF
  817.         jne     poptime                 ;DOS in critical error
  818.                                         ;this indicates that we cannot
  819.                                         ;do any disk operations at this
  820.                                         ;time
  821. no_cs:
  822.         cmp     CS:request_popup,0
  823.         je      no_popup
  824.         call    do_popup
  825. no_popup:
  826.         mov     BX,offset lpt1          ;offset to LPT1
  827.         cmp     DS:[BX].request,ON      ;write LPT1 request?
  828.         jne     no_lpt1                 ;no
  829.         call    do_save
  830. no_lpt1:
  831.         mov     BX,offset lpt2          ;offset to LPT2
  832.         cmp     DS:[BX].request,ON      ;write LPT2 request?
  833.         jne     no_lpt2                 ;no
  834.         call    do_save
  835. no_lpt2:
  836.         mov     BX,offset lpt3          ;offset to LPT3
  837.         cmp     DS:[BX].request,ON      ;write LPT3 request?
  838.         jne     poptime                 ;no
  839.         call    do_save
  840. poptime:
  841.         pop     BX
  842.         pop     SI
  843.         pop     DS
  844. dectime:
  845.         cmp     request_popup,0
  846.         je      i08_exit
  847.         dec     request_popup
  848. i08_exit:
  849.         popff
  850.         iret
  851.  
  852. int_08h endp
  853. ;-----------------------------------------------------------------
  854. ;
  855. ; Interrupt handler for interrupt 28h - idle
  856. ;
  857. ; This function is installed as a handler for hardware interrupt
  858. ; type 28h. It first calls the previous int 28h handler. Then it checks
  859. ; to see if any write requests are pending. If so, it calls do_save
  860. ; and flush to write the buffer to the disk. It does not check the
  861. ; Critical Section Flag as int 28h handlers are allowed to perform
  862. ; DOS disk I/O but not keyboard I/O since most of the time int 28h
  863. ; is called by the keyboard I/O routines when they are waiting
  864. ; for a key press.
  865. ;
  866. ; This function protects itself against secondary invocations by
  867. ; means of the global busy flag.
  868. ;
  869. int_28h proc    far
  870. ; call original int 28h handler - ALWAYS
  871.         pushf
  872.         call    dword ptr CS:old_28h
  873. ; now we can process if possible
  874.         pushf
  875.         cli
  876.         cmp     CS:busy,OFF             ;can we process this?
  877.         jne     i28_exit                ;no
  878.         cmp     CS:flag_10h,0           ;video flag set?
  879.         jne     i28_exit                ;yes, then exit
  880.         cmp     CS:flag_13h,0           ;disk flag set?
  881.         jne     i28_exit                ;yes, then exit
  882.         cmp     CS:cs_switch,OFF        ;is checking off?
  883.         je      i28_no_cs               ;yes, don't check the flag
  884.         push    DS
  885.         push    SI
  886.         lds     SI,dword ptr CS:cerrf_off
  887.         cmp     byte ptr [SI],OFF
  888.         pop     SI
  889.         pop     DS
  890.         jne     i28_exit                ;DOS in critical error
  891.                                         ;this indicates that we cannot
  892.                                         ;do any disk operations at this
  893.                                         ;time
  894. i28_no_cs:
  895.         cmp     CS:request_popup,0
  896.         je      i28_nopopup
  897.         call    do_popup
  898. i28_nopopup:
  899.         call    do_save
  900. i28_exit:
  901.         popff
  902.         iret
  903. int_28h endp
  904. ;-----------------------------------------------------------------
  905. ;
  906. do_popup proc   near
  907.         mov     CS:busy,ON
  908.         mov     CS:request_popup,0
  909.         cli
  910.         mov     CS:stk3_ss,SS
  911.         mov     CS:stk3_sp,SP
  912.         push    cs
  913.         pop     ss
  914.         mov     sp,offset stk3
  915.         sti                             ;interrupts on
  916.         push    AX                      ; Save everything
  917.         push    BX
  918.         push    CX
  919.         push    DX
  920.         push    DS
  921.         push    ES
  922.         push    SI
  923.         push    DI
  924.         push    BP
  925.         pushf
  926. ;
  927.               mov ah,15                     ;get video mode and page
  928.               int 10h
  929.               cmp al,3                      ;mode 0, 1, 2, or 3?
  930.               jbe pu_main1                  ;yes, then continue
  931.               cmp al,7                      ;mode 7?
  932.               je pu_main1                   ;yes, then continue
  933.                                             ;no, we must be in graphics, and
  934.                                             ;I don't know how to open a window
  935.                                             ;while in graphics...
  936. ;
  937. ;Restore registers and stack before exit.
  938. ;
  939. do_pop_exit:
  940.         popff
  941.         pop     BP
  942.         pop     DI
  943.         pop     SI
  944.         pop     ES
  945.         pop     DS
  946.         pop     DX
  947.         pop     CX
  948.         pop     BX
  949.         pop     AX
  950.         cli
  951.         mov     ss,CS:stk3_ss
  952.         mov     sp,CS:stk3_sp
  953.         sti                             ;interrupts on
  954.         mov     CS:busy,OFF
  955.         ret
  956. ;
  957. ;Set DS and ES segment registers.
  958. ;
  959. pu_main1:     push cs                       ;set DS to code segment
  960.               pop ds
  961.               assume ds:cseg
  962.               mov ax,bios_data              ;point ES to BIOS data
  963.               mov es,ax
  964.               assume es:bios_data
  965.               cmp crt_cols,80               ;at least 80 columns displayed?
  966.               jb do_pop_exit                ;no, then exit
  967. ;
  968. ;Save needed video parameters.
  969. ;
  970.               mov video_page,bh             ;active video page
  971.               mov ah,3                      ;get cursor mode and location
  972.               int 10h
  973.               mov cursor_mode,cx
  974.               mov cursor_pos,dx
  975.               mov dx,addr_6845              ;get CRTC base address
  976.               push dx                       ;save it for later
  977.               mov al,14                     ;specify register number
  978.               out dx,al
  979.               inc dx                        ;point DX to data port
  980.               in al,dx                      ;read high byte of address
  981.               mov ah,al                     ;save it
  982.               dec dx                        ;back to index register
  983.               mov al,15                     ;specify register number
  984.               out dx,al
  985.               inc dx                        ;back to data port
  986.               in al,dx                      ;read low byte of address
  987.               mov cursor_addr,ax            ;save cursor address
  988. ;
  989. ;Determine whether an EGA is present and active in the system.
  990. ;
  991.               mov ah,12h                    ;see if EGA is present
  992.               mov bl,10h
  993.               int 10h
  994.               cmp bl,10h                    ;did BL return unchanged?
  995.               je pu_main2                   ;yes, then there's no EGA here
  996.               test ega_info,8               ;is the EGA currently active?
  997.               jnz pu_main2                  ;no, then branch
  998.               mov adapter,2                 ;set ADAPTER for EGA
  999.               push bx                       ;save BX
  1000.               mov ax,1130h                  ;get number of scan lines per char
  1001.               int 10h
  1002.               dec cl                        ;form cursor definition in CX
  1003.               mov ch,cl
  1004.               sub ch,2
  1005.               mov new_cursor,cx             ;save cursor definition
  1006.               mov si,offset color_attr      ;point SI to color parms
  1007.               pop bx                        ;retrieve BX
  1008.               or bh,bh                      ;EGA attached to color monitor?
  1009.               je pu_hascolor                ;yes, then branch
  1010.               mov si,offset mono_attr       ;no, then point SI to mono parms
  1011.               jmp short pu_main4
  1012. pu_hascolor:  cmp nocolors,0                ;use monochrome only ?
  1013.               je pu_main4                   ; no, use color
  1014.               mov si,offset nocolor_attr    ;color card with mono screen
  1015.               jmp short pu_main4
  1016. ;
  1017. ;Determine whether the active video adapter is a CGA or an MDA.
  1018. ;
  1019. pu_main2:     test addr_6845,40h            ;is bit 6 of the CRTC address set?
  1020.               jz pu_main3                   ;no, then it's monochrome
  1021.               mov adapter,1                 ;set ADAPTER for a CGA
  1022.               mov new_cursor,0607h          ;define cursor shape
  1023.               mov si,offset color_attr      ;point SI to color parms
  1024.               jmp short pu_hascolor
  1025. pu_main3:     mov adapter,0                 ;set ADAPTER for an MDA
  1026.               mov new_cursor,0B0Ch          ;define monochrome cursor
  1027.               mov si,offset mono_attr       ;point SI to mono parms
  1028. ;
  1029. ;Set video parameters for color or monochrome.
  1030. ;
  1031. pu_main4:     push cs                       ;set ES to the code segment
  1032.               pop es
  1033.               assume es:cseg
  1034.               mov di,offset video_segment   ;point DI to destination
  1035.               mov cx,5                      ;5 bytes to move
  1036.               cld                           ;clear DF
  1037.               rep movsb                     ;transfer the values
  1038. ;
  1039.               cmp adapter,1                 ;disable CGA video
  1040.               jne pu_main7
  1041.               call disable_cga
  1042. pu_main7:     call save_screen              ;save memory to be overwritten
  1043.               call make_screen              ;open the Pop-up window
  1044.               call make_status              ;update status of the redirection
  1045.               cmp adapter,1                 ;enable CGA video
  1046.               jne pu_main8
  1047.               call enable_cga
  1048. pu_main8:     mov ah,1                      ;hide the cursor
  1049.               mov ch,20h
  1050.               int 10h
  1051. ;
  1052. ;Wait for a keystroke and return when ESC is pressed.
  1053. ;
  1054. pu_mainA:     call getkey                   ;wait for a keypress
  1055.               or al,al                      ;extended code entered?
  1056.               je pu_main12                  ;yes, then branch
  1057.               cmp al,32                     ;SPACE pressed?
  1058.               je toggle                     ;yes, toggle line
  1059.               cmp al,13                     ;ENTER pressed?
  1060.               je escape                     ;yes, then get out
  1061.               cmp al,27                     ;ESC pressed?
  1062.               je escape                     ;yes, then close window and return
  1063.               jmp pu_mainA                  ;no, then ignore keypress
  1064. ;
  1065. ;An extended code was entered.  Check for an Alt-character key combination.
  1066. ;
  1067. pu_main12:    cmp ah,72                     ;UpArrow?
  1068.               je upone                      ;yes, then process it
  1069.               cmp ah,80                     ;DownArrow?
  1070.               je downone                    ;yes, then process it
  1071.               cmp ah,75                     ;LeftArrow?
  1072.               je toggle                     ;toggle line
  1073.               cmp ah,77                     ;RightArrow?
  1074.               je toggle                     ;toggle line
  1075.               jmp pu_mainA                  ;return for another keypress
  1076. ;
  1077. upone:        cmp menu_choice,0             ;are we at the top ?
  1078.               jne upone1                    ; no, decrement it
  1079.               mov menu_choice,3             ; yes, wrap around
  1080. upone1:       dec menu_choice
  1081.               call make_status              ;update status of the redirection
  1082.               jmp pu_mainA
  1083.  
  1084. downone:      cmp menu_choice,2             ;are we at the bottom ?
  1085.               jne downone1                  ; no, increment
  1086.               mov menu_choice,-1            ; yes, wrap around
  1087. downone1:     inc menu_choice
  1088.               call make_status              ;update status of the redirection
  1089.               jmp pu_mainA
  1090.  
  1091. toggle:       call make_change             ;change the state
  1092.               call make_status              ;update status of the redirection
  1093.               jmp pu_mainA
  1094. ;
  1095. ;Close the Pop-up window in preparation for return.
  1096. ;
  1097. escape:       
  1098.               cmp adapter,1                 ;disable CGA video
  1099.               jne esc1
  1100.               call disable_cga
  1101. esc1:         call restore_screen           ;restore screen contents
  1102.               cmp adapter,1                 ;enable CGA video
  1103.               jne esc2
  1104.               call enable_cga
  1105. ;
  1106. ;Restore the cursor's former position in the BIOS data area and the CRTC.
  1107. ;
  1108. esc2:         mov ah,2                      ;set cursor position thru BIOS
  1109.               mov bh,video_page
  1110.               mov dx,cursor_pos
  1111.               int 10h
  1112.               pop dx                        ;recover CRTC base address
  1113.               mov cx,cursor_addr            ;restore cursor address
  1114.               mov al,14                     ;CRTC register number
  1115.               out dx,al
  1116.               inc dx
  1117.               mov al,ch
  1118.               out dx,al                     ;write high byte of address
  1119.               dec dx
  1120.               mov al,15                     ;CRTC register number
  1121.               out dx,al
  1122.               inc dx
  1123.               mov al,cl
  1124.               out dx,al                     ;write low byte
  1125. ;
  1126. ;Display the cursor, bypassing EGA cursor emulation logic, and return.
  1127. ;
  1128.               cmp adapter,2                 ;EGA on board?
  1129.               jne esc3                      ;no, then branch
  1130.               call show_cursor              ;retain current shape
  1131.               jmp do_pop_exit               ; exit from popup
  1132. esc3:         mov ah,1                      ;restore cursor shape
  1133.               mov cx,cursor_mode
  1134.               int 10h
  1135.               jmp do_pop_exit               ; exit from popup
  1136. do_popup endp
  1137. ;
  1138. ;------------------------------------------------------------
  1139. ;
  1140. ; Writes buffer to disk
  1141. ;
  1142. do_save proc    near
  1143.         mov     CS:busy,ON              ; set busy flag
  1144.         push    AX                      ; Start the print process.
  1145.         push    BX                      ; Character is in AL.
  1146.         push    CX
  1147.         push    DX
  1148.         push    DS
  1149.         push    ES
  1150.         push    SI
  1151.         push    DI
  1152.         push    BP
  1153.         pushf
  1154.         sti                             ;interrupts on
  1155. ;
  1156.         push    CS                      ; DS is used as the segment register
  1157.         pop     DS                      ; for all data during the interrupt
  1158. ;
  1159.         pushf
  1160.         cli
  1161.         mov     SI,SS
  1162.         mov     word ptr DS:stk2_save+2,SI      ;save caller's stack pointer
  1163.         mov     SI,SP
  1164.         mov     word ptr DS:stk2_save,SI
  1165.         mov     SI,CS
  1166.         mov     SS,SI                   ;give me new bigger stack
  1167.         mov     SI,offset stk2
  1168.         mov     SP,SI
  1169.         popff
  1170. ;
  1171. ; check to see if any write requests are active.
  1172. ; we do not look at the active flag since the print redirection
  1173. ; may have been inactivated by the time, we are able to write to
  1174. ; the disk.
  1175. ;
  1176. ; set up BX to point to the data area for the requested printer
  1177.         mov     BX,offset lpt1          ;offset to LPT1
  1178.         cmp     DS:[BX].request,ON      ;write LPT1 request?
  1179.         jne     c28_lpt2                ;no
  1180.         call    flush
  1181.         mov     DS:[BX].request,OFF
  1182. c28_lpt2:
  1183.         mov     BX,offset lpt2          ;offset to LPT2
  1184.         cmp     DS:[BX].request,ON      ;write LPT2 request?
  1185.         jne     c28_lpt3                ;no
  1186.         call    flush
  1187.         mov     DS:[BX].request,OFF
  1188. c28_lpt3:
  1189.         mov     BX,offset lpt3          ;offset to LPT3
  1190.         cmp     DS:[BX].request,ON      ;write LPT3 request?
  1191.         jne     i28_done                ;no
  1192.         call    flush
  1193.         mov     DS:[BX].request,OFF
  1194. i28_done:
  1195.         pushf
  1196.         cli
  1197.         mov     SI,word ptr DS:stk2_save
  1198.         mov     SP,SI                   ;restore caller's stack pointer
  1199.         mov     SI,word ptr DS:stk2_save+2
  1200.         mov     SS,SI
  1201.         popff
  1202. ;
  1203.         popff
  1204.         pop     BP
  1205.         pop     DI
  1206.         pop     SI
  1207.         pop     ES
  1208.         pop     DS
  1209.         pop     DX
  1210.         pop     CX
  1211.         pop     BX
  1212.         pop     AX
  1213.         mov     CS:busy,OFF
  1214.         ret
  1215. do_save endp
  1216. ;
  1217. ;------------------------------------------------------------
  1218. ;
  1219. ; FLUSH - Flush print buffer to disk file
  1220. ;
  1221. flush   proc    near
  1222.         cmp     DS:[BX].bufcntr,EMPTY   ;buffer empty?
  1223.         jne     flush_buf               ;no, write it to disk
  1224.         ret                             ;exit
  1225. flush_buf:
  1226.         call    disk_out
  1227.         ret
  1228. flush   endp
  1229. ;------------------------------------------------------------
  1230. ;
  1231. ; DISK_OUT - write to disk
  1232. ;
  1233. disk_out        proc    near
  1234. ;PSP
  1235. ;       push    BX
  1236. ;       mov     AH,51h                  ;get current PSP
  1237. ;       call_dos
  1238. ;       mov     DS:save_psp,BX          ;and save it
  1239. ;       mov     BX,DS:lptx_psp          ;get our PSP
  1240. ;       mov     AH,50h
  1241. ;       call_dos                        ;set it into DOS
  1242. ;       pop     BX
  1243. ;
  1244.         push    ES
  1245.         push    DS
  1246.         mov     AX,DS                   ;set up ES
  1247.         mov     ES,AX
  1248.         push    BX
  1249.         push    ES
  1250.         mov     AX,3524h                ;get old critical error vector
  1251.         call_dos
  1252.         mov     DS:off_crit,BX
  1253.         mov     DS:seg_crit,ES
  1254.         mov     DX,offset crit_int
  1255.         mov     AX,2524h        
  1256.         call_dos                        ;trap critical error vector
  1257.         mov     DS:crit_flag,OFF        ;clear critical error flag
  1258.         pop     ES
  1259.         pop     BX
  1260.         mov     DX,BX                   ;open file
  1261.         add     DX,offset lpt1.filen    ;filename
  1262.         mov     AL,1                    ;open for writing
  1263.         mov     AH,open_FILE
  1264.         call_dos
  1265.         mov     DS:[BX].handle,AX       ;file handle
  1266.         jc      disk_err                ;error
  1267.         cmp     DS:crit_flag,ON         ;critical error?
  1268.         je      disk_err                ;yes
  1269.         push    BX
  1270.         mov     AH,lseek_FILE
  1271.         mov     AL,2                    ;end of file
  1272.         mov     CX,0                    ;offset 0
  1273.         mov     DX,0
  1274.         mov     BX,DS:[BX].handle
  1275.         call_dos
  1276.         pop     BX
  1277.         jc      disk_err                ;some seek error
  1278.         cmp     DS:crit_flag,ON         ;critical error?
  1279.         je      disk_err                ;yes
  1280.         mov     CX,DS:[BX].bufcntr      ;buffer size
  1281.         mov     DX,BX                   ;offset of structure allocation
  1282.         add     DX,offset lpt1.buffer   ;add offset of buffer within the
  1283.                                         ;       allocation
  1284.         push    BX
  1285.         mov     AH,write_FILE
  1286.         mov     BX,DS:[BX].handle       ;file handle
  1287.         call_dos                        ;buffer address is DS:DX
  1288.         pop     BX
  1289.         jnc     disk_ok
  1290.         cmp     DS:crit_flag,ON         ;critical error?
  1291.         je      disk_err                ;yes
  1292.         cmp     AX,DS:[BX].bufcntr      ;did DOS write it all?
  1293.         je      disk_ok                 ;yes
  1294. disk_err:
  1295.         call    beep                    ;ring bell 4 times
  1296.         call    beep
  1297.         call    beep
  1298.         call    beep
  1299.         mov     DS:[BX].active,OFF      ;turn us off
  1300.         mov     DS:crit_flag,OFF        ;clear error flag
  1301.         mov     DS:[BX].prt_status,P_TIMEOUT    ;signal time out error
  1302.                                         ;then try to close the file
  1303.                                         ;to save what we can
  1304.         jmp     disk_close
  1305. disk_ok:
  1306.         mov     DS:[BX].bufcntr,EMPTY   ;set buffer empty
  1307. disk_close:
  1308.         push    BX
  1309.         mov     BX,DS:[BX].handle
  1310.         mov     AH,close_FILE           ;close the file
  1311.         call_dos
  1312.         pop     BX
  1313. disk_exit:
  1314.         pop     DS
  1315.         pop     ES
  1316.         push    DS
  1317.         lds     DX,dword ptr DS:off_crit
  1318.         mov     AX,2524h                ;restore critical error vector
  1319.         call_dos
  1320.         pop     DS
  1321. ; PSP
  1322. ;       push    BX
  1323. ;       mov     BX,DS:save_psp          ;get user's PSP
  1324. ;       mov     AH,50h
  1325. ;       call_dos                        ;set it back into DOS
  1326. ;       pop     BX
  1327. ;
  1328.         ret
  1329. disk_out        endp
  1330. ;
  1331. ;--------------------------------------------------------------
  1332. ; Routine to sound the beeper
  1333. ;
  1334. note            equ     0a98h           ;2712 = 1193180. / 440Hz
  1335. beep    proc    near
  1336.         push    AX
  1337.         push    BX
  1338.         push    CX
  1339.         push    DX
  1340.         mov     AL,0b6h                 ;select tim 2,lsb,msb,binary
  1341.         out     43h,AL                  ;set up timer chip
  1342.         mov     AX,note                 ;get note
  1343.         out     42h,AL                  ;write timer 2 count:  lsb...
  1344.         mov     AL,AH
  1345.         out     42h,AL                  ;...and msb
  1346.         in      AL,61h
  1347.         mov     AH,AL                   ;save current port setting
  1348.         or      AL,3
  1349.         out     61h,AL                  ;turn speaker on
  1350.         mov     CX,07FFFh
  1351. beep_lp:loop    beep_lp
  1352.         mov     AL,AH
  1353.         out     61h,AL                  ;restore port setting
  1354.         mov     CX,03FFFh
  1355. beep1_lp:loop   beep1_lp
  1356.         pop     DX
  1357.         pop     CX
  1358.         pop     BX
  1359.         pop     AX
  1360.         ret                             ;return to caller
  1361. beep    endp
  1362. ;-------------------------------------------------------------------
  1363. ; Routine to sound a key click
  1364. ;
  1365. key_clk         equ     036h            ;59 = 1193180. / 20,000Hz
  1366. click   proc    near
  1367.         push    AX
  1368.         push    BX
  1369.         push    CX
  1370.         mov     AL,0b6h                 ;select tim 2,lsb,msb,binary
  1371.         out     43h,AL                  ;set up timer chip
  1372.         mov     AX,key_clk              ;get note
  1373.         out     42h,AL                  ;write timer 2 count:  lsb...
  1374.         mov     AL,AH
  1375.         out     42h,AL                  ;...and msb
  1376.         in      AL,61h
  1377.         mov     AH,AL                   ;save current port setting
  1378.         or      AL,3
  1379.         out     61h,AL                  ;turn speaker on
  1380.         mov     CX,0FFh
  1381. key_lp:loop     key_lp
  1382.         mov     AL,AH
  1383.         out     61h,AL                  ;restore port setting
  1384.         mov     CX,0FFh
  1385. key1_lp:loop    key1_lp
  1386.         pop     CX
  1387.         pop     BX
  1388.         pop     AX
  1389.         ret                             ;return to caller
  1390. click   endp
  1391. ;--------------------------------------------------------------
  1392. ; Routine to honk the horn
  1393. ;
  1394. horn    proc    near
  1395.         push    AX
  1396.         push    BX
  1397.         push    CX
  1398.         push    DX
  1399.         mov     AL,0b6h                 ;select tim 2,lsb,msb,binary
  1400.         out     43h,AL                  ;set up timer chip
  1401.         mov     AX,2e9bh                ;2712 = 1193180. / 100Hz
  1402.         out     42h,AL                  ;write timer 2 count:  lsb...
  1403.         mov     AL,AH
  1404.         out     42h,AL                  ;...and msb
  1405.         in      AL,61h
  1406.         mov     AH,AL                   ;save current port setting
  1407.         or      AL,3
  1408.         out     61h,AL                  ;turn speaker on
  1409.         mov     CX,07FFFh
  1410. horn_lp:loop    horn_lp
  1411.         mov     AL,AH
  1412.         out     61h,AL                  ;restore port setting
  1413.         mov     CX,03FFFh
  1414. horn1_lp:loop   horn1_lp
  1415.         pop     DX
  1416.         pop     CX
  1417.         pop     BX
  1418.         pop     AX
  1419.         ret                             ;return to caller
  1420. horn    endp
  1421. ;--------------------------------------------------------------------
  1422.  
  1423. ;------------------------------------------------------------------------------
  1424. ;SAVE_SCREEN saves the contents of the screen that underlie the window.
  1425. ;------------------------------------------------------------------------------
  1426. video_address dw ?                          ;video address
  1427. linesum       dw ?
  1428. ;
  1429. save_screen   proc near
  1430.               push ds                       ;save DS
  1431.               mov ax,bios_data              ;point it to BIOS data area
  1432.               mov ds,ax
  1433.               assume ds:bios_data
  1434.               mov ax,crt_cols               ;get number of display columns
  1435.               mov linesum,ax                ;calculate distance from end of
  1436.               sub linesum,34                ;  one line to start of next
  1437.               shl linesum,1                 ;  accounting for attribute bytes
  1438.               mov bl,6                      ;calculate starting addrss (6 lines)
  1439.               mul bl                        ;result in AX
  1440.               add ax,18                     ;add line offset (18 columns)
  1441.               shl ax,1                      ;double result for attr bytes
  1442.               mov si,ax                     ;transfer to SI
  1443.               add si,crt_start              ;add page offset
  1444.               mov video_address,si          ;save offset address
  1445.               mov ds,video_segment          ;then set DS to the video segment
  1446.               mov di,offset screen_buffer   ;point DI to storage buffer
  1447.               mov cx,10                     ;10 lines to save
  1448. save_screen1: push cx                       ;save line count
  1449.               mov cx,34                     ;34 characters per line
  1450.               rep movsw                     ;transfer one line to storage
  1451.               pop cx                        ;retrieve line count
  1452.               add si,linesum                ;point SI to next video line
  1453.               loop save_screen1             ;loop until all lines are saved
  1454.               pop ds                        ;restore DS
  1455.               assume ds:cseg
  1456.               ret                           ;exit
  1457. save_screen   endp
  1458. ;
  1459. ;------------------------------------------------------------------------------
  1460. ;RESTORE_SCREEN writes the stored screen image to video memory.
  1461. ;------------------------------------------------------------------------------
  1462. restore_screen proc near
  1463.               push es                       ;save ES register value
  1464.               mov di,video_address          ;point DI to starting video offset
  1465.               mov es,video_segment          ;point ES to video memory
  1466.               mov si,offset screen_buffer   ;point DS:SI to screen image
  1467.               mov cx,10                     ;10 lines to restore
  1468. restore1:     push cx                       ;save line count
  1469.               mov cx,34                     ;34 characters per line
  1470.               rep movsw                     ;restore one line
  1471.               pop cx                        ;retrieve line count
  1472.               add di,linesum                ;set DI to next video line
  1473.               loop restore1                 ;loop until done
  1474.               pop es                        ;restore ES
  1475.               ret
  1476. restore_screen endp
  1477. ;
  1478. ;------------------------------------------------------------------------------
  1479. ;MAKE_SCREEN writes an image of the Pop-up window to video memory.
  1480. ;------------------------------------------------------------------------------
  1481. make_screen   proc near
  1482.               push es                       ;save ES
  1483.               mov es,video_segment          ;point ES:DI to video memory
  1484.               mov di,video_address
  1485.               mov si,offset window          ;line 1
  1486.               mov cx,34                     ;do 34 characters
  1487.               mov ah,border_attr            ;this makes the top border
  1488. make0:        lodsb
  1489.               stosw
  1490.               loop make0
  1491.               add di,linesum
  1492.               mov cx,5                      ;5 lines of user text
  1493. make1:        push cx
  1494.               call formline
  1495.               add di,linesum
  1496.               pop cx
  1497.               loop make1
  1498.               mov cx,4                      ;4 lines of instructions on bottom
  1499.               mov ah,border_attr
  1500. make2:        push cx
  1501.               mov cx,34                     ;do next 34 characters
  1502. make3:        lodsb
  1503.               stosw
  1504.               loop make3
  1505.               add di,linesum
  1506.               pop cx
  1507.               loop make2
  1508.               pop es                        ;restore ES
  1509.               ret
  1510. make_screen   endp
  1511. ;
  1512. ;------------------------------------------------------------------------------
  1513. ;MAKE_STATUS writes the new status of the redirection window to video memory.
  1514. ;------------------------------------------------------------------------------
  1515. make_status   proc near
  1516.               push es                       ;save ES
  1517.               mov es,video_segment          ;point ES:DI to video memory
  1518.               mov di,video_address          ;this point to the beginning of w.
  1519.               add di,34 * 2                 ;skip over the text
  1520.               add di,linesum                ;skip to next line
  1521.               add di,34 * 2                 ;skip over the text
  1522.               add di,linesum                ;go down one line
  1523.  
  1524.               cmp adapter,1                 ;disable CGA video
  1525.               jne msts0
  1526.               call disable_cga
  1527. msts0:
  1528.               mov bx,offset lpt1            ;structure #1
  1529.               cmp ds:[bx].active,OFF        ;printer redirected ?
  1530.               mov si,offset menuline1       ; no, text says PRINTER
  1531.               je msts1
  1532.               mov si,offset menufile1       ; yes, text says FILE
  1533. msts1:        cmp menu_choice,0             ;is this the highlighted choice?
  1534.               je msts2
  1535.               call formline                 ; no, display normal
  1536.               jmp short msts3
  1537. msts2:        call cursline                 ; yes, display highlighted
  1538. msts3:        add di,linesum                ;go down one line
  1539.  
  1540.               mov bx,offset lpt2            ;structure #2
  1541.               cmp ds:[bx].active,OFF        ;printer redirected ?
  1542.               mov si,offset menuline2       ; no, text says PRINTER
  1543.               je msts4
  1544.               mov si,offset menufile2       ; yes, text says FILE
  1545. msts4:        cmp menu_choice,1             ;is this the highlighted choice?
  1546.               je msts5
  1547.               call formline                 ; no, display normal
  1548.               jmp short msts6
  1549. msts5:        call cursline                 ; yes, display highlighted
  1550. msts6:        add di,linesum                ;go down one line
  1551.  
  1552.               mov bx,offset lpt3            ;structure #3
  1553.               cmp ds:[bx].active,OFF        ;printer redirected ?
  1554.               mov si,offset menuline3       ; no, text says PRINTER
  1555.               je msts7
  1556.               mov si,offset menufile3       ; yes, text says FILE
  1557. msts7:        cmp menu_choice,2             ;is this the highlighted choice?
  1558.               je msts8
  1559.               call formline                 ; no, display normal
  1560.               jmp short msts9
  1561. msts8:        call cursline                 ; yes, display highlighted
  1562. msts9:
  1563.               cmp adapter,1                 ;enable CGA video
  1564.               jne msts10
  1565.               call enable_cga
  1566. msts10:
  1567.               pop es                        ;restore ES
  1568.               ret
  1569. make_status   endp
  1570. ;
  1571. ;------------------------------------------------------------------------------
  1572. ;MAKE_CHANGE changes the status of the redirection.
  1573. ;------------------------------------------------------------------------------
  1574. make_change   proc near
  1575.               push es                       ;save ES
  1576.  
  1577.               cmp menu_choice,0             ;is this the highlighted choice?
  1578.               jne mkchg1
  1579.               mov bx,offset lpt1            ;structure #1
  1580. mkchg1:
  1581.               cmp menu_choice,1             ;is this the highlighted choice?
  1582.               jne mkchg2
  1583.               mov bx,offset lpt2            ;structure #2
  1584. mkchg2:
  1585.               cmp menu_choice,2             ;is this the highlighted choice?
  1586.               jne mkchg3
  1587.               mov bx,offset lpt3            ;structure #3
  1588. mkchg3:
  1589.               cmp ds:[bx].active,OFF        ;flip printer redirection 
  1590.               je mkchg4
  1591.               mov ds:[bx].active,OFF        ;turn redirection OFF
  1592.               mov ax,ds:[bx].bufcntr        ;look at the buffer size
  1593.               or ah,al                      ;is there something in it ?
  1594.               jz mkchg5                     ; no, buffer was empty
  1595.               mov ds:[bx].request,ON        ; yes, request a buffer flush.
  1596.               jmp short mkchg5
  1597. mkchg4:       mov ax,ds:[bx].handle         ;get the file handle to check it
  1598.               or ah,al                      ;if it has ever been used.
  1599.               jz mkchg5                     ; no, don't activate
  1600.               mov ds:[bx].active,ON         ; yes, activate redirection
  1601. mkchg5:       call click                    ;give a little noisy feedback
  1602.               pop es                        ;restore ES
  1603.               ret
  1604. make_change   endp
  1605. ;
  1606. ;------------------------------------------------------------------------------
  1607. ;FORMLINE is called by MAKE_SCREEN to help with the dirty work.
  1608. ;------------------------------------------------------------------------------
  1609. formline      proc near
  1610.               mov ah,border_attr
  1611.               lodsb
  1612.               stosw
  1613.               mov cx,32                     ;do next 32 characters
  1614.               mov ah,text_attr
  1615. form1:        lodsb
  1616.               stosw
  1617.               loop form1
  1618.               mov ah,border_attr
  1619.               lodsb
  1620.               stosw
  1621.               ret
  1622. formline      endp
  1623. ;
  1624. ;
  1625. ;------------------------------------------------------------------------------
  1626. ;CURSLINE is called by MAKE_SCREEN to help with the dirty work.
  1627. ;------------------------------------------------------------------------------
  1628. cursline      proc near
  1629.               mov ah,border_attr
  1630.               lodsb
  1631.               stosw
  1632.               mov cx,32                     ;do next 32 characters
  1633.               mov ah,menu_attr
  1634. curs1:        lodsb
  1635.               stosw
  1636.               loop curs1
  1637.               mov ah,border_attr
  1638.               lodsb
  1639.               stosw
  1640.               ret
  1641. cursline      endp
  1642. ;
  1643. ;
  1644. ;------------------------------------------------------------------------------
  1645. ;DISABLE_CGA and ENABLE_CGA disable and enable CGA video output.
  1646. ;------------------------------------------------------------------------------
  1647. disable_cga   proc near
  1648.               mov dx,3DAh                   ;address of Status Register
  1649. disable1:     in al,dx                      ;get status
  1650.               test al,8                     ;vertical retrace active?
  1651.               je disable1                   ;no, then wait
  1652.               sub dx,2                      ;MSR address in DX
  1653.               mov al,25h                    ;value to disable video
  1654.               out dx,al                     ;disable video output
  1655.               ret
  1656. disable_cga   endp
  1657. ;
  1658. enable_cga    proc near
  1659.               mov ah,15                     ;get video mode
  1660.               int 10h
  1661.               mov bx,offset enable_values   ;get value to enable display
  1662.               xlat                          ;value in AL
  1663.               mov dx,3D8h                   ;MSR address
  1664.               out dx,al                     ;enable video output
  1665.               ret
  1666. enable_cga    endp
  1667. ;
  1668. ;------------------------------------------------------------------------------
  1669. ;GETKEY waits for a keypress and returns the keycode in AX.
  1670. ;Exit:  AX - keycode
  1671. ;This function is careful to generate int28 for other TSR that may need them
  1672. ;while the user is waiting to press a key.
  1673. ;------------------------------------------------------------------------------
  1674. getkey        proc near
  1675.               mov ah,1                      ;check keyboard buffer
  1676.               int 16h
  1677.               jne getkey1                   ;jump if buffer contains a keycode
  1678.               int 28h                       ;no key pressed - issue int 28h
  1679.               jmp getkey                    ;loop back to try again
  1680. getkey1:      mov ah,0                      ;get keycode from buffer
  1681.               int 16h
  1682.               ret                           ;exit with keycode in AX
  1683. getkey        endp
  1684. ;
  1685. ;
  1686. ;------------------------------------------------------------------------------
  1687. ;SHOW_CURSOR displays the cursor and discounts EGA cursor emulation logic.
  1688. ;Entry:  NEW_CURSOR - starting and ending scan lines
  1689. ;------------------------------------------------------------------------------
  1690. show_cursor   proc near
  1691.               cmp adapter,2                 ;is an EGA currently active?
  1692.               jne cursor1                   ;no, then branch
  1693.               push es                       ;save ES
  1694.               mov ax,bios_data              ;point ES to BIOS data area
  1695.               mov es,ax
  1696.               assume es:bios_data
  1697.               push infobyte                 ;save EGA info byte
  1698.               or ega_info,1                 ;disable EGA cursor emulation
  1699. cursor1:      mov ah,1                      ;display the cursor
  1700.               mov cx,new_cursor
  1701.               int 10h
  1702.               cmp adapter,2                 ;is an EGA active?
  1703.               jne cursor_exit               ;no, then exit
  1704.               pop infobyte                  ;restore EGA info byte
  1705.               pop es                        ;restore ES
  1706.               assume es:nothing
  1707. cursor_exit:  ret
  1708. show_cursor   endp
  1709. ;
  1710.  
  1711. ;--------------------------------------------------------------------
  1712. end_res db      0
  1713. ;
  1714. ; This is the end of the memory resident portion of LPTx
  1715. ;
  1716. ;--------------------------------------------------------------------
  1717. ;
  1718. ; All of the following data is in the Code Segment
  1719. ;
  1720. mach_type       db      0
  1721. DOS_version     db      0               ;Major Version Number
  1722.                 db      0               ;Minor Version Number
  1723. lfeed           db      ON              ;linefeed enable switch
  1724. appending       db      0               ;appending to an existing file
  1725. drive           db      0               ;default drive number 0=A etc.
  1726. flag_27         db      OFF             ; 1=make this copy resident
  1727. wrong_dos       db      'DOS 2.0 or later required for LPTx',LF,CR,DOLLAR
  1728. up_msg          db      'LPTx - Line Printer Redirection Program - V7.00'
  1729.                 db      LF,CR,'   Copyright 1987 Mark C. DiVecchio'
  1730.                 db      LF,CR,'   Copyright 1987 Kepa Zubeldia',LF,CR
  1731.                 db      DOLLAR
  1732. lptx_resident   db      LF,CR,'Resident Portion of LPTx Loaded',LF,CR
  1733.                 db      'The pop-up window will be activated by Alt-PrtSc'
  1734.                 db      LF,LF,CR
  1735.                 db      DOLLAR
  1736. lptx_err_3      db      'Could not delete file',LF,CR,DOLLAR
  1737. lptx_over       db      CR,LF,'File already exists. Do you want to overwrite '
  1738.                 db      'it? (y or n)  :',DOLLAR
  1739. lptx_nc         db      'File selection canceled',CR,LF,DOLLAR
  1740. lptx_lf         db      'Stripping Linefeed Characters',CR,LF,DOLLAR
  1741. lptx_sd_on      db      'Sound Enabled',CR,LF,DOLLAR
  1742. lptx_mono       db      'Monochrome only, graphic card ignored',CR,LF,DOLLAR
  1743. lptx_cs_off     db      'Critical Section Checking Disabled',CR,LF,DOLLAR
  1744. lptx_del        db      'File is being overwritten',LF,CR,DOLLAR
  1745. lptx_cr         db      LF,CR,DOLLAR
  1746. lptx_bad        db      'Invalid Option',LF,CR
  1747.                 db      'Calling sequence:',LF,CR
  1748.                 db      'lptx [?] [-m] [-x] [-l] [-i] {-1,-2,-3} {-c -o -a <d:[pathname]filename>}'
  1749.                 db      LF,CR,DOLLAR
  1750. lptx_on         db      LF,CR,'Redirection started. Disk file opened.'
  1751.                 db      LF,CR,DOLLAR
  1752. lptx_off        db      LF,CR,'Redirection ended. Disk file closed.'
  1753.                 db      LF,CR,DOLLAR
  1754. lptx_creat      db      'Could not create the disk file',LF,CR,DOLLAR
  1755. lptx_open       db      'Could not open the disk file',LF,CR,DOLLAR
  1756. lptx_gone       db      LF,'LPTx - Resident portion inactivated',CR,LF,DOLLAR
  1757. ; HELP screen
  1758. help_msg        db      LF,CR,'Calling sequence : ',LF,LF,CR
  1759.                 db      'LPTx [?] [-m] [-x] [-l] [-i] -p -f <[d:][\pathname\pathname]filename>'
  1760.                 db      LF,LF,CR
  1761.                 db      '    where  p = printer number : 1, 2, or 3',LF,CR
  1762.                 db      '           f = function : o for open a print file'
  1763.                 db      LF,CR
  1764.                 db      '                          a for append to a print file'
  1765.                 db      LF,CR
  1766.                 db      '                          c for close a print file'
  1767.                 db      LF,CR
  1768.                 db      '           drive letter & pathname are optional'
  1769.                 db      LF,CR
  1770.                 db      '           m = monochrome mode only, even with graphics cards'
  1771.                 db      LF,CR
  1772.                 db      '           x = disable check of Critical Section Flag'
  1773.                 db      LF,CR
  1774.                 db      '           l = strip Linefeed characters from output'
  1775.                 db      LF,CR
  1776.                 db      '           i = inactivate LPTx',LF,CR
  1777.                 db      '    defaults : p = 1',LF,CR
  1778.                 db      '               f = o',LF,CR
  1779.                 db      '    The pop-up window is activated with Alt-PrtSc'
  1780.                 db      LF,CR
  1781.                 db      DOLLAR
  1782. ;
  1783. stat_stat       db      CR,LF,'LPTx Status :',CR,LF,DOLLAR
  1784. stat_lp         db      'lpt'
  1785. stat_ptr        db      ' : ',DOLLAR
  1786. stat_off        db      ' not redirected',CR,LF,DOLLAR
  1787. stat_dir        db      ' redirected to disk file '
  1788. stat_fn         db      60 dup (BLANK)
  1789. ;
  1790. yn_max          db      2       ;max # of char
  1791. yn_act          db      0
  1792. yn_in           db      2 dup (0)
  1793. ;
  1794. ;--------------------------------------------------------------------
  1795. ;
  1796. ; This is the main routine which is executed each time that LPTx is 
  1797. ; called. In this routine, DS points to the data segment which is
  1798. ; transient. ES points to the data segment which is permanently
  1799. ; resident. ES:BX points to the data structure for the selected 
  1800. ; line printer, 1, 2, or 3.
  1801. ; The offsets are the same for both. If this is the first
  1802. ; time that LPTx is run, then ES=DS.
  1803. ;
  1804. lptx_start:
  1805.         push    DS                      ;Save DS
  1806.         xor     AX,AX                   ;clear AX for return IP
  1807.         push    AX                      ;put 0 on stack
  1808. ;
  1809. ;to check for machine type look at
  1810. ; F000:FFFE                             ; I don't use this currently
  1811. ;       = FF    IBM PC
  1812. ;       = FE    IBM XT or Portable
  1813. ;       = FD    IBM PCjr
  1814. ;       = FC    IBM PC AT
  1815. ;       = F9    IBM Convertible
  1816.         mov     AX,0F000h
  1817.         mov     ES,AX
  1818.         mov     BX,0FFFEh
  1819.         mov     CL,ES:[BX]              ;get machine type
  1820.         mov     mach_type,CL            ;save machine type
  1821. ;
  1822. ;PSP
  1823.         mov     lptx_psp,DS             ;put away our PSP Segment address
  1824. ;
  1825. ; get the DOS version number
  1826. ; returns zero for pre DOS 2.0 releases
  1827.         mov     AH,30h
  1828.         int     DOS_CALL                ;call DOS
  1829.         mov     word ptr DOS_version,AX 
  1830. ; Requires at least version 2.0 and may or may not work
  1831. ; with versions above 2.1
  1832.         cmp     DOS_version,2           ;is it DOS 2.+
  1833.         jge     dos_ok                  ;yes
  1834.         display wrong_dos               ;print error message
  1835.         mov     AH,0
  1836.         int     DOS_CALL                ;terminate
  1837. dos_ok: mov     AH,def_drive            ;get current default drive
  1838.         int     DOS_CALL
  1839.         mov     drive,AL                ;save the drive number
  1840.         display up_msg                  ;print program ID
  1841.         mov     flag_27,OFF             ;to not make resident
  1842. ; is a copy of LPTx already resident in memory?
  1843.         mov     DX,REQ                  ;check if LPTx is already resident
  1844.         mov     AH,3                    ;get status - special call
  1845.         int     17h                     ;call int 17h - BIOS
  1846.         cmp     DX,ACK                  ;my handler sets DX to ACK
  1847.                                         ;and sets ES 
  1848.         je      in_core                 ;LPTx is resident - ES loaded with
  1849.                                         ; segment address
  1850.         mov     flag_27,ON              ;to make this copy resident
  1851.         push    CS
  1852.         pop     ES                      ;set ES to CS for segment address
  1853.         mov     AL,drive
  1854.         add     AL,'a'                  ;make it a letter
  1855.         mov     BX,offset lpt1
  1856.         mov     ES:[BX].filen,AL        ;put it into the filename
  1857.         mov     BX,offset lpt2
  1858.         mov     ES:[BX].filen,AL        ;put it into the filename
  1859.         mov     BX,offset lpt3
  1860.         mov     ES:[BX].filen,AL        ;put it into the filename
  1861. ; ----------------------------------------------------
  1862. in_core:                                ;ES is ok
  1863. ; ES now points to resident data area
  1864. ; set up ES:BX to point to default data structure
  1865.         mov     BX,offset lpt1          ;offset - default to LPT1
  1866. ;get options and file name
  1867. ;scan input line for line printer number
  1868.         mov     SI,81h                  ;starting offset
  1869.         mov     CL,DS:80h               ;length of input line
  1870.         mov     CH,0
  1871.         cmp     CX,0                    ;nothing?
  1872.         jne     cont_scan               ;no
  1873.         jmp     make_res                ;yes, then just make LPTx resident
  1874.                                         ;if it isn't already
  1875.  
  1876. ;
  1877. ; [?]
  1878. ; HELP printer
  1879. ;
  1880. help:   display help_msg                ;display the HELP screen
  1881.         jmp     bail_out
  1882. ;
  1883. ; [-m]
  1884. ; Monochrome monitor, even on graphic card
  1885. ;
  1886. mono_only:
  1887.         mov     ES:nocolors,1
  1888.         display lptx_mono
  1889.         jmp     short inp_ret
  1890. ;
  1891. ; [-a]
  1892. ; appending to a file, similar to open.
  1893. ;
  1894. append:
  1895.         mov     appending,1
  1896.         jmp     file_op
  1897. ;
  1898. ;
  1899. cont_scan:
  1900.         cmp     byte ptr DS:[SI],'?'    ;a ?  ?
  1901.         je      help                    ;yes
  1902.         cmp     byte ptr DS:[SI],dash   ;a dash ?
  1903.         je      got_opt                 ;yes
  1904.         cmp     byte ptr DS:[SI],CR     ;a carriage return?
  1905.         je      scan_done               ;yes
  1906.         cmp     byte ptr DS:[SI],BLANK  ;a blank?
  1907.         je      inp_ret                 ;yes
  1908.         jmp     no_b                    ;assume that we got a file name
  1909.                                         ;without the -o option
  1910. inp_ret:
  1911.         inc     SI                      ;ignore blanks
  1912.         loop    cont_scan               ;continue to scan
  1913. ;
  1914. ; Scan of whole line is complete without a "-o", "-a", "-c" or filename
  1915. scan_done:
  1916.         jmp     make_res
  1917. ;
  1918. got_opt:                                ;we got an option
  1919.         inc     SI                      ;to option
  1920.         cmp     byte ptr DS:[SI],'1'    ;LPT1?
  1921.         jne     chk_2
  1922.         mov     BX,offset lpt1          ;offset from ES
  1923.         jmp     short inp_ret
  1924. chk_2:  cmp     byte ptr DS:[SI],'2'    ;LPT2?
  1925.         jne     chk_3
  1926.         mov     BX,offset lpt2          ;offset from ES
  1927.         jmp     short inp_ret
  1928. chk_3:  cmp     byte ptr DS:[SI],'3'    ;LPT3?
  1929.         jne     chk_fil
  1930.         mov     BX,offset lpt3          ;offset from ES
  1931.         jmp     short inp_ret
  1932. chk_fil:                                ;is it file?
  1933.         cmp     byte ptr DS:[SI],'?'    ;help ?
  1934.         je      help                    ;yes
  1935.         cmp     byte ptr DS:[SI],'o'    ;open a file
  1936.         je      file_op                 ;yes
  1937.         cmp     byte ptr DS:[SI],'a'    ;append to a file
  1938.         je      append                  ;yes
  1939.         cmp     byte ptr DS:[SI],'c'    ;close a file
  1940.         je      file_cl                 ;yes
  1941.         cmp     byte ptr DS:[SI],'m'    ;monochrome only ?
  1942.         je      mono_only               ;yes
  1943.         cmp     byte ptr DS:[SI],'x'    ;inhibit check for Critical Section?
  1944.         je      cs_check_off            ;yes
  1945.         cmp     byte ptr DS:[SI],'l'    ;linefeed switch ?
  1946.         je      lf_off                  ;yes
  1947.         cmp     byte ptr DS:[SI],'s'    ;enable sound?
  1948.         je      sound_on                ;yes
  1949.         cmp     byte ptr DS:[SI],'i'    ;inactivate?
  1950.         jne     bad_opt
  1951.         jmp     inactivate
  1952. bad_opt:
  1953.         display lptx_bad                ;incorrect option
  1954.         jmp     nor_ex
  1955. ;
  1956. ; [-x]
  1957. ; turn OFF Critical Section check
  1958. ;
  1959. cs_check_off:
  1960.         mov     ES:cs_switch,OFF
  1961.         display lptx_cs_off
  1962.         jmp     inp_ret
  1963. ;
  1964. ;[-l]
  1965. ; Turn linefeeds off in captured file
  1966. ;
  1967. lf_off: mov     lfeed,OFF               ;turn LFs off
  1968.         display lptx_lf
  1969.         jmp     inp_ret                 ;continue the scan
  1970. ;
  1971. ; [-s]
  1972. ; turn ON Sound
  1973. ;
  1974. sound_on:
  1975.         mov     ES:sound,ON
  1976.         display lptx_sd_on
  1977.         jmp     inp_ret
  1978. ;
  1979. ; [-c]
  1980. ; close output file
  1981. ;
  1982. file_cl:cmp     ES:[BX].active,ON       ;are we active?
  1983.         jne     no_close                ;no
  1984.         mov     AL,1AH                  ;CTRL-Z
  1985.         push    DS
  1986.         push    ES
  1987.         pop     DS                      ;set DS to point to resident
  1988.                                         ;data segment
  1989. ;       call    prnt                    ;print end of file mark
  1990.         call    flush
  1991.         pop     DS                      ;restore DS
  1992.         mov     ES:[BX].active,OFF      ;make us inactive
  1993.         display lptx_off                ;redirection off message
  1994. no_close:
  1995.         jmp     nor_exit                ;nothing to close so exit
  1996. ;
  1997. ; [-o]
  1998. ; open a file for ouput
  1999. ;
  2000. file_op:                                ;get the file name
  2001.         inc     SI                      ;to next chracter
  2002.         cmp     byte ptr DS:[SI],BLANK  ;a blank?
  2003.         jne     no_b                    ;no
  2004.         inc     SI                      ;skip over blank
  2005. no_b:
  2006. ; at this point, we have found a new file name. We close the old
  2007. ; file if one was open
  2008.         cmp     ES:[BX].active,ON       ;are we active?
  2009.         jne     no_cl                   ;no
  2010.         mov     AL,1AH                  ;CTRL-Z
  2011.         push    DS
  2012.         push    ES
  2013.         pop     DS                      ;set DS to point to resident
  2014.                                         ;data segment
  2015. ;       call    prnt                    ;print end of file mark
  2016.         call    flush
  2017.         pop     DS                      ;restore DS
  2018.         mov     ES:[BX].active,OFF      ;make us inactive
  2019.         display lptx_off                ;redirection off message
  2020. no_cl:  mov     DI,BX                   ;base of structure
  2021.         add     DI,offset lpt1.filen    ;add offset of destination
  2022.         push    SI                      ;save pointer to file name
  2023. ; search for a drive letter
  2024.         inc     SI                      ;should point to a colon if
  2025.                                         ;one is there
  2026.         cmp     byte ptr [SI],COLON     ;?
  2027.         je      got_drive               ;yes
  2028. get_drive:
  2029.         mov     AL,drive                ;get drive letter
  2030.         add     AL,'a'                  ;make it a letter
  2031.         mov     ES:[DI],AL              ;put it in file name
  2032.         inc     DI
  2033.         mov     byte ptr ES:[DI],COLON  ;put in a colon
  2034.         inc     DI
  2035.         jmp     path_search
  2036. got_drive:
  2037.         pop     SI                      ;move pointer back to start
  2038.         mov     AL,[SI]                 ;get the given drive
  2039.         mov     ES:[DI],AL              ;move it
  2040.         sub     AL,'a'                  ;make it a number
  2041.         mov     drive,AL                ;save the drive number
  2042.         inc     SI
  2043.         inc     DI
  2044.         mov     byte ptr ES:[DI],COLON
  2045.         inc     DI
  2046.         inc     SI
  2047.         push    SI                      ;save new start pointer
  2048. path_search:
  2049. ; now search for a backslash which says that a pathname was given
  2050. bk_s_lp:cmp     byte ptr [SI],BACKSLASH
  2051.         je      got_path                ;a path
  2052.         cmp     byte ptr [SI],CR        ;end of the file name?
  2053.         je      get_path                ;yes with no path
  2054.         inc     SI
  2055.         jmp     short bk_s_lp           ;loop
  2056. get_path:
  2057.         mov     byte ptr ES:[DI],BACKSLASH      ;create the path
  2058.         inc     DI
  2059.         mov     DL,drive                ;the current drive
  2060.         inc     DL                      ;bump it for DOS
  2061.         push    DS
  2062.         push    ES
  2063.         pop     DS                      ;set up DS for DOS
  2064.         mov     SI,DI                   ;set up SI for pathname
  2065.         mov     AH,def_path             ;get current directory
  2066.         int     DOS_CALL                ;path goes into DS:SI
  2067.         pop     DS                      ;restore DS
  2068.         cmp     byte ptr ES:[SI],NULL   ;null path?
  2069.         je      null_path               ;yes - root directory
  2070. path_lp:                                ;now find the end of the string
  2071.         cmp     byte ptr ES:[SI],NULL   ;null byte marks end of pathname
  2072.         je      end_path                ;now append the file name
  2073.         inc     SI
  2074.         jmp     short path_lp
  2075. end_path:
  2076.         mov     byte ptr ES:[SI],BACKSLASH
  2077.         inc     SI
  2078. null_path:
  2079.         mov     DI,SI                   ;DI is destination
  2080. got_path:
  2081.         pop     SI                      ;restore source of filename
  2082. ; pick up everything to next blank
  2083. get_lp:
  2084.         mov     AL,DS:[SI]              ;character
  2085.         mov     ES:[DI],AL              ;put it away
  2086.         cmp     AL,CR                   ;was it a Carriage Return?
  2087.         je      end_line
  2088.         cmp     AL,BLANK                ;was it a space?
  2089.         je      end_line
  2090.         inc     SI
  2091.         inc     DI
  2092.         jmp     short get_lp            ;no so get next character
  2093. end_line:
  2094.         mov     byte ptr ES:[DI],NULL   ;zero out the CR or blank
  2095.                                         ;at the end of the filename
  2096.                                         ;it becomes an ASCIIZ string
  2097.         sub     DI,BX                   ;now take out the base and
  2098.         cmp     DI,offset lpt1.filen    ; make sure that we got something
  2099.         jne     lptx_make               ;file name was ok
  2100.         display lptx_creat              ;could not understand the file name
  2101.         jmp     nor_exit                ;don't stay resident
  2102. nor_ex: jmp     nor_exit
  2103.  
  2104. lptx_make:
  2105. ; default DTA used by Find File is set by DOS to an offset of
  2106. ; 80h into this program's Program Segment Prefix
  2107.  push DS
  2108.         push    ES
  2109.         pop     DS                      ;uses DS:DX
  2110.         mov     DX,BX
  2111.         add     DX,offset lpt1.filen    ;file name
  2112.         mov     AH,find_FILE
  2113.         mov     CX,0                    ;normal files only
  2114.         int     DOS_CALL                ;find first match
  2115.         pop     DS
  2116.         jnc     lptx_d                  ;file was found
  2117.         jmp     lptx_create             ;not there - which is ok
  2118. ;file already exists
  2119. lptx_d: cmp     appending,1             ;are we in append mode?
  2120.         je      lptx_x                  ; yes
  2121.         display lptx_over
  2122.         mov     DX,offset yn_max;input buffer
  2123.         mov     AH,0AH
  2124.         int     DOS_CALL
  2125.         cmp     yn_act,0                ;anything typed?
  2126.         display lptx_cr
  2127.         je      lptx_x                  ;no - try to append
  2128.         cmp     yn_in,'y'               ;a yes?
  2129.         je      lptx_d_yes              ;yes
  2130.         cmp     yn_in,'Y'               ;a yes?
  2131.         je      lptx_d_yes              ;yes
  2132. lptx_x: push    DS                      ;we can't overwrite, try to append
  2133.         push    ES
  2134.         pop     DS                      ;uses DS:DX
  2135.         mov     DX,BX                   ;base of this LPT's structure
  2136.         add     DX,offset lpt1.filen    ;file name
  2137.         mov     AL,1                    ;open for writing
  2138.         mov     AH,open_FILE
  2139.         mov     CX,0                    ;normal files only
  2140.         int     DOS_CALL                ;find first match
  2141.         pop     DS
  2142.         jnc     creat_ok
  2143.         display lptx_open               ;could not open the file
  2144.         display lptx_nc
  2145.         jmp     nor_exit                ;don't stay resident
  2146.  
  2147. lptx_d_yes:
  2148.         display lptx_del
  2149.         push    DS
  2150.         push    ES
  2151.         pop     DS                      ;uses DS:DX
  2152.         mov     DX,BX
  2153.         add     DX,offset lpt1.filen    ;file name
  2154.         mov     AH,delete_FILE
  2155.         int     DOS_CALL                ;delete file
  2156.         pop     DS
  2157.         jnc     lptx_create             ;ok its gone
  2158.         display lptx_err_3              ;can't delete it
  2159.         jmp     nor_exit
  2160. lptx_create:                            ; create the file
  2161.         push    DS
  2162.         push    ES
  2163.         pop     DS                      ;uses DS:DX
  2164.         mov     DX,BX                   ;base of this LPT's structure
  2165.         add     DX,offset lpt1.filen    ;file name
  2166.         mov     AH,create_FILE
  2167.         mov     CX,0                    ;normal files only
  2168.         int     DOS_CALL                ;find first match
  2169.         pop     DS
  2170.         jnc     creat_ok
  2171.         display lptx_creat              ;could not create the file
  2172.         jmp     nor_exit                ;don't stay resident
  2173. creat_ok:                               ;now close the file
  2174.         push    BX
  2175.         mov     ES:[BX].handle,AX       ;save the handle, for popup to know.
  2176.         mov     BX,AX                   ;AX was loaded by the create file
  2177.                                         ;       call
  2178.         mov     AH,close_FILE           ;close the file
  2179.         int     DOS_CALL
  2180.         pop     BX
  2181.         display lptx_on
  2182. ; set the program up for writing
  2183.         mov     ES:[BX].bufcntr,EMPTY   ;set buffer empty
  2184.         mov     ES:[BX].active,ON       ;set us on
  2185.         mov     AL,lfeed
  2186.         mov     ES:[BX].linefeed,AL     ;save linefeed switch
  2187. make_res:
  2188.         cmp     flag_27,ON              ;make this one resident?
  2189.         je      resident                ;yes
  2190.         jmp     nor_exit                ;no
  2191. ;
  2192. resident:
  2193.         push    ES
  2194.         push    BX
  2195. ; get old interrupt handler addressses
  2196.         mov     AL,17h                  ;get current vector address for 17h
  2197.         mov     AH,35h
  2198.         int     DOS_CALL
  2199.         mov     word ptr old_17h,BX
  2200.         mov     word ptr old_17h[2],ES  ;save it for later use
  2201.  
  2202.         mov     AL,08h                  ;get current vector address for 08h
  2203.         mov     AH,35h
  2204.         int     DOS_CALL
  2205.         mov     word ptr old_08h,BX
  2206.         mov     word ptr old_08h[2],ES  ;save it for later use
  2207.  
  2208.         mov     AL,09h                  ;get current vector address for 09h
  2209.         mov     AH,35h
  2210.         int     DOS_CALL
  2211.         mov     word ptr old_09h,BX
  2212.         mov     word ptr old_09h[2],ES  ;save it for later use
  2213.  
  2214.         mov     AL,10h                  ;get current vector address for 10h
  2215.         mov     AH,35h
  2216.         int     DOS_CALL
  2217.         mov     word ptr old_10h,BX
  2218.         mov     word ptr old_10h[2],ES  ;save it for later use
  2219.  
  2220.         mov     AL,13h                  ;get current vector address for 13h
  2221.         mov     AH,35h
  2222.         int     DOS_CALL
  2223.         mov     word ptr old_13h,BX
  2224.         mov     word ptr old_13h[2],ES  ;save it for later use
  2225.  
  2226.         mov     AL,DOS_CALL             ;get current vector address for 21h
  2227.         mov     AH,35h
  2228.         int     DOS_CALL
  2229.         mov     word ptr old_21h,BX
  2230.         mov     word ptr old_21h[2],ES  ;save it for later use
  2231.  
  2232.         mov     AL,28h                  ;get current vector address for 28h
  2233.         mov     AH,35h
  2234.         int     DOS_CALL
  2235.         mov     word ptr old_28h,BX
  2236.         mov     word ptr old_28h[2],ES  ;save it for later use
  2237.  
  2238. ;
  2239. ; Set LPTx up as the new int 17h interrupt handler
  2240.         mov     AX,2517h                ;set interrupt vector
  2241.         mov     DX,offset int_17h       ;BIOS printer
  2242.         int     DOS_CALL
  2243. ;
  2244. ; Set LPTx up as the new int 08h interrupt handler
  2245.         mov     AX,2508h                ;set interrupt vector
  2246.         mov     DX,offset int_08h       ;Timer
  2247.         int     DOS_CALL
  2248. ;
  2249. ; Set LPTx up as the new int 09h interrupt handler
  2250.         mov     AX,2509h                ;set interrupt vector
  2251.         mov     DX,offset keyboard      ;Keyboard
  2252.         int     DOS_CALL
  2253. ;
  2254. ; Set LPTx up as the new int 10h interrupt handler
  2255.         mov     AX,2510h                ;set interrupt vector
  2256.         mov     DX,offset video         ;Video
  2257.         int     DOS_CALL
  2258. ;
  2259. ; Set LPTx up as the new int 13h interrupt handler
  2260.         mov     AX,2513h                ;set interrupt vector
  2261.         mov     DX,offset bdisk         ;Disk
  2262.         int     DOS_CALL
  2263. ;
  2264. ; Set LPTx up as the new int 21h interrupt handler
  2265. ;       mov     AX,2521h                ;set interrupt vector
  2266. ;       mov     DX,offset int_21h       ;DOS Functions
  2267. ;       int     DOS_CALL
  2268. ;
  2269. ; Set LPTx up as the new int 28h interrupt handler
  2270.         mov     AX,2528h                ;set interrupt vector
  2271.         mov     DX,offset int_28h       ;Idle
  2272.         int     DOS_CALL
  2273. ;
  2274. ; get the address of the critical section flag
  2275. ;
  2276.         mov     AH,34h
  2277.         int     DOS_CALL                ;Call Special DOS interrupt
  2278.                                         ;returns pointer to critical
  2279.                                         ;section flag in ES:BX
  2280.                                         ;With DOS 2.1, this returns
  2281.                                         ;00EC:012D. I used the XRAY
  2282.                                         ;program to look at this
  2283.                                         ;byte while DOS was running.
  2284.         mov     csect_seg,ES            ;save the pointer
  2285.         mov     csect_off,BX
  2286. ;
  2287. ; get the address of the critical error flag. This piece of code comes from
  2288. ; PC Magazine, October 13, 1987, page 416, CARDFILE.ASM, by Jeff Prosise.
  2289. ;
  2290.               mov cerrf_seg,ES              ;save the pointer for critical error
  2291.               mov ax,3E80h                  ;CMP opcode
  2292.               mov cx,2000h                  ;max search length
  2293.               mov di,bx                     ;start at critical section address
  2294. ce_init4:     repne scasw                   ;do the search
  2295.               jcxz ce_init5                 ;branch if search failed
  2296.               cmp byte ptr es:[di+5],0BCh   ;verify this is it
  2297.               je ce_found                   ;branch if it is
  2298.               jmp ce_init4                  ;resume loop if it's not
  2299. ce_init5:     mov cx,2000h                  ;search again
  2300.               inc bx                        ;search odd addresses this time
  2301.               mov di,bx
  2302. ce_init6:     repne scasw                   ;look for the opcode
  2303.               jcxz ce_notfound              ;not found if loop expires
  2304.               cmp byte ptr es:[di+5],0BCh   ;verify this is it
  2305.               je ce_found
  2306.               jmp ce_init6
  2307. ce_notfound:  mov ax,csect_off              ;flag not found, fake it
  2308.               jmp ce_end
  2309. ce_found:     mov ax,es:[di]                ;get flag offset address
  2310. ce_end:       mov cerrf_off,ax              ;save it
  2311. ;
  2312.         pop     BX
  2313.         pop     ES
  2314.         display lptx_resident           ;resident loaded message
  2315.         call    stat                    ;display status
  2316.         mov     DX,offset end_res
  2317.         int     27h                     ;terminate but stay resident
  2318. ;
  2319. ; Normal exit for transient copy of LPTx
  2320. nor_exit:
  2321.         call    stat                    ;display status
  2322. bail_out:
  2323.         mov     AH,0
  2324.         int     DOS_CALL                ;terminate
  2325. ;
  2326. ; [-i]
  2327. ; unhook LPTx from interrupt vectors 08h, 09h, 17h, 21h, and 28h
  2328. inactivate:
  2329. ;
  2330.         cmp     word ptr ES:old_17h,0   ;is it sill 0?
  2331.         je      bail_out                ;yes, we weren't installed yet
  2332. ; flush all buffers
  2333.         mov     BX,offset lpt1
  2334.         cmp     ES:[BX].active,ON       ;are we active?
  2335.         jne     no_cl1                  ;no
  2336.         mov     AL,1AH                  ;CTRL-Z
  2337.         push    DS
  2338.         push    ES
  2339.         pop     DS                      ;set DS to point to resident
  2340.                                         ;data segment
  2341. ;       call    prnt                    ;print end of file mark
  2342.         call    flush
  2343.         pop     DS                      ;restore DS
  2344.         mov     ES:[BX].active,OFF      ;make us inactive
  2345.         display lptx_off                ;capturing off message
  2346. ;
  2347. no_cl1:
  2348.         mov     BX,offset lpt2
  2349.         cmp     ES:[BX].active,ON       ;are we active?
  2350.         jne     no_cl2                  ;no
  2351.         mov     AL,1AH                  ;CTRL-Z
  2352.         push    DS
  2353.         push    ES
  2354.         pop     DS                      ;set DS to point to resident
  2355.                                         ;data segment
  2356. ;       call    prnt                    ;print end of file mark
  2357.         call    flush
  2358.         pop     DS                      ;restore DS
  2359.         mov     ES:[BX].active,OFF      ;make us inactive
  2360.         display lptx_off                ;capturing off message
  2361. ;
  2362. no_cl2:
  2363.         mov     BX,offset lpt3
  2364.         cmp     ES:[BX].active,ON       ;are we active?
  2365.         jne     no_cl3                  ;no
  2366.         mov     AL,1AH                  ;CTRL-Z
  2367.         push    DS
  2368.         push    ES
  2369.         pop     DS                      ;set DS to point to resident
  2370.                                         ;data segment
  2371. ;       call    prnt                    ;print end of file mark
  2372.         call    flush
  2373.         pop     DS                      ;restore DS
  2374.         mov     ES:[BX].active,OFF      ;make us inactive
  2375.         display lptx_off                ;capturing off message
  2376. no_cl3:
  2377.         push    DS
  2378.         mov     DX,word ptr ES:old_17h  ;original vector, offset
  2379.         mov     AX,word ptr ES:old_17h[2];original vector, segment
  2380.         mov     DS,AX
  2381.         mov     AX,2517h                ;set interrupt vector to DS:DX
  2382.         int     DOS_CALL
  2383.         pop     DS
  2384.  
  2385.         push    DS
  2386.         mov     DX,word ptr ES:old_21h  ;original vector, offset
  2387.         mov     AX,word ptr ES:old_21h[2];original vector, segment
  2388.         mov     DS,AX
  2389.         mov     AX,2521h                ;set interrupt vector to DS:DX
  2390.         int     DOS_CALL
  2391.         pop     DS
  2392.  
  2393.  
  2394.         push    DS
  2395.         mov     DX,word ptr ES:old_08h  ;original vector, offset
  2396.         mov     AX,word ptr ES:old_08h[2];original vector, segment
  2397.         mov     DS,AX
  2398.         mov     AX,2508h                ;set interrupt vector to DS:DX
  2399.         int     DOS_CALL
  2400.         pop     DS
  2401.  
  2402.  
  2403.         push    DS
  2404.         mov     DX,word ptr ES:old_09h  ;original vector, offset
  2405.         mov     AX,word ptr ES:old_09h[2] ;original vector, segment
  2406.         mov     DS,AX
  2407.         mov     AX,2509h                ;set interrupt vector to DS:DX
  2408.         int     DOS_CALL
  2409.         pop     DS
  2410.  
  2411.  
  2412.         push    DS
  2413.         mov     DX,word ptr ES:old_10h  ;original vector, offset
  2414.         mov     AX,word ptr ES:old_10h[2] ;original vector, segment
  2415.         mov     DS,AX
  2416.         mov     AX,2510h                ;set interrupt vector to DS:DX
  2417.         int     DOS_CALL
  2418.         pop     DS
  2419.  
  2420.  
  2421.         push    DS
  2422.         mov     DX,word ptr ES:old_13h  ;original vector, offset
  2423.         mov     AX,word ptr ES:old_13h[2] ;original vector, segment
  2424.         mov     DS,AX
  2425.         mov     AX,2513h                ;set interrupt vector to DS:DX
  2426.         int     DOS_CALL
  2427.         pop     DS
  2428.  
  2429.  
  2430.         push    DS
  2431.         mov     DX,word ptr ES:old_28h  ;original vector, offset
  2432.         mov     AX,word ptr ES:old_28h[2];original vector, segment
  2433.         mov     DS,AX
  2434.         mov     AX,2528h                ;set interrupt vector to DS:DX
  2435.         int     DOS_CALL
  2436.         pop     DS
  2437.  
  2438.         display lptx_gone               ;inactivated
  2439.         jmp     bail_out
  2440. ;------------------------------------------------------------------------
  2441. ;
  2442. ; displays the status of each of the three line printers
  2443. ;
  2444. stat    proc    near
  2445. ; display each LPTx with a message "not redirected"
  2446. ;                       or redirected to <filename>
  2447.         push    AX
  2448.         push    BX
  2449.         push    DX
  2450.         push    SI
  2451.         push    DI
  2452.         display stat_stat
  2453. stat_1: mov     BX,offset lpt1          ;first printer
  2454.         mov     stat_ptr,'1'
  2455.         display stat_lp
  2456.         cmp     ES:[BX].active,ON       ;are we active?
  2457.         je      stat_1_a                ;yes
  2458.         display stat_off
  2459.         jmp     short stat_2
  2460. stat_1_a:
  2461.         mov     SI,BX                   ;base
  2462.         add     SI,offset lpt1.filen    ;offset
  2463.         mov     DI,offset stat_fn
  2464. stat_1_lp:
  2465.         mov     AL,ES:[SI]
  2466.         mov     [DI],AL
  2467.         inc     SI
  2468.         inc     DI
  2469.         cmp     AL,NULL                 ;loop till a null byte is found
  2470.         jne     stat_1_lp
  2471.         mov     byte ptr [DI],CR
  2472.         inc     DI
  2473.         mov     byte ptr [DI],LF
  2474.         inc     DI
  2475.         mov     byte ptr [DI],DOLLAR
  2476.         display stat_dir                ;display file name
  2477. stat_2:
  2478.         mov     BX,offset lpt2          ;second printer
  2479.         mov     stat_ptr,'2'
  2480.         display stat_lp
  2481.         cmp     ES:[BX].active,ON       ;are we active?
  2482.         je      stat_2_a                ;yes
  2483.         display stat_off
  2484.         jmp     short stat_3
  2485. stat_2_a:
  2486.         mov     SI,BX                   ;base
  2487.         add     SI,offset lpt1.filen    ;offset
  2488.         mov     DI,offset stat_fn
  2489. stat_2_lp:
  2490.         mov     AL,ES:[SI]
  2491.         mov     [DI],AL
  2492.         inc     SI
  2493.         inc     DI
  2494.         cmp     AL,NULL                 ;loop till a null byte is found
  2495.         jne     stat_2_lp
  2496.         mov     byte ptr [DI],CR
  2497.         inc     DI
  2498.         mov     byte ptr [DI],LF
  2499.         inc     DI
  2500.         mov     byte ptr [DI],DOLLAR
  2501.         display stat_dir                ;display file name
  2502. stat_3: mov     BX,offset lpt3          ;third printer
  2503.         mov     stat_ptr,'3'
  2504.         display stat_lp
  2505.         cmp     ES:[BX].active,ON       ;are we active?
  2506.         je      stat_3_a                ;yes
  2507.         display stat_off
  2508.         jmp     short stat_done
  2509. stat_3_a:
  2510.         mov     SI,BX                   ;base
  2511.         add     SI,offset lpt1.filen    ;offset
  2512.         mov     DI,offset stat_fn
  2513. stat_3_lp:
  2514.         mov     AL,ES:[SI]
  2515.         mov     [DI],AL
  2516.         inc     SI
  2517.         inc     DI
  2518.         cmp     AL,NULL                 ;loop till a null byte is found
  2519.         jne     stat_3_lp
  2520.         mov     byte ptr [DI],CR
  2521.         inc     DI
  2522.         mov     byte ptr [DI],LF
  2523.         inc     DI
  2524.         mov     byte ptr [DI],DOLLAR
  2525.         display stat_dir                ;display file name
  2526. stat_done:
  2527.         pop     DI
  2528.         pop     SI
  2529.         pop     DX
  2530.         pop     BX
  2531.         pop     AX
  2532.         ret
  2533. stat    endp
  2534. ;
  2535. cseg    ends
  2536. %out EOF
  2537.         end     lptx
  2538.  
  2539.