.IF		EQ,<VRSNAM-^A/LW/>
	.TITLE	LWDRIVER
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	.TITLE	LVDRIVER
.ENDC		;EQ,<VRSNAM-^A/LW/>
	.IDENT	/REV. G/
	.SUBTITLE	History and Explanations
;
;
;	LANGUAGE:  VAX-11 MACRO
;
;	OPERATING SYSTEM:  VAX/VMS
;
;	ORDER NUMBER:  12X-9XV
;
;	PART NUMBER:  000-025809-XXX REV. G  MAY, 1985
;
;	PRODUCT: VAX-11 DIAGNOSTIC,
;	PRODUCT: VAX/VMS DRIVER, SYMBIONT, AND OUTPUT PACKAGE
;
;	VERSATEC, INC., SANTA CLARA, CALIFORNIA 95051
;	A XEROX COMPANY
;
;	Copyright (C) 1985 by Xerox Corporation.  All rights reserved.
;
;	"NOTICE. THIS PROGRAM IS THE EXCLUSIVE PROPERTY OF VERSATEC,
;	INC. AND IS ISSUED IN STRICT CONFIDENCE UNDER A PREARRANGED
;	LICENSE AGREEMENT AND IS NOT TO BE DISCLOSED IN ANY MANNER TO
;	PERSONS OUTSIDE THE LICENSED ORGANIZATION AND SHALL NOT BE
;	REPRODUCED OR DISSEMINATED, IN WHOLE OR PART, TO ANYONE OUTSIDE
;	THE LICENSED ORGANIZATION WITHOUT THE PRIOR WRITTEN APPROVAL OF
;	VERSATEC, INC. UNLESS OTHERWISE PROVIDED FOR BY SUCH LICENSE
;	AGREEMENT.  THIS WORK IS PROTECTED AS AN UNPUBLISHED WORK UNDER
;	THE COPYRIGHT ACT OF 1976."
;
;
;	VERSATEC PRINTER/PLOTTER DRIVER FOR MODEL 121 OR 122
;	CONTROLLERS UNDER THE VAX/VMS OPERATING SYSTEM OR THE
;	MODEL 126 CONTROLLER UNDER THE MICRO-VMS OPERATING SYSTEM.
;
;
;	D. L. W. 15-OCT-80 - Initial Release
;
; MODIFIED BY:
;	D. L. W. Rev B. 01-OCT-81
;
;		To enable user to spool plots through
;		stardard spooler.
;
;	D. L. W. Rev. C 30-APR-82
;
;		Fixed page fault wait problem if transfering
;		more than 64000 bytes in one transfer.
;		Fixed problem running on VAX systems by changing
;		all the BISB instructions addressing the CSR to
;		BISW instructions.
;
;	D. L. W. Rev. D 22-JUL-82
;
;		Modified the DSBINT macro to only raise the
;		interrupt level to the hardware level rather
;		than level 31.
;
;
;	D. L. W. and P. D. Rev. E 03-Oct-83
;
;		Modified driver to support the 122 controller.
;		Removed code not needed by either the 121 or 122 controllers.
;		Modified device timeout code.
;		Enabled swap-bytes capability.
;		Modified remote functions for the 121 controller.
;
;	D. L. W. and B. L. Rev. F - June, 1984
;
;		Made changes for device timeout.
;		Conditionally assemble 121 and 122 from same file.
;		Restore power-fail checking.
;
;	B. L. Rev. G - May, 1985
;
;		Support MicroVMS operating system with model 126 controller.
;		Support new printer characteristics for V4.
;		Check for DCx characters even if WRITEPBLK or /PASSALL.
;		Add $VADEF definitions.
;		Fix problem handling plot remote functions.
;		Change instructions referencing UCB$W_DEVBUFSIZ and JIB$L_BYTCNT
;		Change device type to DT$_LP11 so DEC symbiont expands tabs.
;		Support IO$M_INHRETRY function modifier.
;
;
	.PAGE
	.SUBTITLE	MACRO LIBRARY CALLS
	$CRBDEF					;DEFINE CRB OFFSETS
	$DCDEF					;DEFINE DEVICE CLASSES	
	$DDBDEF					;DEFINE DDB OFFSETS
	$DEVDEF					;DEFINE DEVICE CHARACTERISTICS
	$DPTDEF					;DEFINE DRIVER PROLOGUE TABLE
	$DYNDEF					;DEFINE STRUCTURE TYPE CODES
	$IDBDEF					;DEFINE IDB OFFSETS
	$IODEF					;DEFINE I/O FUNCTION CODES
	$IRPDEF					;DEFINE IRP OFFSETS
	$JIBDEF					;JOB INFORMATION BLOCK
	$LPDEF					;LINE PRINTER CODES
	$MSGDEF					;DEFINE SYSTEM MESSAGE TYPES
	$PCBDEF					;DEFINE PCB OFFSETS
	$PRDEF					;DEFINE PROCESSOR REGISTERS
	$PTEDEF					;DEFINE PAGE TABLE ENTRY DEFS.
	$SSDEF					;DEFINE SYSTEM STATUS VALUES
	$UCBDEF					;DEFINE UCB OFFSETS
	$VADEF					;DEFINE VIRTUAL ADDR. DEFS
	$VECDEF					;DEFINE CRB SUBSTRUCT. OFFSETS
;
;
; Local symbols
;
;	Offline message delay values
;
	VRS_HRCNT = 1920			;Timeout value for 64 seconds
;
; Argument list offset definitions
;
;
P1=0						;FUNCTION DEPENDENT PARAMETER 1
P2=4						;FUNCTION DEPENDENT PARAMETER 2
P3=8						;FUNCTION DEPENDENT PARAMETER 3
P4=12						;FUNCTION DEPENDENT PARAMETER 4
P5=16						;FUNCTION DEPENDENT PARAMETER 5
P6=20						;FUNCTION DEPENDENT PARAMETER 6
;
;
; Character code definitions
;
 
C_CR=13						;CARRIAGE RETURN
C_FF=12						;FORM FEED
C_VT=11						;VERTICAL TAB
C_LF=10						;LINE FEED
C_TAB=9						;TABULATION
C_DC1=17					;DC1 CHARACTER
C_DC2=18					;DC2 CHARACTER
C_DC3=19					;DC3 CHARACTER
C_DC1_S=^X91					;DC1 SWAP CHARACTER
C_DC2_S=^X92					;DC2 SWAP CHARACTER
;
; Flag register bit definitions
;
;
; The following lines are commented out because the Versatec driver
; should always be set /CR.  All lines which reference these are also
; commented out.
;M_CRPEND=1					;Carriage return pending
;V_CRPEND=0
;
M_TRINPRG=^X100					;Transfer in progress
V_TRINPRG=8
M_ERR_DETECT=^X200				;Error detected
V_ERR_DETECT=9
;
;
; Versatec control and status register bit definitions
;
.IF		EQ,<VRSNAM-^A/LW/>
$DEFINI	LW
$DEF	LW_CSR	.BLKW	1			;CSR
	$VIELD	LW_CSR,0,<-
		<DMAGO,,M>,-			;Start DMA operation
		<,5>,-				;Skip five bits
		<IE,,M>,-			;Interrupt enable
		<RDY,,M>,-			;Ready
		<,1>,-				;Unused bit
		<ENSWP,,M>,-			;Enable byte swap
		<MODCTL,,M>,-			;Print/Plot toggle
		<,2>,-				;Skip 2 bits
		<SPPENA,,M>,-			;SPP enable
		<,1>,-				;Skip 1 bit
		<ERR,,M>,-			;ERROR
		>
$DEFEND	LW
;
;
LW_CSR$M_RESETALL	=	12		;Reset cntrlr/plotter
LW_CSR$M_DMA_ADR	=	^X800		;Set CSR for DMA addr.
LW_CSR$M_BYTCNT		=	^X1000		;Set CSR for byte count
LW_CSR$M_PRINT		=	^X400		;Set CSR for print
;
; Versatec device register offset definitions
;
VRS_CSR		=	LW_CSR
VRS_CSR$M_ENSWP	=	LW_CSR$M_ENSWP
LW_CDR		=	2			;Command/data register
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
$DEFINI	LV
$DEF	LV_CSR	.BLKW	1			;CSR
	$VIELD	LV_CSR,0,<-
		<,1>,-				;Enable SPP
		<RESET,,M>,-			;Remote reset
		<,4>,-				;Skip four bits
		<IE,,M>,-			;Interrupt enable
		<RDY,,M>,-			;Ready
		<,1>,-				;Unused (126 erase after read)
		<ENSWP,,M>,-			;Enable byte swap (126 only)
		<BIT22,,M>,-			;22-bit addressing (126 only)
		<,2>,-				;Unused bits
		<DMABSY,,M>,-			;DMA busy
		<,1>,-				;Skip 1 bit
		<ERR,,M>,-			;Error
		>
$DEFEND	LV
;
; Versatec device register offset definitions
;
VRS_CSR		=	LV_CSR
VRS_CSR$M_ENSWP	=	LV_CSR$M_ENSWP
LV_BCR=-8				;BYTE COUNT REGISTER
LV_DBAR=-2				;DMA BUS ADDRESS REGISTER,
					; ONLY IF OFFSET FROM PLOT
					; (NOT PRINT) CSR
LV_DBARX=-6				;ADDRESS EXTENS., SAME REMARK
.ENDC		;EQ,<VRSNAM-^A/LW/>
;
IO$M_PLOT=^X40					;Plot mode flag
IO$V_PLOT=6					;Plot mode request
IO$M_REMOTE=^X80				;Remote function
IO$V_REMOTE=7					;Remote function
;IO$M_NOFORMAT=^X100				;Pass all, print (DEC defined)
;IO$V_NOFORMAT=8				;Pass all print (DEC defined)
IO$M_SWAPBYTES=^X600				;Swap-bytes
; (Versatec recognizes either ^X200 or ^X400 as the swap-bytes
;  function modifier.  Because either of the bits can be used,
;  there is no IO$V_SWAPBYTES.)
;
M_ERR=^X8000
V_ERR=15
M_RDY=^X80
V_RDY=7
;
; DEFINE DEVICE DEPENDENT UNIT CONTROL BLOCK OFFSETS
;
;
	$DEFINI	UCB
 
.=UCB$K_LENGTH	
 
$DEF	UCB$L_LP_MUTEX	.BLKL	1		;LINE PRINTER/PLOTTER UCB MUTEX
$DEF	UCB$L_LP_TIMOUT	.BLKL	1		;MESSAGE TIMEOUT COUNTER
$DEF	UCB$L_LP_OFLCNT	.BLKL	1		;OFFLINE TIME COUNTER
$DEF	UCB$B_LP_CURSOR	.BLKB	1		;CURRENT HORIZONAL POSITION
$DEF	UCB$B_LP_LINCNT	.BLKB	1		;CURRENT LINE COUNT ON PAGE
$DEF	UCB$W_LP_STATUS	.BLKW	1		;LP STATUS
.IF		NE,<VRSNAM-^A/LW/>
$DEF	UCB$A_LP_MOVRTN	.BLKL	1		;ADDRESS OF MOVE ROUTINE
$DEF	UCB$L_LP_SVAPTE	.BLKL	1		;SAVED SVAPTE OF USERS BUFFER
$DEF	UCB$L_LP_BUFADR	.BLKL	1		;VIRTUAL ADDR. OF USER DATA
$DEF	UCB$A_LP_BUF_VA	.BLKL	1		;VIRTUAL ADDRESS OF BUFFER
$DEF	UCB$A_LP_BUF_PA	.BLKL	1		;PHYSICAL ADDRESS OF BUFFER
$DEF	UCB$W_LP_PBCR	.BLKW	1		;PARTIAL BYTE COUNT REMAINING
$DEF	UCB$W_LP_XFSIZ	.BLKW	1		;SIZE OF THIS TRANSFER (BYTES)
$DEF	UCB$W_LP_FLAGS	.BLKW	1		;LP FLAGS
$DEF	UCB$B_LP_DPPE	.BLKB	1		;COUNT OF PURGE ERRORS
$DEF	UCB$B_SPARE	.BLKB	1		;SPARE UNUSED BYTE
	$VIELD	UCB,0,<-
		<LP_HWSWAP,,M>,-		;UNIT HAS HARDWARE SWAP
		<LP_NEGCNT,,M>,-		;UNIT TAKES 2'S COMPL. COUNT
		<LP_BIT22,,M>,-			;UNIT CAN ADDRESS 22 BITS
		<LP_MAPPING,,M>,-		;UNIT HAS ADAPTER MAPPING
		>
$EQU	UCB$K_LP_BUFSZP		8		;MICROVAX I BUFFER IS 8 PAGES
$EQU	UCB$K_LP_BUFSZB		<512 * UCB$K_LP_BUFSZP>
.ENDC		;NE,<VRSNAM-^A/LW/>
;
UCB$K_SIZE=.					;SAVE SIZE OF UCB
	$DEFEND	UCB
	.PAGE
	.SUBTITLE	DRIVER DATABASE
;+
;	DRIVER DATABASE-
;	
.IF		EQ,<VRSNAM-^A/LW/>
	DPTAB	-
		END=VRS_END,-			;END OF DRIVER
		ADAPTER=UBA,-			;ADAPTER TYPE
		UCBSIZE=UCB$K_SIZE,-		;UCB SIZE
		NAME=LWDRIVER			;DRIVER NAME
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	DPTAB	-
		END=VRS_END,-			;END OF DRIVER
		ADAPTER=UBA,-			;ADAPTER TYPE
		FLAGS=DPT$M_SVP,-		;SPT ENTRY REQUIRED
		UCBSIZE=UCB$K_SIZE,-		;UCB SIZE
		NAME=LVDRIVER			;DRIVER NAME
.ENDC		;EQ,<VRSNAM-^A/LW/>
	DPT_STORE	INIT			;CONTROL BLOCK INIT VALUES
	DPT_STORE	UCB,UCB$B_FIPL,B,8	;FORK IPL
	DPT_STORE	UCB,UCB$L_DEVCHAR,L,-	;DEVICE CHARACTERISTICS
			<DEV$M_REC-		;RECORD ORIENTED
			!DEV$M_AVL-		;AVAILABLE
			!DEV$M_CCL-		;CARRIAGE CONTROL DEVICE
			!DEV$M_ODV>		;OUTPUT DEVICE
	DPT_STORE	UCB,UCB$L_DEVCHAR2,L,-	;DEVICE CHARACTERISTICS
			<DEV$M_NNM>		;PREFIX NAME WITH "NODE$"
	DPT_STORE	UCB,UCB$B_DEVCLASS,B,DC$_LP ;DEVICE CLASS=LINE PRINTER
	DPT_STORE	UCB,UCB$B_DEVTYPE,B,DT$_LP11 ;CONTROLLER TYPE=LP11
	DPT_STORE	UCB,UCB$W_DEVBUFSIZ,W,132 ;DEFAULT BUFFER SIZE
	DPT_STORE	UCB,UCB$L_DEVDEPEND,L,-	;PRINTER PARAMETERS
			<66@24+LP$M_MECHFORM+LP$M_TRUNCATE+LP$M_LOWER+LP$M_CR>
	DPT_STORE	UCB,UCB$B_DIPL,B,20	;DEVICE IPL
	DPT_STORE	UCB,UCB$L_LP_MUTEX,W,-1	;INITIALIZE MUTEX
	DPT_STORE	REINIT			;CONTROL BLOCK RE-INIT
	DPT_STORE	CRB,CRB$L_INTD+4,D,-
			VRS$INT			;INTERRUPT SERVICE ADDRESS
.IF		NE,<VRSNAM-^A/LW/>
	DPT_STORE	CRB,CRB$L_INTD2+4,D,-
			VRS$INT			;SECOND VECTOR
.ENDC		;NE,<VRSNAM-^A/LW/>
	DPT_STORE	CRB,CRB$L_INTD+VEC$L_INITIAL,D,VRS_CINIT	
	DPT_STORE	CRB,CRB$L_INTD+VEC$L_UNITINIT,D,VRS_INIT	
	DPT_STORE	DDB,DDB$L_DDT,D,VRS$DDT	;DDT ADDRESS
	DPT_STORE	END					
;
; DRIVER DISPATCH TABLE
;
	DDTAB	VRS,-				;DRIVER DISPATCH TABLE
		STARTIO,-			;START I/O OPERATION
		0,-				;UNSOLICITED INTERRUPT
		FUNCTABLE,-			;FUNCTION TABLE
		VRS_CANCELIO,-			;CANCEL I/O ROUTINE
		0,-				;REGISTER DUMP ROUTINE
		0,-				;SIZE OF DIAGNOSTIC BUFFER
		0				;SIZE OF ERROR LOG BUFFER
	.PAGE
	.SUBTITLE	VERSATEC FUNCTION DECISION TABLE
;
; VERSATEC FUNCTION DECISION TABLE
;
FUNCTABLE:					;FUNCTION DECISION TABLE
	FUNCTAB	,-	;TABLE OF LEGAL FUNCTIONS
		<SENSECHAR,-			;SENSE CHARACTERISTICS
		SETCHAR,-			;SET CHARACTERISTICS
		SENSEMODE,-			;SENSE MODE
		SETMODE,-			;SET MODE
		WRITELBLK,-			;WRITE LOGICAL BLOCK
		WRITEPBLK,-			;WRITE PHYSICAL BLOCK
		WRITEVBLK>			;WRITE VIRTUAL BLOCK
;
			;TABLE OF BUFFERED FUNCTIONS
			;NOTE THAT IN SOME CASES THESE FUNCTIONS ARE
			;EXECUTED DIRECTLY---THE INCLUSION OF THESE
			;FUNCTIONS IN THIS TABLES AFFECTS QUOTAS ONLY
	FUNCTAB	,-				;BUFFERED FUNCTIONS
		<SENSECHAR,-			;SENSE CHARACTERISTICS
		SETCHAR,-			;SET CHARACTERISTICS
		SENSEMODE,-			;SENSE MODE
		SETMODE,-			;SET MODE
		WRITELBLK,-			;WRITE LOGICAL BLOCK
		WRITEPBLK,-			;WRITE PHYSICAL BLOCK
		WRITEVBLK>			;WRITE VIRTUAL BLOCK
	FUNCTAB	VRS_WRITE,-		;WRITE FUNCTIONS
		<WRITELBLK,-			;WRITE LOGICAL BLOCK
		WRITEPBLK,-			;WRITE PHYSICAL BLOCK
		WRITEVBLK>			;WRITE VIRTUAL BLOCK
	FUNCTAB	VRS_SETMODE,-		;SET FUNCTIONS
		<SETCHAR,-			;SET CHARACTERISTICS
		SETMODE>			;SET MODE
	FUNCTAB	+EXE$SENSEMODE,-	;SENSE FUNCTIONS
		<SENSEMODE,-			;SENSE MODE
		SENSECHAR>			;SENSE CHARACTERISTICS
;
.IF		NE,<VRSNAM-^A/LW/>
;
; LVDRIVER REMOTE FUNCTIONS TABLE
;
REMTABL:	.ADDRESS	REMCODE
REMCODE:	.BYTE		0
		.BYTE		^X20		;Remote line term.
		.BYTE		4		;Remote buffer clear
		.BYTE		2		;Remote reset
		.BYTE		^X10		;Remote form feed
		.BYTE		8		;Remote EOT
		.BYTE		0
		.BYTE		0
.ENDC		;NE,<VRSNAM-^A/LW/>
;
	.PAGE
	.SUBTITLE	SET CHARACTERISTICS AND SET MODE FUNCTION PROCESSING
;+
; VRS_SETMODE - SET CHARACTERISTICS AND SET MODE FUNCTION PROCESSING
;
; THIS ROUTINE IS CALLED FROM THE FUNCTION DECISION TABLE DISPATCHER
; TO PROCESS A SET MODE FUNCTION TO A LINE PRINTER/PLOTTER.
;
; INPUTS:
;
;	R0 = SCRATCH.
;	R1 = SCRATCH.
;	R2 = SCRATCH.
;	R3 = ADDRESS OF I/O REQUEST PACKET.
;	R4 = CURRENT PROCESS PCB ADDRESS.
;	R5 = ASSIGNED DEVICE UCB ADDRESS.
;	R6 = ADDRESS OF CCB.
;	R7 = BIT NUMBER OF I/O FUNCTION CODE.
;	R8 = FUNCTION DECISION TABLE DISPATCH ADDRESS.
;	R9 = SCRATCH.
;	R10 = SCRATCH.
;	R11 = SCRATCH.
;	AP = ADDRESS OF FIRST FUNCTION DEPENDENT PARAMETER.
;		See the VMS I/O User's Guide, Line Printer Driver section,
;		for a description of the SETMODE parameters.
;
; OUTPUTS:
;
;	THE SPECIFIED CHARACTERISTICS ARE MOVED INTO THE DEVICE UCB AND THE
;	I/O IS COMPLETED.
;-
 
VRS_SETMODE:					;SET MODE FUNCTION PROCESSING
	MOVL	P1(AP),R1			;GET ADDRESS OF CHARACTERISTICS
	IFNORD	#8,(R1),20$			;CAN CHARACTERISTICS BE READ?
	BBC	#LP$V_CR,4(R1),30$		;DID USER CLEAR CR SETTING?
	PUSHL	R3				;SAVE PACKET ADDRESS
	MOVAB	UCB$L_LP_MUTEX(R5),R0		;GET ADDRESS OF UCB MUTEX
	JSB	G^SCH$LOCKW			;LOCK UCB FOR WRITE ACCESS
	CMPL	#IO$_SETMODE,R7			;SET MODE FUNCTION?
	BEQL	10$				;IF EQL YES
	MOVW	(R1),UCB$B_DEVCLASS(R5)		;SET DEVICE CLASS AND TYPE
10$:	MOVW	2(R1),UCB$W_DEVBUFSIZ(R5) 	;SET DEFAULT BUFFER SIZE
	MOVL	4(R1),UCB$L_DEVDEPEND(R5) 	;SET DEVICE CHARACTERISTICS
	JSB	G^SCH$UNLOCK			;UNLOCK UCB
	POPL	R3				;RESTORE PACKET
	MOVZWL	#SS$_NORMAL,R0			;SET NORMAL COMPLETION STATUS
	JMP	G^EXE$FINISHIOC	
20$:	MOVZWL	#SS$_ACCVIO,R0			;SET ACCESS VIOLATION STATUS
	BRB	40$
30$:	MOVZWL	#SS$_BADPARAM,R0		;SET BAD PARAMETER STATUS
40$:	JMP	G^EXE$ABORTIO	
	.PAGE
	.SUBTITLE	WRITE FUNCTION PROCESSING
;+
; VRS_WRITE - WRITE FUNCTION PROCESSING
;
; THIS ROUTINE IS CALLED FROM THE FUNCTION DECISION TABLE DISPATCHER
; TO PROCESS WRITE FUNCTION TO A LINE PRINTER/PLOTTER
;
; INPUTS:
;
;	R0 = SCRATCH.
;	R1 = SCRATCH.
;	R2 = SCRATCH.
;	R3 = ADDRESS OF I/O REQUEST PACKET.
;	R4 = CURRENT PROCESS PCB ADDRESS.
;	R5 = ASSIGNED DEVICE UCB ADDRESS.
;	R6 = ADDRESS OF CCB.
;	R7 = BIT NUMBER OF I/O FUNCTION CODE.
;	R8 - USE AS SCRATCH
;	R9 = SCRATCH.
;	R10 = SCRATCH.
;	R11 = SCRATCH.
;	AP = ADDRESS OF FIRST FUNCTION DEPENDENT PARAMETER.
;		P1 = BUFFER ADDRESS
;		P2 = BUFFER LENGTH
;		P3 = REMOTE FUNCTION IF NEEDED
;		P4 = CARRIAGE CONTROL INFORMATION
;			See the VMS I/O User's Guide for description
;
; OUTPUTS:
;
;	DATA TRANSFER FUNCTIONS:
;
;
; PRINT:
;	THE FUNCTION PARAMETERS ARE CHECKED AND THE USER'S BUFFER IS FORMATTED
;	AND COPIED INTO A SYSTEM BUFFER FOR PROCESSING BY THE LINE PRINTER/
;	PLOTTER DRIVER.
; PLOT:
;	THE FUNCTION PARAMETERS ARE CHECKED AND THE USER'S BUFFER IS COPIED
;	INTO A SYSTEM BUFFER.
;
;	REMOTE FUNCTION:
;	THE REMOTE FUNCTION CODE IS OBTAINED FROM P3(AP) AND PUT INTO
;	IRP$L_MEDIA (1 BYTE).
;-
VRS_WRITE:					;BUFFERED WRITE FNC. PROCESS
;	JSB	G^INI$BRK			;FOR USE WITH XDELTA
	CLRL	R11				;CLEAR NUMBER OF OVERHEAD BYTES
	CLRL	R10				;ASSUME WRITE PASS ALL FUNCTION
FORMAT:	MOVL	FP,SP				;REMOVE ALL TEMPS FROM STACK
	PUSHR	#^M<R3,R4,R5,R6,R7,AP>		;SAVE REGISTERS
	MOVL	P1(AP),R8			;GET START ADDR OF USER BUFFER
	MOVZWL	P2(AP),R9			;GET LENGTH OF USER BUFFER
	MOVW	IRP$W_FUNC(R3),R0		;GET FUNCTION CODE AND SUBFUNC.
	BBC	#IO$V_REMOTE,R0,501$		;BR IF NOT REMOTE
	BICB3	#^XC0,P3(AP),R11		;CLEAR ALL BUT REM. FUN.
	BRW	REMOTE_INIT			;GO STORE REMOTE CODE
501$:	BBS	#IO$V_NOFORMAT,R0,502$		;BRANCH IF PASSALL REQUEST
	BBC	#IO$V_PLOT,R0,504$		;BR IF NOT PLOT (PLOT=PASSALL)
502$:	BRW	DIR_IO1				;GO DO DIRECT I/O
504$:	BBC	#LP$V_PASSALL,UCB$L_DEVDEPEND(R5),5$ ;IF CLR, NOT PASSALL
	MOVL	#IO$_WRITEPBLK,R7		;FORCE WRITE PHYSICAL
5$:	CMPL	#IO$_WRITEPBLK,R7		;WRITE PHYSICAL BLOCK?
	BEQL	10$				;IF EQL YES SO BRANCH
	MOVL	P4(AP),IRP$B_CARCON(R3)		;INSERT CARRIAGE CONTROL INFO
	JSB	G^EXE$CARRIAGE			;TRANSLATE CARRIAGE CONTROL
	MOVZBL	IRP$B_CARCON(R3),R0		;GET NO. PREFIX CONTROL BYTES
	MOVZBL	IRP$B_CARCON+2(R3),R1		;GET NO. SUFFIX CONTROL BYTES
	ADDL	R0,R1				;# OF CARRIAGE CONTROL BYTES
	MOVAB	32(R1)[R11],R10			;CALCULATE TOTAL OVERHEAD BYTES
10$:	TSTL	R9				;ANY BUFFER SPECIFIED?
	BEQL	20$				;IF EQL NO
	MOVQ	R8,R0				;RETRIEVE BUFFER PARAMETERS
	JSB	G^EXE$WRITECHK			;CHECK ACCESS OF USER BUFFER
511$:	CMPL	R9,#2				;COUNT >= 2?
	BLSSU	20$				;NO, SKIP CHECK FOR SPOOL CODES
	CMPB	#C_DC1,(R8)			;PRINT PASS_ALL?
	BNEQ	513$				;NO
	BISW	#IO$M_NOFORMAT,IRP$W_FUNC(R3)	;INDICATE PASSALL
	BRB	517$				;GO ADJUST ADDRESS AND COUNT
513$:	CMPB	#C_DC2,(R8)			;PLOT RASTER SPOOLED?
	BNEQ	515$				;IF NO SKIP
	BISW	#<IO$M_PLOT!IO$M_NOFORMAT>,-
		IRP$W_FUNC(R3)			;INDICATE PLOT OUTPUT
	BRB	517$				;GO ADJUST ADDRESS AND COUNT
515$:	CMPB	#C_DC1_S,(R8)			;PRINT, PASS_ALL, SWAP?
	BNEQ	516$				;NO, BRANCH
	BISW	#<IO$M_NOFORMAT!IO$M_SWAPBYTES>,IRP$W_FUNC(R3) ;PASSALL, SWAP
	BRB	517$				;GO ADJUST ADDRESS AND COUNT
516$:	CMPB	#C_DC2_S,(R8)			;PLOT, PASS_ALL, SWAP?
	BNEQ	519$				;NO, BRANCH
	BISW	#<IO$M_PLOT!IO$M_NOFORMAT!IO$M_SWAPBYTES>,-
		IRP$W_FUNC(R3)			;INDICATE PLOT OUTPUT, SWAP
517$:	ADDL2	#2,R8				;INCREMENT ADDRESS (SKIP DCX,X)
	SUBL2	#2,R9				;DECREMENT BYTE COUNT
518$:	BRW	DIR_IO1				;GO DO DIRECT I/O
519$:	CMPB	#C_DC3,(R8)			;REMOTE FUNCTION?
	BNEQ	520$				;IF NO,SKIP
	BISW	#<IO$M_REMOTE!IO$M_PLOT>,-
		IRP$W_FUNC(R3)			;SET REMOTE PLOT FUNCT.
	BICB3	#^XF8,1(R8),R11			;CLEAR ALL BUT REMOTE FUNCT.
	BRW	REMOTE_INIT
520$:	CMPL	#IO$_WRITEPBLK,R7		;WRITE PHYSICAL BLOCK?
	BEQL	518$				;IF EQL YES SO BRANCH
20$:	MOVAB	12(R9)[R10],R1			;FIND BUFFER LENGTH REQUIRED
	JSB	G^EXE$BUFFRQUOTA		;CHECK IF ENOUGH QUOTA
	BLBS	R0,25$				;IF LBS QUOTA CHECK OK
	BRW	45$				;ABORT OPERATION
25$:	JSB	G^EXE$ALLOCBUF			;ALLOCATE BUFFER FOR OUTPUT
	BLBC	R0,45$				;IF LBC ALLOCATION FAILURE
	MOVL	(SP),R3				;RETRIEVE ADDRESS OF I/O PACKET
	MOVL	R2,IRP$L_SVAPTE(R3)		;ADDR OF BUFFERED I/O PACKET
	MOVL	PCB$L_JIB(R4),R0		;JIB ADDRESS
	SUBL	R1,JIB$L_BYTCNT(R0)		;ADJUST COUNT
	MOVW	R1,IRP$W_BOFF(R3)		;NO. BYTES CHARGED TO QUOTA
	CLRL	IRP$L_MEDIA(R3)			;CLEAR LF COUNT IN PACKET
	MOVW	R9,IRP$W_BCNT(R3)		;INSERT SIZE OF USER BUFFER
	MOVAB	12(R2),(R2)			;SET UP SYSTEM BUFFER HEADER
	MOVL	R8,4(R2)			;INSERT ADDRESS OF USER DATA
	MOVAB	12(R2),R2			;GET ADDR OF BUFFER DATA AREA
	MOVAB	UCB$L_LP_MUTEX(R5),R0		;GET ADDR OF UCB MUTEX
	JSB	G^SCH$LOCKW			;LOCK UCB FOR WRITE ACCESS
						;VERSATEC DOES PHYS.IO DIFFRENT
;	CMPL	#IO$_WRITEPBLK,R7		;WRITE PASSALL?
;	BEQL	50$				;IF EQL YES
	SUBW	#12,R1				;ACTUAL LENGTH OF DATA AREA
	MOVZBL	UCB$B_LP_CURSOR(R5),R4		;HORIZ. CARRIAGE POSITION
;	MOVZWL	UCB$W_LP_DEVSTS(R5),R6		;GET CR PENDING FLAG
	MOVZBL	UCB$B_LP_LINCNT(R5),R7		;GET CURRENT LINE ON PAGE
	MOVZWL	UCB$W_DEVBUFSIZ(R5),R10		;WIDTH OF PRINTER CARRIAGE
	MOVL	#^X20,AP			;ASSUME NO LOWER CASE
	BBC	#LP$V_LOWER,-
		UCB$L_DEVDEPEND(R5),35$		;IF CLR, NO LOWER CASE
	CLRL	AP				;PRINTER WITH LOWER CASE
35$:	BSBB	70$				;INSERT PREFIX CARRIAGE CONTROL
30$:	DECL	R9				;ANY MORE BYTES ?
	BLSS	40$				;IF LSS NO
	MOVZBL	(R8)+,R0			;GET NEXT BYTE FROM USER BUFFER
	BSBB	WRITE_BYTE			;WRITE BYTE IN SYSTEM BUFFER
	BRB	30$	
40$:	BSBB	80$				;INSERT SUFFIX CARRIAGE CONTROL
	SUBL	IRP$L_SVAPTE(R3),R2		;LENGTH OF OUTPUT PLUS HEADER
;	SUBW3	#12,R2,IRP$L_MEDIA+2(R3)	;ACTUAL LENGTH OF OUTPUT BUFFER
	SUBW3	#12,R2,IRP$W_BCNT(R3)		;VERSATEC PUTS IT IN IRP$BCNT
	MOVB	R4,UCB$B_LP_CURSOR(R5)		;SAVE HORIZ. CARRIAGE POSITION
;	INSV	R6,#V_CRPEND,#1,UCB$W_DEVSTS(R5) ;SAVE CR PENDING
	MOVB	R7,UCB$B_LP_LINCNT(R5)		;SAVE CURRENT LINE ON PAGE
	BRB	60$
45$:	POPR	#^M<R3,R4,R5,R6,R7,AP>		;RESTORE REGISTERS
	JMP	G^EXE$ABORTIO			;ABORT CALL
;50$:	MOVW	R9,IRP$L_MEDIA+2(R3)		;NO. OF BYTES TO PRINT
;	MOVC	R9,(R8),(R2)			;MOVE CHARS TO SYSTEM BUFFER
60$:	POPR	#^M<R3,R4,R5,R6,R7,AP>		;RESTORE REGISTERS
	PUSHL	R3				;SAVE ADDRESS OF I/O PACKET
	MOVAB	UCB$L_LP_MUTEX(R5),R0		;GET ADDRESS OF UCB MUTEX
	JSB	G^SCH$UNLOCK			;UNLOCK UCB
	POPL	R3				;RESTORE ADDRESS OF I/O PACKET
	JMP	G^EXE$QIODRVPKT			;QUEUE I/O PACKET TO DRIVER
;
; SUBROUTINE TO INSERT CARRIAGE CONTROL IN BUFFER
;
70$:	MOVZBL	IRP$B_CARCON(R3),-(SP)		;NUMBER OF CHARACTERS TO OUTPUT
	BEQL	100$				;IF EQL NONE
	MOVZBL	IRP$B_CARCON+1(R3),R0		;GET CHARACTER TO OUTPUT
	BRB	85$
80$:	MOVZBL	IRP$B_CARCON+2(R3),-(SP) 	;NUMBER OF CHARACTERS TO OUTPUT
	BEQL	100$				;IF EQL NONE
	MOVZBL	IRP$B_CARCON+3(R3),R0		;GET CHARACTER TO OUTPUT
85$:	BNEQ	90$				;IF NEQ CHARACTER SPECIFIED
	MOVZBL	#C_CR,R0			;GET CARRIAGE RETURN
	BSBB	WRITE_BYTE			;WRITE BYTE IN SYSTEM BUFFER
	MOVZBL	#C_LF,R0			;GET LINE FEED
90$:	BSBB	WRITE_BYTE			;WRITE BYTE IN SYSTEM BUFFER
	SOBGTR	(SP),90$			;ANY MORE LEFT TO INSERT?
100$:	TSTL	(SP)+				;REMOVE COUNT FROM STACK
	RSB	
	.PAGE
	.SUBTITLE	WRITE BYTE INTO SYSTEM BUFFER
;
; SUBROUTINE TO FORMAT AND FILL SYSTEM BUFFER WITH LINE PRINTER OUTPUT
; ONE BYTE AT A TIME.
;
WRITE_BYTE:					;WRITE BYTE INTO BUFFER
	BBS	R0,CONTROL_TAB,40$		;BR IF CONTROL CHARACTER
;	BBSC	#V_CRPEND,R6,60$		;IF SET, CR PENDING
5$:	BBC	R0,LOWERCASE_TAB,10$		;BR IF NOT LOWER CASE
	SUBL	AP,R0				;CONVERT CHAR. TO UPPER CASE
10$:
	CMPL	R4,R10				;STILL ROOM ON CURRENT LINE?
	BLSSU	15$				;IF LSS, YES
	BBC	#LP$V_TRUNCATE,UCB$L_DEVDEPEND(R5),15$ ;BR IF NOTRUNCATE
	BBC	#LP$V_WRAP,UCB$L_DEVDEPEND(R5),30$ ;BR IF NOWRAP
;
11$:	PUSHL	R0				;SAVE THE CURRENT CHAR.
	MOVZBL	#C_CR,R0			;GET CARRIAGE RETURN CODE
	BSBB	WRITE_BYTE			;INSERT CODE IN SYSTEM BUFFER
	MOVZBL	#C_LF,R0			;GET LINE FEED CODE
	BSBW	110$				;INSERT LF IN SYSTEM BUFFER
	POPL	R0				;RESTORE CURRENT CHAR.
;
15$:	INCL	R4				;INCREMENT HORIZONAL POSITION
20$:	DECL	R1				;ANY ROOM LEFT IN SYSTEM BUFFER?
	BLSS	37$				;BR IF NOT
25$:	BBS	#LP$V_FALLBACK,UCB$L_DEVDEPEND(R5),35$ ;BR IF FALLBACK
	MOVB	R0,(R2)+			;INSERT CHARACTER IN BUFFER
30$:	RSB	
;
35$:	MOVB	@TRANS_TAB[R0],(R2)+		;PUT TRANSLATED CHAR IN BUFFER
	RSB
;
37$:	BRW	150$				;SYSTEM BUFFER FULL
;
; Control character encountered
;
40$:	CMPB	R0,#^X7F			;DELETE CHARACTER?
	BNEQ	45$				;BR IF NOT
;	BBS	#LP$V_PRINTALL,UCB$L_DEVDEPEND(R5),10$ ;IF SET, ALLOW DEL CHAR
	BRB	10$				;VERSATEC ALWAYS ALLOWS DELETES
45$:;	BGEQU	30$				;BR IF MULTINATIONAL UNPRINTABLE
	BGEQU	50$				;DEC IGNORES BUT WE MIGHT NOT
	CMPL	#C_CR,R0			;CARRIAGE RETURN?
	BLSSU	50$				;IF LSS NO
	BGTRU	70$				;IF GTRU NO
	BRW	140$				;ALWAYS PRINT CR ON VERSATEC
;	BBS	#LP$V_CR,UCB$L_DEVDEPEND(R5),140$ ;IF SET, CR REQUIRED
;	BISL	#M_CRPEND,R6			;SET CR PENDING
;	RSB
50$:;	BBSC	#V_CRPEND,R6,60$		;IF SET, CR PENDING
	BBS	#LP$V_PRINTALL,UCB$L_DEVDEPEND(R5),20$ ;IF SET, PRINT CHAR.
	BRW	30$				;EXIT, THIS IS NON-PRINTABLE
60$:	PUSHL	R0				;SAVE CURRENT CHARACTER
	MOVZBL	#C_CR,R0			;GET CR CHARACTER
	BSBB	140$				;INSERT CR IN BUFFER
	POPL	R0				;RETRIEVE CURRENT CHAR.
	BRW	WRITE_BYTE			;
;
; Character is a tab, line feed, vertical tab, or form feed
;
 
70$:	CMPL	#C_TAB,R0			;TABULATION CHARACTER?
	BGTRU	50$				;IF GTRU NO
	BLSSU	80$				;IF LSSU NO
;
; Character is a tab
;
;	BBSC	#V_CRPEND,R6,60$		;IF SET, CR PENDING
	BBS	#LP$V_TAB,UCB$L_DEVDEPEND(R5),20$ ;BR IF NOT EXPANDING TABS
	PUSHAB	8(R4)				;CALCULATE NEXT TAB POSITION
	BICL	#7,(SP)				;CLEAR EXCESS BITS
	SUBL	R4,(SP)				;CALCULATE BLANK COUNT
	MOVZBL	#^A/ /,R0			;SET SPACE CHARACTER
	BRB	100$
;
; Character is a line feed, vertical tab, or form feed
;
80$:
	CMPL	#C_VT,R0			;VERTICLE TAB?
	BEQL	50$				;IF EQL YES
	BGTRU	110$				;IF GTRU LINE FEED
;
; Character is a form feed
;
	MOVZBL	UCB$L_DEVDEPEND+3(R5),R0 	;GET NUMBER OF LINES PER PAGE
	SUBL3	R7,R0,-(SP)			;NUMBER OF LINES TO END OF PAGE
	BBC	#LP$V_MECHFORM,-
		UCB$L_DEVDEPEND(R5),90$		;IF CLR, NO MECHANICAL FEED
	ADDL	(SP)+,IRP$L_MEDIA(R3)		;UPDATE NUMBER OF LINES PRINTED
	MOVZBL	#C_FF,R0			;SET FORM FEED CHARACTER
	BRB	120$
90$:	MOVZBL	#C_LF,R0			;SET LINE FEED CHARACTER
100$:	BSBW	WRITE_BYTE			;INSERT BYTE IN SYSTEM BUFFER
	SOBGTR	(SP),100$			;ANY MORE BYTES TO INSERT?
	TSTL	(SP)+				;REMOVE LOOP COUNT FROM STACK
	RSB	
;
; CHARACTER IS A LINE FEED
;
110$:	INCL	R7				;INCREMENT LINE POSTN ON PAGE
	INCL	IRP$L_MEDIA(R3)			;INCREMENT NO. LINES PRINTED
	CMPB	R7,UCB$L_DEVDEPEND+3(R5) 	;END OF PAGE?
	BNEQ	130$				;IF NEQ NO
120$:	CLRL	R7				;CLEAR LINE POSITION ON PAGE
130$:;	BICL	#M_CRPEND,R6			;CLEAR CARRIAGE RETURN PENDING
140$:	CLRL	R4				;CLEAR HORIZONAL POSITION
	BRW	20$
;
; OUTPUT WILL NOT FIT IN ALLOCATED BUFFER
;
150$:	MOVL	IRP$L_SVAPTE(R3),R0		;ADDR. OF BUFF TO DEALLOCATE
	CLRL	IRP$L_SVAPTE(R3)		;INDICATE NO BUFFER ALLOCATED
	MOVZWL	IRP$W_SIZE(R0),R10		;SAVE SIZE OF BUFFER
	JSB	G^EXE$DEANONPAGED		;DEALLOCATE BUFFER
	MOVAB	-4*6(FP),SP			;REMOVE ALL TEMPS FROM STACK
	POPR	#^M<R3,R4,R5,R6,R7,AP>		;RESTORE REGISTERS
	MOVL	PCB$L_JIB(R4),R0		;JIB ADDRESS
	ADDL	R10,JIB$L_BYTCNT(R0)		;ADJUST BYTE COUNT
	CLRW	IRP$W_BOFF(R3)			;NO. BYTES CHARGED TO QUOTA
	ADDL	#32,R11				;ADJUST COUNT OF OVERHEAD BYTES
	PUSHL	R3				;SAVE ADDRESS OF I/O PACKET
	MOVAB	UCB$L_LP_MUTEX(R5),R0		;GET ADDRESS OF UCB MUTEX
	JSB	G^SCH$UNLOCK			;UNLOCK UCB
	POPL	R3				;RESTORE ADDRESS OF I/O PACKET
	BRW	FORMAT				;TRY AGAIN
	.PAGE
	.SUBTITLE	INITIALIZATION FOR REMOTE AND DIRECT I/O
REMOTE_INIT:
.IF		EQ,<VRSNAM-^A/LW/>
	BBC	#3,R11,80$			;BRANCH IF NOT OLD EOT
	MOVZBL	#5,R11				;STORE NEW EOT CODE
	BRB	100$
80$:	BBC	#4,R11,90$			;BRANCH IF NOT OLD FF
	MOVZBL	#4,R11				;STORE NEW FF CODE
	BRB	100$
90$:	BBC	#5,R11,100$			;BRANCH IF NOT OLD RLTER
	MOVZBL	#1,R11				;STORE NEW RLTER CODE
100$:	ASHL	#1,R11,R11			;SHIFT INTO CORRECT BITS
	MOVB	R11,IRP$L_MEDIA(R3)		;STORE REMOTE FUNCTION
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BITB	#^X38,R11			;RLTER, FF, OR EOT?
	BEQL	100$				;BRANCH IF NOT
	MOVB	R11,IRP$L_MEDIA(R3)		;STORE REMOTE FUNCTION
	BRB	105$				;NO NEED FOR TABLE
100$:	MOVB	@REMTABL[R11],IRP$L_MEDIA(R3)	;STORE REMOTE FUNCT.
105$:	BRB	DIR_IO2
.IF_TRUE_FALSE	;EQ,<VRSNAM-^A/LW/>
DIR_IO1:
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BITW	#IO$M_SWAPBYTES,IRP$W_FUNC(R3)	;SWAP BYTES?
	BEQL	DIR_IO2				;NO,BRANCH
	BBS	#UCB$V_LP_HWSWAP,UCB$W_LP_FLAGS(R5),DIR_IO2 ;BR IF HARDWARE SWAP
	MOVQ	R8,R0				;GET BUFFER ADDR + BYTE COUNT
	TSTL	R1				;SEE IF BYTE COUNT ZERO
	BEQL	DIR_IO2				;SKIP IF ZERO BYTES
	JSB	G^EXE$READCHK			;CHECK WRITE ACCESS TO BUFFER
	BICW	#IRP$M_FUNC,IRP$W_STS(R3)	;CLEAR READ FUNCTION INDICATOR
	INCL	R1				;IN CASE BYTE COUNT ODD
	DIVL	#2,R1				;CONVERT BYTES TO WORDS
200$:	MOVB	(R0),R2				;GET 1ST BYTE IN WORD
	MOVB	1(R0),(R0)+			;STORE 1ST BYTE IN WORD
	MOVB	R2,(R0)+			;STORE 2ND BYTE
	SOBGTR	R1,200$				;SWAP ENTIRE BUFFER
.ENDC		;EQ,<VRSNAM-^A/LW/>
DIR_IO2:
	DECW	PCB$W_DIOCNT(R4)		;ONE LESS DIRECT I/O
	BLSS	235$				;IF NEGATIVE, QUOTA EXCEEDED
220$:	INCW	PCB$W_BIOCNT(R4)		;ONE ADDITIONAL BUFFERRED I/O
	POPR	#^M<R3,R4,R5,R6,R7,AP>		;RESTORE REGISTERS
	BICW	#IRP$M_BUFIO,IRP$W_STS(R3)	;INDICATE DIRECT I/O
	BBS	#IO$V_REMOTE,IRP$W_FUNC(R3),230$ ;REMOTE FUNCTION
	MOVQ	R8,R0				;BUFFER ADDRESS + BYTE COUNT
	JSB	G^EXE$WRITELOCK			;LOCK PAGES
230$:	JMP	G^EXE$QIODRVPKT			;QUEUE I/O PACKET
235$:	INCW	PCB$W_DIOCNT(R4)		;RESET DIRECT I/O
	MOVAW	PCB$W_DIOCNT(R4),R2		;RESOURCE TO CHECK
	JSB	G^EXE$SNGLEQUOTA		;QUOTA EXHAUSTED?
	BLBC	R0,240$				;IF CLEAR YES SO ABORT
	DECW	PCB$W_DIOCNT(R4)		;CHARGE ONE DIRECT I/O
	BRB	220$				;RESUME PROCESSING
240$:	POPR	#^M<R3,R4,R5,R6,R7,AP>		;RESTORE REGISTERS
	JMP	G^EXE$ABORTIO			;ABORT
	.PAGE
	.SUBTITLE	VERSATEC PRINTER/PLOTTER DRIVER (DMA)
;+
; STARTIO - START I/O OPERATION WHEN THE UNIT IS IDLE AND A PACKET
; IS AVAILABLE.
;
; INPUTS:
;
;	R3 = ADDRESS OF I/O REQUEST PACKET (IRP)
;		INCLUDING FUNCTION MODIFIER
;		OPERATION BYTE-COUNT IN IRP$W_BCNT
;		IF REMOTE FUNCTION, FUNC. CODE IN LOW BYTE OF IRP$L_MEDIA
;			IRP$L_SVAPTE IS CLEAR
;		IF NON-BUFFERED:
;			PTE FOR BUFFER IN IRP$L_SVAPTE
;			BYTE OFFSET IN IRP$W_BOFF
;		IF BUFFERED: (IRP$V_BUFIO SET IN IRP$W_STS)
;			IRP$L_SVAPTE IS SVA OF START OF BUFFER -
;				DATA STARTS AT BUFFER+12
;	R5 = UCB ADDRESS FOR UNIT
;
; OUTPUTS:
;	NO EXPLICIT OUTPUTS - HOWEVER, THE CRB IS MODIFIED TO
;	REFLECT THE CSR ETC. RELEVANT TO THE OPERATION (PLOT/PRINT).
;
	.PAGE
	.SUBTITLE	STARTIO routine
STARTIO:
;	JSB	G^INI$BRK			;FOR USE WITH XDELTA
	MOVL	UCB$L_CRB(R5),R2		;GET CRB ADDRESS INTO R2
.IF		EQ,<VRSNAM-^A/LW/>
	MOVL	@CRB$L_INTD+VEC$L_IDB(R2),R4	;PUT CSR IN R4
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BICL3	#4,@CRB$L_INTD+VEC$L_IDB(R2),R4	;PUT PLOTTER CSR IN R4
.IF_TRUE_FALSE	;EQ,<VRSNAM-^A/LW/>
	BBS	#IO$V_PLOT,IRP$W_FUNC(R3),5$	;BRANCH IF PLOT
.IF_TRUE	;EQ,<VRSNAM-^A/LW/>
	BICW	#LW_CSR$M_MODCTL,LW_CSR(R4)	;SET PRINT MODE
	BRB	10$				;
5$:	BISW	#LW_CSR$M_MODCTL,LW_CSR(R4)	;SET PLOT MODE
10$:
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BISB	#4,R4				;FORCE PRINT
5$:	MOVL	R4,@CRB$L_INTD+VEC$L_IDB(R2)	;SAVE CSR ACROSS WFIKPCH
.ENDC		;EQ,<VRSNAM-^A/LW/>
	BICW	#M_TRINPRG,UCB$W_DEVSTS(R5)	;CLEAR TRANSFER IN PROGRESS
	BITW	#M_ERR!M_RDY,VRS_CSR(R4)	;IS PLOTTER READY?
	BGTR	MOD_SET				;BRANCH IF READY
	DSBINT	UCB$B_DIPL(R5)			;RAISE PRIORITY TO DEVICE IPL
.IF		EQ,<VRSNAM-^A/LW/>
	MOVB	#LW_CSR$M_IE,LW_CSR(R4)		;SET INTERRUPT ENABLE
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BISW	#LV_CSR$M_IE,LV_CSR(R4)		;SET INTERRUPT ENABLE
.ENDC		;EQ,<VRSNAM-^A/LW/>
	WFIKPCH	PROBLEM,#10			;BR IF NO INTRPT FOR 10 SECONDS
	IOFORK					;IF INTERRUPT, FORK
	BITW	#M_ERR!M_RDY,UCB$W_LP_STATUS(R5) ;IS PLOTTER READY?
	BGTR	MOD_SET				;BR IF MODE SUCCESSFULLY SET
	BRW	PROB2				;BRANCH IF STILL NOT READY
MOD_SET:BBC	#IO$V_REMOTE,IRP$W_FUNC(R3),20$	;IF NOT REMOTE, SKIP
	BRW	REMOTEIO			;PERFORM REMOTE
20$:	BBC	#IRP$V_BUFIO,IRP$W_STS(R3),30$	;DIRECT I/O?
				;NORMALLY DPC/PIO DEVICES DO DIRECT I/O
				;AND DMA/NPR DEVICES DO BUFFERED I/O.  BECAUSE
				;OF THIS DEC INTIALIZES UCB$BOFF AND UCB$SVAPTE
				;FOR USE BY A PIO DEVICE, AND THEN OVERWRITES
				;IT WITH DMA PARAMETERS ONLY WHEN THE USER
				;CALLS EXE$WRITELOCK TO LOCK PAGES IN MEMORY
				;FOR A DIRECT I/O TRANSFER.  BECUASE VERSATEC
				;DOES DMA IN CONJUNCTION WITH BUFFERED I/O FOR
				;THE STANDARD PRINT FUNCTION, IT IS NECESSARY
				;TO MANUALLY SET UP THESE TWO PARAMETERS.
	MOVL	IRP$L_SVAPTE(R3),R0		;R0 POINTS TO ADDR OF DATA
	EXTZV	#VA$V_VPN,#VA$S_VPN,(R0),R2	;GET VPN OF BUFFER
	MOVL	G^MMG$GL_SPTBASE,R1		;GET SPT BASE ADDRESS
	MOVAL	(R1)[R2],UCB$L_SVAPTE(R5)	;STORE SVA OF PTE
	EXTZV	#VA$V_BYTE,#VA$S_BYTE,(R0),R2	;GET OFFSET IN PAGE
	MOVW	R2,UCB$W_BOFF(R5)		;AND STORE IN UCB
30$:	TSTW	UCB$W_BCNT(R5)			;BYTE COUNT ZERO?
	BNEQ	40$				;NO, BRANCH TO CONTINUE
	BRW	NORMAL				;YES, EXIT WITH NORMAL STATUS
40$:
.IF		NE,<VRSNAM-^A/LW/>
	MOVW	UCB$W_BCNT(R5),UCB$W_LP_XFSIZ(R5) ;ASSUME TRANSFER WHOLE BUFFER
	BBS	#UCB$V_LP_MAPPING,UCB$W_LP_FLAGS(R5),SKPMOV ;BRANCH IF MAPPING
	MOVL	UCB$L_SVAPTE(R5),UCB$L_LP_SVAPTE(R5) ;SAVE SVAPTE
	MOVAB	G^IOC$MOVFRUSER,UCB$A_LP_MOVRTN(R5) ;ADDRESS IF MOVE ROUTINE
	MOVW	UCB$W_BCNT(R5),UCB$W_LP_PBCR(R5) ;COPY BYTE COUNT
NEXFER:	MOVZWL	UCB$W_LP_PBCR(R5),R2		;GET NO. BYTES LEFT TO XFER
	CMPL	R2,#UCB$K_LP_BUFSZB		;BIGGER THAN BUFFER?
	BLEQ	42$				;BRANCH IF NOT
	MOVL	#UCB$K_LP_BUFSZB,R2		;YES, ONLY TRANSFER BUFSZ BYTES
42$:	MOVW	R2,UCB$W_LP_XFSIZ(R5)		;SET SIZE OF THIS TRANSFER
	MOVL	UCB$L_LP_BUFADR(R5),R0		;GET INTERMEDIATE ADDR., IF ANY
	MOVL	UCB$A_LP_BUF_VA(R5),R1		;VIRTUAL ADDR. OF DRIVER BUFFER
	JSB	@UCB$A_LP_MOVRTN(R5)		;COPY DATA FROM USER BUFFER
	MOVL	R0,UCB$L_LP_BUFADR(R5)		;SAVE INTERMEDIATE ADDRESS
	MOVAB	G^IOC$MOVFRUSER2,UCB$A_LP_MOVRTN(R5) ;NEXT MOVE ROUTINE ADDR.
	MOVZWL	UCB$A_LP_BUF_PA(R5),R1		;GET LOW 16 BITS OF PHYS. ADDR.
	MOVZWL	UCB$A_LP_BUF_PA+2(R5),R2	;GET HIGH 6 BITS OF PHYS. ADDR.
	ASHL	#4,R2,R2			;PUT WHERE CONTROLLER EXPECTS
	BRB	CKSWAP				;GO JOIN COMMON CODE
SKPMOV:
.ENDC		;NE,<VRSNAM-^A/LW/>
				;PROCESSORS WHICH DO NOT HAVE BUFFERED DATA
				;PATHS SUCH AS THE VAX-11/730 IGNORE THE NEXT
				;CALL
	REQDPR					;ASK BUFFERED DATA PATH REG.
	REQMPR					;REQUEST ENOUGH MAP REGISTERS
	LOADUBA					;LOAD UBA MAP REGISTERS
	MOVZWL	UCB$W_BOFF(R5),R1		;GET BYTE OFFSET IN FIRST PAGE
	MOVL	UCB$L_CRB(R5),R2		;GET CRB ADDRESS INTO R2
	INSV	CRB$L_INTD+VEC$W_MAPREG(R2),-
		#9,#9,R1			;CREATE BUS ADDRESS
	EXTZV	#16,#2,R1,R2			;GET HI UNIBUS BITS
	ASHL	#4,R2,R2			;PUT WHERE CONTROLLER EXPECTS
.IF		EQ,<VRSNAM-^A/LW/>
	BISW	#LW_CSR$M_DMA_ADR,LW_CSR(R4)	;SET UP FOR DMA ADDR.
	MOVW	R1,LW_CDR(R4)			;DMA ADDRESS TRANSFER
	BISW	#LW_CSR$M_BYTCNT,LW_CSR(R4)	;SET UP FOR BYTE COUNT
	MNEGW	UCB$W_BCNT(R5),LW_CDR(R4)	;LOAD BYTE COUNT
.IF_TRUE_FALSE	;EQ,<VRSNAM-^A/LW/>
CKSWAP:	BITW	#IO$M_SWAPBYTES,IRP$W_FUNC(R3)	;SWAP BYTES?
	BEQL	50$				;NO, BRANCH
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BBC	#UCB$V_LP_HWSWAP,UCB$W_LP_FLAGS(R5),50$	;BR IF NO HARDWARE SWAP
.IF_TRUE_FALSE	;EQ,<VRSNAM-^A/LW/>
	BISW	#VRS_CSR$M_ENSWP,VRS_CSR(R4)	;SET ENABLE SWAP
	BRB	60$				;
50$:	BICW	#VRS_CSR$M_ENSWP,VRS_CSR(R4)	;CLEAR SWAP BIT
60$:
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BICL3	#4,R4,R0			;FORCE PLOT CSR TO R0
	MOVW	R1,LV_DBAR(R0)			;LOAD DMA ADDRESS REG.
	MOVW	R2,LV_DBARX(R0)			;LOAD ADDRESS EXT. REG.
.ENDC		;EQ,<VRSNAM-^A/LW/>
	DSBINT					;DISABLE INTERRUPTS
	BBSC	#UCB$V_POWER,UCB$W_STS(R5),POWERF ;CHECK FOR POWER FAIL
	BISW	#M_TRINPRG,UCB$W_DEVSTS(R5)	;SET XFER IN PROGRESS
.IF		EQ,<VRSNAM-^A/LW/>
	BISB3	#LW_CSR$M_IE!LW_CSR$M_DMAGO,R2,LW_CSR(R4) ;SET 16,17,GO,IE
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BBS	#UCB$V_LP_NEGCNT,UCB$W_LP_FLAGS(R5),70$ ;COUNT + OR -?
	MOVW	UCB$W_LP_XFSIZ(R5),LV_BCR(R4)	;LOAD BYTE COUNT
	BRB	80$
70$:	MNEGW	UCB$W_LP_XFSIZ(R5),LV_BCR(R4)	;LOAD 2'S COMPLEMENT BYTE COUNT
80$:	BISW	#LV_CSR$M_IE,LV_CSR(R4)		;ENABLE INTERRUPT
.ENDC		;EQ,<VRSNAM-^A/LW/>
	WFIKPCH	PROBLEM,#12			;BR IF NO INTRPT 12 SECS
	IOFORK					;INTRPT OCCURED - FORK PROCESS
	BITW	#M_ERR!M_RDY,UCB$W_LP_STATUS(R5) ;CHECK FOR ERROR
						; (STATUS SAVED BY INTERRUPT)
	BLEQ	PROB2				;ERROR OR NOT READY
	BRW	SUCCESS				;ELSE COMPLETE OK
;
POWERF:	ENBINT					;RE-ENABLE INTERRUPTS
	BRW	RESTART				;RE-START TRANSFER
;
REMOTEIO:
	DSBINT					;DISABLE INTERRUPTS
	BBSC	#UCB$V_POWER,UCB$W_STS(R5),POWERF ;CHECK FOR POWER FAIL
	BISW	#M_TRINPRG,UCB$W_DEVSTS(R5)	;SET XFER IN PROGRESS
.IF		EQ,<VRSNAM-^A/LW/>
	BISB3	#LW_CSR$M_IE,IRP$L_MEDIA(R3),LW_CSR(R4)	;REM. + IE
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	MOVZBL	IRP$L_MEDIA(R3),R2		;GET FUNCTION CODE
	BISW3	#LV_CSR$M_IE,R2,LV_CSR(R4)	;INTERRUPT AND REMOTE FUNCT.
.ENDC		;EQ,<VRSNAM-^A/LW/>
	WFIKPCH	PROBLEM,#12			;BR IF NO INTERRUPT 12 SECS
	IOFORK					;INTERRUPTED - FORK PROCESS
	BITW	#M_ERR!M_RDY,UCB$W_LP_STATUS(R5) ;CHECK FOR ERROR
						; (STATUS SAVED BY INTERRUPT)
	BLEQ	PROB2				;ERROR OR NOT READY
	MOVZWL	#SS$_NORMAL,R0			;ASSUME NO ERROR
	MOVZBL	#1,R1				;REMOTE XFERS 1 LINE
	BRW	COMPLET				;IF NONE, CONTINUE
;
PROBLEM:				;HERE IF NO INTERRUPT OCCURED
	MOVW	VRS_CSR(R4),UCB$W_LP_STATUS(R5)	;GET STATUS AND SAVE
.IF		EQ,<VRSNAM-^A/LW/>
	CLRB	LW_CSR(R4)			;DISABLE INTERRUPTS
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	CLRW	LV_CSR(R4)			;DISABLE INTERRUPTS
.ENDC		;EQ,<VRSNAM-^A/LW/>
PROB2:					;HERE IF DEVICE TIMED OUT
	CLRL	UCB$L_LP_OFLCNT(R5)		;INIT. OFFLINE COUNTER
	BICW	#M_ERR_DETECT,UCB$W_DEVSTS(R5)	;CLEAR ERROR DETECTED FLAG
	BRB	8$				;BR SINCE ALREADY GOT STATUS
5$:	MOVW	VRS_CSR(R4),UCB$W_LP_STATUS(R5)	;GET STATUS AND SAVE
8$:	SETIPL	UCB$B_FIPL(R5)			;DROP TO FORK IPL
	BBS	#V_ERR,UCB$W_LP_STATUS(R5),12$	;BR IF ERROR BIT SET
	BBC	#V_RDY,UCB$W_LP_STATUS(R5),16$	;BR IF READY BIT NOT SET
	MOVL	#15,UCB$L_LP_TIMOUT(R5)		;RESET TIMEOUT VALUE
	BRB	NO_PROBLEM
12$:	BISW	#M_ERR_DETECT,UCB$W_DEVSTS(R5)	;SET ERROR DETECTED
.IF		NE,<VRSNAM-^A/LW/>
	BBS	#LV_CSR$V_DMABSY,UCB$W_LP_STATUS(R5),25$ ;IF ACTIVE, CAN'T CANCL
	CLRW	LV_CSR(R4)			;ATTEMPT TO CLEAR ERROR
	BRB	20$				;ALWAYS ALLOW CANCEL IF ERROR
16$:	BBS	#V_TRINPRG,UCB$W_DEVSTS(R5),25$	;CAN'T CANCEL IF ACTIVE
.IF_FALSE	;NE,<VRSNAM-^A/LW/>
	BBC	#IO$V_INHRETRY,IRP$W_FUNC(R3),16$ ;BR IF RETRYING
	MOVB	#LW_CSR$M_RESETALL,LW_CSR(R4)	;ELSE RESET TO ABORT XFER
16$:	BBS	#LW_CSR$V_DMAGO,UCB$W_LP_STATUS(R5),25$ ;IF ACTIVE, CAN'T CANCL
.ENDC		;NE,<VRSNAM-^A/LW/>
20$:	BBS	#UCB$V_CANCEL,UCB$W_STS(R5),CANCEL ;IF SET, CANCEL
	BBC	#V_ERR_DETECT,UCB$W_DEVSTS(R5),25$ ;BR IF NO ERRORS
	BBS	#IO$V_INHRETRY,IRP$W_FUNC(R3),CANCEL ;CANCEL IF NOT RETRYING
25$:	ACBL	UCB$L_LP_TIMOUT(R5),#1,-
		UCB$L_LP_OFLCNT(R5),60$		;TRY 'N' TIMES B4 CALLING OPR
	CLRL	UCB$L_LP_OFLCNT(R5)		;CLEAR OFFLINE COUNTER
	CMPL	#VRS_HRCNT,-
		UCB$L_LP_TIMOUT(R5)		;EXCEEDED TIMEOUT?
	BLEQU	30$
	MULL	#2,UCB$L_LP_TIMOUT(R5)		;DOUBLE TIME COUNT
30$:	BBC	#V_ERR,UCB$W_LP_STATUS(R5),60$	;CAN'T SAY "DEVICE OFFLINE"
	PUSHR	#^M<R3,R4>			;SAVE A FEW REGS
	MOVZBL	#MSG$_DEVOFFLIN,R4		;SET UP DEVICE-OFFLINE MESSAGE
40$:	MOVAB	G^SYS$GL_OPRMBX,R3		;ADDRESS TARGET MAILBOX
	JSB	G^EXE$SNDEVMSG			;SEND MESSAGE
	POPR	#^M<R3,R4>			;RESTORE REGISTERS
60$:	DSBINT	UCB$B_DIPL(R5)			;DISABLE INTERRUPTS
	WFIKPCH	5$,#2				;WAIT 2 SECONDS, THEN BR TO 5$
			;NOT EXPECTING INTERRUPT HERE, BUT HANDLE JUST IN CASE
	IOFORK					;INTERRUPTED - FORK PROCESS
	BRW	8$				;AND SEE WHAT'S UP
;
NO_PROBLEM:
	BBC	#V_TRINPRG,UCB$W_DEVSTS(R5),10$	;FINISHED SETTING MODE
	BBS	#V_ERR_DETECT,UCB$W_DEVSTS(R5),5$ ;BR IF ANY ERRORS DETECTED
	BRW	SUCCESS				;SUCCESS IF NO ERROR
5$:
.IF		EQ,<VRSNAM-^A/LW/>
	BBC	#IRP$V_BUFIO,IRP$W_STS(R3),SUCCESS ;DON'T RESTART DIRECT I/O
.ENDC		;EQ,<VRSNAM-^A/LW/>
	BRB	RESTART
10$:	BRW	MOD_SET
;
CANCEL:
.IF		EQ,<VRSNAM-^A/LW/>
	MOVB	#LW_CSR$M_RESETALL,LW_CSR(R4)	;RESET DEVICE
.IF_FALSE	;EQ,<VRSNAM-^A/LW/>
	BISW	#LV_CSR$M_RESET,LV_CSR(R4)	;RESET DEVICE
.ENDC		;EQ,<VRSNAM-^A/LW/>
	BBS	#IO$V_REMOTE,IRP$W_FUNC(R3),10$	;BRANCH IF REMOTE
	BBC	#V_TRINPRG,UCB$W_DEVSTS(R5),10$	;BRANCH IF NEVER ALLOCATED
	PURDPR					;PURGE DATA PATH
.IF		NE,<VRSNAM-^A/LW/>	
	BLBS	R0,2$				;BR IF SUCCESS
	INCB	UCB$B_LP_DPPE(R5)		;INCREMENT ERROR COUNT
2$:	BBC	#UCB$V_LP_MAPPING,UCB$W_LP_FLAGS(R5),5$ ;BRANCH IF NOT MAPPING
.IF_TRUE_FALSE	;NE,<VRSNAM-^A/LW/>
	RELMPR					;RELEASE MAP REGISTERS
	RELDPR					;RELEASE DATA PATH
.IF_TRUE	;NE,<VRSNAM-^A/LW/>
	BRB	10$				;
5$:	MOVL	UCB$L_LP_SVAPTE(R5),UCB$L_SVAPTE(R5) ;RESTORE SVAPTE
.ENDC		;NE,<VRSNAM-^A/LW/>
10$:	MOVZWL	#SS$_ABORT,R0			;SET OPERATION ABORTED STATUS
	BBS	#UCB$V_CANCEL,UCB$W_STS(R5),20$	;BR IF CANCEL
	MOVZWL	#SS$_DEVOFFLINE,R0		;SET DEVICE OFFLINE STATUS
20$:	CLRL	R1				;AND NO LINES PRINTED
	BRW	COMPLET				;AND COMPLETE
;
RESTART:					;RESTART RECOVERY
	BBS	#UCB$V_CANCEL,UCB$W_STS(R5),CANCEL ;CANCEL?
	BBS	#IO$V_REMOTE,IRP$W_FUNC(R3),10$	;BRANCH IF REMOTE
	PURDPR					;PURGE DATA PATH
.IF		NE,<VRSNAM-^A/LW/>
	BLBS	R0,2$				;BR IF SUCCESS
	INCB	UCB$B_LP_DPPE(R5)		;INCREMENT ERROR COUNT
2$:	BBC	#UCB$V_LP_MAPPING,UCB$W_LP_FLAGS(R5),5$ ;BRANCH IF NOT MAPPING
.IF_TRUE_FALSE	;NE,<VRSNAM-^A/LW/>
	RELMPR					;RELEASE MAP REGISTERS
	RELDPR					;RELEASE DATA PATH
.IF_TRUE	;NE,<VRSNAM-^A/LW/>
	BRB	8$				;
5$:	MOVL	UCB$L_LP_SVAPTE(R5),UCB$L_SVAPTE(R5) ;RESTORE SVAPTE
8$:
.ENDC		;NE,<VRSNAM-^A/LW/>
	MOVL	UCB$L_IRP(R5),R3		;IRP ADDR WAS CLEARED BY PURGE
10$:	BRW	STARTIO				;RESTART
;
SUCCESS:BBS	#IO$V_REMOTE,IRP$W_FUNC(R3),NORMAL
	PURDPR					;PURGE DATA PATH
.IF		NE,<VRSNAM-^A/LW/>
	BLBS	R0,2$				;BR IF SUCCESS
	INCB	UCB$B_LP_DPPE(R5)		;INCREMENT ERROR COUNT
2$:	BBC	#UCB$V_LP_MAPPING,UCB$W_LP_FLAGS(R5),5$ ;BRANCH IF NOT MAPPING
.IF_TRUE_FALSE	;NE,<VRSNAM-^A/LW/>
	RELMPR					;RELEASE MAP REGISTERS
	RELDPR					;RELEASE DATA PATH
.IF_TRUE	;NE,<VRSNAM-^A/LW/>
	BRB	8$				;
5$:	SUBW	UCB$W_LP_XFSIZ(R5),UCB$W_LP_PBCR(R5) ;ADJUST BYTE COUNT
	BEQL	6$				;BR IF NO MORE
	BRW	NEXFER				;BRANCH FOR NEXT TRANSFER
6$:	MOVL	UCB$L_LP_SVAPTE(R5),UCB$L_SVAPTE(R5) ;RESTORE SVAPTE
8$:
.ENDC		;NE,<VRSNAM-^A/LW/>
NORMAL:	MOVZWL	#SS$_NORMAL,R0			;SET NORMAL COMPLETION STATUS
	INSV	UCB$W_BCNT(R5),#16,#16,R0	;ALL BYTES WERE TRANSFERED
	MOVZBL	#1,R1				;ARBITRARILY ASSUME UNBUFFERED
	MOVL	UCB$L_IRP(R5),R3		;IRP ADDR WAS CLEARED BY PURGE
	BBC	#IRP$V_BUFIO,IRP$W_STS(R3),COMPLET ;SKIP FOR DIRECT I/O
	MOVZWL	IRP$L_MEDIA(R3),R1		;GET NUMBER OF LINES PRINTED
;
COMPLET:REQCOM					;COMPLETE REQUEST
;
	.PAGE
	.SUBTITLE	Cancel I/O Routine
;+
; VRS_CANCELIO - Cancel Output
;
; This routine conditionally marks the UCB such that the current
; I/O request will be cancelled if conditions warrant such an action.
; This routine also issues a reset-all command to the controller so
; that it will abort any current command and return to either a ready
; or offline state.
;
; Inputs:
;
;	R2 = Negative of the channel number
;	R3 = Current I/O Packet
;	R4 = PCB address
;	R5 = UCB address
;
; Outputs:
;
;	If the device is busy, the request is for the current process
;	and the I/O was issued from the designated channel, then the
;	cancel I/O bit is set in the corresponding UCB and a reset-all
;	command is issued to the controller.
;
;	R2, R3, R4, and R5 are preserved across call.
;-
;
VRS_CANCELIO:					;Cancel I/O on channel
;	JSB	G^INI$BRK			;FOR USE WITH XDELTA
	BBC	#UCB$V_BSY,UCB$W_STS(R5),10$	;If clear, device not busy
	CMPL	IRP$L_PID(R3),PCB$L_PID(R4)	;Process ID match?
	BNEQ	10$				;If NEQ no
	CMPW	R2,IRP$W_CHAN(R3)		;Channel number match?
	BNEQ	10$				;If NEQ no
	BISW	#UCB$M_CANCEL,UCB$W_STS(R5)	;Set cancel pending
	PUSHL	R4				;Save R4
	MOVL	UCB$L_CRB(R5),R4		;Get CRB address into R4
	MOVL	@CRB$L_INTD+VEC$L_IDB(R4),R4	;Put CSR in R4
.IF		EQ,<VRSNAM-^A/LW/>
	MOVB	#LW_CSR$M_RESETALL,LW_CSR(R4)	;Reset device
.IF_FALSE		;EQ,<VRSNAM-^A/LW/>
	BBC	#UCB$V_LP_NEGCNT,UCB$W_LP_FLAGS(R5),5$ ;Br if 121
	MOVW	#^XFFFF,LV_BCR(R4)		;Load -1 too abort
5$:
.ENDC		;EQ,<VRSNAM-^A/LW/>
	POPL	R4				;Restore R4
10$:	RSB					;
;
	.PAGE
	.SUBTITLE	Printer/Plotter interrupt dispatcher
;+
; VRS$INT - Printer/Plotter interrupt dispatcher.
;
; This routine is entered via a jsb instruction when an interrupt
; occurs on an printer/plotter controller. The state of the stack
; on entry is:
;
;	00(SP) = ADDRESS OF IDB ADDRESS.
;	04(SP) = SAVED R3.
;	08(SP) = SAVED R4.
;	12(SP) = SAVED R5.
;	16(SP) = INTERRUPT PC.
;	20(SP) = INTERRUPT PSL.
;
; Interrupt dispatching occurs as follows:
;
;	If the interrupt is expected, then the driver is called at
;	its interrupt wait address. Else the interrupt is dismissed.
;-
VRS$INT:					;Entry from dispatch
	MOVL	@(SP)+,R3			;Get address of IDB
	MOVQ	IDB$L_CSR(R3),R4		;Controller CSR and owner UCB
	BBCC	#UCB$V_INT,UCB$W_STS(R5),10$ 	;If clr, interrupt not expected
	MOVW	VRS_CSR(R4),UCB$W_LP_STATUS(R5)	;Get status and save
.IF		NE,<VRSNAM-^A/LW/>
	CLRW	LV_CSR(R4)			;Disable output interrupts
.IF_FALSE	;NE,<VRSNAM-^A/LW/>
	CLRB	LW_CSR(R4)			;Disable output interrupts
.ENDC		;NE,<VRSNAM-^A/LW/>
	MOVL	UCB$L_FR3(R5),R3		;Restore driver context
	JSB	@UCB$L_FPC(R5)			;Call driver at wait address
10$:	MOVQ	(SP)+,R0			;Restore registers
	MOVQ	(SP)+,R2			;
	MOVQ	(SP)+,R4			;
	REI					;Return
;
	.PAGE
	.SUBTITLE Printer/Plotter unit initialization
;+
;	 VRS_INIT - Unit initialization
;
; This routine is called at system startup and after a power failure.
; The online bit is set for the specified unit.
;
; Inputs:
;
;	R5 = UCB of unit to be initialized
;
; Outputs:
;
; 	The online bit is set in the device UCB and the address of the UCB
; 	is filled into the IDB owner field.
;
;-
VRS_INIT:					;Unit initialization
	MOVL	UCB$L_CRB(R5),R1		;Get CRB address
	MOVL	CRB$L_INTD+VEC$L_IDB(R1),R0	;Get IDB address
	MOVL	R5,IDB$L_OWNER(R0)		;Set address of device UCB
.IF		NE,<VRSNAM-^A/LW/>
	MOVW	#UCB$M_LP_MAPPING,UCB$W_LP_FLAGS(R5) ;Assume adapter mapping
	BITW	#LV_CSR$M_BIT22,LV_CSR(R4)	;Does controller have 22-bits?
	BEQL	10$				;Branch if not
	BISW	#UCB$M_LP_BIT22!UCB$M_LP_HWSWAP!UCB$M_LP_NEGCNT,-
		UCB$W_LP_FLAGS(R5)  ;126 has 22-bit, hardware swap, neg. count
	CMPB	G^EXE$GB_CPUTYPE,#PR$_SID_TYPUV1 ;Is CPU a MICRO-VAX I?
	BNEQ	10$				;Br if not
	BICW	#UCB$M_LP_MAPPING,UCB$W_LP_FLAGS(R5) ;No adapter mapping
	MOVL	CRB$L_AUXSTRUC(R1),R2		;Alloc failure?
	BEQL	20$				;If yes, leave offline
	MOVL	R2,UCB$A_LP_BUF_VA(R5)		;Save buffers virtual address
	EXTZV	#VA$V_VPN,#VA$S_VPN,R2,R1	;Get virtual page no. of buffer
	MOVL	G^MMG$GL_SPTBASE,R0		;Get base address of SPTS
	MOVL	(R0)[R1],R0			;Get the PTE contents
	BICL3	#^C<VA$M_BYTE>,R2,R1		;Get buffer offset (BA00-BA08)
	ASSUME PTE$S_PFN GE 13
	INSV	R0,#9,#13,R1			;Copy BA09-BA21
	MOVL	R1,UCB$A_LP_BUF_PA(R5)		;Save physical address of buffer
10$:
.ENDC		;NE,<VRSNAM-^A/LW/>
	BISW	#UCB$M_ONLINE,UCB$W_STS(R5)	;Set unit online
20$:	RSB	
VRS_CINIT:					;Controller initialization
	MOVL	IDB$L_UCBLST(R5),R0		;Get address of UCB
	MOVL	#15,UCB$L_LP_TIMOUT(R0)		;Set initial time
	MOVAB	FALLTAB,TRANS_TAB		;Get addr. of fallback table
.IF		NE,<VRSNAM-^A/LW/>
	MOVAB	REMCODE,REMTABL			;Get addr. of remote functions
	CMPB	G^EXE$GB_CPUTYPE,#PR$_SID_TYPUV1 ;Is CPU a MICRO-VAX I?
	BNEQ	10$				;Br if not
	MOVZWL	#UCB$K_LP_BUFSZP,R1		;Load size of buffer
	JSB	G^EXE$ALOPHYCNTG		;Allocate physical contig. mem.
	BLBC	R0,20$				;Exit on error
	MOVL	R2,CRB$L_AUXSTRUC(R8)		;Get buffer virtual address
10$:	RSB					;
20$:	CLRL	CRB$L_AUXSTRUC(R8)		;Indicate memory alloc. fail
.ENDC		;NE,<VRSNAM-^A/LW/>
	RSB					;
;
	.PAGE
	.SUBTITLE	TABLES FOR LOWERCASE AND CONTROL CHARACTERS
;
;	TABLE TO DISTINGUISH CONTROL CHARACTERS
;
CONTROL_TAB:
	.WORD	^B1111111111111111
	.WORD	^B1111111111111111
	.LONG	0
	.LONG	0
	.WORD	0
	.WORD	^B1000000000000000
	.WORD	^B1111111111111111
	.WORD	^B1111111111111111
	.LONG	0
	.LONG	0
	.LONG	0
;
;	TABLE TO DISTINGUISH LOWERCASE CHARACTERS
;
LOWERCASE_TAB:
	.LONG	0
	.LONG	0
	.LONG	0
	.WORD	^B1111111111111110
	.WORD	^B0000011111111111
	.LONG	0
	.LONG	0
	.LONG	0
	.WORD	^B1111111111111111
	.WORD	^B0011111111111110
;
;	POINTER TO FALLBACK TABLE
;
TRANS_TAB:
	.LONG	FALLTAB
	.PAGE
	.SUBTITLE	FALLBACK - TABLE TO CREATE FALLBACK PRESENTATION
;++
;FALLBACK - TABLE TO ALLOW THE PRINTER TO DO FALLBACK PRESENTATION OF
;           8 BIT CHARACTERS WITH 7-BIT CHARACTER PROM.
;
; Description:
;	The following macros generate 1 table.  The table is a 256 byte
; table with the single character fallback representation of all the
; characters that can be represented by a single character, those with
; no fallback presentation at all are represented by the _ character.
;--
	.macro	$fallini
$$=0
.repeat	256
.IF LE $$-<^X9F>	; EVERYTHING BUT THE MULTINATIONAL SET SHOULD
			; ECHO AS ITSELF.
	.byte	$$
.IFF
	.BYTE ^A/_/
.ENDC
$$=$$+1
.endr
$$$=.
	.endm	$fallini
	.PAGE
;++
; $FALL - generates the table entry for a given character
;
; Inputs:
;
;	CHARH - COLUMN IN THE ASCII TABLE
;	CHARL - ROW IN THE ASCII TABLE
;	FALLBACK - String that is the fallback representation
;	COUNT - Number of times to repeat this character
;--
	.MACRO	$FALL	CHARH,CHARL,FALLBACK,COUNT=1
.=FALLTAB+<CHARH*16>+CHARL
.REPEAT COUNT
.NCHR	SLEN,^\FALLBACK\
.IF EQ	SLEN-1
	.BYTE	^A/FALLBACK/
.ENDR
	.ENDM	$FALL
	.PAGE
;++
; $FALLEND - GENERATES END CONDITIONS FOR THE FALLBACK TABLE
;
; Description:
;
;	Resets the . to the end of the fallback table
;
; Inputs:
;
;	None
;--
	.MACRO	$FALLEND
.=$$$
	.ENDM	$FALLEND
	.PAGE
FALLTAB::
	$FALLINI
	$FALL	10,1,!
	$FALL	10,2,c
	$FALL	10,3,L
	$FALL	10,5,Y
	$FALL	10,8,O
	$FALL	10,10,a
	$FALL	11,0,o
	$FALL	11,1,+
	$FALL	11,2,2
	$FALL	11,3,3
	$FALL	11,5,u
	$FALL	11,7,.
	$FALL	11,9,1
	$FALL	11,10,o
	$FALL	11,15,?
	$FALL	12,0,A,6
	$FALL	12,7,C
	$FALL	12,8,E,4
	$FALL	12,12,I,4
	$FALL	13,1,N
	$FALL	13,2,O,5
	$FALL	13,8,O
	$FALL	13,9,U,4
	$FALL	13,13,Y
	$FALL	14,0,a,6
	$FALL	14,7,c
	$FALL	14,8,e,4
	$FALL	14,12,i,4
	$FALL	15,1,n
	$FALL	15,2,o,5
	$FALL	15,8,o
	$FALL	15,9,u,4
	$FALL	15,13,y
	$FALLEND
;
;
VRS_END:					;Address of end of driver
	.END
