;       SCCSID = @(#)i_devct5.asm	6.1 91/02/15
;	SCCSID = @(#)i_devct5.asm	6.2 90/01/22
; ****************************************************************************
; *									     *
; *			  IBM/Microsoft Confidential			     *
; *									     *
; *		    Copyright (c) IBM Corporation  1987, 1990		     *
; *		    Copyright (c) Microsoft Corp.  1987, 1990		     *
; *			      All Rights Reserved			     *
; *									     *
; ****************************************************************************
;***  i_devct5.asm - routines for category 5 IOCTL calls
;
;	Internal routines used by API IOCTL library routines
;	David Silverstein
;	Copyright MicroSoft Corporation, 1986
;	7-May-1986
;
;	This file contains support routines for printer control IOCTLs
;
;	Special Information
;
;	    This file references a table called __api_dev_table
;	    defined in d_open.asm  Access to this table is necessary to
;	    obtain the device number of character devices.
;
;	    If a device driver running under Dos 3.2 or above (except 3xBox)
;	    supports the printer generic ioctls, they will not be called
;	    through this interface.
;
;	    If printer infinite retry is enabled and not disabled, the
;	    application must exit using DOSEXIT, CtrlC or critical error
;	    for the printer interrupt vector to get restored.  Otherwise,
;	    the next int 17h will go out for a very long lunch.
;
;	    The cleanup routine to restore the printer interrupt is common
;	    for exits via CtrlC, critical error, and DOSEXIT.  Because
;	    critical error handlers cannot contain some int 21s, the
;	    interrupt vectors are replaced directly.  It is deemed to
;	    expensive in terms of code size to have a separate cleanup
;	    routine for critical error handling.
;
;	Modification History
;
;	18-Nov-86 (billme): add code to move device handle into BX
;	before IOCTL call
;
;	15-Jan-87 (billme): Change reference to __api_dev_table to
;	call to __api_devtab_ptr ... to reduce code size.
;
;	04-May-87 (robertc): Install hooks for DOSErrorClass Support.
;	All int 21h errors are passed through api_get_extended to get the
;	extended errorcode and to provide the class, action, and locus for
;	the dynamic error FIFO queue.
;
;	22-Jun-87 (roberts): PTM 5590 Modify to account for new handinfo bits

include dos2equ.inc
include api.inc
include error.inc
include d_ioctl.inc
include i_devct5.inc


_TEXT	 segment byte public 'CODE'

	assume	CS:_TEXT, DS:nothing

	PUBLIC	PRINTERCTRL

	EXTRN	api_get_extended:far	;@@ added support for DOSErrorClass
	EXTRN	__api_devtab_ptr:near
	EXTRN	_api_getvrsn	  : NEAR
	EXTRN	FapiAddSigCatch	  : FAR
	EXTRN	FapiSubSigCatch	  : FAR
	EXTRN	__api_dev_table	  : WORD


; This is a table of bit masks. They are used to test and set bits in the
; printer retry enable flag (RetryEnabled). The table is indexed by the
; printer number.

PrtRetry	db	01		; LPT1
		db	02		; LPT2
		db	04		; LPT3
		db	08		; LPT4
MAX_PRINTER	equ	($ - PrtRetry) - 1

RetryEnabled	db	0		; initialized to infinite retry disabled

OrigPrtVect	dd	?		; (17h) original printer vector

PRINTERCTRL	proc	near

	push	es
	push	cx
	push	dx
	push	di
	push	si

	mov	ax, error_invalid_handle
	mov	bx, [DevHandle]		; get handle
	cmp	bx, MAX_HANDLES		; is it within range?
	jbe	pctrl0			; yes, continue
	jmp	pctrl9			; no, exit with error

pctrl0:
	mov	ax, error_bad_command
	mov	cx, [Function]		; printer IOCTL function code
	and	cx, NOT IGNORE_ERROR	; mask off error if unsupported
					; bit
	test	ch, ch			; is the function code bad?
	jz	pctrl1			; no, continue
	jmp	pctrl9			; yes, exit with error

pctrl1:
	call	_api_getvrsn		; get Dos version
	cmp	ah, 10			;@@  is it in the 3xBox?
	jb	pctrl3			; we are not running the 3xBox

	; We are running the 3xBox.  The IOCTL info is passed to the
	; driver through an int 21 interface.
pctrl2:
	mov	bx, [DevHandle]		; get handle
	mov	ch, PRINTER_CATEGORY
	lds	di, [ParmList]		; si:di <= parameter list pointer
	mov	ax, ds
	mov	si, ax
	lds	dx, [DataArea]		; ds:dx <= data packet pointer
	mov	ax, D2_IOCTL SHL 8 + D2_HANDLE_IOCTL ; 2.x IOCTL for handles
	int	21h
	jc	pctrl2x			;@@ error occurred - get extended
	xor	ax,ax			;@@ clear ax (no error)
	jmp	pctrl9			;@@ no error occured

	; get the extended error code - hopefully what the driver returned.
	; Because this code only runs in the 3xBox, we don't have to worry about
	; Get Extended Error not being supported in Dos 2.x.

pctrl2x:
	call	api_get_extended	;@@ added support for DOSErrorClass
	jmp	short pctrl9


; Get device number from handle table

pctrl3:
	mov	ax, bx				; AX <- handle number
	call	__api_devtab_ptr		; BX <- address of entry

	xor	dx, dx				; dh must be clear
	mov	dl, _TEXT:[bx].hi_attrdev	; get device number
						; should be byte, temp. word.
	mov	ax, error_invalid_handle
	test	dl, DEV_FLAG			; is it a device handle?
	jz	pctrl9				; error
	test	dl, LPT_FLAG			; is it a printer handle?
	jz	pctrl9				; error
	and	dl, UNIT_FLAG			;@@5590mask prt handle id bits
	cmp	dl, MAX_PRINTER			; is this a valid printer num?
	ja	pctrl9

	mov	ax, [Function]			; printer IOCTL function code
	and	ax, NOT IGNORE_ERROR		; mask off error if unsupported
						; bit
	cmp	ax, D5_GetPrinterStat
	jne	pctrl4
	call	GetPrinterStat			; get printer status
	jmp	short pctrl9
pctrl4:
	cmp	ax, D5_SetFrameCtrl
	jne	pctrl5
	call	SetFrameCtrl			; set frame control
	jmp	short pctrl9
pctrl5:
	cmp	ax, D5_SetInfRetry
	jne	pctrl6
	call	SetInfRetry			; set infinite retry
	jmp	short pctrl9
pctrl6:
	cmp	ax, D5_InitPrinter
	jne	pctrl7
	call	InitPrinter			; initialize printer
	jmp	short pctrl9
pctrl7:
	cmp	ax, D5_GetInfRetry
	jne	pctrl8
	call	GetInfRetry			; get infinite retry status
	jmp	short pctrl9
pctrl8:
	jmp	pctrl2
pctrl9:
	pop	si
	pop	di
	pop	dx
	pop	cx
	pop	es
	ret

; ***** SetFrameCtrl - Set Frame Control
;
; Since we have no way of determining the current printer status,
; this routine always outputs the control requests to the IBM graphics printer.
; NOTE: This ioctl supports the IBM graphics and compatible printers only.
;
;	ENTRY dx = printer number

SetFrameCtrl:

	les	bx, [DataArea]			; get data param area pointer
	mov	ah, es:[bx]			; get requested chars per line
	cmp	ah, CHARS_PER_LINE_80		; is it 80 chars per line?
	je	setfrm1				; go check lines per inch
	cmp	ah, CHARS_PER_LINE_132		; is it 132 chars per line?
	mov	al, SHIFT_IN			; load in case it is
	je	setfrm2				; go check lines per inch
	mov	ax, error_bad_command		; invalid chars per line, set
	jmp	short setfrm6			; error code and exit
setfrm1:
	mov	al, DEVICE_CTRL_2		; code for 80 chars per line
setfrm2:
	mov	bh, es:[bx+1]			; get requested lines per inch
	cmp	bh, LINES_PER_INCH_6		; is it 6 lines per inch?
	je	setfrm3				; go and send codes to printer
	cmp	bh, LINES_PER_INCH_8		; is it 8 lines per inch?
	mov	bl, ASCII_0			; load in case it is
	je	setfrm4				; go and send codes to printer
	mov	ax, error_bad_command		; invalid lines per inch, set
	jmp	short setfrm6			; error code and exit
setfrm3:
	mov	bl, ASCII_2			; codes for 6 lines per inch
setfrm4:
	mov	ah, B_PrintChar
	int	BIOS_PRINTER_IO			; send chars per line to printer
	test	ah, LPT_TIM_OUT			; did timeout occur?
	jz	setfrm5				; no, continue
	mov	ax, error_not_ready		; load not ready error code
	jmp	short setfrm6			; go exit with error

setfrm5:
	mov	ax, B_PrintChar SHL 8 + ASCII_ESC ; send beginning of lines per
	int	BIOS_PRINTER_IO			; inch sequence
	mov	ah, B_PrintChar
	mov	al, bl
	int	BIOS_PRINTER_IO			; finish sending lines per inch
						; sequence
	xor	ax, ax				; clear error return code
setfrm6:
	ret


; ***** SetInfRetry - Set Infinite Retry
;
; This routine sets up the printer for infinite retry.
; The only way out of the retry loop is the BREAK key.
;
;	ENTRY dx = printer number

SetInfRetry:

	push	ds

	lds	bx, [DataArea]			; get data area address
	test	byte ptr ds:[bx], NOT INFINITE_RETRY   ; is this data valid?
	jz	setret1
	mov	ax, error_bad_command		; data invalid, load error
	jmp	setret6				; go exit
setret1:
	test	byte ptr ds:[bx], INFINITE_RETRY
	jnz	setret2				; enable infinite retry
	jmp	setret4				; disable infinite retry
setret2:
	mov	al, _TEXT:[RetryEnabled]	; get retry enabled flag byte
	mov	bx, dx
	mov	bl, _TEXT:PrtRetry[bx]		; get mask for current printer
	or	byte ptr _TEXT:[RetryEnabled], bl ; set current printer bit
	test	al, 0ffh			; is the printer vector already
						; installed?
	jz	setret3				; go install it
	jmp	setret5				; exit without error

	; Set up exit handling interrupt vectors. This is done to prevent the
	; program from exiting without cleaning up the printer interrupt
	; handler used for infinite retry.

setret3:
; save old and set up new printer interrupt vector

	mov	ax, D2_GetVector SHL 8 + BIOS_PRINTER_IO
	int	21h					; get printer int vector
	mov	word ptr _TEXT:[OrigPrtVect], bx	; save vector
	mov	word ptr _TEXT:[OrigPrtVect+2], es

	mov	dx, seg _TEXT
	mov	ds, dx			; ds:dx <= address of cleanup routine
	mov	dx, offset _TEXT:cleanup
	mov	bx, PRTDEVIOCTL		; Fapi library call number
	call	FapiAddSigCatch		; install cleanup routine

	mov	bx, cs					; set up new vector
	mov	ds, bx
	mov	dx, offset _TEXT:prthook
	mov	ax, D2_SetVector SHL 8 + BIOS_PRINTER_IO
	int	21h					; install it
	jmp	short setret5

setret4:
	test	byte ptr _TEXT:[RetryEnabled], 0ffh	; is retry enabled?
	jz	setret5					; no, exit without error

	; flag that infinite retry for this printer is disabled

	mov	bx, dx
	mov	bl, _TEXT:PrtRetry[bx]		; get mask of current printer
	not	bl
	and	byte ptr _TEXT:[RetryEnabled], bl ; clear this printer bit
	jnz	setret5

	mov	bx, PRTDEVIOCTL		; Fapi library call number
	call	FapiSubSigCatch		; deinstall cleanup routine

setret5:
	xor	ax, ax				; clear error return code

setret6:
	pop	ds
	ret


; ***** InitPrinter - Initialize Printer
;
;	ENTRY:	Printer number in dx
;

InitPrinter:
	mov	ah, B_InitPrinter
	int	BIOS_PRINTER_IO		; call printer_io bios routine
	xor	ax, ax			; clear error return code
	ret


; ***** GetInfRetry - Get Infinite Retry
;
;	ENTRY:	Printer number in dx
;	RETURN: Either infinite retry enabled or disabled
;

GetInfRetry:

	les	di, [DataArea]			; get data parameter area addrs
	xor	al, al
	mov	bx, dx
	mov	bl, _TEXT:PrtRetry[bx]
	test	_TEXT:[RetryEnabled], bl	; is infinite retry enabled?
	jz     getret1				; return zero
	mov	al, INFINITE_RETRY		; infinite retry enabled
getret1:
	mov	es:[di], al			; load retry status
	xor	ax, ax				; clear error return code

	ret


; ***** GetPrinterStat - Get Printer Status
;
;	ENTRY:	Printer number in dx
;	RETURN: Status of the printer port from BIOS
;

GetPrinterStat:

	mov	ah, B_PrinterStat
	int	BIOS_PRINTER_IO		; call printer_io bios routine
	les	di, [DataArea]
	mov	es:[di], ah		; load printer status into data area
	xor	ax, ax			; clear error return code
	ret


prthook:
	cmp	dx, MAX_PRINTER		; is it a valid printer port?
	ja	phook2			; no, don't touch it

	push	bx
	mov	bx, dx			; check if infinite retry is enabled
	mov	bl, _TEXT:PrtRetry[bx]	; for this printer
	test	_TEXT:[RetryEnabled], bl
	pop	bx
	jnz	phook30			; it is

phook2:					; otherwise just jump to original vector
	jmp	dword ptr _TEXT:[OrigPrtVect]

phook30:
	pushf				; save flags for call of the original
					; printer interrupt vector
phook3:
	push	ds
	push	ax

	xor	ax, ax			; establish addressability to
	mov	ds, ax			; break key post location
	and	byte ptr ds:[BiosBreak],  NOT BRK_MASK	; clr break indication
	pop	ax
	pop	ds
	call	dword ptr _TEXT:[OrigPrtVect]  ; call original printer vector
	push	ax
	push	ds

	xor	ax, ax			; establish addressability to
	mov	ds, ax			; break key post location
	test	byte ptr ds:[BiosBreak], BRK_MASK      ; did a break occur?
	pop	ds
	pop	ax
	jnz	phook4			; exit
	test	ah, LPT_TIM_OUT		; did timeout occur?
	jz	phook5			; if not, go exit
	jmp	short phook3		; else go retry
phook4:
	or	ah, LPT_TIM_OUT		; break key hit, simulate error
phook5:
	iret


; This subroutine restores the printer interrupt vector
; to it's original value.  Since the critical error handler
; may call this routine, we cannot use any int21s.

cleanup proc	far
	push	ax			; save Dos function call number
	push	bx
	push	ds

	; restore the printer interrupt vector to its original address
	; We must do this by replacing the interrupt vector directly.

	xor	ax, ax
	mov	ds, ax
	mov	bx, BIOS_PRINTER_IO
	shl	bx, 1
	shl	bx, 1
	mov	ax, word ptr _TEXT:[OrigPrtVect]
	mov	[bx], ax
	mov	ax, word ptr _TEXT:[OrigPrtVect + 2]
	mov	[bx + 2], ax

	pop	ds			; restore Dos function call number
	pop	bx
	pop	ax
	ret

cleanup endp


PRINTERCTRL	endp
_TEXT		ends

		end
