Google
 

Trailing-Edge - PDP-10 Archives - BB-JR93N-BB_1990 - 10,7/mon/mpxser.mac
There are 2 other files named mpxser.mac in the archive. Click here to see a list.
TITLE	MPXSER - SERVICE FOR DEVICE MPX: AND RELATED FUNCTIONS  V160
SUBTTL	DONALD A. LEWINE   02-OCT-90 

	SEARCH	F,S,DEVPRM
IFN	FTNET,<SEARCH NETPRM>

	$RELOC
	$HIGH

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
;  OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION
; 1973,1974,1975,1976,1977,1978,1979,1980,1982,1984,1986,1988.
;ALL RIGHTS RESERVED.

.CPYRT<1973,1988>

XP VMSGSR,160

MPXSER::ENTRY	MPXSER

;AC USAGE WITHIN MPXSER:
;
;S) 	DEVICE STATUS. ALWAYS DEVIOS(F)

;P)	PUSH DOWN POINTER

;J)	USERS JOB NUMBER.

;R)	ADDRESS OF USERS JOB IF KA10, 341000 IF KI10

;F)	ADDRESS OF A DDB.  THIS IS EITHER THE MPX DDB OR
;	THE DEVICE CONTROLED BY THE MPX DDB. USE CAUTION.

;U)	NOT USED TODAY

;T1-T4)	TEMPS. ARGS TO SUBROUTINES PASSED IN
;	THESE AC'S.

;M)	ADDRESS FOR GETWRD/PUTWRD. USERS UUO.

;W)	NOT USED TODAY

;P1)	CHANNEL NUMBER

;P2)	ADDRESS OF THE MPX DDB.

;P3)	ADDRESS OF THE TARGET DDB.

;P4)	ADDRESS OF THE CONNECTED DEVICE TABLE
;FORMAT OF THE CONNECTED DEVICE TABLE:
;
;	!------------------!------------------!
;	!   SIZE OF CDT    ! # OF FREE SLOTS  !
;	!------------------!------------------!
;	!  UDX FOR DEV #1  ! ADDRESS OF DDB #1!
;	!------------------!------------------!
;	!  UDX FOR DEV #2  ! ADDRESS OF DDB #2!
;	!------------------!------------------!
;	!  UDX FOR DEV #3  ! ADDRESS OF DDB #3!
;	!------------------!------------------!
;	!  UDX FOR DEV #4  ! ADDRESS OF DDB #4!
;	!------------------!------------------!
;	/				      /
;	/				      /
;	!------------------!------------------!
;	!  UDX FOR DEV #N  ! ADDRESS OF DDB #N!
;	!------------------!------------------!
;
;NOTE THE CDT IS KEPT SORTED BY UDX SO IT IS EASY (AT LEAST FAST)
; TO CONVERT A UDX FOR A CONNECTED DEVICE TO A DDB ADDRESS.

CDTFRE==0	;OFFSET FOR THE COUNT OF THE NUMBER OF FREE
		; SLOTS IN CDT

CDTSIZ==0	;OFFSET FOR THE CDT WORD COUNT. THIS INCLUDES
		; THIS WORD

CDTQNT==40	;NUMBER OF WORD TO GET EACH TIME THE CDT FILLS UP

CDT4WD==<<CDTQNT+3>/4>	;NUMBER OF 4 WORD BLOCKS IN A CDT QUANTUM
CDTQNT==CDT4WD*4	;REDEFINE SO LOW 2 BITS ARE ALWAYS ZERO

;DEVICE DEPENDENT BITS IN DEVIOS
MPXVMF==1B0			;ON IF A DEVICE IS IN TROUBLE AND THE OUTPUT
				; BUFFER IS PAGED OUT
SUBTTL	AUTOCONFIGURE


;DRIVER CHARARCTERISTICS
;	MPX	= MPXCNF
;	MPX	= MULTIPLEXED CHANNEL
;	0	= MAXIMUM DEVICES IN SYSTEM
;	0	= KONTROLLER TYPE
;	0	= MAXIMUM DRIVES PER KONTROLLER
;	0	= HIGHEST DRIVE NUMBER ON KONTROLLER
;	MDSEC0	= SECTION FOR KDB/UDB
;	MDSEC0	= SECTION FOR DDB
DRVCHR	(MPX,MPX,0,0,0,0,MDSEC0,MDSEC0,<DR.SFT>)

	 .ORG	DEVLEN
MPXLEN:!			;LENGTH OF MPX DDB
	 .ORG


	$LOW
MPXDDB:	DDBBEG	(MPX,MPXLEN)
	SETWRD	(DEVCHR,<103>)			;DEVCHR
	SETWRD	(DEVSER,<MCSEC0+MPXDSP>)	;DEVSER
	SETWRD	(DEVMOD,<DVIN!DVOUT!DVLNG,,14407>) ;DEVMOD
	SETWRD	(DEVTYP,<<.TYMPX*.TYEST>,,DEPEVM>) ;DEVTYP
	SETWRD	(DEVCPU,<707B8>)		;DEVCPU
	DDBEND
	$HIGH


EQUATE	(LOCAL,0,<MPXCKT,MPXKDB,MPXKLN,MPXUDB,MPXULN>)
EQUATE	(LOCAL,0,<MPXICD,MPXICL,MPXINT,MPXULB,MPXULP>)

MPDDSP:	DRVDSP	(MPX,,MPXDDB,MPXLEN,0)

	$HIGH
MPXCFG:	SKIPGE	MPXNUM		;INITIALIZED YET?
	SETZM	MPXNUM		;INIT DDB SERIAL NUMBER
	POPJ	P,		;FOR NOW
;DISPATCH TABLE FOR MPX

	JRST	CPOPJ1##	;MPX IS ALWAYS ON-LINE
	POPJ	P,0		;SPECIAL ERROR STATUS
	JRST	REGSIZ##	;SIZE CAN BE GOTTEN FROM DDB
	POPJ	P,0		;SYSINI CALL
	POPJ	P,0		;HUNG DEVICE
MPXDSP:	JRST	MSGREL		;RELEASE
	POPJ	P,0		;CLOSE
	JRST	MSGDOU		;OUTPUT
	JSP	T1,MSGIN	;INPUT
	JRST	ILLOPR		;ENTER
	JRST	ILLOPR		;LOOKUP
	JRST	ILLOPR		;DUMP MODE OUTPUT
	JRST	ILLOPR		;DUMP MODE INPUT
	JRST	ILLOPR		;USETO
	JRST	ILLOPR		;USETI
	JRST	ILLOPR		;UGETF
	JRST	ILLOPR		;RENAME
	POPJ	P,0		;INPUT CLOSE
	JRST	ILLOPR		;UTPCLR
	JRST	ILLOPR		;MTAPE

;HERE ON AN OPERATION WHICH IS ILLEGAL FOR MPX:
ILLOPR:	JSP	T1,ERRPTU##	;PRINT THE MESSAGE
	ASCIZ	/Illegal operation for multiplex device/
	JRST	UUOPCP##	;PRINT PC AND STOP THE JOB
	SUBTTL	NON-IO INTERFACE WITH UUOCON

;SUBROUTINE TO BUILD THE MPX DDB
;CALL WITH:
;	MOVE	T1,NAME-OF-DEVICE
;	PUSHJ	P,TSTMPX
;	  RETURN HERE IF NOT AN OPEN UUO FOR 'MPX'
;	RETURN HERE WITH DDB BUILT AND F SETUP
TSTMPX::CAME	T1,[SIXBIT 'MPX'] ;IS THIS MPX:?
	POPJ	P,0		;NORMAL CASE.
	HRLZ	T1,.USMUO	;UUO OP CODE
	IOR	T1,.USMUE	;EA-CALC

	TDZ	T1,[777,,UPHNLY] ;ONLY CALLI
	CAMN	T1,[FILOP.]	;FILOP?
	JRST	TSTMP1		;YES, ALLOW THAT
	HLRZS	T1		;OR OPEN
	CAIE	T1,(OPEN)
	POPJ	P,0		;NOPE. ERROR RETURN
TSTMP1:	SE1ENT			;ENTER SECTION ONE
	PUSHJ	P,AUTLOK##	;INTERLOCK AUTCON
	  POPJ	P,		;BUSY & INTERRUPT LEVEL??
	PUSHJ	P,SAVT##	;SAVE T2 THRU T4
	SETZB	T1,T3		;NO DEVICE CODE, NO CHANNEL DATA BLOCK
	XMOVEI	T2,MPDDSP	;DRIVER DISPATCH
	PUSHJ	P,AUTSET##	;RESET THINGS
	AOS	T1,MPXNUM	;NUMBER OF CALLS TO TSTMPX
	HRLI	T1,'MPX'	;INCLUDE GENERIC DEVICE NAME
	SETZ	T2,		;LOCAL DEVICE
	PUSHJ	P,AUTDDB##	;CREATE A DDB
	  POPJ	P,		;NO MORE CORE
	PUSHJ	P,AUTULK##	;RELEASE INTERLOCK
	JRST	CPOPJ1##	;GOOD RETURN


	$LOW
MPXNUM:	EXP	-1		;DDB SERIAL NUMBER
	$HIGH
;HERE TO RELEASE ALL DEVICES CONNECTED TO THE MPX
;	CALLED BY ZAPMPX AND THROUGH RELEASE DISPATCH

MSGREL:	PUSHJ	P,SAVE4##	;SAVE THE P'S
MSGRE1:	HRRZ	P4,DEVXTR(F)	;ANY DEVICES CONNECTED?
	JUMPE	P4,CPOPJ##	;NO--KILL OFF MPX DDB
	HLRZ	P4,DEVXTR(F)	;GET ADDRESS OF CDT
	MOVE	P2,F		;SAVE F IN P2
	HRRZ	F,1(P4)		;ADDRESS OF FIRST DDB
	HRRZS	DEVBUF(F)	; SO DISCONNECT DOESN'T TRY TO RETURN BUFFERS
	HRRZS	DEVBUF(P2)	;DITTO FOR MPX ITSELF
	MOVEI	P1,RELEA1##	;UNCONDITIONAL DISCONNECT
	PUSHJ	P,DISCON	;REMOVE THE CONNECTION
	  JFCL			;DEVICE NOT CONNECTED
	MOVE	F,P2		;RESTORE F
	JRST	MSGRE1		;SEE IF ANY DEVICES CONNECTED

;SUBROUTINE TO WIPE OUT THE MPX DDB ON RELEASE
;CALL WITH:
;	MOVEI	F,ADDRESS-OF-DDB-TO-ZAP
;	PUSHJ	P,ZAPMPX
;	RETURN HERE

ZAPMPX::PUSHJ	P,MSGREL	;DISCONNECT ALL DEVICES FIRST
	MOVEI	T1,MPXLEN	;DDB LENGTH
	MOVEI	T2,(F)		;DDB ADDRESS
	PJRST	AUTKIL##	;DELETE THE DDB
	SUBTTL	CONNECT UUO

;UUO TO CONNECT A DEVICE TO AN MPX CHANNEL
;CALL WITH:
;	MOVEI	AC,E
;	CNECT.	AC,	-OR- 	CALLI   AC,130
;	  RETURN HERE ERROR CODE IN AC
;	RETURN HERE UDX IN AC
;
; E:	XWD	FUNCTION,CHAN
;	SIXBIT	/DEVICE/
;
CONECT::PUSHJ	P,SAVE4##	;SAVE VOLITILE AC'S
	HRR	M,T1		;PICK UP THE
	PUSHJ	P,GETWDU##	; FIRST ARGUMENT
	HRRZ	P1,T1		;COPY CHANEL NUMBER
	HLRZ	P3,T1		;GET FUNCTION CODE
	PUSHJ	P,SETUF##
	  PJRST	ECOD6##		;ELSE GENERATE ERROR
	HRLM	P1,.USCTA	;STORE MPX CHANNEL #
	MOVE	P2,F		;SET F=ADDRESS OF MPX DDB
	LDB	T2,PDVTYP##	;GET THE DEVICE TYPE
	CAIE	T2,.TYMPX	;IS THIS AN MPX DDB?
	PJRST	ECOD1##		;NO--PUNT
	CAILE	P3,CNFNMX	;SKIP IF NOT TOO BIG
	PJRST	ECOD10##	;ERROR 10 -- INVALID FUNCTION
	JRST	@CNFNTB(P3)	;DISPATCH

;TABLE OF FUNCTIONS
CNFNTB:	JRST	ECOD10##	;(0)NO FUNCTION 0
	JRST	CONDEV		;(1)CONNECT
	JRST	CDCDEV		;(2)CONDITIONAL DISCONNECT
	JRST	UDCDEV		;(3)UNCONDITIONAL DISCONNECT
	JRST	OFEDEV		;(4)OUTPUT FEASIBILITY CHECK
CNFNMX==.-CNFNTB-1
;SUBROUTINE TO CONNECT A DEVICE
;CALLED ONLY FROM CONECT
;
CONDEV:	PUSHJ	P,GETWD1##	;PICK UP DEVICE NAME
	MOVEI	T3,ASSPRG	;REJECT INITED DEVICES
	PUSHJ	P,DVASRC##	;LOOK FOR GENERIC DEVICE
	   SKIPA		;NOT A GENERIC DEVICE
	JRST	CNDEV4		;FREE GENERIC DEVICE
	PUSHJ	P,DVCNSG##	;LOOK UP IN DEVICE CHAIN
	  JRST	ECOD2##		;NO SUCH DEVICE
CNDEV4:	PUSHJ	P,LGLMPX	;OK TO USE?
	PJRST	ECOD3##		;CAN NOT USE THAT TYPE OF DEVICE
	MOVE	P3,F		;SAVE DDB ADDRESS
	PUSH	P,J		;SAVE J
	MOVE	J,.CPJCH##	;PICK UP JOB/CONTEXT HANDLE
	MOVE 	T1,DEVNAM(F)	;PICK UP DEVICE NAME
	MOVEI	T2,ASSPRG	;INIT BIT
	TDNN	T2,DEVMOD(F)	;ALREADY INITED?
	PUSHJ	P,ASSASG##	;NO--TRY TO INIT
	  JRST	[POP   P,J	;PHASE STACK
		 JRST  ECOD11##] ;DEVICE NOT INITIED OR CONNECTED
	SKIPGE	DEVSPL(F)	;SPOOLED DEVICE?
	JRST	[PUSHJ P,RELEA4## ;YES--KILL THE DDB
		 POP   P,J	;RESTORE J
		 JRST  ECOD12##] ;GIVE ERROR RETURN
	POP	P,J		;RESTORE J
	PUSHJ	P,GETCDT	;SET P4 = CDT ADDRESS
	  JRST	CNOCOR		;NO MORE FREE CORE
	HRRE	T1,CDTFRE(P4)	;GET FREE SLOT COUNT
	JUMPG	T1,CNDEV1	;JUMP IF ROOM
	PUSHJ	P,XPNCDT	;EXPAND THE CDT
	  PJRST	CNOCOR		;NO MORE CORE
CNDEV1:	SOS	CDTFRE(P4)	;COUNT THE SLOT
	MOVE	T1,DEVNAM(F)	;PICK UP THE DEVICE NAME
	PUSHJ	P,UIONDX##	;COMPUTE UDX
	  PJRST	[PUSHJ P,RELEA4## ;YES--KILL THE DDB
		 PJRST ECOD3##]  ; AND GIVE ERROR RETURN
	MOVEI	T3,1(P4)	;ADDRESS OF FIRST SLOT
CNDEV2:	HLRZ	T4,(T3)		;COPY UDX FROM CDT
	JUMPE	T4,CNDEV3	;JUMP IF TABLE EMPTY
	CAIL	T1,(T4)		;DOES IT GO HERE
	AOJA	T3,CNDEV2	;NO--KEEP LOOKING
	HLRZ	T2,CDTSIZ(P4)	;SIZE OF CDT
	ADDI	T2,-1(P4)	;ADDRESS OF LAST WORD
	MOVE	T4,-1(T2)	;PICK UPNEXT TO LAST WORD
	MOVEM	T4,(T2)		;STORE AS THE LAST WORD
	CAIE	T2,(T3)		;SKIP IF WE MOVED ENOUGH
	SOJA	T2,.-3		;LOOP OVER ENTIRE CDT
CNDEV3:	HRLM	T1,(T3)		;STORE UDX
	HRRM	P3,(T3)		;STORE DDB ADDRESS
	AOS	DEVXTR(P2)	;INCREMENT NUMBER OF CONNECTED DEVICES
	PUSHJ	P,CNDDBI	;DO SOME INITIALIZATION
	HRRZ	T4,DEVSER(F)
	PUSHJ	P,DSZ(T4)
	JRST	CPOPJ1##	;GOOD RETURN

CNOCOR:	PUSHJ	P,RELEA4##	;CLEAR ASSPRG & PJOBN
	PJRST	ECOD4##		;AND SAY NO FREE CORE
;SUBROUTINE TO CLEAN UP A DDB BEING CONNECTED TO DEVICE MPX
;CALL WITH:
;	P2 = ADDRESS OF MPX DDB
;	P3 = ADDRESS OF TARGET DDB
;	PUSHJ	P,CNDDBI
;	RETURN HERE
CNDDBI:	MOVEI	T2,DEPMSG	;FLAG AS CONTROLED VIA MPXSER
	IORM	T2,DEVMSG(P3)	; ..
	HRRM	P2,DEVXTR(P3)	;LINK TARGET BACK TO MPX DDB
	MOVSI	T1,0		;CLEAR OUT BOTH 
	MOVE	F,P3		; DEVIAD AND DEVOAD
	DPB	T1,PDVOAD##	; SO WE DO NOT LOOK
	DPB	T1,PDVIAD##	; ACTIVE BUFFERS
	SETZM	DEVBUF(F)	;ALSO ZAP DEVBUF
	HRR	M,DEVIOS(P2)	;GET STATUS FROM MPX DDB
	PUSHJ	P,SETIOS##	;SET AS STATUS OF TARGET DDB
	MOVSI	T1,IOFST!IOBEG
	IORM	T1,DEVIOS(P3)
	MOVEI	T1,DEPAIO	;NON BLOCK
	TDNE	T1,DEVAIO(P2)	;CHECK MPX
	IORM	T1,DEVAIO(P3)	;SET IN TARGET
	MOVEI	T1,DEPDEL	;DISABLE ERROR LOGGING (ALSO FULL SCNSER PTY)
	ANDCAM	T1,DEVSTA(P3)	;CLEAR IT FIRST
	TDNE	T1,DEVSTA(P2)	;CHECK MPX
	IORM	T1,DEVSTA(P3)	;PROPAGATE STATE TO TARGET
	MOVEI	T1,DVDIBP	;BATCH PTY (ALSO DISABLE MESSAGE REASSEMBLY)
	ANDCAM	T1,DEVCHR(P3)	;CLEAR IT FIRST
	TDNE	T1,DEVCHR(P2)	;CHECK MPX
	IORM	T1,DEVCHR(P3)	;PROPAGATE STATE TO TARGET
	MOVE	F,P3		;MAKE SURE OF DDB POINTER
	MOVSI	T2,DVTTY	;IF THIS IS A TTY
	TDNE	T2,DEVMOD(P3)	; (TEST)
	PUSHJ	P,TTYMPX##	;LET SCNSER DO SOME INITIALIZATION
	POPJ	P,0		;RETURN
;TABLE OF DEVICE TYPES WHICH MAY BE CONNECTED
;DEVICE IS OK IF 1B<DEVTYP> IS = 1

	DEFINE	TYPBTS(A),<
	IRP A,<
	  ZZ..=ZZ..!1B<.TY'A>
	>>

	ZZ..==0
	TYPBTS	<LPT,PTP,TTY,PTY,RDA>
TYPMSK:	EXP	ZZ..

;SUBROUTINE TO SEE IF DEVICE CAN BE USED ON MPX:
;CALL WITH:
;	MOVE	F,DDB-ADDRESS
;	PUSHJ	P,MGLMPX
;	  RETURN HERE IF NO GOOD
;	RETURN HERE IF OK
;RESPECTS T1
LGLMPX::LDB	T2,PDVTYP##	;GET DEVICE TYPE
	CAIN	T2,.TYTTY	;TTY?
	JRST	CPOPJ1##	;YES--ALWAYS GOOD
IFN FTNET,<
	CAIE	T2,.TYCDR	;A CDR?
	JRST	LGLMP1		;NO
	LDB	T3,PDVSTA##	;YES, LOCAL?
	CAME	T3,JBTLOC##
	JRST	CPOPJ1##	;REMOTE CDR IS OK
LGLMP1:>
	MOVSI	T3,(1B0)	;SETUP BIT
	MOVN	T2,T2		;MAKE COUNT NEGATIVE
	LSH	T3,(T2)		;SHIFT BIT
	TDNN	T3,TYPMSK	;VALID TYPE?
	POPJ	P,0		;NO--RETURN
	JRST	CPOPJ1##	;WIN
;SUBROUTINE TO DO A DISCONNECT (CALLED FROM REASSIGN)
;CALLED WITH:
;	F=ADDRESS OF DEVICE DDB
;	PUSHJ	P,MPXDIS
;	  RETURN HERE IF NOT MPX'ED
;	RETURN HERE IF DISCONNECTED
MPXDIS::MOVEI	T1,DEPMSG
	TDNN	T1,DEVMSG(F)	;MPX'ED?
	POPJ	P,		;NO
	PUSHJ	P,SAVE4##
	MOVEI	P1,RELEA1##	;DO RELEASE
	HRRZ	P2,DEVXTR(F)	;LOCATE MPX DDB
	PJRST	DISCON		;DO DISCONNECT

;SUBROUTINE TO DO A DISCONNECT (CALLED ONLY FROM CNECT. UUO)
;CALLED WITH:
;	P2 = ADDRESS OF MPX DDB
;	PUSHJ P,CDCDEV OR UDCDEV
;	  RETURN HERE IF ERROR
;	RETURN HERE IF OK
CDCDEV:	SKIPA	P1,[EXP URELEA##] ;RELEASE AND CLOSE
UDCDEV:	MOVEI	P1,RELEA1##	;RESET
	PUSHJ	P,GETWD1##	;PICK UP DEVICE NAME
	PUSHJ	P,FNDDDB	;FIND DEVICE DATA BLOCK
	  JRST	ECOD2##		;MUST BE VALID DEVICE
IFN FTMP,<
	PUSHJ	P,SETCPP##	;GET ON CORRECT CPU FOR DEVICE
	  JRST	ECOD11##	;CPU DEAD, SAY DEVICE UNAVAILABLE
>
;FALL INTO DISCON
;SUBROUTINE TO DISCONNECT A DEVICE
;CALLED WITH:
;	F = ADDRESS OF DDB
;	P1 = ADDRESS OF ROUTINE TO CALL (RELEA1)
;	P2 = ADDRESS OF MPX DDB
;	PUSHJ	P,DISCON
;	  RETURN HERE ON ERROR (USER AC UPDATED)
;	RETURN HERE IF OK
DISCON:	HLR	M,DEVBUF(P2)	;ADDRESS OF THE RING HEADER
	HRRZS	P3,F		;ADDRESS OF THE TARGET DDB FOR VMCHEK
				; ALSO ZERO LH(F) FOR CAIN BELOW
	TRNE	M,-1		;OK IF NO BUFFERS (RELEASE)
	PUSHJ	P,VMCHKH	;MAKE SURE ALL BUFFERS ARE IN CORE IN
				; CASE THEY SHOULD BE RECLAIMED
	MOVE	S,DEVIOS(F)	;SETUP S
	LDB	T1,PJOBN##	;GET THE JOB # OF OWNER
	MOVEI	T2,ASSPRG	; INIT BIT
	TDNE	T2,DEVMOD(F)	;SKIP IF NOT INITED
	CAME	T1,J		;SKIP IF INITED BY THIS JOB
	JRST	ECOD5##		;NO--BOUNCE HIM
	HLRZ	P4,DEVXTR(P2)	;GET CDT ADDRESS
	JUMPE	P4,ECOD5##	;IF NONE, THERE ARE NO DEVICES IN CDT
	HLRZ	T1,CDTSIZ(P4)	;NUMBER OF WORDS IN CDT
	SUB	T1,CDTFRE(P4)	;LESS NUMBER OF FREE WORDS GIVES # ENTRIES
	MOVEI	T1,-1(T1)	;CLEAR LH AND SUBTRACT 1
	MOVEI	T2,1(P4)	;ADDRESS OF FIRST SLOT
	MOVE	T3,T2		;2 COPIES
DCNDVL:	MOVE	T4,(T2)		;GET AN ENTRY
	CAIN	F,(T4)		;IS THIS THE ONE TO ZAP?
	AOJA	T2,DCNDV1	;YES--GET THE NEXT ENTRY
	MOVEM	T4,(T3)		;STORE BACK ENTRY
	ADDI	T2,1		;UPDATE SOURCE
	ADDI	T3,1		;UPDATE DESTINATION
DCNDV1:	SOJG	T1,DCNDVL	;MOVE THE WHOLE CDT DOWN
	CAIN	T2,(T3)		;DID WE FIND AN ENTRY?
	JRST	ECOD5##		;NO--DEVICE NOT CONNECTED
	SETZM	-1(T2)		;ZERO OLD LAST ENTRY IN CDT
	CAIN	P1,RELEA1##	;UNCONDITIONAL DISCONNECT?
	PUSHJ	P,RTNOB		;YES, RETURN BUFFERS TO FREE LIST
	MOVEI	T2,DEPMSG	;NO LONGER CONTROLED
	ANDCAM	T2,DEVMSG(F)	; BY MPXSER
	SOS	T2,DEVXTR(P2)	;1 LESS DEVICE CONNECTED
	AOS	CDTFRE(P4)	; AND 1 MOVE FREE SLOT
	TRNE	T2,-1		;SKIP IF CDT NOW EMPTY
	JRST	DISXIT		;ELSE JUST EXIT
	HLRZ	T1,CDTSIZ(P4)	;NUMBER OF WORDS IN CDT
	MOVEI	T2,(P4)		;ADDRESS OF CDT
	PUSHJ	P,GIVWDS##	;RETURN CDT
	SETZB	P4,DEVXTR(P2)	;FLAG AS HAVING NO CDT
DISXIT:	MOVE	T1,P1		;COPY ROUTINE ADDRESS
	AOS	(P)		;SKIP RETURN
	PJRST	CALSER		;CALL SERVICE ROUTINE
;SUBROUTINE TO SETUP CDT
;CALL WITH:
;	P2 = ADDRESS OF DDB
;	PUSHJ	P,GETCDT
;	  RETURN HERE IF NO CORE
;	RETURN HERE P4=ADDRESS OF CDT
GETCDT:	HLRZ	P4,DEVXTR(P2)
	JUMPN	P4,CPOPJ1##	;ALL DONE IF NON-ZERO
	MOVEI	T2,CDTQNT	;NUMBER OF WORDS TO GET
	PUSHJ	P,GETWDS##	; ..
	  POPJ	P,0
	MOVE	T2,[CDTQNT,,CDTQNT-1]	;SIZE AND FREE COUNT
	MOVEM	T2,CDTSIZ(T1)	;STORE INFO
	MOVEM	T1,P4		;STORE ADDRESS
	HRLZM	T1,DEVXTR(P2)	;STORE ADDRESS
	HRRZM	T1,DEVCID(P2)	;STORE NEXT INPUT DEV
	SETZM	1(P4)		;ZERO UNUSED WORDS
	HRLI	T1,1(P4)	;FROM
	HRRI	T1,2(P4)	;TO
	BLT	T1,CDTQNT-1(P4)	;ZAP
	JRST	CPOPJ1##	;GOOD RETURN

;SUBROUTINE TO RETURN FULL BUFFERS WHICH HAVE
; NOT BEEN OUTPUT YET TO THE FREE LIST
;CALL WITH:
;	F = ADDRESS OF THE CONNECTED DDB
;	PUSHJ	P,RTNOB
;	ALWAYS RETURN HERE

RTNOB:	MOVSI	T1,DVTTY	;DEVICE IS A TTY BUT
	TDNN	T1,DEVMOD(F)	;IF A TTY, DON'T CALL WAIT1 (IOACT GETS LEFT ON)
	PUSHJ	P,WAIT1##	;MAKE SURE I/O ISN'T GOING ON NOW
	HLRZ	T1,DEVBUF(F)	;ADDRESS OF THE ACTIVE LIST
	JUMPE	T1,CPOPJ##	;RETURN IF NO BUFFERS ON LIST
RTNOB1:	PUSHJ	P,ADVBFE##	;RETURN BUFFER TO THE FREE LIST
	  POPJ	P,		;ALL DONE
	JRST	RTNOB1		;RETURN NEXT BUFFER
;SUBROUTINE TO EXPAND A FULL CDT
;CALL WITH:
;	MOVE	P2,ADDRESS-OF-MPX-DDB
;	MOVE	P4,ADDRESS-OF-CDT
;	PUSHJ	P,XPNCDT
;	  RETURN HERE IF NO MORE CORE
;	RETURN HERE DDB AND P4 UPDATED

XPNCDT:	HLRZ	T2,CDTSIZ(P4)	;CURRENT SIZE
	ADDI	T2,CDTQNT	;PLUS AMOUNT TO EXPAND
	PUSHJ	P,GETWDS##	;GET THAT MUCH CORE
	  POPJ	P,0		;NO MORE CORE
	AOS	(P)		;WE HAVE WON 
	HRL	T1,P4		;COPY THE OLD ADDRESS
	PUSH	P,CDTSIZ(P4)	;REMEMBER THE OLD SIZE
	HRRM	P4,(P)		;AND ADDRESS
	MOVE	T2,[CDTQNT,,CDTQNT]	;AMOUNT WE ADDED
	ADDB	T2,CDTSIZ(P4)	;UPDATE THE CDT
	HLRZ	T2,T2		;PUT NEW SIZE IN THE RH
	ADDI	T2,(T1)		;WHERE TO STOP BLT
	MOVEI	P4,(T1)		;ALSO FIX P4 BEFORE BLT ZAPS AC
	SETZM	(P4)		;ZERO OUT THE NEW CDT
	HRLZ	T3,P4		;BUILT A BLT
	HRRI	T3,1(P4)	;POINTER
	BLT	T3,-1(T2)	;ZAP!!
	SUBI	T2,CDTQNT+1	;NEW STOP ADDRESS
	BLT	T1,(T2)		;COPY THE CDT TO IT'S NEW HOME
	HRLM	P4,DEVXTR(P2)	;REMEMBER ADDRESS OF THE CDT
	HRRZM	P4,DEVCID(P2)	;UPDATE CURRENT BECAUSE OLD MOVED
	POP	P,T2		;RESETORE OLD SIZE AND ADDRESS
	HLRZ	T1,T2		;COPY NUMBER OF WORDS
	TLZ	T2,-1		;CLEAR LH
	PJRST	GIVWDS##	;RETURN OLD CORE
;SUBROUTINE TO DETERMINE OUTPUT FEASIBILITY FOR A MPX-CONTROLLED DEVICE
;CALLED ONLY BY CNECT. MONITOR CALL

OFEDEV:	PUSHJ	P,MPXTBL	;FIRST AND FOREMOST GET I/O GOING
	PUSHJ	P,GETWD1	;GET DEVICE UDX FROM CNECT. CALL
	PUSHJ	P,FNDDDB	;FIND THE DEVICE QUICKLY
				; NO GENERAL DEVICE SEARCHES HERE FOR
				; SPEED AND EFFICIENCY CONSIDERATIONS
	PJRST	ECOD2##		;NO SUCH DEVICE, USER LOSES
	LDB	T1,PDVTYP##	;SEE WHAT SORT OF DEVICE WE CAME UP WITH
	CAIE	T1,.TYTTY	;A TTY?
	JRST	OFEDE2		;NO
	PUSHJ	P,TTYOFE##	;YES, ASK SCNSER'S OPINION
	JRST	OFEDE5		;AND FORGET THE DATA REQUEST COUNT

OFEDE2:
IFE	FTNET,<TRO T1,-1>	;FLAG LOCAL DEVICE IF NO NETWORKS
IFN	FTNET,<
	MOVE	T1,DEVCHR(F)	;NOT A TTY, GET CHARACTERISTICS
	TLNN	T1,DVCNET	;A NETWORK (DATA REQUEST) DEVICE?
	TROA	T1,-1		;NO, LOCAL, FLAG AS SUCH
	HRRZ	T1,DEVDRQ(F)	;YES, RETURN OUTPUT DATA REQUEST COUNT
> ;END IFN FTNET

OFEDE5:	HLL	T1,DEVEVM(F)	;GET ADDRESS OF USER OUTPUT BUFFER
	TLNN	T1,-1		;UNLESS NOT EVM MAPPED
	HRL	T1,DEVOAD(F)	;IN WHICH CASE GET TRUE USER ADDRESS
	JRST	STOTC1##	;RETURN VALUE TO USER
	SUBTTL	SENSE. UUO -- RETURN DEVICE INFORMATION

;UUO TO RETURN INFORMATION ABOUT A SPECIFIC DEVICE
;CALL WITH:
;	MOVE	AC,[LENGTH,,ADDR]
;	SENSE.	AC,
;	  RETURN HERE (ERROR CODE IN AC)
;
;ADDR CONTAINS:
;
;	!------------------!------------------!
;	! UDX, CHANNEL NUMBER OR SIXBIT DEV   !
;	!------------------!------------------!
;	! LENGTH OF BLOCK  ! ADDR OF BLOCK    !
;	!------------------!------------------!
;	/				      /
;	/				      /
;	!------------------!------------------!
;	! UDX, CHANNEL NUMBER OR SIXBIT DEV   !
;	!------------------!------------------!
;	! LENGTH OF BLOCK  ! ADDR OF BLOCK    !
;	!------------------!------------------!
;
;
;
;EACH BLOCK GETS:
;
;	!------------------!------------------!
;	! SIXBIT DEVICE NAME FOR DEVICE       !
;	!------------------!------------------!
;	!         0        ! GETSTS INFO      !
;	!------------------!------------------!
;	!            DEVSTS WORD	      !
;	!------------------!------------------!
;
;
;NOTE: THIS UUO IS RESTARTABLE FOR VM
;
SENSE::	PUSHJ	P,SAVE2##	;SAVE P1 AND P2
	HRRZ	P1,T1		;ADDRESS OF POINTER TABLE
	HLRZ	P2,T1		;NUMBER OF ENTRIES
SENSE1:	SUBI	P2,2		;TWO MORE WORDS?
	JUMPL	P2,CPOPJ1##	;NO--ALL DONE
	HRR	M,P1		;GET THE FIRST WORD
	PUSHJ	P,GETWDU##	;GET THE WORD
	PUSHJ	P,DVCNSG##	;FIND THE DDB
	  JRST	ECOD1##		;(1)ILLEGAL DEVICE
	AOS	M,P1		;POINT TO NEXT WORD
	PUSHJ	P,GETWDU##	;GET THE WORD
	PUSHJ	P,SNSDEV	;STORE THE SENSE INFO
	AOJA	P1,SENSE1	;LOOP OVER ALL THE DEVICES
;SUBROUTINE TO STORE THE SENSE INFO FOR ONE DEVICE
;CALL WITH:
;	T1 = COUNT,,ADDR
;	F = ADDRESS OF DDB
;	PUSHJ	P,SNSDEV
;	RETURN HERE
;
SNSDEV:	HRR	M,T1		;COPY ADDRESS
	HLRE	T2,T1		;COPY COUNT
	SOJL	T2,CPOPJ##	;EXIT IF COUNT .LE. 0
	MOVE	T1,DEVNAM(F)	;PICK UP DEVICE NAME
	PUSHJ	P,PUTWDU##	;STORE FOR USER
	SOJL	T2,CPOPJ##	;EXIT IF COUNT = 1
	HRRZ	T1,DEVIOS(F)	;PICK UP I/O STATUS WORD
	PUSHJ	P,PUTWD1##	;STORE FOR USER
	SOJL	T2,CPOPJ##	;EXIT IF COUNT = 2
	MOVE	T1,DEVSTS(F)	;GET DEVICE STATUS
	PJRST	PUTWD1##	;STORE AND RETURN
	SUBTTL	ERLST. UUO -- RETURN THE STATUS FOR ALL DEVICE WITH ERRORS

;ERLST. UUO
;CALL WITH:
;	MOVEI	AC,BLOCK
;	ERLST.	AC,
;	  RETURN HERE ERROR CODE IN AC
;	RETURN HERE BLOCK UPDATED
;
;FORMAT OF BLOCK:
;
;	!------------------!------------------!
;	! # OF WORD IN BLK ! CHANNEL NUMBER   !
;	!------------------!------------------!
;	! NUMBER OF DEVICES WHICH HAVE ERRORS !
;	!------------------!------------------!
;	! UDX FOR FIRST DEV! GETSTS FOR DEV   !
;	!------------------!------------------!
;	/				      /
;	/				      /
;	/				      /
;	!------------------!------------------!
;	! UDX FOR LAST DEV ! GETSTS FOR DEV   !
;	!------------------!------------------!
;
;NOTE: THE NUMBER OF DEVICE WHICH HAVE ERROR WILL BE GREATER THAT THE 
;	NUMBER OF UDX'S RETURNED IF THERE WAS NOT ENOUGH ROOM IN THE
;	BLOCK.
;
ERLST::	PUSHJ	P,SAVE4##	;SAVE SOME AC'S
	HRR	M,T1		;ADDRESS OF BLOCK
	PUSHJ	P,GETWDU##	;GET THE WORD
	MOVEI	P2,0		;ERROR COUNT
	HLRZ	P3,T1		;NUMBER OF WORDS IN BLOCK
	HRRZ	P1,T1		
	PUSHJ	P,SETUF##
	  JRST	ECOD1##		;(1)NOT AN OPEN CHAN #
	LDB	T1,PDVTYP##	;GET DEVICE TYPE
	CAIE	T1,.TYMPX	;SKIP IF MPX:
	JRST	ECOD2##		;NOT MPX: DEVICE
	AOS	P1,M		;WHERE TO PUT ERROR COUNT
	HLRZ	P4,DEVXTR(F)	;SET P4 = ADDRESS OF CDT
	JUMPE	P4,ERLSTX	;JUMP IF NO DEVICES CONNECTED
	HLRZ	T1,CDTSIZ(P4)	;NUMBER OF WORDS IN CDT
	SUB	T1,CDTFRE(P4)	;MINUS FREE = # IN USE
	MOVNI	T4,-1(T1)	;MAKE NEGATIVE
	HRLZ	T4,T4		;PUT IN LH
	HRRI	T4,1(P4)	;POINTER IN RH
ERLSTL:	HRRZ	F,(T4)		;GET POINTER TO DDB
	JUMPE	F,ERLSTA	;SHOULD NEVER HAPPEN
	MOVE	T1,DEVIOS(F)	;GET STATUS
	TRNN	T1,760000	;ANY ERRORS?
	JRST	ERLSTA		;NO--LOOK AT NEXT DEVICE
	HLL	T1,(T4)		;TOSS IN UDX
	ADDI	P2,1		;COUNT ERROR
	SOSL	P3		;SKIP IF NO ROOM
	PUSHJ	P,PUTWD1##	;ELSE STORE
ERLSTA:	AOBJN	T4,ERLSTL	;LOOP BACK OVER ALL DEVICES
ERLSTX:	MOVE	T1,P2		;COUNT OF ERRORS
	HRR	M,P1		;WHERE TO PUT IT
	PUSHJ	P,PUTWDU##	;STORE IT
	JRST	CPOPJ1##	;GOOD RETURN
	SUBTTL	CLRST. UUO -- CLEAR DEVICE STATUS

;UUO TO CLEAR DEVICE STATUS
;CALL WITH:
;	MOVE	AC,[LENGTH,,BLOCK]
;	CLRST.	AC,
;	  RETURN HERE ERROR CODE IN AC
;	RETURN HERE IF OK
;
;BLOCK CONTAINS:
;
;	!------------------!------------------!
;	! UDX, CHAN # OR SIXBIT DEVICE NAME   !
;	!------------------!------------------!
;	!         0        ! SETSTS VALUE     !
;	!------------------!------------------!
;	! UDX, CHAN # OR SIXBIT DEVICE NAME   !
;	!------------------!------------------!
;	!         0        ! SETSTS VALUE     !
;	!------------------!------------------!
;	/				      /
;	/				      /
;	/				      /
;	!------------------!------------------!
;	! UDX, CHAN # OR SIXBIT DEVICE NAME   !
;	!------------------!------------------!
;	!         0        ! SETSTS VALUE     !
;	!------------------!------------------!
;
CLRST::	PUSHJ	P,SAVE2##	;SAVE P1 AND P2
	HRRZ	P1,T1		;SAVE ADDRESS IN P1
	HLRZ	P2,T1		;SAVE COUNT IN P2
CLRSTL:	SUBI	P2,2		;COUNT ANOTHER TWO WORDS
	JUMPL	P2,CPOPJ1##	;ALL DONE IF NEGATIVE
	HRR	M,P1		;GET ADDRESS OF DEVICE
	PUSHJ	P,GETWDU##	;GET DEVICE NAME
	PUSHJ	P,DVCNSG##	;FIND THE DDB
	  JRST	ECOD1##		;(1) ILLEGAL DEVICE NAME
	LDB	T1,PJOBN##	;GET THE OWNER
	CAME	T1,.CPJOB##	;IS IT THIS JOB?
	JRST	ECOD2##		;(2) DEVICE NOT OWNED BY THIS JOB
	PUSHJ	P,GETWD1##	;GET THE NEW STATUS
	TLNE	T1,-1		;ANYTHING IN LH?
	PJRST	ILLMOD##	;YES--ILLEGAL DATA MODE
	HRR	M,T1		;COPY RH BITS
	PUSHJ	P,SETIOS##	;SET THE STATUS
	ADDI	P1,2		;ADVANCE TO NEXT ENTRY
	JRST	CLRSTL		;LOOP OVER ALL REQUESTS
	SUBTTL	WAIT UUO

;HERE FROM WAIT1 WHEN CALLED TO WAIT FOR AN MPX CHANNEL

MPXWAI::POP	P,T1		;RESTORE T1
	PUSHJ	P,SAVT##	;SAVE THE T ACS
	PUSHJ	P,SAVE3##	;AND THE P ACS TOO
	SETZ	P2,		;INITIALIZE FOR NXTDV LOOP
	MOVE	P3,F		;MPX DDB ADDRESS IN P3

MPXWI1:	PUSHJ	P,NXTDV		;GET NEXT DEVICE ON THIS MPX CHANNEL
	  JRST	MPXWI2		;DONE THEM ALL, RETURN
	MOVEI	T1,WAIT1M##	;CALL THE WAIT1 ROUTINE
	PUSHJ	P,CALSER	;CALL THE CALL CALLER
	  JFCL			;HO HUM
	JRST	MPXWI1		;LOOP FOR REST OF DEVICES

MPXWI2:	MOVE	F,P3		;RESTORE F
	MOVE	S,DEVIOS(F)	;ON G.P.'S
	POPJ	P,		;RETURN TO WAIT1'S CALLER



;HERE FROM UUOCON ON WAIT UUO FOR DEVICE MPX

MPXWAT::PUSHJ	P,SAVE3##	;SAVE P1-P3
MPXWA0:	SETZ	P2,		;INDICATE FIRST CALL TO NXTDV
	MOVE	P3,F		;SAVE MPX DDB
MPXWA1:	PUSHJ	P,NXTDV		;FIND THE NEXT DEVICE CONNECTED TO THE MPX
	  JRST	MPXWA2		;NO MORE
	MOVEI	T1,WAIT1M##	;WAIT FOR I/O TO STOP ON THAT DEVICE
	PUSHJ	P,CALSER	;CALL WAIT1
	  JFCL			;HO HUM
	JRST	MPXWA1		;CHECK NEXT DEVICE
MPXWA2:	MOVE	F,P3		;RESTORE ADDRESS OF MPX DDB
	MOVE	S,DEVIOS(F)	;MPX:'S I/O STATUS
	TLNN	S,IOSTBL	;TROUBLE SET (DEVICE HAS OUTPUT)?
	POPJ	P,		;NO, ALL OUTPUT OUT, ALL DEVICES IDLE
MPXWA3:	PUSHJ	P,MPXTBL	;TRY TO CLEAR UP TROUBLE
	MOVE	S,DEVIOS(F)	;IO STATUS FOR THE MPX DDB
	TLNN	S,IOSTBL	;TROUBLE STILL PRESENT?
	JRST	MPXWA0		;NO, WAIT FOR I/O COMPLETION
	MOVEI	T1,^D1		;YES, SLEEP A WHILE
	MOVE	J,.CPJOB##	;CURRENT JOB'S JOB NUMBER
	PUSHJ	P,SLEEPF##	;WAIT FOR TROUBLE TO BE FIXED
	JRST	MPXWA3		;AND TRY AGAIN
;SUBROUTINE TO SEE IF ANY DEVICE ON AN MPX CHANNEL IS IN TROUBLE
; AND IF SO, GET THE DEVICE ROUTINE STARTED AGAIN
;CALLING SEQUENCE:
;	MOVE	F,ADDRESS OF THE MPXDDB
;	PUSHJ	P,MPXTBL
;ALWAYS RETURN CPOPJ

MPXTBL:	MOVSI	S,IOSTBL(MPXVMF)  ;DEVICE IN TROUBLE BIT
	TDNN	S,DEVIOS(F)	;SOME DEVICE CONNECTED TO THIS MPX IN TROUBLE?
	POPJ	P,		;NO, NOTHING TO DO
	ANDCAB	S,DEVIOS(F)	;CLEAR TROUBLE IN HOPES THAT IT WILL CLEAR UP
	PUSHJ	P,SAVE3##	;SAVE P1-P3
	SETZ	P2,		;INDICATE FIRST CALL TO NXTDV
	MOVE	P3,F		;SAVE ADDRESS OF THE MPX DDB
MPXTB1:	PUSHJ	P,NXTDV		;FIND THE NEXT DEVICE CONNECTED TO THE MPX DDB
	  JRST	MPXTB2		;NO MORE
	TLNN	S,IOSTBL	;IS THIS DDB IN TROUBLE?
	JRST	MPXTB1		;NO, LOOK AT THE NEXT DDB
	MOVSI	T1,(MPXVMF)	;NOTE THE POSSIBILITY OF A PAGE FAULT
	IORM	T1,DEVIOS(P3)	; IN THE MPX DDB
	HRRZ	T1,DEVOAD(F)	;ADDRESS OF CURRENT OUTPUT BUFFER
	PUSHJ	P,UADRCK##	;CHECK THAT THE BUFFER IS OK
	PUSHJ	P,BRNGE##	;MAKE SURE THAT THE ENTIRE BUFFER IS IN CORE
	MOVSI	T1,(MPXVMF)	;A PAGE FAULT DIDN'T OCCUR
	ANDCAM	T1,DEVIOS(P3)	; SO CLEAR THE BIT
	MOVSI	S,IOSTBL	;CLEAR THE TROUBLE BIT
	ANDCAB	S,DEVIOS(F)	; AND SETUP S
	PUSHJ	P,MSGCOU	;DO A CALOUT (START UP OUTPUT)
	JRST	MPXTB1		;AND LOOP OVER ALL CONNECTED DEVICES

MPXTB2:	MOVE	F,P3		;RESTORE F
	POPJ	P,		;AND RETURN
;SUBROUTINE TO FIND THE NEXT DEVICE CONNECTED TO AN MPX CHANNEL
;CALLING SEQUENCE
;	MOVE	F,ADDRESS OF THE MPXDDB FOR THE FIRST CALL
;	MOVEI	P1,0 (FIRST CALL) OR WHAT WAS RETURNED ON PREVIOUS CALL
;				; ON SUBSEQUENT CALLS
;	PUSHJ	P,NXTDV
;	  RETURNS HERE IF NO MORE CONNECTED DEVICES
;	RETURNS HERE WITH F POINTING AT THE NEXT CONECTED DEVICE,
;	S SETUP FROM DEVIOS FOR THAT DEVICE

NXTDV:	JUMPN	P2,NXTDV1	;JUMP IF NOT THE FIRST CALL
	HRRZ	P1,DEVXTR(F)	;FIRST CALL, P1 = THE NUMBER OF CONNECTED DEVICES
	HLRZ	P2,DEVXTR(F)	;P2 = ADDRESS OF THE CDT
NXTDV1:	SOJL	P1,CPOPJ##	;RETURN IF NO MORE CONNECTED DEVICES
	HRRZ	F,1(P2)		;ADDRESS OF THE DDB FOR THE NEXT CONNECTED DEVICE
	MOVE	S,DEVIOS(F)	;IO STATUS FOR THAT DDB
IFN FTMP,<
	PUSHJ	P,SETCPF##	;GET ON CORRECT CPU
>
	AOJA	P2,CPOPJ1##	;STEP P2 TO NEXT SLOT IN CDT AND SKIP RETURN

;SUBROUTINE TO DETERMINE IF THE CURRENT DEVICE IS AN MPX DDB OR IS
; CONNECTED TO AN MPX DDB
;CALLING SEQUENCE:
;	MOVE	F,DDB ADDRESS
;	PUSHJ	P,CHKMPX
;RETURNS CPOPJ IF AN MPX DDB OR A DDB FOR A DEVICE CONTROLLED BY
; MPXSER, CPOPJ1 OTHERWISE

CHKMPX::LDB	T1,PDVTYP##	;DEVICE TYPE
	MOVE	T3,DEVMSG(F)	;WORD WHICH CONTAINS BIT WHICH SAY CONNECTED TO AN MPX
	CAIE	T1,.TYMPX	;AN MPX DDB?
	TRNE	T3,DEPMSG	;OR CONNECTED TO AN MPX CHANNEL?
	POPJ	P,		;YES, MULTIPLEXED RETURN
	JRST	CPOPJ1##	;NO, NORMAL DEVICE RETURN
	SUBTTL	OUT UUO

;HERE FROM UUOCON ON AN OUT UUO FOR MPX:. UUOCON HAS NOT DONE
; ANYTHING YET

MSGOUT::PUSHJ	P,SAVE4##	;EAT SOME PDL
	PUSHJ	P,MPXTBL	;GET DEVICES GOING AGAIN IF IN TROUBLE
	HLRZ	P4,DEVXTR(F)	;P4 GETS ADDRESS OF CDT
	JUMPLE	P4,NDCERR	;BETTER BE ONE
	HRRZ	P2,F		;P2 IS ADDRESS OF MPX DDB
	PUSHJ	P,JDAADP##
	HLL	P2,0(T1)	; IN LEFT HALF OF P2
	HLR	M,DEVBUF(P2)	;ADDRESS OF OUTPUT BUFFER HDR
	ADDI	M,3		;ADDRESS OF 4TH WORD
	PUSHJ	P,GETWDU##	;GET THE WORD
	PUSHJ	P,FNDUDX	;FIND THE UDX
	  PJRST	NCDERR		;NO CONNECTED DEVICE
	MOVE	P3,F		;P3 HOLDS THE ADDRESS OF THE TARGET DDB
IFN FTMP,<
	PUSHJ	P,SETCPF##	;GET ON CORRECT CPU
>
	SUBI	M,3		;FIRST WORD OF HEADER
	PUSHJ	P,GETWDU##	;PICK UP WORD
	TLNN	P2,OUTBFB	;OUTBUF DONE?
	JRST	FIROUT		;NO--SETUP RING
	JUMPE	T1,STOUT1	;GO WAIT FOR AN EMPTY BUFFER IF NEEDED
	JUMPL	T1,FIROUT	;JUMP IF DUMMY OUTPUT
	MOVEI	T2,DEPOND	;NOTHING PENDING.  DO WE KNOW IT?
	TDNE	T2,DEVAIO(P2)	;IF WE DON'T, WE HAVEN'T CLEANED UP THE BUFFER RING HEADER
	JRST	STOUT1		; SO LET'S GO TO NXTOUT IF WE CAN
	PUSH	P,T1		;SAVE ADDRESS OF FIRST BUFFER
	PUSH	P,M		;SAVE ADDRESS OF RING HEADER
	HRR	M,T1		;SETUP M FOR VMCHEK
	PUSHJ	P,VMCHEK	;CHECK ALL BUFFERS ARE IN CORE
	MOVEI	T1,(T1)		;CLEAR LH OF 1ST BUFFER ADDR
	PUSHJ	P,UADRCK##	;MAKE SURE IT'S LEGAL
	POP	P,M		;RESTORE HEADER ADDR
	MOVEI	T1,(M)		;RH ONLY INTO T1
	HRRZ	T2,(P)		;GET 1ST BUFFER ADDRESS
	PIOFF			;PREVENT RACES
	EXCTUX	<HRRZ T3,(T2)>	;GET POINTER TO NEXT BUFFER
	EXCTXU	<MOVEM T3,(T1)>	;AND STORE IT IN HEADER
	PION			;ALLOW IME STOPCDS

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

	MOVSI	T1,IOUSE	;GET THE USE BIT
	EXCTUU	<IORM T1,(T2)>	;AND SET IT FOR THOSE DEVICES THAT CHECK

;***********************************************************************
;
;ALL DEVICES CONTROLLED BY AN MPX CHANNEL MUST EXPLICITLY CHECK THAT
;THE IOUSE BIT IS SET. IF IT IS NOT SET THEN THE DEVICE SERVICE ROUTINE
;MUST SKIP THE OUTPUT AND DIRECTLY CALL ADVBFE IMMEDIATELY. THIS IS BE-
;CAUSE THE LAST CALL TO ADVBFE MARKED THE BUFFER AS OUTPUT BUT WAS UN-
;ABLE TO FREE THE BUFFER (RETURN IT TO THE FREE LIST AND ADVANCE THE
;ACTIVE LIST TO THE NEXT BUFFER TO BE OUTPUT) DUE TO (E.G.,) A PAGE
;FAULT CHAINING DOWN THE MPX FREE LIST. ALTHOUGH THIS SHOULD NOT HAPPEN,
;IF THE DEVICE IS A NETWORK DEVICE (FOR EXAMPLE) AND IT RUNS OUT OF
;DATA REQUESTS THE DEVICE IS SHUT DOWN. AT THIS POINT THE FREE LIST CAN
;BE PAGED OUT WHILE THE DEVICE STILL HAS ACTIVE BUFFERS (DEVOAD POINTS
;TO NEXT BUFFER TO OUTPUT). IF OUTPUT IS STARTED AT THIS POINT THEN
;AT OUTPUT COMPLETION (INTERRUPT LEVEL) THE BUFFER WILL NOT BE FREED UP
;SINCE MSGBFE (ADVBFE) CAN'T CHAIN DOWN THE FREE LIST, BUT IT HAS MARKED
;THE BUFFER AS NO LONGER CONTAINING ACTIVE DATA.
;
;FOR THIS REASON ALL ROUTINES WHICH WANT TO START UP OUTPUT FOR A MPX:
;CONTROLLED DEVICE SHOULD GO THROUGH MSGCOU RATHER THAN CALLING CALOUT
;DIRECTLY.
;
;***********************************************************************

	POP	P,U		;ADDRESS OF BUFFER TO USE
	PUSHJ	P,STOCNT	;COMPUTE BYTE OR WORD COUNT AND STORE
				; IN BUFFER, RETURN WITH WC IN T1
	PUSH	P,T1		;SAVE COMPUTED WORD COUNT
	HRRZI	T3,(U)		;FIRST WORD OF BUFFER
	PUSHJ	P,BADRCK##	;ADDRESS CHECK
	  JRST	ADRERR##	;DID NOT PASS
	HRRI	M,(U)		;ADDRESS OF THE CURRENT BUFFER
	PUSHJ	P,GETWDU##	;GET THE LINK WORD
	HLRZ	T2,T1		;GET USER'S BUFFER SIZE
	POP	P,T3		;RECALL WC FROM USER'S BYTE PTR
	SKIPL	T3		;NEGATIVE WORD COUNTS ARE ILLEGAL
	CAIG	T2,(T3)		;DOES WORD COUNT OVERFLOW BUFFER?
	JRST	ADRERR##	;YES! GO PREVENT HORRIBLE THINGS.
	TRZ	T1,-1		;NOT LINKED TO ANYTHING AT ALL
	PUSHJ	P,PUTWDU##	;STORE UPDATED LINK
	HLRZ	T1,DEVBUF(P3)	;PICK UP ADDRESS OF LAST FULL OUTPUT BUFFER
	SKIPE	T1		;SKIP OVER CHECK IF ZERO (= JUMPE T1,NEWOBF)
	PUSHJ	P,UADRCK##	;ADDRESS CHECK LAST BUFFER
	PIOFF			;AVOID RACES
	HLRZ	T2,DEVBUF(P3)	;ADDRESS OF LAST BUFFER IN LIST
	JUMPE	T2,NEWOBF	;JUMP IF BUFFER FREED AT INTERRUPT LEVEL
	EXCTUU	<HRRM U,@T1>	;STORE NEW LINK
	CAIA			;START UP OUTPUT
NEWOBF:	HRRM	U,DEVOAD(P3)	;REMEMBER ADDRESS OF LIST
	HRLM	U,DEVBUF(P3)	;SAVE ADDRESS AS LAST BUFFER ON ACTIVE LIST
	PION
;CONTINUED FROM PREVIOUS PAGE

STOUTP:	MOVE	F,P3		;ADDRESS OF TARGET DDB
	MOVSI	S,IOSTBL	;MAGIC SYNC BIT
	ANDCAB	S,DEVIOS(F)	;CLEAR (ALSO LOAD UP S)
	PUSHJ	P,MSGCOU	;CALL CALOUT (TO START UP OUTPUT)
STOUT1:	HLR	M,DEVBUF(P2)	;ADDRESS OF NEXT BUFFER
	PUSHJ	P,GETWDU##	;GET IT
	TRNE	T1,-1		;IS IT AVAILABLE
	JRST	NXTOUT		;YES--START THE NEXT OUTPUT IF WE CAN
	MOVEI	T1,DEPAIO	;ASYNCHRONOUS OUTPUT
	TDNE	T1,DEVAIO(P2)	; ..
	JRST	STOUT2		;YES--DO NOT WAIT
	PUSHJ	P,WSYNC##	;NO--WAIT FOR I/O ACTIVE TO CLEAR
	HLR	M,DEVBUF(P2)	;SEE IF USER WIPED-OUT
	PUSHJ	P,GETWDU##	;BUFFER HEADER
	TRNN	T1,-1		;IS IT 0?
	JRST	[MOVE F,P2	;NEED MPXDDB
		 JRST ADRERR##]	;FOR ADRERR
	TLZE	S,IOSTBL	;ERROR NEEDING A RETRY
	JRST	STOUTP		;YES--RETRY
	JRST	STOUT1		;NO--LOOK FOR NEXT BUFFER (BETTER BE ONE)
STOUT2:	MOVEI	T1,DEPOND
	HRRZ	F,P2
	IORM	T1,DEVAIO(F)	;TTY BUT NONE PENDING
	POPJ	P,0
;HERE ON THE FIRST OUTPUT UUO
FIROUT:	TRNE	T1,-1		;OUTBUF DONE?
	JRST	FIROU1		;YES--DO NOT DO ONE NOW
	HRRI	M,2		;TWO BUFFERS
	MOVE	F,P2		;POINT TO MPX DDB
	PUSHJ	P,UOUTBF##	;BUILD THE RING
FIROU1:	HLR	M,DEVBUF(P2)	;ADDRESS OF RING HEADER
	PUSHJ	P,VMCHKH	;MAKE SURE ALL BUFFERS ARE IN CORE
	HLR	M,DEVBUF(P2)	;ADDRESS OF RING HEADER
	PUSHJ	P,GETWDU##	;GET ADDRESS OF FIRST BUFFER
	HRRM	T1,DEVOAD(P2)	;STORE IN DDB
	HRRZ	T1,T1		;CLEAR VIRGIN RING BIT
	PUSHJ	P,PUTWDU##	;STORE BACK IN USER SPACE
	PUSHJ	P,JDAADP##
	MOVSI	T2,OUTBFB	; UUO PROGRESS BIT IS
	IORM	T2,0(T1)	; SET FOR THIS CHANNEL
	HRRZ	T1,DEVOAD(P2)	;RESTORE T1

;HERE WHEN SETUP FOR THE NEXT OUTPUT
NXTOUT:	HRRZS	T1		;CLEAR ANY LH JUNK
	PUSH	P,T1		;SAVE ADDRESS OF BUFFER
	PUSHJ	P,BUFCLR##	;CLEAR THE BUFFER
	  PJRST	ADRERR##	;ADDRESS CHECK
	MOVEI	T2,DEPOND	;CLEAR OUTPUT NOT DONE YET
	ANDCAM	T2,DEVAIO(P2)	; SINCE THERE IS A BUFFER AVAILABLE NOW
	POP	P,U		;RESTORE THE ADDRESS
	HRR	M,U		;PICK UP THE LINK WORD
	HLRZ	U,DEVBUF(P2)	;ADDRESS OF THE RING HEADER
	PUSHJ	P,GETWDU##	; ..
	MOVEI	T2,(M)		;ADDRESS OF THE DATA
	LDB	J,[POINT 17,T1,17]	;PICK UP BUFFER SIZE
	SOJA	J,IOSETC##	;STORE BYTE COUNT AND RETURN
;SUBROUTINE TO MAKE SURE ALL BUFFERS ARE IN CORE
;CALL WITH:
;	M = ADDRESS-OF-FIRST-BUFFER
;	P3 = ADDRESS OF THE TARGET DDB
;	PUSHJ	P,VMCHEK
;	RETURN HERE IF ALL OK
;
VMCHKH:	PUSHJ	P,GETWDU##	;GET FIRST (MPX: FREE) BUFFER ADDRESS
	HRR	M,T1		;TO M FOR VMCHEK
VMCHEK:	PUSH	P,T1		;SAVE T1
	MOVE	T1,DEVIOS(P3)	;DEVICE I/O STATUS
	TRNE	T1,IOACT	;IF DEVICE IS I/O ACTIVE
	JRST	TPOPJ##		;THEN BUFFERS WILL BE ASSUMED OK
				; (EVEN IF THEY AREN'T (WHICH IS QUITE
				;  POSSIBLE) THERE IS NOTHING THAT CAN
				;  BE DONE ABOUT IT IF ANY IOACT IS ON)
	TRNE	M,-1		;ANY (MPX FREE LIST) BUFFERS TO CHECK?
	PUSHJ	P,BCHCK		;MAKE SURE BUFFERS ARE IN CORE

;WE HAVE JUST TOUCHED EVERY BUFFER IN THE FREE LIST.
;NOW CHECK OUT THE DEVICE ACTIVE BUFFER LIST.

	HLR	M,DEVEVM(P3)	;ASSUME EVM, GET USER VIRTUAL ADDRESS
	TRNN	M,-1		;UNLESS OF COURSE NOT EVM MAPPED
	HRR	M,DEVOAD(P3)	;IN WHICH CASE GET TRUE USER ADDRESS
				; NOTE THAT THE ABOVE THREE INSTRUCTIONS
				; NEED NOT BE RUN PIOFF'ED SINCE THE WORST
				; THAT CAN HAPPEN IS THAT THE DEVICE FREED
				; THE BUFFER (FREE LIST KNOWN OK) BUT
				; COULD NOT START UP THE NEXT BUFFER DUE
				; TO A PAGE FAULT ETC. THE DEVICE WILL BE
				; RESTARTED ON NEXT CALL TO MPXTBL.
	TRNE	M,-1		;ANY DEVICE ACTIVE LIST BUFFERS TO CHECK?
	PUSHJ	P,BCHCK		;MAKE SURE BUFFERS ARE IN CORE
	JRST	TPOPJ##		;ALL OK



;VERIFY A BUFFER CHAIN IS IN MEMORY AND ADDRESSABLE

BCHCK:	PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	P1,^D10000	;DEFEND AGAINST LOOPS
BCHCK1:	PUSHJ	P,GETWRD##	;GET LINK WORD
	  JRST	ADRERR##	;ADDRESS CHECK
	MOVEI	T2,(M)		;ADDRESS OF CURRENT BUFFER
	HRR	M,T1		;ADDRESS OF NEXT BUFFER
	MOVE	T1,T2		;ADDRESS OF CURRENT BUFFER
	PUSHJ	P,BRNGE##	;CHECK IT
	TRNE	M,-1		;LAST BUFFER?
	SOJGE	P1,BCHCK1	;NO
	JUMPL	P1,ADRERR##	;JUMP IF ADDRESS CHECK
	PJRST	SCDCHK##	;RETURN WITHOUT HOGGING CPU
;SUBROUTINE TO DO ALL GOOD THINGS FOR BYTE COUNTS AND WORD COUNTS
;CALLED WITH:
;	U = ADDRESS OF BUFFER (ACTUALY BUFFER + 1)
;	M = ADDRESS OF RING HEADER
;	P2 = ADDRESS OF MPX DDB
;	P3 = ADDRESS OF TARGET DDB
;	PUSHJ	P,STOCNT
;	RETURN HERE LENGTH OF BUFFER IN T1 (ALWAYS WORDS)
;
;THE COUNT IN THE BUFFER IS COMPUTED FROM THE BYTE POINTER IF IOWC=0
;	AND THEN STORED IN THE BUFFER

STOCNT:	PUSHJ	P,SAVE1##	;SAVE P1
	PUSH	P,M		;PRESERVE M
	PUSHJ	P,GETWD1##	;PICK UP THE BYTE POINTER
	MOVE	P1,T1		;SAVE POINTER IN P1
	HRRI	M,1(U)		;POINT TO I/O BUFFER
	MOVE	S,DEVIOS(P2)	;PICK UP IOS WORD
	LDB	T2,PIOMOD##	; AND THE ASSOCIATED I/O MODE
	TRNE	S,IOWC		;DID USER COMPUTE WORD COUNT
	JRST	STOCN7		;YES--GO CONVERT TO WORDS
	SUBI	T1,1(U)		;GET THE WORD COUNT
	HRRE	T1,T1		;FILL LH WITH SIGN BITS (0 I HOPE)
	CAIE	T2,BYTMOD	;IF "BYTE" MODE
	CAIN	T2,PIMMOD	;OR PACKED IMAGE MODE
	JRST	STOCN2		;THEN WE MUST DEAL WITH BYTES NOT WORDS
	CAIN	T2,A8		;SIMILARLY FOR 8-BIT ASCII MODE
	JRST	STOCN2		;BE CONSISTENT
	PUSHJ	P,PUTWDU##	;STORE UPDATED WORD COUNT IN BUFFER HEADER
	PJRST	MPOPJ##		;RESTORE M AND RETURN WITH WORD COUNT IN T1

;HERE TO DEAL WITH BYTE-MODE BUFFERS

STOCN2:	LDB	T3,[POINT 6,P1,11]  ;BITS PER BYTE
	MOVEI	T2,^D36		;BITS PER WORD
	IDIVI	T2,(T3)		;BYTES PER WORD
	IMULI	T1,(T2)		;BYTES PER CURRENT BUFFER (GENEROUS ESTIMATE)
	LDB	T2,[POINT 6,P1,5]  ;BITS FREE IN LAST WORD OF BUFFER
	CAILE	T2,^D36		;LIMIT TO ONE WORD'S WORTH
	MOVEI	T2,^D36		;WHAT ABOUT OWGP'S?
	LDB	T3,[POINT 6,P1,11];BITS PER BYTE
	IDIVI	T2,(T3)		;BYTES FREE IN LAST WORD OF BUFFER
	SUBI	T1,(T2)		;TRUE BYTE COUNT FOR BUFFER
	PUSHJ	P,PUTWDU##	;STORE UPDATED BYTE COUNT IN BUFFER HEADER
	SUBI	P1,1(U)		;RECALCULATE THE WORD COUNT
	HRRE	T1,P1		;AND POSITION IT IN T1
	PJRST	MPOPJ##		;RETORE M AND RETURN WITH WORD COUNT IN T1
;HERE WHEN USER-SUPPLIED WORD COUNT

STOCN7:	PUSHJ	P,GETWDU##	;GET THE WORD COUNT FROM USER'S BUFFER HEADER
	CAIE	T2,BYTMOD	;"BYTE" MODE I/O?
	PJRST	MPOPJ##		;NO, RETURN WITH WORD COUNT IN T1
	LDB	T3,[POINT 6,P1,11]  ;BITS PER BYTE
	MOVEI	T2,^D36		;BITS PER WORD
	IDIVI	T2,(T3)		;BYTES PER WORD
	IDIVI	T1,(T2)		;WORDS PER CURRENT BUFFER
	CAILE	T2,0		;IF ANY BYTES LEFT OVER,
	ADDI	T1,1		;ALLOW FOR TRAILING PARTIAL WORD
	PJRST	MPOPJ##		;RETURN WORD COUNT IN T1
;SUBROUTINE TO CALL CALOUT FOR AN MPX-CONTROLLED DEVICE
;CALLED WITH F/ADDRESS OF TARGET DDB
;
;HANDLES THE SPECIAL CASE OF SKIPPING THE OUTPUT BUFFER IF THE
;USE BIT IS CLEARED (SEE OUT UUO CODE).

MSGCOU:	MOVE	S,DEVIOS(F)	;DEVICE I/O STATUS
	TRNE	S,IOACT		;IS DEVICE ALREADY I/O ACTIVE?
	POPJ	P,		;YES, DON'T TOUCH IT!
	HLRZ	T1,DEVEVM(F)	;ADDRESS OF USER BUFFER
	TRNN	T1,-1		;UNLESS NOT MAPPED IN EVM
	HRRZ	T1,DEVOAD(F)	;IN WHICH CASE DEVOAD IS USER ADDRESS
	EXCTUU	<SKIPG (T1)>	;IS USE BIT SET (DATA TO BE OUTPUT)?
	JRST	MSGCO5		;YES, ALL IS WELL
	PUSHJ	P,MSGBFE	;NO, AH HA! ADVANCE PAST THIS BUFFER
	  POPJ	P,		;CAN'T STARTUP OUTPUT (E.G., PAGE FAULT)
	PUSHJ	P,SCDCHK##	;BE SURE WE DON'T LOOP TOO LONG
	JRST	MSGCOU		;JUST TO MAKE SURE . . .

MSGCO5:	MOVEI	T1,CALOUT##	;BUFFER CONTAINS VALID OUTPUT DATA,
	PUSHJ	P,CALSER	;CALL CALOUT TO START UP OUTPUT
	  JFCL			;YAWN
	POPJ	P,		;RETURN



;SUBROUTINE TO EMULATE CALOUT FOR MPX CHANNELS
;CALLED WITH F/ ADDRESS OF MPX DDB

MSGDOU:	PUSHJ	P,SAVE3##	;WE USE ACS LEFT AND RIGHT
	SETZ	P2,		;INITIALIZE FOR NXTDV CALL LOOP
	MOVE	P3,F		;SAVE MPX DDB (AND SET FOR NXTDV)

MSGDO1:	PUSHJ	P,NXTDV		;GET NEXT DEVICE OWNED BY MPX CHANNEL
	  JRST	MSGDO2		;ALL DONE
	PUSHJ	P,MSGCOU	;CALL CALOUT TO START UP OUTPUT
	JRST	MSGDO1		;LOOP FOR REST OF DEVICES

MSGDO2:	MOVE	F,P3		;RESTORE F TO MPX DDB
	POPJ	P,		;RETURN TO CALOUT FOR MPX: DEVICE
	SUBTTL	MSGBFE MPXSER'S ADVBFE

;SUBROUTINE CALL WHEN AN OUTPUT BUFFER IS EMPTY
;CALLED WITH:
;	MOVX	F,DDB-ADDRESS (OF TARGET DEVICE)
;	PUSHJ	P,MSGBFE
;	  RETURN HERE IN NO MORE FULL BUFFERS
;	RETURN HERE IF MORE OUTPUT SHOULD BE DONE
;
MSGBFE::PUSHJ	P,SAVE4##	;SAVE ALL THE AC'S IN SIGHT
	PUSH	P,T3		; ..
	MOVE	P3,F		;P3 POINTS TO TARGET DDB
	HRRZ	P2,DEVXTR(P3)	;P2 POINTS TO MPX DDB
	HRRZ	T4,DEVEVM(P3)	;DOES THIS DEVICE HAVE EVM?
	JUMPE	T4,NOOEVM	;NO OUTPUT EVM
	MOVE	T4,DEVOAD(P3)	;EXEC VIRT ADDR OF CURRENT BUFFER
	MOVEM	S,-1(T4)	;TODD WILL HAVE FUN FINDING THIS
	MOVSI	T3,IOUSE	;THE BUFFER-IN-USE BIT
	ANDCAB	T3,(T4)		;THIS BUFFER IS NOW EMPTY
				; (SEE BIG COMMENT IN OUT UUO CODE)
	HLRZ	T1,DEVBUF(P2)	;POINTER TO THE HEAD OF FREE LIST
	MOVEI	P4,^D10000	;MAX BUFFERS LIMIT
	MOVEI	P1,-1		;IF THERE IS NO FREE BUFFER LIST 
MSBFE1:	PUSHJ	P,IADRCK##	;MAKE SURE POINTER IS LEGAL
	  PJRST	ADVSTP		;ILLEGAL ADDRESS
	  PJRST	ADVSTP		;CAUGHT IN THE ACT! STOP I/O.
	EXCTUU	<TDNN P1,(T1)>
	JRST	[HLL	T2,DEVEVM(P3)	;GET THE USER VIRT ADDR OF THIS BUFFER
		EXCTUU	<HLRM	T2,(T1)>;STORE THE POINTER IN PREVIOUS BUFFER
		HLLZS	(T4)	;MARK END OF LIST
		JRST	MSBFE2]	;LOOK FOR MORE BUFFERS
	EXCTUX	<HRRZ T1,(T1)>	;ELSE LINK TO NEXT BUFFER
	SOJG	P4,MSBFE1	;SEE IF END OF LIST YET
	JRST	ADVSTP		;FREE LIST MESSED UP
MSBFE2:	TRNN	T3,-1		;SKIP IF THERE IS MORE TO DO
	JRST	ADVST2		;NO MORE BUFFERS TO EMPTY, STOP I/O

;HERE IF THERE ARE MORE BUFFERS IN THE OUTPUT LIST

	PUSHJ	P,BADRCK##	;ADDRESS CHECK
	  JRST	ADVSTP		;OOO EVIL PERSON -- STOP I/O
	PUSHJ	P,ADVEVM##	;MAP THE NEXT BUFFER
	  JRST	ADVSTP		;NO MORE EVM -- RETRY ON THE NEXT UUO
	DPB	T1,PDVOAD##	;STORE NEW ADDRESS
	PJRST	ADVBU1##	;TRY TO CONTINUE
;HERE TO ADVANCE BUFFERS WHEN NOT MAPPED VIA EVM

NOOEVM:	HRRZ	T4,DEVOAD(P3)	;USER VIRT ADDRESS IF NOT EVM-MAPPED
	EXCTUU	<MOVEM S,-1(T4)>;STORE UPDATED STATUS BITS
	MOVSI	T3,IOUSE	;THE BUFFER-IS-FULL BIT
	EXCTUX	<ANDCAB T3,(T4)>;MARK BUFFER IS NOW EMPTY
				; (SEE BIG COMMENT IN OUT UUO CODE)
	HLRZ	T1,DEVBUF(P2)	;ADDRESS OF FREE LIST
	MOVEI	P4,^D10000	;SETUP A LOOP COUNTER
	MOVEI	P1,-1		;SETUP AC FOR TDNN BELOW
MSBFE4:	PUSHJ	P,IADRCK##	;BUFFER POINTERS MESSED UP?
	  PJRST	ADVSTP		;YES..STOP JOB.
	  PJRST	ADVSTP		;ILLEGAL ADDRESS
	EXCTUU	<TDNN P1,(T1)>	;END OF THE BUFFER LIST?
	JRST	[EXCTUU	<HRRM T4,(T1)>	;ADD TO END OF FREE LIST
		EXCTUU	<HLLZS (T4)>	;MAKE THIS BUFFER NEW END
		JRST	MSBFE5]	;SEE IF MORE BUFFERS
	EXCTUX	<HRRZ T1,(T1)>	;FOLLOW LINK
	SOJG	P4,MSBFE4	;LOOK AT NEXT BUFFER
	JRST	ADVSTP		;TOO MANY BUFFERS...STOP I/O.
MSBFE5:	TRNN	T3,-1		;MORE BUFFERS
	JRST	ADVST5		;NO MORE BUFFERS, STOP I/O

;HERE IF THERE ARE MORE BUFFERS IN THE OUTPUT LIST

	PUSHJ	P,BADRCK##	;CHECK THE ADDRESS OF NEXT BUFFER
	  JRST	ADVSTP		;BAD DO NOT USE FOR I/O
	HRRM	T3,DEVOAD(P3)	;VALID--STORE IN DDB
	PJRST	ADVBU1##	;TRY TO WRITE NEXT BUFFER



;HERE TO FORCE A DEVICE TO STOP (BUT WITH MORE DATA TO BE OUTPUT)

ADVSTP:	PUSHJ	P,RTNEVM##	;GIVE BACK EVM
	JRST	T3POPJ##	;RESTORE T3 AND RETURN

;HERE TO FORCE A DEVICE TO STOP DUE TO LACK OF DATA

ADVST2:	PUSHJ	P,RTNEVM##	;RETURN ANY EVM WE HAVE
ADVST5:	SETZM	DEVOAD(P3)	;NO FIRST BUFFER IN OUTPUT LIST
	HRRZS	DEVBUF(P3)	;NOR LAST BUFFER IN OUTPUT LIST
	JRST	T3POPJ##	;RESTORE T3, TAKE STOP I/O RETURN
	SUBTTL	IN UUO

;HERE FROM UUOCON ON INPUT UUO

MSGIN:	PUSHJ	P,SAVE4##	;SAVE P1-P4 SINCE WE USE THEM
	PUSHJ	P,MPXTBL	;GET DEVICES GOING AGAIN IF IN TROUBLE
MSGIN0:	MOVE	P2,F		;ADDRESS OF MPX DDB
	HLRZ	P4,DEVXTR(P2)	;ADDRESS OF CDT
	JUMPE	P4,NDCERR	;JUMP IF NO DEVICES CONNECTED
	HRRZ	P1,DEVXTR(P2)	;NUMBER OF DEVICES
	ADD	P4,P1		;END OF THE CDT
	AOS	P3,DEVCID(P2)	;POINTER TO THE NEXT INPUT DEVICE IN THE CDT
MSGIN1:	CAML	P3,P4		;BEYOND THE END OF THE CDT?
	HLRZ	P3,DEVXTR(P2)	;YES, GET THE ADDRESS OF THE START OF THE CDT
	MOVEM	P3,DEVCID(P2)	;STORE NEW CURRENT INPUT DEVICE POINTER
	SOJL	P1,MSGIN3	;JUMP IF ALL INPUT DEVICES HAVE BEEN POLLED
	HRRZ	F,1(P3)		;ADDRESS OF THE TARGET DDB
	MOVE	S,DEVIOS(F)	;PICK UP STATUS FLAG
	LDB	T1,PDVTYP##	;GET DEVICE TYPE
	CAIG	T1,TYPMAX	;SKIP IF TOO BIG
	PUSHJ	P,ITSTAB(T1)	;SEE IF INPUT IS AVAILABLE
MSGIN2:	  AOJA	P3,MSGIN1	;LOOP IF NO INPUT TO BE HAD
	MOVE	T1,DEVCHR(F)	;DEVICE CURRENTLY OFF LINE?
	TLNN	T1,DVOFLN	;IF SO, DON'T CONSIDER IT IN POLLING SEQUENCE
	TRNE	S,IOACT		;SKIP IF NOT ACTIVE
	JRST	MSGIN2		;ACTIVE--IGNORE FOR NOW AND KEEP LOOKING
	MOVE	T4,DEVIAD(P2)	;COPY ADDRESS OF THIS BUFFER
	DPB	T4,PDVIAD##	; INTO TARGET DDB
	EXCTUU	<SETZM 1(T4)>	;CLEAR WORD COUNT IN THE BUFFER HEADER
IFN FTMP,<
	PUSHJ	P,SETCPF##	;GET ON CORRECT CPU
>
	MOVEI	T1,CALIN##	;ROUTINE TO CALL
	PUSHJ	P,CALSER	;CALL SERVICE ROUTINE TO FILL 1 BUFFER
	  JFCL			;IGNORE SKIP/NONSKIP
	HLRZ	T1,1(P3)	;PICK UP THE UDX
	HRRZ	T2,DEVIAD(P2)	;GET UVA OF INPUT BUFFER
	EXCTUU	<SKIPN 1(T2)>	;IF THE SERVICE ROUTINE DON'T PUT ANYTHING
	JRST	MSGIN1		;  IN THE BUFFER, DONT' RETURN IT EMPTY
	EXCTUU	<HRLM T1,1(T2)>	;STORE IN BUFFER
	MOVE	F,P2		;SETUP F TO POINT TO MPX DDB
	PUSHJ	P,ADVBFF##	;ADVANCE BUFFERS
	  JFCL
	POPJ	P,0		;RETURN TO UUOCON

MSGIN3:	MOVE	F,P2		;SETUP F
	MOVEI	T1,DEPAIO	;ASYNCHRONOUS I/O
	TDNE	T1,DEVAIO(F)	; ..
	POPJ	P,0		;YES--RETURN
	MOVEI	T1,^D1		;NO--WAIT FOR A BUFFER TO BE READY
	PUSHJ	P,SLEEPF##	; ..
	JRST	MSGIN0		; ..
DEFINE ITEST(A),<
	REPEAT	TYPMAX+1,<
	POPJ	P,0
	>
	IRP	A,<
	RELOC	.TY'A+ITSTAB
	PJRST	IT$'A
	RELOC
>>

ITSTAB:	ITEST	<TTY,PTY,RDA,CDR>
;SUBROUTINE TO DETERMINE IF A TTY OR PTY DDB WILL GO INTO INPUT WAIT
;CALL WITH:
;	MOVEI	F,ADDRESS-OF-TTY-DDB
;	PUSHJ	P,IT$TTY
;	  RETURN HERE IF NOTHING TO READ
;	RETURN HERE IF 1 FULL LINE (AT LEAST)
;
IT$PTY:	LDB	U,PUNIT##	;GET PTY NUMBER
	ADDI	U,PTYOFS##	;ADD IN MAGIC OFFSET
	MOVE	U,LINTAB##(U)	;GET LDB ADDRESS
	PJRST	TOPSOP##	;CHECK IF OUTPUT IS PRESENT
IT$TTY:	SE1ENT			;ENTER SECTION 1
	MOVE	U,DDBLDB##(F)	;GET ADDRESS OF LDB
	JUMPE	U,CPOPJ##	;PUNT IF NO LDB
	SKIPLE	LDBBKC##(U)	;IS THERE A LINE?
	JRST	CPOPJ1##	;YES--RETURN TO INPUT
	PJRST	TTLCHK##	;NO--DO BETTER TEST

IT$RDA==:NTDIDA##		;CHECK FOR INPUT.
IT$CDR==:NTDIDA##
;SUBROUTINE TO SEE IF IOACT IS ON FOR ANY MPX DEVICE
;CALL WITH:
;	F = MPX DDB
;	PUSHJ	P,MPXACT
;	  RETURN HERE IF IO ACTIVE
;	RETURN HERE IF NOT ACTIVE

MPXACT::PUSHJ	P,SAVE4##	;WE MUST RESPECT ALL AC'S
	HLRZ	P4,DEVXTR(F)	;PICK UP CDT POINTER
	JUMPE	P4,CPOPJ1##	;JUMP IF NO DEVICES CONNECTED
	HLRZ	P1,CDTSIZ(P4)	;GET TOTAL SIZE
	SUB	P1,CDTFRE(P4)	;LESS FREE SLOTS
	MOVNI	P1,-1(P1)	;MAKE NEGATIVE
	JUMPE	P1,CPOPJ1##	;DONE NOW IF CDT EMPTY
	HRL	P1,P1		;PUT IN LH
	HRRI	P1,1(P4)	;POINTER TO CDT
MPXAC1:	MOVEI	P2,IOACT	;SET UP ACTIVE BIT
	MOVE	P3,(P1)		;GET POINTER TO DDB
	TDNN	P2,DEVIOS(P3)	;SKIP IF IOACT ON
	AOBJN	P1,.-2		;LOOK AT NEXT DEVICE
	JUMPGE	P1,CPOPJ1##	;NO ACTIVE DEVICES
	MOVSI	P2,DVTTY	;ACTIVE IS IT A TTY?
	TDNN	P2,DEVMOD(P3)	; ..
	POPJ	P,0		;FAIL--AT LEAST 1 ACTIVE DEVICE
	AOBJN	P1,MPXAC1	;NO--KEEP LOOKING
	PJRST	CPOPJ1##	;NO ACTIVE DEVICES



;SUBROUTINE TO SEE IF IOW IS ON FOR THE MPX DDB
;CALL WITH F = DDB
;	PUSHJ	P,MPXIOD
;	  RETURN HERE IF MPX DOESN'T HAVE IOW ON
;	RETURN HERE IF IOW WAS ON (CLEAR IT)

MPXIOD::PUSHJ	P,SAVE2##
	MOVEI	P1,DEPMSG	;MPX-CONTROLLED DDB?
	TDNN	P1,DEVMSG(F)
	POPJ	P,		;NO, NON-SKIP
	HRRZ	P1,DEVXTR(F)	;YES, GET MPX ADR
	JUMPE	P1,CPOPJ##
	MOVE	P2,DEVIOS(P1)	;IOS
	TLZN	P2,IOW		;WAITING?
	POPJ	P,		;NO-RETURN
	MOVEM	P2,DEVIOS(P1)	;YES, SAVE NEW STATE
	PJRST	CPOPJ1##	;AND SKIP-RETURN
	SUBTTL	SUBROUTINES

;SUBROUTINE TO FIND A DDB EITHER IN THE CDT OR BASED ON DEVICE NAME
;CALL WITH:
;	T1=UDX OR DEVICE NAME
;	P2=ADDRESS OF THE MPX DDB
;	PUSHJ	P,FNDDDB
;	  NO SUCH DEVICE
;	RETURN HERE DDB ADDRESS IN F

FNDDDB:	TLNE	T1,-1		;A UDX?
	PJRST	DVCNSG##	;NO, SEARCH FOR A DEVICE
	PUSHJ	P,SAVE4##	;SAVE ACS
	HLRZ	P4,DEVXTR(P2)	;CDT
	JUMPE	P4,CPOPJ##	;ERROR IF THERE ISN'T ONE
				;FALL INTO FNDUDX

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;SUBROUTINE TO FIND A UDX IN A CDT
;CALL WITH:
;	P4 = ADDRESS OF CDT
;	T1 = UDX TO LOOK FOR
;	PUSHJ	P,FNDUDX
;	  UDX NOT IN CDT
;	RETURN HERE DDB ADDRESS IN F
;
;AC USAGE IN FNDUDX:
;P1 = LOWER LIMIT	P2 = UPPER LIMIT	P3 = INCREMENT
;T2 = TEMP FOR ADDRESS CALCULATION	T3 = UDX FROM TABLE FOR COMPARISON

	CAIA			;ENTER HERE FROM FNDDDB WITH P?S SAVED
FNDUDX:	PUSHJ	P,SAVE3##	;SAVE P1 THRU P3
	MOVEI	P1,1		;INDEX OF FIRST ENTRY IN CDT
	HLRZ	P2,CDTSIZ(P4)	;SIZE 
	SUB	P2,CDTFRE(P4)	;MINUS FREE IS INDEX OF LAST ENTRY
FNUDX1:	CAILE	P1,(P2)		;IS LOWER LIMIT LESS THAN OR
				; EQUAL TO UPPER LIMIT?
	POPJ	P,0		;NO--UDX IS NOT IN CDT

;COMPUTE MID POINT IN RELEVANT RANGE

	MOVEI	P3,1(P1)	;LOWER LIMIT (+1 FOR ROUNDING)
	ADDI	P3,(P2)		;PLUS UPPER LIMIT
	LSH	P3,-1		;DIVIDED BY TWO
	MOVEI	T2,(P4)		;BASE OF TABLE
	ADDI	T2,(P3)		;PLUS INDEX GIVE ADDRESS OF ENTRY
	HLRZ	T3,(T2)		;PICK UP THE UDX
	JUMPE	T3,FNUDX3	;THE BEST ALGORITHMS NEED A FUDGE
	CAMGE	T3,T1		;THE COMPARE
	JRST	FNUDX2		;TRY LOWER HALF OF TABLE
	CAMN	T3,T1		;IS THIS THE ENTRY?
	JRST	[HRRZ F,(T2)	;YES--PICK UP DATA
		 JRST CPOPJ1##]	;GIVE GOOD RETURN
FNUDX3:	MOVEI	P2,-1(P3)	;SET NEW UPPER LIMIT
	JRST	FNUDX1		;TRY AGAIN

;HERE IF ENTRY IS TOO SMALL
FNUDX2:	MOVEI	P1,1(P3)	;BOOST UP THE LOWER LIMIT
	JRST	FNUDX1		;TRY AGAIN
;SUBROUTINE TO CALL A SERVICE ROUTINE VIA UUOCON
;CALL WITH:
;	T1 = ADDRESS OF ROUTINE TO CALL
;	F = A DDB ADDRESS
;	PUSHJ	P,CALSER
;	  RETURN HERE
;	OR HERE

CALSER:	PUSHJ	P,SAVE4##	;SAVE P1 THRU P4
	MOVE	P1,T1		
	PUSHJ	P,JDAADR##
	PUSH	P,USRHCU##	;SAVE THIS
	PUSH	P,0(T1)		;AND THIS
	HLL	F,DEVXTR(F)	;PICK UP FLAGS
	MOVE	S,DEVIOS(F)	;ALWAYS NICE TO HAVE S SET UP
	PUSHJ	P,(P1)		;CALL ROUTINE
	  SOS	-2(P)		;NON-SKIP
	PUSHJ	P,JDAADR##
	HLL	F,0(T1)		;GET UPDATED FLAGS
	HLLM	F,DEVXTR(F)	;STORE BACK IN DDB
	POP	P,0(T1)		;RESTORE JUNK
	POP	P,USRHCU##	; ..
	HRRZ	P1,DEVXTR(F)	;P1 POINTS TO THE MPX DDB
	MOVSI	P2,IOSTBL	;DEVICE OFF-LINE BIT
	TDNE	P2,DEVIOS(F)	;TROUBLE ON THE TARGET DEVICE?
	IORM	P2,DEVIOS(P1)	;YES, SET TROUBLE IN THE MPX DBB
	JRST	CPOPJ1##	;RETURN (SKIP MAY HAVE BEEN UNDONE)
	SUBTTL	ERROR ROUTINES

NDCERR:	JSP	T1,ERRPTU##	;PRINT MESSAGE
	ASCIZ	\I/O with no devices connected for \
	PJRST	DEVEXC##	;SAY DEVICE XXX

NCDERR:	PUSH	P,T1		;SAVE UDX
	PUSHJ	P,DVCNSG##	;FIND REAL DDB
	  SKIPA	T2,[SIXBIT .(NONE).] ;NO SUCH DEVICE NAME
	MOVE	T2,DEVNAM(F)	;REAL DEVICE NAME
	PUSH	P,T2		;SAVE NAME
	JSP	T1,ERRPTU##	;PRINT MESSAGE
	ASCIZ	/Output to unconnected device [/
	MOVE	T2,-2(P)	;PICKUP NAME
	PUSHJ	P,PRNAME##	;PRINT IT
	PUSHJ	P,INLMES##	;ADD SOME WORDS
	ASCIZ	\] UDX=\
	MOVE	T1,-3(P)	;PICKUP UDX
	PUSHJ	P,PRTDI8##	;PRINT UDX
	SUB	P,[2,,2]	;REMOVE TWO ITEMS FROM THE STACK
	PJRST	UUOPCP##	;PRINT PC AND STOP JOB
MPXEND:	END