Google
 

Trailing-Edge - PDP-10 Archives - AP-D543V_SB - msgser.mac
There are 5 other files named msgser.mac in the archive. Click here to see a list.
TITLE	MSGSER - SERVICE FOR DEVICE MPX: AND RELATED FUNCTIONS  V41074
SUBTTL	DONALD A. LEWINE   12 SEP 78
	SEARCH	F,S
	$RELOC
	$HIGH



;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1973,1974,1975,1976,1977,1978 BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
XP VMSGSR,41074

MSGSER:	ENTRY	MSGSER

;AC USAGE WITHIN MSGSER:
;
;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
;THE PROTOTYPE MPX DDB
	$LOW

MPXDDB::EXP	0		;DEVICE NAME. NOTE: WE DO NOT
				; WANT ANYONE TO FIND THE
				; PROTOTYPE, HENCE, THE FUNNY NAME.
	EXP	103		;BUFFER SIZE, MPX DEVICES NEVER
				; HANG
	EXP	0		;DEVIOS
MPXVMF==1B0			;ON IF A DEVICE IS IN TROUBLE AND THE OUTPUT
				; BUFFER IS PAGED OUT
	EXP	MPXDSP		;DEVSER
	XWD	DVIN+DVOUT+DVLNG,<1B<^D35-A>+1B<^D35-AL>+1B<^D35-IB>+1B<^D35-B>+1B<^D35-I>+1B<^D35-PIMMOD>> ;DEVMOD
	EXP	0		;LOGICAL NAME. NOTE: THIS WORD IS
				; ALWAYS ZERO.
	EXP	0		;DEVBUF. LH=ADDRESS OF OUTPUT
				; RH=ADDRESS OF INPUT
	XWD	R,0		;DEVIAD.
	XWD	R,0		;DEVOAD.
	EXP	0		;DEVSTS. DEVSTS FROM CONTROLED
				; DDB.
	XWD	.TYMPX,DEPEVM	;DEVSTA. MPX DDB (WE DO NOT USE
				; EVM)
	XWD	0,0		;DEVXTR. LH = ADDRESS OF THE
				; CDT IF ANY. RH = NUMBER OF
				; CONNECTED DEVICES.
	EXP	0		;DEVEVM. NOTE: WE NEVER HAVE ANY EVM.
	EXP	0		;DEVPSI. FLAGS FOR PSISER
	EXP	0		;DEVESE.
	EXP	0		;DEVECE.
	EXP	0		;DEVJOB. JOB NUMBER
	EXP	0		;DEVCID.  CURRENT INPUT DEVICE
	
MPXLEN==.-MPXDDB
MPX4WD==<MPXLEN+3>/4		;NUMBER OF 4 WORD BLOCKS NEEDED
				; TO HOLD DDB
	$HIGH
;DISPATCH TABLE FOR MPX

	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
	POPJ	P,0		;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.
	HLRZ	T1,M		;WANT TO WIN ONLY FOR
	ANDI	T1,777000	; AN OPEN UUO
	CAIE	T1,(OPEN)
	POPJ	P,0		;NOPE. ERROR RETURN
	PUSHJ	P,SAVT##	;SAVE T2 THRU T4
	MOVEI	T2,MPX4WD	;SIZE OF DDB
	PUSHJ	P,GET4WD##	;GET SOME CORE
	  POPJ	P,0		;NO MORE CORE
	HRR	F,T1		;COPY THE PROTOTYPE
	HRLI	T1,MPXDDB	; DDB
	BLT	T1,MPXLEN-1(F)	; ..
	HRLM	F,MPXDDB+DEVSER	;SET THE LINKS
IFN FTTRACKS,<
	AOS	T1,MPXNUM	;NUMBER OF CALLS TO TSTMPX
	DPB	T1,PUNIT##	;STORE SERIAL NUMBER IN DDB
	MOVE	T1,MPX6NM	;GET SIXBIT NUMBER
	AOS	T1		;INCREMENT 
	TRZE	T1,10
	ADDI	T1,100
	TRZE	T1,1000
	ADDI	T1,10000
	TRZ	T1,100000
	HRRZM	T1,MPX6NM	;SAVE ANSWER
	TRNN	T1,70000	;LEADING ZERO?
	LSH	T1,6		;YES--TRIM
	TRNN	T1,70000	;ANOTHER
	LSH	T1,6		;YES--REMOVE IT
	HRLI	T1,'MPX'	;PUT IN FIRST PART OF NAME
	MOVEM	T1,DEVNAM(F)	;STORE IN DDB
> ;END OF FTTRACK
	JRST	CPOPJ1##	;GOOD RETURN

IFN FTTRACKS,<
	$LOW
MPXNUM:	EXP	0		;DDB SERIAL NUMBER
MPX6NM:	SIXBIT	'000000'	;SAME THING IN SIXBIT
	$HIGH
> ;END FTTRACKS
;SUBROUTINE TO WIPE OUT THE MPX DDB ON RELEASE
;CALL WITH:
;	MOVEI	F,ADDRESS-OF-DDB-TO-ZAP
;	PUSHJ	P,ZAPMPX
;	RETURN HERE
;
MSGREL:	PUSHJ	P,SAVE4##	;SAVE THE P'S
ZAPTST:	HRRZ	P4,DEVXTR(F)	;ANY DEVICES CONNECTED?
	JUMPE	P4,CPOPJ##	;NO--KILL OF 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,RELEAC##	;UNCONDITIONAL DISCONNECT
	PUSHJ	P,DISCON	;REMOVE THE CONNECTION
	  STOPCD CPOPJ##,JOB,CDD, ;++CAN'T DISCONNECT DEVICE
	MOVE	F,P2		;RESTORE F
	JRST	ZAPTST		;SEE IF ANY DEVICES CONNECTED
ZAPMPX::PUSHJ	P,MSGREL	;RELEASE ALL
	HLRZ	T2,DEVXTR(F)	;ADDRESS OF CDT
	HLRZ	T1,CDTSIZ(T2)	;SIZE OF CDT
	SKIPE	T2		;SKIP IF NO CDT
	PUSHJ	P,GIVWDS##	;RETURN THE CORE
	MOVEI	T1,MPXDDB	;ADDRESS OF PROTO
ZPMPX2:	MOVE	T2,T1		;ADDRESS OF LAST DDB
	HLRZ	T1,DEVSER(T2)	;ADDRESS OF NEXT DDB
	JUMPE	T1,ZPMPXE	;DDB CHAIN IS MESSED UP IF WE
				; CAN NOT FIND THE DDB
	CAIE	T1,(F)		;IS THIS IT?
	JRST	ZPMPX2		;NO--KEEP LOOKING
	MOVE	T3,DEVSER(F)	;UNLINK THE DDB
	HLLM	T3,DEVSER(T2)	; ..
	MOVEI	T1,MPX4WD	;THROW AWAY THE
	HRRZ	T2,F		; CORE
	PJRST	GIV4WD##	; ..

;HERE IF F DID NOT POINT TO AN MPX DDB ON THE DDB CHAIN
ZPMPXE:	STOPCD	CPOPJ##,JOB,MXM, ;++MPX: DDB MISSING
	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
	CAMG	P1,USRHCU##	;SKIP IF TOO BIG
	SKIPN	P2,USRJDA##(P1)	;SKIP IF OPEN
	PJRST	ECOD6##		;ELSE GENERATE ERROR
	MOVE	F,P2		;SET F=ADDRESS OF MPX DDB
	LDB	T2,PDVTYP##	;GET THE DEVICE TYPE
	CAIE	T2,.TYMPX/.TYEST;IS THIS AN MPX DDB?
	PJRST	ECOD1##		;NO--PUNT
	HLRZ	T1,T1		;COPY FUNCTION
	CAILE	T1,CNFNMX	;SKIP IF NOT TOO BIG
	PJRST	ECOD10##	;ERROR 10 -- INVALID FUNCTION
	JRST	@CNFNTB(T1)	;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
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
	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
	  PJRST	ECOD11##	;DEVICE INITED OR CONNECTED
	SKIPGE	DEVSPL(F)	;SPOOLED DEVICE?
	JRST	[PUSHJ P,RELEA9## ;YES--KILL THE DDB
		 PJRST ECOD12##]; AND GIVE ERROR RETURN
	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,RELEA9## ;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,RELEA9##	;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 MSGSER
	IORM	T2,DEVMSG(P3)	; ..
	HRRM	P2,DEVXTR(P3)	;LINK TARGET BACK TO MPX DDB
	MOVSI	T1,R		;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
	MOVSI	T2,DVTTY	;IF THIS IS A TTY
	TDNE	T2,DEVMOD(P3)	; CLEAR ANY LEFTOVER OUTPUT
	SETZM	DEVSTS(P3)	; AT CONNECT TIME
	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/.TYEST>>
	>>

	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/.TYEST ;TTY?
	JRST	CPOPJ1##	;YES--ALWAYS GOOD
IFN FTNET,<
	CAIE	T2,.TYCDR/.TYEST	;A CDR?
	JRST	LGLMP1		;NO
	LDB	T3,PDVSTA##	;YES, LOCAL?
	CAME	T3,LOCSTA##
	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,RELEAC##	;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 RELEA2##]	;RELEASE AND CLOSE
UDCDEV:	MOVEI	P1,RELEAC##	;RESET
	PUSHJ	P,GETWD1##	;PICK UP DEVICE NAME
	PUSHJ	P,FNDDDB	;FIND DEVICE DATA BLOCK
	  JRST	ECOD2##		;MUST BE VALID DEVICE
;FALL INTO DISCON
;SUBROUTINE TO DISCONNECT A DEVICE
;CALLED WITH:
;	F = ADDRESS OF DDB
;	P1 = ADDRESS OF ROUTINE TO CALL (RELEAC)
;	P2 = ADDRESS OF MPX DDB
;	PUSHJ	P,DISCON
;	  RETURN HERE ON ERROR (USER AC UPDATED)
;	RETURN HERE IF OK
DISCON:
IFN FTVM,<
	HLR	M,DEVBUF(P2)	;ADDRESS OF THE RING HEADER
	TRNE	M,-1		;GO IF NONE
	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
	TLZ	F,-1		;FOR CAIN
DCNDVL:	MOVE	T4,(T2)		;GET AN ENTRY
	CAIN	F,(T4)		;IS THIS THE ONE TO ZAP?
	AOJA	T2,DCNDVL	;YES--GET THE NEXT ENTRY
	MOVEM	T4,(T3)		;STORE BACK ENTRY
	ADDI	T2,1		;UPDATE SOURCE
	ADDI	T3,1		;UPDATE DESTINATION
	SOJG	T1,DCNDVL	;MOVE THE WHOLE CDT DOWN
	CAIN	T2,(T3)		;DID WE FIND AN ENTRY?
	JRST	ECOD5##		;NO--DEVICE NOT CONNECTED
	CAIN	P1,RELEAC##	;UNCONDITIONAL DISCONNECT?
	PUSHJ	P,RTNOB		;YES, RETURN BUFFERS TO FREE LIST
	SETZM	-1(T3)		;ZERO ZAPPED ENTRY
	MOVEI	T2,DEPMSG	;NO LONGER CONTROLED
	ANDCAM	T2,DEVMSG(F)	; BY MSGSER
	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,CDT4WD	 ;NUMBER OF 4 WORD CHUNKS TO GET
	PUSHJ	P,GET4WD##	; ..
	  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
;PRESERVES ALL ACS

RTNOB:	PUSHJ	P,SAVT##	;SAVE T ACS
	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
	LSH	T2,-2		;IN 4 WORD BLOCKS
	ADDI	T2,CDT4WD	;PLUS AMOUNT TO EXPAND
	PUSHJ	P,GET4WD##	;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
	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
	AOS	P1,M		;WHERE TO PUT ERROR COUNT
	MOVEI	P2,0		;ERROR COUNT
	HLRZ	P3,T1		;NUMBER OF WORDS IN BLOCK
	TLZ	T1,-1		;CLEAR JUNK
	CAMG	T1,USRHCU##	;SKIP IF BOGUS CHAN #
	SKIPN	F,USRJDA##(T1)	;SKIP IF OPEN
	JRST	ECOD1##		;(1)NOT AN OPEN CHAN #
	LDB	T1,PDVTYP##	;GET DEVICE TYPE
	CAIE	T1,.TYMPX/.TYEST;SKIP IF MPX:
	JRST	ECOD2##		;NOT MPX: DEVICE
	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,JOB##	;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 UUOCON ON WAIT UUO FOR DEVICE MPX

MPXWAT::PUSHJ	P,SAVE3##	;SAVE P1-P3
	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,WAIT1##	;WAIT FOR I/O TO STOP ON THAT DEVICE
	PUSHJ	P,CALSER	;CALL WAIT1
	JRST	MPXWA1		;CHECK NEXT DEVICE
MPXWA2:	MOVE	F,P3		;RESTORE ADDRESS OF MPX DDB
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?
	POPJ	P,		;NO, ALL IS WELL
	MOVEI	T1,^D10		;YES, SLEEP A WHILE
	MOVE	J,.C0JOB##	;CURRENT JOB'S JOB NUMBER
	PUSH	P,F		;SLEEP CLOBBERS F
	PUSHJ	P,SLEEP##	;WAIT FOR TROUBLE TO BE FIXED
	POP	P,F
	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
IFN FTVM,<
	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
IFN FTVM,<
	PUSHJ	P,BRNGE##	;MAKE SURE THAT THE ENTIRE BUFFER IS IN CORE
	MOVSI	T1,(MPXVMF)	;A PAGE FAULT DIDN'T OCCUR,
	ANDCAM	T1,DEVIOS(F)	; SO CLEAR THE BIT
>
	MOVSI	S,IOSTBL	;CLEAR THE TROUBLE BIT
	ANDCAB	S,DEVIOS(F)	; AND SETUP S
	MOVEI	T1,CALOUT##	;ROUTINE TO CALL
	TRNN	S,IOACT		;DON'T CALL SERVICE ROUTINE IF I/O IS ACTIVE
	PUSHJ	P,CALSER	;CALL THE DEVICE ROUTINE TO START UP THE IO
	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
	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
; MSGSER, 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/.TYEST;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
	LDB	P1,PUUOAC##	;SETUP UUO PROGRESS FLAGS
	HLL	P2,USRJDA##(P1)	; 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
	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
IFN FTVM,<			;IF VM MONITOR
	HRR	M,T1		;SETUP M FOR VMCHEK
	PUSHJ	P,VMCHEK	;CHECK ALL BUFFERS ARE IN CORE
> ;END FTVM
	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
IFN FTKA10,<
	ADDI	T1,(R)		;RELOCATE BUFFER HEADER ADDR
	ADDI	T2,(R)		;AND 1ST BUFFER ADDR
>	;END FTKA10
	CONO	PI,PIOFF##	;PREVENT RACES
	EXCTUX	<HRRZ T3,(T2)>	;GET POINTER TO NEXT BUFFER
	EXCTXU	<MOVEM T3,(T1)>	;AND STORE IT IN HEADER
	CONO	PI,PION##	;ALLOW IME STOPCDS
	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
	JUMPE	T1,NEWOBF	;JUMP IF DEVICE IDLE
	PUSHJ	P,UADRCK##	;ADDRESS CHECK LAST BUFFER
IFN FTKA10,<
	TLO	T1,R		;RELOCATE
>
	CONO	PI,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
	CONO	PI,PION##
STOUTP:	MOVE	F,P3		;ADDRESS OF TARGET DDB
	MOVSI	S,IOSTBL	;MAGIC SYNC BIT
	ANDCAB	S,DEVIOS(F)	;CLEAR (ALSO LOAD UP S)
	MOVEI	T1,CALOUT##	;CALL CALOUT VIA CALSER
	TRNN	S,IOACT		;IS DEVICE ACTIVE?
	PUSHJ	P,CALSER	;NO--CALL SERVICE ROUTINE VIA UUOCON
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	;GET MPX DDB ADDRESS
	 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:
IFN FTVM,<
	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
	LDB	P1,PUUOAC##	;MAKE SURE THAT THE MAGIC
	MOVSI	T2,OUTBFB	; UUO PROGRESS BIT IS
	IORM	T2,USRJDA##(P1)	; SET FOR THIS CHANNEL

;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
IFN FTKA10,<
	TLO	U,R		;SET UP RELOCATION FOR KA
>	;END FTKA10
	SOJA	J,IOSETC##	;STORE BYTE COUNT AND RETURN
IFN FTVM,<
;SUBROUTINE TO MAKE SURE ALL BUFFERS ARE IN CORE
;CALL WITH:
;	M = ADDRESS-OF-FIRST-BUFFER
;	PUSHJ	P,VMCHEK
;	RETURN HERE IF ALL OK
;
VMCHKH:	PUSHJ	P,GETWDU##	;GET FIRST BUFFER ADDRESS
	HRR	M,T1		;TO M FOR VMCHEK
VMCHEK:	PUSH	P,T1		;SAVE T1
	TRNE	M,-1		;ANY TO CHECK
	PUSHJ	P,BCHCK		;MAKE SURE BUFFERS ARE IN CORE
;WE HAVE JUST TOUCHED EVERY BUFFER IN THE FREE LIST.
	SKIPE	DEVEVM(P3)	;SKIP IF NOT ALREADY MAPPED IN EVM
	JRST	TPOPJ##		;RETURNED
	HRR	M,DEVOAD(P3)	;START OF ACTIVE LIST
	TRNN	M,-1		;SKIP IF LIST EXISTS
	JRST	TPOPJ##		;RETURN--NO BUFFERS OR ALREADY MAPPED
	PUSHJ	P,BCHCK		;MAKE SURE BUFFERS ARE IN CORE
	JRST	TPOPJ##		;ALL OK
BCHCK:	PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	P1,^D10000	;DEFEND AGAINST
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		;CURRENT BUFFER
	PUSHJ	P,BRNGE##	;CHECK IT
	TRNE	M,-1		;LAST BUFFER?
	SOJGE	P1,BCHCK1	;NO
	JUMPL	P1,ADRERR##	;JUMP IF ADDRESS CHECK
	POPJ	P,		;RETURN
>
;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
;
;IF THE DEVICE WANTS THE COUNT IN WORDS NOT BYTES THE BYTE COUNT
;	IS CONVERTED TO A WORD COUNT.
STOCNT:	PUSHJ	P,SAVE1##	;SAVE P1
	PUSHJ	P,GETWD1##	;PICK UP THE BYTE POINTER
	SUBI	M,1		;DO NOT CHANGE M
	MOVE	P1,T1		;SAVE POINTER IN P1
	MOVE	S,DEVIOS(P2)	;PICK UP IOS WORD
	TRNE	S,IOWC		;DID USER COMPUTE WORD COUNT
	JRST	STOCT1		;YES--GO CONVERT TO WORDS
	SUBI	T1,1(U)		;GET THE WORD COUNT
	HRRE	T1,T1		;FILL LH WITH SIGN BITS (0 I HOPE)
	PUSH	P,M		;SAVE M A MOMENT
	HRRI	M,1(U)		;STORE COUNT IN BUFFER
	PUSHJ	P,PUTWDU##	;STORE UPDATED COUNT
	PJRST	MPOPJ##		;RESTORE M
				;AND RETURN

STOCT1:	PUSH	P,M		;SAVE M
	HRRI	M,1(U)		;PICK UP ADDRESS OF BUFFER
	PUSHJ	P,GETWDU##	;GET THE WORD COUNT
	PJRST	MPOPJ##		;RESTORE M
				;AND RETURN
;
;SUBROUTINE CALL WHEN AN OUTPUT BUFFER IS EMPTY
;CALLED WITH:
;	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
IFN FTKI10!FTKL10,<
	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
	MOVE	T3,(T4)
	HLRZ	T1,DEVBUF(P2)	;POINTER TO THE HEAD OF FREE LIST
	MOVEI	P4,^D10000
	MOVEI	P1,-1		;IF THERE IS NO FREE BUFFER LIST 
MSBFE1:	PUSHJ	P,IADRCK##	;MAKE SURE POINTER IS LEGAL
	  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:	TRNE	T3,-1		;SKIP IF THERE IS NO NEXT BUFFER
	JRST	MSBFE3		;MORE EMPTY BUFFERS
	HRRZS	DEVBUF(P3)	;NO BUFFERS IN OUTPUT LIST
	JRST	ADVSTP		;ELSE STOP THE I/O
;HERE IF THERE ARE MORE BUFFERS IN THE OUTPUT LIST
MSBFE3:	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
> ;END FTKI10
NOOEVM:
IFN FTKA10,<
	MOVEI	T4,@DEVOAD(P3)	;EXEC ADDRESS OF BUFFER IF KA10
>
IFN FTKI10!FTKL10,<
	HRRZ	T4,DEVOAD(P3)	;USER VIRT ADDRESS IF KI10
>
	EXCTUU	<MOVEM S,-1(T4)>;STORE UPDATED STATUS BITS
	HLRZ	T1,DEVBUF(P2)	;ADDRESS OF FREE LIST
	EXCTUX	<MOVE T3,(T4)>
	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.
IFN FTKA10,<
	ADDI	T1,(R)		;RELOCATE IF KA10
>	;END FTKA10
	EXCTUU	<TDNN P1,(T1)>	;END OF THE BUFFER LIST?
	JRST	[IFN FTKA10,<
	SUBI	T4 (R)		;UN-RELOCATE
	HRRM	T4,(T1)		;ADD TO END OF FREE LIST
	ADDI	T4,(R)		;NEED RELOCATED ADDRESS AGAIN
>
IFN FTKI10!FTKL10,<
	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:	TRNE	T3,-1		;MORE BUFFERS
	JRST	MSBFE6		;YES--GO ADDRESS CHECK IT
	HRRZS	DEVBUF(P3)	;NO--MARK LIST AS EMPTY
	JRST	ADVSTP		;AND STOP I/O
MSBFE6:	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
ADVSTP:	PUSHJ	P,RTNEVM##	;GIVE BACK EVM
	JRST	T3POPJ##	;RESTORE T3 AND 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
	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	S,DEVIOS(F)	;PICK UP STATUS FLAGS
	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
	MOVEI	T1,CALIN##	;ROUTINE TO CALL
	PUSHJ	P,CALSER	;CALL SERVICE ROUTINE TO FILL 1 BUFFER
	HLRZ	T1,1(P3)	;PICK UP THE UDX
IFN FTKA10,<
	MOVEI	T2,@DEVIAD(P2)	;EXEC ADDRESS OF BUFFER IF KA10
>
IFN FTKI10!FTKL10,<
	HRRZ	T2,DEVIAD(P2)	;GET UVA OF INPUT BUFFER
>
	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
	PUSH	P,F		;SLEEP ZEROES F
	PUSHJ	P,SLEEP##	; ..
	POP	P,F
	JRST	MSGIN0		; ..
DEFINE ITEST(A),<
 REPEAT TYPMAX+1,<
	POPJ	P,0
 >
 IRP A,<
	RELOC	<.TY'A/.TYEST>+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
	SKIPLE	LDBTOC##(U)	;ANYTHING THERE?
	JRST	CPOPJ1##	;YES--SKIP RETURN
	POPJ	P,		;NO--NON SKIP RETURN
IT$TTY:	HRRZ	U,DDBLDB##(F)	;GET ADDRESS OF LDB
	JUMPE	U,CPOPJ##	;PUNT IF NO LDB
ITTTY1:	HLL	U,LDBDCH##(U)	;GET DEVICE BITS
	SKIPLE	LDBBKC##(U)	;IS THERE A LINE?
	JRST	CPOPJ1##	;YES--RETURN TO INPUT
	PJRST	TTLCHK##	;NO--DO BETTER TEST
IT$RDA==:RDA$IN##		;CHECK FOR INPUT.
IT$CDR==:CDR$IN##
	SUBTTL	CLOCK1 -- SUBROUTINE TO SEE IF AN MPX USER CAN BE SWAPPED
;SUBROUTINE TO KEEP IOACT IN THE MPX DDB CURRENT (SO CAN CALL IOWAIT)
;CALLED WHENEVER A DEVICE CONTROLLED BY MSGSER CALLS STOIOS
;
MPXIOS::PUSH	P,F		;SAVE DDB ADDRESS
	PUSH	P,S		;SAVE DEVIOS FROM TARGET DDB
	HRRZ	F,DEVXTR(F)	;GET ADDRESS OF MPX DDB
	MOVE	S,DEVIOS(F)	;IOS FROM MPX DDB
	PUSHJ	P,MPXACT	;SEE IF STILL ACTIVE
	  JRST	[PUSHJ P,ACTON	;YES--SET IOACT
		 JRST  .+2]	;RESTORE F AND RETURN
	PUSHJ	P,ACTOFF	;CLEAR IO ACTIVE
	POP	P,S		;RESTORE S
	JRST	FPOPJ##		;RETURN
ACTON:	TROA	S,IOACT		;IOACT ON
ACTOFF:	TRZ	S,IOACT		;IOACT OFF
	HRRM	S,DEVIOS(F)	;PRESERVE STATE OF IOW
	PJRST	SETHNG##	;SET HUNG TIME AND RETURN
;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

;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
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
	LDB	P1,PUUOAC##	;PICK UP RANDOM NUMBER
	PUSH	P,USRHCU##	;SAVE THIS
	PUSH	P,USRJDA##(P1)	;AND THIS
	HLL	F,DEVXTR(F)	;PICK UP FLAGS
;** DO NOT CHANGE USRJDA SO THERE IS NOT CHANCE FOR ^C TO LEAVE POINTER
;**  TO THE WRONG DDB FLOATING AROUND.
;**	MOVEM	F,USRJDA##(P1)	;INCASE ANYONE SHOULD LOOK HERE
	MOVE	S,DEVIOS(F)	;ALWAYS NICE TO HAVE S SET UP
	PUSHJ	P,(T1)		;CALL ROUTINE
	  SOS	-2(P)		;NON-SKIP
	LDB	P1,PUUOAC##	;I DO NOT TRUST P1 ALL THAT TIME
	HLL	F,USRJDA##(P1)	;GET UPDATED FLAGS
	HLLM	F,DEVXTR(F)	;STORE BACK IN DDB
	POP	P,USRJDA##(P1)	;RESTORE JUNK
	POP	P,USRHCU##	; ..
	MOVE	P1,USRJDA##(P1)	;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
	END