title LPTx : Line Printer Output Capture Routine page 66,132 ;------------------------------------------------------------- ; ; MAIN PROGRAM Version 2.0 ; ; (C) Copyright 1985 by Mark DiVecchio, All Rights Reserved ; ; You may use and freely distribute this program for ; non-commercial applications. ; ; Mark C. DiVecchio ; 9067 Hillery Drive ; San Diego, CA 92126 ; 619-566-6810 ;------------------------------------------------------------- ; This program intercepts the BIOS interrupt 17, the line printer ; interrupt. It will redirect the output of LPT1, LPT2, or LPT3 to a disk ; file. All three redirects may be active at the same time. ; ; Calling sequence: ; lptx -1 -o ; ; where -1 means redirect LPT1, -2 means redirect LPT2, -3 means redirect ; LPT3 ; This option must appear first ; ; -o means start the redirect to file speicfied. If rerouting ; is already in progress for the selected line printer, ; the old file will be closed first. ; (If you do not specify -o but you do specify a line printer, ; LPTx will use either the last file name that you gave when ; you loaded LPTx or will use the file named LPTX1.LST which it ; will create in the root directory ; on the default drive - where x is 1, 2, or 3.) ; ; It is not necessary that you specify the complete path name ; for the file. LPTx opens and closes the file each time that it ; writes out a block. If you change directories, LPTx will ; be able to find the file because it save the complete path. ; ; -c means close the file and send all furthur output directly to the ; line printer. ; ; if neither option is specified, LPTx just displays the program status. ; ; note: -1, -2, and -3 are mutually exclusive ; -o and -c are mutually exclusive ; ; examples: ; ; lptx Displays the program status ; ; lptx -1 routes LPT1 output to file named ; LPTX1.LST on the default drive or the last ; named file. ; ; lptx -o a:\able.xxx routes LPT1 output to file named ; or a:\able.xxx. Any open redirection ; lptx a:\able.xxx disk file for LPT1 is closed. ; ; lptx -2 b:xx.lst routes LPT2 output to file named ; XX.LST in the default directory ; on drive B:. Any open redirection ; disk file for LPT2 is closed. ; ; lptx -3 d:\ab\cd\file.lst redirects LPT3 output to the file named ; file.lst in the directory ab\cd on drive ; d:. ; ; lptx -c closes any disk files open for LPT1 and sends ; or the output back to the line printer ; lptx -1 -c If no rerouting is taking place to LPT1, ; this is a NOP. LPT2 and LPT3 are not ; affected. ; ; lptx -2 -c closes any disk file open for LPT2 and ; sends the output back to line printer. ; if no rerouting is taking place to LPT2, ; this is a NOP. LPT1 and LPT3 are not ; affected. ; ; By rerouting LPT2 or LPT3 to a disk file, you can in effect have 2 or 3 ; printers on your system. LPT1 can be your physical printer and you can ; have LPT2 output going to disk. When you redirect LPT2 or LPT3, LPT1 works ; normally. ; ; If you are rerouting to a diskette file, do not remove the diskette ; once the rerouting starts. I recommend rerouting to a hard disk or ; a RAM disk. ; ; If LPTx encounters any kind of error during the rerouting, it terminates ; operation and sends output back to the line printer. It does not display ; anything but beeps the speaker four times. This prevents your currently ; running program from possibly getting destroyed. ; An error on LPT1 redirect does not shut down LPT2 or LPT3 redirect. ; ; LPTx captures the int 17h interrupt vector. It may not operate correctly ; with other routines what also intercept that vector. Problems may occur ; with the IBM supplied routine PRINT which sometimes works ok with LPTx ; and sometimes does not. Problems may occur with print spoolers as well. ; LPTx must always be the last of the interrupt interceptors run since ; it uses the interrupt vector address at 0000:005C to find the resident ; copy of itself. You may find that with PRINT, you can startup both ; LPTx and PRINT but you will lose control of one or the other. ; ; LPTx also captures the int 24h critical error interrupt vector. This is ; done only for the period that LPTx is using the disk. This prevents ; the generation of funny error messages in the middle of other programs ; that you may be running. (LPTx justs beeps 4 times and clears itself ; out of way if a disk error occurs). ; ; This version of LPTx can redirect all three printers to three different ; files with all 3 active at the same time. ; ; LPTx uses about 7K of memory for the resident data buffers and ; interrupt handler. ; ; If you modify or find any bugs in this program, I would appreciate ; it if you would drop me a line with the changes. Use the address ; above. ; if1 %out Pass 1 else %out Pass 2 endif ; ;----------------------------------------------------------------- null equ 0 off equ 0 on equ 1 empty equ 0 cr equ 13 lf equ 10 dollar equ '$' colon equ ':' backslash equ '\' blank equ ' ' dash equ '-' dos_call equ 21h bufsize equ 200H ;size of DMA buffer display_output equ 9 ;for DOS call def_drive equ 19h create_file equ 3Ch open_file equ 3Dh close_file equ 3Eh write_file equ 40h delete_file equ 41h lseek_file equ 42h def_path equ 47h find_file equ 4Eh ;----------------------------------------------------------------- ; ; Macros display macro msg mov DX,offset msg mov AH,display_output int dos_call endm ; ;----------------------------------------------------------------- ; p_block struc ; ; data structure - these variables are used only in the ; memory resident copy of LPTx. BX is set to point to the offset of the ; allocation of this structure for the selected LPT ; active db off ;1 = this LPTx is on, 0 = off handle dw null ;handle of disk file used by this LPT ; ; space for redirection disk file name ; filen db 'a:\lptx' ptr db blank db '.lst',null db ' ' db ' ' ; sp_left dw empty ;bytes left in DMA buffer for this LPT buffer db bufsize dup(?) ;data buffer for this LPT ; p_block ends ; ; ; ;----------------------------------------------------------------- ; subttl Main Code page %out Assembling CODE Segment cseg segment para public 'CODE' assume CS:cseg,DS:nothing,SS:nothing ; org 100h lptx: jmp l_start id dw 03579h ;unique ID for this program ; ; What follows is three allocations of the Structure p_block ; One for each line printer that we can support. ; With this, all three line printers have DMA buffers and flag ; variables. ; BX is used to point to the offset of the allocation currently in use ; ; Line printer 1 lpt1 p_block <,,,'1'> ; ; Line printer 2 lpt2 p_block <,,,'2'> ; ; Line printer 3 lpt3 p_block <,,,'3'> ; lptxe db 7,7,7,7,dollar ;ring bell four times crit_flag db 0 ;set to one if critical error occured off_crit dw 0 ;save old critical error address seg_crit dw 0 ; dosstk db 0C80H dup(?) ;place to save INT 21H's stack stksav dd 0 ;caller's stack EA db 64 dup('STACK ') stk equ this byte ;----------------------------------------------------------------- ; ; Interrupt handler ; prt_int: ; set up BX push BX cmp DX,0 ;lpt1? jne chk_lpt2 ;no mov BX,offset lpt1 ;offset to LPT1 jmp bx_set chk_lpt2: cmp DX,1 ;lpt2? jne chk_lpt3 ;no mov BX,offset lpt2 ;offset to LPT2 jmp bx_set chk_lpt3: cmp DX,2 ;lpt3? jne ill_ptr ;no - bad printer number mov BX,offset lpt3 ;offset to LPT3 bx_set: cmp CS:[BX].active,off ;are we active? je sleep ;no cmp AH,1 ;initialize call? je do_nix ;yes cmp AH,2 ;status call? je do_nix ;yes cmp AH,0 ;print call? jne do_nix ;no jmp prt_it ;we are active do_nix: mov AH,90h ;Ready Status pop BX iret ; ill_ptr:mov AH,0 pop BX iret ;return with error status ; sleep: pop BX ;restore BX before we go to sleep db 0EAh ;jump immediate to next handler oldint dd ;address of old int 17 routine ;----------------------------------------------------------------- ; ; Start the print process ; prt_it: push AX push BX push CX push DX push DS push ES push SI push DI push BP ; DS is used as the segment register ; for all data during the interrupt ; push CS pop DS ;set up DS ; cli mov SI,SS mov word ptr stksav+2,SI ;save caller's stack mov SI,SP mov word ptr stksav,SI mov SI,CS mov SS,SI ;give me new bigger stack mov SI,offset stk mov SP,SI sti call prnt ;print the character ; cli mov SI,word ptr stksav mov SP,SI ;restore caller's stack mov SI,word ptr stksav+2 mov SS,SI sti ; pop BP pop DI pop SI pop ES pop DS pop DX pop CX pop BX pop AX jmp do_nix ;----------------------------------------------------------------- ; ; Critical Error Handler ; crit_int: ;got critical error mov CS:crit_flag,on ; set flag mov AL,0 ;tells DOS to ignore the iret ;error ;----------------------------------------------------------------- ; ; Print a character in AL ; prnt proc near cmp DS:[BX].active,off je prtext ;nothing there? push AX cmp DS:[BX].sp_left,bufsize ;buffer full jne intadd ;no call flush ;yes, flush buffer intadd: pop AX mov DI,BX ;offset of this printer's allocation add DI,offset buffer ;add in offset of buffer add DI,DS:[BX].sp_left ;add in current byte count mov DS:[DI],AL ;stuff it inc DS:[BX].sp_left prtext: ret ;done prnt endp ; ; Flush print buffer to disk file ; flush proc near cmp DS:[BX].sp_left,empty ;buffer non-empty? jne flush_buf ;empty, skip it ret ;exit flush_buf: mov DS:[BX].sp_left,empty ;else, reset it ; push ES push DS ; ; Preserve a chunk of DOS 2.0 across int 21h ; See PC Technical reference manual page D-7 for hint. ; It comments that only DOS calls 0 - 12 can be safely made ; from an interrupt handler. "Use of any other call will ; destroy the DOS stack and will leave DOS in an ; unpredictable state." What we do here is save and restore ; 3200 bytes of the DOS stack and restore it later. We only ; do it for the DOS stack. If this was invoked by a user ; program, we won't save the DOS stack or the user stack. ; It is not necessary. ; mov AX,word ptr DS:stksav+2 ;get callers stack segment cmp AX,0100h ; is it DOS? ja flusha ;no, don;t bother to save it mov AX,DS ;copy to my segment mov ES,AX mov AX,word ptr DS:stksav+2 ;copying from caller's stack mov DS,AX mov SI,0 ;offset into DOS's stack mov DI,offset dosstk mov CX,0C80h ;length to save cld rep movsb ;copy DOS's stack ; pop DS push DS ; flusha: ; push AX ;save the character push BX push ES mov AX,3524h ;get old critical error vector int dos_call mov DS:off_crit,BX mov DS:seg_crit,ES mov DX,offset crit_int mov AX,2524h int dos_call ;trap critical error vector mov DS:crit_flag,off ;clear critical error flag pop ES pop BX pop AX ; open file mov DX,BX add DX,offset filen ;filename mov AL,1 ;open for writing mov AH,open_file int dos_call mov DS:[BX].handle,AX ;file handle jc flush_err ;error cmp DS:crit_flag,on ;critical error? je flush_err ;yes ; push BX mov AH,lseek_file mov AL,2 ;end of file mov CX,0 ;offset 0 mov DX,0 mov BX,DS:[BX].handle int dos_call pop BX jc flush_err ;some seek error cmp DS:crit_flag,on ;critical error? je flush_err ;yes ; mov CX,bufsize ;buffer length mov DX,BX ;offset of structure allocation add DX,offset buffer ;add offset of buffer within the ; allocation push BX mov AH,write_file mov BX,DS:[BX].handle ;file handle int dos_call ;buffer address is DS:DX pop BX jnc flush_ok cmp DS:crit_flag,on ;critical error? je flush_err ;yes cmp AX,bufsize ;did DOS write it all? je flush_ok ;yes ; flush_err: display lptxe ;ring bell mov DS:[BX].active,off ;turn us off mov DS:crit_flag,off ;clear error flag ; ;then try to close the file ; ;to save what we can flush_ok: push BX mov BX,DS:[BX].handle mov AH,close_file ;close the file int dos_call pop BX ; flush_exit: pop DS pop ES ; push DS lds DX,dword ptr DS:off_crit mov AX,2524h ;restore critical error vector int dos_call pop DS ; mov AX,word ptr DS:stksav+2 ;copying to DOS's workarea cmp AX,100H ja flushe ;must be DOS's segment push ES mov ES,AX mov DI,0 ;restore data areas mov SI,offset dosstk mov CX,0C80H ;length to restore cld rep movsb ;copy DOS's stack pop ES ;restore ES ; flushe: ret flush endp ; end_res db 0 ; ; ; This is the end of the memory resident portion of LPTx ; ; ;-------------------------------------------------------------------- ;-------------------------------------------------------------------- ;-------------------------------------------------------------------- ; ; all following data is in the Code Segment ; mach_type db 0 save_psp dw 0 DOS_version db 0 ;Major Version Number db 0 ;Minor Version Number drive db 0 ;default drive number 0=A etc. flag_27 db 0 ; 1=make this copy resident wrong_dos db 'DOS 2.0 or later required for LPTx',lf,cr,dollar up_msg db 'LPTx - Line Printer Redirection Program - V2.00' db lf,cr,' Copyright 1985 Mark C. DiVecchio',lf,cr db dollar resident db lf,cr,'Resident Portion of LPTx Loaded',lf,lf,cr db dollar lptx_err_3 db 'Could not delete file',lf,cr,dollar lptx_over db cr,lf,'File already exists. Do you want to overwrite ' db 'it? (y or n) :$' lptx_nc db 'File selection canceled',cr,lf,dollar lptx_del db 'File is being overwritten',lf,cr,dollar lptx_cr db lf,cr,dollar lptx_bad db 'Invalid Option',lf,cr db 'Calling sequence:',lf,cr db 'lptx {-1,-2,-3} {-c -o }' db lf,cr,dollar lptx_on db lf,cr,'Redirection started. Disk file opened.' db lf,cr,dollar lptx_off db lf,cr,'Redirection ended. Disk file closed.' db lf,cr,dollar lptx_creat db 'Could not create the disk file',lf,cr,dollar ; ; messages for STAT proc ; stat_stat db cr,lf,'LPTx Status :',cr,lf,dollar stat_lp db 'lpt' stat_ptr db ' : $' stat_off db ' not redirected',cr,lf,dollar stat_dir db ' redirected to disk file ' stat_fn db 60 dup (blank) ; ; yn_max db 2 ;max # of char yn_act db 0 yn_in db 2 dup (0) ; ;-------------------------------------------------------------------- ; ; This is the main routine which is executed each time that LPTx is ; called. In this routine, DS points to the data segment which is ; transient. ES points to the data segment which is permanently ; resident. ES:BX points to the data structure for the selected ; line printer, 1, 2, or 3. ; The offsets are the same for both. If this is the first ; time that LPTx is run, then ES=DS. ; l_start: sti ;interrupts on push DS ;Save DS xor AX,AX ;clear AX for return IP push AX ;put 0 on stack ; ;to check for machine type look at ; F000:FFFE ; = FF IBM PC ; = FE IBM XT ; = FD IBM PCjr ; = FC IBM PC AT ; mov AX,0F000h mov ES,AX mov BX,0FFFEh mov CL,ES:[BX] ;get machine type mov mach_type,CL ;save machine type mov save_psp,DS ;segment address of PSP ; ; get the DOS version number ; returns zero for pre DOS 2.0 releases mov AH,30h int dos_call ;call DOS mov word ptr DOS_version,AX ; cmp DOS_version,2 ;is it DOS 2.+ jge dos_ok ;yes display wrong_dos ;print error message mov AH,0 int dos_call ;terminate dos_ok: ; mov AH,def_drive ;get current default drive int dos_call mov drive,AL ;save the drive number ; display up_msg ;print program ID mov flag_27,off ;to not make resident ; are we already resident? mov AL,17h ;get current vector address mov AH,35h int dos_call mov word ptr oldint,BX mov word ptr oldint[2],ES ;save it for later use ; cmp ES:id,03579h ;check if already resident je in_core ;yes - ES has segment address mov flag_27,on ;to make this copy resident push CS pop ES ;set ES to CS for segment address ; mov AL,drive add AL,'a' ;make it a letter mov BX,offset lpt1 mov ES:[BX].filen,AL ;put it into the filename mov BX,offset lpt2 mov ES:[BX].filen,AL ;put it into the filename mov BX,offset lpt3 mov ES:[BX].filen,AL ;put it into the filename in_core: ;ES is ok ; ; ES now points to resident data area ; ; set up ES:BX to point to default data structure ; mov BX,offset lpt1 ;offset - default to LPT1 ; ;get options and file name ;scan input line for line printer number ; mov SI,81h ;starting offset mov CL,DS:80h ;length of input line mov CH,0 cmp CX,0 ;nothing? jne inp_lp ;no jmp nor_exit ;yes, then just display status inp_lp: cmp byte ptr DS:[SI],dash ;a dash ? je got_opt ;yes cmp byte ptr DS:[SI],cr ;a carriage return? je scan_done ;yes cmp byte ptr DS:[SI],blank ;a blank? je inp_ret ;yes jmp no_b ;assume that we got a file name ;without the -o option inp_ret: inc SI ;ignore blanks loop inp_lp ;continue to scan ; ; scan of whole line is complete, if options were not found, we ; use defaults : LPT1 and file LPTX1.LST on the default drive. ; note : at least one option must be specified ; scan_done: jmp lptx_make ;go create the file ; got_opt: ;we got an option inc SI ;to option cmp byte ptr DS:[SI],'1' ;LPT1? jne chk_2 mov BX,offset lpt1 ;offset from ES jmp inp_ret chk_2: cmp byte ptr DS:[SI],'2' ;LPT2? jne chk_3 mov BX,offset lpt2 ;offset from ES jmp inp_ret chk_3: cmp byte ptr DS:[SI],'3' ;LPT3? jne chk_fil mov BX,offset lpt3 ;offset from ES jmp inp_ret chk_fil: ;is it file? cmp byte ptr DS:[SI],'o' ;open a file je file_op ;yes cmp byte ptr DS:[SI],'c' ;close a file je file_cl ;yes display lptx_bad ;incorrect option jmp nor_ex ; file_cl: ;close the output file cmp ES:[BX].active,on ;are we active? jne no_close ;no mov AL,1AH ;CTRL-Z mov word ptr ES:stksav+2,AX ;do this so that prnt does ;not bother to save the DOS stack push DS push ES pop DS ;set DS to point to resident ;data segment call prnt ;print end of file mark call flush ;flush out write buffer pop DS ;restore DS ; mov ES:[BX].active,off ;make us inactive display lptx_off ;redirection off message no_close: jmp nor_exit ;nothing to close so exit file_op: ;open a file for output ;get the file name inc SI ;to next chracter cmp byte ptr DS:[SI],blank ;a blank? jne no_b ;no inc SI ;skip over blank no_b: ; at this point, we have found a new file name. We close the old ; file if one was open cmp ES:[BX].active,on ;are we active? jne no_cl ;no mov AL,1AH ;CTRL-Z mov word ptr ES:stksav+2,AX ;do this so that prnt does ;not bother to save the DOS stack push DS push ES pop DS ;set DS to point to resident ;data segment call prnt ;print end of file mark call flush ;flush out write buffer pop DS ;restore DS ; mov ES:[BX].active,off ;make us inactive display lptx_off ;redirection off message no_cl: mov DI,BX ;base of structure add DI,offset filen ;add offset of destination ; push SI ;save pointer to file name ; search for a drive letter inc SI ;should point to a colon if ;one is there cmp byte ptr [SI],colon ;? je got_drive ;yes get_drive: mov AL,drive ;get drive letter add AL,'a' ;make it a letter mov ES:[DI],AL ;put it in file name inc DI mov byte ptr ES:[DI],colon ;put in a colon inc DI jmp path_search got_drive: pop SI ;move pointer back to start mov AL,[SI] ;get the given drive mov ES:[DI],AL ;move it sub AL,'a' ;make it a number mov drive,AL ;save the drive number inc SI inc DI mov byte ptr ES:[DI],colon inc DI inc SI push SI ;save new start pointer path_search: ; now search for a backslash which says that a pathname was given bk_s_lp:cmp byte ptr [SI],backslash je got_path ;a path cmp byte ptr [SI],cr ;end of the file name? je get_path ;yes with no path inc SI jmp bk_s_lp ;loop get_path: mov byte ptr ES:[DI],backslash ;create the path inc DI mov DL,drive ;the current drive inc DL ;bump it for DOS push DS push ES pop DS ;set up DS for DOS mov SI,DI ;set up SI for pathname mov AH,def_path ;get current directory int dos_call ;path goes into DS:SI pop DS ;restore DS cmp byte ptr ES:[SI],null ;null path? je null_path ;yes - root directory path_lp: ;now find the end of the string cmp byte ptr ES:[SI],null ;null byte marks end of pathname je end_path ;now append the file name inc SI jmp path_lp end_path: mov byte ptr ES:[SI],backslash inc SI null_path: mov DI,SI ;DI is destination got_path: pop SI ;restore source of filename ; pick up everything to next blank get_lp: mov AL,DS:[SI] ;character mov ES:[DI],AL ;put it away cmp AL,cr ;was it a Carriage Return? je end_line cmp AL,blank ;was it a space? je end_line inc SI inc DI jmp get_lp ;no so get next character end_line: mov byte ptr ES:[DI],null ;zero out the CR or blank ;at the end of the filename ;it becomes an ASCIIZ string sub DI,BX ;now take out the base and cmp DI,offset filen ; make sure that we got something jne lptx_make ;file name was ok display lptx_creat ;could not understand the file name jmp nor_exit ;don't stay resident ; nor_ex: jmp nor_exit lptx_make: ; ; default DTA used by Find File is set by DOS to an offset of ; 80h into this program's Program Segment Prefix ; push DS push ES pop DS ;uses DS:DX mov DX,BX add DX,offset filen ;file name mov AH,find_file mov CX,0 ;normal files only int dos_call ;find first match pop DS jnc lptx_d ;file was found jmp lptx_create ;not there - which is ok ;file already exists lptx_d: display lptx_over mov DX,offset yn_max;input buffer mov AH,0AH int dos_call cmp yn_act,0 ;anything typed? display lptx_cr je lptx_x ;no - exit cmp yn_in,'y' ;a yes? je lptx_d_yes ;yes cmp yn_in,'Y' ;a yes? je lptx_d_yes ;yes lptx_x: display lptx_nc jmp nor_exit ;all done if we can't overwrite ;see if we should abort the host lptx_d_yes: display lptx_del ; push DS push ES pop DS ;uses DS:DX mov DX,BX add DX,offset filen ;file name mov AH,delete_file int dos_call ;delete file pop DS jnc lptx_create ;ok its gone display lptx_err_3 ;can't delete it jmp nor_exit ; ; lptx_create: ; ; create the file push DS push ES pop DS ;uses DS:DX mov DX,BX ;base of this LPT's structure add DX,offset filen ;file name mov AH,create_file mov CX,0 ;normal files only int dos_call ;find first match pop DS jnc creat_ok display lptx_creat ;could not create the file jmp nor_exit ;don't stay resident ; creat_ok: ;now close the file push BX mov BX,AX ;AX was loaded by the create file ; call mov AH,close_file ;close the file int dos_call pop BX ; display lptx_on ; set the program up for writing mov ES:[BX].sp_left,empty ;set buffer empty mov ES:[BX].active,on ;set us on ; cmp flag_27,on ;make this one resident? jne nor_exit ;no mov AH,25h ;set interrupt vector mov AL,17h ;BIOS printer mov DX,offset prt_int int dos_call display resident ;resident loaded message call stat ;display status mov DX,offset end_res int 27h ;terminate but stay resident nor_exit: call stat ;display status mov AH,0 int dos_call ;terminate ;------------------------------------------------------------------------ ; ; displays the status of each of the three line printers ; stat proc near ; display each LPTx with a message "not redirected" ; or redirected to display stat_stat stat_1: mov BX,offset lpt1 ;first printer mov stat_ptr,'1' display stat_lp cmp ES:[BX].active,on ;are we active? je stat_1_a ;yes display stat_off jmp stat_2 stat_1_a: mov SI,BX ;base add SI,offset filen ;offset mov DI,offset stat_fn stat_1_lp: mov AL,ES:[SI] mov [DI],AL inc SI inc DI cmp AL,null ;loop till a null byte is found jne stat_1_lp mov byte ptr [DI],cr inc DI mov byte ptr [DI],lf inc DI mov byte ptr [DI],dollar display stat_dir ;display file name ; stat_2: mov BX,offset lpt2 ;second printer mov stat_ptr,'2' display stat_lp cmp ES:[BX].active,on ;are we active? je stat_2_a ;yes display stat_off jmp stat_3 stat_2_a: mov SI,BX ;base add SI,offset filen ;offset mov DI,offset stat_fn stat_2_lp: mov AL,ES:[SI] mov [DI],AL inc SI inc DI cmp AL,null ;loop till a null byte is found jne stat_2_lp mov byte ptr [DI],cr inc DI mov byte ptr [DI],lf inc DI mov byte ptr [DI],dollar display stat_dir ;display file name ; stat_3: mov BX,offset lpt3 ;third printer mov stat_ptr,'3' display stat_lp cmp ES:[BX].active,on ;are we active? je stat_3_a ;yes display stat_off jmp stat_done stat_3_a: mov SI,BX ;base add SI,offset filen ;offset mov DI,offset stat_fn stat_3_lp: mov AL,ES:[SI] mov [DI],AL inc SI inc DI cmp AL,null ;loop till a null byte is found jne stat_3_lp mov byte ptr [DI],cr inc DI mov byte ptr [DI],lf inc DI mov byte ptr [DI],dollar display stat_dir ;display file name ; stat_done: ret stat endp ; cseg ends %out EOF end lptx