Google
 

Trailing-Edge - PDP-10 Archives - BB-F493Z-DD_1986 - 10,7/703mon/scsuuo.mac
There are 3 other files named scsuuo.mac in the archive. Click here to see a list.
TITLE	SCSUUO - SCS. UUO SUPPORT	V24
SUBTTL	JOSEPH A. DZIEDZIC/JAD	17-DEC-85


	SEARCH	F,S,ICHPRM,KLPPRM,SCAPRM,MACSYM
	$RELOC
	$HIGH


	T20SYM			;SWITCH TO TOPS-20 AC NAMES
				;(NEEDED FOR INTERFACE TO SCASER)


;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1984,1986>
;COPYRIGHT (C) 1984,1986,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.


XP	VSCSUU,24		;VERSION NUMBER FOR GLOB AND MAP


SCSUUO:!ENTRY	SCSUUO		;LOAD IF LIBRARY SEARCH


;NOTE ON ACCUMULATOR USAGE:
;
;DON'T USE Q1 - Q3 UNLESS YOU WANT TO STEP ON W, M, OR U.  KLPSER
;USES Q2 AS THE PCB ADDRESS REGISTER.  INTERNALLY WE WILL USE P6
;TO AVOID STEPPING ON M.  THIS IS THE ONLY AC DIFFERING BETWEEN
;KLPSER/SCASER AND SCSUUO.
SUBTTL	UUO ERROR RETURN DEFINITIONS


	ERCODX	ERRNPV,SSNPV%	;NOT PRIVILEGED
	ERCODX	ERRIFC,SSIFC%	;ILLEGAL FUNCTION CODE
	ERCODX	ERRARG,SSARG%	;BAD ARGUMENT LIST LENGTH
	ERCODX	ERRACR,SSACR%	;ADDRESS CHECK READING ARGUMENTS
	ERCODX	ERRACS,SSACS%	;ADDRESS CHECK STORING DATA
	ERCODX	ERRCPN,SSCPN%	;CPU NUMBER OUT OF RANGE
	ERCODX	ERRNPC,SSNPC%	;NO CI PORT ON SPECIFIED CPU
	ERCODX	ERRNNK,SSNNK%	;CPU'S CI NODE NUMBER NOT KNOWN
	ERCODX	ERRINN,SSINN%	;INVALID CI NODE NUMBER
	ERCODX	ERRNFC,SSNFC%	;NO FREE CORE
	ERCODX	ERRVNO,SSVNO%	;VIRTUAL CIRCUIT NOT OPEN
	ERCODX	ERRICI,SSICI%	;INVALID CONNECT ID
	ERCODX	ERRRQE,SSRQE%	;RECEIVE QUEUE EMPTY
	ERCODX	ERRNBQ,SSNBQ%	;NO BUFFER QUEUED FOR PACKET RECEPTION
	ERCODX	ERRRCF,SSRCF%	;REJECT CONNECTION FAILED
	ERCODX	ERRDCF,SSDCF%	;DISCONNECT CONNECTION FAILED
	ERCODX	ERRNFB,SSNFB%	;NO FREE BUFFERS TO SEND PACKET
	ERCODX	ERRQBF,SSQBF%	;QUEUE BUFFERS FAILED
	ERCODX	ERRCBF,SSCBF%	;CANCEL BUFFERS FAILED
	ERCODX	ERRPSF,SSPSF%	;PACKET SEND FAILED
	ERCODX	ERRDQE,SSDQE%	;DATA ENTRY QUEUE EMPTY
	ERCODX	ERREQE,SSEQE%	;EVENT QUEUE EMPTY
	ERCODX	ERRCRB,SSCRB%	;CAN'T REMOVE BUFFER FROM DATABASE
	ERCODX	ERRCUB,SSCUB%	;CAN'T UNMAP BUFFER
	ERCODX	ERRNSB,SSNSB%	;NO SUCH BUFFER NAME
	ERCODX	ERRTMS,SSTMS%	;TOO MANY BUFFER SEGMENT DESCRIPTORS
	ERCODX	ERRIDM,SSIDM%	;ILLEGAL DATA MODE
	ERCODX	ERRSCP,SSSCP%	;SEGMENT CROSSES PAGE BOUNDARY
	ERCODX	ERRSTL,SSSTL%	;SEGMENT TOO LONG (GREATER THAN 1 PAGE)
	SUBTTL	FORMAT OF PROCESS QUEUE BLOCK


;THE PROCESS QUEUE BLOCK CONTAINS VARIOUS QUEUES ON WHICH MESSAGE
;BUFFERS, EVENT BLOCKS, CONNECTION BLOCKS, AND ETC. ARE QUEUED.
;THE PROCESS QUEUE BLOCK (PQB) IS ALWAYS POINTED TO BY AC P3.

	PHASE	0		;DEFINE AS OFFSETS

SCSTMQ:!BLOCK	1		;TOP OF MESSAGE QUEUE
SCSBMQ:!BLOCK	1		;BOTTOM OF MESSAGE QUEUE

SCSTDQ:!BLOCK	1		;TOP OF DATAGRAM QUEUE
SCSBDQ:!BLOCK	1		;BOTTOM OF DATAGRAM QUEUE

SCSTXQ:!BLOCK	1		;TOP OF DMA TRANSFER COMPLETE QUEUE
SCSBXQ:!BLOCK	1		;BOTTOM OF DMA TRANSFER COMPLETE QUEUE

SCSTEQ:!BLOCK	1		;TOP OF EVENT BLOCK QUEUE
SCSBEQ:!BLOCK	1		;BOTTOM OF EVENT BLOCK QUEUE

SCSTCQ:!BLOCK	1		;TOP OF CONNECTION BLOCK QUEUE
SCSBCQ:!BLOCK	1		;BOTTOM OF CONNECTION BLOCK QUEUE

SCSTXN:!BLOCK	1		;TOP OF DMA BUFFER NAME QUEUE
SCSBXN:!BLOCK	1		;BOTTOM OF DMA BUFFER NAME QUEUE

PQBLEN:!			;LENGTH OF PROCESS QUEUE BLOCK

	DEPHASE
	SUBTTL	INITIALIZATION


;HERE FROM SCASER TO INITIALIZE THE SCS. UUO INTERFACE.

SCSINI::SETZM	SCSTIQ		;ZERO FLINK OF INTERRUPT LEVEL PACKET QUEUE
	XMOVEI	T1,SCSTIQ	;SET BLINK
	MOVEM	T1,SCSBIQ	; AS POINTER TO FLINK
	BLCAL.	(SC.SNA##,<<.,SCSINT>>)	;TELL SCA ABOUT THIS ADDRESS
	  STOPCD CPOPJ##,DEBUG,SCSCIS ;++CAN'T INITIALIZE SCS. UUO INTERFACE
	POPJ	P,		;ALL DONE, RETURN
	SUBTTL	SCS. UUO - DISPATCH


;HERE FROM UUOCON ON AN SCS. UUO.  T1 CONTAINS THE CONTENTS OF THE
;USER'S ACCUMULATOR.

SCS::	MOVE	M,T1		;GET ADDRESS OF ARGUMENT BLOCK
	PUSHJ	P,SXPCS##	;SET UP FOR EXTENDED GETWRDS
	  JRST	ERRACR		;ADDRESS CHECK
	PUSHJ	P,GETEWD##	;GRAB THE FIRST WORD OF USER'S ARGUMENT BLOCK
	  JRST	ERRACR		;ADDRESS CHECK
	MOVE	P1,T1		;SAVE FOR A WHILE
	LDB	P4,[POINTR T1,SS.FNC] ;GET FUNCTION CODE IN P4
	HRRES	P4		;EXTEND THE SIGN
	CAML	P4,[MINFCN]	;LOWER THAN LOWEST?
	CAILE	P4,MAXFCN	;GREATER THAN GREATEST?
	JRST	ERRIFC		;NO, GIVE ERROR
	MOVSI	T1,JP.POK	;PRIVILEGE BIT REQUIRED
	MOVSI	T2,DF.UPV	;UNPRIVILEGED FUNCTION?
	TDNN	T2,FCNTAB(P4)	;...
	PUSHJ	P,PRVBIT##	;NO, MAKE SURE PRIVILEGED
	  SKIPA	T1,M		;GET ADDRESS OF USER'S ARGUMENT BLOCK AND SKIP
	JRST	ERRNPV		;GIVE AN ERROR
	LDB	T2,[POINTR P1,SS.LEN] ;GET LENGTH OF ARGUMENT BLOCK
	PUSHJ	P,ARNGE##	;SEE IF LEGAL FOR I/O
	 JRST	ERRACR		;ADDRESS CHECK
	  JRST	ERRACS		;ADDRESS CHECK
	SKIPE	P3,.PDSCS##(W)	;GET ADDRESS OF PROCESS QUEUE BLOCK
	JRST	SCS.1		;THERE IS ONE
	MOVSI	T1,DF.NPB	;DOES THIS FUNCTION REQUIRE A PQB?
	TDNE	T1,FCNTAB(P4)	;...
	  JRST	SCS.1		;NO, SO DON'T BOTHER
	PUSHJ	P,GETPQB	;TRY TO GET A PQB
	  JRST	ERRNFC		;NO FREE CORE
SCS.1:	MOVE	T1,P1		;GET FIRST WORD OF ARGUMENT BLOCK BACK
	LDB	P1,[POINTR T1,SS.LEN] ;GET LENGTH OF ARGUMENT BLOCK
	SOJLE	P1,ERRARG	;MUST HAVE AT LEAST 2 ARGUMENTS
	LDB	P2,[POINTR T1,SS.CPU] ;GET POSSIBLE CPU ARGUMENT
	HRRZ	T1,FCNTAB(P4)	;GET DISPATCH ADDRESS
	TXO	T1,IFIW		;MAKE A LOCAL INDIRECT WORD
	PJRST	(T1)		;DISPATCH TO PROCESSING ROUTINE


;ALL FUNCTIONS ARE DISPATCHED TO WITH THE FOLLOWING DATA IN THE AC'S:
;
;	P1 - LENGTH OF ARGUMENT LIST EXCLUDING FUNCTION WORD
;	P2 - CPU NUMBER (ALWAYS LOADED FROM FUNCTION WORD, BUT MAY
;	     NOT ALWAYS BE APPLICABLE)
;	P3 - ADDRESS OF PROCESS QUEUE BLOCK
;	M  - ADDRESS OF USER'S ARGUMENT BLOCK
;DISPATCH FLAGS FOR SCS. UUO FUNCTIONS

	DF.UPV==(1B0)		;JOB NEED NOT BE PRIVILEGED
	DF.NPB==(1B1)		;FUNCTION DOES NOT REQUIRE A PQB

;FUNCTION CODE DISPATCH TABLE FOR SCS. UUO

ZZ==.				;ADDRESS OF MINIMUM FUNCTION
				;CUSTOMER FUNCTIONS GO HERE
FCNTAB:	EXP		SCSCON		;(0) REQUEST A CONNECTION
	EXP		SCSLIS		;(1) LISTEN FOR A CONNECTION
	EXP		SCSREJ		;(2) REJECT A CONNECTION REQUEST
	EXP		SCSDIS		;(3) DISCONNECT AND CLOSE A CONNECTION
	EXP		SCSSDG		;(4) SEND A DATAGRAM
	EXP		SCSQRD		;(5) QUEUE DATAGRAM BUFFERS
	EXP		SCSSMG		;(6) SEND MESSAGE
	EXP		SCSQRM		;(7) QUEUE MESSAGE BUFFERS
	XWD	   DF.NPB,SCSCSP	;(10) CONNECT STATE POLL
	XWD DF.UPV+DF.NPB,SCSRCD	;(11) RETURN CONFIGURATION DATA
	EXP		SCSSTS		;(12) RETURN CONNECTION STATUS
	EXP		SCSRMG		;(13) RECEIVE A MESSAGE
	EXP		SCSMAP		;(14) MAP A BUFFER FOR DMA OPERATIONS
	EXP		SCSUMP		;(15) UNMAP A MAPPED BUFFER
	EXP		SCSSND		;(16) SEND DATA TO REMOTE HOST
	EXP		SCSREQ		;(17) REQUEST DATA DELIVERY
	EXP		ERRIFC		;(20) ADD INTERRUPT CHANNELS
	EXP		ERRIFC		;(21)
	EXP		SCSRDG		;(22) RECEIVE DATAGRAM
	EXP		SCSACC		;(23) ACCEPT CONNECTION
	EXP		SCSGDE		;(24) GET DATA REQUEST COMPLETE QUEUE ENTRY
	EXP		SCSEVT		;(25) GET EVENT QUEUE ENTRY
	EXP		SCSCRD		;(26) CANCEL DATAGRAM RECEPTION
	EXP		SCSCRM		;(27) CANCEL MESSAGE RECEPTION
	XWD DF.UPV+DF.NPB,SCSGLN	;(30) GET LOCAL NODE NUMBER
	EXP		ERRIFC		;(31)
	EXP		ERRIFC		;(32)
	EXP		ERRIFC		;(33)
	EXP		ERRIFC		;(34)
	XWD DF.UPV+DF.NPB,SCSRBS	;(35) RETURN MINIMUM BUFFER SIZES
	XWD DF.UPV+DF.NPB,SCSRPS	;(36) RETURN PATH STATUS
MINFCN==ZZ-FCNTAB		;MINIMUM LEGAL FUNCTION NUMBER
MAXFCN==.-FCNTAB-1		;MAXIMUM LEGAL FUNCTION NUMBER
	SUBTTL	SCS. UUO - CONNECT

;*** WARNING - THE STKVAR AT SCSCON MUST MATCH THAT AT SCSLIS EXACTLY
;AS THESE TWO FUNCTIONS SHARE COMMON ERROR PROCESSING ROUTINES WHICH
;EXPECT THE ORDER OF THE STKVAR ARGUMENTS TO BE IDENTICAL.

SCSCON:	STKVAR	<SPN,DPN,CID,ERRCOD,STRRET,DATRET,STRADR,DATADR,MSGBUF,DGBUF>
	SETZM	DATADR		;ZERO THE POINTER TO CONNECTION DATA
	SETZM	STRADR		;DITTO FOR STRING STORAGE
	CAIGE	P1,.SQRCI	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	PUSHJ	P,CPUPCB	;TRANSLATE CPU ARGUMENT INTO PCB
	  POPJ	P,		;ERROR, CODE ALREADY STORED
	EXCTUX	<HLRE T1,.SQSYS(M)> ;GET THE NODE NUMBER
	SKIPL	T1		;CHECK FOR VALIDITY
	CAIL	T1,MAXNDS	;...
	JRST	ERRINN		;INVALID NODE NUMBER
	ADD	T1,P6		;OFFSET PCB ADDRESS BY NODE NUMBER
	SKIPN	P5,.PCPBK(T1)	;GET PATH BLOCK ADDRESS
	JRST	ERRVNO		;ERROR
	PUSHJ	P,SCSUNM	;MOVE PROCESS NAMES INTO MONITOR SCRATCH STORAGE
	  JRST	ERRACR		;ADDRESS CHECK
	MOVEM	T1,SPN		;SAVE THE POINTER TO SOURCE PROCESS NAME
	MOVEM	T2,DPN		; AND DESTINATION PROCESS NAME
	MOVEM	T3,STRADR	; AND ADDRESS OF FREE SPACE
	MOVEM	T4,STRRET	; AND ROUTINE TO RETURN FREE SPACE
	EXCTUX	<SKIPN T1,.SQCDT(M)> ;GET POINTER TO CONNECTION DATA
	JRST	CON.01		;NONE SUPPLIED, PROCEED
	PUSHJ	P,SCSUDM	;MOVE THE DATA INTO MONITOR SCRATCH STORAGE
	  JRST	LISACR		;ADDRESS CHECK
	MOVEM	T1,DATADR	;SAVE THE POINTER TO CONNECTION DATA
	MOVEM	T2,DATRET	; AND ROUTINE TO RETURN SPACE
CON.01:	SETZM	MSGBUF		;ZERO MESSAGE BUFFER COUNT
	EXCTUX	<SKIPN T1,.SQAMC(M)> ;ANY MESSAGE BUFFERS?
	JRST	CON.02		;NO
	MOVX	T2,C%MGSZ	;GET SIZE OF A MESSAGE BUFFER
	PUSHJ	P,SCSCUB	;COUNT USER BUFFERS AND ADDRESS CHECK THEM
	  JRST	LISACR		;ADDRESS CHECK
	MOVEM	T3,MSGBUF	;STORE NUMBER OF BUFFERS IN THE CHAIN

CON.02:	SETZM	DGBUF		;ZERO DATAGRAM BUFFER COUNT
	EXCTUX	<SKIPN T1,.SQADC(M)> ;ANY DATAGRAM BUFFERS?
	JRST	CON.03		;NO
	MOVX	T2,C%DGSZ	;GET SIZE OF A DATAGRAM BUFFER
	PUSHJ	P,SCSCUB	;COUNT USER BUFFERS AND ADDRESS CHECK THEM
	  JRST	LISACR		;ADDRESS CHECK
	MOVEM	T3,DGBUF	;STORE NUMBER OF BUFFERS IN THE CHAIN
CON.03:	EXCTUX	<HRRZ T1,.SQSYS(M)> ;GET CID BITS FROM USER
	LOAD	T2,PBPBI,(P5)	;GET PBI
	BLCAL.	(SC.CON##,<SPN,DPN,T2,[5],[0],<.,SCSINT>,T1,DATADR,MSGBUF,DGBUF>)
	  JRST	LISERR		;ERROR
	UMOVEM	T1,.SQRCI(M)	;STORE RETURNED CONNECT ID
	MOVEM	T1,CID		;SAVE FOR LATER
	SKIPE	T1,DATADR	;ANY CONNECTION DATA?
	PUSHJ	P,@DATRET	;YES, RETURN THE SPACE
	SKIPE	T1,STRADR	;ANY STRING SPACE?
	PUSHJ	P,@STRRET	;YES, RETURN THE SPACE
	$LDCID	P1,CID		;GET CONNECTION BLOCK ADDRESS
	PUSHJ	P,SCSCBI	;INIT THE CB WITH JSYS DATA
	PUSHJ	P,SCSLCB	;LINK THIS CB ONTO THIS JOB'S CB QUEUE

	SKIPN	MSGBUF		;ANY MESSAGE BUFFERS?
	JRST	CON.04		;NO
	UMOVE	T1,.SQAMC(M)	;GET POINTER TO FIRST BUFFER
	MOVX	T2,.BDFMG	;PUT IT ON THE MESSAGE BUFFER LIST
	PUSHJ	P,SCSXUB	;LINK THIS CHAIN ONTO CB
	  JRST	LISACR		;ADDRESS CHECK

CON.04:	SKIPN	DGBUF		;ANY DATAGRAM BUFFERS?
	JRST	CPOPJ1##	;NO, SKIP RETURN
	UMOVE	T1,.SQADC(M)	;GET POINTER TO FIRST BUFFER
	MOVX	T2,.BDFDG	;PUT IT ON THE DATAGRAM BUFFER LIST
	PUSHJ	P,SCSXUB	;LINK THIS CHAIN ONTO CB
	  JRST	LISACR		;ADDRESS CHECK
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SCS. UUO - LISTEN

;*** WARNING - THE STKVAR AT SCSCON MUST MATCH THAT AT SCSLIS EXACTLY
;AS THESE TWO FUNCTIONS SHARE COMMON ERROR PROCESSING ROUTINES WHICH
;EXPECT THE ORDER OF THE STKVAR ARGUMENTS TO BE IDENTICAL.

SCSLIS:	STKVAR	<SPN,DPN,CID,ERRCOD,STRRET,DATRET,STRADR,DATADR,MSGBUF,DGBUF>
	SETZM	DATADR		;ZERO THE POINTER TO CONNECTION DATA
	SETZM	STRADR		;DITTO FOR STRING STORAGE
	CAIGE	P1,.SQLCI	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	PUSHJ	P,CPUPCB	;TRANSLATE CPU NUMBER INTO PCB
	  POPJ	P,		;ERROR, CODE ALREADY STORED
	SETO	P5,		;ASSUME A "DON'T CARE" LISTENER
	EXCTUX	<HLRE T1,.SQSYS(M)> ;GET THE NODE NUMBER
	CAMN	T1,[-1]		;IS IT A "DON'T CARE" LISTEN?
	JRST	LIS.01		;YES
	SKIPL	T1		;CHECK FOR VALIDITY
	CAIL	T1,MAXNDS	;...
	JRST	ERRINN		;INVALID NODE NUMBER
	ADD	T1,P6		;OFFSET PCB ADDRESS BY NODE NUMBER
	SKIPN	P5,.PCPBK(T1)	;GET PATH BLOCK ADDRESS
	JRST	ERRVNO		;ERROR
LIS.01:	PUSHJ	P,SCSUNM	;MOVE PROCESS NAMES INTO MONITOR SCRATCH STORAGE
	  JRST	ERRACR		;ADDRESS CHHECK
	MOVEM	T1,SPN		;SAVE THE POINTER TO SOURCE PROCESS NAME
	MOVEM	T2,DPN		; AND DESTINATION PROCESS NAME
	MOVEM	T3,STRADR	; AND ADDRESS OF FREE SPACE
	MOVEM	T4,STRRET	; AND ROUTINE TO RETURN FREE SPACE

	SKIPL	T1,P5		;IS THIS A "DON'T CARE" LISTEN?
	LOAD	T1,PBPBI,(P5)	;NO, GET PATH BLOCK INDEX
	EXCTUX	<HRRZ T2,.SQSYS(M)> ;GET CID BITS
	BLCAL.	(SC.LIS##,<SPN,DPN,T1,<.,SCSINT>,T2,[0],[0]>)
	  JRST	LISERR		;ERROR
	UMOVEM	T1,.SQLCI(M)	;STORE RETURNED CONNECT ID
	MOVEM	T1,CID		;SAVE FOR LATER
	MOVE	T1,STRADR	;GET THE ADDRESS OF THE STRING SPACE
	PUSHJ	P,@STRRET	;RETURN THE SPACE

	$LDCID	P1,CID		;GET THE CB ADDRESS FOR THE CID
	PUSHJ	P,SCSCBI	;INITIALIZE THE CONNECTION BLOCK
	PUSHJ	P,SCSLCB	;LINK THIS NEW CB ONTO FORK CB QUEUE
	JRST	CPOPJ1##	;SKIP RETURN
LISACR:	MOVEI	T1,SSACR%	;GET ERROR CODE
LISERR:	MOVEM	T1,ERRCOD	;SAVE IT
	SKIPE	T1,DATADR	;ANY CONNECTION DATA?
	PUSHJ	P,@DATRET	;YES, RETURN THE SPACE
	SKIPE	T1,STRADR	;ANY STRING SPACE?
	PUSHJ	P,@STRRET	;YES, RETURN THE SPACE
	MOVE	T1,ERRCOD	;GET ERROR CODE BACK
	PJRST	STOTAC##	;STORE ERROR CODE AND RETURN

	ENDSV.			;END STACK VARIABLE FROM SCSCON
	SUBTTL	SCS. UUO - RECEIVE DATAGRAM/MESSAGE

SCSRDG:	TDZA	T1,T1		;INDICATE WE ARE DOING DATAGRAM
SCSRMG:	SETO	T1,		;INDICATE WE ARE DOING MESSAGE
	CAIGE	P1,.SQLRP	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	SAVEAC	<Q1>		;SAVE AN AC
	STKVAR	<PAKADR,USRBUF,MSGFLG,ERRCOD,LEN> ;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,MSGFLG	;SAVE ENTRY FLAG
	XMOVEI	Q1,MSG		;ASSUME WE ARE DOING MESSAGES
	SKIPN	MSGFLG		;GOOD GUESS?
	XMOVEI	Q1,DG		;NO
	SETZM	USRBUF		;ZERO SOME ERROR RECOVERY VARIABLES
	SETZM	PAKADR		;...

	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	CAMN	T1,[-1]		;DO WE WANT FIRST FOR CONNECTION OR CONTEXT?
	JRST	RDG.01		;FIRST FOR CONTEXT
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	SKIPN	T1,@.TOPCQ(Q1)	;ARE THERE ANY PACKETS QUEUED FOR THIS CONNECT?
	JRST	ERRRQE		;NO, ERROR
	MOVEM	T1,PAKADR	;SAVE THE ADDRESS OF THE PACKET
	JRST	RDG.02		;CONTINUE

RDG.01:	SKIPN	T1,@.TOPFQ(Q1)	;IS THERE A PACKET WAITING FOR THIS CONTEXT?
	JRST	ERRRQE		;NO
	MOVEM	T1,PAKADR	;SAVE THE ADDRESS OF THE PACKET
	MOVE	T1,.MECID(T1)	;GET THE CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	UMOVEM	T1,.SQCID(M)	;LET THEM KNOW WHO PACKET WAS FOR
RDG.02:	SKIPG	@.JBUFF(Q1)	;ARE THERE BUFFERS QUEUED?
	JRST	ERRNBQ		;NO
	MOVE	T1,PAKADR	;GET THE PACKET ADDRESS BACK
	MOVE	T2,Q1		;POINT TO THE LIST POINTERS
	PUSHJ	P,SCSDEQ	;DEQUEUE THE BUFFER FROM THE QUEUES
	MOVX	T1,.BDFMG	;ASSUME DOING MESSAGES
	SKIPN	MSGFLG		;GOOD GUESS?
	MOVX	T1,.BDFDG	;NO, DATAGRAMS
	PUSHJ	P,SCSGUB	;GET A USER DATAGRAM BUFFER ADDRESS
	  JRST	RDGNBQ		;NEED TO RETURN THIS BUFFER
	MOVEM	T1,USRBUF	;STORE USER BUFFER ADDRESS
	UMOVEM	T1,.SQARB(M)	;RETURN THE ADDRESS OF THE DATA
	MOVE	T4,PAKADR	;GET ADDRESS OF THE PACKET
	LOAD	T1,MELEN,(T4)	;GET THE LENGTH OF THE DATAGRAM
	MOVE	T2,.METYP(T4)	;GET PACKET FLAGS
	TXNE	T2,F.SPM	;HIGH DENSITY MODE?
	SUBI	T1,.MHLNW	;YES, ACCOUNT FOR PACKET HEADER
	TXNN	T2,F.SPM	;INDUSTRY COMPATIBLE MODE?
	SUBI	T1,.MHLNB	;YES, ACCOUNT FOR PACKET HEADER
	MOVEM	T1,LEN		;SAVE LENGTH
	UMOVEM	T1,.SQLRP(M)	;RETURN TO THE USER
	TXNE	T2,F.SPM	;INDUSTRY COMPATIBLE?
	JRST	RDG.03		;NO, NO EXTRA BYTES TO ZERO
	IDIVI	T1,4		;CONVERT TO LENGTH IN WORDS
	JUMPE	T2,RDG.03	;JUMP IF NO REMAINDER
	ADDI	T1,1		;ROUND UP TO EVEN NUMBER OF WORDS
	IMULI	T2,^D8		;GET NUMBER OF BITS TO CLEAR
	MOVNI	T2,-1(T2)	;NEGATE NUMBER OF BITS AND SUBTRACT ONE
	MOVX	T3,1B0		;GET LEFT-MOST BIT
	ASH	T3,(T2)		;MAKE A MASK
	MOVE	T2,PAKADR	;GET PACKET ADDRESS
	ADDI	T2,.MHUDA-1(T1)	;OFFSET TO LAST WORD OF USER DATA
	ANDM	T3,(T2)		;CLEAR EXCESS BITS IN LAST WORD

RDG.03:	MOVE	T2,PAKADR	;GET SOURCE ADDRESS
	ADDI	T2,.MHUDA	;DON'T MOVE THE HEADER
	MOVE	T3,USRBUF	;ADDRESS OF USER'S BUFFER
	PUSHJ	P,SCSDMU	;MOVE DATA TO USER SPACE
	  JRST	RDGACS		;ERROR

	MOVE	T1,PAKADR	;GET PACKET ADDRESS AGAIN
	LOAD	T3,MEFLG,(T1)	;GET THE FLAGS
	EXCTXU	<HRLM T3,.SQDFL(M)> ;STORE FOR USER
	SKIPN	MSGFLG		;DOING MESSAGES?
	PUSHJ	P,SC.RLD##	;NO, DATAGRAM, RETURN IT
	SKIPE	MSGFLG		;DOING MESSAGES?
	PUSHJ	P,SC.RBF##	;NO, MESSAGE, RETURN IT

	LOAD	T1,PBDPN,(P5)	;GET PATH BLOCK ADDRESS
	EXCTXU	<HRRM T1,.SQDFL(M)> ;STORE FOR USER
IFN FTPI,<
	PUSHJ	P,SCSSIG	;SIGNAL ANOTHER INTERRUPT IF NECESSARY
>; END IFN FTPI
	JRST	CPOPJ1##	;SKIP RETURN
;HERE ON ERROR

RDGNBQ:	SKIPA	T1,[SSNBQ%]	;GET ERROR CODE AND SKIP
RDGACS:	MOVEI	T1,SSACS%	;GET ERROR CODE
RDGERR:	MOVEM	T1,ERRCOD	;SAVE ERROR CODE
	SKIPN	T1,USRBUF	;IS THERE A USER BUFFER TO WORRY ABOUT?
	JRST	RDGER2		;NO
	MOVX	T2,.BDFMG	;ASSUME MESSAGE
	SKIPN	MSGFLG		;GOOD GUESS?
	MOVX	T2,.BDFDG	;NO, DATAGRAM
	PUSHJ	P,SCSLUB	;RE-LINK USER BUFFER
	  JFCL			;ERROR

RDGER2:	SKIPN	T1,PAKADR	;DID WE DEQUEUE A PACKET?
	JRST	RDGER3		;NO
	MOVE	T2,Q1		;POINT TO THE 4-WORD LIST HEADER BLOCK
	PUSHJ	P,SCSLFQ	;PUT ON BEGINNING OF QUEUE
RDGER3:	MOVE	T1,ERRCOD	;GET ERROR CODE
	PJRST	STOTAC##	;STORE ERROR CODE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - REJECT

SCSREJ:	CAIGE	P1,.SQREJ	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	UMOVE	T2,.SQREJ(M)	;GET REJECT REASON FROM USER
	BLCAL.	(SC.REJ##,<T1,T2>) ;DO THE REJECT
	  JRST	ERRRCF		;ERROR
	PUSHJ	P,SCSCBD	;DONE WITH CONNECT, DELETE JSYS DATA
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SCS. UUO - DISCONNECT

SCSDIS:	CAIGE	P1,.SQDIS	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	LOAD	T1,CBJCH,(P1)	;GET THE JCH
	CAME	T1,.CPJCH##	;DOES THIS JOB/CONTEXT OWN IT?
	  JRST	ERRICI		;ERROR
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID AGAIN
	UMOVE	T2,.SQDIS(M)	;GET DISCONNECT REASON FROM USER
	BLCAL.	(SC.DIS##,<T1,T2>) ;DO THE DISCONNECT
	  JRST	ERRDCF		;ERROR
	PUSHJ	P,SCSCBD	;DONE WITH CONNECT, DELETE JSYS DATA
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SCS. UUO - SEND DATAGRAM/MESSAGE

SCSSDG:	TDZA	T1,T1		;INDICATE WE ARE DOING DATAGRAM
SCSSMG:	SETO	T1,		;INDICATE WE ARE DOING MESSAGE
	CAIGE	P1,.SQFLG	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	STKVAR	<BUFADR,CID,MSGFLG,ERRCOD> ;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,MSGFLG	;SAVE THE ENTRY TYPE
	SETZM	BUFADR		;INIT THE BUFFER ADDRESS AS ZERO
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	MOVEM	T1,CID		;SAVE THE CID FOR LATER
	MOVEI	T1,1		;ASK FOR A PACKET BUFFER
	SKIPN	MSGFLG		;DOING MESSAGE SEND?
	JRST	SDG.01		;NO, REQUEST A DATAGRAM BUFFER
	PUSHJ	P,SC.ABF##	;ASK SCASER FOR A MESSAGE BUFFER
	  JRST	ERRNFB		;ERROR
	MOVX	T2,JH%DGB	;INDICATE THIS IS A MESSAGE BUFFER
	ANDCAM	T2,.JHFLG(T1)	;...
	JRST	SDG.02		;CONTINUE

SDG.01:	PUSHJ	P,SC.ALD##	;ASK SCASER FOR A DATAGRAM BUFFER
	  JRST	ERRNFB		;ERROR
	MOVX	T2,JH%DGB	;INDICATE THIS IS A DATAGRAM BUFFER
	IORM	T2,.JHFLG(T1)	;...
SDG.02:	MOVEM	T1,BUFADR	;SAVE ADDRESS OF BUFFER
	UMOVE	T2,.SQAPT(M)	;GET USER ADDRESS OF PACKET TEXT
	MOVEM	T2,.JHAUB(T1)	;STORE THE ADDRESS OF THE USER BUFFER
	UMOVE	T1,.SQLPT(M)	;GET LENGTH OF PACKET
	UMOVE	T2,.SQFLG(M)	;GET FLAGS WORD
	TXNE	T2,SC%MOD	;IS THIS A HIGH DENSITY PACKET?
	JRST	SDG.03		;YES
	ADDI	T1,3		;NO, ROUND UP BYTE COUNT
	LSH	T1,-2		;CONVERT BYTE COUNT TO WORD COUNT
SDG.03:	MOVX	T3,<C%MGSZ-.MHUDA> ;ASSUME A MESSAGE
	SKIPN	MSGFLG		;GOOD GUESS?
	MOVX	T3,<C%DGSZ-.MHUDA> ;NO
	CAMLE	T1,T3		;IS THE TEXT TOO LONG?
	JRST	SDGSTL		;YES
	MOVE	T3,BUFADR	;GET THE ADDRESS OF THE MONITOR BUFFER
	MOVE	T2,.JHAUB(T3)	;ADDRESS OF USER BUFFER
	ADDI	T3,.MHUDA	;OFFSET TO TEXT AREA IN BUFFER
	PUSHJ	P,SCSDUM	;MOVE THE DATA FROM USER TO MONITOR
	  JRST	SDGACS		;ERROR
	MOVX	T2,F.RTB	;START WITH JUST THE RETURN BUFFER FLAG
	UMOVE	T3,.SQFLG(M)	;GET FLAGS WORD AGAIN
	TXNE	T3,SC%MOD	;IS THIS A HIGH DENSITY PACKET?
	TXO	T2,F.SPM	;YES
	LOAD	T3,SC%OPS,T3	;GET OPTIONAL PATH SPEC
	UMOVE	T4,.SQLPT(M)	;GET LENGTH OF PACKET
	SKIPN	MSGFLG		;DOING MESSAGE SEND?
	JRST	SDG.04		;NO
	BLCAL.	(SC.SMG##,<CID,T2,T4,BUFADR,[SCSPRI],[0],T3>)
	  JRST	SDGERR		;ERROR
	JRST	CPOPJ1##	;SKIP RETURN

SDG.04:	BLCAL.	(SC.SDG##,<CID,T2,T4,BUFADR,[SCSPRI],T3>)
	  JRST	SDGERR		;ERROR
	JRST	CPOPJ1##	;SKIP RETURN

SDGSTL:	SKIPA	T1,[SSSTL%]	;GET ERROR CODE AND SKIP
SDGACS:	MOVEI	T1,SSACS%	;GET ERROR CODE
SDGERR:	MOVEM	T1,ERRCOD	;SAVE ERROR CODE
	SKIPN	T1,BUFADR	;IS THERE A BUFFER OWNED?
	JRST	SDGER2		;NO
	SKIPN	MSGFLG		;DATAGRAM?
	PUSHJ	P,SC.RLD##	;YES
	SKIPE	MSGFLG		;MESSAGE?
	PUSHJ	P,SC.RBF##	;YES
SDGER2:	MOVE	T1,ERRCOD	;GET ERROR CODE BACK
	PJRST	STOTAC##	;STORE ERROR CODE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - QUEUE DATAGRAM/MESSAGE BUFFERS

SCSQRD:	TDZA	T1,T1		;INDICATE WE ARE DOING DATAGRAM
SCSQRM:	SETO	T1,		;INDICATE WE ARE DOING MESSAGE
	CAIGE	P1,.SQAFB	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	STKVAR	<QRMFLG,CID>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,QRMFLG	;SAVE ENTRY TYPE FLAG
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	MOVEM	T1,CID		;SAVE THE CID FOR LATER
	UMOVE	T1,.SQAFB(M)	;GET THE ADDRESS OF THE FIRST BUFFER
	MOVX	T2,C%MGSZ	;ASSUME THEY'RE QUEUEING MESSAGE BUFFERS
	SKIPN	QRMFLG		;GOOD GUESS?
	MOVX	T2,C%DGSZ	;NO, GET THE SIZE FOR DATAGRAMS
	PUSHJ	P,SCSCUB	;COUNT AND ADDRESS CHECK USER BUFFERS
	  JRST	ERRACR		;ADDRESS CHECK
	MOVE	P2,T3		;SAVE COUNT FOR A MOMENT

	UMOVE	T1,.SQAFB(M)	;GET ADDRESS OF USER BUFFER AGAIN
	MOVX	T2,.BDFMG	;ASSUME THEY'RE QUEUEING MESSAGE BUFFERS
	SKIPN	QRMFLG		;GOOD GUESS?
	MOVX	T2,.BDFDG	;NO, THEY'RE DATAGRAM BUFFERS
	PUSHJ	P,SCSXUB	;LINK THESE BUFFERS INTO DATABASE
	  POPJ	P,		;ERROR
	SKIPN	QRMFLG		;DOING DATAGRAMS?
	JRST	QRD.01		;YES
	BLCAL.	(SC.RMG##,<CID,P2,[0]>) ;QUEUE BUFFERS FOR THIS CONNECTION
	  JRST	ERRQBF		;ERROR
	JRST	CPOPJ1##	;SUCCESS

QRD.01:	BLCAL.	(SC.RDG##,<CID,P2,[0]>) ;QUEUE BUFFERS FOR THIS CONNECTION
	  JRST	ERRQBF		;ERROR
	JRST	CPOPJ1##	;SUCCESS

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - CANCEL BUFFERS

SCSCRM:	TDZA	T1,T1		;SET THE MESSAGE FLAG
SCSCRD:	SETO	T1,		;SET THE DATAGRAM FLAG
	CAIGE	P1,.SQADB	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	STKVAR	<CRDFLG,CID>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,CRDFLG	;SAVE THE ENTRY FLAG
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	MOVEM	T1,CID		;SAVE CONNECT ID FOR LATER
	UMOVE	T1,.SQADB(M)	;GET ADDRESS OF THE BUFFER TO RETURN
	PUSHJ	P,SCSRUB	;REMOVE THE BUFFER FROM THE DATABASE
	  POPJ	P,		;ERROR
	SKIPN	CRDFLG		;ARE WE DOING DATAGRAMS?
	JRST	CRM.01		;NO, CANCEL A MESSAGE
	BLCAL.	(SC.CRD##,<CID,[1]>) ;CANCEL ONE DATAGRAM
	  JRST	ERRCBF		;ERROR
	SOS	.CBDGJ(P1)	;ONE LESS DATAGRAM QUEUED
	JRST	CPOPJ1##	;SKIP RETURN

CRM.01:	BLCAL.	(SC.CRM##,<CID,[1]>) ;CANCEL ONE MESSAGE
	  JRST	ERRCBF		;ERROR
	SOS	.CBMGJ(P1)	;ONE LESS MESSAGE QUEUED
	JRST	CPOPJ1##	;SKIP RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - CONNECT STATE POLL

SCSCSP:	CAIGE	P1,.SQREA	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	LOAD	T1,CBCNST,(P1)	;GET CONNECT STATE
	UMOVEM	T1,.SQCST(M)	;STORE IT
	MOVE	T1,.CBDCI(P1)	;GET DESTINATION CONNECT ID
	UMOVEM	T1,.SQDCI(M)	;STORE IT
	MOVE	T1,.CBPBK(P1)	;ADDRESS OF PATH BLOCK
	LOAD	T1,PBDPN,(T1)	;GET DESTINATION NODE NUMBER
	UMOVEM	T1,.SQNOD(M)	;STORE IT
	LOAD	T1,CBDDRE,(P1)	;GET DESTINATION DISCONNECT REASON
	LOAD	T2,CBSDRE,(P1)	;GET SOURCE DISCONNECT REASON
	HRL	T1,T2		;COMBINE IN T1
	UMOVEM	T1,.SQREA(M)	;STORE IT
	UMOVE	T1,.SQBDN(M)	;GET BYTE POINTER TO RETURN PROCESS NAME
	TLC	T1,-1		;SEE IF A GENERIC BYTE POINTER
	TLCE	T1,-1		;...
	JRST	CSP.01		;NO, USE WHAT USER SUPPLIED
	HLL	T1,M		;MAKE A SECTION LOCAL
	TLO	T1,610000	; ONE-WORD GLOBAL BYTE POINTER
CSP.01:	MOVE	P2,T1		;SAVE THE BYTE POINTER ACROSS CALL TO PRNGE
	MOVX	T2,C%PNMN	;LENGTH OF PROCESS NAME IN BYTES
	PUSHJ	P,PRNGE##	;SEE IF LEGAL TO STORE IN THAT BUFFER
	 JRST	ERRACS		;ILLEGAL ADDRESS
	  JRST	ERRACS		;ILLEGAL FOR I/O
	MOVX	T1,C%PNMN	;LENGTH OF PROCESS NAME IN BYTES
	MOVE	T2,[POINT 8,.CBDPN(P1)] ;BYTE POINTER TO PROCESS NAME
CSP.02:	ILDB	T3,T2		;GET A BYTE
	EXCTXU	<IDPB T3,P2>	;STORE THE BYTE
	SOJG	T1,CSP.02	;LOOP FOR THE WHOLE THING
	JRST	CPOPJ1##	;NOW SKIP RETURN
	SUBTTL	SCS. UUO - RETURN CONFIGURATION DATA

SCSRCD:	CAIGE	P1,.SQLPN	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	EXCTUX	<SKIPN T1,.SQCID(M)> ;GET CONNECT ID
	JRST	RCD.01		;CONNECT ID IS ZERO, USE OPTIONAL NODE NUMBER
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	JRST	RCD.02		;CONTINUE

RCD.01:	PUSHJ	P,CPUPCB	;TRANSLATE CPU ARGUMENT INTO PCB
	  POPJ	P,		;ERROR, CODE ALREADY STORED
	EXCTUX	<SKIPL T1,.SQOND(M)> ;GET NODE NUMBER, CHECK FOR VALIDITY
	CAIL	T1,MAXNDS	;...
	JRST	ERRINN		;INVALID NODE NUMBER
	ADD	T1,P6		;OFFSET PCB ADDRESS BY NODE NUMBER
	SKIPN	P5,.PCPBK(T1)	;GET PATH BLOCK ADDRESS
	JRST	ERRVNO		;ERROR
RCD.02:	LOAD	P4,PBSBI,(P5)	;GET SYSTEM BLOCK INDEX
	SKIPN	P4,SBLIST##-1(P4) ;GET THE SYSTEM BLOCK ADDRESS
	JRST	ERRVNO		;ERROR
	LOAD	T1,SBDPN,(P4)	;GET DESTINATION PORT NUMBER
	LOAD	T2,PBVCST,(P5)	;GET VIRTUAL CIRCUIT STATE
	HRL	T1,T2		;COMBINE IN T1
	UMOVEM	T1,.SQVCS(M)	;STORE IT
	DMOVE	T1,.SBDSA(P4)	;GET TWO WORDS OF SYSTEM ADDRESS
	EXCTXU	<DMOVEM T1,.SQSAD(M)> ;STORE THEM
	LOAD	T1,SBMXDG,(P4)	;GET MAXIMUM DATAGRAM SIZE
	UMOVEM	T1,.SQMDD(M)	;STORE IT
	LOAD	T1,SBMXMG,(P4)	;GET MAXIMUM MESSAGE SIZE
	UMOVEM	T1,.SQMDM(M)	;STORE IT
	MOVE	T1,.SBDST(P4)	;GET DESTINATION SOFTWARE TYPE
	UMOVEM	T1,.SQDST(M)	;STORE IT
	MOVE	T1,.SBDSV(P4)	;GET DESTINATION SOFTWARE VERSION
	UMOVEM	T1,.SQDSV(M)	;STORE IT
	DMOVE	T1,.SBDSE(P4)	;GET TWO WORDS OF DESTINATION SOFTWARE EDIT
	EXCTXU	<DMOVEM T1,.SQDSE(M)> ;STORE THEM
	MOVE	T1,.SBDHT(P4)	;GET DESTINATION HARDWARE TYPE
	UMOVEM	T1,.SQDHT(M)	;STORE IT
	DMOVE	T1,.SBDHV(P4)	;GET TWO WORDS OF DESTINATION HARDWARE VERSION
	EXCTXU	<DMOVEM T1,.SQDHV(M)> ;STORE THEM
	MOVE	T1,.SBDHV+2(P4)	;GET THIRD WORD OF DESTINATION HARDWARE VERSION
	UMOVEM	T1,.SQDHV+2(M)	;STORE IT
	DMOVE	T1,.SBNNM(P4)	;GET TWO WORDS OF DESTINATION NODE NAME
	EXCTXU	<DMOVEM T1,.SQNNM(M)> ;STORE THEM
	MOVE	T1,.PBDPC(P5)	;GET DESTINATION PORT CHARACTERISTICS
	UMOVEM	T1,.SQPCW(M)	;STORE IT
	MOVEI	T1,KLPICH	;RH20 CHANNEL FOR THE KLIPA
	UMOVEM	T1,.SQLPN(M)	;STORE IT
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SCS. UUO - RETURN STATUS OF CONNECT

SCSSTS:	CAIGE	P1,.SQSBR	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	LOAD	T1,CBCNST,(P1)	;GET THE CONNECTION STATE
	SKIPE	.CBTMQ(P1)	;ANY MESSAGES PENDING?
	TXO	T1,SC%MSA	;YES
	SKIPE	.CBTDQ(P1)	;ANY DATAGRAMS AVAILABLE?
	TXO	T1,SC%DGA	;YES
	SKIPE	.CBTXQ(P1)	;ANY DATA REQUESTS PENDING?
	TXO	T1,SC%DTA	;YES
	SKIPE	.CBTEQ(P1)	;AN EVENT PENDING?
	TXO	T1,SC%EVA	;YES
	UMOVEM	T1,.SQFST(M)	;STORE IT
	MOVE	T1,.CBPBK(P1)	;GET PATH BLOCK ADDRESS
	LOAD	T1,PBDPN,(T1)	;GET DESTINATION NODE NUMBER
	UMOVEM	T1,.SQSBR(M)	;STORE IT
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SCS. UUO - MAP A BUFFER

SCSMAP:	CAIGE	P1,.SQBNA+.SQBAD+1 ;ROOM IN ARGUMENT BLOCK
	JRST	ERRARG		;NO
	PUSHJ	P,CPUPCB	;TRANSLATE CPU ARGUMENT INTO PCB
	  POPJ	P,		;ERROR, CODE ALREADY STORED
	MOVEI	T1,-.SQBNA(P1)	;GET USER LENGTH MINUS OVERHEAD
	CAILE	T1,<C%DGSZ-2>/<.SQBAD+1> ;MORE SEGMENTS THAN WE CAN HANDLE?
	JRST	ERRTMS		;TOO MANY SEGMENTS
	LSH	T1,-1		;CONVERT TO NUMBER OF SEGMENT DESCRIPTORS
	MOVE	P1,T1		;SAVE COUNT IN P1
	MOVE	P2,M		;COPY ADDRESS OF ARGUMENT BLOCK
	ADDI	P2,.SQBNA+1	;OFFSET TO FIRST USER SEGMENT DESCRIPTOR
;WHAT ABOUT CROSSING SECTION BOUNDARIES HERE?  IS THERE A BETTER WAY
;TO INCREMENT THE ADDRESS?
	STKVAR	<FBADDR,ERRCOD,MODE,WRDLEN> ;ALLOCATE SOME STACK STORAGE
	MOVEI	T1,1		;ASK FOR A DATAGRAM BUFFER
	PUSHJ	P,SC.ALD##	; TO USE FOR THE MAP DESCRIPTOR BLOCK
	  JRST	ERRNFC		;NOT AVAILABLE
	MOVEM	T1,FBADDR	;SAVE HERE FOR LATER USE
	MOVE	P4,T1		;COPY HERE FOR IMMEDIATE USE

	UMOVE	T1,.SQXFL(M)	;GET THE USERS FLAGS
	ANDX	T1,MD%FLG	;KEEP ONLY THE DEFINED ONES
	TXO	T1,SQ%WRT	;SET THE WRITABLE BIT ALWAYS
	MOVEM	T1,.MDFLG(P4)	;STORE FLAGS/MODE IN DESCRIPTOR BLOCK
	LOAD	T1,MD%DMD,T1	;GET THE BUFFER MODE
	CAILE	T1,MD%DHD	;IS IT A LEGAL VALUE?
	JRST	MAPIDM		;NO
	MOVEM	T1,MODE		;SAVE MODE FOR LATER
	ADDI	P4,.MDSSD	;OFFSET TO FIRST DESCRIPTOR BLOCK

MAP.01:	UMOVE	T3,.SQBLN(P2)	;GET LENGTH OF SEGMENT
	JUMPE	T3,MAP.04	;ZERO LENGTH MEANS END OF DESCRIPTORS
	MOVEM	T3,.MDLEN(P4)	;STORE LENGTH IN DESCRIPTOR
	MOVE	T1,MODE		;GET THE MODE
	CAIN	T1,MD%DIC	;IS IT INDUSTRY COMPATIBLE?
	JRST	MAP.02		;YES
	LSH	T3,1		;NO, CONVERT NIBBLES TO WORDS
	IDIVI	T3,^D9		;(MULTIPLY BY 2/9)
	SKIPE	T4		;A REMAINDER?
	ADDI	T3,1		;YES, ADD ONE TO THE WORD COUNT
	JRST	MAP.03		;CONTINUE

MAP.02:	LSH	T3,-2		;CONVERT BYTE COUNT TO WORD COUNT
MAP.03:	MOVEM	T3,WRDLEN	;SAVE NUMBER OF WORDS IN SEGMENT
	CAILE	T3,PGSIZ	;BLOCK LONGER THAN ONE PAGE?
	JRST	MAPSTL		;YES
	UMOVE	T1,.SQBAD(P2)	;GET ADDRESS OF SEGMENT
	MOVE	T2,T3		;COPY LENGTH
	PUSHJ	P,ARNGE##	;SEE IF WRITABLE
	 JFCL			;YES
	  JRST	MAPACS		;ERROR
	EXCTUU	<MAP T1,(T1)>	;GET THE PHYSICAL ADDRESS
	TXNE	T1,MP.BAD	;BAD MAPPING?
	JRST	MAPSCP		;YES, ASSUME THEY PASSED US GARBAGE
	TXZ	T1,MP.NAD	;CLEAR NON-ADDRESS BITS
	MOVE	T2,T1		;COPY THE PHYSICAL ADDRESS
	ANDI	T2,PG.BDY##	;KEEP ONLY THE PAGE OFFSET
	ADD	T2,T3		;ADD THE LENGTH OF THE SEGMENT
	CAILE	T2,PGSIZ	;MORE THAN A PAGE IN LENGTH?
	JRST	MAPSCP		;YES
	MOVEM	T1,.MDADR(P4)	;SAVE SEGMENT ADDRESS

	ADDI	P4,.MDLSD	;POINT AT NEXT SEGMENT DESCRIPTOR
	ADDI	P2,.SQBAD+1	;...
	SOJG	P1,MAP.01	;LOOP FOR OTHER SEGMENTS

;CALL SCA TO MAP THIS BUFFER

MAP.04:	BLCAL.	(SC.MAP##,<FBADDR>) ;DO THE REAL WORK
	  JRST	MAPERR		;ERROR
	UMOVEM	T1,.SQBNA(M)	;TELL USER THE BUFFER NAME
	PUSHJ	P,SCSAXN	;ADD THE NAME TO THE CONTEXT'S DATABASE
	  JRST	MAPNFC		;ERROR
	MOVE	T1,FBADDR	;GET THE BUFFER ADDRESS
	PUSHJ	P,SC.RLD##	;RETURN IT TO SCA
	JRST	CPOPJ1##	;SKIP RETURN

;HERE ON ERRORS

MAPSCP:	MOVEI	T1,SSSCP%	;GET ERROR CODE
	JRST	MAPERR		;JOIN COMMON CODE

MAPSTL:	SKIPA	T1,[SSSTL%]	;GET ERROR CODE AND SKIP
MAPACS:	MOVEI	T1,SSACS%	;GET ERROR CODE
	JRST	MAPERR		;JOIN COMMON CODE

MAPIDM:	SKIPA	T1,[SSIDM%]	;GET ERROR CODE AND SKIP
MAPNFC:	MOVEI	T1,SSNFC%	;GET ERROR CODE
MAPERR:	MOVEM	T1,ERRCOD	;SAVE ERROR CODE
	MOVE	T1,FBADDR	;GET THE BUFFER ADDRESS
	PUSHJ	P,SC.RLD##	;RETURN IT TO SCA
	MOVE	T1,ERRCOD	;GET THE ERROR CODE
	PJRST	STOTAC##	;STORE IT AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - UNMAP A BUFFER


SCSUMP:	CAIGE	P1,.SQNAM	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	PUSHJ	P,CPUPCB	;TRANSLATE CPU ARGUMENT INTO PCB
	  POPJ	P,		;ERROR, CODE ALREADY STORED
	STKVAR	<BUFNAM>	;ALLOCATE SOME STACK STORAGE
	UMOVE	T1,.SQNAM(M)	;GET THE BUFFER NAME
	MOVEM	T1,BUFNAM	;SAVE IT
	PUSHJ	P,SCSFXN	;SEE IF WE HAVE SUCH A BUFFER NAME
	  JRST	ERRNSB		;ERROR
	MOVE	T1,BUFNAM	;GET BUFFER NAME AGAIN
	PUSHJ	P,SCSDXN	;REMOVE IT FROM THE DATABASE
	  JRST	ERRCRB		;ERROR
	JRST	CPOPJ1##	;SKIP RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - SEND/REQUEST DATA

SCSREQ:	TDZA	T1,T1		;INDICATE THIS IS A REQUEST
SCSSND:	SETO	T1,		;INDICATE THIS IS A SEND
	CAIGE	P1,.SQOFS	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	STKVAR	<SNDFLG,CID>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,SNDFLG	;SAVE ENTRY TYPE FLAG
	UMOVE	T1,.SQCID(M)	;GET CONNECTION ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	MOVEM	T1,CID		;SAVE FOR LATER
	UMOVE	T1,.SQSNM(M)	;GET NAME OF BUFFER TO SEND
	UMOVE	T2,.SQRNM(M)	;GET NAME OF BUFFER TO RECEIVE
	EXCTUX	<HLRZ T3,.SQOFS(M)> ;GET SEND BUFFER OFFSET
	EXCTUX	<HRRZ T4,.SQOFS(M)> ;GET RECEIVE BUFFER OFFSET
	SKIPN	SNDFLG		;ARE WE DOING A SEND?
	JRST	REQ.01		;NO
	BLCAL.	(SC.SND##,<CID,T1,T2,T3,T4>)
	  JRST	ERRPSF		;ERROR
	JRST	CPOPJ1##	;SKIP RETURN

REQ.01:	BLCAL.	(SC.REQ##,<CID,T1,T2,T3,T4>)
	  JRST	ERRPSF		;ERROR
	JRST	CPOPJ1##	;SKIP RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - ACCEPT CONNECTION

SCSACC:	CAIGE	P1,.SQCDA	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	STKVAR	<DATADR,DATRET,ERRCOD,CID> ;ALLOCATE SOME STACK STORAGE
	SETZM	DATADR		;ZERO ADDRESS OF CONNECTION DATA
	UMOVE	T1,.SQCID(M)	;GET CONNECT ID
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	MOVEM	T1,CID		;SAVE FOR LATER
	UMOVE	T1,.SQCDA(M)	;GET THE ADDRESS OF THE CONNECTION DATA
	JUMPE	T1,ACC.01	;JUMP IF NO USER CONNECTION DATA
	PUSHJ	P,SCSUDM	;MOVE THE USER CONNECT DATA
	  JRST	ERRACR		;ADDRESS CHECK
	MOVEM	T1,DATADR	;SAVE THE ADDRESS OF THE CONNECT DATA
	MOVEM	T2,DATRET	; AND THE ROUTINE TO RETURN FREE SPACE
ACC.01:	BLCAL.	(SC.ACC##,<CID,DATADR,[0],[0]>) ;DO THE ACCEPT
	  JRST	ACCERR		;ERROR
	SKIPE	T1,DATADR	;ANY USER CONNECTION DATA?
	PUSHJ	P,@DATRET	;YES, RETURN IT
	JRST	CPOPJ1##	;SKIP RETURN

ACCERR:	MOVEM	T1,ERRCOD	;SAVE THE ERROR CODE
	SKIPE	T1,DATADR	;ANY USER CONNECTION DATA?
	PUSHJ	P,@DATRET	;YES, RETURN IT
	MOVE	T1,ERRCOD	;GET ERROR CODE BACK
	PJRST	STOTAC##	;STORE ERROR CODE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - GET DATA QUEUE ENTRY

SCSGDE:	CAIGE	P1,.SQBID	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	STKVAR	<BLKADR>	;ALLOCATE A WORD OF STACK STORAGE
	UMOVE	T1,.SQCID(M)	;GET THE CONNECT ID
	CAMN	T1,[-1]		;WANT NEXT FOR THE CONTEXT?
	JRST	GDE.01		;YES
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	SKIPN	T1,.CBTXQ(P1)	;IS THERE ANYTHING ON THE QUEUE?
	JRST	ERRDQE		;NO
	JRST	GDE.02		;CONTINUE

GDE.01:	SKIPN	T1,SCSTXQ(P3)	;ANYTHING ON CONTEXT QUEUE?
	JRST	ERRDQE		;NO
	$LDCID	P1,.MECID(T1)	;GET CB ADDRESS FROM CID
GDE.02:	XMOVEI	T2,XFER		;POINT TO THE BLOCK
	PUSHJ	P,SCSDEQ	;DEQUEUE THE BUFFER
	MOVE	T2,.DMNAM(T1)	;GET THE BUFFER NAME FROM THE BLOCK
	UMOVEM	T2,.SQBID(M)	;GIVE IT TO THE USER
	PUSHJ	P,RELRES	;RETURN THE FREE SPACE
IFN FTPI,<
	PUSHJ	P,SCSSIG	;SIGNAL ANOTHER INTERRUPT IF NECESSARY
>; END IFN FTPI
	JRST	CPOPJ1##	;SKIP RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - GET EVENT QUEUE ENTRY

SCSEVT::CAIGE	P1,.SQEVT	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	STKVAR	<EVTADR,EVTCOD,USRLEN,ERRCOD>
	MOVEM	P1,USRLEN	;SAVE LENGTH OF BLOCK
	UMOVE	T1,.SQCID(M)	;GET THE CONNECT ID
	CAMN	T1,[-1]		;FOR CONTEXT OR CONNECTION?
	JRST	EVT.01		;CONTEXT
	PUSHJ	P,CHKCID	;CHECK THE CID
	  JRST	ERRICI		;ERROR
	SKIPN	T1,.CBTEQ(P1)	;IS THERE ANYTHING ON THE QUEUE?
	JRST	ERREQE		;NO
	JRST	EVT.02		;PROCEED

EVT.01:	SKIPN	T1,SCSTEQ(P3)	;IS THERE SOMETHING ON THE CONTEXT QUEUE?
	JRST	ERREQE		;NO
EVT.02:	MOVEM	T1,EVTADR	;SAVE ADDRESS OF EVENT BLOCK
	LOAD	T3,EBLEN,(T1)	;GET LENGTH OF BLOCK
	SUBI	T3,.EBDAT	;DON'T COUNT THE OVERHEAD AREA
	CAML	T3,USRLEN	;IS THERE ROOM IN USER'S BLOCK FOR THIS EVENT?
	JRST	ERRARG		;NO
	XMOVEI	T2,EVT		;POINT TO THE EVENT BLOCK
	$LDCID	P1,.MECID(T1)	;GET CB ADDRESS
	MOVE	P5,.CBPBK(P1)	;GET PBK ADDRESS
	LOAD	T4,PBDPN,(P5)	;GET DESTINATION NODE
	UMOVEM	T4,.SQESB(M)	;STORE FOR USER
EVT.03:	PUSHJ	P,SCSDEQ	;DEQUEUE THIS ENTRY
	LOAD	T4,EBCOD,(T1)	;GET THE CODE FROM THE BLOCK
	MOVEM	T4,EVTCOD	;SAVE IT
	UMOVEM	T4,.SQEVT(M)	;RETURN TO USER
	MOVE	T4,.MECID(T1)	;GET CID FROM THE BLOCK
	UMOVEM	T4,.SQCID(M)	;RETURN TO USER
	LOAD	T3,EBLEN,(T1)	;GET THE LENGTH BLOCK
	SUBI	T3,.EBDAT	;DON'T COUNT THE OVERHEAD
	JUMPE	T3,EVT.04	;QUIT IF NO DATA TO MOVE
	MOVE	T2,T1		;SOURCE ADDRESS FOR XLBT
	MOVE	T1,T3		;LENGTH OF DATA TO MOVE
	MOVE	T3,M		;GET ADDRESS OF USER'S BLOCK
	ADDI	T2,.EBDAT	;OFFSET SOURCE TO JUST EVENT DATA
	ADDI	T3,.SQDTA	;OFFSET TO DATA AREA IN USER BLOCK
	PUSHJ	P,SCSDMU	;MOVE DATA TO THE USER
	  JRST	EVTERR		;ERROR
EVT.04:	MOVE	T1,EVTCOD	;GET THE EVENT CODE
	CAIN	T1,.SEPBC	;DID THE VC CLOSE?
	JRST	EVT.05		;YES
	CAIE	T1,.SERID	;IS THIS A REMOTE DISCONNECT?
	CAIN	T1,.SECRR	;OR A CONNECT REJECTION?
EVT.05:	PUSHJ	P,SCSCBD	;YES, CLEAN UP JSYS CONNECTION DATA

	MOVE	T1,EVTADR	;GET ADDRESS OF THE BLOCK
	PUSHJ	P,RELRES	;RETURN THE SPACE
IFN FTPI,<
	PUSHJ	P,SCSSIG	;SIGNAL ANOTHER INTERRUPT IF NECESSARY
>; END IFN FTPI
	JRST	CPOPJ1##	;SKIP RETURN

;HERE ON ERROR

EVTERR:	PUSHJ	P,EVT.04	;HANDLE MOST OF THE WORK
	JRST	ERRACS		;THEN GIVE AN ADDRESS CHECK ERROR

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SCS. UUO - GET LOCAL NODE NUMBER

SCSGLN:	CAIGE	P1,.SQLNN	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	PUSHJ	P,CPUPCB	;TRANSLATE CPU ARGUMENT INTO A PCB ADDRESS
	  POPJ	P,		;ERROR, CODE ALREADY STORED
	MOVX	T1,ST.DED	;SEE IF PORT IS DEAD
	TDNN	T1,.PCSTS(P6)	;IF SO, GIVE AN ERROR HERE
	SKIPGE	T1,.PCONN(P6)	;GET NODE NUMBER, SKIP IF KNOWN
	JRST	ERRNNK		;NODE NUMBER NOT KNOWN
	UMOVEM	T1,.SQLNN(M)	;STORE IT
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SCS. UUO - RETURN BUFFER SIZES

SCSRBS:	CAIGE	P1,.SQLDG	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	MOVX	T1,<C%MGSZ-.MHUDA> ;GET THE SIZE OF A MESSAGE BUFFER
	UMOVEM	T1,.SQLMG(M)	;STORE IT
	MOVX	T1,<C%DGSZ-.MHUDA> ;GET THE SIZE OF A DATAGRAM BUFFER
	UMOVEM	T1,.SQLDG(M)	;STORE IT
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SCS. UUO - RETURN PATH STATUS

SCSRPS:	CAIGE	P1,.SQRPS	;ROOM IN ARGUMENT BLOCK?
	JRST	ERRARG		;NO
	PUSHJ	P,CPUPCB	;TRANSLATE CPU ARGUMENT INTO A PCB ADDRESS
	  POPJ	P,		;ERROR, CODE ALREADY STORED
	EXCTUX	<SKIPL T1,.SQRPN(M)> ;GET NODE NUMBER, CHECK FOR VALIDITY
	CAIL	T1,MAXNDS	;...
	JRST	ERRINN		;INVALID NODE NUMBER
	ADD	P6,T1		;OFFSET PCB ADDRESS BY CI NODE NUMBER
	MOVE	P6,.PCRIS(P6)	;GET STATUS OF PATHS TO THAT NODE
	TXNN	P6,RI.PAO	;PATH A OPEN?
	TLZA	T1,-1		;NO, CLEAR LH OF T1 AND SKIP
	HRLI	T1,1		;YES, SET A FLAG
	TXNN	P6,RI.PBO	;PATH B OPEN?
	TRZA	T1,-1		;NO, CLEAR RH OF T1 AND SKIP
	HRRI	T1,1		;YES, SET A FLAG
	UMOVEM	T1,.SQRPS(M)	;STORE IT
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SCS INTERRUPT NOTIFICATION


;ROUTINE CALLED WHEN SCS DETERMINES THE SYSAP SHOULD BE TOLD ABOUT
;A CHANGE IN THE STATE OF ONE OF HIS CONNECTIONS, ETC.  SEE THE
;VARIOUS .SS??? NOTIFICATION DEFINITIONS IN SCAPRM.
;CALL:
;	T1/ REASON
;	T2 - T4/ REASON DEPENDENT DATA
;	PUSHJ	P,SCSINT
;RETURN:
;	CPOPJ ALWAYS
;ENTRIES IN THE DISPATCH TABLE FLAGGED WITH "*" ARE CALLED
;WITH THE CONNECT ID IN T2.

SCSINT:	PUSHJ	P,SAVPQ##	;SAVE THE "PRESERVED" SCASER REGISTERS
	CAILE	T1,.SSAFT	;A CODE WE KNOW OF?
	STOPCD	.,STOP,SCSNOR,	;++NOTIFICATION CODE OUT OF RANGE
	JRST	@SINDSP(T1)	;DISPATCH BASED ON REASON

SINDSP:	IFIW	SINDGR		;(0) DATAGRAM RECEIVED *
	IFIW	SINMGR		;(1) MESSAGE RECEIVED *
	IFIW	SINPBC		;(2) PORT BROKE CONNECTION *
	IFIW	SINCTL		;(3) CONNECT TO LISTEN *
	IFIW	SINCRA		;(4) CONNECTION RESPONSE AVAILABLE *
	IFIW	SINPSC		;(5) PACKET SEND COMPLETE *
	IFIW	CPOPJ##		;(6) DATAGRAM DROPPED *
	IFIW	SINLCL		;(7) LITTLE CREDIT LEFT *
	IFIW	SINONL		;(10) NODE CAME ONLINE
	IFIW	SINOSD		;(11) OK TO SEND DATA *
	IFIW	SINRID		;(12) REMOTE INITIATED DISCONNECT *
	IFIW	SINCIA		;(13) CREDIT IS AVAILABLE *
	IFIW	SINDMA		;(14) DMA TRANSFER COMPLETE *
IF2,<IFN .SSAFT-<.-SINDSP-1>,<PRINTX ?Dispatch table SINDSP out of order>>
	SUBTTL	INTERRUPT NOTIFICATION - NODE(PATH) ONLINE


;ROUTINE CALLED WHEN A NODE(PATH) HAS COME ONLINE.
;CALL:
;	T1/ .SSNCO
;	T2/ PATH BLOCK INDEX
;	PUSHJ	P,SINONL
;RETURN:
;	CPOPJ ALWAYS
;NOTE THAT WE DO NOT GET CALLED HERE FOR CONNECTIONS, ONLY FOR
;SCSUUO'S CALL TO SC.SNA.  THEREFORE, WE DON'T HAVE A CONNECT ID,
;AND MUST INSTEAD LOOP THROUGH ALL JOBS GIVING THEM AN ONLINE
;INTERRUPT IF THEY ARE PSI'ING ON SCS EVENTS.

SINONL:	POPJ	P,		;RETURN
	SUBTTL	INTERRUPT NOTIFICATION - MESSAGE/DATAGRAM RECEIVED


;ROUTINE CALLED WHEN A MESSAGE/DATAGRAM HAS BEEN RECEIVED.
;CALL:
;	T1/ .SSDGR OR .SSMGR
;	T2/ CONNECT ID
;	T3/ PACKET ADDRESS
;	T4/ FLAGS FROM PPD
;	PUSHJ	P,SINDGR/SINMGR
;RETURN:
;	CPOPJ ALWAYS

SINDGR:	TDZA	T1,T1		;INDICATE DATAGRAM ENTRY
SINMGR:	SETO	T1,		;INDICATE MESSAGE ENTRY
	EXCH	T1,T3		;GET PACKET ADDRESS WHERE EXPECTED
	MOVEM	T2,.MECID(T1)	;STORE CONNECT ID
	ANDX	T4,C%FLGM	;KEEP JUST THE FLAGS
	IORM	T4,.METYP(T1)	;STORE IN BLOCK
	MOVX	T2,.ETMSG	;ASSUME DOING MESSAGES
	SKIPN	T3		;GOOD GUESS?
	MOVX	T2,.ETDG	;NO, DATAGRAMS
	MOVE	T3,.MHPKL(T1)	;GET PACKET LENGTH
	STOR	T3,MELEN,(T1)	;STORE IT
	PJRST	SCSQUE		;QUEUE UP BLOCK AND RETURN
	SUBTTL	INTERRUPT NOTIFICATION - PORT BROKE CONNECTION


;ROUTINE CALLED WHEN A VC TO A REMOTE NODE HAS BEEN BROKEN.
;CALL:
;	T1/ NOTIFICATION CODE
;	T2/ CONNECT ID
;	PUSHJ	P,SINPBC
;RETURN:
;	CPOPJ ALWAYS

SINPBC:	STKVAR	<CID>		;ALLOCATE A WORD OF STACK STORAGE
	MOVEM	T2,CID		;SAVE CONNECT ID
	MOVEI	T1,C%PBCL	;LENGTH OF BLOCK NEEDED
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,.SEPBC	;GET EVENT TYPE
	STOR	T2,EBCOD,(T1)	;STORE IN BLOCK
	MOVEI	T2,C%PBCL	;LENGTH OF BLOCK
	STOR	T2,EBLEN,(T1)	;STORE IN BLOCK
	MOVE	T2,CID		;GET CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE IN BLOCK
	MOVX	T2,.ETEVT	;GET BLOCK TYPE
	PJRST	SCSQUE		;QUEUE BLOCK AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	INTERRUPT NOTIFICATION - CONNECT TO LISTEN


;ROUTINE CALLED WHEN A CONNECTION WAS REQUESTED TO A LISTENER.
;CALL
;	T1/ .SSCTL
;	T2/ CONNECT ID
;	T3/ POINTER TO CONNECTION DATA

SINCTL:	STKVAR	<CID,CDATA>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T2,CID		;SAVE CONNECT ID
	MOVEM	T3,CDATA	;SAVE POINTER TO CONNECTION DATA
	MOVX	T1,C%CTLL	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,C%CTLL	;LENGTH
	STOR	T2,EBLEN,(T1)	;STORE IN BLOCK
	MOVX	T2,.SECTL	;EVENT CODE
	STOR	T2,EBCOD,(T1)	;STORE
	MOVE	T3,CDATA	;GET THE POINTER TO CONNECTION DATA
	MOVE	T4,T1		;MAKE THE BLOCK ADDRESS THE DESTINATION
	ADDI	T4,.EBDAT	;ADD THE OFFSET TO THE DATA PORTION
	MOVX	T2,C%DTLW	;LENGTH OF DATA IN WORDS
	EXTEND	T2,[XBLT]	;TRANSFER THE DATA
	MOVE	T2,CID		;GET THE CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVX	T2,.ETEVT	;BLOCK TYPE
	PJRST	SCSQUE		;QUEUE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	INTERRUPT NOTIFICATION - CONNECT RESPONSE AVAILABLE


;ROUTINE CALLED WHEN A CONNECT REPONSE HAS BECOME AVAILABLE.
;CALL:
;	T1/ .SSCRA
;	T2/ CONNECT ID
;	T3/ -1 IF ACCEPTED, 0 IF REJECTED
;	T4/ REJECT REASON OR POINTER TO CONNECTION DATA

SINCRA:	STKVAR	<CID,RJTADR>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T2,CID		;SAVE CONNECT ID
	MOVEM	T4,RJTADR	;SAVE REJECT REASON/DATA ADDRESS
	JUMPE	T3,CRA.01	;JUMP IF CONNECTION WAS REJECTED
	MOVX	T1,C%CRAL	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,C%CRAL	;LENGTH
	STOR	T2,EBLEN,(T1)	;STORE IN BLOCK
	MOVX	T2,.SECRA	;EVENT CODE
	STOR	T2,EBCOD,(T1)	;STORE
	MOVE	T2,CID		;CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVE	T3,RJTADR	;ADDRESS OF CONNECTION DATA
	XMOVEI	T4,.EBDAT(T1)	;DESTINATION ADDRESS
	MOVX	T2,C%DTLW	;LENGTH OF DATA IN WORDS
	EXTEND	T2,[XBLT]	;TRANSFER THE DATA
	MOVX	T2,.ETEVT	;BLOCK TYPE
	PJRST	SCSQUE		;QUEUE AND RETURN

;HERE IF CONNECTION REJECTED

CRA.01:	MOVX	T1,C%CRRL	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,C%CRRL	;LENGTH
	STOR	T2,EBLEN,(T1)	;STORE IN BLOCK
	MOVX	T2,.SECRR	;EVENT CODE
	STOR	T2,EBCOD,(T1)	;STORE
	MOVE	T2,CID		;CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVE	T2,RJTADR	;REJECT REASON
	MOVEM	T2,.EBDAT(T1)	;STORE
	MOVX	T2,.ETEVT	;BLOCK TYPE
	PJRST	SCSQUE		;QUEUE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	INTERRUPT NOTIFICATION - OK TO SEND DATA


;ROUTINE CALLED WHEN A CONNECTION IS FULLY OPEN AND IT IS OK
;TO SEND DATA.
;CALL:
;	T2/ CONNECT ID
;	PUSHJ	P,SINOSD
;RETURN:
;	CPOPJ ALWAYS

SINOSD:	STKVAR	<CID>		;ALLOCATE A WORD OF STACK STORAGE
	MOVEM	T2,CID		;SAVE THE CONNECT ID
	MOVX	T1,C%OSDL	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,C%OSDL	;LENGTH
	STOR	T2,EBLEN,(T1)	;STORE
	MOVX	T2,.SEOSD	;EVENT CODE
	STOR	T2,EBCOD,(T1)	;STORE
	MOVE	T2,CID		;CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVX	T2,.ETEVT	;BLOCK TYPE
	PJRST	SCSQUE		;QUEUE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	INTERRUPT NOTIFICATION - REMOTE INITIATED DISCONNECT


;ROUTINE CALLED WHEN THE REMOTE NODE HAS INITIATED A DISCONNECT.
;CALL:
;	T2/ CONNECT ID
;	PUSHJ	P,SINRID
;RETURN:	CPOPJ ALWAYS

SINRID:	STKVAR	<CID>		;ALLOCATE A WORD OF STACK STORAGE
	MOVEM	T2,CID		;SAVE CONNECT ID
	MOVX	T1,C%RIDL	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,C%RIDL	;LENGTH
	STOR	T2,EBLEN,(T1)	;STORE
	MOVX	T2,.SERID	;EVENT CODE
	STOR	T2,EBCOD,(T1)	;STORE
	MOVE	T2,CID		;CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVX	T2,.ETEVT	;BLOCK TYPE
	PJRST	SCSQUE		;QUEUE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	INTERRUPT NOTIFICATION - CREDIT IS AVAILABLE


;ROUTINE CALLED WHEN CREDIT BECOMES AVAILABLE.
;CALL:
;	T2/ CONNECT ID
;	T3/ SEND CREDIT
;	T4/ RECEIVE CREDIT
;	PUSHJ	P,SINCIA
;RETURN:
;	CPOPJ ALWAYS

SINCIA:	STKVAR	<CID,RCDT,SCDT>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T2,CID		;SAVE CONNECT ID
	MOVEM	T3,SCDT		; AND SEND CREDIT
	MOVEM	T4,RCDT		; AND RECEIVE CREDIT
	MOVX	T1,C%CIAL	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,C%CIAL	;LENGTH
	STOR	T2,EBLEN,(T1)	;STORE
	MOVX	T2,.SECIA	;EVENT CODE
	STOR	T2,EBCOD,(T1)	;STORE
	MOVE	T2,CID		;CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVE	T2,SCDT		;GET SEND CREDIT
	MOVEM	T2,.EBDAT(T1)	;STORE AS FIRST WORD OF DATA
	MOVE	T2,RCDT		;GET RECEIVE CREDIT
	MOVEM	T2,.EBDAT+1(T1)	;STORE AS SECOND WORD OF DATA
	MOVX	T2,.ETEVT	;BLOCK TYPE
	PJRST	SCSQUE		;QUEUE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	INTERRUPT NOTIFICATION - DMA TRANSFER COMPLETE


;ROUTINE CALLED WHEN A DMA TRANSFER COMPLETES.
;CALL:
;	T2/ CONNECT ID
;	T3/ BUFFER NAME
;	PUSHJ	P,SINDMA
;RETURN:
;	CPOPJ ALWAYS

SINDMA:	STKVAR	<CID,BUFNAM>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T2,CID		;SAVE CONNECT ID
	MOVEM	T3,BUFNAM	;SAVE BUFFER NAME
	MOVX	T1,.DMLEN	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,.DMLEN	;LENGTH
	STOR	T2,MELEN,(T1)	;STORE
	MOVE	T2,BUFNAM	;BUFFER NAME
	MOVEM	T2,.DMNAM(T1)	;STORE
	MOVE	T2,CID		;CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVX	T2,.ETDMA	;GET THE BLOCK TYPE
	PJRST	SCSQUE		;QUEUE UP BLOCK AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	INTERRUPT NOTIFICATION - PACKET SEND COMPLETE


;ROUTINE CALLED WHEN A PACKET SEND COMPLETES.
;CALL:
;	T2/ CONNECT ID
;	T3/ BUFFER ADDRESS
;	PUSHJ	P,SINPSC
;RETURN:
;	CPOPJ ALWAYS

SINPSC:	STKVAR	<CID,BUFADR>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T2,CID		;SAVE CONNECT ID
	MOVEM	T3,BUFADR	;SAVE THE BUFFER ADDRESS
	$LDCID	P1,T2		;GET CONNECTION BLOCK ADDRESS
	MOVX	T1,CB.RAP	;IS THIS AN ABORTED CONNECTION?
	TDNE	T1,.CBFLG(P1)	;...
	JRST	PSC.01		;YES
	MOVX	T1,C%MSCL	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,C%MSCL	;LENGTH
	STOR	T2,EBLEN,(T1)	;STORE
	MOVX	T2,.SEMSC	;EVENT CODE
	STOR	T2,EBCOD,(T1)	;STORE
	MOVE	T2,CID		;CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVE	T3,BUFADR	;GET BUFFER ADDRESS
	MOVE	T2,.JHAUB(T3)	;GET ADDRESS OF USER BUFFER
	MOVEM	T2,.EBDAT(T1)	;STORE AS FIRST DATA WORD
	MOVX	T2,.ETEVT	;BLOCK TYPE
	PUSHJ	P,SCSQUE	;QUEUE THE BLOCK

PSC.01:	MOVE	T1,BUFADR	;GET BUFFER ADDRESS
	PJRST	SCSRET		;RETURN THE BUFFER AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	INTERRUPT NOTIFICATION - LITTLE CREDIT LEFT


;ROUTINE CALLED WHEN CREDIT HAS DROPPED BELOW THE THRESHOLD.
;CALL:
;	T2/ CONNECT ID
;	T3/ NUMBER OF BUFFERS NEEDED
;	PUSHJ	P,SINLCL
;RETURN:
;	CPOPJ ALWAYS

SINLCL:	STKVAR	<CID,SHRTBF>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T2,CID		;SAVE CONNECT ID
	MOVEM	T3,SHRTBF	;SAVE NUMBER OF BUFFERS NEEDED
	MOVX	T1,C%LCLL	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;ASK FOR THE SPACE
	  POPJ	P,		;ERROR
	MOVX	T2,C%LCLL	;LENGTH
	STOR	T2,EBLEN,(T1)	;STORE
	MOVX	T2,.SELCL	;EVENT CODE
	STOR	T2,EBCOD,(T1)	;STORE
	MOVE	T2,CID		;CONNECT ID
	MOVEM	T2,.MECID(T1)	;STORE
	MOVE	T2,SHRTBF	;GET NUMBER OF BUFFERS NEEDED
	MOVEM	T2,.EBDAT(T1)	;STORE AS FIRST WORD OF DATA
	MOVX	T2,.ETEVT	;BLOCK TYPE
	PJRST	SCSQUE		;QUEUE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - QUEUE EVENT BLOCK

;ROUTINE TO QUEUE AN EVENT BLOCK TO THE INTERRUPT LEVEL QUEUE.
;CALL:
;	T1/ BLOCK ADDRESS
;	T2/ BLOCK TYPE
;	PUSHJ	P,SCSQUE
;RETURN:
;	CPOPJ ALWAYS
;AS SCSINT HAS SAVED P'S & Q'S NO SAVE WILL BE DONE HERE.

SCSQUE:	SETZM	.MEANC(T1)	;ZERO OLD LINK POINTERS
	SETZM	.MEAPC(T1)	;...
	SETZM	.MEANF(T1)	;...
	SETZM	.MEAPF(T1)	;...
	STOR	T2,METYP,(T1)	;STORE TYPE IN BLOCK

	$LDCID	P1,.MECID(T1)	;GET CB ADDRESS
	LOAD	T2,CBJCH,(P1)	;GET OWNING CONTEXT
	STOR	T2,MEJCH,(T1)	;STORE IN EVENT BLOCK

;WHILE A BLOCK IS ON THE INTERRUPT LEVEL QUEUE, THE .MEANC AND .MEAPC
;WORDS WILL BE USED TO LINK BLOCKS ON THE QUEUE.

	CIOFF			;PREVENT RACES
	SKIPN	SCSTIQ		;ANY ENTRIES ON THE QUEUE?
	JRST	QUE.01		;NO
	MOVE	T2,SCSBIQ	;YES, GET ADDRESS OF LAST ENTRY
	MOVEM	T2,.MEAPC(T1)	;UPDATE THE BACKWARD LINK IN ENTRY TO BE ADDED
QUE.01:	MOVEM	T1,@SCSBIQ	;LINK THIS ENTRY
	MOVEM	T1,SCSBIQ	;UPDATE BLINK
	PJRST	CIONPJ##	;TURN INTS BACK ON AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - CLOCK LEVEL PACKET DEQUEUEING

;ROUTINE CALLED FROM CLOCK LEVEL TO REMOVE PACKETS FROM THE INTERRUPT
;LEVEL PACKET QUEUE AND QUEUE THEM TO A JOB'S CONTEXT QUEUE.
;CALL:
;	PUSHJ	P,SCSCLK
;RETURN:
;	CPOPJ ALWAYS

SCSCLK::SKPCPU	(0)		;BOOT CPU?
	POPJ	P,		;NO, QUIT
	SE1ENT			;ENTER SECTION 1
	PUSHJ	P,SAVPQ##	;SAVE THE AC'S WE WILL BE USING
	STKVAR	<BLKADR,NXTENT> ;ALLOCATE SOME STACK STORAGE

CLK.01:	SKIPN	T1,SCSTIQ	;IS THERE AN ENTRY HERE?
	POPJ	P,		;NO, QUIT
	JRST	CLK.03		;CONTINUE

CLK.02:	SKIPN	T1,NXTENT	;A NEXT ENTRY?
	JRST	CLK.01		;NO, CHECK TOP OF QUEUE ONCE MORE
CLK.03:	MOVEM	T1,BLKADR	;SAVE ADDRESS OF CURRENT BLOCK
	MOVE	T2,.MEANC(T1)	;GET ADDRESS OF NEXT ENTRY
	MOVEM	T2,NXTENT	;SAVE
	LOAD	T1,MEJCH,(T1)	;GET JCH FOR THIS ENTRY
	PUSHJ	P,CTXVAL##	;VALIDATE IT
	 JRST	CLK.05		;NON-EXISTANT CONTEXT, DELETE THIS BLOCK
	  SKIPA			;CURRENT CONTEXT FOR JOB, SKIP
	JRST	CLK.02		;NOT CURRENT CONTEXT, CHECK NEXT
	ANDI	T1,JOBMSK##	;GET A JOB NUMBER
	MOVE	J,T1		;COPY HERE FOR CALL TO FNDPDB
	PUSHJ	P,FNDPDB##	;FIND PDB
	  JRST	CLK.02		;NO PDB (?), CHECK NEXT
	MOVE	P3,.PDSCS##(W)	;GET ADDRESS OF PROCESS QUEUE BLOCK

	MOVE	T1,BLKADR	;GET ADDRESS OF ENTRY
	$LDCID	P1,.MECID(T1)	;GET CB ADDRESS
	PUSHJ	P,SCSDEB	;DEQUEUE THE EVENT BLOCK
	LOAD	T2,METYP,(T1)	;GET EVENT TYPE
	SKIPLE	T2		;CHECK FOR INVALID TYPES
	CAILE	T2,EVTMAX	;...
	STOPCD	CLK.02,DEBUG,SCSBEB, ;++BAD EVENT BLOCK
	MOVE	T2,[EXP MSG,DG,EVT,XFER]-1(T2) ;GET ADDRESS OF LIST POINTER BLOCK
	MOVE	Q1,.TOPCQ(T2)	;POINTER TO CB QUEUE TOP POINTER
	XMOVEI	Q1,@Q1		;TURN INDEX INTO CB INTO A REAL ADDRESS
	MOVE	Q2,.BOTCQ(T2)	;POINTER TO CB QUEUE BOTTOM POINTER
	XMOVEI	Q2,@Q2		;TURN INDEX INTO CB INTO A REAL ADDRESS
	TXO	Q2,1B1		;SET THE GLOBAL INDIRECT BIT
	SETZM	.MEANC(T1)	;THERE IS NO NEXT BUFFER
	SETZM	.MEAPC(T1)	; AND FOR NOW ASSUME THERE IS NO PREVIOUS
	MOVE	T3,(Q2)		;GET THE CURRENT LAST ENTRY
	CAME	T3,Q1		;IS THIS A POINTER TO THE QUEUE HEADER POINTER?
	MOVEM	T3,.MEAPC(T1)	;NO, UPDATE THE PREVIOUS POINTER OF NEW ENTRY
	MOVEM	T1,@Q2		;LINK THIS ENTRY ONTO THE END OF THE QUEUE
	MOVEM	T1,(Q2)		;AND UPDATE THE TAIL POINTER
	MOVE	Q1,.TOPFQ(T2)	;POINTER TO CONTEXT QUEUE TOP POINTER
	XMOVEI	Q1,@Q1		;TURN INDEX INTO PQB INTO A REAL ADDRESS
	MOVE	Q2,.BOTFQ(T2)	;POINTER TO CONTEXT QUEUE BOTTOM POINTER
	XMOVEI	Q2,@Q2		;TURN INDEX INTO PQB INTO A REAL ADDRESS
	SETZM	.MEANF(T1)	;THERE IS NO NEXT BUFFER
	SETZM	.MEAPF(T1)	; AND FOR NOW ASSUME THERE IS NO PREVIOUS
	MOVE	T3,(Q2)		;GET ADDRESS OF CURRENT LAST ENTRY
	CAMN	T3,Q1		;IS THIS A POINTER TO THE FLINK?
	JRST	[MOVEM	T1,(Q1)	;YES, UPDATE THE CONTEXT QUEUE FLINK
		 JRST	CLK.04]	;CONTINUE
	MOVEM	T3,.MEAPF(T1)	;NO, UPDATE LINK TO PREVIOUS OF NEW ENTRY
	MOVEM	T1,.MEANF(T3)	;UPDATE NEXT FIELD OF PREVIOUS ENTRY
CLK.04:	MOVEM	T1,(Q2)		;UPDATE CONTEXT QUEUE BLINK
	SIGNAL	C$SCS		;SIGNAL SCS EVENT
	  JFCL			;USER NOT ENABLED?
	JRST	CLK.02		;LOOK FOR MORE BLOCKS

;HERE WHEN AN EVENT BLOCK WAS QUEUED TO A CONTEXT WHICH NO LONGER
;EXISTS.  DEQUEUE IT AND RETURN IT AS APPROPRIATE.

CLK.05:	MOVE	T1,BLKADR	;GET ADDRESS OF ENTRY
	PUSHJ	P,SCSDEB	;DEQUEUE THE EVENT BLOCK
	LOAD	T2,METYP,(T1)	;GET EVENT TYPE
	SKIPLE	T2		;CHECK FOR INVALID TYPES
	CAILE	T2,EVTMAX	;ONE WE KNOW OF?
	STOPCD	CLK.02,DEBUG,SCSUET, ;++UNKNOWN EVENT TYPE
	PUSHJ	P,@RETBLK-1(T2)	;CALL APPROPRIATE ROUTINE
	JRST	CLK.02		;TRY FOR ANOTHER ENTRY

RETBLK:	IFIW	SC.RBF##	;.ETMSG
	IFIW	SC.RLD##	;.ETDG
	IFIW	RELRES		;.ETEVT
	IFIW	RELRES		;.ETDMA
EVTMAX==.-RETBLK		;MAXIMUM EVENT TYPE

	ENDSV.			;END OF STACK VARIABLE RANGE
;ROUTINE TO DEQUEUE AN EVENT BLOCK FROM THE INTERRUPT LEVEL
;QUEUE.  CALLED ONLY BY SCSCLK.
;CALL:
;	T1/ ADDRESS OF ENTRY
;	PUSHJ	P,SCSDEB
;RETURN:
;	CPOPJ ALWAYS

SCSDEB:	CIOFF			;PREVENT RACES
	MOVE	T2,.MEANC(T1)	;GET ADDRESS OF NEXT ENTRY
	SKIPN	T3,.MEAPC(T1)	;GET ADDRESS OF PREVIOUS ENTRY
	SKIPE	T2		;ANYTHING LEFT ON QUEUE?
	JRST	DEB.01		;YES
	SETZM	SCSTIQ		;NO, RE-INITIALIZE THE QUEUE
	XMOVEI	T2,SCSTIQ	;INIT BLINK
	MOVEM	T2,SCSBIQ	; AS POINTER TO FLINK
	JRST	DEB.02		;CONTINUE

DEB.01:	SKIPE	T2		;IS THERE A NEXT ENTRY?
	MOVEM	T3,.MEAPC(T2)	;YES, UPDATE PREVIOUS ENTRY POINTER
	SKIPN	T2		;IS THERE A NEXT ENTRY?
	MOVEM	T3,SCSBIQ	;NO, UPDATE TAIL POINTER
	SKIPE	T3		;IS THERE A PREVIOUS ENTRY?
	MOVEM	T2,.MEANC(T3)	;YES, UPDATE NEXT POINTER OF PREVIOUS
	SKIPN	T3		;IS THERE A PREVIOUS ENTRY?
	MOVEM	T2,SCSTIQ	;NO, UPDATE HEAD POINTER
DEB.02:	CION			;ALLOW INTERRUPTS
	POPJ	P,		;RETURN
	SUBTTL	SUPPORT ROUTINES - JOB/CONTEXT KILLED

;ROUTINE CALLED WHEN A CONTEXT IS KILLED.  DELETE ANY INTERESTING STUFF.
;CALL:
;	J/ JOB NUMBER
;	PUSHJ	P,SCSLGO
;RETURN:
;	CPOPJ ALWAYS

SCSPOP::!			;UNTIL THIS NEEDS TO DO SOMETHING DIFFERENT
SCSRST::!			;DITTO
SCSLGO::PUSHJ	P,FNDPDS##	;SET UP PDB FOR JOB
	SE1ENT			;ENTER SECTION 1
	PUSHJ	P,SAVP##	;SAVE AC'S WE WILL SMASH
	SKIPN	P3,.PDSCS##(W)	;CONTEXT DOING SCS?
	POPJ	P,		;NO, THAT WAS EASY
	CIOFF			;PREVENT RACES

	SKIPE	P1,SCSTCQ(P3)	;ANYTHING CONNECTIONS FOR THIS CONTEXT?
	PUSHJ	P,KILCLP	;YES, LOOP TO KILL THEM

	PUSHJ	P,SCSCXN	;DELETE ANY DMA RESOURCES OWNED

	SETZM	.PDSCS##(W)	;CLEAR POINTER TO PQB
	CION			;OK TO INTERRUPT AGAIN

	MOVE	T1,P3		;COPY ADDRESS OF PQB
	PJRST	RELRES		;RETURN THE SPACE AND RETURN
;ROUTINE TO LOOP OVER ALL CONNECTIONS OWNED BY A CONTEXT AND
;DELETE (DISCONNECT) THEM.
;CALL:
;	P1/ POINTER TO FIRST CB
;	PUSHJ	P,KILCLP
;RETURN:
;	CPOPJ ALWAYS

KILCLP:	LOAD	T1,CBJCH,(P1)	;GET CONTEXT NUMBER OF CONNECT
	PUSHJ	P,CTXVAL##	;IS IT CURRENT?
	 JRST	KILCL1		;NON-EXISTANT?
	  SKIPA			;CURRENT
	JRST	KILCL1		;NON-CURRENT
	MOVX	T1,CB.KIL	;LIGHT THE BIT IN THE CB
	IORM	T1,.CBFLG(P1)	;...
	PUSHJ	P,SCSCBD	;DELETE JSYS CB DATA FROM THIS CB
	MOVE	T1,.CBSCI(P1)	;GET THE CONNECT ID
	MOVE	P1,.CBJNB(P1)	;GET NEXT CB FOR JOB/CONTEXT
	BLCAL.	(SC.DIS##,<T1,[0]>) ;ABORT THE CONNECTION
	  JFCL			;OH WELL
	JUMPN	P1,KILCLP	;LOOP IF ANOTHER CB
	JRST	KILCL2		;ALL DONE

KILCL1:	MOVE	P1,.CBJNB(P1)	;GET NEXT CB FOR JOB/CONTEXT
	JUMPN	P1,KILCLP	;LOOP IF ANOTHER CB
KILCL2:	XMOVEI	T1,SCSTCQ(P3)	;RE-INIT QUEUE HEADER
	MOVEM	T1,SCSBCQ(P3)	;...
	SETZM	SCSTCQ(P3)	;AS IF IT MATTERS
	POPJ	P,		;RETURN
	SUBTTL	SUPPORT ROUTINES - COUNT USER BUFFERS


;ROUTINE TO CHASE A BUFFER CHAIN AND RETURN A COUNT OF THE NUMBER
;OF BUFFERS IN THE CHAIN.  IT ALSO ADDRESS CHECKS THE BUFFERS.
;CALL:
;	T1/ STARTING BUFFER ADDRESS
;	T2/ SIZE OF BUFFER
;	PUSHJ	P,SCSCUB
;RETURN:
;	CPOPJ IF ADDRESS CHECK
;	CPOPJ1 IF VALID BUFFER CHAIN WITH:
;	T3/ NUMBER OF BUFFERS IN THE CHAIN

SCSCUB:	SETZ	T3,		;INITIALIZE THE COUNT
CUB.01:	PUSHJ	P,ARNGE##	;HOW NICE THAT THE ARGUMENTS ARE READY FOR A
				; CALL TO ARNGE!
	 POPJ	P,		;ADDRESS CHECK
	  POPJ	P,		;ILLEGAL FOR I/O
	AOS	T3		;COUNT THE BUFFER
	CAILE	T3,C%MXBF	;HAVE WE EXCEEDED THE MAXIMUM NUMBER OF BUFFERS?
	POPJ	P,		;YES, PRETEND LIKE ADDRESS CHECK FOR NOW
	EXCTUX	<SKIPE T1,(T1)>	;GET LINK TO NEXT BUFFER IN CHAIN
	JRST	CUB.01		;LOOP FOR OTHERS
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SUPPORT ROUTINES - LINK BUFFER CHAIN


;ROUTINE TO LINK A BUFFER CHAIN TO THE CONNECTION BLOCK.
;CALL:
;	T1/ ADDRESS OF FIRST BUFFER IN USER SPACE
;	T2/ OFFSET INTO BSD FOR LINKING THESE BUFFERS
;	P1/ ADDRESS OF CONNECT BLOCK
;	PUSHJ	P,SCSXUB
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSXUB:	SAVEAC	<Q1,Q2>		;FREE UP SOME AC'S
	DMOVE	Q1,T1		;COPY THE ARGUMENTS SOMEWHERE PERMANENT
XUB.01:	PUSHJ	P,SCSLUB	;LINK THIS BUFFER
	  POPJ	P,		;ERROR
	EXCTUX	<SKIPN Q1,(Q1)>	;GET LINK TO NEXT BUFFER IN CHAIN
	JRST	CPOPJ1##	;DONE, SKIP RETURN
	DMOVE	T1,Q1		;POINT AT CURRENT BUFFER
	JRST	XUB.01		;LOOP
;ROUTINE TO MOVE SOURCE AND DESTINATION PROCESS NAMES FROM USER
;MEMORY INTO SCRATCH MONITOR SPACE.  THIS ROUTINE EXPECTS A SPECIFIC
;ARGUMENT BLOCK FORMAT (COMMON TO .SSCON AND .SSLIS).
;CALL:
;	M/ ADDRESS OF USER ARGUMENTS
;	PUSHJ	P,SCSUNM
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS WITH:
;	T1/ ADDRESS OF SOURCE PROCESS NAME
;	T2/ ADDRESS OF DESTINATION PROCESS NAME (OR ZERO IF NONE)
;	T3/ ADDRESS OF FREE SPACE BLOCK USED
;	T4/ ADDRESS OF ROUTINE TO CALL TO RETURN FREE SPACE

SCSUNM:	SAVEAC	(Q1)		;FREE UP AN AC
	STKVAR	<SPN,DPN>	;ALLOCATE SOME STACK STORAGE
	SETZM	DPN		;INIT POINTER TO DESTINATION PROCESS NAME
	MOVEI	T1,C%PNLW*2	;AMOUNT OF SPACE WE NAME
	PUSHJ	P,ASGRES	;GET THE SPACE FOR THE EVENT BLOCK
	  POPJ	P,		;ERROR
	MOVE	Q1,T1		;SAVE ADDRESS FOR LATER
	UMOVE	T1,.SQSPN(M)	;GET ADDRESS OF SOURCE PROCESS NAME
	JUMPE	T1,UNMERR	;JUMP IF NO POINTER
	MOVEM	Q1,SPN		;SAVE ADDRESS OF SOURCE PROCESS NAME
	MOVX	T2,<POINT 8,(Q1)> ;BUILD BYTE POINTER WE CAN USE
	MOVX	T3,C%PNMN	;LENGTH OF THE STRING
	PUSHJ	P,SCSSUM	;MOVE THE STRING
	  JRST	UNMERR		;ERROR
	UMOVEM	T1,.SQSPN(M)	;UPDATE POINTER FOR LATER USE

	UMOVE	T1,.SQDPN(M)	;GET ADDRESS OF DESTINATION PROCESS NAME
	JUMPE	T1,UNM.02	;JUMP IF NOTHING SPECIFIED
	XMOVEI	T2,C%PNLW(Q1)	;ADDRESS OF DESTINATION NAME
	MOVEM	T2,DPN		;SAVE THE POINTER
	MOVX	T2,<POINT 8,C%PNLW(Q1)> ;BUILD BYTE POINTER WE CAN USE
	MOVX	T3,C%PNMN	;LENGTH OF THE STRING
	PUSHJ	P,SCSSUM	;MOVE THE STRING
	  JRST	UNMERR		;ERROR
	UMOVEM	T1,.SQDPN(M)	;UPDATE POINTER FOR LATER USE
UNM.02:	MOVE	T1,SPN		;RETURN ARGUMENTS AS PROMISED
	MOVE	T2,DPN		;...
	MOVE	T3,Q1		;ADDRESS OF FREE SPACE
	XMOVEI	T4,RELRES	;ROUTINE TO RETURN FREE SPACE
	JRST	CPOPJ1##	;SKIP RETURN

;HERE ON ERROR

UNMERR:	EXCH	T1,Q1		;SAVE ERROR IN Q1, GET ADDRESS IN T1
	PUSHJ	P,RELRES	;GIVE UP THE SPACE
	MOVE	T1,Q1		;GET THE ERROR CODE BACK
	POPJ	P,		;RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
;ROUTINE TO MOVE CONNECTION DATA FROM USER MEMORY INTO SCRATCH MONITOR
;SPACE.
;CALL:
;	T1/ BASE ADDRESS OF CONNECTION DATA IN USER SPACE
;	PUSHJ	P,SCSUDM
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS WITH:
;	T1/ MONITOR ADDRESS OF CONNECTION DATA
;	T2/ ADDRESS OF ROUTINE TO CALL TO RETURN FREE SPACE

SCSUDM:	STKVAR	<DATADR,USRDAT>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,USRDAT	;SAVE THE ADDRESS OF THE USER DATA
	MOVX	T1,C%DTLW	;LENGTH OF BLOCK
	PUSHJ	P,ASGRES	;GET THE SPACE
	  POPJ	P,		;ERROR
	MOVEM	T1,DATADR	;SAVE ADDRESS OF DATA
	MOVE	T3,T1		;MAKE THIS THE DESTINATION ADDRESS
	MOVX	T1,C%DTLW	;LENGTH IN WORDS
	MOVE	T2,USRDAT	;USER ADDRESS
	PUSHJ	P,SCSDUM	;MOVE THE DATA
	  JRST	UDMERR		;ERROR
	MOVE	T1,DATADR	;GET THE ADDRESS OF THE SPACE
	XMOVEI	T2,RELRES	;ROUTINE TO RETURN SPACE
	JRST	CPOPJ1##	;SKIP RETURN

;HERE ON ERROR

UDMERR:	EXCH	T1,DATADR	;SAVE ERROR CODE, GET FREE SPACE ADDRESS
	PUSHJ	P,RELRES	;FREE IT UP
	MOVE	T1,DATADR	;GET ERROR CODE BACK
	POPJ	P,		;RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - CONNECT BLOCK INITIALIZATION


;ROUTINE TO INITIALIZE THE CONNECT BLOCK WITH REQUIRED DATA.
;CALL:
;	P1/ ADDRESS OF BLOCK
;	PUSHJ	P,SCSCBI
;RETURN:
;	CPOPJ ALWAYS

SCSCBI:	MOVX	T1,CB.JSY	;INDICATE CB IS FOR JSYS CONNECT
	IORM	T1,.CBFLG(P1)	;...
	MOVE	T1,.CPJCH##	;JCH OF OWNING CONTEXT
	STOR	T1,CBJCH,(P1)	;STORE IN CB
	MOVE	T1,.CPJOB##	;JOB OF OWNING CONTEXT
	STOR	T1,CBJOB,(P1)	;STORE IN CB
	POPJ	P,		;RETURN
	SUBTTL	SUPPORT ROUTINES - LINK CB ONTO CONTEXT CB QUEUE


;ROUTINE TO LINK A CB ONTO THE CB QUEUE OF A CONTEXT.
;CALL:
;	P1/ CB ADDRESS
;	P3/ PQB ADDRESS
;	PUSHJ	P,SCSLCB
;RETURN:
;	CPOPJ ALWAYS

SCSLCB:	MOVE	T1,SCSBCQ(P3)	;GET ADDRESS OF LAST ENTRY ON THE QUEUE
	XMOVEI	T2,SCSTCQ(P3)	; AND THE ADDRESS OF THE TOP OF QUEUE POINTER
	CAMN	T1,T2		;IS THE QUEUE EMPTY?
	JRST	LCB.01		;YES
	MOVEM	T1,.CBJPB(P1)	;NO, UPDATE PREVIOUS POINTER OF NEW CB
	ADDI	T1,.CBJNB	; AND OFFSET TO THE FORWARD POINTER OF LAST
LCB.01:	MOVEM	P1,(T1)		;LINK NEW BUFFER ONTO THE CONTEXT CB QUEUE
	MOVEM	P1,SCSBCQ(P3)	; AND UPDATE THE QUEUE BLINK
	POPJ	P,		;RETURN
	SUBTTL	SUPPORT ROUTINES - DELETE JSYS CB DATA


;ROUTINE TO DELETE JSYS DATA FROM A CONNECTION BLOCK.  BUFFERS
;ON THE PORT QUEUES WILL BE RETURNED BY SC.RAP.
;CALL:
;	P1/ CB ADDRESS
;	PUSHJ	P,SCSCBD
;RETURN:
;	CPOPJ ALWAYS

SCSCBD:	XMOVEI	T1,.CBTMQ(P1)	;POINT TO TOP OF THE QUEUE
	XMOVEI	T2,.CBBMQ(P1)	; AND THE BOTTOM
	XMOVEI	T3,SC.RBF##	; WHO TO CALL TO RETURN BUFFERS
	XMOVEI	T4,MSG		; AND THE 4-WORD BLOCK
	PUSHJ	P,SCSGPR	;RETURN THE PACKETS

	XMOVEI	T1,.CBTDQ(P1)	;POINT TO TOP OF THE QUEUE
	XMOVEI	T2,.CBBDQ(P1)	; AND THE BOTTOM
	XMOVEI	T3,SC.RLD##	; WHO TO CALL TO RETURN BUFFERS
	XMOVEI	T4,DG		; AND THE 4-WORD BLOCK
	PUSHJ	P,SCSGPR	;RETURN THE PACKETS

	XMOVEI	T1,.CBTXQ(P1)	;POINT TO TOP OF THE QUEUE
	XMOVEI	T2,.CBBXQ(P1)	; AND THE BOTTOM
	XMOVEI	T3,RELRES	; WHO TO CALL TO RETURN BUFFERS
	XMOVEI	T4,XFER		; AND THE 4-WORD BLOCK
	PUSHJ	P,SCSGPR	;RETURN THE PACKETS

	XMOVEI	T1,.CBTEQ(P1)	;POINT TO TOP OF THE QUEUE
	XMOVEI	T2,.CBBEQ(P1)	; AND THE BOTTOM
	XMOVEI	T3,RELRES	; WHO TO CALL TO RETURN BUFFERS
	XMOVEI	T4,EVT		; AND THE 4-WORD BLOCK
	PUSHJ	P,SCSGPR	;RETURN THE PACKETS

	XMOVEI	T1,.CBTBQ(P1)	;POINT TO TOP OF THE QUEUE
	XMOVEI	T2,.CBBBQ(P1)	; AND THE BOTTOM
	PUSHJ	P,SCSCBS	;CLEAN UP ANY BSDS THAT ARE AROUND

	PUSHJ	P,SCSRCB	;RETURN CB FROM OWNING CONTEXT CB QUEUE
	  JFCL			;ERROR

	MOVX	T1,CB.JSY	;TURN OFF THE JSYS FLAG BIT
	ANDCAB	T1,.CBFLG(P1)	;...
	TXNN	T1,CB.PTC	;IS PROTOCOL COMPLETE?
	POPJ	P,		;NO, RETURN
	MOVX	T1,CB.RAP	;YES, SET THE REAP FLAG
	IORM	T1,.CBFLG(P1)	;...
	SETOM	SCAREP##	;TELL SCASER TO RUN THE REAPER
	POPJ	P,		;AND RETURN
	SUBTTL	SUPPORT ROUTINES - LINK PACKET TO FRONT OF QUEUE


;THIS ROUTINE LINKS A MONITOR PACKET ONTO THE FRONT OF THE CONTEXT
;AND CB QUEUES.
;CALL:
;	T1/ ADDRESS OF PACKET
;	T2/ ADDRESS OF 4-WORD LIST HEADER
;	P1/ CB ADDRESS
;	PUSHJ	P,SCSLUB
;RETURN:
;	CPOPJ ALWAYS

SCSLFQ:	MOVE	T3,@.TOPFQ(T2)	;GET THE ADDRESS OF THE FIRST ON CONTEXT QUEUE
	MOVEM	T3,.MEANF(T1)	;LINK NEW BUFFER AT THE FRONT
	SETZM	.MEAPF(T1)	;MAKE BACKWARD LINK BE ZERO
	MOVEM	T1,@.TOPFQ(T2)	;UPDATE THE QUEUE FLINK
	MOVE	T3,@.TOPCQ(T2)	;GET ADDRESS OF FIRST BUFFER ON CB QUEUE
	MOVEM	T3,.MEANC(T1)	;LINK NEW BUFFER AT THE FRONT
	SETZM	.MEAPC(T1)	;MAKE BACKWARD LINK BE ZERO
	MOVEM	T1,@.TOPCQ(T2)	;UPDATE THE QUEUE FLINK
	POPJ	P,		;RETURN
	SUBTTL	SUPPORT ROUTINES - REMOVE CB FROM OWNING CONTEXT CB QUEUE


;ROUTINE TO REMOVE A CB FROM THE OWNING CONTEXT'S LIST OF CB'S.
;CALL:
;	P1/ ADDRESS OF CONNECT BLOCK
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSRCB:	LOAD	T1,CBJCH,(P1)	;GET OWNING JCH
	CAME	T1,.CPJCH##	;IS IT FOR THE CURRENT JOB/CONTEXT?
	POPJ	P,		;NO

	XMOVEI	T3,SCSTCQ(P3)	;GET ADDRESS OF QUEUE FLINK
	XMOVEI	T4,SCSBCQ(P3)	;GET ADDRESS OF QUEUE BLINK
	MOVE	T1,.CBJNB(P1)	;GET FORWARD POINTER FROM CB
	SKIPN	T2,.CBJPB(P1)	;IS THERE A PREVIOUS POINTER?
	SKIPE	T1		;OR A FORWARD POINTER?
	JRST	RCB.01		;YES
	XMOVEI	T1,SCSTCQ(P3)	;GET ADDRESS OF QUEUE FLINK
	MOVEM	T1,(T4)		;REINIT BLINK AS POINTER TO FLINK
	SETZM	(T3)		;REINIT FLINK AS ZERO
	JRST	CPOPJ1##	;SKIP RETURN

RCB.01:	SKIPN	T1		;IS THERE A NEXT CB?
	MOVEM	T2,(T4)		;NO, UPDATE QUEUE BLINK WITH POINTER TO PREVIOUS
	SKIPE	T1		;IS THERE A NEXT CB?
	MOVEM	T2,.CBJPB(T1)	;YES, UPDATE ITS PREVIOUS POINTER
	SKIPN	T2		;IS THERE A PREVIOUS CB?
	MOVEM	T1,(T3)		;NO, UPDATE QUEUE FLINK
	SKIPE	T2		;IS THERE A PREVIOUS CB?
	MOVEM	T1,.CBJNB(T2)	;YES, UPDATE ITS NEXT POINTER
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL 	SUPPORT ROUTINES - ADD DMA BUFFER NAME


;ROUTINE TO ADD A BUFFER NAME TO THE DMA NAME DATABASE.
;CALL:
;	T1/ BUFFER NAME
;	P3/ PQB ADDRESS
;	PUSHJ	P,SCSAXN
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSAXN:	STKVAR	<BUFNAM>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,BUFNAM	;SAVE BUFFER NAME
	MOVX	T1,.XNLEN	;LENGTH OF THE BLOCK
	PUSHJ	P,ASGRES	;GET THE SPACE
	  POPJ	P,		;ERROR
	MOVE	T2,BUFNAM	;GET BUFFER NAME
	MOVEM	T2,.XNNAM(T1)	;SAVE BUFFER NAME
	AOS	(P)		;SET FOR SKIP RETURN
	PJRST	SCSLXN		;LINK ENTRY ONTO QUEUE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - DELETE DMA BUFFER NAME


;ROUTINE TO DELETE A BUFFER NAME FROM THE DMA NAME DATABASE.
;CALL:
;	T1/ BUFFER NAME
;	P3/ PQB ADDRESS
;	PUSHJ	P,SCSDXN
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSDXN:	STKVAR	<BUFNAM>	;ALLOCATE A WORD OF STACK STORAGE
	MOVEM	T1,BUFNAM	;SAVE BUFFER NAME FOR A BIT
	XMOVEI	T1,SCSTXN(P3)	;GET ADDRESS OF QUEUE FLINK
DXN.01:	SKIPN	T1,.XNNXT(T1)	;IS THERE ANOTHER ENTRY ON THE QUEUE?
	POPJ	P,		;NO, INVALID BUFFER NAME
	MOVE	T2,.XNNAM(T1)	;GET THE BUFFER NAME FROM THE ENTRY
	CAME	T2,BUFNAM	;DOES IT MATCH?
	JRST	DXN.01		;NO, LOOP
	AOS	(P)		;SET FOR SKIP RETURN
	PJRST	SCSRXN		;REMOVE THE ENTRY FROM THE QUEUE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - FIND BUFFER NAME


;ROUTINE TO DETERMINE IF A BUFFER NAME IS CURRENTLY OWNED.
;CALL:
;	T1/ BUFFER NAME
;	P3/ PQB ADDRESS
;	PUSHJ	P,SCSFXN
;RETURN:
;	CPOPJ IF BUFFER NOT OWNED
;	CPOPJ1 IF BUFFER OWNED

SCSFXN:	STKVAR	<BUFNAM>	;ALLOCATE A WORD OF STACK STORAGE
	MOVEM	T1,BUFNAM	;SAVE BUFFER NAME
	XMOVEI	T1,SCSTXN(P3)	;GET ADDRESS OF QUEUE FLINK
FXN.01:	SKIPN	T1,.XNNXT(T1)	;IS THERE A NEXT ENTRY?
	POPJ	P,		;NO, BUFFER NAME NOT OWNED
	MOVE	T2,.XNNAM(T1)	;GET BUFFER NAME
	CAME	T2,BUFNAM	;DOES IT MATCH?
	JRST	FXN.01		;NO, LOOP
	JRST	CPOPJ1##	;YES, SKIP RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - CLEAN A BUFFER NAME LIST


;ROUTINE TO CLEAN THE BUFFER NAME LIST.
;CALL:
;	P3/ PQB ADDRESS
;	PUSHJ	P,SCSCXN
;RETURN:
;	CPOPJ ALWAYS

SCSCXN:	PUSHJ	P,SAVE1##	;SAVE P1
	XMOVEI	P1,SCSTXN(P3)	;GET ADDRESS OF QUEUE FLINK
	MOVE	P1,.XNNXT(P1)	;GET ADDRESS OF FIRST ENTRY

CXN.01:	JUMPE	P1,CPOPJ##	;RETURN AT END
	MOVE	T1,.XNNXT(P1)	;GET ADDRESS OF NEXT ENTRY
	EXCH	T1,P1		;SAVE NEXT IN P1, GET CURRENT IN T1
	PUSHJ	P,SCSRXN	;RETURN THE ENTRY
	JRST	CXN.01		;LOOP FOR OTHERS
	SUBTTL	SUPPORT ROUTINES - LINK ENTRY ONTO DMA DATABASE


;ROUTINE TO ADD AN ENTRY TO A DMA BUFFER NAME LIST.
;CALL:
;	T1/ ENTRY ADDRESS
;	P3/ PQB ADDRESS
;	PUSHJ	P,SCSLXN
;RETURN:
;	CPOPJ ALWAYS

SCSLXN:	SKIPE	SCSTXN(P3)	;IS THERE ANYTHING ON THE LIST?
	JRST	LXN.01		;YES
	MOVEM	T1,SCSTXN(P3)	;SET UP THE FLINK
	MOVEM	T1,SCSBXN(P3)	;SET UP THE BLINK
	SETZM	.XNNXT(T1)	;CLEAR LINKS
	SETZM	.XNPRV(T1)	;...
	POPJ	P,		;RETURN

LXN.01:	MOVE	T4,SCSBXN(P3)	;GET ADDRESS OF LAST ENTRY
	MOVEM	T1,.XNNXT(T4)	;LINK THIS TO LAST
	MOVEM	T1,SCSBXN(P3)	;SET UP BLINK
	SETZM	.XNNXT(T1)	;CLEAR FLINK
	MOVEM	T4,.XNPRV(T1)	;SET UP BLINK
	POPJ	P,		;RETURN
	SUBTTL	SUPPORT ROUTINES - REMOVE ENTRY FROM DMA DATABASE


;ROUTINE TO REMOVE AN ENTRY FROM A DMA NAME LIST.
;CALL:
;	T1/ ENTRY ADDRESS
;	P3/ PQB ADDRESS
;	PUSHJ	P,SCSRXN
;RETURN:
;	CPOPJ ALWAYS

SCSRXN:	PUSHJ	P,SAVE1##	;SAVE P1
	STKVAR	<ENTADR>	;ALLOCATE A WORD OF STACK STORAGE
	MOVEM	T1,ENTADR	;SAVE ADDRESS OF ENTRY
	MOVE	T1,.XNNAM(T1)	;GET BUFFER NAME
	BLCAL.	(SC.UMP##,<T1>)	;TELL SCASER TO UNMAP THIS BUFFER
	  JFCL			;ERROR RETURN, STILL REMOVE FROM DATABASE
	XMOVEI	T1,SCSTXN(P3)	;GET ADDRESS OF QUEUE FLINK
	XMOVEI	T2,SCSBXN(P3)	;GET ADDRESS OF QUEUE BLINK
	MOVE	P1,ENTADR	;GET ADDRESS OF ENTRY BEING REMOVED
	MOVE	T3,.XNNXT(P1)	;GET THE FORWARD LINK
	MOVE	T4,.XNPRV(P1)	;GET THE BACKWARD LINK
	SKIPN	T3		;IS THERE A FORWARD LINK?
	SKIPE	T4		;AND NO BACKWARD LINK?
	JRST	RXN.01		;OTHER ENTRIES ON THE QUEUE
	SETZM	(T1)		;LAST ENTRY ON THE QUEUE, ZERO FLINK
	MOVEM	T1,(T2)		;INIT BLINK AS POINTER TO FLINK
	MOVE	T1,P1		;GET THE ENTRY ADDRESS
	PJRST	RELRES		;RETURN THE SPACE AND RETURN

RXN.01:	SKIPN	T3		;IS THERE A FORWARD LINK?
	MOVEM	T4,(T2)		;NO, LAST ENTRY, UPDATE QUEUE BLINK
	SKIPE	T3		;IS THERE A FORWARD LINK?
	MOVEM	T4,.XNPRV(T3)	;YES, UPDATE BLINK OF NEXT ENTRY
	SKIPN	T4		;IS THERE A PREVIOUS ENTRY?
	MOVEM	T3,(T1)		;NO, LAST ENTRY, UPDATE QUEUE FLINK
	SKIPE	T4		;IS THERE A PREVIOUS ENTRY?
	MOVEM	T3,.XNNXT(T4)	;YES, UPDATE ENTRY FLINK
	MOVE	T1,P1		;GET ENTRY ADDRESS
	PJRST	RELRES		;RETURN THE SPACE AND RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - GENERAL PACKET RETURN


;ROUTINE TO RETURN A SET OF PACKETS/BLOCKS LINKED WITH A STANDARD
;QUEUE HEADER.  THE HEADER IS THE ONE USED BY THE SCS. UUO TO QUEUE
;EVERYTHING IT HANGS OFF THE CONNECT BLOCK.
;CALL:
;	T1/ QUEUE FLINK
;	T2/ QUEUE BLINK
;	T3/ ADDRESS OF ROUTINE TO RETURN BLOCK IN T1
;	T4/ ADDRESS OF 4-WORD LIST HEADER BLOCK (IF ONE)
;	P1/ CB ADDRESS
;	PUSHJ	P,SCSGPR
;RETURN:
;	CPOPJ ALWAYS

SCSGPR:	SAVEAC	<Q1>		;FREE UP AN AC
	STKVAR	<RTNADR,BLKADR>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T3,RTNADR	;SAVE THE RETURN ROUTINE ADDRESS
	MOVEM	T4,BLKADR	;AND THE 4-WORD BLOCK ADDRESS

	SKIPN	Q1,(T1)		;ANYTHING AT ALL ON THE QUEUE?
	POPJ	P,		;NO, RETURN
GPR.01:	MOVE	T1,Q1		;GET THE ADDRESS OF THE CURRENT ENTRY
	MOVE	Q1,(Q1)		;GET THE ADDRESS OF THE NEXT ENTRY
	SKIPE	T2,BLKADR	;IS THERE A 4-WORD BLOCK ADDRESS?
	PUSHJ	P,SCSDEQ	;YES, DEQUEUE THE PACKET/BLOCK
	PUSHJ	P,@RTNADR	;RETURN THE FREE SPACE
	JUMPN	Q1,GPR.01	;JUMP IF MORE TO DO
	POPJ	P,		;RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - CLEAN UP A BSD QUEUE


;ROUTINE TO CLEAN UP THE ENTRIES ON A BSD QUEUE.  NOTE THAT THIS
;ROUTINE SHOULD BE MERGED WITH A GENERAL QUEUE CLEANING ROUTINE.
;CALL:
;	P1/ CB ADDRESS
;	PUSHJ	P,SCSCBS
;RETURN:
;	CPOPJ ALWAYS

SCSCBS:	SAVEAC	<Q1>		;FREE UP AN AC
	SKIPN	T1,.CBTBQ(P1)	;IS THERE SOMETHING HERE?
	POPJ	P,		;NO, RETURN
CBS.01:	MOVE	Q1,.BDNXT(T1)	;GET THE ADDRESS OF THE NEXT ENTRY
	PUSHJ	P,SC.RLD##	;RETURN THE DATAGRAM
	SKIPE	T1,Q1		;IS THERE ANOTHER BLOCK TO RETURN?
	JRST	CBS.01		;YES

	XMOVEI	T1,.CBTBQ(P1)	;GET A POINTER TO TOP OF QUEUE
	MOVEM	T1,.CBBBQ(P1)	;INIT BLINK AS POINTER TO FLINK
	SETZM	.CBTBQ(P1)	;INIT FLINK AS ZERO
	POPJ	P,		;RETURN
	SUBTTL	SUPPORT ROUTINES - MOVE DATA FROM MONITOR TO USER


;ROUTINE TO MOVE A BLOCK OF WORDS FROM MONITOR SPACE INTO USER SPACE.
;CALL:
;	T1/ LENGTH IN WORDS OF BLOCK
;	T2/ ADDRESS OF SOURCE BLOCK IN EXEC SPACE
;	T3/ ADDRESS OF DESTINATION BLOCK IN USER SPACE
;	PUSHJ	P,SCSDMU
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSDMU:	EXCH	T2,T3		;SWAP ARGUMENTS FOR CALL TO ARNGE
	EXCH	T1,T2		;...
	PUSHJ	P,ARNGE##	;SEE IF LEGAL ADDRESSES
	 POPJ	P,		;ADDRESS CHECK
	  POPJ	P,		;ILLEGAL FOR I/O
	EXCH	T1,T2		;PUT ARGUMENTS BACK AS THEY WERE
	EXCH	T2,T3		;...
	XBLTXU	T1		;XBLT THE DATA OVER
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SUPPORT ROUTINES - MOVE DATA FROM USER TO MONITOR


;ROUTINE TO MOVE A BLOCK OF WORDS FROM USER SPACE INTO MONITOR SPACE.
;CALL:
;	T1/ LENGTH IN WORDS OF BLOCK
;	T2/ ADDRESS OF SOURCE BLOCK IN USER SPACE
;	T3/ ADDRESS OF DESTINATION BLOCK IN EXEC SPACE
;	PUSHJ	P,SCSDUM
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSDUM:	EXCH	T1,T2		;SWAP ARGUMENTS FOR CALL TO ARNGE
	PUSHJ	P,ARNGE##	;ADDRESS CHECK IT
	 POPJ	P,		;ADDRESS CHECK
	  POPJ	P,		;ILLEGAL FOR I/O
	EXCH	T1,T2		;PUT ARGUMENTS BACK THE WAY THEY SHOULD BE
	XBLTUX	T1		;XBLT THE DATA OVER
	JRST	CPOPJ1##	;SKIP RETURN
;ROUTINE TO MOVE A STRING FROM USER SPACE TO MONITOR SPACE.
;CALL:
;	T1/ BYTE POINTER TO SOURCE STRING
;	T2/ BYTE POINTER TO DESTINATION STRING
;	T3/ MAXIMUM LENGTH OF THE STRING
;	PUSHJ	P,SCSSUM
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSSUM:	PUSHJ	P,SAVE1##	;SAVE AN AC
	MOVE	P1,T3		;SAVE LENGTH OF STRING
	TLC	T1,-1		;SEE IF GENERIC BYTE POINTER
	TLCE	T1,-1		;...
	JRST	SUM.01		;NO, USE WHAT USER SUPPLIED
	HLL	T1,M		;MAKE A SECTION LOCAL
	TLO	T1,610000	; ONE-WORD GLOBAL BYTE POINTER
SUM.01:	EXCTUX	<ILDB T4,T1>	;GET A BYTE FROM THE USER STRING
	ERJMP	CPOPJ##		;HANDLE ADDRESS CHECK
	JUMPE	T4,SUM.02	;JUMP IF NULL BYTE (SEE IF PADDING NEEDED)
	IDPB	T4,T2		;STORE BYTE IN MONITOR SPACE
	JUMPE	P1,SUM.01	;IF NO MAXIMUM COUNT LOOP UNTIL NULL
	SOJG	T3,SUM.01	;HAVE A BYTE COUNT, LOOP IF MORE ALLOWED
	JRST	CPOPJ1##	;SKIP RETURN

SUM.02:	JUMPE	P1,CPOPJ1##	;IF NO MAXIMUM COUNT NO PADDING REQUIRED
	JUMPE	T3,CPOPJ1##	;RETURN IF NO PADDING REQUIRED
	MOVEI	T4," "		;GET A SPACE
	IDPB	T4,T2		;STORE BYTE IN MONITOR SPACE
	SOJG	T3,.-1		;LOOP FOR NECESSARY PAD CHARACTERS
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SUPPORT ROUTINES - DEQUEUE A BUFFER FROM QUEUES


;ROUTINE TO DEQUEUE A BUFFER FROM THE QUEUES IT IS ON.
;CALL:
;	T1/ ADDRESS OF PACKET
;	T2/ ADDRESS OF 4-WORD LIST HEADER BLOCK
;	P1/ ADDRESS OF CB (OR -1 IF NO CB ENTRY)
;	PUSHJ	P,SCSDEQ
;	CPOPJ ALWAYS WITH:
;	T1/ ADDRESS OF PACKET

SCSDEQ:	PUSHJ	P,SAVQ##	;FREE UP SOME AC'S TO USE
	JUMPL	P1,DEQ.03	;IF A CONTEXT ONLY ENTRY, SKIP CB STUFF
	MOVE	T3,.MEANC(T1)	;GET NEXT CONNECTION ENTRY
	SKIPN	T4,.MEAPC(T1)	;IS THERE A PREVIOUS ENTRY?
	SKIPE	T3		;OR A NEXT?
	JRST	DEQ.01		;YES
	XMOVEI	T3,@.TOPCQ(T2)	;GET THE ADDRESS OF THE TOP OF THE QUEUE
	MOVEM	T3,@.BOTCQ(T2)	;INIT TAIL POINTER AS POINTER TO HEAD
	SETZM	@.TOPCQ(T2)	;INIT HEAD POINTER AS ZERO
	JRST	DEQ.02		;PROCEED

DEQ.01:	SKIPE	T3		;IS THERE A NEXT ENTRY?
	MOVEM	T4,.MEAPC(T3)	;YES, UPDATE PREVIOUS ENTRY POINTER
	SKIPN	T3		;IS THERE A NEXT ENTRY?
	MOVEM	T4,@.BOTCQ(T2)	;NO, UPDATE THE TAIL POINTER
	SKIPE	T4		;IS THERE A PREVIOUS ENTRY?
	MOVEM	T3,.MEANC(T4)	;YES, UPDATE NEXT POINTER OF PREVIOUS
	SKIPN	T4		;IS THERE A PREVIOUS ENTRY?
	MOVEM	T3,@.TOPCQ(T2)	;NO, UPDATE THE HEAD POINTER

DEQ.02:	MOVX	T3,CB.KIL	;SEE IF CONTEXT WITH CONNECT IS DEAD
	TDNE	T3,.CBFLG(P1)	;IS THE CONTEXT ALREADY GONE?
	POPJ	P,		;YES, RETURN
DEQ.03:	MOVE	Q1,.TOPFQ(T2)	;GET ADDRESS OF THE CONTEXT QUEUE TOP
	MOVE	Q2,.BOTFQ(T2)	;GET ADDRESS OF THE CONTEXT QUEUE BOTTOM
	MOVE	T3,.MEANF(T1)	;GET THE NEXT ENTRY
	SKIPN	T4,.MEAPF(T1)	;IS THERE A PREVIOUS ENTRY?
	SKIPE	T3		; AND NO NEXT?
	JRST	DEQ.04		;NO
	XMOVEI	T3,@Q1		;GET THE ADDRESS OF THE TOP OF THE QUEUE
	MOVEM	T3,@Q2		;INIT TAIL POINTER AS POINTER TO HEAD
	SETZM	@Q1		;INIT HEAD POINTER AS ZERO
	POPJ	P,		;ALL DONE
DEQ.04:	SKIPE	T3		;IS THERE A NEXT ENTRY?
	MOVEM	T4,.MEAPF(T3)	;YES, UPDATE PREVIOUS ENTRY IN NEXT
	SKIPN	T3		;IS THERE A NEXT ENTRY?
	MOVEM	T4,@Q2		;NO, UPDATE THE TAIL POINTER IN THE PQB
	SKIPE	T4		;IS THERE A PREVIOUS ENTRY?
	MOVEM	T3,.MEANF(T4)	;YES, UPDATE NEXT POINTER OF PREVIOUS
	SKIPN	T4		;IS THERE A PREVIOUS ENTRY?
	MOVEM	T3,@Q1		;NO, UPDATE THE HEADER POINTER IN THE PQB
	POPJ	P,		;RETURN
	SUBTTL	SUPPORT ROUTINES - MAKE A BSD


;THIS ROUTINE CREATES A BSD AND LINKS ONTO THE CB BSD LIST.
;CALL:
;	P1/ ADDRESS OF CONNECTION BLOCK
;	PUSHJ	P,SCSMBS
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS WITH:
;	T1/ ADDRESS OF NEW BSD
;	P1/ ADDRESS OF CONNECTION BLOCK

SCSMBS:	MOVEI	T1,1		;ASK FOR A DATAGRAM BUFFER
	PUSHJ	P,SC.ALD##	;...
	  POPJ	P,		;NOT AVAILABLE
	XMOVEI	T2,.BDFMG(T1)	;ADDRESS OF FIRST MESSAGE POINTER
	MOVEM	T2,.BDLMG(T1)	;INIT MESSAGE TAIL AS POINTER TO HEAD
	SETZM	.BDFMG(T1)	;ZERO THE MESSAGE ENTRY HEAD
	XMOVEI	T2,.BDFDG(T1)	;ADDRESS OF FIRST DATAGRAM POINTER
	MOVEM	T2,.BDLDG(T1)	;INIT DATAGRAM TAIL AS POINTER TO HEAD
	SETZM	.BDFDG(T1)	;ZERO THE DATAGRAM ENTRY HEAD
	MOVX	T4,C%NBSD	;GET THE NUMBER OF ENTRIES IN A BSD
	MOVE	T2,T1		;COPY BSD ADDRESS
	ADDI	T2,.BDBDB	;OFFSET TO FIRST SLOT ADDRESS
	MOVEM	T2,.BDFFD(T1)	;STORE AS ADDRESS OF FIRST FREE ENTRY
	MOVE	T3,T2		;DON'T SMASH THE POINTER TO LAST ENTRY
MBS.01:	ADDI	T3,.BBLEN	;ADD THE OFFSET TO THE NEXT ENTRY
	MOVEM	T3,(T2)		;LINK ON THIS NEXT ENTRY
	MOVE	T2,T3		;MOVE TO THE NEXT BUFFER
	SOJG	T4,MBS.01	;LOOP FOR THE ENTIRE BSD
	MOVEM	T3,.BDLFD(T1)	;STORE THE LIST TAIL POINTER
	SETZM	(T3)		;ENSURE A NULL LAST ENTRY POINTER
	MOVEM	T1,@.CBBBQ(P1)	;LINK THIS NEW BSD ONTO THE END OF THE QUEUE
	MOVEM	T1,.CBBBQ(P1)	;AND UPDATE THE TAIL POINTER
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SUPPORT ROUTINES - LINK ON USER BUFFER


;ROTINE TO LINK A USER BUFFER ADDRESS INTO A BSD FOR THE CURRENT
;CONNECTION.
;CALL:
;	T1/ USER ADDRESS OF BUFFER
;	T2/ OFFSET IN BSD TO DESIRED FLINK
;	P1/ CB ADDRESS
;	PUSHJ	P,SCSLUB
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSLUB:	STKVAR	<BUFADR,BSDOFS>	;ALLOCATE SOME STACK STORAGE
	MOVEM	T1,BUFADR	;SAVE USER BUFFER ADDRESS
	MOVEM	T2,BSDOFS	;AND THE BSD OFFSET
	SKIPE	T3,.CBTBQ(P1)	;ARE THERE ANY BSDS ON THIS CONNECT?
	JRST	LUB.02		;YES, DON'T NEED TO CREATE ONE
LUB.01:	PUSHJ	P,SCSMBS	;CREATE A BSD
	  POPJ	P,		;ERROR
	MOVE	T3,T1		;PUT BSD ADDRESS WHERE EXPECTED
	MOVE	T2,BSDOFS	;RESTORE THE BSD OFFSET
	MOVE	T1,BUFADR	; AND THE USER BUFFER ADDRESS
LUB.02:	SKIPE	T4,.BDFFD(T3)	;IS THERE A FREE ENTRY IN THE BSD?
	JRST	LUB.03		;YES
	SKIPE	T3,.BDNXT(T3)	;IS THERE ANOTHER BSD ON THE CHAIN?
	JRST	LUB.02		;YES, SEE IF IT HAS A FREE ENTRY
	JRST	LUB.01		;NO, CREATE A NEW BSD

LUB.03:	ADD	T2,T3		;CALCULATE ADDRESS OF QUEUE FLINK
	MOVEM	T1,.BBUVA(T4)	;STORE USER ADDRESS IN BSD ENTRY
	MOVE	T1,.BBNXT(T4)	;GET ADDRESS OF NEXT BUFFER
	MOVEM	T1,.BDFFD(T3)	;DELETE THIS ENTRY FROM THE FREE QUEUE
	SETZM	.BBNXT(T4)	;ZERO THE FORWARD LINK OF NEW ENTRY
	JUMPN	T1,LUB.04	;JUMP IF ANY ENTRIES LEFT
	XMOVEI	T1,.BDFFD(T3)	;GET ADDRESS OF FREE QUEUE FLINK
	MOVEM	T1,.BDLFD(T3)	;INIT BLINK AS POINTER TO FLINK

LUB.04:	MOVEM	T4,@.BDF2B(T2)	;LINK THIS BUFFER ONTO THE END OF THE QUEUE
	MOVEM	T4,.BDF2B(T2)	; AND UPDATE THE QUEUE BLINK
	MOVE	T2,BSDOFS	;GET THE BSD OFFSET
	CAIN	T2,.BDFDG	;NEW DATAGRAM?
	AOS	.CBDGJ(P1)	;YES, INCREMENT COUNT OF JSYS DATAGRAM BUFFERS
	CAIN	T2,.BDFMG	;NEW MESSAGE?
	AOS	.CBMGJ(P1)	;YES, INCREMENT COUNT OF JSYS MESSAGE BUFFERS
	JRST	CPOPJ1##	;SKIP RETURN

	ENDSV.			;END OF STACK VARIABLE RANGE
	SUBTTL	SUPPORT ROUTINES - GET USER BUFFER


;ROUTINE TO OBTAIN A USER BUFFER.
;CALL:
;	T1/ OFFSET INTO BSD INDICATING PACKET TYPE
;	P1/ ADDRESS OF CONNECTION BLOCK
;	PUSHJ	P,SCSGUB
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS WITH:
;	T1/ ADDRESS OF BUFFER

SCSGUB:	SKIPN	T2,.CBTBQ(P1)	;ANY BSDS ON THE CB?
	POPJ	P,		;NO, RETURN
	PUSHJ	P,SAVQ##	;FREE UP SOME AC'S
	MOVE	Q1,T2		;SAVE THE BSD ADDRESS
	MOVE	Q2,T2		;ANOTHER COPY
	MOVE	Q3,T1		;SAVE THE BSD OFFSET
	ADD	Q2,T1		;MAKE A FLINK ADDRESS
GUB.01:	SKIPN	T4,(Q2)		;GET ADDRESS OF FIRST PACKET ENTRY
	JRST	GUB.06		;NONE IN THIS BSD, TRY THE NEXT ONE
	SKIPE	.BBNXT(T4)	;ONLY ENTRY ON QUEUE?
	JRST	GUB.02		;NO
	SETZM	(Q2)		;YES, SET FIRST ENTRY ADDRESS TO ZERO
	MOVEM	Q2,.BDF2B(Q2)	;POINT BLINK AT FLINK
	JRST	GUB.03		;CONTINUE
GUB.02:	MOVE	T1,.BBNXT(T4)	;GET ADDRESS OF NEXT ENTRY
	MOVEM	T1,(Q2)		;MAKE IT THE FIRST
GUB.03:	SKIPE	.BDFFD(Q1)	;ANY ENTRIES IN FREE QUEUE?
	JRST	GUB.04		;NO
	MOVEM	T4,.BDLFD(Q1)	;YES, UPDATE LAST FREE ENTRY POINTER
	SETZM	.BBNXT(T4)	;SET END OF QUEUE TO ZERO
	JRST	GUB.05		;CONTINUE
GUB.04:	MOVE	T1,.BDFFD(Q1)	;GET ADDRESS OF FIRST ENTRY
	MOVEM	T1,.BBNXT(T4)	;MAKE NEW ENTRY POINT TO THE PREVIOUS ENTRY
GUB.05:	MOVE	T1,.BBUVA(T4)	;GET USER ADDRESS OF BUFFER
	CAIN	Q3,.BDFDG	;DID WE GET A DATAGRAM?
	SOS	.CBDGJ(P1)	;YES, DECREMENT JSYS BUFFER COUNT
	CAIN	Q3,.BDFMG	;DID WE GET A MESSAGE?
	SOS	.CBMGJ(P1)	;YES, DECREMENT JSYS BUFFER COUNT
	JRST	CPOPJ1##	;SKIP RETURN

GUB.06:	SKIPN	Q1,.BDNXT(Q1)	;IS THERE A NEXT BSD?
	POPJ	P,		;NO, ERROR
	MOVE	Q2,Q1		;GET NEW BSD ADDRESS
	ADD	Q2,T1		;ADD IN FLINK OFFSET
	JRST	GUB.01		;LOOP FOR MORE
	SUBTTL	SUPPORT ROUTINES - REMOVE USER BUFFER


;ROUTINE TO REMOVE A USER BUFFER ENTRY FROM THE BSD SET.
;CALL:
;	T1/ USER BUFFER ADDRESS (TO BE REMOVED)
;	T2/ OFFSET INTO BSD FOR BUFFER TYPE
;	P1/ ADDRESS OF CONNECTION BLOCK
;	PUSHJ	P,SCSRUB
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS

SCSRUB:	SKIPN	T3,.CBTBQ(P1)	;ANY BSDS ON THIS CONNECTION?
	POPJ	P,		;NO, ERROR
	SAVEAC	<Q1>		;SAVE AN AC
RUB.01:	SETZ	Q1,		;SET INITIAL BACK POINTER TO ZERO
	MOVE	T4,T3		;BUILD OFFSET TO FIRST BUFFER
	ADD	T4,T2		; BY ADDING OFFSET TO BSD ADDRESS
	SKIPN	T4,(T4)		;IS THERE A FIRST ENTRY?
	JRST	RUB.03		;NO, TRY NEXT BSD
RUB.02:	CAMN	T1,.BBUVA(T4)	;THIS THE ONE WE WANT?
	JRST	RUB.04		;YES
	MOVE	Q1,T4		;SAVE ADDRESS OF CURRENT BUFFER
	SKIPE	T4,.BBNXT(T4)	;IS THERE A NEXT BUFFER?
	JRST	RUB.02		;YES, CHECK THAT ONE
RUB.03:	SKIPN	T3,.BDNXT(T3)	;IS THERE A NEXT BSD?
	POPJ	P,		;NO, ERROR
	JRST	RUB.01		;TRY NEXT BSD

RUB.04:	SETZM	.BBUVA(T4)	;ZERO USER BUFFER ADDRESS
	SKIPN	Q1		;WAS THERE A PREVIOUS ENTRY?
	SKIPE	T1,.BBNXT(T4)	;OR A NEXT ENTRY?
	SKIPA			;YES, QUEUE ISN'T EMPTY
	JRST	RUB.06		;QUEUE IS EMPTY
	ADD	T2,T3		;GET ADDRESS OF BUFFER FLINK
	SKIPN	Q1		;IS THERE A PREVIOUS?
	MOVEM	T1,(T2)		;NO, UPDATE FLINK TO NEW FIRST
	SKIPN	T1		;IS THERE A NEXT ENTRY?
	MOVEM	Q1,.BDF2B(T2)	;NO NEXT, UPDATE BLINK FOR LAST NEW ENTRY
	SKIPE	Q1		;IS THERE A PREVIOUS?
	MOVEM	T1,.BDNXT(Q1)	;YES, POINT IT TO THE NEXT ENTRY
RUB.05:	MOVEM	T4,@.BDLFD(T3)	;LINK BUFFER ONTO FREE QUEUE
	MOVEM	T4,.BDLFD(T3)	;UPDATE THE BLINK
	JRST	CPOPJ1##	;SKIP RETURN

RUB.06:	MOVE	T1,T3		;GET ADDRESS OF BSD
	ADD	T1,T2		;GET ADDRESS OF FLINK
	MOVEM	T1,.BDF2B(T1)	;STORE FLINK ADDRESS IN BLINK
	SETZM	(T1)		;INIT FLINK AS ZERO
	JRST	RUB.05		;PUT ENTRY ON QUEUE AND RETURN
	SUBTTL	SUPPORT ROUTINES - RETURN BUFFER


;ROUTINE CALLED TO RETURN BUFFERS TO THE SCA POOL.
;CALL:
;	T1/ BUFFER ADDRESS
;	PUSHJ	P,SCSRET
;RETURN:
;	CPOPJ ALWAYS

SCSRET:	MOVX	T2,JH%DGB	;IS THIS A DATAGRAM BUFFER?
	TDNN	T2,.JHFLG(T1)	;...
	PJRST	SC.RBF##	;NO, RETURN MESSAGE BUFFER
	PJRST	SC.RLD##	;YES, RETURN DATAGRAM BUFFER
	SUBTTL	TEMPORARY ROUTINES


;HERE UNTIL DEFINED SOMEWHERE ELSE

;ROUTINE TO OBTAIN RESIDENT FREE SPACE.
;CALL:
;	T1/ NUMBER OF WORDS DESIRED
;	PUSHJ	P,ASGRES
;RETURN:
;	CPOPJ IF NOT AVAILABLE
;	CPOPJ1 IF AVAILABLE WITH:
;	T1/ ADDRESS OF SPACE

ASGRES:	MOVEI	T2,1(T1)	;ADD AN EXTRA WORD, MOVE TO T2
	PUSH	P,T2		;SAVE LENGTH
	PUSHJ	P,GETSWS##	;GET FROM SCA FREE POOL
	  JRST	T2POPJ##	;SORRY
	POP	P,T2		;RESTORE LENGTH
	MOVEM	T2,0(T1)	;SAVE LENGTH AS FIRST WORD
	SUBI	T2,2		;NUMBER OF WORDS TO ZERO
	AOS	T3,T1		;BUMP ADDRESS, COPY TO T3
				; (SKIP OUR LENGTH WORD)
	XMOVEI	T4,1(T3)	;DESTINATION ADDRESS
	SETZM	(T3)		;ZERO FIRST WORD
	EXTEND	T2,[XBLT]	;ZERO REMAINDER
	JRST	CPOPJ1##	;SKIP RETURN


;ROUTINE TO RETURN RESIDENT FREE SPACE.
;CALL:
;	T1/ ADDRESS OF SPACE TO RETURN
;	PUSHJ	P,RELRES
;RETURN:
;	CPOPJ ALWAYS

RELRES:	MOVE	T2,-1(T1)	;GET LENGTH WE STUFFED HERE EARLIER
	EXCH	T1,T2		;SWAP SO WE HAVE LENGTH IN T1, ADDRESS IN T2
	SOJA	T2,GIVSWS##	;ACCOUNT FOR THE WORD WE SWIPED EARLIER,
				; RETURN THE SPACE TO SCA FREE POOL AND RETURN
	SUBTTL	MISCELLANEOUS ROUTINES

;ROUTINE TO CHECK A CID AND GET ON THE PROPER CPU.
;CALL:
;	T1/ CONNECT ID
;	PUSHJ	P,CHKCID
;RETURN:
;	CPOPJ IF ERROR
;	CPOPJ1 IF SUCCESS WITH:
;	T1/ CONNECT ID
;	P1/ CBK ADDRESS
;	P5/ PBK ADDRESS

CHKCID:	PUSHJ	P,SC.CSC##	;FIRST CHECK THE CID FOR VALIDITY
	  POPJ	P,		;ERROR
				;RETURNS P1=CBK ADDRESS, P5=PBK ADDRESS
IFN FTMP,<
	MOVE	T1,.PBCPU(P5)	;GET CPU NUMBER
	PUSHJ	P,ONCPUS##	;GET ON THAT CPU
	  POPJ	P,		;CPU IS DOWN
>; END IFN FTMP
	MOVE	T1,.CBSCI(P1)	;GET SOURCE CONNECT ID
	JRST	CPOPJ1##	;SKIP RETURN
;ROUTINE TO OBTAIN A PROCESS QUEUE BLOCK WHEN NONE HAS PREVIOUSLY
;EXISTED FOR THIS CONTEXT.
;CALL:
;	W/ PDB ADDRESS
;	PUSHJ	P,GETPQB
;RETURN:
;	CPOPJ IF NO FREE CORE
;	CPOPJ1 IF SUCCESS WITH:
;	P3/ PQB ADDRESS

GETPQB:	MOVEI	T1,PQBLEN	;LENGTH OF THE BLOCK
	PUSHJ	P,ASGRES	;GET SOME FREE SPACE
	  POPJ	P,		;NO FREE CORE, RETURN
	MOVE	P3,T1		;COPY TO STANDARD REGISTER

	SETZM	SCSTMQ(P3)	;ZERO HEAD POINTER
	XMOVEI	T1,SCSTMQ(P3)	;INIT BLINK
	MOVEM	T1,SCSBMQ(P3)	; AS POINTER TO FLINK
	SETZM	SCSTDQ(P3)	;ZERO HEAD POINTER
	XMOVEI	T1,SCSTDQ(P3)	;INIT BLINK
	MOVEM	T1,SCSBDQ(P3)	; AS POINTER TO FLINK
	SETZM	SCSTXQ(P3)	;ZERO HEAD POINTER
	XMOVEI	T1,SCSTXQ(P3)	;INIT BLINK
	MOVEM	T1,SCSBXQ(P3)	; AS POINTER TO FLINK
	SETZM	SCSTEQ(P3)	;ZERO HEAD POINTER
	XMOVEI	T1,SCSTEQ(P3)	;INIT BLINK
	MOVEM	T1,SCSBEQ(P3)	; AS POINTER TO FLINK
	SETZM	SCSTCQ(P3)	;ZERO HEAD POINTER
	XMOVEI	T1,SCSTCQ(P3)	;INIT BLINK
	MOVEM	T1,SCSBCQ(P3)	; AS POINTER TO FLINK
	SETZM	SCSTXN(P3)	;ZERO HEAD POINTER
	XMOVEI	T1,SCSTXN(P3)	;INIT BLINK
	MOVEM	T1,SCSBXN(P3)	; AS POINTER TO FLINK

	MOVEM	P3,.PDSCS##(W)	;STORE ADDRESS IN PDB
	JRST	CPOPJ1##	;SKIP RETURN
;ROUTINE TO RETURN A PCB ADDRESS GIVEN A CPU NUMBER.  CALLED
;BY FUNCTIONS WHICH ACCEPT A CPU NUMBER ARGUMENT.
;CALL:
;	P2/ CPU NUMBER
;	PUSHJ	P,CPUPCB
;RETURN:
;	CPOPJ IF NO PCB AFTER CALLING ERRNPC
;	CPOPJ1 WITH:
;	P6/ PCB ADDRESS

;***	DO I LIKE THE CPU NUMBER = 6/7 HACK FOR THE CPU WE'RE RUNNING ON?

CPUPCB:	CAIN	P2,6		;SPECIAL CODE FOR "THIS" CPU?
	MOVE	P2,.CPCPN##	;YES, SUBSTITUTE "THIS" CPU NUMBER
	SKIPL	P6,P2		;RANGE CHECK CPU ARGUMENT
	CAIL	P2,CPUN##	;...
	JRST	ERRCPN		;CPU NUMBER OUT OF RANGE
	LSH	P6,.CPSOF##	;OFFSET FROM CPU0'S CDB
	SKIPG	.C0OK##(P6)	;SKIP IF CPU NOT RUNNING
	SKIPN	P6,.C0PCB##(P6)	;GET ADDRESS OF PCB AND SKIP
	JRST	ERRNPC		;NO PORT ON CPU (OR CPU NOT RUNNING)
	JRST	CPOPJ1##	;SKIP RETURN
	SUBTTL	SOFTWARE INTERRUPT SUPPORT

;ROUTINE TO RETURN THE INTERRUPT STATUS WORD.  CALLED FROM PSISER
;WHEN GRANTING AN SCS PSI EVENT.
;CALL:
;	J/ JOB NUMBER
;	PUSHJ	P,SCSPSI
;RETURN:
;	CPOPJ ALWAYS WITH:
;	T2/ INTERRUPT STATUS WORD

IFN FTPI,<
SCSPSI::SE1ENT			;RUN IN NZS
	PUSHJ	P,SAVJW##	;SAVE W (J COMES ALONG FOR THE RIDE)
	SETZ	T2,		;START WITH A BIG FAT ZERO
	PUSHJ	P,FNDPDB##	;FIND PDB FOR JOB
	  POPJ	P,		;THAT'S ODD
	SKIPN	T1,.PDSCS##(W)	;GET POINTER TO PROCESS QUEUE BLOCK
	POPJ	P,		;THAT'S ODD TOO!
	SKIPE	SCSTMQ(T1)	;ANYTHING ON MESSAGE QUEUE?
	TXO	T2,SC%MSA	;YES
	SKIPE	SCSTDQ(T1)	;ANYTHING ON DATAGRAM QUEUE?
	TXO	T2,SC%DGA	;YES
	SKIPE	SCSTXQ(T1)	;ANYTHING ON DMA XFER QUEUE?
	TXO	T2,SC%DTA	;YES
	SKIPE	SCSTEQ(T1)	;ANYTHING ON EVENT QUEUE?
	TXO	T2,SC%EVA	;YES
	POPJ	P,		;RETURN
>; END IFN FTPI
;ROUTINE TO GENERATE ANOTHER SOFTWARE INTERRUPT WHEN AN EVENT BLOCK
;OR A MESSAGE/DATAGRAM IS REMOVED FROM THE CONTEXT QUEUE.
;CALL:
;	P3/ PQB ADDRESS
;	PUSHJ	P,SCSSIG
;RETURN:
;	CPOPJ ALWAYS, INTERRUPT SIGNALLED IF NECESSARY

IFN FTPI,<
SCSSIG:	SKIPE	SCSTMQ(P3)	;ANYTHING ON MESSAGE QUEUE?
	JRST	SIG.01		;YES
	SKIPE	SCSTDQ(P3)	;ANYTHING ON DATAGRAM QUEUE?
	JRST	SIG.01		;YES
	SKIPE	SCSTXQ(P3)	;ANYTHING ON DMA XFER QUEUE?
	JRST	SIG.01		;YES
	SKIPN	SCSTEQ(P3)	;ANYTHING ON EVENT QUEUE?
	POPJ	P,		;NO, SO NO NEED FOR ANOTHER INTERRUPT
SIG.01:	PUSH	P,J		;SAVE J
	MOVE	J,.CPJOB##	;MAKE SURE J CONTAINS THE JOB NUMBER
	SIGNAL	C$SCS		;SIGNAL SCS EVENT
	  JFCL			;USER NOT ENABLED?
	PJRST	JPOPJ##		;RESTORE J AND RETURN
>; END IFN FTPI
	SUBTTL	QUEUE HEADER BLOCKS

MSG:	IFIW	.CBTMQ(P1)	;(.TOPCQ) TOP OF CB MESSAGE QUEUE
	IFIW	.CBBMQ(P1)	;(.BOTCQ) BOTTOM OF CB MESSAGE QUEUE
	IFIW	SCSTMQ(P3)	;(.TOPFQ) TOP OF CONTEXT MESSAGE QUEUE
	IFIW	SCSBMQ(P3)	;(.BOTFQ) BOTTOM OF CONTEXT MESSAGE QUEUE
	IFIW	.CBMGJ(P1)	;(.JBUFF) COUNT OF JSYS MESSAGE BUFFERS

DG:	IFIW	.CBTDQ(P1)	;(.TOPCQ) TOP OF CB DATAGRAM QUEUE
	IFIW	.CBBDQ(P1)	;(.BOTCQ) BOTTOM OF CB DATAGRAM QUEUE
	IFIW	SCSTDQ(P3)	;(.TOPFQ) TOP OF CONTEXT DATAGRAM QUEUE
	IFIW	SCSBDQ(P3)	;(.BOTFQ) BOTTOM OF CONTEXT DATAGRAM QUEUE
	IFIW	.CBDGJ(P1)	;(.JBUFF) COUNT OF JSYS DATAGRAM BUFFERS

XFER:	IFIW	.CBTXQ(P1)	;(.TOPCQ) TOP OF CB DMA XFER QUEUE
	IFIW	.CBBXQ(P1)	;(.BOTCQ) BOTTOM OF CB DMA XFER QUEUE
	IFIW	SCSTXQ(P3)	;(.TOPFQ) TOP OF CONTEXT DMA XFER QUEUE
	IFIW	SCSBXQ(P3)	;(.BOTFQ) BOTTOM OF CONTEXT DMA XFER QUEUE
	0			;(.JBUFF) NO COUNTER OF JSYS MESSAGE BUFFERS

EVT:	IFIW	.CBTEQ(P1)	;(.TOPCQ) TOP OF CB EVENT QUEUE
	IFIW	.CBBEQ(P1)	;(.BOTCQ) BOTTOM OF CB EVENT QUEUE
	IFIW	SCSTEQ(P3)	;(.TOPFQ) TOP OF CONTEXT EVENT QUEUE
	IFIW	SCSBEQ(P3)	;(.BOTFQ) BOTTOM OF CONTEXT EVENT QUEUE
	0			;(.JBUFF) NO COUNTER OF JSYS MESSAGE BUFFERS
	SUBTTL	IMPURE STORAGE

	$LOW

SCSTIQ:	BLOCK	1		;POINTER TO TOP OF INTERRUPT LEVEL PACKET QUEUE
SCSBIQ:	BLOCK	1		;POINTER TO BOTTOM OF QUEUE

	$HIGH
	SUBTTL	THE END


SCSEND::!END