[net.sources] Calling DOS from within Interrupt Handlers

mark@sdencore.UUCP (Mark DiVecchio) (05/27/85)

----------------------------------------------------


I have had a substantual number of requests for a copy of my
PC-DOS program which permits interrupt handlers to call other
DOS functions.

I post it here for your edification. 
The documentation :-) is first, followed by the .ASM source.


LPTx : Line Printer Output Capture Routine

-------------------------------------------------------------

 Version 3.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
-------------------------------------------------------------

	Have you ever wanted to get some data from your screen into a
program? Have you ever wanted to grab onto some printer data and put it into
a disk file but the program you are using does not have that as a option?
Well here is the answer to your problem. This program will grab onto
anything sent out of a line printer port as long as the program uses the
standard BIOS INT 17h call. I have tried this with printscreen, with
printscreen in graphics mode (produces some mighty unusual output),  with
SIDEWAYS, with the copy command to LPT1: etc, with my word processor
(FinalWord), with MASM (direct output to LPT1) with Wordstar, with 1-2-3 and
with the DOS '>' redirection command line option. It all seems to work.

	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 redirections may be active at the same time.

	LPTx requires DOS 2.0 or later.

Calling sequence:
lptx -1 -o <d:[pathname]filename>

where -1 means redirect LPT1, -2 means redirect LPT2, -3 means redirect
	 LPT3
	 This option must appear first

      -o means start the redirection to file specified. If redirection
         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 LPTXy.LST which it
	 will create in the root directory on the default drive - where
	 y is 1, 2, or 3.)

	 It is not necessary that you specify the complete path name
	 for the file. LPTx will create the file in the default 
	 directory if you don't specify a directory. LPTx will always
	 be able to find the file because it saves the complete path.

	-c means close the file and send all further 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 ?				Displays a HELP screen

lptx -1				redirects LPT1 output to file named
				LPTX1.LST in the root directory
				on the default drive or the last
				named file.

lptx -o a:\able.xxx		redirects 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		redirects 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 redirection 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 redirection is taking place to LPT2,
				this is a NOP. LPT1 and LPT3 are not
				affected.

	By redirecting 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 redirecting to a diskette file, do not remove the diskette
once the redirection starts. I recommend redirecting to a hard disk or a RAM
disk.

	If LPTx encounters any kind of error during the redirection, 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 with
LPT1 redirection does not shut down LPT2 or LPT3 redirection.

	LPTx captures the int 17h interrupt vector. Problems may occur with
print spoolers which also take over the int 17h  vector. You can be sure that
LPTX works correctly by running LPTX after you have run your print spooler.
LPTX will be transparent to the print spooler but your print spooler may not
be transparent to LPTX. LPTX works fine with IBM's PRINT command.

	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 just 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. 


-------------Program Follows---------------------------



title LPTx : Line Printer Output Capture Routine
page	66,132
;-------------------------------------------------------------
;
;	MAIN PROGRAM	Version 3.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.
;
; This version (3.0) is fully compatible with IBM's PRINT command and
; hopefully most other print spoolers. I changed the method by which
; LPTX determines if a resident copy of itself is already in memory.
;
; Calling sequence:
; lptx -1 -o <d:[pathname]filename>
;
; 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 ?			Displays a HELP screen
;
; 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 print spoolers which also take over the int 17h 
; vector. You can be sure that LPTX works correctly by running LPTX after
; you have run your print spooler. LPTX will be transparent to the print
; spooler but your print spooler may not be transparent to LPTX.
; LPTX works fine with IBM's PRINT command.
;
; 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.
;
; My thanks to Don D. Worth of UCLA/OAC for his program named SPOOL.
; He included the concept of the saving the DOS stack when the interrupt
; routine called DOS on its own. I stole the idea for this program.
;
; 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:
	cmp	DX,0F0Fh	;my flag to detect that LPTX is
				;already loaded and alive.
	jne	reg_call	;This is a regular print call
	jmp	ret_ack
reg_call:			; set up BX
	push	BX
	cmp	DX,0		;lpt1?
	jne	chk_lpt2	;no
	mov	BX,offset lpt1	;offset to LPT1
	jmp	short bx_set
chk_lpt2:
	cmp	DX,1		;lpt2?
	jne	chk_lpt3	;no
	mov	BX,offset lpt2	;offset to LPT2
	jmp	short 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
;
ret_ack:			;return acknowledgement that I'm here
	mov	DX,05555h
	mov	AX,0AAAAh
	push	CS		;now set up ES to point to the resident
	pop	ES		; data area
	iret
;
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 - V3.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 <d:[pathname]filename>}'
		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
;
; HELP screen
;
help_msg	db	lf,cr,'Calling sequence : ',lf,lf,cr
		db	'LPTX -p -f <[d:][\pathname\pathname]filename>'
		db	lf,lf,cr
		db	'    where  p = printer number : 1, 2, or 3',lf,cr
		db	'           f = function : o for open a print file'
		db	lf,cr
		db	'                          c for close a print file'
		db	lf,cr
		db	'           drive letter & pathname are optional'
		db	lf,cr
		db	'    defaults : p = 1',lf,cr
		db	'               f = o',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
;
; get old interrupt handler
;
	mov	flag_27,off	;to not make 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
;
; are we already resident in memory?
;
	mov	DX,0F0Fh		;check if LPTX is already resident
	mov	AX,2			;get status
	int	17h			;call int 17h - BIOS
	cmp	DX,5555h		;my handler sets DX to 5555h
					;and sets ES 
	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],'?'	;a ?  ?
	jne	cont_scan		;no
	jmp	help			;yes - go show help data
cont_scan:
	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	short inp_ret
chk_2:	cmp	byte ptr DS:[SI],'2'	;LPT2?
	jne	chk_3
	mov	BX,offset lpt2		;offset from ES
	jmp	short inp_ret
chk_3:	cmp	byte ptr DS:[SI],'3'	;LPT3?
	jne	chk_fil
	mov	BX,offset lpt3		;offset from ES
	jmp	short 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	short 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	short 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	short 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
;
; Now set LPTX up as the new int 17h interrupt handler
;
	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
;
; HELP printer
;
help:	display	help_msg		;display the HELP screen
	jmp	short nor_exit
;
; Normal exit for transient copy of LPTX
;
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 <filename>
	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	short 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	short 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	short 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
;
;
;
;
;
; Good Luck
;

-- 
Mark C. DiVecchio
K3FWT
[ihnp4|akgua|decvax|dcdwest|ucbvax]sdcsvax!sdencore!mark