	.TITLE	TTINI
	.IDENT	/06.02/
;
; COPYRIGHT (c) 1986 BY DIGITAL EQUIPMENT CORPORATION.
; ALL RIGHTS RESERVED.
;
; THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
; OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;
; 02-FEB-78 PETER WANNHEDEN
;
; PREVIOUSLY MODIFIED BY:
;
;	P. WANNHEDEN
;	C. F. SPITZ
;	D. R. DONCHIN
;	T. LEKAS
;	S. C. ADAMS
;	R. S. PHILPOTT
;
; MODIFIED FOR RSX-11M-PLUS  V3.0 UPDATE B  BY:
;
;	
;	LK705 -- MAKE 11M AND 11M+ TTDRV MODULES IDENTICAL 06.01
;
; MODIFIED FOR RSX-11M-PLUS  V3.0 UPDATE D  BY:
;
;	SA460 -- ALLOW BROADCAST WRITE TO TI: OF NON-PRIVILEGED TASK
;		 AND CLEAN UP SOME COMMENTS  06.02
;
;
;+
;
;			****** T T I N I ******
;
;	THIS MODULE CONTAINS THE ROUTINES TO INITIATE A NEW I/O REQUEST
; FOR A TERMINAL.  THERE ARE THREE PARTS TO THIS.  FIRST, THE I/O REQUESTS
; ARE VALIDATED PRIOR TO PLACING THEM IN THE DEVICE'S I/O PACKET QUEUE.
; THIS MUST OCCUR WHILE THE USER TASK WHICH ISSUED THE REQUEST IS STILL
; THE CURRENT TASK.  NEXT, THE REQUEST IS PLACED IN THE REQUEST QUEUE FOR
; THE TERMINAL.  FINALLY, ANY REQUEST IN THE QUEUE WHICH CAN BE INITIATED
; IS DEQUEUED AND PASSED TO A FUNCTION-DEPENDENT INITIATION ROUTINE.
;-
;
	.MCALL	PKTDF$
	PKTDF$

	.PSECT	MAP5
.PAGE
	.SBTTL	TTINI - TERMINAL DRIVER I/O REQUEST INITIATION ENTRY POINT
;
;+
; **-TTINI - REQUEST INITIATION ENTRY POINT.
;
; THIS ROUTINE IS CALLED FROM "DRQIO" WHEN A TASK HAS ISSUED AN I/O REQUEST
; FOR A TERMINAL.  THE CALL IS MADE PRIOR TO QUEUEING THE I/O PACKET TO THE
; SCB QUEUE SINCE TERMINALS HAVE THE "UC.QUE" BIT SET IN THEIR UCB'S.  THIS
; IS DONE WHILE THE TASK CONTEXT IS STILL VALID, SINCE MANY FUNCTIONS WILL
; NEED ADDITIONAL PARAMETER CHECKS NOT DONE BY THE EXECUTIVE.  THIS ROUTINE
; WILL MAP THE DRIVER DATA AREA (SINCE WE WERE CALLED FROM THE EXEC) AND
; DISPATCH ON THE FUNCTION CODE TO VALIDATE THE I/O REQUEST.
;
;  INPUTS:
;	R1 => I/O REQUEST PACKET TO BE PROCESSED
;	R4 => SCB FOR THE REQUESTED TERMINAL
;	R5 => UCB FOR THE REQUESTED TERMINAL
;
;  OUTPUTS:  NONE.
;
;  REGISTERS MODIFIED:  R0, R1, R3
;-
;
TTINI::
	CALL	MAPD		;MAP DRIVER DATA AREA
	MOV	R1,R3		;COPY I/O PACKET POINTER
	MOVB	I.FCN+1(R3),R0	;GET THE I/O FUNCTION CODE
	ASL	R0		;CONVERT IT TO A WORD INDEX

	.IF DF	R$$IIC

	CALLR	@QPDSP(R0)	;DISPATCH TO PROCESS THIS REQUEST

	.IFF	;R$$IIC

	CALLR	@QPDSP-2(R0)	;DISPATCH TO PROCESS THIS REQUEST

	.ENDC	;R$$IIC


	.IF DF	T$$EIO&T$$OVL

;HERE IS THE STUB FOR TRANSFERING TO THE OVERLAY. QPEIO IS IN TTATT.

QPEIO::
	CALLR	QPETRA		;BRANCH TO TRANSFER IN APR6

	.ENDC	;T$$EIO&T$$OVL

.PAGE
	.SBTTL	FPGRQ - GET A REQUEST PACKET FROM THE REQUEST QUEUE
;
;+
; FPGRQ - GET A REQUEST PACKET FROM THE I/O QUEUE.
;	THIS SUBROUTINE IS ENTERED EITHER FROM THE REQUEST INITIATOR,
;	OR FROM THE FORK DISPATCHER.
;
; INPUT:
;	R5	POINTER TO U.TSTA
;	S1.DSI	1 TO PREVENT PROCESSING OF INPUT
;
; OUTPUT:
;	IF THERE IS AN IO.WBT PACKET IN THE QUEUE, A CTRL-Q IS
;	SIMULATED.
;
;	AS MANY PACKETS AS CAN BE DEQUEUED FROM THE IO QUEUE
;	ARE STARTED.
;
; REGISTERS ALTERED: R0,R1,R2,R3,R4
;-
;
;
	.ENABL	LSB
FPGRQ::
	BIT	#FR.PFP,U.TFRQ-U.TSTA(R5) ;POST-FORK PROCESSING REQUESTED?
	BNE	FPRET		;IF NE, YES...MUST DO THAT FIRST.

GRQPKT:

	.IF NDF	T$$SPL

	MOV	#DEUCBX,-(SP)	;IF TSTRQ OR $GSPKT FAILS, DEALLOCATE UCBX

	.IFTF	;NDF T$$SPL

	CALL	TSTRQ		;TEST IF ANYTHING TO DEQUEUE
				;IF NOT, DEALLOCATE UCBX AND RETURN TO CALLER
	MOV	R3,-(SP)	;SAVE FLAGS IN CASE $GSPKT FAILS
	MOV	#GRQAC,R2	;POINT TO ACCEPTANCE ROUTINE
	SUB	#U.TSTA,R5	;POINT TO START OF UCB
	MOV	R5,-(SP)	;SAVE POINTER TO UCB
	CALL	$GSPKT		;GET A PACKET
	MOV	(SP)+,R5	;RESTORE POINTER TO UCB
	BCC	10$		;IF CC, SUCCESSFULLY GOT A REQUEST...SKIP
;
; NOPKDQ - NO PACKET CAN CURRENTLY BE DEQUEUED.  EITHER THERE WERE NO PACKETS
;	OR THE ACCEPTANCE ROUTINE ABBORTED THE SCAN AND BRANCHED HERE DIRECTLY.
;
NOPKDQ:
	ADD	#U.TSTA,R5	;POINT TO UCB STATUS WORD
	BIC	(SP)+,2(R5)	;CLEAR FLAGS THAT WE SHOULD NOW IGNORE
FPRET:
	RETURN			;ALL DONE

;
; FOUND A REQUEST PACKET THAT CAN BE PROCESSED.  IT'S ADDRESS IS IN R1.
;
10$:

	.IFT	;NDF T$$SPL

	CMP	(SP)+,(SP)+	;CLEAN STACK OF FLAGS AND DEALLOCATE ROUTINE

	.IFF	;NDF T$$SPL

	TST	(SP)+		;CLEAN STACK OF FLAGS

	.ENDC	;NDF T$$SPL

	.IF DF	T$$BTW

	MOV	U.SCB(R5),R3	;GET POINTER TO SCB
20$:
	MOV	(R3),R3		;GET POINTER TO NEXT I/O PACKET IN QUEUE
	BEQ	30$		;IF EQ, NONE...SKIP
	CMP	I.UCB(R3),R5	;IS THAT PACKET FOR THIS UCB?
	BNE	20$		;IF NE, NO...SKIP THIS PACKET
	CMPB	I.PRI(R3),#251.	;IS REQUEST FOR A BREAKTHROUGH WRITE?
	BEQ	40$		;IF EQ, YES...ACCEPT THE REQUEST
30$:
	BIC	#S2.BRQ,U.TSTA+2(R5) ;OTHERWISE, SHOW NO MORE IO.WBTS IN QUEUE
40$:

	.ENDC	;T$$BTW

	ADD	#U.TSTA,R5	;POINT TO FIRST STATUS WORD
	MOV	R1,R3		;COPY PACKET POINTER
	CLR	(R3)		;CLEAR LINK WORD USED TO HOLD COMPLETION STATUS
	MOVB	I.FCN+1(R3),R1	;GET I/O REQUEST FUNCTION CODE (HIGH BYTE)

	.IF DF	T$$SPL

	MOV	U.TAPR-U.TSTA(R5),@#KDSAR5 ;MAP THE UCBX
	MOV	#120000,R4	;SET VIRTUAL ADDRESS OF UCBX

	.IFTF	;T$$SPL

	.IIF NDF T$$SPL, N$$SPL=0
	.IF DF	T$$MOD!N$$SPL

;
; NOTE - HERE WE ASSUME NUMERIC VALUES FOR FUNCTION CODES ACCORDING
; TO WHAT IS DESCRIBED IN MODULE TTDAT!
;
	CMP	R1,#IO.RPR/400	;IS REQUEST ONE OF THE FOLLOWING:
				;	IO.RPR, IO.RTT, IO.EIO, IO.RSD, IO.WSD?
	BHIS	50$		;IF HIS, YES...REQUIRE UCBX AND DO MODEM CHECK
	CMP	R1,#IO.ATT/400	;IS IT ONE OF THE FOLLOWING:
				;	IO.DET, IO.GTS, SF.GMC, SF.SMC?
	BHI	91$		;IF HI, YES...DON'T NEED UCBX OR MODEM CHECK
				;OTHERWISE, REQUEST IS IO.ATT, IO.WLB, OR IO.RLB

	.ENDC	;T$$MOD!N$$SPL

	.IF DF	T$$MOD

	BEQ	60$		;IF EQ, IO.ATT...NEED UCBX BUT NOT MODEM CHECK

	.IFTF	;T$$MOD

50$:

	.IFT	;T$$MOD

	BITB	#US.DSB!US.CRW,U.STS-U.TSTA(R5) ;IS THE LINE DISABLED OR...
				; ...WAITING FOR CARRIER (CHECKED ONLY ON...
				; ...IO.RLB, IO.WLB, IO.RPR, IO.RTT, IO.EIO,
				; ...IO.RSD, AND IO.WSD REQUESTS)
	BEQ	60$		;IF EQ, NO...REQUEST CAN CONTINUE
	BIT	#S4.DLO,6(R5)	;IS THIS A DIAL-OUT LINE?
	BNE	60$		;IF NE, YES...LET THE REQUEST BE TRIED
	MOV	#IE.DNR&377,R0	;ELSE, RETURN "DEVICE NOT READY" ERROR

	JMP	910$		;SKIP TO COMPLETE THE REQUEST
60$:

	.ENDC	;T$$MOD

	.IFF	;T$$SPL

	CALL	ALUCBX		;ALLOCATE UCBX IF IT ISN'T THERE
	BCS	105$		;IF CS, NO FREE POOL...RETURN ERROR

	.ENDC	;T$$SPL

	.IF DF	T$$EIO

	CMP	R1,#IO.EIO/400	;IS THIS AN EXTENDED I/O FUNCTION?
	BNE	90$		;IF NE, NO...CHECK NEXT FUNCTION

	MOV	U.TIXL-U.TSTA(R5),R2 ;GET POINTER TO FIRST IOPX IN LIST
70$:
	CMP	I.XIOP(R2),R3	;IS THIS IOPX FOR THE CURRENT I/O PACKET?
	BEQ	73$		;IF EQ, YES...EXIT THE LOOP

	ASSUME	I.XLNK,0

	MOV	(R2),R2		;GET POINTER TO NEXT IOPX
	BR	70$		;CHECK THIS ONE (IT MUST BE HERE SOMEWHERE)

73$:
	BIT	#TF.RDI,I.XMOD(R2) ;READ WITH DEFAULT INPUT SPECIFIED?
	BEQ	80$		;IF EQ NO
	MOV	I.XDBL(R2),U.TDIC(R4) ;SAVE DEFAULT INPUT BUFFER LENGTH
	MOV	R1,-(SP)	;SAVE R1 IN CASE THIS IS USER TASK EIO
	CALL	$TSTBF		;SHOULD WE ATTEMPT TO BUFFER THE DEFAULT INPUT?
	BCS	77$		;IF CS NO
;
; ATTEMPT ALLOCATING AND FILLING TTDRV BUFFERS WITH DEFAULT INPUT DATA
;
	BIS	#S1.IBF,@R5	;ASSUME BUFFERED INPUT
	MOV	R2,-(SP)	;SAVE IOPX POINTER ACROSS ALTB CALL
	CALL	ALTB		;ALLOCATE A BUFFER TO STORE DEFAULT INPUT
	MOV	R2,R0		;PASS THE BUFFER ADDRESS TO R0
	MOV	(SP)+,R2	;RESTORE IOPX ADDRESS TO R2
	BCS	77$		;IF CS, NO BUFFER IS AVAILABLE - JUMP
	MOV	R0,U.TDIF(R4)	;SAVE FIRST DEFAULT INPUT BUFFER ADDRESS
	MOV	#T$$BFL-4,U.TDIP(R4) ;SET SIZE OF DATA PORTION OF BUFFER
	MOV	R0,U.TDIP+2(R4)	;SET DATA POINTER TO FIRST DATA BUFFER
	ADD	#4,U.TDIP+2(R4)	;POINT PAST BUFFER HEADER TO DATA ITSELF
74$:	CLR	(R0)+		;CLEAR BUFFER LINK WORD
	MOV	R0,R1		;SAVE POINTER TO BUFFER CHARACTER COUNT
	MOV	#T$$BFL-4,(R0)+	;SET MAXIMUM DATA THAT CAN BE STORED IN 1 BUFFER
75$:	MOV	I.XDBF(R2),KISAR6 ;MAP USER DEFAULT INPUT BUFFER
	MOVB	@I.XDBF+2(R2),-(SP) ;GET CHARACTER FROM USER'S BUFFER
	MOV	$DALED,KISAR6	;REMAP DRIVER
	MOVB	(SP)+,(R0)+	;INSERT CHARACTER INTO OUR BUFFER
	INC	I.XDBF+2(R2)	;UPDATE POINTER INTO USER'S BUFFER
	DEC	I.XDBL(R2)	;ONE LESS CHARACTER TO GO
	BEQ	79$		;IF EQ ALL DONE
	DEC	(R1)		;MORE CHARACTERS YET TO GO - IS OUR BUFFER FULL?
	BNE	75$		;IF NE NO - INSERT MORE CHARACTERS INTO BUFFER
	MOV	R2,-(SP)	;YES - SAVE IOPX POINTER ACROSS ALTB CALL
	CALL	ALTB		;ALLOCATE ANOTHER BUFFER TO STORE DEFAULT INPUT
	MOV	R2,R0		;PASS THE BUFFER ADDRESS TO R0
	MOV	(SP)+,R2	;RESTORE IOPX ADDRESS TO R2
	BCS	76$		;IF CS, NO ADDITIONAL BUFFER IS AVAILABLE
	MOV	R0,-(R1)	;LINK OLD BUFFER TO NEW ONE
	BR	74$		;STUFF CHARACTERS INTO NEW BUFFER
;
; COME HERE IF NOT ENOUGH TTDRV BUFFERS COULD BE ALLOCATED TO CONTAIN ALL OF
; THE CHARACTERS OF THE USER'S DEFAULT INPUT
;
76$:	MOV	R2,-(SP)	;SAVE IOPX POINTER ACROSS DELTB CALL
	MOV	U.TDIF(R4),R2	;GET ADDRESS OF FIRST OF OUR BUFFERS
	CALL	DELTB		;DEALLOCATE THEM
	MOV	(SP)+,R2	;RESTORE IOPX POINTER
;
; SAVE POINTERS TO USER'S DEFAULT INPUT BUFFER IN UCBX
;
77$:	MOV	I.XDBF(R2),U.TDIP(R4) ;SAVE APR BIAS OF USER'S BUFFER
	MOV	I.XDBF+2(R2),U.TDIP+2(R4) ;SAVE VIRTUAL ADDRESS OF BUFFER
	CLR	U.TDIF(R4)	;NO DEFAULT BUFFERS ALLOCATED
	BIC	#S1.IBF,(R5)	;DO NON-BUFFERED INPUT
;
; PROCESS AN IO.RTT (TF.RTT) REQUEST, IF ANY
;
79$:
	MOV	(SP)+,R1	;RESTORE R1
80$:
	BIT	#TF.RTT,I.XMOD(R2) ;READ W/TERMINATOR TABLE SPECIFIED?
	BEQ	PKTDSP		;IF EQ, NO...SKIP TO DISPATCH PACKET

	MOV	I.XTTB(R2),I.PRM+10(R3) ;COPY 1.ST ADDRESS DOUBLE WORD OF NEW TABLE
	MOV	I.XTTB+2(R2),I.PRM+16(R3) ;COPY 2.ND ADDRESS DOUBLE WORD OF NEW TABLE

	MOV	I.XTTL(R2),R0	;COPY LENGTH OF TABLE
	CMP	R0,#32.		;IS IT OVER 32 BYTES

	BHI	92$		;YES, SET TO 32.
	BR	100$		;JOIN COMMON CODE

	.ENDC	;T$$EIO

90$:
	CMP	R1,#IO.RTT/400	;IS THIS A READ W/TERMINATOR TABLE?
	BEQ	92$		;IF EQ YES
91$:
	BR	PKTDSP		;DISPATCH ON FUNCTION CODE
92$:
	MOV	#32.,R0		;COPY A 32. BYTE TABLE

100$:
	MOV	R0,-(SP)
	CALL	ALTB		;ALLOCATE A TABLE
	MOV	(SP)+,R0
105$:	BCS	900$		;IF CS, ALLOCATION FAILED...RETURN ERROR
	MOV	R2,U.TRTT(R4)	;SAVE TERMINATOR TABLE ADDRESS
110$:
	MOV	I.PRM+10(R3),KISAR6 ;MAP USER'S TABLE
	MOVB	@I.PRM+16(R3),-(SP) ;RETRIEVE AN ENTRY FROM IT
	MOV	$DALED,KISAR6	;RESTORE DRIVER MAPPING
	MOVB	(SP)+,(R2)+	;COPY ENTRY TO ALLOCATED TABLE
	INC	I.PRM+16(R3)	;POINT TO NEXT ENTRY
	SOB	R0,110$		;LOOP TIL DONE
	MOV	U.TRTT(R4),R0	;FORM REMAINING COUNT
	ADD	#32.,R0		;...
	SUB	R2,R0		;...
	BEQ	PKTDSP		;ALL DONE IF ZERO
115$:
	CLRB	(R2)+		;ZERO REST OF TABLE
	SOB	R0,115$		;...
;	BR	PKTDSP		;FALL THROUGH TO "PKTDSP"
.PAGE
	.SBTTL	PKTDSP - DISPATCH ON FUNCTION CODE TO INITIATE REQUEST
;
;+
; **-PKTDSP - DISPATCH ON FUNCTION CODE TO INITIATE REQUEST
;-
;
PKTDSP:
	ASL	R1		;CONVERT FUNCTION CODE TO WORD INDEX

	.IF DF	T$$ACD

	MOV	#UA.ALL,U.AFLG-U.TSTA(R5) ;SET DEFAULT PROCESSING FLAGS
	MOV	U.ACB-U.TSTA(R5),R0 ;GET ACD CONTROL BLOCK ADDRESS
	BEQ	120$		;IF EQ, NONE THERE...SKIP
	MOV	R1,R2		;COPY FUNCTION CODE
	CMPB	R2,#30.		;FUNCTION CODE IN FIRST MASK WORD?
	BLOS	117$		;Y - JUMP
	TST	(R0)+		;BUMP ACB POINTER TO CHECK SECOND MASK WORD
	SUB	#32.,R2		;REDUCE FUNCTION CODE
117$:	BIT	$BTMSK(R2),A.MAS(R0) ;IS THE ACD INTERESTED IN THIS FUNCTION?
	BEQ	120$		;IF EQ, NO...SKIP
	MOV	#A.DEQU,R0	;SET I/O PACKET DEQUEUE ENTRY POINT
	CALL	$SWACD		;CALL THE ACD
	BIT	#UA.ALL,U.AFLG-U.TSTA(R5) ;ALLOW THIS I/O REQUEST?
	BNE	120$		;IF NE, YES...CONTINUE ON
	MOV	U.ACB-U.TSTA(R5),R0 ;GET ACD CONTROL BLOCK ADDRESS AGAIN
	MOV	A.IOS(R0),R0	;GET I/O ERROR CODE TO RETURN TO TASK
	BR	910$		;FINISH I/O
120$:

	.ENDC	;T$$ACD

	.IF DF	T$$OVL

	MOV	PPDSP-2(R1),R1  ;GET ADDRESS TO WHICH TO DISPATCH
	ASL	R1		;CONVERT TO A REAL ADDRESS
	BCS	130$		;AND SEE IF ROUTINE IS IN OVERLAY
	CALL	MPEXT		;MAP AND CALL ROUTINE
	BR	140$		;
130$:
	CALL	(R1)		;IT'S NOT IN THE OVERLAY
140$:

	.IFF	;T$$OVL

	CALL	@PPDSP-2(R1)	;DO OR START FUNCTION

	.ENDC	;T$$OVL

	JMP	GRQPKT		;LOOP TO DEQUEUE ANYTHING ELSE WE CAN
;
; ERROR HANDLING FOR REQUEST INITIATION ROUTINE.
;
900$:
	MOV	#IE.NOD&377,R0	;RETURN ERROR FOR INSUFFICIENT DYNAMIC MEMORY
910$:

	.IF DF	T$$EIO

	CMP	R1,#IO.EIO/400	;IS THIS AN IO.EIO?
	BNE	990$		;IF NE, NO...JUST FINISH OFF PACKET WITH ERROR
	MOV	R0,-(SP)	;SAVE RETURN STATUS
	MOV	R5,R0		;GET COPY OF UCB POINTER
	ADD	#U.TIXL-U.TSTA,R0 ;MAKE POINTER TO U.TIXL (IOPX LISTHEAD)
980$:
	MOV	R0,R1		;CURRENT IOPX POINTER BECOMES BACKPOINTER

	ASSUME	I.XLNK,0

	MOV	(R1),R0		;POINT TO NEXT IOPX
	CMP	I.XIOP(R0),R3	;DOES THIS IOPX BELONG TO CURRENT I/O PACKET?
	BNE	980$		;IF NE,NO...LOOP TILL WE FIND IT

	ASSUME	I.XLNK,0

	MOV	(R0),(R1)	;REMOVE IOPX FROM LIST
	MOV	R3,-(SP)	;SAVE POINTER TO IOPX
	MOV	#I.XLEN,R1	;GET SIZE OF IOPX
	CALL	$DEACB		;DEALLOCATE IOPX
	MOV	(SP)+,R3	;RESTORE POINTER FOR IOFIN
	MOV	U.TDIF(R4),R2	;GET DEFAULT INPUT BUFFER LIST POINTER
	CALL	DELTB		;DEALLOCATE THEM, IF ANY

	MOV	(SP)+,R0	;RESTORE ERROR CODE
990$:

	.ENDC	;T$$EIO

	CALL	IOFIN		;FINISH THE I/O REQUEST
	JMP	GRQPKT		;TRY FOR ANOTHER REQUEST

	.DSABL	LSB
.PAGE
	.SBTTL	TTINI SUBROUTINES
	.SBTTL	.	GRQAC - $GSPKT ACCEPTANCE ROUTINE FOR GET RQST (FPGRQ)
;
;+
; **-GRQAC - $GSPKT ACCEPTANCE ROUTINE FOR GETTING A NEW REQUEST PACKET (FPGRQ)
;-
;
GRQAC:
	CMP	I.UCB(R1),G$$SPA(SP) ;IS THIS PACKET FOR THE CURRENT UCB?
	SEC			;ASSUME ERROR
	BNE	10$		;IF NE, NO...ASSUMPTION CORRECT
	ASSUME	S2.IRQ,S1.IBY
	ASSUME	S2.ORQ,S1.OBY
	BITB	I.PRM+7(R1),U.TSTA(R5) ;CAN WE PROCESS THIS PACKET?
	BNE	20$		;IF NE, NO...SKIP TO CHECK FOR SCAN ABORT

; IF TERMINAL IS IN TERMINAL MANAGEMENT MODE AND THE REQUEST IS AN INTERNAL
; I/O REQUEST, REJECT THIS PACKET.  THIS IS A BIT OF A HACK.  WHEN TIME
; PERMITS, WE COULD USE EVENT PACKETS TO TELL THE NET TO STOP SENDING I/O.
;
	.IF DF	T$$SCA & R$$IIC

	BIT	#S5.TMM,U.TST5(R5) ;IS TERMINAL IN TERM. MANAGEMENT MODE?
	BEQ	5$		;IF NOT, SO FAR, SO GOOD
	BIT	#1,I.IOSB+4(R1) ;IF TMM, IS IT ALSO INTERNAL I/O?
	BNE	10$		;REJECT.. SEC STILL FROM ABOVE
5$:
	.ENDC	;T$$SCA & R$$IIC


	.IF DF	T$$BTW

	ASSUME	S3.FDX,200
	TSTB	U.TSTA+4(R5)	;IS THIS A FULL DUPLEX LINE? (CLEAR CC-C)
	BMI	10$		;IF MI, YES...ACCEPT THIS PACKET
	BIT	#S1.IBY!S1.OBY,U.TSTA(R5) ;IS ANYTHING BUSY?
	BEQ	10$		;IF EQ, NO...ACCEPT THIS REQUEST
;
; THE LINE IS NOT IN FULL DUPLEX MODE, AND EITHER INPUT OR OUTPUT IS BUSY.
; IN THIS CASE, THE ONLY PACKET WE CAN ACCEPT IS IO.WBT.  IO.WBT PACKETS HAVE
; PRIORITY 251., AND ALL OTHERS HAVE LOWER PRIORITY.  IF THIS PACKET IS NOT
; AN IO.WBT, WE KNOW THAT THERE ARE NO IO.WBT'S FURTHER DOWN THE QUEUE EITHER,
; SO WE ABORT THE SCAN.
;
	CMPB	I.PRI(R1),#251.	;IS THIS AN IO.WBT? (CLEAR CC-C IF YES)
	BNE	30$		;IF NE, NO...ABORT THE SCAN
;
;
; DON'T BEGIN AN IO.WBT FUNCTION IF A READ IS ACTIVE THAT SPECIFIED TF.XOF.
; THIS IS NECESSARY TO PREVENT A COLLISION BETWEEN THE IO.WBT DATA AND THE
; XOF CHARACTER OUTPUT.
;
	BIT	#S1.USI,U.TSTA(R5) ;UNSOLICITED INPUT IN PROGRESS?
	BNE	10$		;Y - ACCEPT IO.WBT

	.IF DF	T$$SPL

	MOV	U.TAPR(R5),KDSAR5 ;MAP UCBX
	MOV	#120000,R2	;POINT TO UCBX

	.IFF	;T$$SPL

	MOV	U.TUX(R5),R2	;GET UCBX (THERE MUST BE ONE SINCE LINE IS BUSY)

	.ENDC	;T$$SPL

	TSTB	U.TISV(R2)	; CHECK IF ^R AFTER IO.WBT IS IN PROGRESS	;LK705
	BNE	30$		; YES DO NOT TAKE ANOTHER IO.WBT		;LK705
	MOV	@R2,R2		;GET CURRENT INPUT PACKET			;**-2
	BEQ	10$		;NONE - ACCEPT IO.WBT
	BIT	#TF.XOF,I.FCN(R2) ;READ IS ACTIVE - TF.XOF SPECIFIED?
	BNE	30$		;Y - REJECT IO.WBT AND STOP SCAN AT THIS POINT
;
; ACCEPT THE CURRENT REQUEST PACKET.
;

	.IFF	;T$$BTW

	CLC			;RETURN WITH CC-C CLEAR TO ACCEPT PACKET

	.ENDC	;T$$BTW

10$:
	RETURN			;ALL DONE
;
; IF PACKET REQUIRES BOTH INPUT AND OUTPUT TO BE FREE, ABORT THE SCAN.
; OTHERWISE JUST REJECT THE PACKET BY RETURNING WITH CC-C SET.
;
	ASSUME	S2.ORQ,100
	ASSUME	S2.IRQ,200
20$:
	CMPB	I.PRM+7(R1),#S1.IBY!S1.OBY ;ARE BOTH INPUT AND OUTPUT...
				; ...REQUIRED TO BE FREE FOR THIS REQUEST?
	BLO	10$		;IF LO, NO...RETURN TO REJECT THIS PACKET...
				; ...CC-C WAS SET BY COMPARE INSTRUCTION
;
; WE FOUND A PACKET THAT REQUIRES BOTH INPUT AND OUTPUT TO BE FREE.  ABORT THE
; SCAN SO THE PACKET WILL GET A CHANCE TO BE DEQUEUED ONCE ALL REQUESTS IN
; FRONT OF IT ARE DONE.
;
30$:
	ADD	#G$$SPA,SP	;FLUSH STACK
	MOV	(SP)+,R5	;RESTORE POINTER TO UCB
	CLR	(SP)		;DON'T CHANGE FLAGS IN U.TSTA+2
	JMP	NOPKDQ		;FINISH AS THOUGH NO PACKET TO DEQUEUE
.PAGE
	.SBTTL	.	KILAC - $GSPKT ACCEPTANCE ROUTINE FOR KILL I/O (QPKIL)
;
;+
; **-KILAC - KILL I/O ACCEPTANCE ROUTINE FOR $GSPKT
;
; "$GSPKT" CALLS THIS ROUTINE WITH AN I/O PACKET TO BE CHECKED AGAINST THE
; KILL I/O ACCEPTANCE CRITERIA:
;	1. THE I/O PACKET UCB ADDRESS MUST MATCH THE I/O KILL REQUEST UCB.
;	2. THE I/O PACKET TCB ADDRESS MUST MATCH THE I/O KILL REQUEST TCB.
;	3. IF THE I/O KILL SUBFUNCTION WAS TF.WLB, THE I/O PACKET FUNCTION
;		CODE MUST BE "IO.EIO!TF.WLB" OR "IO.WLB".
;	4. IF THE I/O KILL SUBFUNCTION WAS TF.RLB, THE I/O PACKET FUNCTION
;		CODE MUST BE "IO.EIO!TF.RLB", "IO.RLB", "IO.RPR", OR "IO.RTT".
;
;  INPUTS:
;	R1 => I/O REQUEST PACKET TO BE ACCEPTED/REJECTED.
;	G$$SPA(SP) => UCB OF PACKETS TO ACCEPT
;	G$$SPA+2   => I/O KILL REQUEST PACKET
;
;  OUTPUTS:
;	CC-C =  0 IF PACKET IS TO BE ACCEPTED
;	     =  1 IF PACKET IS TO BE REJECTED
;
;  REGISTERS MODIFIED:  R2, R3
;-
;

	.IF DF	R$$IIC

KILAC:
	CMP	I.UCB(R1),G$$SPA(SP) ;IS THIS PACKET FOR THE CORRECT UCB?
	BNE	30$		;IF NE, NO...DON'T ACCEPT IT
	MOV	G$$SPA+2(SP),R3	;GET POINTER TO KILLER REQUEST PACKET
	CMP	I.TCB(R1),I.TCB(R3) ;PACKET FOR THE CORRECT TCB?
	BNE	30$		;IF NE, NO...DON'T ACCEPT IT
	MOVB	I.FCN(R3),R3	;GET KILLER PACKET SUBFUNCTION CODE
	MOVB	I.FCN+1(R1),R2	;GET PACKET SUBFUNCTION CODE

	.IF DF	T$$EIO

	CMPB	#IO.EIO/400,R2	;IS THIS PACKET AN EXTENDED I/O REQUEST?
	BNE	5$		;IF NOT, BR

;WARNING: THIS	ASSUMES	THAT IO.RLB/400,TF.RLB AND IO.WLB/400,TF.RLB

	MOVB	I.FCN(R1),R2	;GET NON-EXTENDED I/O REQUEST FUNCTION CODE
5$:

	.ENDC	;T$$EIO

	CMPB	#IO.RPR/400,R2	;IS THIS AN OLD-FASHIONED IO.RPR?
	BEQ	10$		;IF EQ, YES...SKIP
	CMPB	#IO.RTT/400,R2  ;IS THIS AN OLD-FASHIONED IO.RTT?
	BNE	20$		;IF NE, NO...SKIP
10$:
	MOVB	#TF.RLB,R2	;MAKE THESE LOOK LIKE STRAIGHT READS
20$:
	CLC			;ASSUME WE ARE GOING TO ACCEPT THIS PACKET
	BITB	R3,R2		;IS THIS A PACKET TO BE KILLED?
	BNE	40$		;IF NE, YES...ACCEPT IT
30$:
	SEC			;DON'T ACCEPT THIS PACKET
40$:
	RETURN			;ALL DONE

	.ENDC	;R$$IIC

.PAGE
	.SBTTL	I/O REQUEST PRE-QUEUE PROCESSING ROUTINES
	.SBTTL	.	QPKIL - INTERNAL I/O KILL REQUEST
;
;+
; **-QPKIL - INTERNAL I/O KILL REQUEST (IO.KIL)
;
; THIS ROUTINE PROCESSES AN I/O REQUEST WITH A FUNCTION CODE OF ZERO.  AN
; I/O PACKET WILL ONLY HAVE THIS FUNCTION CODE IF IT IS AN INTERNAL (SYSTEM
; STATE) I/O REQUEST.  IT PERFORMS FUNCTIONS SIMILAR TO THE DRIVER'S CANCEL
; I/O ENTRY POINT "TTCAN".  HOWEVER, IF CAN BE SPECIFIED THAT ONLY READ OR
; ONLY WRITE REQUESTS ARE TO BE CANCELLED.  THIS IS DONE WITH THE REQUEST
; MODIFIERS "TF.WLB" AND "TF.RLB".  A "$GSPKT" ACCEPTANCE ROUTINE IS USED TO
; FIND ALL SPECIFIED REQUESTS IN THE SCB QUEUE.  AN I/O KILL REQUEST IS ALWAYS
; PROCESSED AND COMPLETED IMMEDIATELY, SO IT IS NEVER PLACED IN THE SCB QUEUE.
;
;  INPUTS:
;	R3 => I/O KILL REQUEST PACKET
;	R4 => SCB FOR THE TERMINAL
;	R5 => UCB FOR THE TERMINAL
;	I.FCN =  TF.WLB OR TF.RLB
;	I.TCB => TCB OF TASK WHOSE REQUESTS ARE TO BE ABORTED
;
;  OUTPUTS:
;	SPECIFIED I/O REQUESTS ARE ABORTED WITH STATUS IE.ABO
;
;  REGISTERS MODIFIED:  R0, R1, R2
;-
;

	.IF DF	R$$IIC

QPKIL::

	.IF DF M$$PRO

	BIT	S.URM(R4),@$CPURM ;ALREADY ON CORRECT CPU?
	BNE	5$		;IF NE YES, CONTINUE

;	CONVERT PARAMETER LIST PART OF I/O PACKET INTO A FORK
;	BLOCK FOR INTERPROCESSOR FORK.

	ADD	#I.PRM+12,R3	;POINT TO KISAR5 FIELD
	MOV	KINAR5,(R3)	;INSERT DRIVER I MAPPING
	MOV	R4,-(R3)	;SAVE R4 IN FORK BLOCK
	MOV	S.URM(R4),-10(R3) ;SET URM OF DEVICE IN FORK BLOCK
	MOV	R3,R4		;POINT R4 AT FORK BLOCK FOR $FORK1
	CALL	$FORK1		;SWITCH TO CORRECT CPU
	SUB	#I.PRM+4,R3	;POINT BACK TO BEGINNING OF FORK BLOCK
	CALL	MAPD		;RE-MAP DRIVER
5$:

	.ENDC ; DF M$$PRO

	MOV	R3,-(SP)	;SAVE REQUEST PACKET POINTER
	MOV	R5,-(SP)	;SAVE CURRENT UCB POINTER
				;**NOTE: THESE ARE SAVED FOR INSPECTION...
				; ...IN THE $GSPKT ACCEPTANCE ROUTINE
	MOV	I.TCB(R3),R1	;GET TCB OF TASK TO KILL REQUESTS FOR
	MOV	I.FCN(R3),R3	;GET FUNCTION CODE OF THINGS TO KILL IN R3
	CALL	TTCN1		;CANCEL SELECTIVE I/O (DEPENDS ON TF.WLB/TF.RLB)
10$:
	MOV	(SP),R5		;RESTORE UCB ADDRESS
	MOV	#KILAC,R2	;SET POINTER TO ACCEPTANCE ROUTINE
	CALL	$GSPKT		;GET A PACKET
	BCS	20$		;IF CS, NO MORE PACKETS...ALL DONE
	MOV	R1,R3		;COPY KILLED PACKET ADDRESS FOR IOFIN
	MOV	#IE.ABO&377,R0	;SET REQUEST COMPLETION STATUS
	CALL	IOFIN		;FINISH OFF THE I/O REQUEST
	BR	10$		;LOOK FOR ANOTHER PACKET
20$:
	TST	(SP)+		;CLEAN STACK
	MOV	(SP)+,R3	;GET BACK ADDRESS OF I/O KILL REQUEST PACKET
	MOV	#IS.SUC&377,R0	;SUCCESSFUL I/O KILL
	JMP	IOFIN		;FINISH I/O KILL

	.ENDC	;R$$IIC

.PAGE
	.SBTTL	.	BUFFER ADDRESS CHECKING AND RELOCATION ROUTINES
;
;+
; **-CKBFR  - ADDRESS CHECK BUFFER FOR READ ACCESS
; **-CKBFRW - ADDRESS CHECK BUFFER FOR READ ACCESS AND WORD ALIGNMENT
; **-CKBFB  - ADDRESS CHECK BUFFER FOR READ/WRITE ACCESS
;
; THESE ROUTINES PERFORM THE DESCRIBED ADDRESS CHECKING ON A USER BUFFER.
;
;  INPUTS:
;	R0 => USER BUFFER (VIRTUAL ADDRESS)
;	R1 =  LENGTH OF USER BUFFER
;
;  OUTPUTS:
;	CC-C = 0 IF ADDRESS CHECK WAS SUCCESSFUL
;	CC-C = 1 IF ADDRESS CHECK FAILED
;	R0 =  ERROR CODE FOR ADDRESS CHECK FAILURE REASON:
;		"IE.SPC" IF SPECIFIED BUFFER LENGTH IS ZERO
;		"IE.SPC" IF BUFFER NOT WORD ALIGNED OR EXEC ADDRESS CHECK FAILS
;		"IE.BAD" IF BUFFER SIZE IS GREATER THAN 8128. BYTES
;	R1 =  BIAS OF RELOCATED USER BUFFER ADDRESS
;	R2 =  DISPLACEMENT OF RELOCATED USER BUFFER ADDRESS
;
;  REGISTERS MODIFIED:  R0
;-
;

	.IF DF	A$$CHK

	.ENABL	LSB
CKBFRW:
	BIT	#1,R0		;DOES BUFFER START ON ODD ADDRESS?
	BNE	910$		;IF NE, YES...RETURN INVALID BUFFER ERROR
CKBFR::

	.IF DF	R$$MPL

	MOV	#$CKBFR,-(SP)	;SET UP CALL FOR READ-ACCESS CHECK
	BR	10$		;SKIP TO COMMON CODE

	.IFTF	;R$$MPL

CKBFB::

	.IFT	;R$$MPL

	MOV	#$CKBFB,-(SP)	;SET UP CALL FOR WRITE-ACCESS CHECK
10$:

	.IFTF	;R$$MPL

	TST	R1		;IS BUFFER SIZE VALID?
	BEQ	900$		;IF EQ, NO...RETURN INVALID BUFFER ERROR
	CMP	R1,#20000-100	;IS BUFFER BIGGER THAN 8128. BYTES?
	BHI	920$		;IF HI, YES...RETURN BAD BUFFER SIZE ERROR

	.IFT	;R$$MPL

	.IF DF	X$$HDR

	MOV	(SP),-(SP)	;DUPLICATE MAPPING ROUTINE ADDRESS ON STACK
	MOV	KISAR6,2(SP)	;SAVE CURRENT EXEC MAPPING
	MOV	$SAHDB,KISAR6	;MAP CURRENT TASK HEADER

	.IFTF	;X$$HDR

	CALL	@(SP)+		;CALL REQUESTED ADDRESS CHECK ROUTINE

	.IFT	;X$$HDR

	MOV	(SP)+,KISAR6	;RESTORE PREVIOUS MAPPING

	.ENDC	;X$$HDR

	.IFF	;R$$MPL

	CALL	$ACHKB		;DO NORMAL ADDRESS CHECKING

	.ENDC	;R$$MPL

	BCS	910$		;IF CS, IT FAILED...RETURN ERROR

	.IFF	;A$$CHK

CKBRW:
CKBFR::
CKBFB::

	.IFTF	;A$$CHK

	CALL	$RELOC		;RELOCATE ADDRESS AND CONVERT TO DOUBLEWORD
	RETURN			;ALL DONE

	.IFT	;A$$CHK

900$:

	.IIF DF	R$$MPL,	TST	(SP)+ ;POP OFF CO-ROUTINE ADDRESS

910$:
	MOV	#IE.SPC&377,R0	;SET COMPLETION ERROR CODE FOR INVALID BUFFER
	BR	990$		;SKIP
920$:

	.IIF DF	R$$MPL,	TST	(SP)+ ;POP OFF CO-ROUTINE ADDRESS

	MOV	#IE.BAD&377,R0	;SET COMPLETION ERROR CODE FOR BAD SIZE
990$:
	SEC			;RETURN WITH ERROR FLAG
	RETURN			;ALL DONE

	.DSABL	LSB

	.ENDC	;A$$CHK

.PAGE
	.SBTTL	.	TSTRQ - CHECK I/O REQUEST QUEUE FOR PACKET TO DEQUEUE
;
;+
; SUBROUTINE TSTRQ - TEST I/O QUEUE.
;
; INPUT:
;	R5	POINTER TO U.TSTA
;
; OUTPUT:
;	IF THERE IS (POTENTIALLY) A PACKET IN THE I/O QUEUE THAT
;	CAN BE PROCESSED:
;	RETURN TO CALLER WITH R3 = MASK OF BITS TO CLEAR IN U.TSTA+2
;	IF NO PACKET COULD BE DEQUEUED
;
;	IF NOT:
;	RETURN TO CALLER'S CALLER.
;
; REGISTERS ALTERED: R3
;-
;
TSTRQ::
	MOV	(R5),R3		;GET FIRST TERMINAL STATUS WORD
	COM	R3		;INVERT IT
	BIC	#^C<S1.IBY!S1.OBY>,R3 ;CLEAR ALL TO SEE WHAT CAN BE DEQUEUED
	BIS	#S2.SRQ,R3	;OR WITH "SPECIAL REQUEST" FLAG
	BIT	R3,2(R5)	;IS ANYTHING IN QUEUE THAT CAN BE PROCESSED?
	BEQ	10$		;IF EQ, NO...POP OUR WAY OUT OF THIS ROUTINE
	BIT	#S2.BRQ,2(R5)	;IS THERE A "BREAKTHROUGH WRITE" IN THE QUEUE?
	BNE	20$		;IF NE, YES...IT CAN POSSIBLY BE PROCESSED

	ASSUME	S3.FDX,200

	TSTB	4(R5)		;USER ON A FULL DUPLEX LINE?
	BMI	20$		;IF MI, YES...IT CAN POSSIBLY BE PROCESSED
	BIT	#S1.IBY!S1.OBY,(R5) ;IS ANYTHING BUSY ON THE LINE?
	BEQ	20$		;IF EQ, NO...RETURN TO PROCESS A REQUEST
10$:
	TST	(SP)+		;POP RETURN ADDRESS SO WE WON'T PROCESS REQUEST
20$:
	RETURN			;ALL DONE


.PAGE
	.PSECT	MAP5.6

	.SBTTL	.	WRITE REQUEST PROCESSING
	.SBTTL	.		QPWSB - WRITE SPECIAL DATA
;
;+
; **-QPWSD - WRITE SPECIAL DATA (IO.WSD)
;-
;

	.IF DF	B$$MAP

QPWSD::
	BIC	#^C<IO.WSD!TF.CCO>,I.FCN(R3)  ;CLEAR DISALLOWED BITS
	BIS	#TF.WAL,I.FCN(R3) ;SET REQUIRED BIT
	CMP	#T.RSDN,I.PRM+10(R3) ;IS THE DATA TYPE SPECIFIER LEGAL?
	BHIS	QPWLB1		;IF HIS, YES...CONTINUE
	BR	EQPBAD		;ELSE, RETURN REQUEST WITH BAD PARAMENTERS

	.ENDC	;B$$MAP

	.SBTTL	.		QPWLB - WRITE LOGICAL BLOCK
;
;+
; **-QPWLB - NORMAL WRITE REQUEST (IO.WLB)
;-
;
QPWLB::

	.IF DF	T$$CUP

	MOV	I.PRM+6(R3),I.PRM+10(R3) ;SAVE VFC/CURSOR-CONTROL FOR LATER

	.ENDC	;T$$CUP

QPWLB1:
	MOV	#S2.ORQ,R4	;A WRITE MUST BLOCK IF OUTPUT IS ACTIVE

	.IF DF	T$$BTW

	BITB	#TF.WBT,I.FCN(R3) ;IS THIS A BREAKTHROUGH WRITE REQUEST?
	BEQ	CKBFLN		;IF EQ, NO...CHECK BUFFER LENGTH AND QUEUE IT

	.IF DF	M$$MUP

	MOV	I.TCB(R3),R0	;GET ISSUEING TASK TCB
	BIT	#T3.PRV!T3.CLI,T.ST3(R0) ;IS TASK PRIVILEGED OR A CLI?
	BNE	10$		;IF NE, YES...DO BREAKTHROUGH			;SA460
	CMP	T.UCB(R0),R5	;IS THE IO.WBT TO OWN TI:?			;SA460
	BNE	EQPPRI		;IF NOT, DON'T ALLOW IT				;SA460
10$:										;SA460
										;**-1
	.ENDC	;M$$MUP

	MOVB	#251.,I.PRI(R3)	;CHANGE REQUEST TO PRIORITY 251.
	BIS	#TF.CCO,I.FCN(R3) ;BREAKTHROUGH IMPLIES CANCEL CONTROL-O

	MOV	#S2.ORQ!S2.BRQ,R4 ;FLAG BREAKTHROUGH REQUEST AND WAIT FOR WRITE

	.ENDC	;T$$BTW

	BR	CKBFLN		;CHECK BUFFER LENGTH AND QUEUE THE REQUEST
.PAGE
	.SBTTL	.	READ REQUEST PROCESSING
	.SBTTL	.		QPRSD - READ SPECIAL DATA
;
;+
; **-QPRSD - READ SPECIAL DATA REQUEST (IO.RSD)
;-
;
	.IF DF	B$$MAP

QPRSD::
	BIC	#^C<IO.RSD!TF.TMO>,I.FCN(R3) ;CLEAR DISALLOWED BITS
	BIS	#<TF.RNE!TF.RAL>,I.FCN(R3) ;FORCE REQUIRED BITS
	TST	I.PRM+10(R3)	;IS THE DATA TYPE SPECIFIER LEGAL?
	BEQ	EQPBAD		;ZERO IS NOT ALLOWED, BAD PARAMETERS
	CMP	#T.RSDN,I.PRM+10(R3) ;IS THE DATA TYPE SPECIFIER LEGAL?
	BHIS	CKBFLN		;IF HIS, YES...FINISH CHECKS AND QUEUE REQUEST
	BR	EQPBAD		;BAD PARAMETERS

	.ENDC	;B$$MAP

	.SBTTL	.		QPRPR - READ AFTER PROMPT
;
;+
; **-QPRPR - READ AFTER PROMPT REQUEST (IO.RPR)
;-
;
	.ENABL	LSB

	.IF DF	T$$RPR

QPRPR::
	MOV	I.PRM+10(R3),R0	;GET VIRTUAL ADDRESS OF PROMPT BUFFER
	MOV	I.PRM+12(R3),R1	;GET PROMPT BUFFER SIZE
	MOV	#S2.IRQ!S2.ORQ,R4 ;PROMPT MUST BLOCK IF INPUT OR OUTPUT ACTIVE
	CALL	CKBFR		;CHECK READ ACCESS AND RELOCATE PROMPT BUFFER
	BCC	10$		;IF CC, OK...GO STORE ADDRESS AND QUEUE REQUEST
	BR	IOFIN		;CHECK FAILED...COMPLETE REQUEST WITH THE ERROR

	.ENDC	;T$$RPR

	.SBTTL	.		QPRTT - READ WITH TERMINATOR TABLE
;
;+
; **-QPRTT - READ WITH TERMINATOR TABLE REQUEST (IO.RTT)
;-
;
QPRTT::
	MOV	#S2.IRQ,R4	;READ W/TERMINATORS MUST BLOCK IF INPUT ACTIVE

;	BIT	#TF.PTT,I.FCN(R3) ;WANT TO USE PREVIOUSLY SPECIFIED TERM. TABLE?
;	BNE	CKBFLN		;IF NE, YES...DON'T BOTHER CHECKING PARAMETER
	MOV	I.PRM+10(R3),R0	;GET ADDRESS OF TERMINATOR TABLE
	MOV	#32.,R1		;LENGTH OF TABLE IS FIXED AT 32. BYTES

	CALL	CKBFRW		;CHECK READ ACCESS AND RELOCATE TABLE ADDRESS
	BCS	IOFIN		;IF CS, CHECK FAILED...RETURN WITH THE ERROR
10$:
	MOV	R1,I.PRM+10(R3)	;SAVE FIRST WORD OF ADDRESS DOUBLE WORD
	MOV	R2,I.PRM+16(R3)	;SAVE SECOND WORD OF ADDRESS DOUBLE WORD
	BR	CKBFLN		;CHECK BUFFER LENGTH AND QUEUE THE REQUEST

	.DSABL	LSB

	.SBTTL	.		QPRLB - READ LOGICAL BLOCK
;
;+
; **-QPRLB - NORMAL READ REQUEST (IO.RLB)
;-
;
QPRLB::
	MOV	#S2.IRQ,R4	;NORMAL READ MUST WAIT FOR ACTIVE READ
	BR	CKBFLN		;CHECK BUFFER LENGTH AND QUEUE THE REQUEST
.PAGE
	.SBTTL	.	ERROR HANDLING
;
; EQPPRI - RETURN PRIVILEGE VIOLATION ERROR.
;
EQPPRI:
	MOV	#IE.PRI&377,R0	;SET ERROR CODE FOR PRIVILEGE VIOLATION
	BR	IOFIN		;FINISH I/O REQUEST (NO NEED TO RESTORE MAPPING)
;
; EQPBAD - RETURN ERROR FOR BAD PARAMETERS.
;
EQPBAD::
	MOV	#IE.BAD&377,R0	;SET ERROR FOR BAD PARAMETER SPECIFIED

	.IF DF	A$$CHK

	BR	EQPFIN		;FINISH REQUEST (REMAP DRIVER ON THE WAY)
;
; EQPSPC - RETURN ERROR BECAUSE OF INVALID BUFFER SPECIFICATION.
;
EQPSPC:
	MOV	#IE.SPC&377,R0	;SET ERROR FOR ILLEGAL BUFFER SPECIFICATION
;	BR	EQPFIN		;RETURN THE ERROR (REMAP DRIVER ON THE WAY)

	.ENDC	;A$$CHK

EQPFIN::

	.IF DF	T$$EIO

	MOV	$DALED,KISAR6	;RESTORE DRIVER MAPPING

	.ENDC	;T$$EIO

IOFIN::
	CLR	R1		;CLEAR SECOND WORD OF IOSB
	CALLR	$IOFIN		;FINISH IO AND RETURN
.PAGE
	.SBTTL	.	CKBFLN - FINAL BUFFER LENGTH CHECK
;
; CKBFLN - CHECK THAT BUFFER LENGTH IS WITHIN (1,8K-64) BYTES
;
CKBFLN:

	.IF DF	A$$CHK

	MOV	I.PRM+4(R3),R1	;GET LENGTH OF TRANSFER REQUEST
	BEQ	EQPSPC		;IF EQ, RETURN ZERO BUFFER LENGTH ERROR
	CMP	R1,#20000-100	;IS BUFFER BIGGER THAN 8128. BYTES?
	BHI	EQPBAD		;IF HI, BUFFER IS TOO BIG...RETURN ERROR

	.ENDC	;A$$CHK

	.SBTTL	QUEUE - PLACE I/O REQUEST PACKET INTO TERMINAL QUEUE
;
; R4 NOW CONTAINS FLAGS TO BE USED BY THE ACCEPTANCE ROUTINE FO
; GETTING PACKETS FROM THE QUEUE VIA $GSPKT. SAVE THE FLAGS IN
; I.PRM+7 OF THE I/O PACKET.
;
QUEUE::
	ADD	#U.TSTA,R5	;POINT UCB POINTER TO THE FIRST STATUS WORD

	.IF DF	T$$ACD

	MOV	#UA.ALL,U.AFLG-U.TSTA(R5) ;SET DEFAULT PROCESSING
	MOV	U.ACB-U.TSTA(R5),R0 ;GET ACB
	BEQ	10$		;THERE IS NONE - JUMP
	MOVB	I.FCN+1(R3),R1	;GET FUNCTION CODE
	ASL	R1		;CONVERT TO A WORD INDEX
	CMPB	R1,#30.		;FUNCTION CODE IN FIRST MASK WORD?
	BLOS	5$		;Y - JUMP
	TST	(R0)+		;BUMP ACB POINTER TO CHECK SECOND MASK WORD
	SUB	#32.,R1		;REDUCE FUNCTION CODE
5$:	BIT	$BTMSK(R1),A.MAS(R0) ;IS THE ACD INTERESTED IN THIS FUNCTION?
	BEQ	10$		;N - JUMP
	MOV	#A.ACCE,R0	;Y - SET TO I/O REQUEST ACCEPTANCE ENTRY POINT
	CALL	$SWACD		;CALL ACD
	BIT	#UA.ALL,U.AFLG-U.TSTA(R5) ;ALLOW THIS I/O REQUEST?
	BNE	10$		;Y - JUMP
	SUB	#U.TSTA,R5	;POINT TO BEGINNING OF UCB
	MOV	U.ACB(R5),R0	;GET ACB
	MOV	A.IOS(R0),R0	;GET I/O ERROR CODE TO RETURN TO TASK
	BR	IOFIN		;FINISH I/O
10$:

	.ENDC	;T$$ACD

	ASSUME	<S2.BRQ!S2.SRQ!S2.ORQ!S2.IRQ&177400>,0
	MOVB	R4,I.PRM+7(R3)	;SAVE FLAGS IN I/O PACKET
	BNE	20$		;PACKET CANNOT BE PROCESSED
				;IMMEDIATELY - JUMP

	.IF DF	T$$GTS!T$$GMC

	CMPB	#IO.HNG/256.,I.FCN+1(R3) ;IS THIS HANGUP OR GET CHAR.?
	BEQ	15$		;IF EQ, IT'S A HANG - BRANCH

	.IF DF	T$$OVL

	MOV	#PPSPC1,R1	;GET ADDRESS OF ROUTINE IN TTEXT
	CALLR	MPEXT		;MAP IT AND CALL IT

	.IFF	;T$$OVL

	CALLR	PPSPC1		;PROCESS IMMEDIATELY

	.ENDC	;T$$OVL
15$:
	.ENDC	;T$$GTS!T$$GMC

	.IF DF	T$$OVL

	MOV	#PPHNG,R1	;GET ADDRESS OF ROUTINE IN TTEXT
	CALLR	MPEXT		;MAP IT AND CALL IT

	.IFF	;T$$OVL

	CALLR	PPHNG		;PROCESS IMMEDIATELY

	.ENDC	;T$$OVL

20$:
	BICB	#S2.SRQ!S2.BRQ,I.PRM+7(R3) ;CLEAR SRQ AND BRQ FLAGS IN PACKET
	MOV	U.SCB-U.TSTA(R5),R0 ;GET I/O QUEUE LISTHEAD
	MOV	R3,R1		;GET PACKET POINTER
	CALL	$QINSP		;INSERT PACKET
	BIT	#S2.SRQ,R4	;SPECIAL FUNCTION (IO.ATT, IO.DET, IO.SMC)?
	BEQ	30$		;N - JUMP
	MOV	#S2.SRQ,R4	;Y - CLEAR ALL OTHER BITS
30$:
	BISB	R4,2(R5)	;SET FLAGS TO INDICATE WHAT WE
				;PUT IN THE QUEUE
				;NOTE - MUST BE DONE  A F T E R  WE
				;PUT PACKET IN THE QUEUE!!

	.IF DF	M$$PRO

	MOV	#FR.GRQ,R3	;COME BACK AT FPGRQ
	CALL	SWCPU		;SWITCH TO CORRECT CPU
;
; IF WE FALL THRU HERE, WE ARE ALREADY ON THE CORRECT CPU
;

	.ENDC	;M$$PRO

	CALL	SETDSI		;DISABLE INPUT PROCESSING

	FRKBIT	BIS,#FR.GRQ	;POST "GET REQUEST" FORK
	RETURN			;LET SETDSI DISPATCH IT. THAT'S WHAT
				;IT GETS PAID FOR.

.PAGE
	.SBTTL	.	QPATT/QPDET - ATTACH/DETACH REQUESTS
;
;+
; **-QPATT - ATTACH TERMINAL REQUEST (IO.ATT)
; **-QPDET - DETACH TERMINAL REQUEST (IO.DET)
;
; ENTRY POINT "QPATT" PERFORMS UNSOLICITED INPUT AST ADDRESS ADJUSTMENT FOR
; SUPERVISOR MODE LIBRARIES (IF SUPPORTED) AND THEN FALLS THROUGH TO "QPDET".
; FROM QPDET, CONTROL IS TRANSFERRED TO THE PACKET QUEUEING ROUTINE WITH THE
; DEQUEUE BLOCKING FLAGS SET TO CHECK FOR ACTIVE INPUT AND OUTPUT.
;
;  INPUTS:
;	R3 => I/O PACKET FOR ATTACH/DETACH REQUEST
;	R5 => UCB FOR THE TERMINAL
;
;  OUTPUTS:
;	R3 => I/O PACKET TO BE QUEUED
;	R4 =  REQUEST DEQUEUE BLOCKING FLAGS
;
;  REGISTERS MODIFIED:  R0, R1, R2
;-
;
QPATT::

	.IF DF	S$$LIB

	BITB	#TF.AST,I.FCN(R3) ;ATTACH FOR UNSOLICITED INPUT AST?
	BEQ	QPDET		;IF EQ NO
	MOV	I.PRM(R3),R2	;GET ALL CHARACTER AST ADDRESS
	CALL	$CALTA		;ADJUST FOR SUPER/USER MODE
	MOV	R2,I.PRM(R3)	;AND PUT IT BACK
	MOV	I.PRM+4(R3),R2	;GET CONTROL C AST ADDRESS
	CALL	$CALTA		;ADJUST FOR SUPER/USER MODE
	MOV	R2,I.PRM+4(R3)	;AND PUT IT BACK

	.ENDC	;S$$LIB

QPDET::
	MOV	#S2.IRQ!S2.ORQ!S2.SRQ,R4 ;BLOCK REQUEST IF EITHER INPUT OR...
				; ...OUTPUT ACTIVE AND MARK AS SPECIAL FUNCTION
	JMP	QUEUE		;QUEUE UP THE REQUEST NOW
.PAGE
	.SBTTL	.	QPSPC - "SPECIAL" REQUESTS
;
;+
; **-QPSPC - HANDLE "SPECIAL" REQUESTS (IO.GTS/SF.GMC/SF.SMC)
;
; THIS ROUTINE CHECKS PARAMETERS FOR THE "GET TERMINAL SUPPORT" AND THE
; "GET/SET MULTIPLE CHARACTERISTICS" FUNCTIONS.  THE USER BUFFER FOR THESE
; REQUESTS MUST BE WORD-ALIGNED.  THE IO.GTS AND SF.GMC FUNCTIONS WILL BE
; PASSED TO "QUEUE" WITH NO DEQUEUE BLOCKING FLAGS SET.  THIS ALLOWS THEM TO
; BE PROCESSED SYNCHRONOUSLY (NEVER QUEUED).  AN SF.SMC FUNCTION MUST WAIT
; FOR ACTIVE INPUT AND OUTPUT REQUESTS TO FINISH BEFORE BEING DEQUEUED.
;
;  INPUTS:
;	R3 => I/O PACKET FOR THE REQUEST
;	R5 => UCB FOR THE TERMINAL
;
;  OUTPUTS:
;	R3 => I/O PACKET TO BE QUEUED
;	R4 =  REQUEST DEQUEUE BLOCKING FLAGS
;
;  REGISTERS MODIFIED:  R0, R1, R2
;-
;

	.IF DF	T$$GTS!T$$GMC!T$$SMC

QPSPC::

	.IF DF	T$$GTS!T$$GMC

	CLR	R4		;ASSUME REQUEST IS IO.GTS OR SF.GMC - IF SO...
				; ...IT IS PROCESSED SYNCHRONOUSLY (NOT QUEUED)

	.ENDC	;T$$GTS!T$$GMC

	.IF DF	T$$GTS

	CMP	I.FCN(R3),#IO.GTS ;IO.GTS? ;GET TERMINAL DRIVER SUPPORT REQUEST?

	.IF DF	T$$GMC!T$$SMC

	BEQ	20$		;IF EQ, YES...CHECK BUFFER ALIGNMENT

	.IFF	;T$$GMC!T$$SMC

	BNE	EQPIFC		;IF NE, NO...RETURN ILLEGAL FUNCTION ERROR

	.ENDC	;T$$GMC!T$$SMC

	.ENDC	;T$$GTS

	.IF DF	T$$GMC

	CMP	I.FCN(R3),#SF.GMC ;GET MULTIPLE CHARACTERISTICS REQUEST?

	.IF DF	T$$SMC

	BEQ	20$		;IF EQ, YES...CHECK BUFFER ALIGNMENT

	.IFF	;T$$SMC

	BNE	EQPIFC		;IF NE, NO...RETURN ILLEGAL FUNCTION ERROR

	.ENDC	;T$$SMC

	.ENDC	;T$$GMC

	.IF DF	T$$SMC

	CMP	I.FCN(R3),#SF.SMC ;SET MULTIPLE CHARACTERISTICS REQUEST?
	BNE	EQPIFC		;IF NE, NO...RETURN ILLEGAL FUNCTION ERROR
	MOV	I.TCB(R3),R0	;GET ISSUING TASK'S TCB
	CMP	T.UCB(R0),R5	;IS THIS TERMINAL HIS TI:?
	BEQ	10$		;IF EQ, YES...REQUEST IS LEGAL
	BIT	#T3.PRV,T.ST3(R0) ;IS THE TASK PRIVILEGED?
	BNE	10$		;IF NE YES, OK
	JMP	J2EQPR		;IF EQ, NO...CAN'T ALLOW USER TO DO THIS
10$:
	MOV	#S2.IRQ!S2.ORQ!S2.SRQ,R4 ;SF.SMC MUST BLOCK IF INPUT OR OUTPUT
				; ...IS ACTIVE.  ALSO FLAG AS "SPECIAL" FUNCTION

	.ENDC	;T$$SMC

20$:

	.IF DF	A$$CHK

	MOV	I.PRM+2(R3),-(SP) ;GET BUFFER ADDRESS
	BIS	I.PRM+4(R3),(SP) ;"OR" IN THE BUFFER LENGTH
	ROR	(SP)+		;IS EITHER THE ADDRESS OR THE LENGTH ODD?
	BCC	30$		;IF CC, BUFFER IS WORD-ALIGNED...SKIP
	JMP	EQPSPC		;RETURN ERROR FOR BUFFER NOT WORD-ALIGNED
30$:

	.ENDC	;A$$CHK

	JMP	CKBFLN		;CHECK FOR VALID BUFFER LENGTH AND QUEUE REQUEST

	.ENDC	;T$$GTS!T$$GMC!T$$SMC

;
; EQPIFC - RETURN AN ERROR DUE TO ILLEGAL FUNCTION CODE OR INVALID MODIFIERS.
;

	.IF DF	T$$GTS!T$$GMC!T$$SMC!T$$EIO

EQPIFC::
	MOV	#IE.IFC&377,R0	;SET ERROR FOR ILLEGAL FUNCTION CODE OR MODIFIER
	JMP	EQPFIN		;FINISH OFF THE REQUEST (RESTORE MAPPING ALSO)

	.ENDC	;T$$GTS!T$$GMC!T$$SMC!T$$EIO

.PAGE
	.PSECT	MAP5

	.SBTTL	.	QPHNG - HANGUP REQUEST
;
;+
; **-QPHNG - HANGUP REQUEST (IO.HNG)
;
; THIS ROUTINE CHECKS AN IO.HNG REQUEST.  A NON-PRIVILEGED TASK CAN HANG	;SA460
; UP ONLY ITS "TI:" LINE.  PRIVILEGED TASKS CAN HANG UP ANY LINE.  A HANGUP	;SA460
; REQUEST, LIKE AN IO.WBT OR GET CHARACTERISTICS, BREAKS THROUGH THE QUEUE. 	;SA460
;										;**-3
;  INPUTS:
;	R3 => I/O PACKET FOR THE REQUEST
;	R5 => UCB FOR THE TERMINAL
;
;  OUTPUTS:
;	R3 => I/O PACKET TO BE QUEUED
;	R4 =  REQUEST DEQUEUE BLOCKING FLAGS
;
;  REGISTERS MODIFIED:  R0
;-
;
QPHNG::
;	MOV	#S2.IRQ!S2.ORQ,R4 ;HANGUP MUST WAIT FOR READ AND WRITE
	MOV	I.TCB(R3),R0	;GET ISSUING TASK'S TCB
	CMP	T.UCB(R0),R5	;IS THIS TERMINAL HIS TI:?
	BEQ	J2QUE		;IF EQ, YES...ALLOW THE HANGUP
	BIT	#T3.PRV,T.ST3(R0) ;IS THE TASK PRIVILEGED?
	BEQ	J2EQPR		;IF EQ, NO...RETURN PRIVILEGE VIOLATION ERROR
J2QUE:
	.IF DF M$$PRO

	MOV	U.SCB(R5),R4	;GET SCB ADDRESS
	BIT	S.URM(R4),@$CPURM ;ALREADY ON CORRECT CPU?
	BNE	5$		;IF NE YES, CONTINUE

;	CONVERT PARAMETER LIST PART OF I/O PACKET INTO A FORK
;	BLOCK FOR INTERPROCESSOR FORK.

	ADD	#I.PRM+12,R3	;POINT TO KISAR5 FIELD
	MOV	KINAR5,(R3)	;INSERT DRIVER I MAPPING
	CLR	-(R3)		;CLEAR R4 IN FORK BLOCK
	MOV	S.URM(R4),-10(R3) ;SET URM OF DEVICE IN FORK BLOCK
	MOV	R3,R4		;POINT R4 AT FORK BLOCK FOR $FORK1
	CALL	$FORK1		;SWITCH TO CORRECT CPU
	SUB	#I.PRM+4,R3	;POINT BACK TO BEGINNING OF FORK BLOCK
	CALL	MAPD		;RE-MAP DRIVER
5$:

	.ENDC ; DF M$$PRO

	CLR	R4		;HANGUP DOESN'T WAIT FOR ANYTHING
	JMP	QUEUE		;QUEUE THE REQUEST
J2EQPR:
	JMP	EQPPRI		;RETURN PRIVILEGE VIOLATION ERROR

	.END
