;       SCCSID = @(#)demdd.asm	6.1 91/07/12
;       SCCSID = @(#)demdd.asm	6.1 90/11/17
;	SCCSID = @(#)demdd.asm	      13.1 90/07/15
	TITLE	demdd - Real Mode Device Driver for MVDM
	NAME	demdd

	page	,132

;***	demdd - Dos Device drivers for MVDM
;
;	Copyright (C) Microsoft Corp. 1982, 1983, 1984, 1985
;
;
;	P R N - PRINTER DEVICE
;	A U X - AUXILLARY DEVICE
;
;
; This file contains the PRN dand AUX device Drivers.
;
; The routines in this file are:
;
;	routine			function
;	-------			--------
;	PRN$WRIT		Write to printer device
;	PRN$STAT		Printer status routine
;	PRN$TilBusy		Print spooler routine
;	Prn$GenIOCTL		Generic IOCTL routine
;
;	AUX$READ		Read characters
;	AUX$RDND		Non-desrucrtive read
;	AUX$FLSH		Flush AUX
;	AUX$WRIT		Write characters
;	AUX$WRST		Get status
;
;
; Only PRN device is supported. LPT1-3 are treated as OS/2 devices.
;
;   Modification History
;	sudeepb bharati 10-Sep-1989  Ported for MVDM (DCR 540)
;
;
.xlist
include v86seg.inc
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include demmac.inc
include devsym.inc
include error.inc
include dossymc.inc
include ioctl.inc
	.SFCOND
	.LALL
.list

;
; EQU for this file:
;
; The following are used to test status on return from ROM-BIOS

; For PRN Device driver

BusyBit		equ	10000000b
OutPaperBit	equ	00100000b
IOErrorBit	equ	00001000b
TimeOutBit	equ	00000001b

; For AUX Device driver

DSetReadyBit	equ	00100000b   ; Data Set Ready Status Bit
DReadyBit	equ	00000001b   ; Data Ready Status Bit
THRegEmptyBit	equ	00100000b   ; Tran Holding Register Empty Status Bit
InputErrorBits	equ	00001110b   ; Framing or Parity or Overrun error

V86DATA	    SEGMENT

AUXPRN	    DB	    ?		    ; 0 for PRN and 1 for AUX
PTRSAV	    DD	    ?		    ; Svae Request Header address
WAIT_COUNT  DW	    4		    ; For IOCTL subfunction 16 of PRN
AUXBUF	    DB	    ?		    ; Buffer for AUX

V86DATA	    ENDS

;
; Device Header for device PRN
;

V86CODE	   SEGMENT
	ASSUME	    CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

	PUBLIC	PRNDEV
PRNDEV	DD	-1
	DW	DEV_CHAR_DEV OR OutTilBusy + Dev320
	DW	OFFSET V86GROUP:STRATEGY
	DW	OFFSET V86GROUP:PRNInt
	DB	'PRN     '

PRNTBL	LABEL	WORD
	DW	OFFSET V86GROUP:EXIT	      ;	 0  Initialization - do nothing
	DW	OFFSET V86GROUP:EXIT	      ;	 1  Media Check
	DW	OFFSET V86GROUP:EXIT	      ;	 2  Build BPB
	DW	OFFSET V86GROUP:CMDERR	      ;	 3  Reserved
	DW	OFFSET V86GROUP:EXIT$ZER      ;	 4  Input (Read)
	DW	OFFSET V86GROUP:BUS$EXIT      ;	 5  Non-Destructive read, no wait
	DW	OFFSET V86GROUP:EXIT	      ;	 6  Input Status
	DW	OFFSET V86GROUP:EXIT	      ;	 7  Input Flush
	DW	OFFSET V86GROUP:PRN$WRIT      ;	 8  Output (Write)
	DW	OFFSET V86GROUP:PRN$WRIT      ;	 9  Output with verify
	DW	OFFSET V86GROUP:PRN$STAT      ; 10  Output Status
	DW	OFFSET V86GROUP:EXIT	      ; 11  flush output buffers
	DW	OFFSET V86GROUP:EXIT	      ; 12  I/O control write
	DW	OFFSET V86GROUP:EXIT	      ; 13  device open
	DW	OFFSET V86GROUP:EXIT	      ; 14  device close
	DW	OFFSET V86GROUP:EXIT	      ; 15  removalble media
	DW	OFFSET V86GROUP:PRN$TilBusy   ; 16  output until busy
	DW	OFFSET V86GROUP:EXIT	      ; 17  reserved
	DW	OFFSET V86GROUP:EXIT	      ; 18  reserved
	DW	OFFSET V86GROUP:PRN$GenIOCTL  ; 19  generic IOCTL
	DW	OFFSET V86GROUP:EXIT	      ; 20  reserved
	DW	OFFSET V86GROUP:EXIT	      ; 21  reserved
	DW	OFFSET V86GROUP:EXIT	      ; 22  reserved
	DW	OFFSET V86GROUP:EXIT	      ; 23  get logical device
	DW	OFFSET V86GROUP:EXIT	      ; 24  set logical device
ENDPRN	    EQU ($-PRNTBL) / 2

; Device Header for device "AUX"

	PUBLIC	AUXDEV
AUXDEV	DD	-1			 ; pointer to next device
	DW	1000000000000000B	 ; attribute word, character device
	DW	OFFSET V86GROUP:STRATEGY ; device strategy routine
	DW	OFFSET V86GROUP:AUXInt	 ; device interrupt routine
	DB	'AUX     '		 ; device name

; AUX Dispatch Table

AUXTBL	LABEL	WORD
	DW	OFFSET V86GROUP:EXIT
	DW	OFFSET V86GROUP:EXIT
	DW	OFFSET V86GROUP:EXIT
	DW	OFFSET V86GROUP:CMDERR
	DW	OFFSET V86GROUP:AUX$READ
	DW	OFFSET V86GROUP:AUX$RDND
	DW	OFFSET V86GROUP:EXIT
	DW	OFFSET V86GROUP:AUX$FLSH
	DW	OFFSET V86GROUP:AUX$WRIT
	DW	OFFSET V86GROUP:AUX$WRIT
	DW	OFFSET V86GROUP:AUX$WRST
ENDAUX	    EQU ($-AUXTBL) / 2

;   COMMOMN Device driver Entry Points

;   Strategy routine

STRATEGY PROC	FAR
	MOV	WORD PTR [PTRSAV],BX
	MOV	WORD PTR [PTRSAV+2],ES
	RET
STRATEGY ENDP


;   PRN Interrupt entry point

PRNInt	PROC	FAR
	mov	BYTE PTR [AUXPRN],0	; AUXPRN = 0 means PRN
	jmp	short ENTRY1
PRNInt	ENDP

;   AUX Interrupt entry point

AUXInt	PROC	FAR
	mov	BYTE PTR [AUXPRN],1	; AUXPRN = 1 means AUX
	jmp	short ENTRY1
AUXInt	ENDP


ENTRY1:
	cld
	pusha
	push	DS
	push	ES
	push	CS
	pop	DS
	xor	AX,AX
	ASSUME	DS:V86GROUP
	les	bx,PTRSAV
	mov	al,es:[bx].PktCmd
	cmp	BYTE PTR [AUXPRN],0
	je	short ITS_PRN
	cmp	al,ENDAUX
	jmp	short CHECK
ITS_PRN:
	cmp	al,ENDPRN

CHECK:	jbe	short prn5
	jmp	CMDERR

prn5:	xor	ah,ah
	shl	ax,1
	mov	si,ax
	mov	al,es:[bx].PktUnit	;AL = Unit code
	mov	ah,es:[bx].IOmedia	;AH = Media descriptor
	mov	cx,es:[bx].IOcount	;CX = Count
	mov	dx,es:[bx].IOstart	;DX = Start sector
	les	di,es:[bx].IOpData	;ES:DI = address of data
	cmp	BYTE PTR [AUXPRN],0
	je	short CALL_PRN
	jmp	AUXTBL[SI]
CALL_PRN:
	jmp	PRNTBL[SI]


; PRN Device Driver Procedures

;----------------------------------------------------------------
;								:
;		WRITE TO PRINTER DEVICE				:
;								:
;   CX has count of bytes to be printed				:
;   ES:DI point to source buffer contains characters		:
;								:
;----------------------------------------------------------------
PRN$WRIT:
	jcxz	EXVEC3			; no chars to output, Get out
PRN$LOOP:
	mov	BX,2			; Initialize retry flag
PRN$out:
	mov	AL,ES:[DI]		; Get a character into AL
	inc	DI			; Point to next character
	call	PRNOP			; print character
	jnz	PrRetry			; if error, try to print again
	loop	PRN$LOOP		; if more character, keep printing
EXVEC3: jmp	EXIT

PrRetry:
	dec	DI			; undo the inc above...
	dec	BX			; Decrement retry count
	jnz	PRN$out			; See if done with retrys
PMESSG: JMP	ERR$CNT			;   if so return with the error

;----------------------------------------------------------------
;								:
;		PRINTER STATUS ROUTINE				:
;								:
;----------------------------------------------------------------
;
PRN$STAT:
	call	PRNSTAT			; get the status
	jnz	PMESSG		; if error jump to error routine
	test	AH, BusyBit	; test busy bit of status
	jnz	EXVEC3		; if not busy return via EXVEC3
	Jump	BUS$EXIT	; else busy, return to busy exit




;
;   PRNSTAT	get printer status
;   PRNOP	print a character
;
; PRNSTAT and PRNOP are two routines which call on the ROM-BIOS
; printer routines.  The routines share code which calls on the bios and
; then determines which, if any, error occured.  PRNSTAT and PRNOP differ
; only by the value put into AH before the ROM-BIOS call.
;
;   INPUT	if PRNOP then character in AL
;
;   OUTPUT	- AL holds error code (as indicated on PG 2-17 of device
;		     driver guide.
;		- AH status byte from printer
;		- flag NZ if error
PRNSTAT:
	mov	AH, 2			; set command for get status
	jmp	short PRNOP1

PRNOP:	xor	AH, AH			; set command for print character
PRNOP1:
	sub	dx,dx			; PRN printer is 0
	int	17h			; call ROM-BIOS printer routine
	test	AH, IOErrorBit		; test for I/O error
	jnz	short PIO_ERR		; if I/O error, find what it is
	mov	AL, error_I24_not_ready	; assume timeout error
	test	AH, TimeOutBit		; test for timeout, set z flag
	ret

PIO_ERR:				; determine which IO error
	mov	AL, error_I24_out_of_paper; assume out of paper error
	test	AH, OutPaperBit		; out of paper error ?
	jnz	PRNOP2			; yes, then return
	mov	AL, error_I24_write_fault	; no, must be write fault
	test	AH, IOErrorBit		; clear the zero flag, indicate error
PRNOP2: ret


;----------------------------------------------------------------
;								:
;		Output until Busy				:
;								:
; Output until busy.  This entry point is used EXCLUSIVELY by	:
; the print spoolers.  Under no curcumstances should the device :
; driver block waiting for the device to become ready.		:
;								:
;   Inputs:	CX has count of bytes to output.		:
;		ES:DI points to source buffer			:
;   Outputs:	Set the number of bytes transferred		:
;		  appropriately.				:
;								:
;----------------------------------------------------------------

PRN$TilBusy:
	push	DS			; save DS
	push	ES			; copy ES to DS
	pop	DS
	mov	SI, DI			; everything is set for LODSB
PRN$TilBLoop:
	push	CX
	mov	CX,WAIT_COUNT	     ; wait COUNT times to come ready
PRN$GetStat:
	call	PrnStat			; get status
	jnz	PRN$BPERR	; if error jump to error routine
	test	AH, BusyBit	; test busy bit of status
	loopz	PRN$GetStat	; if busy keep trying
NOT$BUS:
	pop	CX			; get original count
	jz	PRN$BErr		; still not ready => done
	lodsb
	call	PrnOp			; print the character
	jnz	PRN$BErr		; error
	loop	PRN$TilBLoop		; go for more
PRN$B:
	pop	DS			; recover DS
	lds	BX,[PTRSAV]		; get pointer to header
	sub	WORD PTR [BX].IOcount,CX  ; Determine number of succ. I/O's
	jmp	EXIT			; all done, successful return
PRN$BPErr:
	pop	CX			; recover number of char left
PRN$BErr:
	pop	DS			; get pointer to header
	lds	BX,[PTRSAV]
	sub	WORD PTR [BX].IOcount,CX  ; Determine number of succ. I/O's
	jmp	err$exit		; jump to error exit


;
; Prn$GenIOCTL:
;
; Manipulates the value in WAIT_COUNT depending on the value passed in the
; Generic IOCTL packet.
; It either sets or returns the current value for the retry count for the
; device.
;
Prn$GenIOCTL:
	les	di,[PTRSAV]
	cmp	es:[di].IOGCMA,IOC_PC	; Major code
	je	PrnFunc_OK
PrnFuncErr:
	jmp	CMDERR

PrnFunc_OK:
	mov	al,es:[di].IOGCMI	; Minor code
	les	di,es:[di].IOGCAD
	mov	CX,WAIT_COUNT		; pull out retry count for device
	cmp	al,GET_RETRY_COUNT
	jz	PrnGetCount
	cmp	al,SET_RETRY_COUNT
	jnz	PrnFuncErr
	mov	cx,es:[di].RC_Count
PrnGetCount:
	mov	WAIT_COUNT,CX		; place "new" retry count
	mov	es:[di].RC_Count,cx	; return current retry count
	jmp	EXIT


;  AUX Device driver Procedures

;----------------------------------------------------------------
;								:
;	Read zero or more characters from Auxilary Device	:
;								:
;----------------------------------------------------------------
AUX$READ:
	jcxz	EXVEC2		; if no characters, get out
	call	GETBX		; put address of AUXBUF in BX
	xor	AX,AX		; clear AX register
	xchg	AL,[BX]		; Get character , if any, from
				;   buffer and clear buffer
	or	AL,AL		; if AL is nonzero there was a
				;   character in the buffer
	jnz	AUX2		; if so skip AUXIN call
AUX1:	call	AUXIN		; get character from port
AUX2:	stosb			; store character
	loop	AUX1		; if more character, go around again
EXVEC2: Jump	EXIT		; all done, successful exit

;
; AUXIN: make a call on ROM BIOS to read character from
;	 the auxilary device, then do some error checking.
;	 If an error occurs then AUXIN jumps to ERR$CNT and
;	 does NOT return to where it was called from.
;

AUXIN:
	xor	DX,DX		; set the value of DX to COM0
	mov	AH, 2		; set command for read
	int	14h		; call rom-bios
				; see if error occured
	test	AH, InputErrorBits
	jz	AROK		;   then no error
	mov	AL, 0BH		;   else report a 'read error'
	pop	BX		; pop return address off of stack
	jmp	short ERR$CNT	; jump to error routine

AROK:	ret			; successful return


;----------------------------------------------------------------
;								:
;	Aux non-destructive read with no waiting		:
;								:
;----------------------------------------------------------------
;
AUX$RDND:
	call	GETBX		; have BX point to AUXBUF
	mov	AL,[BX]		; copy contents of buffer to AL
	or	AL,AL		; if AL is non-zero (char in buffer)
	jnz	AUXRDX		;   then return character
	call	AUXSTAT		;   if not, get status of AUX device
	test	AH, DReadyBit	; if this bit is not set,
	jz	AUXBUS		;   then device is busy (not ready)

	test	AL, DSetReadyBit; if this bit is not set
	jz	AUXBUS		;   then device is busy (not ready)
	call	AUXIN		;   else aux is ready, get character
	call	GETBX		; have bx point to AUXBUF
	mov	[BX],AL		; save character in buffer
AUXRDX: jmp	short RDEXIT	; return character

AUXBUS: jmp	short BUS$EXIT	; jump to device busy exit

;----------------------------------------------------------------
;								:
;		Aux Output Status				:
;								:
;----------------------------------------------------------------
AUX$WRST:
	call	AUXSTAT		; get status of AUX in AX
				; now test to see if device is busy
				; if this bit is not set,
	test	AH, THRegEmptyBit
	jz	AUXBUS		;   then device is busy (not ready)
	test	AL, DSetReadyBit; if this bit is not set,
	jz	AUXBUS		;   then device is busy (not ready)
	Jump	Exit


;
; AUXSTAT makes a call on the ROM-BIOS to determine the status
;	  of the auxilary device
;	  Outputs:
;		AX is filled with status of port.
;		DX is changes to specify which card - either 0, 1
;		NO other registers are modified
;
AUXSTAT:
	xor	DX,DX		; have DX point to proper card (COM0)
	mov	AH, 3		; set command to get status
	int	14h		; call rom-bios for status
	ret

;----------------------------------------------------------------
;								:
;  Flush AUX Input buffer - set contents of AUXBUF to zero	:
;								:
;----------------------------------------------------------------
AUX$FLSH:
	call	GETBX		; get BX to point to AUXBUF
	mov	BYTE PTR [BX],0 ; zero out buffer
	Jump	Exit		; all done, successful return



;----------------------------------------------------------------
;								:
;		Write to Auxilary Device			:
;								:
;----------------------------------------------------------------
AUX$WRIT:
	jcxz	EXVEC2		; if CX is zero, no characters
				;   to be written, jump to exit
AUX$LOOP:
	mov	AL,ES:[DI]	; get character to be written
	inc	DI		; move DI pointer to next character
	xor	DX,DX		; have DX point to proper card (COM0)
	mov	AH, 1		; set command to send character
	int	14h		; call rom-bios
	test	AH, 80h		; if bit seven is not set
	jz	AWOK		;   then no error
	mov	AL,10		;   else indicate write fault
	jmp	short ERR$CNT	; call error routines

				; if CX is non-zero, still more
AWOK:	loop	AUX$LOOP	; more characrter to print
	Jump	Exit		; all done, successful return


;
;  GETBX puts the address of AUXBUF (the Auxilary Device buffer)
;	 in BX.  After calling GETBX, a routine can get to AUXBUF
;	 with [BX].
;
GETBX:	mov	BX,OFFSET AUXBUF
	ret

;-----------------------------------------------------
; Exit	Points
;-----------------------------------------------------

RDEXIT: lds	BX, [PTRSAV]		; get pointer to request header
	mov	[BX].IOmedia, AL	  ; move character into req. header
	Jump	EXIT			; all done -- successful return

BUS$EXIT:				; device busy exit
	mov	AH,00000011B		; set error code
	jmp	SHORT ERR1

CMDERR:
	mov	AL,3			; unknown command error

ERR$CNT:lds	BX,[PTRSAV]
	sub	WORD PTR [BX].IOcount,CX  ;# of successful I/O's

ERR$EXIT:
	mov	AH,10000001B		; mark error and return
	jmp	SHORT ERR1

EXIT$ZER:
	lds	BX,[PTRSAV]
	xor	AX,AX
	mov	WORD PTR [BX].IOcount,AX  ; indicate no character read

EXIT:
	mov	AH,00000001B
ERR1:	lds	BX,CS:[PTRSAV]
	mov	WORD PTR [BX].PktStatus,AX ; mark operation complete

	pop	ES
	pop	DS
	popa
	retf

V86CODE ENDS

	END
