	.TITLE	XMDRIVER - VAX/VMS DMC-11 DRIVER
	.IDENT	/X05/
;
; COPYRIGHT (C) 1977
; DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
;
; THIS SOFTWARE IS FURNISHED UNDER  A LICENSE FOR USE ONLY  ON  A
; SINGLE COMPUTER SYSTEM AND MAY BE  COPIED ONLY WITH  THE INCLU-
; SION OF  THE  ABOVE  COPYRIGHT NOTICE.  THIS SOFTWARE,  OR  ANY
; OTHER COPIES THEREOF, MAY NOT BE  PROVIDED  OR  OTHERWISE  MADE
; AVAILABLE TO ANY OTHER PERSON EXCEPT  FOR  USE  ON  SUCH SYSTEM
; AND TO  ONE WHO AGREES  TO  THESE LICENSE  TERMS.  TITLE TO AND
; OWNERSHIP OF THE SOFTWARE SHALL AT ALL TIMES REMAIN IN DEC.
;
; THE INFORMATION IN THIS SOFTWARE  IS  SUBJECT TO CHANGE WITHOUT
; NOTICE AND SHOULD NOT BE CONSTRUED  AS  A COMMITMENT BY DIGITAL
; EQUIPMENT CORPORATION.
;
; DEC ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS
; SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DEC.
;++
; FACILITY:
;
;	VAX/VMS DMC-11 I/O DRIVER
;
; ABSTRACT:
;
;	THIS MODULE CONTAINS THE DMC-11 DRIVER FDT ROUTINES,
;	INTERRUPT DISPATCHER, INTERRUPT SERVICE AND FORK ROUTINES.
;
; AUTHOR:
;
;	R.HEINEN 24-AUG-77
;--
;
; EXTERNAL SYMBOLS
;
	$ACBDEF				; DEFINE AST CONTROL BLOCKS
	$CRBDEF				; DEFINE CRB
	$DYNDEF				; DEFINE DYNAMIC DATA TYPES
	$EMBDEF				; DEFINE ERROR LOG SYMBOLS
	$IDBDEF				; DEFINE IDB
	$IODEF				; DEFINE I/O SYMBOLS
	$IPLDEF				; DEFINE IPLS
	$IRPDEF				; DEFINE I/O PACKET
	$MSGDEF				; DEFINE SYSTEM MESSAGES
	$PCBDEF				; DEFINE PCB
	$PRDEF				; DEFINE PROCESSOR CONSTANTS
	$RSNDEF				; DEFINE RESOURCE NUMBERS
	$UBADEF				; DEFINE UBA VALUES
	$UCBDEF				; DEFINE UCB
	$VADEF				; DEFINE VIRTUAL ADDRESS
	$XMDEF				; DEFINE DMC DRIVER SYMBOLS
	$VECDEF				; DEFINE CRB VECTOR
;
; LOCAL SYMBOL DEFINITIONS
;
; ARGUMENT LIST OFFSETS FOR QIO 
;
P1	= 0				; ARG #1 ETC.
P2	= 4				;
P3	= 8				;
;
; DMC UCB DEFINITIONS
;
	$DEFINI
 			.BLKB	-76 
$DEF	UCB$Q_XM_ATTN 
 			.BLKQ	1 
$DEF	UCB$Q_XM_RCVS
 			.BLKQ	1 
$DEF	UCB$Q_XM_FREE
			.BLKQ	1 
$DEF	UCB$Q_XM_CURCV1
 			.BLKQ	1 
$DEF	UCB$Q_XM_CURCV2
 			.BLKQ	1 
$DEF	UCB$L_XM_ERRFKB
			.BLKQ	1
$DEF	UCB$W_XM_QUOTA
 			.BLKW	1 
			.BLKB	1
$DEF	UCB$B_XM_EIPL
			.BLKB	1
			.BLKL	1
$DEF	UCB$L_XM_LSTPRT
			.BLKL	1
$DEF	UCB$L_XM_LSTCSR
			.BLKL	1
$DEF	UCB$L_XM_BASAD
			.BLKL	1
$DEF	UCB$L_XM_AST
			.BLKL	1
$DEF	UCB$W_XM_BASMP
			.BLKW	1
			.BLKW	1
;
; DMC-11 CSR DEFINITIONS
;
; CHANGE OVER TO 0+
;
	_VIELD	XM_RCV,0,<-		; DEFINITION FOR RCV CONTROL BLOCK
		<INUS,,M>,-		; BLOCK IN USE BUT IDLE
		<INPR,,M>,-		; HARDWARE RECEIVE IN PROGRESS
	>
	_VIELD	XM_DS,0,<-		; DEFINITION FOR DEVSTS
		<RCVSTS,8,M>,-		; STATUS OF RECEIVES
		<XMTPND,,M>,-		; TRANSMIT CONTROL I PENDING
		<NOTIF,,M>,-		; MAILBOX NOTIFIED
	>
	_VIELD	XM_FC,0,<-		; INTERNAL FUNCTION CODES
		<WRITE,,M>,-		; TRANSMIT - REQ. ONLINE
		<START,,M>,-		; START PROTOCOL - REQ. OFFLINE
		<STOP,,M>,-		; STOP PROTOCOL - REQ. ONLINE
		<CHANGE,,M>,-		; CHANGE MODE - REQ. OFFLINE
	>
$DEF	XM_I_CSR	.BLKW	1	; INPUT CSR
	_VIELD	XM_I,0,<-		; BIT DEFINITIONS
		<TYPE,2,M>,-		; REQUEST TYPE
		<RCV,,M>,-		; RECEIVE OR XMIT FLAG
		<,2>,-			; RESERVED BITS
		<RQSTI,,M>,-		; REQUEST INTERRUPT
		<INTEN,,M>,-		; INTERRUPT ENABLED
		<READY,,M>,-		; PORT AVAILABLE
		<,3>,-			; MAINT BITS
		<LOOPB,,M>,-		; LOOP BACK
		<,2>,-			; MAINT BITS
		<MASTCLR,,M>,-		; MASTER CLEAR
		<RUN,,M>,-		; RUN
	>
$DEF	XM_O_CSR	.BLKW	1	; OUTPUT CSR
	_VIELD	XM_O,0,<-		; BIT DEFINITIONS
		<TYPE,2,M>,-		; INTERRUPT TYPE
		<RCV,,M>,-		; RECEIVE OR XMIT
		<,3>,-			; RESERVED BITS
		<INTEN,,M>,-		; INTERRUPT ENABLE
		<READY,,M>,-		; READY
	>
$DEF	XM_PORT		.BLKW	1	; DEFINE PORT OFFSET
	_VIELD	XM_E,0,<-		; ERROR WORD DEFINITIONS
		<DCHK,,M>,-		; DATA CHECK
		<TIMO,,M>,-		; TIMEOUT
		<ORUN,,M>,-		; DATA OVERRUN
		<MOP,,M>,-		; ENTER MOP MODE RECEIVED
		<LOST,,M>,-		; LOST DATA
		<TRNER,,M>,-		; TRANSFER ERROR ON UBA
		<LINEDWN,,M>,-		; LINE DOWN
		<START,,M>,-		; START RECEIVED
		<NONEXMEM,,M>,-		; NON EXMEM
		<PROCERR,,M>,-		; PROCEEDURE ERROR
		>
;
; DEFINITION OF THE XM DRIVER RECEIVE MESSAGE BLOCK
;
	.=0
MSG_LINK:	.BLKL	2
MSG_BLKSIZE:	.BLKW	1
MSG_BLKTYPE:	.BLKB	1
MSG_FIPL:	.BLKB	1
MSG_UBVA:
MSG_FPC:	.BLKL	1
MSG_MAP1:
MSG_PORT:	.BLKL	1
MSG_MAP2:
MSG_CSR:	.BLKL	1
MSG_DATA:	

	.PSECT	WIONONPAGED	
;
; LOCAL STORAGE
;
; DRIVER DDT
;
XM$DDT::	
	.LONG	STARTIO			; START I/O
	.LONG	0			; UNSOLICITED INTERRUPT
	.LONG	FUNCTABLE		; FUNCTION DECESION TABLE
	.LONG	EXT_CANCELIO		; CANCEL I/O, EXTERNAL
	.LONG	REGDUMP			; REGISTER DUMP ROUTINE
	.WORD	16			; DIAG BUFFER SIZE
	.WORD	16+EMB$L_DV_REGSAV	; ERROR LOG ENTRY SIZE
;
; FUNCTION TABLE
;
FUNCTABLE:				;
	FUNCTAB	,-			; LEGAL FUNCTIONS
		<WRITELBLK,READLBLK,WRITEPBLK,READPBLK,SETMODE,SETCHAR>
	FUNCTAB	,<READLBLK,READPBLK>	; BUFFERED I/O - READ
	FUNCTAB	XMITFDT,<WRITELBLK,WRITEPBLK>;
	FUNCTAB	RCVFDT,<READLBLK,READPBLK>;
	FUNCTAB	SETMODEFDT,<SETMODE,SETCHAR>;

	.SBTTL	XM$INIT - INITIALIZE DMC-11 UNIT AND UCB
;++
; XM$INIT - INITIALIZE DMC-11 UNIT AND UCB
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS ENTERED AT SYSTEM STARTUP TO INITIALIZE THE UNIT UCB
; AND CHECK FOR THE PRESENCE OF THE PHYSICAL UNIT.
;
; INPUTS:
;
;	R4 = ADDRESS OF THE UNIT CSR
;	R5 = ADDRESS OF THE UNITS IDB
;	R9 = ADDRESS OF THE UNIT CRB
;
; OUTPUTS:
;
;	R4,R5,R9 ARE PRESERVED
;--
XM$INITIAL::				; INITIALIZE THE DMC-11 UCB
	PUSHL	R5			; SAVE IDB ADDRESS
	MOVZBL	IDB$B_UNITS(R5),R3	; GET NUMBER OF UNITS ON THIS CONTROLLER
	BEQL	100$			; IF EQL THEN NONE
	MOVL	IDB$L_UCBLST-4(R5)[R3],R5; GET THE ADDRESS OF THE UCB
	BEQL	100$			; IF EQL THEN NONE
	BSBW	RESET_UCB		; INIT THE UCB
100$:	POPL	R5			;
	TSTW	(R4)			; SYNCH THE UBA
	RSB				; RETURN TO INIT PATH

	.SBTTL XMITFDT - TRANSMIT I/O OPERATION FDT ROUTINE
;++
; XMITFDT - TRANSMIT I/O OPERATION FDT ROUTINE
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE SETS UP THE INTERNAL FUNCTION CODE FOR TRANSMIT AND
; TRANSFERS CONTROL TO THE EXEC DIRECT I/O WRITE FDT ROUTINE.
;
; INPUTS:
;
;	R3 = I/O PACKET ADDRESS
;	R4 = CURRENT PCB ADDRESS
;	R5 = UCB ADDRESS
;	R6 = CCB ADDRESS
;	R7 = FUNCTION CODE
;
; OUTPUTS:
;
;	R3,R4,R5,R6,R7 ARE PRESERVED
;
;--
XMITFDT:				; TRANSMIT FDT ROUTINE
	INSV	#XM_FC_V_WRITE,#IRP$V_FCODE,#IRP$S_FCODE,IRP$W_FUNC(R3)
	BRW	EXE$WRITE		; CONTINUE

	.SBTTL	RCVFDT - RECEIVE I/O OPERATION FDT ROUTINE
;++
; RCVFDT - RECEIVE I/O OPERATION FDT ROUTINE
;
; FUNCTIONAL DESCRIPTION:
;
; THE SPECIFIED BUFFER IS CHECKED FOR ACCESSIBILITY. THE BUFFER ADDRESS AND COUNT
; ARE SAVED IN THE PACKET. THEN IPL IS SET TO DEVICE FORK IPL AND IF A MESSAGE IS
; AVAILABLE THE OPERATION IS COMPLETED. OTHERWISE THE PACKET IS QUEUED ONTO
; THE WAITING RECEIVE LIST. THE MAILBOX NOTIFIED BIT IS CLEARED.
;
; FOR FUNCTIONS SPECIFING IO$M_NOW, THE I/O IS COMPLETED WITH STATUS EQUAL TO
;	SS$_ENDOFILE IF NO MESSAGE IS AVAILABLE WHEN THE TEST IS MADE.
;
; FOR FUNCTIONS SPECIFING IO$M_DSABLMBX THE MAILBOX IS DISABLED.
;
; INPUTS:
;
;	R3 = I/O PACKET ADDRESS
;	R4 = PCB ADDRESS
;	R5 = UCB ADDRESS
;	R6 = CCB ADDRESS
;	R7 = FUNCTION CODE
;	AP = ADDRESS OF THE FIRST OPERATION SPECIFIC QIO PARAMETER
;
; OUTPUTS:
;
;	R0 = THE STATUS OF THE RECEIVE QIO OPERATION
;	R3 = I/O PACKET ADDRESS
;	R4 = PCB ADDRESS
;	R5 = UCB ADDRESS
;	R6 = CCB ADDRESS
;	R7 = FUNCTION CODE
;	AP = ADDRESS OF THE FIRST OPERATION SPECIFIC PARAMETER
;
;--
RCVFDT:					; READ OPERATION FDT
	MOVL	P1(AP),R0		; GET USER BUFFER ADDRESS
	MOVZWL	P2(AP),R1		;
	BSBW	EXE$READCHK		; CHECK THE BUFFER
					; NO RETURN ON 0 SIZE OR NO ACCESS
	SETIPL	UCB$B_FIPL(R5)		; RAISE IPL TO LOCK DATA BASE
	BICW	#XM_DS_M_NOTIF,UCB$W_DEVSTS(R5); CLEAR NOTIFIED STATUS
	BBC	#IO$V_DSABLMBX,IRP$W_FUNC(R3),10$; BR IF NO DISABLE MAILBOX
	BICW	#XM$M_STS_MBX,UCB$L_DEVDEPEND(R5); DISABLE MAILBOX
10$:	REMQUE	@UCB$Q_XM_ATTN(R5),R2	; DEQUEUE A RECEIVED MESSAGE
	BVS	15$			; IF V-SET THEN NONE
	MOVZBL	#SS$_NORMAL,R0		; SET TRANSFER STATUS
	BSBW	FINISHRCVQIO		; MATCH THE RECEIVE WITH THE MESSAGE
	BRB	20$			; RETURN TO CALLER
15$:	BBS	#IO$V_NOW,IRP$W_FUNC(R3),25$; BR IF READ NOW
	INSQUE	(R3),@UCB$Q_XM_RCVS+4(R5); PUT PACKET ON WAITING LIST
20$:	MOVZBL	#SS$_NORMAL,R0		; SET QIO STATUS
	BRW	EXE$QIORETURN		; RETURN TO CALLER
25$:	BSBW	IOC$DIAGBUFILL
	MOVZWL	#SS$_ENDOFFILE,R0	; SET END OF FILE ERROR
	MOVL	UCB$L_DEVDEPEND(R5),R1	; SET SECOND STATUS
	BRW	EXE$FINISHIO		; COMPLETE THE REQUEST

	.SBTTL	SETMODEFDT - SET MODE I/O OPERATION FDT DISPATCH ROUTINE
;++
; SETMODEFDT - SET MODE FDT PROCESSING
;
; FUNCTIONAL DESCRIPTION:
;
; THIS IS THE FDT ROUTINE FOR SETMODE FUNCTIONS. THERE ARE THREE FUNCTIONS BASED ON
; SUBFUNCTION MODIFIER BIT.
;
; 1)	CHANGE MODE -- NO MODIFIER BIT.
;	THIS FUNCTION IS DONE IN THE STARTIO ROUTINE. CONTROL IS PASSED TO
;	EXE$SETMODE TO VALIDATE THE NEW MODE BUFFER AND QUEUE THE PACKET.
;
; 2)	INITIALIZE THE UNIT -- IO$M_STARTUP SET.
;	THIS FUNCTION IS DONE PARTIALLY HERE AND THE REMAINDER IS DONE IN STARTIO.
;	THE ACTION HERE IS TO PICK UP THE USER BUFFERED I/O QUOTA AND ALLOCATE
;	THE BASE TABLE. THE BASE TABLE ADDRESS IS SAVED IN IRP$L_SVAPTE. THE QUOTA
;	TAKEN FROM THE USER IS IN IRP$W_BCNT. THIS VALUE WILL BE THE IOSB+2 VALUE
;	AT I/O DONE. THIS FUNCTION IS COMPLETE WHEN THE BASE TABLE HAS BEEN GIVEN
;	TO THE UNIT. THE MAILBOX IS ENABLED AND A RECEIVE IS STARTED.
;
; 3)	SHUTDOWN UNIT -- IO$M_SHUTDOWN SET.
;	THIS FUNCTION SHUTS DOWN THE UNIT AND OPTIONALLY RESETS THE MODE.
;	A CANCEL I/O IS PREFORMED, ALL OUTSTANDING I/O IS COMPLETED, THE BASE 
;	TABLE AND MESSAGE BLOCKS ARE ALL RETURNED AND THE UNIT IS LEFT IN AN IDLE
;	STATE. THIS FUNCTION CANNOT BE DONE HERE AND THE FDT PROCESSING IS
;	THAT OF ALL SETMODE OPERATIONS.
;
; 4)	REQUEST ATTENTION AST -- IO$M_ATTNAST
;	THIS FUNCTION SETS UP A AST TO BE DELIVERED ON ONE OF THE FOLLOWING
;	CONDITIONS:
;	
;	FATAL ERROR THAT CAUSED SHUTDOWN.
;	MESSAGE AVAILABLE TO BE RECEIVED.
;
;
; INPUTS:
;
;	R3 = I/O PACKET ADDESS
;	R4 = PCB ADDRESS
;	R5 = UCB ADDRESS
;	R6 = CCB ADDRESS
;	R7 = FUNCTION CODE
;	AP = ADDRESS OF THE FIRST QIO PARAMETER
;
; OUTPUTS:
;
;	R3 = I/O PACKET ADDRESS
;	R4 = PCB ADDRESS
;	R5 = UCB ADDRESS
;	R6 = CCB ADDRESS
;	R7 = FUNCTION CODE
;
;--
SETMODEFDT:				; SET MODE FDT PROCESSING
	MOVW	IRP$W_FUNC(R3),R7	; GET ENTIRE FUNCTION CODE
	BBS	#IO$V_ATTNAST,R7,10$	; BR IF AST REQUEST
	BBS	#IO$V_STARTUP,R7,20$	; BR IF STARTUP FUNCTION
	MOVZBW	#XM_FC_V_CHANGE,IRP$W_FUNC(R3); ASSUME MODE CHANGE
	BBC	#IO$V_SHUTDOWN,R7,5$	; BR IF NOT SHUTDOWN OF UNIT
	MOVZBW	#XM_FC_V_STOP,IRP$W_FUNC(R3); SET INTERNAL FUNCTION CODE
5$:	BRW	EXE$SETMODE		; PROCESS IN EXEC COMMON
10$:	MOVAB	UCB$L_XM_AST(R5),R7	; SET ADDRESS OF CONTROL BLOCK LIST
	BSBW	COM$SETATTNAST		; SET UP ATTENTION AST
	DSBINT	UCB$B_FIPL(R5)		; CHECK FOR MESSAGES AVAILABLE
	MOVAB	UCB$Q_XM_ATTN(R5),R0	; ADDRESS LIST HEAD FOR MESSAGES
	CMPL	R0,UCB$Q_XM_ATTN(R5)	; EMPTY LIST?
	BEQL	15$			; IF EQL THEN YES
	PUSHL	R3			; SAVE PACKET
	BSBW	DEL_ATTN_AST		; DELIVER THE AST
	POPL	R3
15$:	BRW	EXE$FINISHIOC		; COMPLETE THE I/O
;
; FDT ROUTINE FOR UNIT INIT
;
20$:	MOVZBW	#XM_FC_V_START,IRP$W_FUNC(R3); INSERT INTERNAL FUNCTION CODE
	MOVL	P1(AP),R2		; ADDRESS MODE DATA
	MOVZWL	#SS$_ACCVIO,R0		; ASSUME ACCESS VIOLATION ON BUFFER
	IFNORD	#8,(R2),100$		; BR IF NOT READABLE
	MOVQ	(R2),IRP$L_MEDIA(R3)	; SAVE DATA IN PACKET
	MOVZBL	P3(AP),R1		; GET NUMBER OF MESSAGES TO ALLOCATE
	MOVZWL	2(R2),R2		; GET THE MESSAGE SIZE
	MOVZWL	#SS$_BADPARAM,R0	; ASSUME BAD PARAMETER
	MULW	R2,R1			; GET NEEDED QUOTA
	BVS	100$			; IF V-SET THE OVERFLOW
	ADDW	#256,R1			; ADD IN BASE TABLE SIZE
	BVS	100$			; IF V-SET THEN OVERFLOW
	MOVL	R1,R7			; COPY QUOTA TAKEN
	PUSHL	R3			; SAVE PACKET ADDRESS
	BSBW	EXE$BUFFRQUOTA		; CHECK QUOTA
	BLBC	R0,100$			; BR IF ERROR
	MOVZWL	#256,R1			; ALLOCATE THE BASE TABLE
	BSBW	EXE$ALONONPAGED		; ALLOCATE THE MEMORY
	POPR	#^M<R3>			; BRING BACK PACKET ADDRESS
	BLBC	R0,110$			; ENTER RESOURCE WAIT STATE FOR MEMORY
	MOVW	R7,IRP$W_BOFF(R3)	; SAVE AS QUOTA FOR THIS I/O
	SUBW	R7,PCB$W_BYTCNT(R4)	; ADJUST QUOTA
	MOVL	R2,IRP$L_SVAPTE(R3)	; SAVE BASE TABLE ADDRESS
	MOVW	#256,8(R2)		; SET UP AS BUFFERED I/O BLOCK
	CLRQ	(R2)			; WITH NO DATA
	BISW	#IRP$M_BUFIO,IRP$W_STS(R3); FAKE A BUFFERED I/O
	BICW	#IRP$M_FUNC,IRP$W_STS(R3); SET WRITE DIRECTION
	BRW	EXE$QIODRVPKT		; GIVE PACKET TO DRIVER
100$:	POPL	R3			; RESTORE PACKET
	BRW	EXE$ABORTIO		; ABORT THE I/O
;
; MEMORY ALLOCATION FAILURE
;
110$:	MOVZBL	#RSN$_NPDYNMEM,R0	; SET RESOURCE TO AWAIT
	SETIPL	#IPL$_SYNCH		; RAISE IPL
	BRW	EXE$IORSNWAIT		; RESTART I/O

	.SBTTL	STARTIO - START XMIT OR SETMODE I/O OPERATION
;++
; STARTIO - START XMIT OR SETMODE OPERATION
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS ENTERED WHEN THE UNIT IS IDLE AND THERE IS A PACKET AVAILABLE
; TO PROCESS. IN THE CASE OF SETMODE, THE FUNCTION CODE HAS BEEN CLEARED TO
; SPEED UP THE FUNCTION DISPATCH.
;
; XMIT FUNCTIONS --
;
;	THE ACTION IS TO SET UP THE UBA MAP AND DATA PATH AND REQUEST
;	THE CONTROL IN INTERRUPT. THE UNIT MUST BE IN A RUN STATE.
;
; SETMODE FUNCTIONS --
;
;	FOR ALL FUNCTIONS A CHANGE IN THE CHARACTERISTICS IS DONE.
;
;	FOR STARTUP, THE ACTION IS TO REQUEST AND SET UP THE UBA
;	MAP AND DATAPATH FOR THE BASE TABLE AND RECEIVES. THIS DATA IS SAVED
;	AFTER ALLOCATION IN THE UCB. AFTER THIS A BASEIN IS REQUESTED AND
;	WHEN IT COMPLETES, THE I/O IS COMPETED.
;
;	FOR SHUTDOWN, THE ACTION IS TO MASTER CLEAR THE UNIT AND TRANSFER CONTOROL
;	TO THE SHUTUP UNIT ROUTINE WHICH COMPLETES ALL OUTSTANDING I/O AND DELEATES
;	THE ALLOCATED BUFFERS AS IF A FATAL ERROR HAD OCCURED.
;
; INPUTS:
;
;	R3 = PACKET ADDRESS
;	R5 = UCB ADDRESS
;
; OUTPUTS:
;
;	NONE
;--
STARTIO:				; START SETMODE OR XMIT OPERATION
	MOVL	UCB$L_DEVDEPEND(R5),R1	; GET STATUS AND CHARACTERISTICS
	CLRL	R2			; ASSUME UNIT NEED NOT BE ACTIVE
	BLBS	IRP$W_FUNC(R3),10$	; BR IF NOT EVEN - NOT ACTIVE
	INCL	R2			; MUST BE ACTIVE
10$:	CMPZV	#XM$V_STS_ACTIVE,#1,R1,R2; CHECK STATUS - ACTIVE?
	BNEQ	20$			; IF NEQ THEN DEVICE NOT READY
	BICB3	#^X0C0,IRP$W_FUNC(R3),R1; GET XM FUNCTION ALONE
	CASE	R1,TYPE=B,<XMIT,START,STOP,CHANGE>
;
; DEVICE NOT READY
;
20$:
	MOVZWL	#SS$_DEVACTIVE,R0	; SET DEVICE ACTIVE
	TSTB	R2			; EXPECT ON OR OFF
	BEQL	30$			; IF EQL THEN ACTIVE AND SHOULD BE IDLE
	MOVZWL	#SS$_DEVOFFLINE,R0	; SET DEVICE NOT ON LINE
30$:	REQCOM				; COMPLETE REQUEST

	.SBTTL	XMIT - START XMIT I/O
;++
; XMIT - START XMIT I/O
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS ENTERED TO START A TRANSMIT I/O OPERATION. THE MAP REGISTERS
; AND DATA PATH HAVE BEEN ALLOCATED. PRIOR TO STARTING THE I/O THE ACTUAL
; TRANSFER REQUEST SIZE IS ADJUSTED TO REFLECT THE LINE'S MAX MESSAGE SIZE.
;
; INPUTS:
;
;	R3 = I/O PACKET FOR TRANSMIT FUNCTION
;	R5 = UCB ADDRESS
;
; OUTPUTS:
;
;	CONTROL IS PASSED TO RQSTXMITSTRT.
;
;	R4 = CSR ADDRESS
;	R5 = UCB ADDRESS
;--
XMIT:					; START TRANSMIT I/O
	BBC	#IO$V_ENABLMBX,IRP$W_FUNC(R3),10$; BR IF MAILBOX NOT TO BE ENABLED
	BBSS	#XM$V_STS_MBX,UCB$L_DEVDEPEND(R5),10$; ENABLE MAILBOX
10$:	BSBW	IOC$REQMAPREG		; REQUEST MAP REGISTER FOR OPERATION
	BSBW	IOC$REQDATAP		; REQUEST DATA PATH
	MOVL	IRP$W_BOFF(R3),IRP$L_MEDIA(R3); MOVE OFFSET AND SIZE
	CMPW	UCB$W_DEVBUFSIZ(R5),IRP$L_MEDIA+2(R3); REQUEST SIZE IN RANGE?
	BGEQU	15$			; IF GEQU THEN OK
	MOVW	UCB$W_DEVBUFSIZ(R5),IRP$L_MEDIA+2(R3)
15$:	MOVL	UCB$L_CRB(R5),R2	; ADDRESS CRB
	INSV	CRB$L_INTD+VEC$W_MAPREG(R2),-
		#9,#7,IRP$L_MEDIA(R3); INSERT MAP REGISTER IN ADDRESS
	EXTZV	#7,#2,CRB$L_INTD+VEC$W_MAPREG(R2),R2; GET HIGH TWO BITS
	INSV	R2,#30,#2,IRP$L_MEDIA(R3); INSERT HIGH BITS
	BSBW	IOC$LOADUBAMAP		; LOAD THE UBA FOR TRANSFER
	BRW	RQSTXMITSTRT		; SET STATE TO REQUEST XMIT START

	.SBTTL	START - START UNIT 
;++
; START - START PROTOCOL
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE INITIATES PROTOCOL ON THE UNIT. THE ACTION IS TO ALLOCATE
; THE MAPS AND DATAPATHS FOR THE BASE TABLE AND RECEIVES. ONCE THIS IS DONE,
; THE UNIT IS MASTER CLEARED AND THE BASE TABLE AND MODE ARE SET UP.
; THE THE FREE LIST IS FILLED AND THE FIRST RECEIVE STARTED.
;
; IF A FAILURE OCCURS THE UNIT SHUTDOWN SEQUENCE IS ENTERED.
;
; INPUTS:
;
;	R3 = I/O PACKET
;	R5 = UCB ADDRESS
;
; IMPLICIT INPUTS:
;
;	IRP$L_MEDIA CONTAINS A COPY OF THE MODE BUFFER SPECIFIED BY THE USER.
;	IRP$L_SVAPTE CONTAINS THE ADDRESS OF THE ALLOCATED BASE TABLE.
;	IRP$W_BOFF CONTAINS THE QUOTA TAKEN FROM THE USER FOR THE UNIT.
;
; OUTPUTS:
;
;	THE REQUEST IS COMPETED.
;--
START:					; START PROTOCOL OPERATION
	BSBW	CHANGE_MODE		; INSERT NEW CHARACTERISTICS
	SUBW3	#256,IRP$W_BOFF(R3),UCB$W_XM_QUOTA(R5); CALC AND SAVE FREE LIST QUOTA
	MOVL	IRP$L_PID(R3),UCB$L_PID(R5); SAVE STARTER'S PID
;
; IF THE I/O PACKET QUOTA ADJUSTMENT IS ZEROED, THEN THE QUOTA IS CONSISTENT
;
	CLRW	IRP$W_BOFF(R3)		; SET NO QUOTA CHANGE ON THIS I/O
;
; SAVE UCB MAP AND DATA PATH INFO. THIS DATA IS USED IN TRANSMIT FUNCTIONS
; IF THE DATA SHOWS THAT THIS UNIT IS PERMANENTLY ALLOCATED DATA PATH 0
; THEN NO ALLOCATION TAKES PLACE
;
	MOVL	IRP$L_SVAPTE(R3),UCB$L_XM_BASAD(R5); SAVE ADDRESS OF THE BASE TABLE
	CLRL	IRP$L_SVAPTE(R3)	; SET NO BLOCK FOR I/O POST
	MOVW	UCB$W_DEVBUFSIZ(R5),UCB$W_BCNT(R5); SET UP TO ALLOCATE THE RECEIVE MAPS
	MOVW	#511,UCB$W_BOFF(R5)	; SET WORST CASE PAGE CROSS
	MOVL	UCB$L_CRB(R5),R4	; ADDRESS UNIT CRB
	MOVW	CRB$L_INTD+VEC$W_MAPREG(R4),UCB$L_XM_ERRFKB(R5); SAVE XMIT MAP AND DP DATA
	MOVB	CRB$L_INTD+VEC$B_DATAPATH(R4),UCB$L_XM_ERRFKB+4(R5)
	CMPB	#1@VEC$V_PATHLOCK,CRB$L_INTD+VEC$B_DATAPATH(R4); PERM DP=0?
	BEQL	50$			; IF EQL THEN NO MORE ALLOCATION NEEDED
	CLRB	CRB$L_INTD+VEC$B_DATAPATH(R4); RESET DATA
	BSBW	IOC$REQDATAP		; REQUEST DATA PATH FOR FIRST RECEIVE BLOCK
	MOVB	CRB$L_INTD+VEC$B_DATAPATH(R4),UCB$Q_XM_CURCV1(R5); SAVE DP NUMBER
	BSBW	IOC$REQDATAP		; REQUEST DATA PATH FOR SECOND RECEIVE BLOCK
	MOVB	CRB$L_INTD+VEC$B_DATAPATH(R4),UCB$Q_XM_CURCV2(R5); SAVE DP NUMBER
;
; ALLOCATE MAP REGISTERS FOR RECEIVE BLOCKS AND BASE TABLE. THE BASE TABLE
; IS MAPPED USING DATA PATH 0
;
50$:	CLRW	CRB$L_INTD+VEC$W_MAPREG(R4); SET NO MAP ALLOCATED
	BSBW	IOC$REQMAPREG		; REQUEST THE MAP REGISTERS
	MOVW	CRB$L_INTD+VEC$W_MAPREG(R4),UCB$Q_XM_CURCV1+2(R5); SAVE DATA
	BSBW	IOC$REQMAPREG		; REQUEST OTHER SET
	MOVW	CRB$L_INTD+VEC$W_MAPREG(R4),UCB$Q_XM_CURCV2+2(R5); SAVE DATA
;
; MAP BASE TABLE
;
	EXTZV	#0,#9,UCB$L_XM_BASAD(R5),UCB$W_BOFF(R5); GET PAGE OFFSET FOR BASE TABLE
	MOVW	#256,UCB$W_BCNT(R5)	; SET SIZE
	BSBW	IOC$REQMAPREG		; REQUEST MAP REGISTER FOR BASE TABLE
	MOVW	CRB$L_INTD+VEC$W_MAPREG(R4),UCB$W_XM_BASMP(R5)
	EXTZV	#0,#VEC$V_MAPLOCK,UCB$W_XM_BASMP(R5),R2; GET MAP REG NUMBER
	MOVL	@CRB$L_INTD+VEC$L_ADP(R4),R1; ADDRESS ADAPTER
	MOVAL	UBA$L_MAP(R1)[R2],R2	; ADDRESS MAP REGISTER SET
	EXTZV	#VA$V_VPN,#VA$S_VPN,UCB$L_XM_BASAD(R5),R1; GET VPN FROM BUFFER ADDRESS
	MOVAL	@MMG$GL_SPTBASE[R1],R1	; ADDRESS SYSTEM PAGE TABLE BASE
	MOVL	(R1)+,R3		; START WITH PTE
	BLSS	55$			; IF LSS THEN VALID
	BSBW	IOC$PTETOPFN		; GET VALID PTE
55$:	INSV	#^X0400,#21,#11,R3	; INSERT UBA BITS
	MOVL	R3,(R2)+		; LOAD MAP
	MOVL	(R1)+,R3		; START WITH PTE
	BLSS	60$			; IF LSS THEN VALID
	BSBW	IOC$PTETOPFN		; GET VALID PTE
60$:	INSV	#^X0400,#21,#11,R3	; INSERT UBA BITS
	MOVL	R3,(R2)			; LOAD MAP
	MOVZWL	UCB$W_BOFF(R5),R1	; GET OFFSET OF BASE TABLE
	INSV	CRB$L_INTD+VEC$W_MAPREG(R4),#9,#7,R1;
	EXTZV	#7,#2,CRB$L_INTD+VEC$W_MAPREG(R4),R0; GET HIGH TWO BITS
	INSV	R0,#30,#2,R1		; INSERT IN RESULT
	MOVW	UCB$L_XM_ERRFKB(R5),CRB$L_INTD+VEC$W_MAPREG(R4)
	MOVB	UCB$L_XM_ERRFKB+4(R5),CRB$L_INTD+VEC$B_DATAPATH(R4)
	MOVL	@CRB$L_INTD+VEC$L_IDB(R4),R4; GET THE CSR
	DSBINT	UCB$B_DIPL(R5)		; LOCK OUT INTERRUPTS FROM DEVICE
	MOVW	#XM_I_M_MASTCLR,(R4)	; MASTER CLEAR THE UNIT
	CLRW	2(R4)			;
	CLRW	4(R4)
	CLRW	6(R4)
	BSBW	SYNCH_UNIT		; LOOP TO AWAIT SYNC
	BBC	#XM$V_CHR_LOOPB,UCB$L_DEVDEPEND(R5),70$; BR IF NO LOOP BACK
	BISW	#XM_I_M_LOOPB,(R4)	; SET LOOP BACK
	BSBW	SYNCH_UNIT		; AWAIT UNIT
70$:	BISW	#XM_I_M_RQSTI!3,(R4)	; REQUEST BASE IN
	BISW	#XM_I_M_RQSTI!3,(R4)	; REQUEST BASE IN
	BSBB	SYNCH_UNIT		; AWAIT UNIT
	BITW	#^X080,(R4)		; READY?
	BEQL	100$			; IF EQL THEN NO
	MOVW	R1,XM_PORT(R4)		; INSERT DATA
	ASHL	#-16,R1,R1		; ADJUST FOR OTHER WORD
	MOVW	R1,XM_PORT+2(R4)	; INSERT
	BSBW	SYNCH_UNIT		; AWAIT UNIT
	BICW	#XM_I_M_RQSTI,(R4)	; FREE PORT
	BSBW	SYNCH_UNIT		; AWAIT UNIT
	BISW	#XM_I_M_RQSTI!1,(R4)	; REQUEST CONTROL I
	BISW	#XM_I_M_RQSTI!1,(R4)	; REQUEST CONTROL I
	BSBB	SYNCH_UNIT		; AWAIT UNIT
	BITW	#^X080,(R4)		; READY?
	BEQL	100$			; IF EQL THEN NO
	CLRW	XM_PORT(R4)
	BICW3	#^C<XM$M_CHR_MOP!XM$M_CHR_HDPLX!XM$M_CHR_SLAVE>,-
		UCB$L_DEVDEPEND(R5),XM_PORT+2(R4); SET MODE DATA
	BICW	#XM_I_M_RQSTI,(R4)	; FREE PORT
	BSBB	SYNCH_UNIT		; SYNCH THE UNIT
	BISW	#XM_I_M_INTEN,(R4)	; ENABLE INTERRUPT
	BISW	#XM_I_M_INTEN,2(R4)	; ENABLE INTERRUPT
	MOVW	XM_I_CSR(R4),UCB$L_XM_LSTCSR(R5); SAVE CSR AND PORT
	MOVW	XM_O_CSR(R4),UCB$L_XM_LSTCSR+2(R5);
	MOVW	XM_PORT(R4),UCB$L_XM_LSTPRT(R5);
	MOVW	XM_PORT+2(R4),UCB$L_XM_LSTPRT+2(R5);
	BISB	#3,UCB$W_DEVSTS(R5)	; SET BOTH BLOCKS AVAIL FOR RECEIVE
	ENBINT				; RETURN TO FORK IPL
	BSBW	FILLFREELIST		; FILL THE FREE LIST AND START THE FIRST RECEIVE
	MOVL	UCB$L_IRP(R5),R3	; GET THE CURRENT PACKET
	BISW	#XM$M_STS_ACTIVE,UCB$L_DEVDEPEND(R5); SET DEVICE ACTIVE
	BRW	REQNORMAL		; COMPLETE THE REQUEST
;
; ERROR IN STARTING DEVICE
;
100$:					;
	ENBINT				; ENABLE INTERRUPTS
	BRB	STOP			; SHUT THE UNIT DOWN
;
; WAIT ROUTINE
;
; THE STACK CONTAINS A RETURN ADDRESS AND AN IPL
;
SYNCH_UNIT:
	SETIPL	4(SP)			; RE-ENABLE INTERRUPTS
	MOVZWL	#^X0400,R0		; SET LOOP COUNT
10$:	SOBGTR	R0,10$
	SETIPL	UCB$B_DIPL(R5)		; RESET IPL
	RSB				; AND RETURN

	.SBTTL	CHANGE MODE AND CHARACTERISTIC DATA
;++
; CHANGE - CHANGE MODE AND CHARACTERISTICS FUNCTION PROCESSING
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS ENTERED FOR CHANGING THE MODE AND CHARACTERISTICS ON AN IDLE
; UNIT:
;
; INPUTS:
;
;	R3 = ADDRESS OF THE PACKET
;	R5 = UCB ADDRESS
;
; OUTPUTS:
;
;	THE I/O IS COMPLETED VIA REQNORMAL
;--
CHANGE:
	BSBB	CHANGE_MODE		; INSERT NEW DATA
	BRW	REQNORMAL		; COMPLETE
CHANGE_MODE:
	MOVL	IRP$L_MEDIA+2(R3),UCB$W_DEVBUFSIZ(R5); INSERT NEW BUFFER SIZE AND MODE
RESET_UCB:				; RESET UCB DATA STORAGE
	MOVAB	UCB$Q_XM_RCVS(R5),UCB$Q_XM_RCVS(R5); SET UP EMPTY RECEIVE LIST
	MOVAB	UCB$Q_XM_RCVS(R5),UCB$Q_XM_RCVS+4(R5);
	CLRL	UCB$L_XM_AST(R5)	; SET EMPTY ATTN AST LIST
	MOVAB	UCB$Q_XM_FREE(R5),UCB$Q_XM_FREE(R5); SET UP EMPTY FREE LIST
	MOVAB	UCB$Q_XM_FREE(R5),UCB$Q_XM_FREE+4(R5);
	MOVAB	UCB$Q_XM_ATTN(R5),UCB$Q_XM_ATTN(R5); SET UP EMPTY ATTN LIST
	MOVAB	UCB$Q_XM_ATTN(R5),UCB$Q_XM_ATTN+4(R5);
	CLRL	UCB$L_XM_BASAD(R5)	; NO BASE TABLE
	CLRW	UCB$L_DEVDEPEND+2(R5)	; NO ERROR STATE
	CLRB	UCB$L_DEVDEPEND+1(R5)	; SET NO STATUS
	CLRW	UCB$W_DEVSTS(R5)	;
	CLRW	UCB$W_XM_QUOTA(R5)	; SET NO ASSOCIATED QUOTA
	CLRQ	UCB$Q_XM_CURCV1(R5)	; RESET BOTH CURRENT RCV CONTROL BLOCKS
	CLRQ	UCB$Q_XM_CURCV2(R5)
	MOVB	UCB$B_FIPL(R5),UCB$B_XM_EIPL(R5)
	RSB

	.SBTTL	STOP - SHUT DOWN UNIT I/O FUNCTION
;++
; STOP - SHUT DOWN UNIT FUNCTION PROCESSING
;
; FUNCTIONAL DESCRIPTION:
;
; THIS FUNCTION SHUTS THE UNIT DOWN, RETURNING ALL BUFFERS, CANCELLING I/O
; AND STOPING THE PROTOCOL.
;
; INPUTS:
;
;	R3 = I/O PACKET ADDRESS
;	R5 = UCB ADDRESS
;
; OUTPUTS:
;
;	I/O COMPLETED VIA REQNORMAL
;--
STOP:					; SHUT DOWN UNIT
	BICW	#UCB$M_BSY,UCB$W_STS(R5); SET NOT BUSY TO AVOID THIS BEING CANCELED
	BSBW	SHUTDOWN		; SHUT DOWN THE UNIT
	MOVL	UCB$L_IRP(R5),R3	; RESTORE PACKET ADDRESS
	BSBB	CHANGE_MODE		; CHANGE MODE
	BRW	REQNORMAL		; COMPLETE FUNCTION

	.SBTTL	XM$ACPRCV - PRIVILEGED RECEIVE SUBROUTINE
;++
; XM$ACPRCV - ENTRY FOR PRIVILEGED SOFTWARE TO RECEIVE MESSAGES
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS USED BY PRIVILEGED SOFTWARE TO RECEIVE MESSAGES
; WITHOUT PERFORMING READ QIO'S. THE NEXT AVAILABLE MESSAGE BLOCK
; IS TAKEN FROM THE ATTENTION QUEUE AND GIVEN TO THE CALLER. THE SIZE
; AND OFFSET TO THE DATA ARE SETUP IN BLOCK+12 AND +14.
;
; INPUTS:
;
;	R5 = ADDRESS OF THE UCB
;
; KERNEL MODE AT ANY IPL LOWER THAN DEVICE FORK IPL.
;
; OUTPUTS:
;
;	R0 = 0 IF NO BLOCK , 1 IF FOUND
;	R2 = BLOCK ADDRESS IF FOUND
;	R5 = UCB ADDRESS
;
;	R1,R3,R4 ARE  PRESERVED.
;--
XM$ACPRCV::				; 
	PUSHR	#^M<R1,R3,R4>		; SAVE REGISTERS
	DSBINT	UCB$B_FIPL(R5)
	CLRL	R0			; ASSUME NO RETURN
	REMQUE	@UCB$Q_XM_ATTN(R5),R2	; GET A BLOCK
	BVS	40$			; IF V-SET THEN NONE
	ADDW	UCB$W_DEVBUFSIZ(R5),UCB$W_XM_QUOTA(R5); ADJUST QUOTA FOR ALLOCATION
	PUSHL	R2			; SAVE REGISTERS
	BSBB	FILLFREELIST		; FILL FREE LIST AND START RECEIVE
	POPL	R2			; RESTORE BLOCK ADDRESS
	MOVZBW	#MSG_DATA,14(R2)	; SET UP OFFSET AND DATA SIZE
	BICW3	#^X0C000,MSG_PORT+2(R2),12(R2);
	MOVZBL	#1,R0			; SET SUCCESS
40$:	ENBINT				;
	POPR	#^M<R1,R3,R4>
	RSB

	.SBTTL	FILLFREELIST - FILL MESSAGE FREE LIST
;++
; FILLFREELIST - FILL MESSAGE BLOCK FREE LIST
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE FILLS THE MESSAGE FREE LIST UP TO THE QUOTA ALLOCATED
; AT UNIT INITIALIZATION.
;
; INPUTS:
;
;	R5 = UCB
; 
; OUTPUT:
;
;	R5 = UCB ADDRESS
;--
FILLFREELIST:				; FILL FREE LIST
	CMPW	UCB$W_DEVBUFSIZ(R5),UCB$W_XM_QUOTA(R5); ENOUGH SPACE?
	BGTRU	100$			; IF GTRU THEN NO, STOP LOOP
	CLRL	R1			; ZERO SIZE REGISTER
	ADDW3	#MSG_DATA,UCB$W_DEVBUFSIZ(R5),R1; GET BLOCK SIZE
	BSBW	EXE$ALONONPAGED		; ALLOCATE THE MEMORY
	BLBC	R0,100$			; IF FAILURE THEN DONE
	MOVW	R1,MSG_BLKSIZE(R2)	; INSERT SIZE
	MOVB	#DYN$C_NET,MSG_BLKTYPE(R2); INSERT TYPE
	MOVB	UCB$B_FIPL(R5),MSG_FIPL(R2); INSERT FUTURE FORK IPL
	INSQUE	(R2),UCB$Q_XM_FREE(R5)	; INSERT BLOCK ON LIST
	SUBW	UCB$W_DEVBUFSIZ(R5),UCB$W_XM_QUOTA(R5); REMOVE BLOCK FROM QUOTA
	BRB	FILLFREELIST		; CONTINUE
100$:	TSTB	UCB$W_DEVSTS(R5)	; ANY RCV CONTROL BLOCKS AVAIL?
	BNEQ	STARTRECEIVE		; IF NEQ THEN START RECEIVE
	RSB				; OTHERWISE RETURN

	.SBTTL	STARTRECEIVE - START RECEIVE ON UNIT
;++
; STARTRECEIVE - START RECEIVE ON IDLE UNIT
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE REMOVES A FREE MESSAGE BLOCK FROM THE FREE LIST AND IF ONE IS
; PRESENT, LOADS THE UBA TO MAP IT AND SETS THE STATE TO REQUEST A RECEIVE START.
;
; INPUTS:
;
;	R5 = UCB ADDRESS
;
; OUTPUTS:
;
;	CONTROL IS PASSED TO RQSTRCVSTRT.
;
;	R4 = CSR ADDRESS
;	R5 = UCB ADDRESS
;--
STARTRECEIVE:				; START RECEIVE OPERATION
	FFS	#0,#8,UCB$W_DEVSTS(R5),R4; GET FIRST AVAILABLE CONTROL BLOCK
	BEQL	1$			; BR IF NO BIT FOUND
	REMQUE	@UCB$Q_XM_FREE(R5),R2	; REMOVE FREE BLOCK FROM LIST
	BVC	5$			; IF V-SET THEN NONE
1$:	RSB
5$:	BBCC	R4,UCB$W_DEVSTS(R5),10$	; SET RCV CONTROL BLOCK NOT AVAILABLE
10$:	MOVAQ	UCB$Q_XM_CURCV1(R5)[R4],R4; ADDRESS CONTROL BLOCK
	MOVL	R2,4(R4)		; SAVE BLOCK ADDRESS
	MOVB	#XM_RCV_M_INUS,1(R4)	; SET CONTROL BLOCK INUSE BUT IDLE
	MOVW	UCB$W_DEVBUFSIZ(R5),MSG_UBVA+2(R2); INSERT SIZE
	MOVAB	MSG_DATA(R2),R1		; ADDRESS BUFFER ITSELF
	MOVW	R1,MSG_UBVA(R2)		; INSERT BUFFER OFFSET
	INSV	2(R4),#9,#7,MSG_UBVA(R2); INSERT MAP REGISTER DATA
	EXTZV	#7,#2,2(R4),R0		; GET TWO HIGH BITS
	INSV	R0,#30,#2,MSG_UBVA(R2)	; INSERT IN UBVA
;
; BUILD UBA MAP REGISTER DATA AND LOAD MAP
;
	EXTZV	#0,#VEC$V_MAPLOCK,2(R4),R0; GET MAP REG NUMBER TO START
	MOVL	UCB$L_CRB(R5),R2	; ADDRESS ADAPTER
	MOVL	@CRB$L_INTD+VEC$L_ADP(R2),R2;
	MOVAL	UBA$L_MAP(R2)[R0],R2	; ADDRESS MAP IN ADAPTER
	PUSHL	R6			;
	MOVZWL	UCB$W_DEVBUFSIZ(R5),R6	; GET NUMBER OF MAPS TO LOAD
	EXTZV	#VA$V_BYTE,#VA$S_BYTE,R1,R0; GET OFFSET OF BUFFER
	MOVAB	^X01FF(R6)[R0],R6	; CALC HIGHEST RELITIVE ADDRESS
	ASHL	#-9,R6,R6		; GET NUMBER OF MAPS TO LOAD
	MOVZBL	(R4),R0			; GET DATA PATH NUMBER
	BLBC	R1,15$			; IF LOW CLEAR THEN BYTE ALIGNED
	BISB	#^X040,R0		; INSERT BYTE BIT FOR UBA
15$:	BISW	#^X0400,R0		; SET VALID
	EXTZV	#VA$V_VPN,#VA$S_VPN,R1,R1; GET PTE INDEX OF VA
	MOVAL	@MMG$GL_SPTBASE[R1],R1	; ADDRESS PTE FOR BUFFER
20$:	MOVL	(R1)+,R3		; GET PTE
	BLSS	25$			; IF LESS THEN VALID
	BSBW	IOC$PTETOPFN		; VALIDATE IT
25$:	INSV	R0,#21,#11,R3		; MERGE UBA DATA BITS
	MOVL	R3,(R2)+		; LOAD MAP
	SOBGTR	R6,20$			; CONTINUE UNTIL DONE
	POPL	R6			; RESTORE REGISTER
	BRW	RQSTRCVSTRT		; START RECEIVE

	.SBTTL	XM$PORTINT - DMC-11 PORT READY INTERRUPT SERVICE
;++
; XM$PORTINT - DMC-11 PORT READY INTERRUPT SERVICE
;
; FUNCTIONAL DESCRIPTION:
;
; THIS INTERRUPT OCCURS WHEN THE PORT IS READY FOR THE DRIVER TO PASS DATA
; TO THE DMC-11. PRIOR TO THIS, THE REQUEST FOR THE PORT WAS MADE BY
; RQSTRCVSTRT/RQSTXMITSTRT. AFTER THE PORT IS LOADED, IT IS FREED. IF MORE
; RECEIVE BUFFERS ARE FREE THEN ANOTHER PORT LOAD IS REQUESTED.
;
; INPUTS:
;
;	0(SP) = ADDRESS OF THE UNIT IDB ADDRESS
;
;	R1,R2,R3,R4,R5 ARE AT 4(SP)
;
; OUTPUTS:
;
;	INTERRUPT IS DISMISSED.
;--
XM$PORTINT::				; DMC11 INPUT PORT AVAILABLE
	MOVL	@(SP)+,R4		; GET IDB ADDRESS
	MOVQ	(R4),R4			; R4= CSR, R5= UCB
	BICB	#UCB$M_TIM,UCB$W_STS(R5); CLEAR TIME OUT EXPECTED
	BBCC	#UCB$V_INT,UCB$W_STS(R5),100$; EXIT IF NOT EXPECTED
	CLRL	R3
	BICW3	#^X0FFFB,XM_I_CSR(R4),R3; GET THE CSR DATA
	BEQL	80$			; IF EQL THEN TRANSMIT
	MOVAB	UCB$Q_XM_CURCV1(R5),R3	; ADDRESS FIRST CONTROL BLOCK
	BBS	#XM_RCV_V_INUS,1(R3),20$; BR IF FIRST BLOCK AVAIL
	ADDL	#8,R3			; POINT TO NEXT BLOCK
	BBC	#XM_RCV_V_INUS,1(R3),60$; BR IF THIS BLOCK NOT AVAIL
20$:	MOVB	#XM_RCV_M_INPR,1(R3)	; SET CONTROL BLOCK IN PROGRESS
	MOVL	4(R3),R2		; LOAD PORT
	MOVW	MSG_UBVA(R2),XM_PORT(R4); LOAD PORT
	MOVW	MSG_UBVA+2(R2),XM_PORT+2(R4);
60$:	BICW	#XM_I_M_RQSTI,XM_I_CSR(R4); FREE PORT
	BBC	#XM_RCV_V_INUS,UCB$Q_XM_CURCV2+1(R5),INTEXIT; IS SECOND BLOCK STILL AVAIL?
	BSBB	RQSTRCVSTRT		; REQUEST RECEIVE START
	BRB	INTEXIT			; CONTINUE
80$:	BBCC	#XM_DS_V_XMTPND,UCB$W_DEVSTS(R5),100$; CLEAR AND BR IF NOT ASSERTED
	MOVL	UCB$L_IRP(R5),R3	; GET PACKET ADDRESS
	MOVW	IRP$L_MEDIA(R3),XM_PORT(R4); LOAD PORT
	MOVW	IRP$L_MEDIA+2(R3),XM_PORT+2(R4);
100$:	BICW	#XM_I_M_RQSTI,XM_I_CSR(R4); FREE PORT
INTEXIT:				; EXIT INTERRUPT
	POPR	#^M<R1,R2,R3,R4,R5>	;
	REI				;

	.SBTTL	RQSTRCVSTRT/RQSTXMITSTRT
	.ENABL	LSB
;++
; RQSTRCVSTRT - REQUEST PORT FOR RECEIVE START
; RQSTXMITSTRT - REQUEST PORT FOR XMIT START
;
; FUNCTIONAL DESCRIPTION:
;
; EACH OF THESE ROUTINES SETS UP THE PROPER PORT DATA, SETS THE STATE BIT,
; SETS THE UNIT IN A WAIT FOR INTERRUPT STATE ( 5 SECOND TIMEOUT ) AND
; REQUESTS THE PORT.
;
; INPUTS:
;
;	R5 = UCB
;
; OUTPUTS:
;
;	R5 = UCB
;--
RQSTRCVSTRT:				; REQUSET RECEIVE START
	MOVZBL	#XM_I_M_RCV,R2		; SET UP CSR
	BRB	50$			; CONTINUE IN COMMON
RQSTXMITSTRT:				; REQUEST XMIT START
	CLRL	R2			; SET DIRECTION
	BISW	#XM_DS_M_XMTPND,UCB$W_DEVSTS(R5)
;
; SET UP WAIT FOR NEXT INTERRUPT
;
50$:	MOVL	UCB$L_CRB(R5),R4	; GET CRB ADDRESS
	MOVL	@CRB$L_INTD+VEC$L_IDB(R4),R4; GET CSR ADDRESS
	DSBINT	UCB$B_DIPL(R5)		; LOCK OUT DEVICE INTERRUPTS
	BITB	#UCB$M_INT,UCB$W_STS(R5); INTERRUPT EXPECTED?
	BNEQ	120$			; IF NEQ THEN YES
	SETIPL	#IPL$_POWER		; RAISE TO LOCK OUT ALL INTERRUPTS
	ADDL3	#5,EXE$GL_ABSTIM,UCB$L_DUETIM(R5); SET UP TIMER
	BISB	#UCB$M_TIM!UCB$M_INT,UCB$W_STS(R5); SET TIMER
	BBCC	#UCB$V_TIMOUT,UCB$W_STS(R5),100$; CLEAR TIMED OUT
100$:	BISW	#XM_I_M_RQSTI,R2	; SET UP NEW CSR
	BISW	R2,(R4)			; MAKE REQUEST
	BISW	R2,(R4)			; AND AGAIN
120$:	ENBINT				; ENABLE INTERRUPTS
	RSB				; RETURN
 
	.DSABL	LSB

	.SBTTL	XM$CTRLINT - DMC11 CONTROL INTERRUPT
;++
; XM$CNTRLINT - DMC11 CONTROL INTERRUPT
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS THE INTERRUPT SERVICE FOR DMC11 CONTROL INTERRUPTS.
; THE INTERRUPTS CAN SIGNAL RECEIVE OR TRANSMIT BUFFER DONE AND ERRORS.
;
; INPUTS:
;
;	00(SP) = ADDRESS OF UNIT IDB ADDRESS
;
;	R1,R2,R3,R4,R5 ARE AT 4(SP)
;
; OUTPUTS:
;
;	THE INTERRUPT IS DISMISSED
;
; IMPLICIT OUTPUTS:
;
;	IF THE INTERRUPT SIGNALS AN ERROR,
;		THE PORT IS HELD AND A FORK IS DONE ON THE UCB CONTEXT
;		TO THE ERROR PROCESSING LOGIC.
;
;	IF THE INTERRUPT SIGNALS RECEIVE DONE,
;		THE PORT IS FREED.
;		THE NEXT RECEIVE IS STARTED IF POSSIBLE.
;		A FORK IS CREATED USING THE MESSAGE BLOCK TO COMPLETE THE OPERATION.
;
;	IF THE INTERRUPT SIGNALS XMIT DONE,
;		THE PORT IS FREED. A FORK TO PROCESS THE COMPLETED MESSAGE
;		IS DONE ON THE CURRENT I/O PACKET.
;--
XM$CTRLINT::				; DMC11 CONTROL INTERRUPT
	MOVL	@(SP),R4		; R4= CSR, R5= UCB
	MOVQ	(R4),R4			;
	MOVAB	B^INTEXIT,(SP)		; SET UP RETURN ADDRESS
	MOVW	XM_O_CSR(R4),R2		; GET ENTIRE CSR
	ASHL	#16,R2,R2		; SHIFT
	MOVW	XM_I_CSR(R4),R2		; AND LOAD LOW
	MOVW	XM_PORT+2(R4),R3	; GET ENTIRE PORT
	ASHL	#16,R3,R3
	MOVW	XM_PORT(R4),R3		;
	BITL	#<^X03>@16,R2		;
	BEQL	10$			; IF EQL THEN YES
5$:	MOVAB	UCB$L_XM_ERRFKB(R5),R5	; ADDRESS FORK BLOCK FOR ERROR FORK
	PUSHAB	W^ERRORFORK		; SET UP FORK PROCESS ADDR
	MOVL	R2,R4			; SAVE CSR'S AS DATA IN FORK
	BRW	EXE$FORK		; FORK
10$:	MOVL	R2,UCB$L_XM_LSTCSR(R5)	; SAVE PORT AND CSR
	MOVL	R3,UCB$L_XM_LSTPRT(R5)	;
	BICW	#XM_O_M_READY,XM_O_CSR(R4); FREE DATA PORT
	BITL	#XM_O_M_RCV@16,R2	; WAS IT XMIT DONE?
	BEQL	40$			; IF EQL THEN YES
	MOVL	R3,R1			; COPY PORT DATA
	INSV	UCB$W_DEVBUFSIZ(R5),#16,#14,R1; RESTORE ORIGINAL DATA
	MOVAB	UCB$Q_XM_CURCV1(R5),R4	; ADDRESS CONTROL BLOCK
	BBC	#XM_RCV_V_INPR,1(R4),15$; BR IF NOT IN USE
	MOVL	4(R4),R2		; ADDRESS BLOCK
	CMPL	R1,MSG_UBVA(R2)		; IS THIS THE BLOCK?
	BEQL	20$			; IF EQL THEN YES
15$:	MOVAB	UCB$Q_XM_CURCV2(R5),R4	; ADDRESS OTHER CONTROL BLOCK
	BBC	#XM_RCV_V_INPR,1(R4),120$; BR IF NOT IN PROGRESS
	MOVL	4(R4),R2		; ADDRESS BLOCK
	CMPL	R1,MSG_UBVA(R2)		; THIS MESSAGE
	BNEQ	120$			; IF NEQ THEN NO
20$:	CLRB	1(R4)			; FREE CONTROL BLOCK
	PUSHL	R2			; SAVE BLOCK ADDRESS
	PUSHR	#^M<R3,R5>		; SAVE PORT DATA AND UCB
	MOVZBL	(R4),R2			; GET DATA PATH NUMBER
	BSBB	200$			; PURGE DATA PATH AND CHECK FOR ERROR
	MOVAB	UCB$Q_XM_CURCV1(R5),R1	; ADDRESS BLOCK STARTS
	SUBL	R1,R4			; GET BLOCK NUMBER
	BBSS	R4,UCB$W_DEVSTS(R5),25$	; SET CONTROL BLOCK FREE
25$:	BSBW	STARTRECEIVE		; START A NEW RECEIVE IF POSSIBLE
	MOVL	R5,R4			; SAVE UCB IN FORK
	POPR	#^M<R3,R4,R5>		; R3 = PORT DATA, R4 = UCB, R5 = BLOCK ADDR
	PUSHAB	B^RCVDONEFORK		; SET UP FORK PROCESS ADDR
	BRW	EXE$FORK		; FORK
40$:	BITW	#UCB$M_BSY,UCB$W_STS(R5); SKIP IF NOT BUSY
	BEQL	120$			; IF EQL THEN NOT BUSY
	MOVL	UCB$L_CRB(R5),R1	; GET DATA PATH USED
	MOVZBL	CRB$L_INTD+VEC$B_DATAPATH(R1),R2;
	BSBB	200$			; PURGE DATA PATH AND CHECK FOR ERROR
	MOVL	R5,R4			; SAVE UCB IN FORK
	MOVL	UCB$L_IRP(R5),R5	; ADDRESS CURRENT PACKET
	MOVL	R3,IRP$W_BOFF(R5)	; SAVE TRANSFER DATA
	MOVL	IRP$L_PID(R5),IRP$L_MEDIA(R5);SAVE PACKET DATA FROM FORK
	MOVB	IRP$B_RMOD(R5),IRP$L_MEDIA+4(R5); SAVE RMODE
	MOVB	UCB$B_FIPL(R4),IRP$B_RMOD(R5); SET IPL FOR FORK
	MOVQ	IRP$L_AST(R5),R3	;
	PUSHAB	W^XMITDONEFORK		; XMIT DONE FORK
	BRW	EXE$FORK		; FORK
120$:	RSB				; RETURN
;
; PURGE DATA PATH AND CHECK FOR ERROR IN TRANSFER
;
200$:	MOVL	UCB$L_CRB(R5),R1	; ADDRESS ADAPTER
	MOVL	@CRB$L_INTD+VEC$L_ADP(R1),R1;
	ASHL	#UBA$V_DPR_BNE,#1,UBA$L_DPR(R1)[R2]
	ASHL	#31-UBA$V_DPR_XMTER,UBA$L_DPR(R1)[R2],R1; ANY ERROR?
	BGEQ	210$			; IF GEQ THEN NO
	MOVL	UCB$L_XM_LSTCSR(R5),R2	; GET LAST CSR
	ASHL	#XM_E_V_TRNER,#1,R3	;
	MOVAB	W^5$,(SP)		; CHANGE RETURN ADDRESS
210$:	RSB

	.SBTTL	RCVDONEFORK - RECEIVE DONE FORK PROCESS
;++
; RCVDONEFORK - RECEIVE DONE FORK PROCESS 
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS ENTERED AT DEVICE FORK LEVEL WHEN A RECEIVE BUFFER DONE
; HAS OCCURED. THE COMPLETED MESSAGE BLOCK IS USED AS THE FORK BLOCK.
;
; INPUTS:
;
;	R3 = PORT DATA
;	R4 = UCB ADDRESS
;	R5 = ADDRESS OF THE MESSAGE BLOCK OR FORK BLOCK
;
; OUTPUTS:
;
;	R5 = UCB ADDRESS
;
;--
	.WORD	RCVTIMEOUT-.
RCVDONEFORK:				; RECEIVE DONE FORK
	MOVL	R5,R2			; COPY ADDRESS OF THE MESSAGE BLOCK
	MOVL	R4,R5			; RESTORE UCB ADDRESS
	MOVZBL	#SS$_NORMAL,R0		; SET UP TRANSFER STATUS
	REMQUE	@UCB$Q_XM_RCVS(R5),R3	; REMOVE WIATING RECEIVE
	BVC	FINISHRCVQIO		; IF FOUND THEN FINISH THE I/O
	INSQUE	(R2),@UCB$Q_XM_ATTN+4(R5); QUEUE MESSAGE BLOCK
	BITW	#XM_DS_M_NOTIF,UCB$W_DEVSTS(R5); ALREADY NOTIFIED?
	BNEQ	40$			; IF NEQ THEN YES
	BITW	#XM$M_STS_MBX,UCB$L_DEVDEPEND(R5); ENABLED?
	BEQL	40$			; IF EQL THEN NO
	MOVL	UCB$L_AMB(R5),R3	; ADDRESS MAILBOX
	BEQL	40$			; IF EQL THEN NONE
	MOVZBL	#MSG$_XMUNSOLIC,R4	; SET MESSAGE TYPE
	BSBW	EXE$SNDEVMSG		; SEND MESSAGE
	BLBC	R0,40$			; IF LOW CLEAR THEN NOT SENT
	BISW	#XM_DS_M_NOTIF,UCB$W_DEVSTS(R5); SET NOTIFIED
40$:	BSBW	DEL_ATTN_AST		;
	RSB				; RETURN FROM FORK

	.SBTTL	FINISHRCVQIO - FINISH RECEIVE QIO PROCESSING
;++
; FINISHRCVQIO - FINISH RECEIVE QIO PROCESSING
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE COMPLETES A RECEIVE OPERATION THAT HAS BEEN MATCHED WITH A
; MESSAGE BLOCK. AFTER THE RECEIVE HAS BEEN COMPLETED THE MESSAGE FREE LIST IS FILLED
; AND A RECEIVE IS STARTED IF NEEDED.
;
; INPUTS:
;
;	R0 = STATUS OF THE OPERATION
;	R2 = ADDRESS OF MESSAGE BLOCK OR 0 IF THE I/O IS TO ABORTED
;	R3 = PACKET ADDRESS OF THE RCV QIO
;	R5 = UCB ADDRESS
;
; OUTPUTS:
;
;	R5 = UCB ADDRESS
;--
FINISHRCVQIO:				; FINISH RCV QIO PROCESSING
	CLRL	R1			; ASSUME NO TRANSFER
	MOVL	R2,IRP$L_SVAPTE(R3)	; INSERT ADDRESS OF THE BUFFERED BLOCK
	BEQL	10$			; IF EQL THEN NO MESSAGE BLOCK
	MOVAB	MSG_DATA(R2),(R2)	; SET UP BLOCK FOR DATA MOVE
	MOVL	IRP$L_MEDIA(R3),4(R2)	; INSERT SAVED USER VIRTUAL ADDRESS
	ADDW	UCB$W_DEVBUFSIZ(R5),UCB$W_XM_QUOTA(R5); ADJUST UNIT QUOTA
	PUSHAB	W^FILLFREELIST		; SET UP TRIP THROUGH FREELISTFILL
	BICW3	#^X0C000,MSG_PORT+2(R2),R1; GET SIZE OF TRANSFER
	CMPW	R1,IRP$W_BCNT(R3)	; REQUEST LARGER THAN ACTUAL?
	BGTRU	COMPLETE_IO		; BR IF NO
10$:	MOVW	R1,IRP$L_MEDIA+2(R3)	; SAVE TRANSFER COUNT
COMPLETE_IO:
	MOVL	UCB$L_DEVDEPEND(R5),IRP$L_MEDIA+4(R3);
	MOVW	R0,IRP$L_MEDIA(R3)	; SET STATUS
	BBC	#IRP$V_DIAGBUF,IRP$W_STS(R3),20$; BR IF NO DIAGNOSTIC BUFFER
	ADDL3	#8,@IRP$L_DIAGBUF(R3),R0; ADDRESS BUFFER PAST START TIME
	MOVQ	EXE$GQ_SYSTIME,(R0)+	; INSERT STOP TIME
	MOVZWL	UCB$W_ERRCNT(R5),(R0)+	; INSERT ERROR COUNTER
	BSBB	REGDUMP			; DUMP REGISTERS
20$:	BRW	COM$POST		; POST THE I/O

	.SBTTL	XMITDONEFORK - TRANSMIT DONE FORK PROCESS
;++
; XMITDONEFORK - TRANSMIT DONE FORK PROCESS
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS A FOR PROCESS TO COMPLETE TRANSMIT OPERATIONS.
; THE FORK BLOCK USED IS THE I/O PACKET.
;
; INPUTS:
;
;	R3,R4 CONTAIN IRP DATA
;	R5 = ADDRESS OF THE FORKBLOCK - I/O PACKET
;
; OUTPUTS:
;
;	THE REQUEST IS COMPLETED VIA REQCOM.
;--
	.WORD	XMTIMEOUT-.
XMITDONEFORK:				; TRANSMIT DONE FORK
	MOVQ	R3,IRP$L_AST(R5)	; SAVE FORK DATA
	MOVL	R5,R3			; COPY BLOCK ADDRESS
	MOVB	IRP$L_MEDIA+4(R3),IRP$B_RMOD(R3); RESTORE MODE
	MOVL	IRP$L_MEDIA(R3),IRP$L_PID(R3); RESTORE PID
	BICW	#^X0C000,IRP$W_BCNT(R3)	; SET UP TRANSFER SIZE
	BICW	#^C<VA$M_BYTE>,IRP$W_BOFF(R3); RESET I/O POST DATA
	MOVL	IRP$L_UCB(R3),R5	; GET UCB FOR UNIT
	BSBW	IOC$RELDATAP		; RELEASE DATA PATH
	BSBW	IOC$RELMAPREG		; RELEASE MAP REGISTER
REQNORMAL:				; COMPLETE A NORMAL REQUEST
	BSBW	IOC$DIAGBUFILL
	MOVL	IRP$W_BCNT-2(R3),R0	; SET UP FOR I/O DONE
	MOVZBW	#SS$_NORMAL,R0		; INSERT STATUS
	MOVL	UCB$L_DEVDEPEND(R5),R1	;
	REQCOM				; COMPLETE AND START NEXT

	.SBTTL	TIMEOUT ROUTINES
;
; DEVICE TIMEOUT ROUTINES
;
RCVTIMEOUT:
	MOVL	R4,R5			; GET UCB ADDRESS
	BRW	SHUTDOWN		; SHUTDOWN THE UNIT
XMTIMEOUT:
	MOVL	IRP$L_UCB(R5),R5	; GET THE UCB ADDRESS
	BRW	SHUTDOWN		; SHUT THE UNIT DOWN
ERRTIMEOUT:
	MOVAB	UCB$L_XM_ERRFKB(R5),R5	; GET THE UCB ADDRESS
	BRW	SHUTDOWN		; SHUT THE UNIT DOWN

	.SBTTL	REGDUMP - DMC-11 ERROR LOG AND DIAGNOSTICS REGISTER DUMP
;++
; REGDUMP - ERROR LOG AND DIAGNOSTICS REGISTER DUMP ROUTINE
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS USED TO RETURN THE DMC-11 ERROR LOG AND DIAGNOSTICS
; BUFFER ON ERROR OR DIAGNOSTIC QIO FUNCTION.
;
; INPUTS:
;
;	R0 = ADDRESS OF THE BUFFER
;	R5 = UCB ADDRESS OF THE UNIT
;
; OUTPUTS:
;
;	R0,R1 ARE USED
;	R5 = UCB ADDRESS OF THE UNIT
;--
REGDUMP:
	MOVZBL	#8,(R0)+		; INSERT NUMBER OF RETURNED LONG WORDS
	MOVL	UCB$L_XM_LSTCSR(R5),(R0)+; INSERT LAST CSR
	MOVL	UCB$L_XM_LSTPRT(R5),(R0)+; INSERT LAST PORT
	BBC	#XM$V_STS_ACTIVE,UCB$L_DEVDEPEND(R5),20$; BR IF NOT ACTIVE
	MOVL	UCB$L_XM_BASAD(R5),R1	; ADDRESS UNIT BASE TABLE
	MOVL	3(R1),(R0)+		; RETURN 8 BYTES OF ERROR COUNTERS
	MOVL	7(R1),(R0)+		;
	BRB	30$			; CONTINUE
20$:	CLRQ	(R0)+			; RETURN NO ERRORS
30$:	CLRQ	(R0)+			;
	CLRQ	(R0)			;
	RSB

	.SBTTL	ERRORFORK - ERROR FORK  RPOCESS
;++
; ERRORFORK - ERROR PROCESSING FORK
;
; FUNCTIONAL DESCRIPTION:
;
; THIS IS THE FORK PROCESS THAT HANDLES ERRORS ON THE DMC UNIT.
; THE ACTION IS TO SIGNAL THE OWNER PROCESS VIA MAILBOX OR AST OF THE 
; ATTENTION CONDITION. AND THEN, IF THE ERROR IS FATAL, SHUT DOWN THE UNIT.
; 
;
; INPUTS:
;
;	R3 = PRE FORT PORT VALUE
;	R4 = PRE FORK CSR VALUE
;	R5 = ADDRESS OF THE UCB FORK BLOCK
;
; OUTPUTS:
;
;	NONE
;--
	.WORD	ERRTIMEOUT-.
ERRORFORK:				; ERROR PROCESSING FORK
	MOVAB	-UCB$L_XM_ERRFKB(R5),R5	; ADDRESS UCB
	BSBW	ERL$DEVICERR		; LOG THE ERROR
	INCW	UCB$W_ERRCNT(R5)	; ADJUST COUNT
	BITW	#<XM_E_M_PROCERR!-	; FATAL ERROR?
		XM_E_M_NONEXMEM!-
		XM_E_M_START!-
		XM_E_M_LOST!-
		XM_E_M_MOP>,UCB$L_XM_LSTPRT+2(R5)
	BNEQ	FATALERROR		; IF NEQ THEN YES
	BISB	UCB$L_XM_LSTPRT+2(R5),UCB$L_DEVDEPEND+1(R5); SAVE DATA
	BSBB	DEL_ATTN_AST		; TELL OWNER
	MOVL	UCB$L_CRB(R5),R1	; FREE DATA PORT
	MOVL	@CRB$L_INTD+VEC$L_IDB(R1),R1; GET CSR ADDRESS
	BICW	#XM_O_M_READY,XM_O_CSR(R1);
	RSB				; RETURN
;
; DELIVER ERROR ATTENTION AST'S
;
DEL_ATTN_AST:
	MOVAB	UCB$L_XM_AST(R5),R1	;
	MOVL	R1,R4			; COPY LIST HEAD ADDRESS
10$:	MOVL	(R1),R1			; ADDRESS A BLOCK
	BEQL	15$			; IF EQL THEN DONE
	MOVL	UCB$L_DEVDEPEND(R5),ACB$L_KAST+4(R1); CHANGE PARAM
	BRB	10$
15$:	BRW	COM$DELATTNAST		; DELIVER AST'S
;
; FATAL ERROR
;
FATALERROR:
	BICW	#XM$M_STS_ACTIVE,UCB$L_DEVDEPEND(R5); CLEAR ACTIVE
	BICW3	#^C<XM_E_M_MOP!-
			XM_E_M_LOST!-
			XM_E_M_START>,UCB$L_XM_LSTPRT+2(R5),UCB$L_DEVDEPEND+2(R5)
	BITW	#<XM_E_M_PROCERR!XM_E_M_NONEXMEM>,UCB$L_XM_LSTPRT+2(R5)
	BEQL	10$			; SOFTWARE ERROR?
	BISB	#XM$M_ERR_FATAL@-16,UCB$L_DEVDEPEND+2(R5); SET FATAL ERROR
10$:	MOVZBL	#MSG$_XMLINEDOWN,R4	; SEND MESSAGE
	MOVL	UCB$L_AMB(R5),R3	; ASSOCIATED MAILBOX?
	BEQL	15$			; IF EQL THEN NO
	BSBW	EXE$SNDEVMSG		; SEND MESSAGE
					; IGNORE ERROR
15$:	BSBB	DEL_ATTN_AST		; TELL AST HOLDERS

	.SBTTL SHUTDOWN - SHUT DOWN UNIT 
;++
; SHUTDOWN - SHUT DOWN UNIT
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS USED TO SHUT DOWN THE DMC UNIT AS A RESULT OF A SETMODE AND STOP
; PROTOCOL OR FATAL ERROR. THE ACTION IS TO PERFORM A CACNEL I/O AND THEN TO
; CLEAN UP THE UNIT DATA BASE.
;
; INPUTS:
;
;	R5 = UCB ADDRESS
;
; OUTPUTS:
;
;	R5 = UCB ADDRESS
;
;	R0-R4 ARE DISTROYED.
;--
SHUTDOWN:				; SHUT DOWN UNIT
	MOVL	UCB$L_CRB(R5),R4	; GET UNIT CSR
	MOVL	@CRB$L_INTD+VEC$L_IDB(R4),R4;
	DSBINT	UCB$B_DIPL(R5)		; RAISE TO DEVICE IPL FOR MASTER CLEAR
	BICW	#UCB$M_INT!UCB$M_TIM,UCB$W_STS(R5); SET NO EXPECT OR TIMEOUT
	BICB	#XM$M_STS_ACTIVE@-8,UCB$L_DEVDEPEND+1(R5); SET INACTIVE
	CLRW	(R4)			; MASTER CLEAR THE UNIT TO STOP IT
	ENBINT				; RETURN TO FORK LEVEL
	CLRL	R2			; SET TO CANCEL ALL THE I/O
	BSBW	CANCELIO		; CANCEL ALL
	MOVL	UCB$L_XM_BASAD(R5),R0	; ADDRESS THE BASE TABLE 
	MOVZWL	#256,8(R0)		; MAKE IT A REAL MEMORY BLOCK
	ADDW	8(R0),UCB$W_XM_QUOTA(R5); ADJUST QUOTA
	BSBW	EXE$DEANONPAGED		; DEALLOCATE IT
	MOVL	UCB$L_CRB(R5),R4	; ADDRESS CRB
	MOVW	UCB$Q_XM_CURCV1+2(R5),CRB$L_INTD+VEC$W_MAPREG(R4); DEALLOCATE THE MAP REGISTER
	BSBW	IOC$RELMAPREG		;
	MOVW	UCB$Q_XM_CURCV2+2(R5),CRB$L_INTD+VEC$W_MAPREG(R4); DEALLOCATE THE MAP REGISTER
	BSBW	IOC$RELMAPREG		;
	MOVB	UCB$Q_XM_CURCV1(R5),CRB$L_INTD+VEC$B_DATAPATH(R4); REALEASE RECEIVE MAP
	BSBW	IOC$RELDATAP		;
	MOVB	UCB$Q_XM_CURCV2(R5),CRB$L_INTD+VEC$B_DATAPATH(R4); REALEASE RECEIVE MAP
	BSBW	IOC$RELDATAP		;
	MOVW	UCB$W_XM_BASMP(R5),CRB$L_INTD+VEC$W_MAPREG(R4)
	BSBW	IOC$RELMAPREG
	BITB	#XM_RCV_M_INPR!XM_RCV_M_INUS,UCB$Q_XM_CURCV1+1(R5); 
	BEQL	5$			; IF EQL THEN NOT IN USE
	INSQUE	@UCB$Q_XM_CURCV1+4(R5),UCB$Q_XM_FREE(R5)
5$:
	BITB	#XM_RCV_M_INPR!XM_RCV_M_INUS,UCB$Q_XM_CURCV2+1(R5); 
	BEQL	10$			; IF EQL THEN NOT IN USE
	INSQUE	@UCB$Q_XM_CURCV2+4(R5),UCB$Q_XM_FREE(R5)
10$:	REMQUE	@UCB$Q_XM_FREE(R5),R0	; DEALLOCATE THE FREE LIST
	BVS	20$			; IF V-SET THEN NONE
	ADDW	8(R0),UCB$W_XM_QUOTA(R5); ADJUST QUOTA
	BSBW	EXE$DEANONPAGED		; DEALLOCATE THE BLOCK
	BRB	10$			; CONTINUE UNTIL DONE
20$:	MOVZWL	UCB$L_PID(R5),R0	; GET PID OF LAST STARTER
	MOVL	SCH$AL_PCB[R0],R0	; GET PCB OF OWNER
	CMPL	PCB$L_PID(R0),UCB$L_PID(R5); STILL THERE?
	BNEQ	25$			; IF NOT THEN BR
	ADDL	UCB$W_XM_QUOTA(R5),PCB$W_BYTCNT(R0); RETURN QUOTA
25$:	RSB				; RETURN TO CALLER
	

	.SBTTL	CANCELIO - CANCEL I/O ON UNIT, INTERNAL AND EXTERNAL
;++
; CANCELIO - CANCEL I/O ON UNIT, INTERNAL
; EXT_CANCELIO - CANCEL I/O ON UNIT, EXTERNAL
;
; FUNCTIONAL DESCRIPTION:
;
; THIS ROUTINE IS USED TO CANCEL SPECIFIC OR ALL I/O PENDING ON A DMC UNIT.
;
; INPUTS:
;
;	R2 = CHANNEL NUMBER OR 0 IF CANCEL ALL
;	R3 = CURRENT I/O PACKET IF BUSY
;	R4 = PCB ADDRESS IF SPECIFIC CANCEL
;	R5 = UCB ADDRESS
;
; OUTPUTS:
;
;	R2,R3,R4,R5 ARE SAVED
;
;--
EXT_CANCELIO:				; EXTERNAL CANCEL I/O
	TSTB	UCB$W_REFC(R5)		; REF COUNT 0?
	BNEQ	CANCELIO		; IF NEQ THEN STILL ACTIVE
	BRW	SHUTDOWN		; OTHERWISE SHUT THE UNIT DOWN
CANCELIO:				; CANCEL I/O
	MOVZWL	#SS$_ABORT,R0		; ASSUME ABORT AS STATUS
	BBC	#UCB$V_BSY,UCB$W_STS(R5),5$; BR IF NOT BUSY
	BSBB	CHECKPKT		; SEE IF PACKET SHOULD BE CANCELED AND CANCEL
	BNEQ	5$			; IF NEQ THEN NO MATCH
	MOVZWL	#SS$_ABORT,R0		; SET STATUS
	MOVL	UCB$L_DEVDEPEND(R5),R1	;
	PUSHR	#^M<R2,R4,R5>		; SAVE REGISTERS
	BSBW	IOC$REQCOM		; COMPLETE REQUEST
	POPR	#^M<R2,R4,R5>		;
5$:	MOVAB	UCB$Q_XM_RCVS(R5),R1	; ADDRESS LIST HEAD
	MOVL	(R1),R3			; GET LIST ENTRY
10$:	CMPL	R3,R1			; LIST END?
	BEQL	20$			; IF EQL THEN YES
	PUSHL	(R3)			; SAVE LINK TO NEXT
	BSBB	CHECKPKT		; CANCEL IF MATCH
	BNEQ	15$			; IF NEQ THEN NO MATCH
	REMQUE	(R3),R3			; REMOVE ENTRY FROM LIST
	CLRW	IRP$L_MEDIA+2(R3)	; SET NO TRANSFER
	BSBW	COMPLETE_IO		; COMPLETE THE I/O
15$:	POPL	R3			; RESTORE LINK
	BRB	10$			
20$:	RSB				; RETURN TO CALLER
;
; SUBROUTINE TO CHECK FOR SPECIFIC CANCEL
;
CHECKPKT:
	TSTL	R2			; ALL?
	BEQL	30$			; IF EQL THEN YES - CANCEL
	CMPL	R4,IRP$L_PID(R3)	; PID MATCH?
	BNEQ	30$			; IF NEQ THEN NO
	CMPW	R2,IRP$W_CHAN(R3)	; CHANNEL MATCH?
30$:	RSB				; RETURN TO CALLER
;
	.END
