	.TITLE	DADRIVER - VAX/VMS DA11-B DRIVER
	.IDENT	/V01/

;
; COPYRIGHT (C) 1978
; 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 THAT IS NOT SUPPLIED BY DEC.
;
;++
;
; FACILITY:
;
;	VAX/VMS DA11-B I/O Driver
;
; ABSTRACT:
;
;	This module contains the DA11-B driver:
;
;		Tables for loading and dispatching
;		A controller initialization routine
;		An FDT routine
;		The start I/O routine
;		The interrupt service routine
;
; AUTHOR:
;
;	C. Peters 07-SEP-78
;
;--

	.SBTTL	Driver Description

;+
; Functional specification:
;
; The DA11-B interprocessor link is a DMA data transfer channel between
; 2 VAX-11 processors. The device consists of the following:
;
;	2 DR11-B general purpose DMA interfaces
;	2 M7229 modules and BC08R cables that connect the 2 DR11-Bs
;
; The modules and cables send output signals from 1 DR11-B to the input
; signal connections of the other DR11-B.
;
; The link is a half-duplex channel that transfers data from memory on
; 1 processor to memory on the other processor in blocks of up to 32K
; words.
;
; The driver that follows depends on an outer protocol layer created by
; the application programs that request I/O from the DA11-B. One program
; on each processor drives the DA11-B. Program 1 is the master, and
; begins by initiating a transmit to the second processor. Program 2 is
; the slave, and begins by requesting a receive from the first
; processor.
;
; When Program 1's transmit completes, and consequently Program 2's
; receive completes, the programs reverse roles: i.e., Program 2
; initiates a transmit and Program 1 requests a receive.
;
; If the application programs are not synchronized, and both request
; transmits or receives simultaneously, both requests time out, and
; the driver returns status of DEV_TIMEOUT to both programs.
;
; The device works as follows:
;
;	When a transmit request arrives, the driver sets the GO,
;	INTERRUPT ENABLE, and REQUEST INTERRUPT ON OTHER PROCESSOR
;	bits in the CSR, and sets the direction to OUTPUT.
;
;	When the other processor's driver sets the INTERRUPT ENABLE
;	bit in that device's CSR, the driver receives an interrupt.
;	That driver then sets the GO, INTERRUPT ENABLE bits in the
;	CSR, and sets the direction to INPUT.
;
;	When the transmit completes, the device interrupts on both
;	processors. Both drivers check for complete transfer and return
;	status to the user. Complete transfer is indicated by a zero
;	count in the device's word count register.
;
;--

	.SBTTL	External and local symbol definitions

;
; External symbols
;
	$CRBDEF				; Channel request block
	$DDBDEF				; Device data block
	$IDBDEF				; Interrupt data block
	$IODEF				; I/O function codes
	$IPLDEF				; Hardware IPL definitions
	$IRPDEF				; I/O request packet
	$UCBDEF				; Unit control block
	$VECDEF				; Interrupt vector block

;
; Local symbols
;

;
; Argument list (AP) offsets for device-dependent QIO parameters
;
P1	= 0				; First QIO parameter
P2	= 4				; Second QIO parameter
P3	= 8				; Third QIO parameter
P4	= 12				; Fourth QIO parameter
P5	= 16				; Fifth QIO parameter
P6	= 20				; Sixth QIO parameter

;
; Other constants
;
DA_TIMEOUT	= 10			; 10 second device timeout
DA_DEF_BUFSIZ	= 65535			; Default buffer size

;
; DA11-B definitions that follow the standard UCB fields
;
	$DEFINI	UCB			; Start of UCB definitions

	.=UCB$K_LENGTH			; Position at end of UCB

$DEF	UCB$W_RCVWRDCNT			; Word count for a receive
			.BLKW	1
$DEF	UCB$W_RCVMEMEXT			; Memory extension bits of a
			.BLKW	1	; receive buffer address
$DEF	UCB$W_RCVBUFADR			; Buffer address for a receive
			.BLKW	1
$DEF	UCB$W_DA_DATABF			; Contents of device data buffer
			.BLKW	1	; register.
$DEF	UCB$W_DA_WRDCNT			; Contents of device word count
			.BLKW	1	; register.
$DEF	UCB$B_FUNC_CODE			; Extracted I/O function code.
			.BLKB	1
$DEF	UCB$K_DA_UCBLEN			; Length of extended UCB

;
; Bit positions for device-dependent status field in UCB
;
	$VIELD	UCB,0,<-		; Device status
		<RCVPENDNG,,M>,-	; Receive pending
		<RCVACTIVE,,M>,-	; Receive active
		>

	$DEFEND	UCB			; End of UCB definitions

;
; Device register offsets from CSR address
;
	$DEFINI	DA			; Start of DA11-B definitions

	.=-4				; Position to first register.
$DEF	DA_WRDCNT			; Word count
			.BLKW	1
$DEF	DA_BUFADR			; Buffer address
			.BLKW	1
$DEF	DA_STATUS			; Control/status
			.BLKW	1

;
; Bit positions for device control/status register
;
	_VIELD	DA_STS,0,<-		; Control/status register
		<GO,,M>,-		; Start device
		<OMODE,,M>,-		; Block or word transfer mode
		<ODIREC,,M>,-		; Transfer direction
		<OREQIN,,M>,-		; Interrupt request
		<XBA,2,M>,-		; Extended address bits
		<INTENA,,M>,-		; Enable interrupts
		<READY,,M>,-		; Device ready for command
		<CYCLE,,M>,-		; Starts slave transmit
		<OMODE,,M>,-		; Block or word transfer mode
		<IDIREC,,M>,-		; Transfer direction
		<IREQIN,,M>,-		; Interrupt request
		<,1>,-			; Maintenance bit
		<ATTN,,M>,-		; Status from other processor
		<NEX,,M>,-		; Nonexistent memory flag
		<ERROR,,M>,-		; Error or external interrupt
	>

$DEF	DA_DATABUF			; Data buffer (holds other CPU's
			.BLKW	1	; transmit word count).

	$DEFEND	DA			; End of DA11-B definitions

	.SBTTL	Standard tables

;
; Driver prologue table
;
	DPTAB	-				; DPT-creation macro
		END=DA_END,-			; End of driver label
		ADAPTER=UBA,-			; Adapter type
		UCBSIZE=<UCB$K_DA_UCBLEN>,-	; Length of UCB
		NAME=DADRIVER			; Driver name
	DPT_STORE INIT				; Start of load
						; initialization table
	DPT_STORE UCB,UCB$B_FIPL,B,8		; Device fork IPL
	DPT_STORE UCB,UCB$B_DIPL,B,22		; Device interrupt IPL
	DPT_STORE UCB,UCB$L_DEVCHAR,L,<-	; Device characteristics
		DEV$M_NET!-			;   network device
		DEV$M_IDV!-			;   input device
		DEV$M_ODV>			;   output device
	DPT_STORE UCB,UCB$B_DEVCLASS,B,DC$_SCOM	; Device class
	DPT_STORE UCB,UCB$W_DEVBUFSIZ,W,-	; Default buffer size
		DA_DEF_BUFSIZ

	DPT_STORE REINIT			; Start of reload
						; initialization table
	DPT_STORE DDB,DDB$L_DDT,D,DA$DDT	; Address of DDT
	DPT_STORE CRB,CRB$L_INTD+4,D,-		; Address of interrupt
		DA_INTERRUPT			; service routine
	DPT_STORE CRB,CRB$L_INTD+VEC$L_INITIAL,-; Address of controller
		D,DA_CONTROL_INIT		; initialization routine

	DPT_STORE END				; End of initialization
						; tables

;
; Driver dispatch table
;
	DDTAB	-				; DDT-creation macro
		DEVNAM=DA,-			; Name of device
		START=DA_START,-		; Start I/O routine
		FUNCTB=DA_FUNCTABLE,-		; FDT address
		CANCEL=DA_CANCEL		; Cancel I/O routine

;
; Function dispatch table
;
DA_FUNCTABLE:					; FDT for driver
	FUNCTAB	,-				; Valid I/O functions
		<READPBLK,-			; Read physical
		WRITEPBLK>			; Write physical
	FUNCTAB	,				; No buffered functions
	FUNCTAB	DA_CHECK_COUNT,-		; Device-specific FDT
		<READPBLK,-			; read/write routine
		WRITEPBLK>
	FUNCTAB	+EXE$READ,<READPBLK>		; FDT read routine
	FUNCTAB	+EXE$WRITE,<WRITEPBLK>		; FDT write routine

	.SBTTL	DA_CONTROL_INIT, Controller initialization routine

;++
; DA_CONTROL_INIT, Readies controller for I/O operations
;
; Functional description:
;
;	Allocates the direct data path permanently, and assigns the
;	controller data channel permanently. Then clears the device's
;	control status register.
;
; Inputs:
;
;	R4	- address of the CSR
;	R5	- address of the IDB
;	R6	- address of the DDB
;	R8	- address of the CRB
;
;	IDB$L_UCBLST contains address of device's UCB
;
; Outputs:
;
;	VEC$V_PATHLOCK bit set in CRB$L_INTD+VEC$B_DATAPATH
;	IDB$L_OWNER contains UCB address
;
;--

DA_CONTROL_INIT:			; Initialize controller

;	JSB	G^INI$BRK		; Debugging breakpoint

	BISB	#VEC$M_PATHLOCK,-	; Insist on direct data path.
		CRB$L_INTD+VEC$B_DATAPATH(R8)
	MOVL	IDB$L_UCBLST(R5),R0	; Get address of UCB.
	MOVL	R0,IDB$L_OWNER(R5)	; Make the device the permanent
					; controller channel owner.
	BISW	#UCB$M_ONLINE,-		; Turn the device online.
		UCB$W_STS(R0)
	CLRW	DA_STATUS(R4)		; Clear device's control/status
					; register.
	RSB				; Return

	.SBTTL	DA_CHECK_COUNT, FDT transfer-count-valid routine

;++
; DA_CHECK_COUNT, Rejects QIOs with odd transfer byte counts
;
; Functional description:
;
;	Determines whether the specified transfer byte count is even
;	or odd. Rejects QIOs with an odd transfer byte count.
;
; Inputs:
;
;	R3	- Address of the IRP
;	R4	- Address of the PCB
;	R5	- Address of the UCB
;	R6	- Address of the CCB
;	R7	- Bit number of the I/O function code
;	R8	- Address of the FDT routine
;	AP	- Address of the 1st function dependent QIO parameter
;
; Outputs:
;
;	R0	- error status code if byte count is an odd value
;
;--

DA_CHECK_COUNT:				; Check for odd transfer count.
	BLBC	P2(AP),EVEN_COUNT	; Branch on even count.
	MOVZWL	#SS$_BADPARAM,R0	; Return error status code.
	JMP	G^EXE$ABORTIO		; Odd count. Abort the request.

EVEN_COUNT:				; Success, even count.
	RSB				; Return to FDT loop in QIO
					; service routine.

	.SBTTL	DA_START, Start I/O routine

;++
; DA_START - Start a transmit, receive, or initialize operation
;
; Functional description:
;
;	This routine starts an I/O operation for a READ or WRITE I/O
;	transfer. The steps are the following:
;
;		Allocate a data path (NOOP, direct data path).
;		Allocate and load UBA map registers.
;		Compute UNIBUS start address and transfer word count.
;
;	The routine then branches according to the I/O function code.
;
;	The WRITE operation is performed as follows:
;
;		Load device's buffer address register.
;		Load device's word count register.
;		Load count in device's data buffer register (informs
;			receiver of number of bytes transmitted).
;		Check for power failure, and exit if failure occurred.
;		Activate device.
;		Wait for interrupt.
;		Fork.
;		Compute number of words transmitted.
;		Return status and transferred byte count.
;
;	The READ operation is performed as follows:
;
;		Save UNIBUS starting address and transfer word count
;			in UCB.
;		Check for power failure, and exit if failure occurred.
;		Enable interrupts from device.
;		Wait for interrupt.
;		Fork.
;		Load device's buffer address register.
;		Load device's word count register.
;		Check for power failure, and exit if failure occurred.
;		Activate device.
;		Wait for interrupt.
;		Fork.
;		Compute number of words transmitted.
;		Return status and transferred byte count.
;		
; Inputs:
;
;	R3	- address of the I/O request packet
;	R5	- address of the device's UCB
;
; Outputs:
;
;	R0	- I/O status, and number of bytes transferred
;	R1	- null
;
;--

DA_START:				; Process an I/O packet

;
; Obtain address of the controller's CSR. Clear the UCB's device-
; dependent status field.
;

;	JSB	G^INI$BRK		; Debugging breakpoint

	MOVL	UCB$L_CRB(R5),R4	; Get address of CRB.
	MOVL	@CRB$L_INTD+VEC$L_IDB(R4),-
		R4			; Get address of CSR.
	CLRW	UCB$W_DEVSTS(R5)	; Clear device-dependent status.

;
; Isolate and save the I/O function code.
;

	BICB3	#^C<IO$M_FCODE>,-	; Obtain I/O function code from
		IRP$W_FUNC(R3),-	; the I/O packet. Store in UCB.
		UCB$B_FUNC_CODE(R5)

;
; Set up the I/O transfer by allocating a data path and map registers,
; and loading the map registers.
;

TRANSFER:				; Prepare for a transfer.
	REQDPR				; Request a data path.
	REQMPR				; Request a set of map
					; registers.
	LOADUBA				; Load the map registers.
					; ***32K boundary problem***

;
; Calculate UNIBUS start address of the transfer, and the word count
; of the transfer.
;

	MOVZWL	UCB$W_BOFF(R5),R0	; Get byte offset in first
					; page of transfer buffer.
	MOVL	UCB$L_CRB(R5),R1	; Get address of the CRB.
	INSV	CRB$L_INTD+VEC$W_MAPREG(R1),-
		#9,#9,-			; Put starting map register
		R0			; number into high bits of
					; UNIBUS start address.
	EXTZV	#16,#2,R0,R1		; Extract the high 2-bits of
					; UNIBUS start address.
	ASHL	#DA_STS_V_XBA,R1,R1	; Shift bits to memory extension
					; positions.
	MOVW	UCB$W_BCNT(R5),R2	; Get number of bytes to
					; transfer.
	DIVL	#2,R2			; Convert to words.

;
; Determine whether this is a transmit or receive request.
;

CHECK_FUNCTION:				; Set up is unique for both
					; receive and transmit.
	CMPB	#IO$_WRITEPBLK,-	; Is it a write code?
		UCB$B_FUNC_CODE(R5)
	BEQL	START_WRITE		; If equal, yes, branch.
					; Otherwise, it's a receive.

;
; Prepare for receive:
;
; Mark the UCB to indicate that a receive is pending. Write the start
; address, memory extension bits, and word count into the UCB. Then set
; up the device's control/status bit mask to enable interrupts. When the
; companion computer starts a transmit, an interrupt will occur on the
; device.
;

START_RCVPENDNG:			; Prepare for receive.
	MOVW	R0,UCB$W_RCVBUFADR(R5)	; Store low 16-bits of start
					; address in UCB receive block.
	MOVW	R1,UCB$W_RCVMEMEXT(R5)	; Set memory extension bits
					; in UCB memory bits field.
	MOVW	R2,UCB$W_RCVWRDCNT(R5)	; Load word count into UCB.
	BISW	#UCB$M_RCVPENDNG,-	; Set receive-pending flag in
		UCB$W_DEVSTS(R5)	; the UCB.
	MOVZWL	#DA_STS_M_INTENA,R3	; Enable interrupts in status
					; bit mask.
	BRB	CHECK_POWERFAIL		; Join powerfail checkout code.

;
; Set up to start transmit:
;
; Load word count, buffer address, and memory extension bits into the
; device's registers. Load the starting word count into the device's
; data buffer register so that the receiver can read it.
;
; Set three bits in the status setup register. These bit settings will
; cause the device to request an interrupt on the companion computer,
; and enable interrupts on this device.
;
; Since the direction and mode bits both remain clear, the direction is
; transmit from this device to the companion computer, and do a block
; mode transfer.
;

START_WRITE:				; Prepare for transmit.
	MOVW	R0,DA_BUFADR(R4)	; Store low 16-bits of start
					; address in buffer address
					; device register.
	MOVW	R1,R3			; Set memory extension bits
					; in status setup register.
	MOVW	R2,DA_DATABUF(R4)	; Load transfer count in data
					; buffer. This is the protocol.
	MNEGW	R2,DA_WRDCNT(R4)	; Load negative transfer count
					; into word count register.
	BISW	#DA_STS_M_INTENA!-	; Set interrupt enable, go, and
		DA_STS_M_GO!-		; request interrupt on companion
		DA_STS_M_OREQIN,R3	; computer in register that
					; holds device control bits.

;
; Raise IPL to check for a power failure. If no power failure, activate
; the device and wait for an interrupt. When the interrupt occurs,
; create a fork process at the device's fork IPL.
;

CHECK_POWERFAIL:			; Check for power failure.
	DSBINT				; Raise IPL to IPL$_POWER.
	BBCC	#UCB$V_POWER,-		; Branch if no power failure.
		UCB$W_STS(R5),-		; Clear bit in any case.
		START_DEVICE
	SETIPL	UCB$B_FIPL(R5)		; Lower IPL back to fork IPL.
	BRW	POWER_FAILURE		; Report an error.

START_DEVICE:				; Activate device.
	MOVW	R3,DA_STATUS(R4)	; Activate device.
	WFIKPCH	TIMEOUT,#DA_TIMEOUT	; Wait for interrupts.

;
; Back in the main driver after a solicited interrupt. IPL is at
; device interrupt level (UCB$B_DIPL). Determine whether the interrupt
; was from a pending receive, or from an active transmit or receive.
;
;	R4	- address of the CSR
;	R5	- address of the UCB
;

;	JSB	G^INI$BRK		; Debugging breakpoint.

	BBCC	#UCB$V_RCVPENDNG,-	; If a transmit or receive,
		UCB$W_DEVSTS(R5),-	; go check for success.
		CHECK_TRANSFER

;
; Start the receive:
;
; Mark the UCB for an active receive operation. Then retrieve the device
; register settings from the UCB and start the device.
;

START_RECEIVE:				; Start a receive operation.
	BISW	#UCB$M_RCVACTIVE,-	; Mark receive-active bit in the
		UCB$W_DEVSTS(R5)	; UCB.
	MNEGW	UCB$W_RCVWRDCNT(R5),-	; Load negative word count into
		DA_WRDCNT(R4)		; device's count register.
	MOVW	UCB$W_RCVBUFADR(R5),-	; Load buffer address into
		DA_BUFADR(R4)		; device register.
	MOVW	UCB$W_RCVMEMEXT(R5),R3	; Set the memory extension bits.
	BISW	#DA_STS_M_INTENA!-	; Set the interrupt enable, go,
		DA_STS_M_GO!-		; and input direction bits in
		DA_STS_M_ODIREC,R3	; the status holding register.
	BRB	CHECK_POWERFAIL		; Go check for power failure.

;
; Complete transfer:
;
; A receive or transmit has completed. First create a fork process.
; Then release UBA resources, return status and the number of bytes
; transferred to I/O postprocessing.
;

CHECK_TRANSFER:				; Test for transfer completion.
	IOFORK				; Create a fork process.

;
; Release UBA resources.
;

	RELMPR				; Release map registers.
	RELDPR				; Release data path.

;
; Calculate number of bytes transferred. On a transmit, if the device's
; word count is zero, then all bytes were transmitted. Otherwise, the
; receive buffer was too small. On a receive, if the device's word
; count is zero, and the starting word count sent by the transmitter
; is larger than the receive buffer size, then a complete transmit did
; not occur. Report data overrun.
;

	TSTW	UCB$W_DA_WRDCNT(R5)	; Did all words transfer?
	BNEQ	INCOMPLETE_XFER		; If not equal, no. Report an
	MOVZWL	UCB$W_BCNT(R5),R0	; Obtain byte transfer value.
	MULL2	#^X10000,R0		; Shift into high 16-bits of R0.
	BBC	#UCB$V_RCVACTIVE,-	; If transmitting, and all words
		UCB$W_DEVSTS(R5),-	; transmitted, go return success
		LOAD_STATUS		; status.
	CMPW	UCB$W_RCVWRDCNT(R5),-	; If receiving, see if buffer
		UCB$W_DA_DATABF(R5)	; was large enough for data.
	BEQL	LOAD_STATUS		; If match, buffer is okay.
	MOVW	#SS$_DATAOVERUN,R0	; Otherwise, report data did not
					; all fit in buffer.
	BRB	COMPLETE_IO		; And finish the I/O.

LOAD_STATUS:				; Return normal status.
	MOVW	#SS$_NORMAL,R0		; Load a success code into R0.
	BRB	COMPLETE_IO		; And call I/O postprocessing.

;
; Incomplete transfer: report an error and return the number of bytes
; successfully transferred.
;

INCOMPLETE_XFER:			; All bytes did not transfer.
	MULL3	#2,UCB$W_DA_WRDCNT(R5),-; Calculate number of bytes left
		R0			; to transfer.
	ADDL2	UCB$W_BCNT(R5),R0	; Add negative bytes remaining
					; to original transfer count.
					; Result is bytes transferred.
	MULL2	#^X10000,R0		; Shift result to high 16-bits
					; of status register.
	MOVW	#SS$_OPINCOMPL,R0	; Return error status code.
	BRB	COMPLETE_IO		; Call I/O postprocessing.

;
; Device timeout handling. Control arrives here if an operation does not
; complete within the allowed time limit, or if a power failure occurs.
; Release UBA resources, clear the device's control/status register, and
; return an error status code.
;

TIMEOUT:				; An operation did not complete.

;	JSB	G^INI$BRK		; Debugging breakpoint.

	CLRW	DA_STATUS(R4)		; Clear device status.
	SETIPL	UCB$B_FIPL(R5)		; Restore IPL to fork level.

POWER_FAILURE:				; The power failed.
	RELMPR				; Release map registers.
	RELDPR				; Release data path.
	MOVZWL	#SS$_TIMEOUT,R0		; Return error status.

;
; Clear the 2nd longword of I/O status and call I/O postprocessing.
;

COMPLETE_IO:				; Driver processing is finished.
	CLRL	R1			; Return null 2nd word of I/O
					; status.
	REQCOM				; Complete I/O.

	.SBTTL	DA_INTERRUPT, Interrupt service routine

;++
; DA_INTERRUPT, Analyzes interrupts, processes solicited interrupts
;
; Functional description:
;
;	This routine begins by confirming that the interrupt was
;	generated by a ready device. If the device is not ready, the
;	routine clears the device's CSR and dismisses the interrupt.
;
;	The routine then examines the device's UCB to see if a receive
;	or a transmit has been started. If so, the routine restores the
;	suspended driver code.
;
;	Otherwise, the routine assumes that the interrupt is from the
;	other computer trying to transmit, and that a receive is pending
;	on this computer. If the device CSR and the UCB confirm this
;	assumption, and no errors have occurred, the routine restores
;	the suspended driver code.
;
;	To dismiss the interrupt, the routine clears the device's CSR
;	(on error only) and restores registers R0-R5 before executing
;	an REI.
;
; Inputs:
;
;	 0(SP)	- pointer to the address of the IDB
;	 4(SP)	- saved R0
;	 8(SP)	- saved R1
;	12(SP)	- saved R2
;	16(SP)	- saved R3
;	20(SP)	- saved R4
;	24(SP)	- saved R5
;	28(SP)	- saved PSL
;	32(SP)	- saved PC
;
; Outputs:
;
;	Saves the device's word count register in UCB$W_DA_WRDCNT.
;	Saves the device's data buffer register in UCB$W_DA_DATABF.
;
;--

DA_INTERRUPT:				; Service device interrupt

;	JSB	G^INI$BRK		; Debugging breakpoint

	MOVL	@(SP)+,R4		; Get address of IDB and remove
					; pointer from stack.
	MOVQ	(R4),R4			; Get address of device's CSR
					; and address of device's UCB.
	TSTB	(R4)			; Device ready bit set?
	BGEQ	UNSOL_INTERRUPT		; No, dismiss interrupt.
	BBC	#UCB$V_INT,-		; If device does not expect
		UCB$W_STS(R5),-		; interrupt, dismiss it.
		UNSOL_INTERRUPT
	MOVW	DA_DATABUF(R4),-	; Save the device's data buffer
		UCB$W_DA_DATABF(R5)	; register.
	MOVW	DA_WRDCNT(R4),-		; Save the device's word count
		UCB$W_DA_WRDCNT(R5)	; register.

;
; If a receive or a transmit is active, restore control to the main
; driver code.
;

	BBC	#UCB$V_RCVPENDNG,-	; If not receive pending, just
		UCB$W_DEVSTS(R5),-	; reactivate driver at saved PC.
		RESTORE_DRIVER

;
; Ascertain that the companion computer requested an interrupt on the
; device, and that the device reports no error conditions. If a device
; error occurs, the active request will time out.
;

	BBC	#DA_STS_V_IREQIN,(R4),-	; If the interrupt is of unknown
		DISMISS_INT		; origin, dismiss it.
	BBS	#DA_STS_V_ERROR,(R4),-	; If a device error occurred,
		UNSOL_INTERRUPT		; dismiss the interrupt.

;
; This is a solicited interrupt. Restore the main driver.
;

RESTORE_DRIVER:				; Jump to main driver code.
	MOVL	UCB$L_FR3(R5),R3	; Restore driver's R3.
	JSB	@UCB$L_FPC(R5)		; Call driver at interrupt
					; wait address.
	BRB	DISMISS_INT		; Then dismiss the interrupt.

;
; On an unsolicited interrupt, clear the device's control/status
; register if an error occurred, or if the UCB has no active packet.
;

UNSOL_INTERRUPT:			; Dismiss unsolicited interrupt.
	CLRW	DA_STATUS(R4)		; Clear device status.

;
; Restore the state of the stack before returning from the interrupt.
;

DISMISS_INT:				; Return from interrupt.
	POPR	#^M<R0,R1,R2,R3,R4,R5>	; Restore R0-R5
	REI				; Return from interrupt.

	.SBTTL	DA_CANCEL, Cancel I/O routine

;++
; DA_CANCEL, Cancels an I/O operation in progress
;
; Functional description:
;
; Inputs:
;
;	R2	- negated value of the channel index number
;	R3	- address of the current I/O request packet
;	R4	- address of the PCB canceling I/O
;	R5	- address of the device's UCB
;
; Outputs:
;
;--

DA_CANCEL:				; Cancel an I/O operation
	RSB				; Return

	.SBTTL	DA_END, End of driver

;++
; Label that marks the end of the driver
;--

DA_END:					; Last location in driver
	.END
