Google
 

Trailing-Edge - PDP-10 Archives - BB-F493Z-DD_1986 - 10,7/703mon/sclink.mac
Click 10,7/703mon/sclink.mac to see without markup as text/plain
There are 9 other files named sclink.mac in the archive. Click here to see a list.
;TITLE SCLINK - Session Control Layer Control for DECnet-36


;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY  BE  USED
;OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT  (C)  DIGITAL  EQUIPMENT  CORPORATION  1976, 1986.
;ALL RIGHTS RESERVED.

	SUBTTL V.Brownell & W.Nichols/Tarl
	SEARCH D36PAR,SCPAR,MACSYM
	SALL

	ENTRY SCTL,SCTINI

IFN FTOPS10,<
.CPYRT<1976,1986>
> ;END IFN FTOPS10

IFN FTOPS20,<
	SEARCH PROLOG
	TTITLE SCLINK,,< - Session Control Layer for DECnet-36>
	>

IFN FTOPS10,<
	SEARCH F,S
	TITLE SCLINK - Session Control Layer Control for DECnet-36
	$RELOC
	>

	D36SYM			;SET UP D36 SPECIFIC PARAMETERS
	XRESCD			;Start in extended resident code

	SUBTTL	Table of Contents


;		Table of Contents for SCLINK
;
;
;			   Section			      Page
;   1. Table of Contents. . . . . . . . . . . . . . . . . . .    2
;   2. Definitions
;        2.1.   Internal References . . . . . . . . . . . . .    3
;        2.2.   External References . . . . . . . . . . . . .    4
;        2.3.   Accumulators. . . . . . . . . . . . . . . . .    6
;        2.4.   Node name/number database . . . . . . . . . .    7
;        2.5.   Loopback node block . . . . . . . . . . . . .    8
;   3. Macros
;        3.1.   Code-Generating
;             3.1.1.     IFxSTATE . . . . . . . . . . . . . .    9
;             3.1.2.     NEWSTATE . . . . . . . . . . . . . .   10
;   4. SCTINI - Session Control Initialization. . . . . . . .   11
;   5. SCTPSQ - Grant a PSI interrupt . . . . . . . . . . . .   12
;   6. SCTSTM - Check Active Connect Timers for Expiration. .   13
;   7. SCTSEC - Once-a-second service for SCLINK. . . . . . .   14
;   8. SCTRFJ - Request Second Service for a Link . . . . . .   15
;   9. SCTL's Interlock
;        9.1.   Queuing Version . . . . . . . . . . . . . . .   16
;        9.2.   Waiting Version . . . . . . . . . . . . . . .   17
;        9.3.   Flag Version. . . . . . . . . . . . . . . . .   18
;  10. SCTULK - SCTL's Interlock
;       10.1.   Unlock Checks . . . . . . . . . . . . . . . .   19
;  11. NSP.
;       11.1.   Function Dispatch Table . . . . . . . . . . .   20
;       11.2.   Wait Check Tables
;            11.2.1.     Definitons . . . . . . . . . . . . .   21
;       11.3.   Wait check tables
;            11.3.1.     "Before" NSP. table. . . . . . . . .   22
;            11.3.2.     "After" NSP. table . . . . . . . . .   23
;            11.3.3.     Routines . . . . . . . . . . . . . .   24
;       11.4.   SCTNSF - The Entry to SCLINK from SCMUUO. . .   25
;       11.5.   NSFRE - REset all links . . . . . . . . . . .   31
;       11.6.   NSFEA - Enter Active State. . . . . . . . . .   32
;       11.7.   NSFEP - Enter Passive State . . . . . . . . .   35
;       11.8.   NSFRI - Read Connect Information. . . . . . .   36
;       11.9.   NSFAC - Accept the Connect. . . . . . . . . .   37
;       11.10.  NSFRJ - Reject the Connect. . . . . . . . . .   39
;       11.11.  NSFRC - Read Connect Confirm Information. . .   40
;       11.12.  NSFSD - Synchronous Disconnect. . . . . . . .   41
;       11.13.  NSFAB - Abort and Release . . . . . . . . . .   43
;       11.14.  NSFRD - Read Disconnect Data. . . . . . . . .   44
;       11.15.  NSFRL - Release the Channel . . . . . . . . .   45
;       11.16.  NSFRS - Read the Channel Status . . . . . . .   46
;       11.17.  NSFIS - Send Interrupt Data . . . . . . . . .   47
;       11.18.  NSFIR - Receive Interrupt Data. . . . . . . .   49
;       11.19.  NSFDS - Send Normal Data. . . . . . . . . . .   51
;       11.20.  NSFDR - Receive Normal Data . . . . . . . . .   55
;       11.21.  NSFSQ - Set Quotas and Goals. . . . . . . . .   57
;       11.22.  NSFRQ - Read Quotas and Goals . . . . . . . .   58
;       11.23.  NSFJS - Set Job Quotas and Goals. . . . . . .   59
;       11.24.  NSFJR - Read Job Quotas and Goals . . . . . .   60
;       11.25.  NSFPI - Set PSI Reason Mask . . . . . . . . .   61
;  12. RETFLW - Return current flow controls. . . . . . . . .   62
;  13. SNDDRQ - Send Data Requests. . . . . . . . . . . . . .   63
;  14. RLSLNK - Release a Link. . . . . . . . . . . . . . . .   64
;  15. LLINKS Calls
;       15.1.   Entry Vector Table. . . . . . . . . . . . . .   65
;       15.2.   SCTRIB - Reserve Input Buffer . . . . . . . .   66
;       15.3.   SCTUCG - Uncongestion Call. . . . . . . . . .   67
;       15.4.   SCTLCI - Connect Initiate Call. . . . . . . .   68
;       15.5.   The Vectored Call Entry Point SCTL. . . . . .   71
;       15.6.   SCCCR - Connect Confirmed call from NSP . . .   72
;       15.7.   SCDIR - Disconnect Initiate received call . .   73
;       15.8.   SCDCR - Disconnect Confirm received call. . .   74
;       15.9.   SCODN - Output done call. . . . . . . . . . .   75
;       15.10.  SCSEG - Segment received call . . . . . . . .   76
;       15.11.  SCDRQ - Data request received call. . . . . .   77
;       15.12.  SCNCF - No confidence in port call. . . . . .   78
;       15.13.  SCNRS - No resources call . . . . . . . . . .   79
;       15.14.  SCCLS - Close Completed call. . . . . . . . .   80
;       15.15.  SCNLK - No link call. . . . . . . . . . . . .   81
;       15.16.  SCNCM - No communication call . . . . . . . .   82
;       15.17.  SCNRN - Not in Run State call . . . . . . . .   83
;       15.18.  SCCAK - Got a Connect ACK . . . . . . . . . .   84
;  16. Subroutines
;       16.1.   CHKABO - Check SLABO flag . . . . . . . . . .   85
;       16.2.   CHKPPN - Check self PPN for validity. . . . .   86
;       16.3.   FREMSG - Free a message block . . . . . . . .   87
;       16.4.   CONBUF - Invoke Conservative Buffering. . . .   88
;       16.5.   FNDSLB - Find SLB given a channel number. . .   89
;       16.6.   FNDSBI - Find SLB from SLBid. . . . . . . . .   90
;       16.7.   FRESLB - Deallocate a SLB . . . . . . . . . .   91
;       16.8.   FRECBP - Free Connect Block . . . . . . . . .   92
;       16.9.   CDMCBP - Copy User Data from Message Block. .   93
;       16.10.  CPMSCB - Copy User Data from Message Block. .   94
;       16.11.  FRESJB - Deallocate a SJB . . . . . . . . . .   95
;       16.12.  MAKSLB - Create a SLB, filling in defaults. .   96
;       16.13.  MAKSJB - Create a SJB, filling in defaults. .   98
;       16.14.  SCSSTS/CHKSTS - Set Status & Inform PSISER. .   99
;       16.15.  SCTWKQ - Queue a Link for later call to SCTPSQ 101
;       16.16.  SLBMAT - Pattern match connect to passive SLBs 102
;       16.17.  CDBMAT - Match Two Connect Blocks . . . . . .  103
;       16.18.  STRMAT - Pattern Matcher. . . . . . . . . . .  106
;       16.19.  STPTMR - Stop the Connect Initiate Timer. . .  107
;       16.20.  STRTMR - Start the Connect Initiate Timer . .  108
;       16.21.  TMRREJ - Send a Reject with Reason RSNNRO . .  109
;       16.22.  BLDCTX - Build connect message. . . . . . . .  110
;       16.23.  PRSCTX - Parse a connect message. . . . . . .  114
;       16.24.  CPYS2M - Copy string block to message segment  118
;       16.25.  CPYM2S - Copy message data to string block. .  119
;       16.26.  SCTGSS - Get segment size for a destination .  120
;       16.27.  SCTCSS - Check segment size for a destination  121
;  17. Node name/number database
;       17.1.   Initialize. . . . . . . . . . . . . . . . . .  122
;       17.2.   Lock/unlock database. . . . . . . . . . . . .  123
;       17.3.   Add a node. . . . . . . . . . . . . . . . . .  124
;       17.4.   Address to name . . . . . . . . . . . . . . .  125
;       17.5.   Name to address . . . . . . . . . . . . . . .  126
;       17.6.   Name to NO block. . . . . . . . . . . . . . .  127
;       17.7.   Default area #. . . . . . . . . . . . . . . .  128
;       17.8.   Hash routine. . . . . . . . . . . . . . . . .  129
;       17.9.   Get a bucket. . . . . . . . . . . . . . . . .  130
;       17.10.  SCTANL - Add a loopback node name . . . . . .  131
;       17.11.  SCTN2L - Convert name to loop circ. . . . . .  132
;       17.12.  SCTL2N - Convert loopback circuit . . . . . .  132
;       17.13.  SCTCKL - Check for loopback node. . . . . . .  132
;  18. Network management
;       18.1.   Dispatch. . . . . . . . . . . . . . . . . . .  133
;       18.2.   SET parameter . . . . . . . . . . . . . . . .  134
;       18.3.   CLEAR parameter . . . . . . . . . . . . . . .  134
;       18.4.   READ parameter. . . . . . . . . . . . . . . .  134
;       18.5.   Return list of entity ids . . . . . . . . . .  135
;       18.6.   Map node address to node name . . . . . . . .  136
;       18.7.   Map node name to node address . . . . . . . .  136
;       18.8.   Check loopback node name. . . . . . . . . . .  136
;       18.9.   Event
;            18.9.1.     Invalid message. . . . . . . . . . .  137
;            18.9.2.     CSSE event . . . . . . . . . . . . .  138
;  19. Local SCTL Storage . . . . . . . . . . . . . . . . . .  139
;  20. End of SCLINK. . . . . . . . . . . . . . . . . . . . .  140

	SUBTTL	Definitions -- Internal References

;These are the routines and variables in SCLINK that are referenced
; by other modules

;Initialization
	INTERN SCTINI		;Initialize SCLINK

;Calls from higher layers
	INTERN SCTPSQ		;Grant PSI to user
	INTERN SCTWKQ		;Queue a link to SCTPSQ
	INTERN SCTSEC		;Once-a-second service
	INTERN SCTLCW		;Process level interlock grabber
	INTERN SCTNSF		;Session control function entry point

  IFN FTOPS20 <
	INTERN SCTLOK		;Session control interlock
  >

;Calls from LLINKS
	INTERN SCTRIB		;Reserve input buffer
	INTERN SCTUCG		;Congestion
	INTERN SCTLCI		;Connect initiate
	INTERN SCTL		;LLINKS entry point in general

;Network management
	INTERN SCLNMX		;NTMAN entry point
	INTERN SCTLNL		;Loopback node name list
	INTERN SCTINT		;Incoming timer
	INTERN SCTOTT		;Outgoing timer
	INTERN LEVT.0		;LCG event parameter 0

;SJB routines
	INTERN FRESJB		;Free a SJB
	INTERN MAKSJB		;Allocate a SJB
	INTERN CHKSJB		;Check an SJB for SLBs

;Node name and address mapping
	INTERN SCTAND		;Add or delete a node
	INTERN SCTA2N		;Map a node address to a name
	INTERN SCTN2A		;Map a node name to an address
	INTERN SCTNDC		;# of local nodes defined (for JNTMAN)

;Loopback nodes
	INTERN SCTN2L		;Map node name to loopback node
	INTERN SCTCKL		;Check for existance of loopback node

	SUBTTL	Definitions -- External References

;These are the external references to the D36COM library of routines.

	EXT DNGNBF		;GET TOTAL NUMBER OF BUFFERS IN SYSTEM

	EXT DNGINI		;INITIALIZE FOR INPUT (DGXYBY)
	EXT DNPINI		;INITIALIZE FOR OUTPUT (DPXYBY)
	EXT DNPINR		;REINITIALIZE FOR OUTPUT (DPXYBY)

	EXT DNP1BY		;PUT ONE BYTE IN MESSAGE
	EXT DNP2BY		;PUT TWO BYTES IN MESSAGE
	EXT DNPEBY		;PUT AN EXTENSIBLE BYTE IN MESSAGE

	EXT DNG1BY		;GET ONE BYTE FROM MESSAGE
	EXT DNG2BY		;GET TWO BYTES FROM MESSAGE
	EXT DNGEBY		;GET EXTENSIBLE BYTE FROM MESSAGE

	EXT DNGMSG		;GET DECNET-36 MESSAGE BLOCK
	EXT DNFMSG		;FREE MESSAGE BLOCK
	EXT DNMINI		;REUSE A MESSAGE BLOCK

	EXT DNLENG		;GET THE MESSAGE LENGTH
	EXT DNSLNG		;FIND THE SEGMENT LENGTH

	EXT DNGWDS		;GET SOME WORDS
	EXT DNGWDP		;Get some words in process context
	EXT DNGWDZ		;GET SOME ZEROED WORDS
	EXT DNGWZP		;Get som zeroed words in process context
	EXT DNFWDS		;FREE SOME WORDS
	EXT DNSWDS		;SMEAR SOME WORDS

	EXT DNBKBY		;GO BACKWARDS SOME BYTES
	EXT DNSKBY		;SKIP SOME BYTES

	EXT DNSMRK		;SET MARK IN MESSAGE
	EXT DNGMRK		;POSITION AT MARK IN MESSAGE

	EXT DNLMSS		;LINK MESSAGE SEGMENT INTO MESSAGE BLOCK

	EXT DNCPYW		;COPY WORDS WITH BLT OR XBLT

	EXT DNCM2U		;COPY MESSAGE TO USER BUFFER (TOPS-10)
	EXT DNCU2M		;COPY USER BUFFER TO MESSAGE (TOPS-10)
	EXT DNCM2B		;COPY MESSAGE TO MONITOR BUFFER
	EXT DNCB2M		;COPY MONITOR BUFFER TO MESSAGE

	EXT DNGTIM		;GET CURRENT TIME IN MS

	EXT DCNTSB		;TOTAL SYSTEM BUFFERS, USED AND UNUSED
	EXT DCNCON		;NON-ZERO IF SYSTEM IS CONGESTED
	EXT DCNRSB		;RESERVED BUFFERS, MODIFIED UNDER D36OFF
				; TO PROTECT AGAINST SCTRIB CALL FROM LLINKS
	EXT DCNRHT		;HIGH TIDE FOR DCNRSB
	EXT DCNRIF		;INPUT RESERVATION FAILURES
	EXT DCNROF		;OUTPUT RESERVATION FAILURES

	EXT NTPARM		;Network management parameter processing

	EXT EVPBYT		;Network management event parameter output
	EXT EVP2BT		;   "         "       "       "        "

;Entries into NSP.

	EXT NSP			;THE MAIN ENTRY TO NSP
	EXT NSPINI		;INITIALIZATION ROUTINE FOR NSP
	EXT NSPEVT		;SIGNAL AN NSP-LEVEL EVENT, USING EVENT MACRO
				; FROM SCPAR.UNV

;Entry into ROUTER.

;Calls to ROUTER violate the layering. However, it is necessary to
; ask ROUTER for the maximum buffer sizes to a given destination node in order
; to implement 'big buffers' on the NI.

	EXT RTRGBS		;Get maximum buffer size for a node
	EXT RTRCBS		;Check maximum buffer size for a node

;Here are some external references to system things.

	EXT RTN			;RETURN
	EXT RSKP		;SKIP RETURN

	EXT EV96.0		;Determines whether CSSE event 96.0 is logged

IFN FTOPS10,<
	EXT BITTBL		;TABLE OF BIT POSITIONS
	BITS==BITTBL		;SCLINK USES BITS TABLE
>; END IFN FTOPS10

IFN FTOPS20,<
	EXT ASGVAS		;Assign virtual address space
>
;Externals for TOPS10 interlock management

IFN FTOPS10,<			;EXTERNAL FOR SMP INTERLOCK BREAKER
	EXT SCTLOK		;INTERLOCK WORD
	EXT SCTLKO		;INTERLOCK OWNER WORD
>

;External data.

	EXT %SCHDR		;MAX LENGTH OF HEADERS BELOW SCTL
	EXT RTRBSZ		;CELL CONTAINING ROUTER'S BUFFER SIZE
	EXT IBBLK		;DECnet initialization block
	EXT RTRHOM		;CELL CONTAINING ROUTER'S HOME AREA
	EXT RTRMXN		;MAXIMUM NODE ADDRESS IN AREA
	EXT %SCP2Q		;"PHASE II" QUOTA
	EXT %SCINT		;DEFAULT INCOMING TIMER VALUE
	EXT %SCOTT		;DEFAULT OUTGOING TIMER VALUE
	EXT %RTMXN		;MAX-NODES
	SUBTTL	Definitions -- Accumulators

;These are some local AC defintions for Session Control.

	SL=FREE1		;SL POINTS TO THE CURRENT SC LINK BLOCK
	SA=FREE2		;SA POINTS TO THE ARGUMENT BLOCK
	PURGE FREE1,FREE2	;WE DON'T NEED THIS SYMBOL ANYMORE

	SUBTTL	Definitions -- Node name/number database

;Calculate number of buckets in database.
	SCNHSZ==^D223		;Hash table size
	NRNOPB==^D3		;Nodes per bucket

;The LDAREA macro extracts an area # from a node address
	DEFINE LDAREA(DST,SRC) <
		LDB DST,[POINTR(SRC,RN%ARE)]
	>

;The STAREA macro stores an area # into a node address
	DEFINE STAREA(SRC,DST) <
		DPB SRC,[POINTR(DST,RN%ARE)]
	>

;The LDNOD macro extracts a local node index from a node address
	DEFINE LDNODE(DST,SRC) <
		LDB DST,[POINTR(SRC,RN%NOD)]
	>

;The BEGSTR BU defines a bucket
	BEGSTR BU
		WORD NXT	;Pointer to next bucket
		WORD NO1,<NO.LEN * NRNOPB>
	ENDSTR

;The BEGSTR NO defines a single node in a bucket
	BEGSTR NO		;Represents a single node
		WORD NAM	;Node name
		WORD ADR	;Node address
	ENDSTR

	SUBTTL	Definitions -- Loopback node block

BEGSTR LN
	WORD NXT		;PTR TO NEXT LOOPBACK NODE BLOCK
	WORD NAM		;LOOPBACK NODE NAME
	WORD CIR		;LOOPBACK CIRCUIT
ENDSTR

	SUBTTL	Macros -- Code-Generating -- IFxSTATE

;Use:	AC/ One of the .NSSxx state codes
;
;a)	IFSTATE AC,<OP,CC,RJ>
;	  executed if match found
;b)	IFSTATE AC,<OP,CC,RJ>,LABEL    (Go there if match found)

DEFINE IFSTATE(AC,STATES,GLABEL),<IFST1(AC,<STATES>,<GLABEL>,N,GE,L)>


;Use:	AC/ One of the .NSSxx state codes
;
;a)	IFNSTATE AC,<OP,CC,RJ>
;	  executed if match NOT found
;b)	IFNSTATE AC,<OP,CC,RJ>,LABEL    (Go there if match NOT found)

DEFINE IFNSTATE(AC,STATES,GLABEL),<IFST1(AC,<STATES>,<GLABEL>,E,L,GE)>

DEFINE IFST1(AC,STATES,GLABEL,MOD1,MOD2,MOD3),<
	ZZ==0
	ZZCNT==0
	IRP STATES,<ZZ==ZZ ! 1B<.NSS'STATES>
		    ZZCNT==ZZCNT+1>
  IFE ZZCNT-1,<
	CAI'MOD1 AC,.NSS'STATES
	IFNB <GLABEL>,<
		IFIDN <GLABEL>,<RTN>,<RET>
		IFDIF <GLABEL>,<RTN>,<JRST GLABEL>
	>>
  IFG ZZCNT-1,<			;;IF MORE THAN ONE STATE TESTED
	MOVX CX,ZZ
	ROT CX,(AC)
	IFB  <GLABEL>,< SKIP'MOD2 CX >
	IFNB <GLABEL>,< JUMP'MOD3 CX,GLABEL >
	>
PURGE ZZCNT,ZZ
>
	SUBTTL	Macros -- Code-Generating -- NEWSTATE

;NEWSTATE will change the state of the link and call SCSSTS to tell
;PSISER to possibly let the user know about the change.

DEFINE NEWSTATE(suffix),<
	MOVX T1,.NSS'suffix	;;SET UP STATE
	CALL SCSSTS		;;LET PSISER KNOW ABOUT IT
	TRACE SC,New State: suffix
>
	SUBTTL	SCTCCR - Compute Core Requirements

;SCTCCR computes SCLINK's core requirements.
;Call:
;	PUSHJ	P,SCTCCR
;Returns:
;	T1/ Size of SCLINK core

IFN FTOPS10,<
SCTCCR::MOVEI T1,SCNHSZ		;HASH TABLE SIZE
	OPSTR <ADD T1,>,IBMXA,+IBBLK ;PLUS NUMBER OF NODES IN AREA
	ADDI T1,1		;AND AN EXTRA WORD FOR NODE 0
	RET			;AND RETURN
>; END IFN FTOPS10
	SUBTTL	SCTINI - Session Control Initialization

;SCTINI - Session Control Initialization
;
; Call:
;	IBBLK filled in
;
; Return:
;	RET			;COULDN'T INITIALIZE DUE TO RESOURCE FAILURE
;	RETSKP			;INITIALIZED
;
; Uses: T1-T4
;
; Context:
;	Process

	XSWAPCD
SCTINI:	TRACE SC,<Initializing Session Control>

;Initialize node name/number database, then call NSP to initialize itself
; and ROUTER
	CALL SCTNIN		;Initialize node name/number database
	  RET			; -failed, return error
	CALLRET NSPINI		;NSP, you can fly now!

	XRESCD			;Back to resident code

	SUBTTL SCTPSQ - Grant a PSI interrupt

;SCTPSQ - Grant a PSI interrupt for user
;
; Call:		T1/ SJB Pointer
;
; Return:
;		RET		;IF NOTHING IN QUEUE
;		RETSKP		;IF FOUND AN SLB QUEUED
;			T1+T2/ STRUCTURE PS, SEE SCPAR
;
; Uses:
;Note that this routine is not under the interlock

;Note that if we use T5 or T6 in this routine, we have to save
;them for caller.

	XRENT SCTPSQ		;Run in extended resident code

	D36OFF			;#AVOID PROBLEMS WITH SLPSI FLAG
	DEQUE T4,SJ.PSQ(T1),SL.NXP,SCTPQ1 ;#ANYTHING ON THE PSI Q?
	SETZRO SLPSI,(T4)	;#NOT ON THE PSI Q ANY MORE
	D36ON

IF1,IFN PS.MOR,<PRINTX SCTPSQ needs PSMOR to be in T1>

	MOVE T3,T1		;GET POINTER TO SJB
	LOAD T1,SLSST,(T4)	;NO INTERLOCK, DON'T CALL CHKSTS
	STOR T1,PSSTS,+T1	;STORE FOR CALLER (IN T2)
	TMNE QHBEG,+SJ.PSQ(T3)	;MORE SLBS ON PSI Q?
	TXO T1,PSMOR		;YES, SET THE MORE FLAG FOR CALLER
	LOAD T3,SLCHN,(T4)	;GET CHANNEL NUMBER
	STOR T3,PSCHN,+T1
	LOAD T3,SLPSM,(T4)	;GET PSI MASK
	STOR T3,PSPSM,+T1

	RETSKP			;SUCCESS RETURN


;Here if DEQUE found nothing

SCTPQ1:	D36ON
	RET
	SUBTTL	SCTSTM - Check Active Connect Timers for Expiration

;SCTSTM - Check active connect timers for expiration
;
; Call:
;	With nothing special
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCTSTM:	SAVEAC <SA,SL,P1,P2>	;SAVE THE JOB AND LINK BLOCK POINTERS
	LOAD SA,QHBEG,+SCTSJQ	;POINT TO THE FIRST SJB
	TRNA
SCTST1:	LOAD SA,SJNXT,(SA)	;GET THE NEXT SJB IN LIST
	JUMPE SA,RTN		;NO MORE, JUST RETURN
	JE SJCTA,(SA),SCTST1	;IF THERE ARE NO TIMERS ACTIVE FOR JOB
				; JUST LOOK AT THE NEXT GUY
	LOAD P1,SJCHC,(SA)	;GET THE COUNT OF ENTRIES IN SLB TABLE
	LOAD P2,SJCHT,(SA)	;POINT TO THE SLB TABLE

SCTST2:	SKIPN SL,(P2)		;DO WE HAVE AN SLB?
	JRST SCTST3		;NO, CHECK THE NEXT ONE
	JN <SLFSL,SLLBC>,(SL),SCTST3 ;YES, IGNORE IF ALREADY CLOSING
	LOAD T1,SLSTA,(SL)  	;GET THE STATE
	IFSTATE T1,CR,SCTST4	;CHECK THE INCOMING CONNECT TIMER
	IFSTATE T1,CS,SCTST5	;CHECK THE OUTGOING CONNECT TIMER

SCTST3:	ADDI P2,1		;INCREMENT SLB POINTER
	SOJG P1,SCTST2		;ANY MORE LEFT?
	JRST SCTST1		;NO, CHECK THE NEXT JOB

;Here with a SLB in CR state.  Check the incoming timer for expiration.

SCTST4:	CALL DNGTIM		;GET THE CURRENT TIME
	OPSTR <SUB T1,>,SLCTM,(SL) ;FIND TIME SINCE TIMER STARTED
	CAMGE T1,SCTINT		;HAS IT EXPIRED?
	JRST SCTST3		;NO, LOOK AT NEXT SLB
	CALL TMRREJ		;YES, SEND REJECT
	  JRST SCTST3		;ALLOCATION FAILURE, LEAVE TIMER RUNNING
	CALLRET SCTST6		;REJECT, STOP TIMERS AND CHECK THE NEXT ONE

;Here with a SLB in CS state.  Check the outgoing timer for expiration.

SCTST5:	CALL DNGTIM		;GET THE CURRENT TIME
	OPSTR <SUB T1,>,SLCTM,(SL) ;FIND TIME SINCE TIMER STARTED
	CAMGE T1,SCTOTT		;HAS OUTGOING TIMER EXPIRED?
	JRST SCTST3		;NO, LOOK AT NEXT SLB
	MOVX T1,RSNNRO		;YES, GET 'NO RESPONSE FROM OBJECT' REASON CODE
	STOR T1,SLRSN,(SL)	;STORE REASON FOR INTERESTED CALLER
	NEWSTATE RJ		;TELL USER LINK IS REJECTED
SCTST6:	CALL STPTMR		;STOP THE TIMERS
	JRST SCTST3		; AND CHECK THE NEXT ONE

	SUBTTL SCTSEC - Once-a-second service for SCLINK

;SCTSEC - Once-a-second service for Session Control
;
; Once a second the following things are checked:
;	connect timers
;	slb's that can be freed
;
; Call:
;	with nothing, every second
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

	XRESCD
SCTSEC:

   IFN FTOPS10 <
	SEC1			;RUN ALL OUR JUNK IN SECTION 1
   >
	SAVEAC <MB,MS,T5,T6,SL,SA,P1,P2>
	SETOM SCTJFF		;TELL SCTULK WE NEED SECOND SERVICE
	CALLRET SCTLCF		;GET INTERLOCK, CALL SCTSES


SCTSES:	SKIPE SCTCTA		;ARE THERE ANY OUTSTANDING CONNECTS?
	CALL SCTSTM		;SCAN FOR RUNNING CONNECT TIMERS

;We must not continue processing after getting a no-resources return
;because the procedure which gave that return put the current port
;back on the second-request queue.  If we were to continue along that
;queue, we would visit that port over and over forever.

SCTSS1:!DEQUE SL,SCTJFQ,SL.JFQ,RTN ;RETURN WHEN Q EMPTY
	SETZRO SLJFR,(SL)	;NO LONGER ON THE SECOND-REQ Q
	TMNN SLFSL,(SL)		;SKIP IF SLB NEEDS TO BE FREED
Repeat 0,< ;See comment below and at SNDDRQ
	JRST SCTSS2		;NO, SEE IF WE NEED DRQ CHECK
>
Repeat 1,<
	JRST SCTSS1		;Should never happen, but if! just loop back
>
	CALL SCTSFR		;YES, GET RID OF IT
	  CALLRET SCTRFJ	;LEAVE, REQUESTING SERVICE AGAIN
	JRST SCTSS1		; AND CHECK THE NEXT CHANNEL

;This code to resend DRQ is commented out since it is never used.
; See comment at SNDDRQ for more information

Repeat 0,<

SCTSS2:	OPSTR <SKIPE T1,>,SLDRR,(SL) ;ARE THERE ANY DATA REQUESTS?
	CALL SCTJDR		;YES, RESEND THE DATA REQUESTS
	  CALLRET SCTRFJ	;LEAVE, REQUESTING SERVICE AGAIN
	JRST SCTSS1		;GET NEXT SLB THAT NEEDS SERVICE

>

;Here to free an SLB that we are finished with

SCTSFR:	JN SLBSY,(SL),RTN	;CAN'T FREE IT NOW, TRY LATER
	CALL FRESLB		;CLEAN UP THAT ONE.
	RETSKP			;SUCCESS

Repeat 0,<

;Here with a SLB that needs to have DRQs resent.

SCTJDR:	TXZ T2,MBOTH		;ON THE NORMAL SUB-LINK
	CALL SNDDRQ		;SEND THOSE DATA REQUESTS
	  RET			;CAN'T, LET CALLER HANDLE THIS
	SETZRO SLDRR,(SL)	;WE SENT THE DATA REQUESTS
	RETSKP			; AND RETURN

> ;End repeat 0

	SUBTTL	SCTRFJ - Request Second Service for a Link

;SCTRFJ - Request Second Service for a Link
;
; Call:
;	SL/ The SLB for which service is requested
;
; Return:
;	RET			;ALWAYS WITH ALL TEMP ACS SMASHED
;
; Uses: T1

SCTRFJ:	MOVX T1,SLJFR		;GET QUEUED-FOR-SERVICE FLAG
	TDNE T1,SL.JFR(SL)	;ALREADY QUEUED?
	RET			;YES, DON'T Q AGAIN
	IORM T1,SL.JFR(SL)	;NO, WILL BE NOW THOUGH
	ENDQUE SL,SCTJFQ,SL.JFQ,T1
	RET
	SUBTTL	SCTL's Interlock -- Queuing Version

;SCTLCQ - Interlock routine for interrupt level callers
;
; Call:
;	T1/ Address of processor to call with the interlock
;	MB/ Pointer to Message Block with arguments stored
;		or zero if called from SCTJIF
;
; Return:
;	RET			;ALWAYS WITH ALL TEMP ACS SMASHED
;
; Uses: T1
;
;If it could not get the interlock, it queues the message block with its
;arguments.  When the interlock is given up, the queue is checked and
;if it has a message on it, it will service it.

SCTLCQ:	STOR T1,MBPRC,(MB)	;SAVE THE PROCESSING PROCEDURE

	D36OFF			;GET GLOBAL INTERLOCK FOR ENDQUE BELOW
	AOSN SCTLOK		;TEST AND SET THE INTERLOCK
	JRST SCTLQ1		;GO PROCESS, NOW THAT WE HAVE THE LOCK

;Here when we have to queue the message and dismiss.

	ENDQUE MB,SCTPRQ,MB.NXT,T1 ;QUEUE THE MESSAGE
	D36ON			;TURN INTERLOCK OFF
	RET

;Here to process the message now.

SCTLQ1:	APRID SCTLKO		;SET THE OWNER OF THE INTERLOCK
IFN FTOPS20,<
	CONSO PI,1B<DLSCHN+^D20> ;AT DTE PI LEVEL?
	CSKED			;NO, ASSURE WE GET THROUGH THIS QUICKLY
>;END IFN FTOPS20		;MUSTN'T CALL CSKED AT INTERRUPT LEVEL
	D36ON			;TURN INTERLOCK OFF
	LOAD T1,MBAR1,(MB)	;GET THE FIRST ARGUMENT
	LOAD T2,MBAR2,(MB)	; SECOND
	LOAD T3,MBAR3,(MB)	; THIRD
	OPSTR <CALL @>,MBPRC,(MB) ;GET THE PROCESSOR

	CALLRET SCTULK		;DO THE UNLOCK CHECKS
	SUBTTL	SCTL's Interlock -- Waiting Version

;SCTLCW - Interlock routine for process level callers
;
; Call:
;	T1/ Address of processor to call with the interlock
;	MB/ Pointer to Message Block with arguments stored
;
; Return:
;	RET			;ALWAYS WITH ALL TEMP ACS SMASHED
;
; Uses: T1
;
;This version of the interlock will spin on SCTLOK until it is freed.
;This is to be called only from process level, thus it will only spin
;if another process or interrupt level user has it on another processor.
;It will not fight with any other process level users on this processor
;since only one can be active on any one processor at any one time.
;
;Note that SCTLOK must be uncached if this is a multiprocessor system.

SCTLCW:
	SAVEAC <MB,SA>		;TEMPORARY UNTIL AC-TRASHING BUG FOUND.

;Even though we don't really have to store the routine address in
;the message block here, we do because its a convenient debugging tool.

	STOR T1,MBPRC,(MB)	;SAVE THE PROCESSING PROCEDURE

IFN FTOPS10,<
SCTLW1:	SKIPGE SCTLOK		;TEST INTERLOCK TO SEE IF WE HAVE A CHANGE
	AOSE SCTLOK		;TEST AND SET THE INTERLOCK
	JRST SCTLW1		;ITS LOCKED, SPIN UNTIL ITS FREED
	APRID SCTLKO		;SET THE OWNER OF THE INTERLOCK
>;END IFN FTOPS10		; CAN ONLY BE COMPETING WITH ANOTHER
				; PROCESSOR ON TOPS10
IFN FTOPS20,<
SCTLW1:	CSKED			;ASSURE WE GET THROUGH THIS QUICKLY
        AOSN SCTLOK		;TEST AND SET THE INTERLOCK
	JRST SCTLW3		;ITS OK, GO USE IT
	ECSKED			;NO, UNDO THE CSKED ABOVE.
	SKIPE INSKED		;IN USE, ARE WE AT SCHEDULAR LEVEL?
	JRST SCTLW2		;YES
	MOVE T1,[MSEC1,,SCTLWB]	;No, set up scheduler test
	MDISMS			;WAIT FOR COMPETING PROCESS TO FINISH
	JRST SCTLW1		;OK, LETS TRY AGAIN

;Must run in section 1 since the data structure used to keep the scheduler
; test routine is currently only 18-bits wide.
	RESCD
SCTLWB:	SKIPL SCTLOK		;IS SESSION CONTROL LOCK FREE YET?
	JRST (T4)		;NO, SLEEP ON
	JRST 1(T4)		;YES, WAKE UP FORK
	XRESCD

SCTLW2:	BUG.(CHK,SCTBWK,SCLINK,SOFT,<SCTNSF call from sched without lock>,,<

Cause:	The DECnet entry point SCTNSF has been called from scheduler
	level when the Session Control interlock was locked.

Action:	All scheduler level routines which call SCTNSF should first
	check SCTLOK.  If SCTLOK is not -1, then the caller should
	wait for the next scheduler cycle before calling SCTNSF.
	Inspect the stack to find out who the offender is.

>)
	  RET			;LET CALLER WORRY ABOUT NO DECNET ACTION

SCTLW3:
>;END IFN FTOPS20

;Here to process the message now.

	LOAD T1,MBAR1,(MB)	;GET THE FIRST ARGUMENT
	LOAD T2,MBAR2,(MB)	; SECOND
	LOAD T3,MBAR3,(MB)	; THIRD
	OPSTR <CALL @>,MBPRC,(MB) ;GET THE PROCESSOR

	CALLRET SCTULK		;DO THE UNLOCK CHECKS
	SUBTTL	SCTL's Interlock -- Flag Version

;SCTLCF - Interlock routine for Flagged Routines
;
; Call:
;	T1/ Address of processor to call with the interlock
;	SCTJFF or SCTUCF set non-zero for SCTULK
;
; Return:
;	RET			;ALWAYS WITH ALL TEMP ACS SMASHED
;
; Uses: T1
;

SCTLCF:

;We have no message block here, so we cannot store any processor
;address.  Caller has set a SCTxxF flag instead.
;
;All we have to do here is call the unlocker, for it will check
;the Service flags SCTJFF and SCTUCF.
;
IFN FTOPS20,<
	CONSO PI,1B<DLSCHN+^D20> ;AT DTE PI LEVEL?
	CSKED			;NO, ASSURE WE GET THROUGH THIS QUICKLY
>;END IFN FTOPS20		;MUSTN'T CALL CSKED AT INTERRUPT LEVEL
	AOSE SCTLOK		;TEST AND SET THE INTERLOCK
IFN FTOPS20,<
	IFNSK.
	  CONSO PI,1B<DLSCHN+^D20> ;NOT FREE, LEAVE NOW. AT DTE PI LEVEL?
	  ECSKED		;NO, UNDO CSKED FROM ABOVE.
	  RET			;LEAVE.
	ENDIF.
>;END IFN FTOPS20		
IFN FTOPS10,<
	RET			;NOT FREE, LEAVE NOW
>;END IFN FTOPS10		
	APRID SCTLKO		;SET THE OWNER OF THE INTERLOCK
	CALLRET SCTULK		;AND DO THE UNLOCK CHECKS.
	SUBTTL	SCTULK - SCTL's Interlock -- Unlock Checks

;SCTULK - All interlock routines call SCTULK to unlock
;
; Call:
;	No args in registers
;	Called with the Session Control Interlock still on
;
; Return:
;	RET			;ALWAYS WITH ALL TEMP ACS SMASHED
;
; Uses: T1
;

SCTULK:	SAVEAC <MB,MS,SL>	;SAVE SOME ACS

;It is OK to have the window between SKIPE SCTJFF and SETZM SCTJFF
;since the worst that can happen is that we lose a second interrupt.
;If we are so close to the next second when we respond to the last one
;that we fall into the window, its just as well not to introduce more
;overhead by closing the window!

SCTUL1:	SETZ T1,		;LOAD UP A ZERO TO TO TURN FLAG OFF
	EXCH T1,SCTJFF		;NEED TO DO
	SKIPE T1		; CLOCK LEVEL STUFF?
	CALL SCTSES		;YES, GIVE SECOND SERVICE

	SETZ T1,		;LOAD UP A ZERO TO TO TURN FLAG OFF
	EXCH T1,SCTUCF		;NEED TO DEAL WITH
	SKIPE T1		; CONGESTION RELIEF?
	CALL SCIUCG		;YES, DO SO

	D36OFF			;TURN OF THE INTERRUPTS
	DEQUE MB,SCTPRQ,MB.NXT,SCTUL2 ;DEQUEUE MSG FROM PROCESS Q
	D36ON			;UNDO THE D36OFF

	LOAD T1,MBAR1,(MB)	;GET THE FIRST ARGUMENT
	LOAD T2,MBAR2,(MB)	; SECOND
	LOAD T3,MBAR3,(MB)	; THIRD
	OPSTR <CALL @>,MBPRC,(MB) ;GET THE PROCESSOR
	JRST SCTUL1		;PROCESS THE NEXT ONE

SCTUL2:	SETOM SCTLKO		;CLEAR THE INTERLOCK OWNER
	SETOM SCTLOK		;TURN OFF THE INTERLOCK
	D36ON			;UNDO THE D36OFF
IFN FTOPS20,<
	CONSO PI,1B<DLSCHN+^D20> ;AT DTE PI LEVEL?
	ECSKED			;NO, UNDO THE CSKED WE DID BEFORE
>;END IFN FTOPS20		;MUSTN'T CALL ECSKED AT INTERRUPT LEVEL
	RET			; AND RETURN
	SUBTTL	NSP. -- Function Dispatch Table

;Define some macros to set up the dispatch table for the NSP. UUO functions.

DEFINE INIDSP(PREFIX,LABEL,START<0>,FSTNAM<MN>),<
	PREFIX''FSTNAM==START
	%%%CTR==START-1

DEFINE DSP(NAME),<
	%%%CTR==%%%CTR+1	;;INCREMENT THE COUNTER
	IFN <%%%CTR-PREFIX''NAME>,<
		PRINTX ?Function PREFIX''NAME in SCLINK not in
		PRINTX ? the same order as in SCPAR.UNV
		PASS2
		END>

	IFIW <LABEL''NAME & 777777>> ;;ENTRY POINT FOR INDIRECT CALL

DEFINE ENDDSP(MAXNAM<MX>),<
	PREFIX''MAXNAM==%%%CTR>
>

;This is the table which is used to find the entry vector to for a specific
;NSP. function.   Symbols are also defined in the for .NSFxx for each of the
;function types.

NSFNT:				;START OF THE TABLE

INIDSP .NSF,NSF,0
	DSP RE			;RESET ALL CHANNELS
	DSP EA			;ENTER ACTIVE STATE
	DSP EP			;ENTER PASSIVE STATE
	DSP RI			;READ CONNECT INFORMATION
	DSP AC			;ACCEPT THE CONNECT
	DSP RJ			;REJECT THE CONNECT
	DSP RC			;READ CONNECT CONFIRM INFORMATION
	DSP SD			;SYNCHRONOUS DISCONNECT
	DSP AB			;ABORT
	DSP RD			;READ DISCONNECT DATA
	DSP RL			;RELEASE THE CHANNEL
	DSP RS			;READ THE CHANNEL STATUS
	DSP IS			;SEND INTERRUPT DATA
	DSP IR			;RECEIVE INTERRUPT DATA
	DSP DS			;SEND NORMAL DATA
	DSP DR			;RECEIVE NORMAL DATA
	DSP SQ			;SET QUOTAS
	DSP RQ			;READ QUOTAS
	DSP JS			;SET JOB QUOTAS
	DSP JR			;READ JOB QUOTAS
	DSP PI			;SET PSI REASON MASK
ENDDSP
	SUBTTL	NSP. -- Wait Check Tables -- Definitons

;The following definitions are for the wait check tables.  The tables
;are used to find out if session control should wait for a condition
;when the user has set the NS.WAI bit.  Each table is indexed by
;function number and returns in T1 a value which means one of:

	XP CW.ERR,0		;GIVE THE USER A UNEXPECTED STATE ERROR
	XP CW.NWA,1		;DON'T BOTHER WAITING
	XP CW.WAI,2		;WAIT FOR SOME MORE TIME
	XP CW.SAT,3		;WAIT FOR SATISFYING COND BEFORE PROCEEDING
	XP CW.DAT,4		;WAIT FOR MORE DATA TO BE SENT

BEGSTR CF			;SEE CHKFOR, BELOW
	FIELD IFI,1		;THE SIGN BIT IS RESERVED FOR IFIW FLG
	FILLER 11		;ROOM FOR SOME FUNCTION FLAGS
	FIELD CHN,1		;SET IF A CHANNEL IS REQ'D FOR THIS FCN
	HWORD TST		;LOCAL ADDRESS OF TESTER FUNCTION
ENDSTR

	XP CHNREQ,1		;CHANNEL REQUIRED FOR FUNCTION
	XP NOCHAN,0		;NO CHANNEL REQ'D


;Define some macros to set up the table.

DEFINE INITAB(START<0>),<
	%%%CTR==START-1

DEFINE CHKFOR(func,reqchn,tester),<
	%%%CTR==%%%CTR+1

IFN <%%%CTR-.NSF'func>,<
	PRINTX ?NSP. Function .NSF'func in SCLINK not in
	PRINTX ? the same order as in SCPAR.UNV
	PASS2
	END>

;;The following doubtful format is designed to prevent MACRO from
;;going Polish and thus encountering its bugs.
	IFIW reqchn,tester	;SEE BEGSTR CF, ABOVE
>

DEFINE ENDTAB,<
	PURGE CHKFOR,...TST
>>

;CHKSTA checks for one of STATES, returning successfully if in WAISTA
;and the wait bit is off.

DEFINE CHKSTA(SUCSTA,WAISTA),<[IFSTATE T2,<SUCSTA>,NOWAIT
	IFNB <WAISTA>,<	       IFSTATE T2,<WAISTA>,WAITMORE >
		 	       JRST BLEWIT]
>

;CHKSTE checks for one of STATES, returning an error if in WAISTA
;and the wait bit is off.

DEFINE CHKSTE(SUCSTA,WAISTA),<[	IFSTATE T2,<SUCSTA>,NOWAIT
	IFNB <WAISTA>,<		JE SAWAI,(SA),BLEWIT
				IFSTATE T2,<WAISTA>,WAITMORE >
				JRST BLEWIT]
>

;CHKBIT checks for on of the .NSxxx status bits.
;CONDSTATES are conditional states, in which we will allow a data or
;interrupt read only if there is something queued now, else the state
;is illegal.  We don't want the poor user who sets the wait bit to hang
;in DR or DC state forever!

DEFINE CHKBIT(BIT,STATES,CONDSTATES),<[
	 IFB <STATES>,<PRINTX ?No STATE(s) declared for CHKBIT macro>
	 IFB <CONDSTATES>,IFNSTATE T2,<STATES>,BLEWIT
	 IFNB <CONDSTATES>,IFNSTATE T2,<STATES,CONDSTATES>,BLEWIT
		 	    TXNE T1,NS'BIT
	 	     	    JRST NOWAIT
	 IFNB <CONDSTATES>,IFNSTATE T2,<STATES>,BLEWIT
	 		    JRST WAITMORE]
>
	SUBTTL	NSP. -- Wait check tables -- "Before" NSP. table

;The following is the wait table which is checked before the NSP.
;function is called.  This table is indexed by function type and
;returns CW.xxx as explained above.

;*Note* The macro seems to rely on the address argument not going
; polish.  As SCLINK now runs in section 6, this may happen if you
; change anything below, including moving any of the routines (for
; instance NOWAIT) to another psects. Beware!

SCTWTB:

INITAB
	CHKFOR RE,NOCHAN,NOWAIT
	CHKFOR EA,NOCHAN,NOWAIT
	CHKFOR EP,NOCHAN,NOWAIT
IFN FTOPS10,<CHKFOR RI,CHNREQ,CHKSTE(CR,CW)> ;SUCCESS STATE,WAITMORE STATE
IFN FTOPS20,<CHKFOR RI,CHNREQ,NOWAIT>
	CHKFOR AC,CHNREQ,CHKSTA(CR)	;SUCCESS STATE,WAITMORE STATE
	CHKFOR RJ,CHNREQ,CHKSTA(CR)	;SUCCESS STATE,WAITMORE STATE
IFN FTOPS10,<CHKFOR RC,CHNREQ,CHKSTE(RN,CS)> ;ERROR IF CS STATE AND NS.WAI=0
IFN FTOPS20,<CHKFOR RC,CHNREQ,NOWAIT>
	CHKFOR SD,CHNREQ,CHKSTA(RN)	;SUCCESS STATE,WAITMORE STATE
	CHKFOR AB,CHNREQ,CHKSTA(RN)	;SUCCESS STATE,WAITMORE STATE
IFN FTOPS10,<CHKFOR RD,CHNREQ,CHKSTA(<DR,RJ>)> ;SUCCESS STATE,WAITMORE STATE
IFN FTOPS20,<CHKFOR RD,CHNREQ,NOWAIT>
	CHKFOR RL,CHNREQ,NOWAIT
	CHKFOR RS,CHNREQ,NOWAIT
	CHKFOR IS,CHNREQ,CHKBIT(IDR,<CS,RN>)
	CHKFOR IR,CHNREQ,CHKBIT(IDA,<CS,RN,DS>,<DR,DC>)
	CHKFOR DS,CHNREQ,CHKBIT(NDR,<CS,RN>)
	CHKFOR DR,CHNREQ,CHKBIT(NDA,<CS,RN,DS>,<DR,DC>)
	CHKFOR SQ,CHNREQ,NOWAIT
	CHKFOR RQ,CHNREQ,NOWAIT
	CHKFOR JS,CHNREQ,NOWAIT
	CHKFOR JR,CHNREQ,NOWAIT
	CHKFOR PI,CHNREQ,NOWAIT
ENDTAB
	SUBTTL	NSP. -- Wait check tables -- "After" NSP. table

;The following is the wait table which is checked after the NSP. function
;is called.  This table is indexed by function type and returns CW.xxx

;*Note* See comment at SCTWTB about changing the table

SCTWTA:

INITAB
	CHKFOR RE,NOCHAN,NOWAIT
	CHKFOR EA,CHNREQ,CHKSTA(RN,CS)	;SUCCESS STATE,WAITMORE STATE
	CHKFOR EP,CHNREQ,CHKSTA(CR,CW)	;SUCCESS STATE,WAITMORE STATE
	CHKFOR RI,CHNREQ,NOWAIT
	CHKFOR AC,CHNREQ,CHKSTA(RN)	;SUCCESS STATE,WAITMORE STATE
	CHKFOR RJ,CHNREQ,NOWAIT
	CHKFOR RC,CHNREQ,CHKSTA(RN,CS)	;SUCCESS STATE,WAITMORE STATE
	CHKFOR SD,CHNREQ,CHKSTA(DC,DS)	;SUCCESS STATE,WAITMORE STATE
	CHKFOR AB,CHNREQ,CHKSTA(DC,DS)	;SUCCESS STATE,WAITMORE STATE
	CHKFOR RD,CHNREQ,NOWAIT
	CHKFOR RL,CHNREQ,NOWAIT
	CHKFOR RS,CHNREQ,NOWAIT
	CHKFOR IS,CHNREQ,NOWAIT
	CHKFOR IR,CHNREQ,NOWAIT
	CHKFOR DS,CHNREQ,WAITDAT
	CHKFOR DR,CHNREQ,WAITSAT
	CHKFOR SQ,CHNREQ,NOWAIT
	CHKFOR RQ,CHNREQ,NOWAIT
	CHKFOR JS,CHNREQ,NOWAIT
	CHKFOR JR,CHNREQ,NOWAIT
	CHKFOR PI,CHNREQ,NOWAIT
ENDTAB
	SUBTTL	NSP. -- Wait check tables -- Routines

;The following routines perform the correct action for the function.  They
;are dispatched to by the "before" and "after" tables.

;Don't wait any longer.

NOWAIT:	MOVX T1,CW.NWA
	RET

;Error (possibly expected) occured.

BLEWIT:	MOVX T1,CW.ERR
	RET

;Wait some more.

WAITMO:	MOVX T1,CW.WAI
	RET

;Loop to do it again.

WAITSAT:
	MOVX T1,CW.SAT
	RET

;Wait if we are to get more data.

WAITDAT:
	MOVX T1,CW.DAT
	RET
	SUBTTL	NSP. -- SCTNSF - The Entry to SCLINK from SCMUUO

;SCTNSF - Perform a NSP. UUO function
;
; Call:
;	T1/ Pointer to Message Block in SA format (See BEGSTR SA in D36PAR)
;
; Return:
;	RET			;ALWAYS WITH SJERR CONTAINING A ZERO FOR SUC.
;				; AND A NON-ZERO ERROR CODE FOR FAILURES
;
; Uses: T1-T6
;
;We return the updated user arguments in the SJB (or error code on a
;failure).
;
;Note:  It is assumed that SCJSYS, or whatever equivalent, has range
;and privilege checked all of the user arguments.  The arguments are
;all in either the SAB portion of the message block or in internal
;blocks pointed to by the SAB (SACBP and SASBP).

	XRENT SCTNSF

	SAVEAC <MB,MS,SL,SA,T5,T6,P1,P2> ;SAVE ALMOST ALL ACS
	SKIPG SA,T1		;DID HE SUPPLY A MESSAGE BLOCK?
	SCERR %NEABE,SCTNIE,<No argument block>
	SETZRO SAERR,(SA)	;INITIALIZE THE ERROR LOCATION

	LOAD T1,SAAFN,(SA)	;GET THE FUNCTION
	CAIL T1,.NSFMN		;RANGE CHECK
	CAILE T1,.NSFMX		; THE FUNCTION CODE
	SCERR %NEILF,SCTNIE,<Illegal function>
	;...
	;...

;The code on this page and the following page is a series of coroutines.  The
;Session Control interlock will only interlock a whole routine (to reduce the
;possibility of interlock screwups).  The routines on this page call
;coroutines on the following page (only) via the Session Control interlock.

;We start on this page, not owning the Session Control interlock.  The first
;interlocked coroutine called is SCTNIS (Setup).  SCTNIS will do what it can,
;then, when it needs to do something outstide of the Session Control
;interlock, it will put the address of one of the labels on this page into
;MBPRC,(SA) and then RET.  The RET takes us back to SCTULK (Session Control
;unlock) where we process any Session Control requests which may have been
;queued.  Then control returns to this page after the original coroutine call.
;Each of the coroutines on this page has OPSTR <CALLRET @>,MBPRC,(SA) after
;its call the the Session Control interlock.

;Control leaves this page either via a coroutine call to an interlocked
;routine on the next page or by a RET to SCTNSF's caller.

	MOVE MB,SA		;INTERLOCK ROUTINE NEEDS MB SET UP
	XMOVEI T1,SCTNIS	;INTERLOCKED SETUP ROUTINE
	CALL SCTLCW		;CALL INTERLOCKED SETUP ROUTINE
	OPSTR <CALLRET @>,MBPRC,(SA) ;CALL COROUTINE CHOSEN BY SCTNIS

;Here to HIBER without the interlock before calling the BEFORE-function
;coroutine (again).

SCTNS1:	LOAD T1,SASJB,(SA)	;PASS PTR TO SJB IN T1 TO @SAHBA(SA)
	LOAD T2,SAACH,(SA)	;PASS CHANNEL # IN T2
	MOVE T3,SA		;PASS SAB ADDR IN T3 (SIGH)
	OPSTR <CALL @>,SAHBA,(SA) ;CALL THE HIBER'ER
	MOVE MB,SA		;MB MUST BE SET UP FOR INTERLOCK
	XMOVEI T1,SCTNIB	;CALL THE "BEFORE" INTERLOCKED PROCESS
	CALL SCTLCW		; AFTER WE GET THE INTERLOCK
	OPSTR <CALLRET @>,MBPRC,(SA) ;CALL COROUTINE CHOSEN BY SCTNIB

;Here to HIBER without the interlock before calling the AFTER-function
;coroutine (again).

SCTNS2:	LOAD T1,SASJB,(SA)	;PASS PTR TO SJB IN T1 TO @SAHBA(SA)
	LOAD T2,SAACH,(SA)	;PASS CHANNEL # IN T2
	MOVE T3,SA		;PASS SAB ADDR IN T3 (SIGH)
	OPSTR <CALL @>,SAHBA,(SA) ;CALL THE HIBER'ER
	MOVE MB,SA		;MB MUST BE SET UP FOR INTERLOCK
	XMOVEI T1,SCTNIA	;CALL THE "AFTER" INTERLOCKED PROCESS
	CALL SCTLCW		; AFTER WE GET THE INTERLOCK
	OPSTR <CALLRET @>,MBPRC,(SA) ;CALL COROUTINE CHOSEN BY SCTNIA

;Here to give up the interlock and call the system schedular to give others
;a chance between segments of a long message.

SCTNS3:
IFN FTOPS10,<
	MCALL (RG,MSEC1,INTLVL##) ;ARE WE AT UUO LEVEL?
	  MCALL (RG,MSEC1,SCDCHK##) ;YES, LET OTHER USERS HAVE A CHANCE
>; END IFN FTOPS10
	MOVE MB,SA		;MB MUST BE SET UP FOR INTERLOCK
	XMOVEI T1,SCTNIB	;CALL THE "BEFORE" PROCESS AGAIN
	CALL SCTLCW		; AFTER WE GET THE INTERLOCK
	OPSTR <CALLRET @>,MBPRC,(SA) ;CALL COROUTINE CHOSEN BY SCTNIB
;All of these routines have the interlock.  These routines may only go
;back to non-interlocked code by the return address in MBPRC.

;Setup the SLB.

SCTNIS:	LOAD T1,SAAFN,(SA)	;GET THE FUNCTION CODE
	SETO SL,		;ASSUME WE HAVE NO SLB (-1 IS 'UNASSIGNED')
	JE CFCHN,+SCTWTB(T1),SCNIS1 ;IF NO CHANNEL IS REQ'D,
				    ; LEAVE SL AS 'UNASSIGNED' (-1)
	LOAD T1,SAACH,(SA)	;GET THE CHANNEL NUMBER
	LOAD T2,SASJB,(SA)	;POINT TO THE JOB BLOCK
	CALL FNDSLB		;FIND THE CORRESPONDING SLB
	  SCERR %NEBCN,SCTNIE,<Bad Channel Number>
	SETONE SLBSY,(SL)	;NO ONE MAY FREE THIS SLB
	LOAD T1,SLUID,(SL)	;GET THE SLB'S SERIAL NUMBER
	STOR T1,SAUID,(SA)	; AND STASH IT IN THE SAB
SCNIS1:	STOR SL,SASLB,(SA)	;STORE THE SLB POINTER
	JRST SCNIB1		;WE ALREADY HAVE THE INTERLOCK

;Do the BEFORE-function state checking and perform the function.

SCTNIB:	LOAD SL,SASLB,(SA)	;GET POINTER TO LINK BLOCK
SCNIB1:	JUMPL SL,[SETZB T1,T2	;NO STATUS IF NO SLB (EA & EP FUNCTS)
		  JRST SCNIB3]	;GO SEE IF WE CAN MAKE ONE
	LOAD T1,SLUID,(SL)	;IS THIS SLB POINTER
	OPSTR <CAMN T1,>,SAUID,(SA) ; STALE ?
	  JRST SCNIB2		;NO. CONTINUE
	SETO SL,		;YES. LOSE THIS POINTER.
	SCERR %NEBCN,SCTNIE,<Bad Channel Number> ;FAIL.
SCNIB2:	LOAD T1,SLSST,(SL)	;GET CURRENT LINK STATUS
	LOAD T2,NSSTA,+T1	;PUT JUST THE STATE IN T2
SCNIB3:	LOAD T3,SAAFN,(SA)	;GET THE FUNCTION BACK
	CALL @SCTWTB(T3)	;SEE IF WE SHOULD WAIT A WHILE
	CALLRET @.+1(T1)	;BRANCH ON SCTWTB'S RETURN CODE
	   IFIW <SCTNIU&777777>	;CW.ERR - ERROR RETURN TO MONUSER
	   IFIW <SCNIB4&777777>	;CW.NWA - NO WAIT, GO DO FUNCTION
	   IFIW <SCNIB5&777777>	;CW.WAI - WAIT IF MONUSER SO REQUESTED

;Here to call the function processor

SCNIB4:	LOAD T1,SAAFN,(SA)	;GET THE FUNCTION TYPE
	CALL @NSFNT(T1)		;CALL FUNCTION PROCESSOR
	  JRST SCTNIE		;REPORT THE ERROR IF NEEDED
	STOR SL,SASLB,(SA)	;SAVE THE POSSIBLY NEW SLB FOR AFTER CHECKING
	JRST SCNIA1		;DON'T GET INTERLOCK, WE HAVE IT

;Here when function not yet complete to see if we should wait

SCNIB5:	TMNN SAWAI,(SA)		;MONUSER ASK US TO WAIT FOR FCN COMPLETION?
	JRST SCTNIX		;NO, RETURN TO MONUSER NOW
	XMOVEI T2,SCTNS1	;YES, DO OUR HIBER
	STOR T2,MBPRC,(SA)	;STORE COROUTINE ADDRESS
	RET			;BACK TO NON-INTERLOCKED CODE
;Here to do the AFTER-function checking.

SCTNIA:	LOAD SL,SASLB,(SA)	;GET POINTER TO LINK BLOCK
SCNIA1:	JUMPL SL,SCTNX1		;IF WE DON'T HAVE A SLB, JUST LEAVE

;Note that none of the NSFxx routines call CHKSTS when they change
;a status other than the link state, because they assume that we will
;call CHKSTS from here.

	CALL CHKSTS		;UPDATE SLB'S LINK STATUS WORD
	LOAD T1,SLSST,(SL)	;GET CURRENT LINK STATUS
	LOAD T2,NSSTA,+T1	;PUT JUST THE STATE IN T2
	LOAD T3,SAAFN,(SA)	;GET THE FUNCTION CODE BACK
	CALL @SCTWTA(T3)	;SEE IF WE SHOULD WAIT A WHILE
	CALLRET @.+1(T1)	;BRANCH ON ANSWER FROM SCTWTA
	   IFIW <SCTNIU&777777>	;CW.ERR - ERROR RETURN REQUIRED
	   IFIW <SCTNIX&777777>	;CW.NWA - FCN COMPLETE, RETURN TO MONUSER
	   IFIW <SCNIA2&777777>	;CW.WAI - FCN NOT COMPLETE, WAIT
	   IFIW <SCNIA3&777777>	;CW.SAT - CHECK FOR I/O SATISFIED
	   IFIW <SCNIA4&777777>	;CW.DAT - BRIEF PAUSE BETWEEN MSG SEGMENTS

;Here if function not yet complete, see if we have to wait

SCNIA2:	TMNN SAWAI,(SA)		;DID MONUSER ASK TO WAIT FOR FCN TO COMPLETE?
	JRST SCTNIX		;NO, RETURN TO USER NOW
	XMOVEI T1,SCTNS2	;YES, POINT TO HIBER ROUTINE, OUT-OF-INTERLOCK
	STOR T1,MBPRC,(SA)	;TELL CO-ROUTINE WHERE TO GO TO HIBER
	RET			; AND RETURN TO TO HIBER

;Here to see if .NSFDR function is complete, wait if so requested

SCNIA3:	TMNN SAWAI,(SA)		;MONUSER WANT US TO WAIT FOR I/O COMPLETE?
	JRST SCTNIX		;NO, RETURN TO MONUSER NOW
	TMNN SASAT,(SA)		;YES, IS THE I/O SATISFIED YET?
	JRST SCNIB2		;NO, GO BACK AND TRY AGAIN
	JRST SCTNIX		;YES, RETURN TO MONUSER NOW

;Here to free the Session Control interlock briefly between message segments
;so that we don't keep the lock too long when sending long messages.

SCNIA4:	TMNE SASAT,(SA)		;I/O SATISFIED YET?
	JRST SCTNIX		;YES, RETURN TO CALLER NOW
	LOAD T1,SLSST,(SL)	;NO, CAN WE SEND ANOTHER SEGMENT
	TXNE T1,NSNDR		; NOW? (ONLY NORMAL DATA CAN HAVE MULT SEGS)
	JRST SCNIA5		;YES
	TMNN SAWAI,(SA)		;NO, USER WANT TO BLOCK?
	JRST SCTNIX		;NO, RETURN TO USER NOW
	JRST SCNIB2		;YES, GET BEFORE PROCESSOR TO BLOCK

SCNIA5:	XMOVEI T1,SCTNS3	;RELEASE INTERLOCK A BIT
	STOR T1,MBPRC,(SA)	; THEN DO THE NEXT SEGMENT
	RET			;RETURN FROM THE SCTL INTERLOCK

;Here to exit with the normal return.

SCTNIX:	SETZRO SAAST,(SA)	;START WITH NO STATUS SO THAT A RESET
				; FUNCTION WILL RETURN A ZERO AS STATUS
	JUMPL SL,SCTNX1		;IF WE DON'T HAVE AN SLB, PUNT
	LOAD T1,SLSST,(SL)	;GET CURRENT LINK STATUS
	STOR T1,SAAST,(SA)	;STORE STATUS FOR USER AS WE LEAVE
	SETZRO SLBSY,(SL)	;WE'RE DONE WITH SLB NOW, IF NEED BE
				; IT MAY BE FREED
SCTNX1:	XMOVEI T1,RTN		;JUST RETURN WHEN DONE
	STOR T1,MBPRC,(SA)	;STORE THAT AS THE PROCEDURE
	RET			;RETURN TO OTHER CO-ROUTINE
;Here to report the Unexpected State errors.  We will give the error
;based upon the state that was unexpected.

SCTNIU:	CALL CHKSTS		;UPDATE SLB'S LINK STATUS WORD
	LOAD T1,SLSST,(SL)	;GET CURRENT LINK STATUS
	LOAD T2,NSSTA,+T1	;GET THE STATE
	IFSTATE T2,<DR,RJ>,SCTNIR ;IF WE'RE IN DR OR RJ GIVE REASON ERROR
				; MESSAGE
	ADJBP T2,ERRSBP		;INDEX INTO UNEXPECTED STATE CODE TABLE
	LDB T1,T2		;GET THE VALUE OF THE ERROR MESSAGE
	JUMPE T1,SCTNUE		;JUMP IF UNEXPECTED STATE
	CALLRET SCTNIE		;REPORT THE ERROR TO USER

SCTNUE:	SCERR %NEUXS,SCTNIE,<Unexpected State: Unspecified>


;Here to give the error codes which correspond to the REASON
;codes given by NSP.

SCTNIR:	LOAD T3,SLRSN,(SL)	;GET THE REASON CODE NSP GAVE US
	JUMPE T3,SCTNR1		;IF REASON IS ZERO, WE HAVE TO FIGURE
				;OUT ERROR BASED ON STATE
	CAILE T3,RSNLEN		;IS THE REASON VALUE REASONABLE?
	JRST SCTNR2		;NO, CALL IT UNSPECIFIED
	MOVE T1,ERRRBP		;GET THE REASON ERROR TABLE BYTE POINTER
	ADJBP T3,T1		;INDEX TO THE CORRECT MESSAGE
	LDB T1,T3		;GET THE VALUE OF THE ERROR MESSAGE
	JUMPE T1,SCTNR2		;IF TABLE SAYS ZERO, IT UNSPECIFIED
	CALLRET SCTNIE		;RETURN THE FAILURE TO THE USER

SCTNR1:	IFSTATE T2,RJ
	  SCERR %NERBO,SCTNIE,<Rejected by Object>
	IFSTATE T2,DR
	  SCERR %NEDBO,SCTNIE,<Disconnected by Object>
SCTNR2:	SCERR %NEREJ,SCTNIE,<Unspecified Reject Reason>


;Here with an error code in T1.

SCTNIE:	SKIPN T1		;IS THERE AN ERROR CODE?
	BUG.(CHK,SCLNZE,SCLINK,SOFT,<Passing zero error code to SCMUUO>,,<

Cause:	The routine that is supposed to store an error code
	for the user is zero.  This is an illegal value.

Action:	Find who called SCTNIE with T1/ 0 and correct the caller's behavior.

>)
	STOR T1,SAERR,(SA)	;STORE THE ERROR TYPE
	SKIPLE SL		;DON'T UPDATE STATUS IF WE HAVE NO SLB
	CALL CHKSTS		;UPDATE SLB'S LINK STATUS WORD
	CALLRET SCTNIX		;FREE THE MESSAGE
;The following is the table (indexed by SC state) which contains the
;error code to give (i.e., %NSUDR = Unexpected State:  Disconnect
;Received).

ERRSTB:	BYTE(6)	%NEUXS,	%NEUCW,	%NEUCR,	%NEUCS,	%NEURJ,	%NEURN
	BYTE(6) %NEUDS,	%NEUDC,	%NEUCF,	%NEULK,	%NEUCM,	%NEUNR

ERRSBP:	POINT 6,ERRSTB		;BYTE POINTER TO ERROR MESSAGES


;This table is indexed by reason code, with each cell corresponding to
;the correct error message to give for that reason.  A zero in the
;table means "return the Unspecified Reject Reason error code".
;This should only happen when someone gives us a reason that we
;never heard of.

ERRRSB:	BYTE(6)	%NERES,	%NEUNN,	%NERNS,	%NEURO,	%NEIOF,	%NEOTB
	BYTE(6)	0,	%NEABM,	%NEABO,	%NEINF,	%NELNS,	0
	BYTE(6)	0,	0,	0,	0,	0,	0
	BYTE(6)	0,	0,	0,	0,	0,	0
	BYTE(6)	0,	0,	0,	0,	0,	0
	BYTE(6)	0,	0,	0,	%NEACR,	0,	0
	BYTE(6)	0,	%NERNO,	%NENUR,	0,	%NENLK,	%NEDSC
	BYTE(6) %NEIMG

RSNLEN==<.-ERRRSB>*6

ERRRBP:	POINT 6,ERRRSB		;BYTE POINTER TO REASON ERROR MESSAGES
	SUBTTL	NSP. -- NSFRE - REset all links

;NSFRE - REset all links
;
; Call:
;	SA/ Pointer to Session Control Argument Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;This function is only done by the system when the user does a RESET
;UUO.  There are no arguments.

NSFRE:	SAVEAC SL		;PRESERVE SL FOR SCTNI*
	LOAD T1,SASJB,(SA)	;GET POINTER TO SJB
	LOAD P1,SJCHT,(T1)	;GET THE POINTER TO SLB TABLE
	LOAD P2,SJCHC,(T1)	;GET THE COUNT OF LINKS

NSFRE1:	SKIPN SL,(P1)		;IS THERE ANYTHING THERE?
	JRST NSFRE2		;NO, TRY NEXT LINK
	SETZRO SLBSY,(SL)	;THIS LINK IS NO LONGER BUSY
	SETZ MB,		;RESET PASSES NO MSG BLK, NO Q'D INTERLOCKS
	CALL RLSLNK		;(MB)DO A RELEASE ON THIS LINK
NSFRE2:	ADDI P1,1		;POINT TO THE NEXT SLB
	SOJG P2,NSFRE1		;DO THE NEXT ONE, IF THERE ARE ANY LEFT
	RETSKP			;RETURN SUCCESS
	SUBTTL	NSP. -- NSFEA - Enter Active State

;NSFEA - Enter Active State
;
; Call:
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFEA function, the function dependant arguments are:
;
;	SAACH/ Returned: zero on failure, channel # on success
;	SAAA1/ Ptr to connect-block
;	SAAA2/ Segment Size
;	SAAA3/ Flow control mode

NSFEA:	TRACE SC,<Performing Enter Active (.NSFEA)>
	SETZRO SAACH,(SA)	;ASSUME WE CANNOT ASSIGN A CHANNEL
	CALL MAKSLB		;MAKE A SLB
	 SCERR %NEALF,RTN,<Allocation failure>
	STOR T1,SAACH,(SA)	;STORE CHANNEL NUMBER FOR MONUSER
	NEWSTATE CS		;INITIAL STATE IS CONNECT SENT

; Get the Object type and if format 0, store both the source (SLSOB)
; and destination (SLDOB) object type into the SLB.

	LOAD T3,SACBP,(SA)	;GET POINTER TO CONNECT BLOCK
	XMOVEI T1,CB.SRC(T3)	;GET THE POINTER TO SOURCE PDB
	LOAD T2,PBFOR,(T1)	;GET THE FORMAT TYPE
	CAIE T2,FRM.2		;FORMAT TYPE WITH PPN?
	 IFSKP.
	  CALL CHKPPN		;AND VERIFY THE PPN
	   SCERR %NEPRV,NSFEAC,<No privs to use bad PPN>
	 ELSE.
	 IFN FRM.0,<PRINTX ?FRM.0 MUST BE EQUAL TO 0>
	 ANDE. T2		;IF OBJECT TYPE, STORE THE NUMBER
	  LOAD T2,PBOBJ,(T1)	;GET OBJECT TYPE WE CLAIM TO BE
   IFN FTOPS10,<
	  LOAD T3,SLSJB,(SL)	;GET POINTER TO SJB
	  TMNN SJPRV,(T3)	;ARE WE PRIVED
	  CAIL T2,^D128		;IS OBJECT TYPE PRIVED?
	  TRNA			;WE ARE O.K.
	   SCERR %NEPRV,NSFEAC,<No privs for this object type>
   > ; END FTOPS10
	  STOR T2,SLSOB,(SL)	;SAVE SOURCE OBJ TYPE FOR SYSDPY AND THE LIKE
	 ENDIF.
	LOAD T3,SACBP,(SA)	;GET POINTER TO CONNECT BLOCK
	XMOVEI T1,CB.DST(T3)	;GET THE POINTER TO THE DEST PDB
	LOAD T2,PBFOR,(T1)	;GET THE FORMAT TYPE
	CAIE T2,FRM.0		;FORM WITH JUST AN OBJECT TYPE
	 IFSKP.
	  LOAD T2,PBOBJ,(T1)	;GET THE OBJECT TYPE FROM PDB
	  STOR T2,SLDOB,(SL)	;SAVE DEST OBJ TYPE FOR SYSDPY AND THE LIKE
	 ENDIF.
	LOAD T1,SAKCB,(SA)      ;GET USER'S 'KEEP CONNECT BLOCK' FLAG
	STOR T1,SLKCB,(SL)      ;REMEMBER IT
	JUMPE T1,NSFEA3         ;JUMP IF WE'RE NOT TO KEEP CONNECT BLOCK
	MOVX T1,CB.LEN		;GET 'CONNECT BLOCK LENGTH' WORDS
	CALL DNGWDS		;GET NON-ZEROED WORDS
  	 SCERR %NEALF,NSFEAC,<Allocation failure>
	STOR T1,SLCBP,(SL)	;SAVE POINTER TO SLB'S NEW CB
				; FRESLB WILL FREE THIS CB FROM NOW ON
	MOVE T2,T1		;DESTINATION IS NEW CB
	LOAD T1,SACBP,(SA)	;SOURCE IS CONNECT BLOCK FROM SAB
	MOVX T3,CB.LEN		;LENGTH IS LENGTH OF A CONNECT BLOCK
	CALL DNCPYW		;COPY SAB'S CB TO SLB'S CB
NSFEA3:
	LOAD P1,SANAG,(SA)	;GET THE NUMBER OF ARGS USER SPECIFIED
	CAIG P1,.NSAA3		;DID USER SPECIFY THE FLOW CONTROL MODE?
	JRST NSFEA4		;NO

	LOAD T1,SAAA3,(SA)	;GET THE FLOW CONTROL MODE
	JUMPLE T1,NSFEA4	;DIDN'T SPECIFY, USE THE DEFAULT
	CAIL T1,NSF.C0		;RANGE CHECK THE
	CAILE T1,NSF.MX		; FLOW CONTROL MODE
	SCERR %NEIFM,NSFEAC,<Illegal flow control mode>
	SUBI T1,1		;TRANSLATE FLOW CONTROL MODE INTO THE
				; FCM.XX FORM THAT NSP USES
	STOR T1,SLRFL,(SL)	;STORE THE FLOW CONTROL MODE

NSFEA4:	SETZ T1,		;NO USER DATA NEEDED
	CALL DNGMSG		;GET MESSAGE BLOCK
	 SCERR %NEALF,NSFEAC,<Allocation failure>
	MOVE MB,T1		;PUT MESSAGE BLOCK POINTER IN MB

	MOVX T1,OA.LEN		;LENGTH OF OPEN ARGS
	CALL DNGWDZ		;GET BLOCK FOR OPEN ARGS
	 SCERR %NEALF,NSFEAD,<Allocation failure>
;Now fill in the Open block with the arguments.

	MOVE P1,T1		;MAKE P1 POINT TO THE ARGUMENT BLOCK
	STOR SL,OASCB,(P1)	;PUT ID IN OPEN ARG BLOCK

	OPSTR <SKIPN T2,>,SACBP,(SA) ;GET THE CONNECT BLOCK POINTER
	SCERR %NENCD,NSFEAE,<No connect block>

	OPSTR <SKIPE T1,>,CBNUM,(T2) ;Get node number and check for zero
	IFSKP.			;If it is zero, then its a loopback circuit
	  LOAD T1,CBCIR,(T2)	;Get loopback circuit ID
	  STOR T1,OACIR,(P1)	; and store it
	  LOAD T1,IBADR,+IBBLK	;Get local node number
	ENDIF.
	STOR T1,OANOD,(P1)	;PUT DESTINATION NODE ADDRESS IN ARG BLOCK
	STOR T1,SLDNA,(SL)	; AND STORE AS DESTINATION OF THIS LINK

;	LOAD T1,SLDNA,(SL)
	OPSTR <SKIPN>,OACIR,(P1) ;Loopback?
	IFSKP.			; -yes
	  MOVE T1,RTRBSZ	;  Use default buffer size
	  SUBI T1,%SCHDR	;   minus header
	ELSE.			; -no,
	  CALL SCTGSS		;  Get segment size for that destination
	ENDIF.
	LOAD T2,SANAG,(SA)	;Get the number of args user specified
	CAIG T2,.NSAA2		;Did the user specifiy the segment size?
	IFSKP.			;Yes, use the supplied value
	  OPSTR <SKIPLE T2,>,SAAA2,(SA) ;Get the segment size
	  CAMLE T2,T1		;  is it bigger than the legal max?
	  IFSKP. <MOVE T1,T2>	;   -no smaller, use that value
	ENDIF.
	STOR T1,SLSIZ,(SL)	;STORE THE SEGMENT SIZE IN SLB

	LOAD T1,SLRFL,(SL)	;GET THE FLOW CONTROL MODE BACK
	STOR T1,OAFLO,(P1)	;PUT IN OPEN ARG BLOCK
	LOAD T2,SLGOL,(SL)	;GET THE DATA REQUEST GOAL
	CAIN T1,FCM.MG		;ARE WE IN MESSAGE FLOW CONTROL?
	CAIL T2,1		;YES, IS OUR GOAL SET TO LESS THAN ONE?
	TRNA			;NO, DON'T WORRY ABOUT IT
	MOVEI T2,1		;IN MSG FLOW MODE WE GOAL OF AT LEAST 1
	STOR T2,SLGOL,(SL)	;REMEMBER THE GOAL WE DECIDED ON
	STOR T2,OAGOL,(P1)	;TELL LLINKS ABOUT THE GOAL TOO

	LOAD T1,SLSIZ,(SL)	;GET THE MAXIMUM SEGMENT SIZE
	STOR T1,OASIZ,(P1)	;THAT TOO

	MOVE T1,[XCDSEC,,SCTL]	;HE WANT'S OUR ENTRY VECTOR ADDRESS
	STOR T1,OASCV,(P1)	;GIVE IT TO HIM

	MOVE T1,P1		;PASS ARG POINTER IN T1
	MOVX T2,OA.LEN		;LENGTH OF THE OPEN ARG BLOCK
	MOVX T3,NV.OPN		;FUNCTION IS OPEN A PORT
	MOVE T4,MB		;POINT TO THE MESSAGE BLOCK WE ALLOCATED
	CALL NSP		;ASK NSP TO OPEN A PORT
	SKIPG T1		;Did we get a port?
	 SCERR %NERES,NSFEAD,<Resource error>

;Now we have an open port to NSP, let's try to make it active.

	STOR T1,SLPID,(SL)	;STORE THE NSPpid

	MOVE T1,MB		;LET'S REUSE THE MESSAGE BLOCK
	MOVEI T2,^D200		;GET ENOUGH ROOM FOR THE CONNECT MESSAGE
	CALL DNMINI		;MAKE THE MESSAGE BLOCK LIKE NEW
	 SCERR %NEALF,NSFEAD,<Allocation failure>

	XMOVEI T1,UD.MSD(MB)	;POINT TO USER DATA PORTION OF MESSAGE
	CALL DNPINI		;INITIALIZE FOR PUTTING BYTES IN MESSAGE

	LOAD T1,SACBP,(SA)	;POINT TO THE CONNECT BLOCK
	CALL BLDCTX		;BUILD THE CONNECT TEXT IN THE MESSAGE
	 SCERR %NECFE,NSFEAD,<Connect Block format error>

	LOAD T1,SLPID,(SL)	;GET THE NSPpid BACK AGAIN
	MOVX T3,NV.ACT		;FUNCTION IS ENTER ACTIVE
	MOVE T4,MB		;POINT TO MB WITH CONNECT DATA
	CALL NSP		;CALL NSP TO DO THE FUNCTION

	CALL STRTMR		;START THE CONNECT TIMERS
	RETSKP			;GIVE A GOOD RETURN

;The following are the error returns that free up things that we have
;allocated before we got the error.

;Free an arg block, a message block and a SLB. Report "illegal function".

NSFEAE:	MOVE T1,P1		;POINT TO THE ARGUMENT BLOCK
	CALL DNFWDS		; AND GO AND FREE UP THE WORDS
	MOVEI T1,%NEILF		;"Illegal function"
	JRST NSFEAD		;GO AND FREE MESSAGE BLOCK

;Free a message block and a SLB.

NSFEAD:	SAVEAC T1		;SAVE THE ERROR CODE
	CALL FREMSG		;FREE THE MESSAGE BLOCK POINTED TO BY MB
	JRST NSFEC1		;GO AND FREE THE SLB

;Free an SLB.

NSFEAC:	SAVEAC T1		;SAVE THE ERROR CODE
NSFEC1:	SETONE SLFSL,(SL)	;FREE THE SLB AFTER THE "AFTER" PROCESSING
	SETZRO SAACH,(SA)	;TELL USER THERE'S NO CHANNEL OPEN
	CALLRET SCTRFJ		;REQUEST SECOND SERVICE TO FREE SLB
	SUBTTL	NSP. -- NSFEP - Enter Passive State

;NSFEP - Enter Passive State
;
; Call:
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFEP function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to connect-block

NSFEP:	TRACE SC,<Performing Enter Passive (.NSFEP)>
	CALL MAKSLB		;MAKE A SLB
	 SCERR %NEALF,RTN,<Allocation failure>
	STOR T1,SAACH,(SA)	;STORE THE CHANNEL FOR USER

; Get the Object type and if format 0, store the source (SLSOB) object type
; in the SLB.

	LOAD T3,SACBP,(SA)	;GET POINTER TO CONNECT BLOCK
	XMOVEI T1,CB.SRC(T3)	;GET THE POINTER TO THE SOURCE PDB
	LOAD T2,PBFOR,(T1)	;GET THE FORMAT TYPE
	 IFE. T2		;IF FRM.0, IT HAS AN OBJECT TYPE
	  LOAD T2,PBOBJ,(T1)	;GET THE OBJECT TYPE FROM THE PDB
	  STOR T2,SLSOB,(SL)	;STORE IT IN THE SLB
	 ENDIF.
	XMOVEI T1,CB.DST(T3)	;GET THE POINTER TO THE DEST PDB
	LOAD T2,PBFOR,(T1)	;GET THE FORMAT TYPE
	CAIE T2,FRM.2		;FORMAT TYPE WITH PPN?
	 IFSKP.
	  CALL CHKPPN		;AND VERIFY THE PPN
	   SCERR %NEPRV,NSFEPC,<No privs to use bad PPN>
	 ELSE.
	 IFN FRM.0,<PRINTX ?FRM.0 MUST BE EQUAL TO 0>
	 ANDE. T2		;IF OBJECT TYPE, STORE THE NUMBER
	  LOAD T3,SLSJB,(SL)	;GET POINTER TO SJB
	  LOAD T2,PBOBJ,(T1)	;GET OBJECT TYPE WE CLAIM TO BE
   IFN FTOPS20,<
	  MOVE T1,CAPENB 	;ARE WE PRIVED
	  TXNN T1,SC%WHL!SC%OPR
   > ; END FTOPS20
   IFN FTOPS10,<
	  TMNN SJPRV,(T3)	;ARE WE PRIVED
   > ; END FTOPS10
	  CAIL T2,^D128		;IS OBJECT TYPE PRIVED?
	  TRNA			;WE ARE O.K.
	   SCERR %NEPRV,NSFEPC,<No privs for this object type>
	  STOR T2,SLSOB,(SL)	;SAVE IT FOR SYSDPY AND THE LIKE
	 ENDIF.

;Now allocate another block for the internal connect block, so that the
;original goes back to SCLINK's caller.

NSFEP2:	LOAD T1,SAKCB,(SA)      ;GET USER'S 'KEEP CONNECT BLOCK' FLAG
	STOR T1,SLKCB,(SL)      ;REMEMBER IT

	MOVX T1,CB.LEN		;LENGTH OF A INTERNAL CONNECT BLOCK
	CALL DNGWDS		;GET THE WORDS
	 SCERR %NEALF,NSFEPC,<Allocation failure>
	STOR T1,SLCBP,(SL)	;STORE THE POINTER IN THE SLB
	MOVE T2,T1		;SET IT UP IN T2 ALSO FOR DNCPYW

	LOAD T1,SACBP,(SA)	;POINT TO THE CONNECT BLOCK
	MOVX T3,CB.LEN		;LENGTH OF THE CONNECT BLOCK
	CALL DNCPYW		;COPY THE WHOLE THING

	NEWSTATE CW		;NEW STATE IS CONNECT WAIT
	SETONE SLPAS,(SL)	;Flag that this link is passive
	RETSKP			;RETURN WITH SUCCESS

;Here to free up the SLB after an error occured.

NSFEPC:	SAVEAC T1		;SAVE THE ERROR CODE
	SETONE SLFSL,(SL)	;SAY THAT WE SHOULD FREE THE SLB
	CALLRET SCTRFJ		;REQUEST SECOND SERVICE TO FREE SLB
	SUBTTL	NSP. -- NSFRI - Read Connect Information

;NSFRI - Read Connect Information
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFRI function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to connect-block
;	.NSAA2/ Maximum segment size
;	.NSAA3/ Flow control mode
;
;This function can be called in any state.  If we were asked to keep the
;connect in the Enter Active or Enter Passive, then .NSFRI will be able to
;return the connect block at any time, and the user data will have been
;updated with the most recently received user data from CC or DI messages.

NSFRI:	TRACE SC,<Performing Read Connect Info (.NSFRI)>
	SETZRO SASBP,(SA)	;ZERO THE ARGUMENTS IN
	SETZRO SAAA2,(SA)	; CASE WE CAN'T
	SETZRO SAAA3,(SA)	; GET THE RIGHT ANSWER
				;CDMCBP WILL BUILD A CONNECT BLOCK IF NEEDED
	CALL CDMCBP		;COPY ANY MESSAGE DATA TO CONNECT BLOCK
	  RET			;PROPOGATE ERROR RETURN, CODE IN T1
	TMNN SLCBP,(SL)		;ANY DIS/CONNECT DATA TO READ?
	  SCERR %NENCD,RTN,<No dis/connect data to read>


	JE SLKCB,(SL),NSFRI1    ;USER WANT US TO KEEP CONNECT BLOCK

;Here if we are to keep connect block, copy it for user

	MOVX T1,CB.LEN		;YES, LENGTH OF AN INTERNAL CONNECT BLOCK
	CALL DNGWDS		;GET NON-ZEROED WORDS
	  SCERR %NEALF,RTN,<Allocation Failure>
	STOR T1,SACBP,(SA)	;STORE THE POINTER FOR SCJSYS
	MOVE T2,T1		;SET UP DEST POINTER FOR DNCPYW
	LOAD T1,SLCBP,(SL)	;GET THE INTERNAL CB POINTED TO IN THE SLB
	MOVX T3,CB.LEN		;COUNT OF WORDS TO COPY
	CALL DNCPYW		;COPY CONNECT BLOCK FOR USER
	JRST NSFRI2             ;CONTINUE

;Here if we are not to keep the connect block, just hand it over

NSFRI1:	LOAD T1,SLCBP,(SL)	;GET THE INTERNAL CB POINTED TO IN THE SLB
	SETZRO SLCBP,(SL)	;SCLINK NO LONGER OWNS THE CONNECT BLOCK
	SKIPN T1		;DO WE STILL HAVE A CONNECT BLOCK?
	  SCERR %NEWRS,RTN,<Function called in wrong state>
	STOR T1,SACBP,(SA)	;STORE THE POINTER FOR SCMUUO

NSFRI2:	LOAD P1,SANAG,(SA)	;GET THE NUMBER OF ARGUMENTS
	CAIG P1,.NSAA2		;DID HE WANT SOME OTHER ARGUMENTS?
	RETSKP			;NO, JUST RETURN

	LOAD T1,SLSIZ,(SL)	;YES, GET IT FROM THE SLB
	STOR T1,SAAA2,(SA)	;STORE REASON CODE,,SEGMENT SIZE

	CAIG P1,.NSAA3		;HOW ABOUT THE FLOW CONTROL OPTION?
	RETSKP			;NO, JUST RETURN

	CALL RETFLW		;GET THE RFL,,XFL IN T1
	STOR T1,SAAA3,(SA)	; AND STORE IT IN THE ARG BLOCK
	RETSKP			;RETURN SUCCESS
	SUBTTL	NSP. -- NSFAC - Accept the Connect

;NSFAC - Accept the Connect
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFAC function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to user-data (which is the DATA-CTL optional response)
;	.NSAA2/ Maximum segment size
;	.NSAA3/ Flow control mode

NSFAC:	TRACE SC,<Performing Accept Connect (.NSFAC)>

	CALL STPTMR		;STOP THE CONNECT INITIATE TIMER AND FIX COUNTS

	LOAD P1,SANAG,(SA)	;GET THE NUMBER OF ARGS USER SPECIFIED
	CAIG P1,.NSAA2		;DID THE USER SPECIFIY THE SEGMENT SIZE?
	JRST NSFAC1		;NO

	LOAD T2,SLSIZ,(SL)	;Get the segment size that we and remote
				; decided upon.
	OPSTR <SKIPLE T1,>,SAAA2,(SA) ;Get the segment size user wants
	CAML T1,T2		;User's smaller?
	IFSKP. <STOR T1,SLSIZ,(SL)> ; -yes, use the user's value

	CAIG P1,.NSAA3		;DID USER SPECIFY THE FLOW CONTROL MODE?
	JRST NSFAC1		;NO

	LOAD T1,SAAA3,(SA)	;GET THE FLOW CONTROL MODE
	JUMPLE T1,NSFAC1	;DIDN'T SPECIFY IT, USE THE DEFAULT
	CAIL T1,NSF.C0		;RANGE CHECK THE FLOW
	CAILE T1,NSF.MX		; CONTROL MODE
	SCERR %NEIFM,RTN,<Illegal flow control mode>
	SUBI T1,1		;TRANSLATE FROM NSP.'S VERSION OF FLOW CONTROL
				; VALUE TO NSP'S
	STOR T1,SLRFL,(SL)	;STORE THE FLOW CONTROL MODE
;Allocate two message blocks, one for the accept arguments and one to
;send out data requests with.

NSFAC1:	MOVX T1,^D20		;GET ENOUGH ROOM FOR DATA-CTL
	CALL DNGMSG		;GET A MESSAGE BLOCK
	 SCERR %NEALF,RTN,<Allocation failure>
	MOVE MB,T1		;MAKE MB POINT TO THE MESSAGE BLOCK

	SETZ T1,		;DON'T NEED ANY USER DATA FOR DRQ MB
	CALL DNGMSG		;GET THE MESSAGE BLOCK
	 JRST [CALL FREMSG	;FREE THE MESSAGE BLOCK WE JUST ALLOCATED
	       SCERR %NEALF,RTN,<Allocation failure>] ; AND GIVE A FAILURE TO USER
	MOVE P2,T1		;SAVE THE POINTER TO THE DRQ MB

;Copy the user's DATA-CTL (in the user data area) from the user area to
;the message block.

	XMOVEI T1,UD.MSD(MB)	;WE WANT TO WRITE DATA-CTL INTO USER DATA
	CALL DNPINI		;INITIALIZE THE MSD

	LOAD T1,SASBP,(SA)	;GET THE POINTER TO SBLOCK
	CALL CPYS2M		;COPY THE DATA-CTL TO THE MESSAGE

;Now set up the accept argument block for NSP.

	MOVX T1,AA.LEN		;LENGTH OF THE ACCEPT ARGUMENT BLOCK
	CALL DNGWDZ		;GET THAT MANY WORDS
	 SCERR %NEALF,NSFACD,<Allocation failure>

	LOAD T2,SLSLB,(SL)	;THE SCB ID FOR NEW PORT
	STOR T2,AASCB,(T1)

	LOAD T2,SLPID,(SL)	;NSP'S PORT IDENTIFIER
	STOR T2,AAPID,(T1)

	LOAD T3,SLRFL,(SL)	;RECEIVE FLOW CONTROL TYPE
	STOR T3,AAFLO,(T1)
	LOAD T2,SLGOL,(SL)	;GET THE DATA REQUEST GOAL
	CAIN T3,FCM.MG		;ARE WE IN MESSAGE FLOW CONTROL?
	CAIL T2,1		;YES, IS OUR GOAL SET TO LESS THAN ONE?
	TRNA			;NO, DON'T WORRY ABOUT IT
	MOVEI T2,1		;IN MSG FLOW MODE WE GOAL OF AT LEAST 1
	STOR T2,SLGOL,(SL)	;REMEMBER THE GOAL WE DECIDED ON
	STOR T2,AAGOL,(T1)	;TELL LLINKS ABOUT THE GOAL TOO

	LOAD T2,SLSIZ,(SL)	;MAX BYTES ALLOWED IN A SEGMENT
	STOR T2,AASIZ,(T1)

	MOVE T2,[XCDSEC,,SCTL]	;OUT ENTRY VECTOR ADDRESS
	STOR T2,AASCV,(T1)	; SO HE KNOWS WHERE TO CALL US

	MOVX T2,AA.LEN		;LENGTH OF THE ARG BLOCK
	MOVX T3,NV.ACC		;FUNCTION IS ACCEPT THE CONNECT
	MOVE T4,MB		;SET UP T4 WITH MB POINTER
				;T1 IS SET UP ALREADY WITH ARG BLOCK ADDRESS
	CALL NSP		;PERFORM THE FUNCTION

	NEWSTATE RN		;NEWSTATE IS CONNECT INITIATE
	SETONE SLCCB,(SL)	;HAVE TO CHECK CONNECT BLOCK NOW

;Now let's send the job's input quota worth of DRQs.

	MOVE MB,P2		;POINT TO THE MB WE ALLOCATED BEFORE

	TMNE SLPH2,(SL)		;SHOULD WE BE CONSERVATIVE WITH THIS FELLOW?
	RETSKP			;YES, WE BETTER LEAVE NICELY

	LOAD T1,SLINQ,(SL)	;GET THE INPUT QUOTA
	STOR T1,QACNT,+T2	; USE THAT AS THE DATA REQUEST COUNT
	STOR T1,SSRDO,+SL.NSL(SL) ;WE KEEP COUNT OF OUTGOING DATA REQUESTS

	TXZ T2,MBOTH		;ON THE NORMAL SUBLINK
	CALL SNDDRM		;SEND THE DATA REQUESTS
	 SCERR %NERES,RTN,<Failed to get memory>
	RETSKP			;RETURN SUCCESS

;Here on a error.

NSFACD:	SAVEAC T1		;SAVE THE ERROR CODE
	CALLRET FREMSG		;FREE THE MESSAGE BLOCK POINTED TO BY MB
	SUBTTL	NSP. -- NSFRJ - Reject the Connect

;NSFRJ - Reject the Connect
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFRJ function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to user-data (which is the DATA-CTL optional response)
;	.NSAA2/ Reason code or zero to take default 'rejected by object'

NSFRJ:	TRACE SC,<Performing Reject the Connect (.NSFRJ)>

	CALL STPTMR		;STOP THE CONNECT INITIATE TIMER AND FIX COUNTS

	MOVX T1,^D20		;GET ENOUGH ROOM FOR DATA-CTL
	CALL DNGMSG		;GET A MESSAGE BLOCK
	 SCERR %NEALF,RTN,<Allocation failure>
	MOVE MB,T1		;MAKE MB POINT TO THE MESSAGE BLOCK

	XMOVEI T1,UD.MSD(MB)	;WE WANT TO WRITE DATA-CTL INTO USER DATA
	CALL DNPINI		;INITIALIZE THE MSD

;Put in the reason code (RSNRBO - Rejected by object).

	LOAD T2,SANAG,(SA)	;GET NUMBER OF ARGUMENTS USER OFFERED
	OPSTR <SKIPLE T1,>,SAAA2,(SA) ;USER SPECIFY A REASON?
	CAIGE T2,4		;YES, SANAG INCLUDE SAAA2?
	MOVX T1,RSNRBO		;NO, REJECTED BY THE OBJECT
	CALL DNP2BY		;PUT REASON IN THE MESSAGE

;Copy the user's DATA-CTL (in the user data area) from the user area to the
;message block.

	LOAD T1,SASBP,(SA)	;GET THE POINTER TO SBLOCK
	CALL CPYS2M		;COPY THE DATA-CTL TO THE MESSAGE

	LOAD T1,SLPID,(SL)	;GET THE NSPpid FOR NSP
	MOVX T3,NV.REJ		;FUNCTION IS REJECT THE CONNECT
	MOVE T4,MB		;SET UP T4 WITH MB POINTER
				;T1 IS SET UP ALREADY WITH ARG BLOCK ADDRESS
	CALL NSP		;PERFORM THE FUNCTION

;There is no need to change the state of the link here, as we are
;about to forget about the link.  The user may possibly clean out
;his database about this link and then get the state change interrupt,
;confusing him.

	SETONE SLFSL,(SL)	;SAY THAT WE SHOULD FREE THE SLB LATER
	CALL SCTRFJ		;REQUEST SECOND SERVICE TO FREE SLB
	RETSKP			;RETURN NICELY
	SUBTTL	NSP. -- NSFRC - Read Connect Confirm Information

;NSFRC - Read Connect Confirm Information
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFRC function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to string block to receive data
;	.NSAA2/ Segment size
;	.NSAA3/ Flow control mode

NSFRC:	TRACE SC,<Performing Read Connect Confirm Data (.NSFRC)>
	SETZRO SASBP,(SA)	;ZERO THE ARGUMENTS IN
	SETZRO SAAA2,(SA)	; CASE WE CAN'T
	SETZRO SAAA3,(SA)	; GET THE RIGHT ANSWER
				;CDMCBP WILL BUILD A CONNECT BLOCK IF NEEDED
	CALL CDMCBP		;COPY ANY MESSAGE DATA TO CONNECT BLOCK
	  RET			;PROPOGATE ERROR RETURN, CODE IN T1
	TMNN SLCBP,(SL)		;DO WE HAVE ANY CONNECT DATA
	SCERR %NENCD,RTN,<No dis/connect data to read>

	MOVX T1,SB.LEN		;GET SOME WORDS FOR THE DATA-CTL FIELD
	CALL DNGWDS		;GET THEM FROM MEMORY MANAGER
	 SCERR %NEALF,RTN,<Allocation failure>
	MOVE P2,T1		;POINTER TO STRING BLOCK
	STOR P2,SASBP,(SA)	;PUT POINTER IN ARG BLOCK FOR SCMUUO

	MOVEI T1,SB.LEN		;LENGTH OF STRING BLOCK IN WORDS
	STOR T1,SBWDS,(P2)	;STORE LENGTH IN WORDS
	LOAD P1,SLCBP,(SL)	;GET PTR TO CONNECT BLOCK
	LOAD T3,CBCCT,(P1)	;LENGTH OF USER DATA
	STOR T3,SBCNT,(P2)	;STORE IN STRING BLOCK
	ADDI T3,3		;ROUND UP
	ASH T3,-2		;CONVERT BYTES TO WORDS
	XMOVEI T1,CB.UDA(P1)	;POINTER TO CONNECT BLOCK'S USER DATA
	XMOVEI T2,SB.DAT(P2)	;POINTER TO STRING BLOCK'S DATA SECTION
	CALL DNCPYW		;(T1,T2,T3)COPY USER DATA TO STRING BLOCK

	JN SLKCB,(SL),NSFRC1	;JUMP IF WE'RE TO KEEP CONNECT BLOCK
	MOVE T1,P1		;POINTER TO CONNECT BLOCK
	CALL DNFWDS		;DEALLOCATE MSG BLK WHICH HAD CONNECT DATA
	SETZRO SLCBP,(SL)	; AND REMEMBER WE'VE USED IT

NSFRC1:	LOAD T1,SLSIZ,(SL)	;GET THE SEGMENT SIZE
	STOR T1,SAAA2,(SA)	;STORE IT IN THE ARGUMENT WORD

	CALL RETFLW		;GET THE FLOW CONTROL TYPES
	STOR T1,SAAA3,(SA)	;STORE IN THE ARG WORD
	RETSKP			;RETURN SUCCESS
	SUBTTL	NSP. -- NSFSD - Synchronous Disconnect

;NSFSD - Synchronous Disconnect
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFSD function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to user-data (which is the optional disconnect data)
;	.NSAA2/ Reason code or zero for 'Disconnected by Object'

NSFSD:	TRACE SC,<Performing Synchronous Disconnect (.NSFSD)>
	TMNE SLOTM,(SL)		;IS THERE A PARTIALLY FILLED OUTPUT MESSAGE?
	  SCERR %NEWRS,RTN,<Function called in wrong state>

	MOVX T1,^D20		;GET ENOUGH ROOM FOR DATA-CTL
	CALL DNGMSG		;GET A MESSAGE BLOCK
	 SCERR %NEALF,RTN,<Allocation failure>
	MOVE MB,T1		;MAKE MB POINT TO THE MESSAGE BLOCK

;Copy the user's DATA-CTL (in the user data area) from the user area to the
;message block.

	XMOVEI T1,UD.MSD(MB)	;WE WANT TO WRITE DATA-CTL INTO USER DATA
	CALL DNPINI		;INITIALIZE THE MSD

	LOAD T2,SANAG,(SA)	;GET NUMBER OF ARGUMENTS USER OFFERED
	OPSTR <SKIPLE T1,>,SAAA2,(SA) ;USER SPECIFY A REASON?
	CAIGE T2,4		;YES, SANAG INCLUDE SAAA2?
	MOVX T1,RSNDBO		;NO, REASON IS DISCONNECTED BY OBJECT
	CALL DNP2BY		;PUT REASON (2 BYTES) IN MESSAGE

	LOAD T1,SASBP,(SA)	;GET THE POINTER TO SBLOCK
	CALL CPYS2M		;COPY THE DATA-CTL TO THE MESSAGE
;Now call NSP to do the disconnect.

	LOAD T1,SLPID,(SL)	;GET THE NSPpid FOR NSP
	MOVX T3,NV.DSC		;FUNCTION IS DISCONNECT
	MOVE T4,MB		;SET UP T4 WITH MB POINTER
	CALL NSP		;PERFORM THE FUNCTION

	NEWSTATE DS		;NEWSTATE IS DISCONNECT SENT
	RETSKP			;DID OK
	SUBTTL	NSP. -- NSFAB - Abort and Release

;NSFAB - Abort and Release
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFAB function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to user-data (which is the optional disconnect data)
;	.NSAA2/ Reason code or zero for 'Aborted by Object'

NSFAB:	TRACE SC,<Performing Abort and Release (.NSFAB)>

	MOVX T1,^D20		;GET ENOUGH ROOM FOR DATA-CTL
	CALL DNGMSG		;GET A MESSAGE BLOCK
	 SCERR %NEALF,RTN,<Allocation failure>
	MOVE MB,T1		;MAKE MB POINT TO THE MESSAGE BLOCK

;Copy the user's DATA-CTL (in the user data area) from the user area to the
;message block.

	XMOVEI T1,UD.MSD(MB)	;WE WANT TO WRITE DATA-CTL INTO USER DATA
	CALL DNPINI		;INITIALIZE THE MSD

	LOAD T2,SANAG,(SA)	;GET NUMBER OF ARGUMENTS USER OFFERED
	OPSTR <SKIPLE T1,>,SAAA2,(SA) ;USER SPECIFY A REASON?
	CAIGE T2,4		;YES, SANAG INCLUDE SAAA2?
	MOVX T1,RSNABO		;NO, REASON IS ABORTED BY OBJECT
	CALL DNP2BY		;PUT THE TWO BYTES OF REASON CODE IN MSG

	LOAD T1,SASBP,(SA)	;GET THE POINTER TO SBLOCK
	CALL CPYS2M		;COPY THE DATA-CTL TO THE MESSAGE

	LOAD T1,SLPID,(SL)	;GET THE NSPpid FOR NSP
	MOVX T3,NV.ABO		;FUNCTION IS DISCONNECT
	MOVE T4,MB		;SET UP T4 WITH MB POINTER
	CALL NSP		;PERFORM THE FUNCTION

	NEWSTATE DS		;NEWSTATE IS DISCONNECT SENT
	CALL STPTMR		;STOP THE CI TIMER IN CASE IT'S GOING
	SETONE SLABO,(SL)	;WE HAVE TO CLOSE WHEN LINK IS FINISHED
	RETSKP			;RETURN WITH GOODNESS
	SUBTTL	NSP. -- NSFRD - Read Disconnect Data

;NSFRD - Read Disconnect Data
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFRD function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to Connect-Block
;	.NSAA2/ The Disconnect Reason

NSFRD:	TRACE SC,<Performing Read Disconnect Data (.NSFRD)>
	SETZRO SASBP,(SA)	;ZERO THE ARGUMENTS IN
	SETZRO SAAA2,(SA)	; CASE WE CAN'T GET THE RIGHT ANSWER
				;CDMCBP WILL BUILD A CONNECT BLOCK IF NEEDED
	CALL CDMCBP		;COPY ANY MESSAGE DATA TO CONNECT BLOCK
	  RET			;PROPOGATE ERROR RETURN, CODE IN T1
	OPSTR <SKIPN P1,>,SLCBP,(SL) ;GET PTR TO DIS/CONNECT BLOCK
	  SCERR %NENCD,RTN,<No dis/connect data to read>

	MOVX T1,SB.LEN		;GET SOME WORDS FOR THE DATA-CTL FIELD
	CALL DNGWDS		;GET THEM FROM MEMORY MANAGER
	  SCERR %NEALF,RTN,<Allocation failure>
	MOVE P2,T1		;POINTER TO STRING BLOCK
	STOR P2,SASBP,(SA)	;PUT POINTER IN ARG BLOCK FOR SCMUUO

	MOVEI T1,SB.LEN		;LENGTH OF STRING BLOCK IN WORDS
	STOR T1,SBWDS,(P2)	;STORE LENGTH IN WORDS
	LOAD T3,CBCCT,(P1)	;LENGTH OF USER DATA IN CONNECT BLOCK
	STOR T3,SBCNT,(P2)	;STORE IN STRING BLOCK
	ADDI T3,3		;ROUND UP TO NEAREST WORD
	ASH T3,-2		;CONVERT BYTES TO WORDS
	XMOVEI T1,CB.UDA(P1)	;POINTER TO CONNECT BLOCK'S USER DATA
	XMOVEI T2,SB.DAT(P2)	;POINTER TO STRING BLOCK'S DATA SECTION
	CALL DNCPYW		;(T1,T2,T3)COPY USER DATA TO STRING BLOCK

	LOAD T1,SLRSN,(SL)	;GET THE DISCONNECT REASON CODE
	STOR T1,SAAA2,(SA)	;RETURN THE REASON CODE
	RETSKP			; AND GIVE THE GOOD RETURN
	SUBTTL	NSP. -- NSFRL - Release the Channel

;NSFRL - Release the Channel
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFRL function, the function dependant arguments are:
;
;	.NSAA1/ Ptr to user-data (which is the optional disconnect data)

NSFRL:	TRACE SC,<Performing Release (.NSFRL)>
	SETZ MB,		;WE HAVE NO MESSAGE BLOCK, NO Q'D INTERLOCKS
	CALL RLSLNK		;(MB)RESET THE LINK (DO THE CLOSE)
	RETSKP			;RETURN SUCCESS
	SUBTTL	NSP. -- NSFRS - Read the Channel Status

;NSFRS - Read the Channel Status
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFRS function, the function dependant arguments are:
;
;	.NSAA1/ State
;	.NSAA2/ Segment Size
;	.NSAA3/ Flow control type

NSFRS:	TRACE SC,<Performing Read Status (.NSFRS)>
	LOAD P1,SANAG,(SA)	;GET NUMBER OF ARGUMENTS USER WANTED

	CAIG P1,.NSAA1		;DOES HE WANT TO KNOW SEGMENT SIZE?
	RETSKP			;NO, JUST RETURN

	LOAD T1,SLSIZ,(SL)	;GET THE MAX SEG SIZE WE'RE USING
	STOR T1,SAAA1,(SA)	;STORE IT IN SAB ARGUMENT BLOCK

	CAIG P1,.NSAA2		;DOES HE WANT TO KNOW FLOW CONTROL TYPE?
	RETSKP			;NO, JUST RETURN

	CALL RETFLW		;RETURN THE FLOW CONTROL TYPES
	STOR T1,SAAA2,(SA)	;STORE IT IN THE ARGUEMENT BLOCK
	RETSKP			; AND RETURN SUCCESSFULLY
	SUBTTL	NSP. -- NSFIS - Send Interrupt Data

;NSFIS - Send Interrupt Data
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block with user arguments stored
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;	.NSAA1/ Ptr to user-data (which is the interrupt data to be sent)

NSFIS:	TRACE SC,<Sending Interrupt Data (.NSFIS)>

	TMNE SLCCB,(SL)		;SHOULD WE CHECK CONNECT BLOCK?
	CALL FRECBP		;YES, GET RID OF CONNECT DATA IF WE CAN

NSFIS1:	OPSTR <SKIPN P1,>,SASBP,(SA) ;POINT TO THE SBLOCK POINTER
	 SCERR %NEABE,RTN,<No Sblock supplied>
	LOAD T1,SBCNT,(P1)	;GET THE COUNT OF BYTES
	SKIPL T1		;RANGE CHECK THE LENGTH
	CAILE T1,^D16		;IS IT WITHIN THE SPEC?
	 SCERR %NEIDL,RTN,<Interrupt data too long>

;...
;...
;Allocate the message block.

	MOVX T1,^D16		;GET 16 BYTES (MIGHT AS WELL GO ALL THE WAY
				; SINCE THEY'RE IN THE MB ALREADY)
	CALL DNGMSG		;GET THE MESSAGE BLOCK
	 SCERR %NEALF,NSFIS2,<Allocation failure>
	MOVE MB,T1		;SET UP MB
	XMOVEI T1,UD.MSD(MB)	;SET UP TO PUT IN BYTES
	CALL DNPINI		; IN THE USER SEGMENT

	D36OFF			;INTERLOCK AGAINST SCTRIB CALL FROM LLINKS
	JE SLOTU,(SL),NSFISO	;JUMP IF THIS WILL BE ONLY OUTPUT BUFFER NOW
	MOVE T1,DCNRSB		;GET # NOW RESERVED
	SKIPN DCNCON		;IS SYSTEM CONGESTED?
	CAML T1,DCNTSB		;NO, IS THERE ROOM FOR ONE MORE?
	JRST NSFISF		;NO, RETURN FAILURE
	AOS T1,DCNRSB		;ONE MORE BUFFER RESERVED
	CAMLE T1,DCNRHT		;NEW HIGH TIDE?
	MOVEM T1,DCNRHT		;YES, RECORD IT
	JRST NSFISA		;GOT ROOM, GO ALLOCATE

NSFISF:				;NO ROOM FOR THIS LOGICAL LINK
	SETOM DCNCON		;CALL FOR CHKSTS WHEN CONGESTION FREES
	D36ON			;END CRITICAL SECTION
	AOS DCNROF		;INCREMENT OUTPUT RESERVATION FAILURES
	CALL FREMSG		;RETURN THE MESSAGE BLOCK
	JRST NSFIS2		;SUCCEEDED IN SENDING ZERO BYTES (CONGESTION)

NSFISO:	JN SLINU,(SL),NSFISA	;JUMP IF INPUT BUFF ALLOC, THAT RESERVES 1 OUT
	MOVE T1,DCNRSB		;GET # ALREADY RESERVED
	ADDI T1,2		;WE INTEND TO RESERVE 2 MORE (1 IN + 1 OUT)
	CAMLE T1,DCNTSB		;WILL THAT FIT IN TOTAL?
	JRST NSFISF		;NO, RETURN FAILURE
	MOVEM T1,DCNRSB		;YES, D36OFF MAKES THIS STORE OK
	CAMLE T1,DCNRHT		;NEW HIGH TIDE?
	MOVEM T1,DCNRHT		;YES, RECORD IT
NSFISA:	INCR SLOTU,(SL)		;INCREMENT NUMBER OF OUTPUT BUFFERS IN USE
	D36ON			;END OF CRITICAL SECTION

;Here to copy the bytes from the user's buffer into the message.

	MOVE T1,P1		;POINT TO THE SBLOCK
	CALL CPYS2I		;(T1)COPY INTERRUPT DATA FROM STRING BLOCK
				; TO MESSAGE
	SETONE <MBBOM,MBEOM>,(MB) ;INTERRUPT MESSAGES ARE ONE SEGMENT

;Update counts
	INCR SLPKS,(SL)		;# of 'packets' sent
	LOAD T1,SBCNT,(P1)	;Get # of bytes in string block
	OPSTRM <ADDM T1,>,SLBYS,(SL) ; and update count in SLB

;Now send the message to NSP.

	LOAD T1,SLPID,(SL)	;GET THE NSPpid
	SETZ T2,		;T2 HAS THE DA STRUCTURE
	SETONE MBOTH,(MB)	;SEND ON THE OTHER SUB-LINK
	MOVX T3,NV.SEG		;FUNCTION IS SEND A SEGMENT
	MOVE T4,MB		;POINT TO THE MESSAGE BLOCK
	CALL NSP		;SEND INTERRUPT MESSAGE

	OPSTRM <SOS>,SSXDO,+SL.OSL(SL) ;DECR COUNT OF XMT DATA REQUESTS
	SETZRO SBCNT,(P1)	;TELL ASYNCHRONOUS USER WE SENT THE DATA
	RETSKP			; AND RETURN SUCCESSFULLY


;Here when DNGMSG or DCNRSB fails due to congestion.

NSFIS2:	LOAD T1,SLSST,(SL)	;GET LINK STATUS HALF-WORD
	TXZ T1,NSNDR ! NSIDR	;NEITHER TYPE OF OUTPUT IS OK NOW
	STOR T1,SLSST,(SL)	;STORE BACK SO WHEN CONGESTION IS FREED
	RET			; CHKSTS WILL WAKE USER
	SUBTTL	NSP. -- NSFIR - Receive Interrupt Data

;NSFIR - Receive Interrupt Data
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block with user arguments stored
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;	.NSAA1/ Ptr to interrupt data

NSFIR:	TRACE SC,<Receiving Interrupt Data (.NSFIR)>

	TMNE SLCCB,(SL)		;SHOULD WE CHECK CONNECT BLOCK?
	CALL FRECBP		;YES, GET RID OF CONNECT DATA IF WE CAN

	MOVX T1,SB.LEN		;GET SB TO STORE THE INTERRUPT DATA
	CALL DNGWDZ		; FROM MEMORY MANAGER
	  SCERR %NEALF,RTN,<Allocation failure>
	STOR T1,SASBP,(SA)	;STORE THE POINTER TO STRING BLOCK
	MOVX T2,SB.LEN		;LENGTH OF STRING BLOCK IN WORDS
	STOR T2,SBWDS,(T1)	;STORE LENGTH IN WORDS IN STRING BLOCK

	XMOVEI T1,SL.OSL(SL)	;POINT TO THE OTHER SUB-LINK'S AREA

	DEQUE MB,SS.INQ(T1),MB.NXT,NSFIR2 ;TRY TO DEQUE A MESSAGE ON INPUT Q

	MOVE T1,MB		;SET UP FOR DNLENG
	CALL DNLENG		;FIGURE OUT LENGTH OF MESSAGE
	OPSTRM <ADDM T1,>,SLBYR,(SL) ;Update count of bytes received
	CAILE T1,^D16		;IS IT TOO LONG?
	CALLRET SCEIVM		;--INVALID MESSAGE EVENT
	MOVE P1,T1		;SAVE THE LENGTH

;Now copy the data into the intermediate buffer.

	MOVE T1,MB		;POINT TO THE MESSAGE BLOCK
	CALL DNGINI		;SET UP TO GET IT

	LOAD T1,SASBP,(SA)	;POINT TO THE INTERNAL STRING BLOCK
	CALL CPYI2S		;COPY INTERRUPT DATA TO INTERNAL SB
	 CALLRET SCEIVM		;--INVALID MESSAGE EVENT

	OPSTRM <SOS T1,>,SSRDO,+SL.OSL(SL) ;DECREMENT DATA REQUEST COUNT
	OPSTR <CAML T1,>,SLINQ,(SL) ;IS NEW COUNT WITHIN QUOTA?
	JRST [CALL FREMSG	;NO, CAN'T SEND ANOTHER DRQ, FREE THE MB
	      RETSKP]		;AND RETURN TO SCMUUO

	OPSTRM <AOS>,SSRDO,+SL.OSL(SL) ;WE'RE SENDING OUT ONE DATA REQUEST
	TXO T2,MBOTH		;PUT IT ON THE OTHER SUB-LINK
	MOVX T1,1		;LET'S SEND OUT ONE DATA REQUEST
	CALL SNDDRM		;SEND THE DATA REQUESTS
	  SCERR %NEALF,RTN,<Allocation Failure>
	RETSKP			;RETURN SUCCESSFULLY

;Here when there was nothing on the input queue.

NSFIR2:	RETSKP			;SCTNA? WILL CALL CHKSTS FOR US
	SUBTTL	NSP. -- NSFDS - Send Normal Data

;NSFDS - Send Normal Data
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block with user arguments stored
;
; Return:
;	RET		;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP		;ALL IS WELL
;
; Uses: T1-T6
;
;For .NSFDS function, the function dependant arguments are:
;
;	SAAA1/ Byte count
;	SAAA2/ Byte pointer to data
;	SAAA3/ Optional second word of byte pointer

NSFDS:	TRACE SC,<Sending Normal Data (.NSFDS)>

	SETZRO SASAT,(SA)	;NOT YET SATISFIED THIS REQUEST.
	TMNE SLCCB,(SL)		;SHOULD WE CHECK CONNECT BLOCK?
	CALL FRECBP		;YES, GET RID OF CONNECT DATA IF WE CAN

;We only check the data requests here, not the quotas as well, because
;we may have a msg blk already allocated on the output queue.

	LOAD T1,SLXFL,(SL)	;GET FLOW CONTROL TYPE
	TMNN SSXDO,+SL.NSL(SL)	;ARE THERE ANY DRQS LEFT?
	JUMPN T1,RSKP		;NO, RETURN NICELY UNLESS NO FLOW CTL
IF1,<IFN FCM.NO,<PRINTX ?NSFDS expects FCM.NO to be zero; it isn't>>
;Fall through to next page
;Fall through from previous page

;Here we get a message block in which to put user's data and calculate
;how much of the user's data will fit.

	OPSTR <SKIPGE P1,>,SAAA1,(SA) ;GET USER'S BYTE COUNT
	  SCERR %NEADE,RTN,<Address Error> ;DON'T ALLOW NEGATIVE COUNT
	SETZ T1,		;ASSUME NO BYTES IN SAVED MESSAGE
	LOAD MB,SLOTM,(SL)	;ALREADY HAVE A MESSAGE?
	JUMPE MB,NSFDS0		;JUMP IF NOT
	SETZRO SLOTM,(SL)       ;YES, ITS NOT THERE ANY MORE
	XMOVEI T1,UD.MSD(MB)	;REINITIALIZE FOR PUTTING BYTES INTO
	CALL DNPINR		; A PARTIALLY FILLED MESSAGE BLOCK
	MOVE T1,MB		;SEE HOW MANY ARE BYTES ALREADY
	CALL DNLENG		; IN IT BY GETTING LENGTH
	ADD P1,T1		;ADD IN BYTES ALREADY IN MESSAGE
NSFDS0:	OPSTR <SUB P1,>,SLSIZ,(SL) ;SUBTRACT SEGMENT SIZE WE'RE USING

;P1 is now:
;	Negative if user data fits in segment with room to spare
;	Zero     if user data fits in segment exactly
;	Positive if user data is too big to fit in segment
;This fact is used throughout the rest of this routine.

	LOAD P2,SAAA1,(SA)	;ASSUME USER'S BUFFER WILL FIT IN SEGMENT
	JUMPLE P1,NSFDS1	;WILL IT FIT?
	LOAD P2,SLSIZ,(SL)	;NO, SEND ONE SEGMENT'S WORTH
	SUB P2,T1		;P2 NOW HAS NUMBER OF BYTES WE'LL COPY
NSFDS1:	JUMPN MB,NSFDS2		;JUMP IF WE JUST DEQUEUED A MESSAGE BLOCK

;Get a message block

	MOVE T1,P2		;NUMBER OF BYTES WE'LL COPY INTO FIRST SEGMENT
	TMNN SAEOM,(SA)		;IS THIS SEGMENT AN END OF MESSAGE?
	LOAD T1,SLSIZ,(SL)	;NO, ALLOCATE A WHOLE SEGMENT'S WORTH SINCE
				; USER WILL BE SENDING MORE IN THIS SEG
	CALL DNGMSG		;(T1)GET MESSAGE BLOCK
	  JRST NSFDS6		;SUCCEEDED IN SENDING ZERO BYTES (CONGESTION)
	MOVE MB,T1		;SET UP MB WITH MESSAGE BLOCK POINTER
	XMOVEI T1,UD.MSD(MB)	;INITIALIZE FOR PUTTING BYTES
	CALL DNPINI		; IN USER DATA PART OF MESSAGE BLOCK

	D36OFF			;INTERLOCK AGAINST SCTRIB CALL FROM LLINKS
	JE SLOTU,(SL),NSFDSO	;JUMP IF THIS WILL BE ONLY OUTPUT BUFFER NOW
	MOVE T1,DCNRSB		;GET # NOW RESERVED
	SKIPN DCNCON		;IS SYSTEM CONGESTED?
	CAML T1,DCNTSB		;NO, IS THERE ROOM FOR ONE MORE?
	JRST NSFDSF		;NO, RETURN FAILURE
	AOS T1,DCNRSB		;ONE MORE BUFFER RESERVED
	CAMLE T1,DCNRHT		;NEW HIGH TIDE?
	MOVEM T1,DCNRHT		;YES, RECORD IT
	JRST NSFDSA		;GOT ROOM, GO ALLOCATE

NSFDSF:				;NO ROOM FOR THIS LOGICAL LINK
	SETOM DCNCON		;CALL FOR CHKSTS WHEN CONGESTION FREES
	D36ON			;END CRITICAL SECTION
	AOS DCNROF		;INCREMENT OUTPUT RESERVATION FAILURES
	CALL FREMSG		;RETURN THE MESSAGE BLOCK
	JRST NSFDS6		;SUCCEEDED IN SENDING ZERO BYTES (CONGESTION)

NSFDSO:	JN SLINU,(SL),NSFDSA	;JUMP IF INPUT BUFF ALLOC, THAT RESERVES 1 OUT
	MOVE T1,DCNRSB		;GET # ALREADY RESERVED
	ADDI T1,2		;WE INTEND TO RESERVE 2 MORE (1 IN + 1 OUT)
	CAMLE T1,DCNTSB		;WILL THAT FIT IN TOTAL?
	JRST NSFDSF		;NO, RETURN FAILURE
	MOVEM T1,DCNRSB		;YES, D36OFF MAKES THIS STORE OK
	CAMLE T1,DCNRHT		;NEW HIGH TIDE?
	MOVEM T1,DCNRHT		;YES, RECORD IT
NSFDSA:	INCR SLOTU,(SL)		;INCREMENT NUMBER OF OUTPUT BUFFERS IN USE
	D36ON			;END OF CRITICAL SECTION
NSFDS2:

;Fall through to next page
;Fall through from previous page

;Message block is now ready to receive data and P2 has the count of
;data to copy into it.

	MOVE T1,P2		;PASS # OF BYTES TO COPY TO COPIER
	LOAD T2,SAAA2,(SA)	;GET BYTE POINTER
	LOAD T3,SAAA3,(SA)	;GET OPTIONAL SECOND WORD OF BYTE PTR

  IFN FTOPS10,<
	MOVE T4,[XCDSEC,,DNCU2M] ;Assume buffer in user space
	TMNE SAEVA,(SA)		;IS THAT TRUE?
  >
;Note: SAEVA must be set on TOPS-20 - user mode buffers are not supported
	MOVE T4,[XCDSEC,,DNCB2M] ; -no, copy from monitor buffer
	CALL 0(T4)		;COPY BUFFER TO THE MESSAGE BLOCK
	STOR T2,SAAA2,(SA)	;STORE UPDATED BYTE POINTER
	STOR T3,SAAA3,(SA)	;OPTIONAL SECOND WORD OF BYTE PTR

	SUB P2,T1		;T1 contains count of bytes we didn't copy
	SKIPLE T1		;Any bytes not sent?
	 MOVE P1,T1		;Yes, remember that
	MOVN T1,P2		;GET # OF BYTES WE COPIED
	OPSTRM <ADDM T1,>,SAAA1,(SA) ;FIX USER'S COUNT OF BYTES TO SEND

	TMNN SAEOM,(SA)		;IS THIS END OF MESSAGE
	SKIPL P1		; OR DID WE GET A WHOLE SEGMENTS WORTH?
	JRST NSFDS3		;YES, SEND SEGMENT TO NSP

;Here to save pointer to message so that the user can add data to it
;later.  Note that in this case we allocated a whole segment when we
;allocated the message block.

	SETONE SASAT,(SA)	;WE HAVE SATISFIED THIS NSFDS REQUEST
	STOR MB,SLOTM,(SL)	;SAVE PTR TO PARTIALLY FILLED OUTPUT MSG
	RETSKP			;RETURN SUCCESSFULLY
;Send segment to NSP.

;Set the BoM and EoM flags in the message block and remember whether
;or not this segment had EoM for figuring the next segment's BoM.

NSFDS3:	MOVE T1,SL.EOM(SL)	;GET "LAST SEGMENT'S EOM" WORD
	MOVX T2,MBBOM		;GET MESSAGE BLOCK'S BOM FLAG
	TXOE T1,SLEOM		;LAST SEGMENT HAVE EOM? (ASSUME THIS WILL)
	 IORM T2,MB.BOM(MB)	;YES, THIS MUST BE FIRST SEGMENT OF NEXT MSG
	JUMPG P1,NSFDS4		;NEVER EOM IF WE'RE SEGMENTING
	MOVX T2,MBEOM		;GET MESSAGE BLOCK'S EOM FLAG
	TMNN SAEOM,(SA)		;IS THIS EOM?
NSFDS4:	 TXZA T1,SLEOM		;NO, REMEMBER THAT FOR NEXT SEGMENT
	  IORM T2,MB.EOM(MB)	;YES, MARK MESSAGE BLOCK WITH EOM
	MOVEM T1,SL.EOM(SL)	;REMEMBER STATE OF EOM FLAG FOR NEXT TIME

;Update counts
	INCR SLPKS,(SL)		;# of 'packets' sent
	XMOVEI T1,UD.MSD(MB)	;Point to user MSD
	CALL DNSLNG		;Get # of bytes in it
	OPSTRM <ADDM T1,>,SLBYS,(SL) ; and update bytes sent

;Send the message

	LOAD T1,SLPID,(SL)	;GET NSPpid
	SETZ T2,		;DAOTH IS ZERO
	MOVX T3,NV.SEG		;FUNCTION IS SEND A SEGMENT
 	MOVE T4,MB		;POINT TO MESSAGE BLOCK
	CALL NSP		;DO SEND

;Determine whether or not to claim that we have satisfied the request

	MOVX T1,SASAT		;PICK UP SASAT FLAG
	SKIPG P1		;MORE DATA TO SEND FOR THIS REQUEST?
	IORM T1,SA.SAT(SA)	;NO, WE'VE SATISFIED THE REQUEST

;Update DRQs.

	LOAD T1,SLXFL,(SL)	;GET FLOW CONTROL TYPE WE ARE USING
	CAIN T1,FCM.SG		;IS IT SEGMENT?
	JRST NSFDS5		;YES, DECREMENT DATA REQUEST COUNT

	SKIPG P1		;DO WE HAVE ANY MORE SEGMENTS TO SEND LATER?
	CAIE T1,FCM.MG		;NO, SAEOM MUST BE SET IF WE'RE HERE
	RETSKP			;MORE SEGS TO GO OR NO FLOW CONTROL

NSFDS5:	OPSTRM <SOS>,SSXDO,+SL.NSL(SL) ;DECR COUNT OF XMT DATA REQUESTS
	RETSKP			; AND RETURN SUCCESS


;Here when DNGMSG fails due to congestion.  We have not yet changed
;SAAA1, so the user will see that we succeeded in sending zero bytes.

NSFDS6:	LOAD T1,SLSST,(SL)	;GET LINK STATUS HALF-WORD
	TXZ T1,NSNDR ! NSIDR	;NEITHER TYPE OF OUTPUT IS OK NOW
	STOR T1,SLSST,(SL)	;STORE BACK SO WHEN CONGESTION IS FREED
	RETSKP			; CHKSTS WILL WAKE USER
	SUBTTL	NSP. -- NSFDR - Receive Normal Data

;NSFDR - Receive Normal Data
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block with user arguments stored
;
; Return:
;	RET		;ON ERROR WITH T1 CONTAINING THE ERROR CODE
;	RETSKP		;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFDR function, the function dependant arguments are:
;
;	SAAA1/ Byte count
;	SAAA2/ Byte pointer
;	SAAA3/ Optional second word of byte pointer

NSFDR:	TRACE SC,<Receiving Normal Data (.NSFDR)>

	SETZRO SASAT,(SA)	;NOT YET SATISFIED THIS REQUEST.
	TMNE SLCCB,(SL)		;SHOULD WE CHECK CONNECT BLOCK?
	CALL FRECBP		;YES, GET RID OF CONNECT DATA IF WE CAN

	LOAD T1,SAFLG,(SA)	;GET THE FLAGS
	TXNN T1,SA%WAI		;IS WAIT OFF
	TXNN T1,SA%EOM		; AND EOM ON?
	TRNA			;NO, NO DEADLY EMBRACE PROBLEM
	SCERR %NEBCF,RTN,<Bad Combination of NS.xxx Flags>

	XMOVEI P2,SL.NSL(SL)	;POINT TO NORMAL SUBLINK AREA

NSFDR1:	LOAD MB,QHBEG,+SS.INQ(P2) ;GET THE FIRST THING ON THE INPUT QUEUE
	JUMPE MB,RSKP		;THERE'S NOTHING THERE,
				; SCTNI? WILL CALL CHKSTS
;Here with MB/ The Input Message
;	   P2/ SL.NSL(SL)

;The SASAT flag is set when a read is satisfied, that is, when we have
;read a message segment containing an End of Message flag. SASAT is
;used to tell whether to wake a user who has requested NS.WAIt. SASAT
;is for internal use only: it has no meaning to the user. We cannot use
;SAEOM for this purpose because SAEOM has meaning when passed from the
;user to us: if it is set, then we must put the next message in one
;buffer, possibly truncating data, if SAEOM is cleared by the user, we
;must keep the overflow and present it to the next read.  Also, we
;consider a read satisfied if the user's buffer is filled and the
;user has not asserted SAEOM.

	MOVE T1,MB		;SET UP FOR CALL TO DNLENG
	CALL DNLENG		;GET THE LENGTH OF THE MESSAGE
	MOVE P1,T1		;P1 HAS LENGTH OF USER'S DATA IN MSG

	MOVE T1,MB		;GET READY TO READ REST OF BYTES
	CALL DNGINI		; IN THE MESSAGE

;Note that the following code does not depend on SASAT being initialized

	MOVE T4,SA.FLG(SA)	;GET USER'S FLAGS BEFORE THEY'RE MODIFIED
	LOAD T5,MBEOM,(MB)	;GET EOM FLAG FROM MSG BLK, T5 USED BELOW
	STOR T5,SASAT,(SA)	;READ IS SATISFIED IF THIS IS EOM
				; IOR IF BUFFER IS FULL, SEE BELOW
	LOAD T1,SAAA1,(SA)	;GET THE LENGTH OF THE USER'S BUFFER
;
; SPR 10-34926
; If there's a negative count here, it should be because we're discarding
; DECnet messages for the user until we see EOM in an incoming message.
; This happens if the user sets the EOM bit in his arg block, but
; the incoming message is too big to fit in his buffer, and it doesn't
; have EOM set itself.  We should discard bytes until we see an EOM.
; SCMUUO already checked to make sure that the user didn't give us a
; negative count to begin with.
;
	SUB T1,P1		;WILL THIS MESSAGE FIT?
	JUMPE T1,NSFDR2		;MSG FITS IN USER BUFFER EXACTLY
	JUMPG T1,NSFDR3		;MSG FITS IN USER BUFFER WITH ROOM TO SPARE

;Here if message overfills user buffer
	LOAD P1,SAAA1,(SA)	;COPY ONLY WHAT FITS IN USER BUFFER
	SETZ T5,		;BUFFERFUL HAS NO EOM, SINCE IT OVERFLOWED
	TXNN T4,SAEOM		;USER WANT WHOLE MSG IN 1 READ?
	SETZB P2,T1		;NO, LEAVE MSG BLK FOR NEXT READ, COUNT := 0
				;YES, TOSS REST OF MSG, P2 STILL PTS TO NSL
;Here if message fills or overfills user buffer
NSFDR2:	MOVX T2,SASAT		;SATISFACTION FLAG
	TXNN T4,SAEOM		;USER WANT WHOLE MSG IN 1 READ?
	IORM T2,SA.SAT(SA)	;NO, READ IS SATISFIED SINCE BUFFER IS FULL
				; WHETHER OR NOT MBEOM IS TRUE
;Here for all messages, note that count is negative if we toss overflow

NSFDR3:	TXNN T4,SAEOM		;USER WANT WHOLE MSG IN 1 READ?
	STOR T5,SAEOM,(SA)	;NO, STORE EOM FLAG WE'VE CALC'D HERE

	STOR T1,SAAA1,(SA)	;STORE # BYTES REMAINING IN USER BFR

;We now have:	P1/ Length of data to copy
;		P2/ Pointer to NSL if we're to free msg blk, else 0

	JUMPLE P1,NSFDRA	;IF COUNT IS NEGATIVE, DON'T COPY ANYTHING

;Update byte count in SLB
	OPSTRM <ADDM P1,>,SLBYR,(SL) ;# of bytes received

;Copy the message data into the user's buffer.

	MOVE T1,P1		;NUMBER OF BYTES TO COPY FROM MSG TO USER
	LOAD T2,SAAA2,(SA)	;GET THE BYTE POINTER
	LOAD T3,SAAA3,(SA)	; AND OPTIONAL SECOND WORD OF BPTR
  IFN FTOPS10,<
	MOVE T4,[XCDSEC,,DNCM2U] ;ASSUME BUFFER IN USER SPACE
	TMNE SAEVA,(SA)		;IS THAT TRUE?
  >
;Note: SAEVA must be set on TOPS-20 - buffer can never be in user space
	MOVE T4,[XCDSEC,,DNCM2B] ;No, copy to monitor buffer
	CALL 0(T4)		;COPY MESSAGE BLOCK TO BUFFER
	  CALLRET SCEIVM	;--INVALID MESSAGE EVENT
	STOR T2,SAAA2,(SA)	;STORE THE UPDATED BYTE POINTER
	STOR T3,SAAA3,(SA)	; AND OPTIONAL SECOND WORD OF BPTR

NSFDRA:	JUMPE P2,RSKP		;IF THERE'S NO PTR, WE HAVE MORE TO READ
				; FROM THIS MSG, SO DON'T DEQUE MSG
				; OR SEND OUT DATA REQUESTS YET

	DEQUE MB,SS.INQ(P2),MB.NXT,NSFDRB ;DEQUE MSG WE WERE PEEKING AT

;Here we unreserve an appropriate number of buffers, but we don't
;reserve any again to correspond to the data requests we send, that is
;done by a call to SCTRIB from LLINKS when a message arrives.

NSFDRB:	D36OFF			;PROTECT THIS CODE FROM LLINKS CALL TO SCTRIB
	OPSTRM <SOS T1,>,SLINU,(SL) ;ONE LESS INPUT BUFFER USED
	MOVX T2,-1		;ASSUME WE'RE NOT FREEING LAST INPUT BUFFER
	JUMPG T1,NSFDR4		;JUMP IF THAT'S TRUE
	MOVX T2,-2		;WE ARE FREEING LAST INPUT, ASSUME NO OUTPUT
	TMNE SLOTU,(SL)		;ANY OUTPUT BUFFERS RESERVED?
	MOVX T2,0		;YES, LEAVE AN INPUT BUFFER RESERVED
NSFDR4:	ADDB T2,DCNRSB		;ADJUST THE RESERVED BUFFER COUNT
	D36ON			;END OF CRITICAL SECTION
	LOAD T1,SLINQ,(SL)	;GET THE INPUT QUOTA
	OPSTRM <SOS T2,>,SSRDO,+SL.NSL(SL) ;ONE LESS REC DATA REQUEST
	SUB T1,T2		;HOW MANY REQUESTS SHOULD WE SEND NOW?
	JUMPL T1,[CALL FREMSG	;IF WE DON'T HAVE ENOUGH QUOTA, CAN'T SEND
				; ANOTHER DRQ, SO FREE MESSAGE BLOCK
	          RETSKP]	;AND RETURN TO MONITOR USER
	OPSTRM <ADDM T1,>,SSRDO,+SL.NSL(SL) ;UPDATE RECEIVE DRQ COUNT

;Send the data requests to NSP.

	TXZ T2,MBOTH		;(T1,T2)ON THE NORMAL SUB-LINK
	CALL SNDDRM		;SEND DATA REQUESTS USING MESSAGE BLOCK
				; WE CURRENTLY HAVE
	  SCERR %NEALF,RTN,<Allocation Failure>
	  RETSKP			;RETURN SUCCESSFULLY

	SUBTTL	NSP. -- NSFSQ - Set Quotas and Goals

;NSFSQ - Set Quotas and Goals
;Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block with user arguments stored
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6, P1 & MB
;
;For the .NSFSQ function, the function dependant arguments are:
;	.NSAA1/ Link Quota, input + output
;	.NSAA2/ % Input
;	.NSAA3/ Input Goal

NSFSQ:	TRACE SC,<Performing Set Link Quota (.NSFSQ)>
	LOAD T1,SLSTA,(SL)	;GET LINK STATE
	IFSTATE T1,<DC,LK,CM,CF,NR>,RSKP ;LEAVE QUIETLY IF IN TERMINAL STATE
	LOAD P1,SANAG,(SA)	;GET NUMBER OF ARGUMENTS USER SUPPLIED
	CAIG P1,.NSAA1		;DID HE GIVE THE TOTAL QUOTA?
	SCERR %NEWNA,RTN,<Wrong number of arguments>

	LOAD T1,SLINQ,(SL)	;GET THE OLD QUOTA
	OPSTR <ADD T1,>,SLOTQ,(SL) ;TOTAL OF INPUT + OUTPUT QUOTA
	OPSTR <SKIPG>,SAAA1,(SA) ;USER SUPPLY A NEW QUOTA?
	STOR T1,SAAA1,(SA)	;NO, STORE OLD QUOTA TOTAL AS DEFAULT

	MOVEI T2,0		;NO VALUE OFFERED YET
	CAILE P1,.NSAA2		;DID HE GIVE THE % INPUT?
	LOAD T2,SAAA2,(SA)	;GET HIS VALUE
	CAILE T2,^D100		; AND 100
	  SCERR %NEPIO,RTN,<Percentage input out of bounds>
	JUMPG T2,NSFSQ1		;USER OFFER A VALUE?
	MOVEI T2,^D50		;NO, ASSUME HALF INPUT & HALF OUTPUT
	STOR T2,SAAA2,(SA)	;REMEMBER PERCENTAGE WE DECIDED ON
NSFSQ1:
	SETZB T1,MB		;DON'T NEED ANY USER DATA
	CAIG P1,.NSAA3		;USER SPECIFY AN INPUT GOAL?
	IFSKP.			;Yes, apply default if necessary
	  LOAD T2,SASJB,(SA)	;Get pointer to SJB
	  LOAD T1,SJGOL,(T2)	;Get job goal for default
	  OPSTR <SKIPGE>,SAAA3,(SA) ;Verify supplied value
	  STOR T1,SAAA3,(SA)	;Apply default

	  CALL DNGMSG		;GET MESSAGE BLOCK IN CASE WE NEED IT
	    SCERR %NEALF,RTN,<Allocation failure>
	  MOVE MB,T1		;SAVE POINTER TO MSG BLK
	ENDIF.

;No more error returns now

	LOAD T1,SAAA1,(SA)	;GET THE LINK QUOTA
	LOAD T2,SAAA2,(SA)	;GET PERCENTAGE VERIFIED ABOVE
	IMUL T1,T2		;MULTIPY LINK QUOTA BY % INPUT
	IDIVI T1,^D100		;DIVIDE BY 100. TO FIND INPUT QUOTA
	STOR T1,SLINQ,(SL)	;STORE THE INPUT QUOTA IN THE SLB
	LOAD T2,SAAA1,(SA)	;GET THE LINK QUOTA BACK
	SUB T2,T1		;SUBTRACT TO FIND THE OUTPUT QUOTA
	STOR T2,SLOTQ,(SL)	;STORE THE OUTPUT QUOTA IN THE SLB

	CAIG P1,.NSAA3		;WAS THE GOAL SPECIFIED?
	RETSKP			;USE DEFAULT THAT'S THERE ALREADY
				;WE ONLY GOT MSG BLK IF WE HAVE A GOAL
	LOAD T1,SLRFL,(SL)	;GET THE FLOW CONTROL TYPE
	LOAD T2,SAAA3,(SA)	;GET THE INPUT GOAL
	CAIN T1,FCM.MG		;ARE WE IN MESSAGE FLOW CONTROL?
	CAIL T2,1		;YES, IS OUR GOAL SET TO LESS THAN ONE?
	TRNA			;NO, DON'T WORRY ABOUT IT
	MOVEI T2,1		;IN MSG FLOW MODE WE GOAL OF AT LEAST 1
	STOR T2,SLGOL,(SL)	;REMEMBER THE GOAL WE DECIDED ON

;Notify NSP of the change in the goal.

	LOAD T1,SLPID,(SL)	;GET THE NSPpid
				;T2 CARRIED DOWN FROM ABOVE
	IFE. T1			;IS THERE ONE ?
	  MOVE T1,MB		;NO. DON'T TELL NSP
	  CALL DNFMSG		; SINCE THERE IS NO PLACE 
	  RETSKP		; TO PUT IT YET.
	ENDIF.
	MOVX T3,NV.GOL		;FUNCTION IS SET GOAL
	MOVE T4,MB		;NSP TAKES MB POINTER IN T4
	CALL NSP		;(T1,T2,T3,T4)PERFORM THE SET GOAL
	RETSKP			;RETURN SUCCESS
	SUBTTL	NSP. -- NSFRQ - Read Quotas and Goals

;NSFRQ - Read Quotas and Goals
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block with user arguments stored
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFRQ function, the function dependant arguments are:
;
;	.NSAA1/ Link Quota
;	.NSAA2/ % Input
;	.NSAA3/ Input Goal

NSFRQ:	TRACE SC,<Performing Read Link Quota (.NSFRQ)>

	LOAD T1,SLINQ,(SL)	;GET THE INPUT QUOTA
	LOAD T2,SLOTQ,(SL)	; AND THE OUTPUT QUOTA
	ADD T1,T2		;TOTAL THEM UP

	STOR T1,SAAA1,(SA)	;STORE IN ARG BLOCK WERE THE LINK QUOTA GOES

	LOAD P1,SANAG,(SA)	;GET THE NUMBER OF ARGUMENTS USER SUPPLIED
	CAIG P1,.NSAA2		;DOES HE WANT THE %INPUT?
	RETSKP			;NO, JUST GIVE GOOD RETURN

	LOAD T2,SLINQ,(SL)	;Get the input quota
	IMULI T2,^D100		;Multiply by 100 to get % input
	IDIV T2,T1		; as an integer between 0 and 100.
	STOR T2,SAAA2,(SA)	;Store it in argument block

	CAIG P1,.NSAA3		;DOES HE WANT THE INPUT GOAL?
	RETSKP			;NO, JUST RETURN

	LOAD T1,SLGOL,(SL)	;GET THE GOAL
	STOR T1,SAAA3,(SA)	;PUT IT IN THE ARG BLOCK
	RETSKP			; AND RETURN
	SUBTTL	NSP. -- NSFJS - Set Job Quotas and Goals

;NSFJS - Set Job Quotas and Goals
;
; Call:
;	Never Happens: serviced in SCMUUO
;
; Return:
;
; Uses: Nothing
;
;This function is performed entirely by SCMUUO or equivalent.

NSFJS:	SCERR %NEILF,RTN,<Illegal function - handled in SCMUUO>

	SUBTTL	NSP. -- NSFJR - Read Job Quotas and Goals

;NSFJR - Read Job Quotas and Goals
;
; Call:
;	Never Happens: serviced in SCMUUO
;
; Return:
;
; Uses: Nothing
;
;This function is performed entirely by SCMUUO or equivalent.

NSFJR:	SCERR %NEILF,RTN,<Illegal function - handled by SCMUUO>

	SUBTTL	NSP. -- NSFPI - Set PSI Reason Mask

;NSFPI - Set PSI Reason Mask
;
; Call:
;	SL/ Pointer to Session Control Link Block
;	SA/ Pointer to Session Control Arg Block with user arguments stored
;
; Return:
;	RET			;ON ERROR WITH T1 CONTAINING ERROR CODE
;	RETSKP			;ALL IS WELL
;
; Uses: T1-T6
;
;For the .NSFPI function, the function dependent arguments are:
;
;	.NSAA1/ XWD 0,reason mask

NSFPI:	TRACE SC,<Performing Set PSI Mask (.NSFPI)>

	LOAD T1,SAAA1,(SA)	;GET .NSAA1 ARGUMENT
	STOR T1,SLPSM,(SL)	;STORE THE PSI MASK FOR SCTPSI

	TMNE <SLPSI,SLLBC,SLFSL>,(SL) ;PSI ALREADY PENDING OR LINK CLOSING?
	RETSKP			;YES, AVOID RACE

				;T1 HOLDS OLD STATUS (0),,NEW MASK
	LOAD T2,SLCHN,(SL)	;THE LINK NUMBER
	LOAD T5,SLSST,(SL)	;T5 PASSES STATUS CHANGES (CURRENT STATUS)
	HRL T2,T5		;T2 HOLDS NEW STATUS,,CHN NUMBER
	LOAD T3,SLSJB,(SL)	;@SLWKA NEEDS T3 SET UP WITH PTR TO SJB
	MOVE T4,SL		;'LINK IDENTIFIER' TO PASS TO SCTWKQ
	OPSTR <SKIPE T6,>,SLWKA,(SL) ;IS THERE A WAKE ROUTINE?
	CALL 0(T6)		;(T1,T2,T3,T4,T5)YES, CALL WAKE ROUTINE

	RETSKP			;RETURN SUCCESSFULLY
	SUBTTL RETFLW - Return current flow controls

;RETFLW - Return Receive Flow Mode,,Transmit Flow Control
;
; Call:
;	SL/ Pointer to Session Control Line Block
;
; Return:
;	RET			;ALWAYS, WITH T1 CONTAINING RFL,,XFL
;
; Uses: T1,T2

RETFLW:	LOAD T1,SLRFL,(SL)	;GET THE RECEIVE FLOW CONTROL
	LOAD T2,SLXFL,(SL)	;GET THE TRANSMIT FLOW CONTROL TYPE
	ADDI T1,1		;TRANSLATE TO USER'S VALUE
	HRLI T1,1(T2)		;BUILD RFL,XFL
				;TRANSLATE THAT ALSO
	RET			; AND RETURN
	SUBTTL SNDDRQ - Send Data Requests

;SNDDRQ -  Send some data requests to NSP
;
; Call:
;	T1/ Number of Data Requests to Send
;	T2/ MBOTH set to the correct value
;	(for SNDDRM:
;	MB/ Pointer to Message Block)
;
; Return:
;	RET			;ON ALLOCATION FAILURE
;	RETSKP			;ON SUCCESS
;
; Uses: T1-T6

;The SNDDRQ code is commented out since there are no callers of it.
; All callers come in at the SNDDRM entry point

Repeat 0,<

SNDDRQ:	SAVEAC <MB,P1,P2>	;GET OUR OWN COPY OF MB AND SAVE SOME PEAS
	DMOVE P1,T1		;PRESERVE THE ARGUMENTS
	SETZ T1,		;DON'T NEED ANY USER DATA
	CALL DNGMSG		;GET A MESSAGE BLOCK
	 RET			;COULDN'T ALLOCATE, JUST RETURN
	MOVE MB,T1		;POINT TO THE NEW MESSAGE BLOCK
	CALLRET SNDDRX		;JOIN COMMON CODE

>

;This is the alternate entry for those guys which already have a message
;block.

SNDDRM:	DMOVE P1,T1		;SAVE THE COUNT AND MBOTH
	MOVE T1,MB		;POINT TO THE MESSAGE BLOCK
	MOVEI T2,0		;NO USER DATA NEEDED
	CALL DNMINI		;RE-INITIALIZE THE MESSAGE BLOCK
	 RET			;COULDN'T DO IT

SNDDRX:	SETZ T2,		;BUILD THE DATA REQUEST ARG BLOCK (QA) IN T2
	STOR P1,QACNT,+T2	; USE THAT AS THE DATA REQUEST COUNT

	MOVX T1,MBOTH		;GET THE "OTHER" SUBLINK FLAG
	TXNE P2,MBOTH		;CALLER WANT IT SET IN MESSAGE BLOCK?
	IORM T1,MB.OTH(MB)	;YES, IT WAS CLEARED BY DNMINI

	LOAD T1,SLPID,(SL)	;GET THE NSPpid FOR NSP
	MOVX T3,NV.DRQ		;FUNCTION IS SEND DATA REQUESTS
	MOVE T4,MB		;POINT TO MESSAGE BLOCK
	CALL NSP		;CALL NSP AND RETURN EVENTUALLY
	RETSKP			;RETURN SUCCESS
	SUBTTL RLSLNK - Release a Link

;RLSLNK - Release a Link
;
; Call:
;	MB/ Ptr to msg blk or zero if LLINKS will not have to queue NV.CLS call
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6,MB,MS

RLSLNK:	JN <SLFSL,SLLBC>,(SL),RLSLN1 ;IGNORE IF LINK ALREADY BEING CLOSED
	SKIPN EV96.0		;Shall we log the CSSE event?
	IFSKP.			; -yes, unfortunately
	  EVENT(LCG,CSE,Link close) ;Log "link close" event for CSSE
	ENDIF.
	CALL STPTMR		;STOP THE CI TIMER, IF IT'S GOING
	JN SLPID,(SL),RLSLN2	;JUMP IF WE HAVE A PID FROM NSP
	SETONE SLFSL,(SL)	;WE HAVEN'T TOLD NSP ABOUT
				; THIS LINK YET, DON'T CALL HIM NOW
	CALL SCTRFJ		;REQUEST SECOND SERVICE, SLFSL WILL
				; TELL IT TO FREE THE SLB
RLSLN1:	SKIPN MB		;DID WE HAVE A MSG BLK?
	RET			;NO, ONLY RETURN
	CALLRET FREMSG		;YES, FREE IT AND RETURN

RLSLN2:	LOAD T1,SLPID,(SL)	;GET THE NSPpid FROM SLB
	LOAD T2,SLSLB,(SL)	;GET THIS PORT'S SLBid
	MOVX T3,NV.CLS		;CLOSE FUNCTION FOR NSP
	MOVE T4,MB		;NSP WANTS MSG BLK PTR IN T4 (ZERO IF RESET)
	CALL NSP		;CALL NSP TO CLOSE THE PORT
	SETONE SLLBC,(SL)	;LINK IS BEING CLOSED
	RET			;ONLY RETURN
	SUBTTL	LLINKS Calls -- Entry Vector Table

;See procedure SCTL for a description of the calling sequence used to
;call SCTL.

;The offsets SV.xxx into this table are defined in D36PAR.

DEFINE SV(NAM),<
IFN .-SCFNT-SV.'NAM,<	PRINTX ?SCFNT table (NAM) not in same order
			PRINTX ? as SV.'NAM in D36PAR.UNV
			PASS2
			END>
	IFIW <SC'NAM&777777>>

;These are the function codes for the calls to Session Control from
;NSP.

SCFNT:	SV CCR			;CONNECT CONFIRMED CALL
	SV DIR			;DISCONNECT INITIATE RECEIVED CALL
	SV DCR			;DISCONNECT CONFIRM RECEIVED CALL
	SV OND			;OUTPUT NOT DONE CALL
	SV ODN			;OUTPUT DONE CALL
	SV SEG			;SEGMENT RECEIVED CALL
	SV DRQ			;DATA REQUEST RECEIVED CALL
	SV NCF			;NO CONFIDENCE IN PORT CALL
	SV NRS			;NO RESOURCES CALL
	SV CLS			;CLOSE COMPLETED CALL
	SV NLK			;NO LINK CALL
	SV NCM			;NO COMMUNICATION CALL
	SV NRN			;NOT IN RUN STATE
	SV CAK			;GOT A CONNECT ACK
SV.MAX==.-SCFNT-1		;THE HIGHEST RECOGNIZED ENTRY OFFSET

				;The Connect Initiate call does not go
				;through the vectored interface, since
				;it must go to a single routine
				;capable of sorting out which Session
				;Control will handle the incoming
				;message.  the connect initiate call
				;goes to the global label SCTLCI.

PURGE SV
	SUBTTL	LLINKS Calls -- SCTRIB - Reserve Input Buffer

;SCTRIB - Reserve Input Buffer
;
;Call:	T1/ Pointer to SLB
;	CALL	SCTRIB
;	  Error Return: no input buffer has been reserved
;	Normal Return: one input buffer has been reserved
;Changes T1,T2,T3,T4

;This routine and all others which reference DCNRSB must run under
;D36OFF because this routine is called from LLINKS without the SCLINK
;interlock.  This routine is only called from LLINKS.  It is here in
;SCLINK so that all references to the reserved buffer counter will be
;together in one module.

;The scheme here is that we want to count the buffers which are
;actually in use, plus those which are required to be reserved in
;order to prevent a deadlock.  We must be sure that no logical link is
;put in the position of being having a allowed to read a message while
;being stuck not allowed to send a message.  If both ends of a link
;were to be in that state, they would be in a deadly embrace.
;
;So whenever we want to commit a buffer for output (by sending it) or
;for input (by ACKing it), we must check first if we have room in the
;system pool to reserve it.  If we reserve a buffer for one direction,
;then we must assure that at least one is reserved for the other
;direction.
;
;The handling of DCNRSB in SCLINK preserves the above guarantees.
;This routine is called from LLINKS when it receives a message and
;there are Session Control data requests for that message.  If this
;routine returns failure, LLINKS will keep the message, unACKed, and
;will try to send it to Session Control again when the memory manager
;tells LLINKS that congestion has been relieved.

	XRESCD
SCTRIB:	OPSTR <CAME T1,>,SLSLB,(T1) ;IS THIS A LEGIT SLB POINTER?
	BUG.(CHK,SCLRIB,SCLINK,SOFT,<Bad SCTRIB call from LLINKS>,<<T1,ADDR>>,<

Cause:	LLINKS has called SCTRIB for permission to send a message to SCLINK
	and has passed an invalid SLB address in T1.  The data structures
	for this logical link are inconsistent.

Action:	Find out what is in LLINK's ELSCB and why its not an SLB pointer.

Data:	ADDR - The bad SLB pointer

>,RTN)
	D36OFF			;INTERLOCK AGAINST SCTRIB CALL FROM LLINKS
	JE SLINU,(T1),SCTRB1	;JUMP IF THIS WILL BE ONLY INPUT BUFFER NOW
	MOVE T2,DCNRSB		;GET # NOW RESERVED
	SKIPN DCNCON		;IS THE SYSTEM CONGESTED?
	CAML T2,DCNTSB		;NO, CAN WE FIT ONE MORE?
	JRST SCTRB3		;NO ROOM FOR THIS LINK
	AOS T2,DCNRSB		;ONE MORE BUFFER RESERVED
	JRST SCTRB2		;SUCCESS

SCTRB1:	JN SLOTU,(T1),SCTRB4	;JUMP IF OUTPUT BUFF ALLOC, THAT RESERVES 1 IN
	MOVE T2,DCNRSB		;GET # ALREADY RESERVED
	ADDI T2,2		;WE INTEND TO RESERVE 2 MORE (1 IN + 1 OUT)
	CAMLE T2,DCNTSB		;ENOUGH ROOM IN TOTAL SYSTEM BUFFERS?
	JRST SCTRB3		;NO, NO ROOM FOR THIS LINK
	MOVEM T2,DCNRSB		;YES, D36OFF MAKES THIS STORE OK
SCTRB2:	CAMLE T2,DCNRHT		;NEW HIGH TIDE?
	MOVEM T2,DCNRHT		;YES, RECORD IT
SCTRB4:	INCR SLINU,(T1)		;UPDATE INPUT_BUFFERS_IN_USE COUNT
	D36ON			;END OF CRITICAL SECTION
	RETSKP			;GOT ROOM, SUCCESS RETURN

SCTRB3:	SETOM DCNCON		;CALL FOR CHKSTS WHEN CONGESTION FREES
	D36ON			;END OF CRITICAL SECTION
	AOS DCNRIF		;INCREMENT INPUT RESERVATION FAILURES
	RET			;FAIL RETURN
	SUBTTL	LLINKS Calls -- SCTUCG - Uncongestion Call

;SCTUCG - LLINKS calls this routine when memory manager announces
;	  that congestion is relieved.
;
; Call:
;	No arguments
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

	XRESCD
SCTUCG:	SAVEAC <MB,SL,SA,MS,P1,P2>
	TRACE SC,Session Control told of congestion relief

	SETOM SCTUCF		;NO MESSAGE BLOCK TO QUEUE HERE
	CALLRET SCTLCF		;GET THE INTERLOCK, FLAG IF FAIL

;When NSFDS (data send) and NSFIS (interrupt send) fail to get a
;message block, they will both turn off both NSNDR (normal data
;requests available) and NSIDR (interrupt data requests available).
;Presumably this will only happen if the system is congested.  Later,
;when the system congestion is relieved, we must tell the stalled
;links that we again have message blocks available.  Since the NSxDR
;flags in the status half-word have been turned off, this call to
;CHKSTS will turn them back on again if appropriate and this will
;trigger CHKSTS to call the wake routine.

SCIUCG:	LOAD SL,QHBEG,+SCTASQ	;PTR TO FIRST SLB ON ALL SLBs Q
SCIUC1:	JUMPE SL,RTN		;RETURN WHEN NO MORE SLBs TO VISIT
	CALL CHKSTS		;SEE IF THIS SLB NEEDS WAKING
	LOAD SL,SLASQ,(SL)	;STEP TO NEXT SLB ON ALL SLBs Q
	JRST SCIUC1		;LOOP OVER ALL SLBs IN SYSTEM
	SUBTTL	LLINKS Calls -- SCTLCI - Connect Initiate Call

;SCTLCI - Connect Initiate Call
;
; Call:
;	T1/ NSPpid for port just created
;	T2/ Connect Call arguments (see BEGSTR IA in D36PAR)
;	T4/ Pointer to Message Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

	XRESCD
SCTLCI:	SAVEAC <MB,SL,SA,MS,P1,P2>
	TRACE SC,Connect Initiate call
	MOVE MB,T4		;SET UP THE MESSAGE BLOCK POINTER
	STOR T1,MBAR1,(MB)	;STORE THE FIRST ARGUMENT
	STOR T2,MBAR2,(MB)	; AND THE SECOND ONE
	XMOVEI T1,SCTLC1	;PROCESSOR TO CALL WHEN WE
	CALLRET SCTLCQ		; GET THE INTERLOCK

;Now try to find a matching SLB for the incoming connect.

SCTLC1:	TRACE SC,Processing Connect Initiate

	MOVE T1,MB		;POINT TO THE INCOMING MESSAGE BLOCK
	CALL DNGINI		;SET UP MS FOR DNGxBY ROUTINES

	MOVX T1,CB.LEN		;WE HAVE TO GET A CONNECT BLOCK
	CALL DNGWDZ		; TO FILL IN AND COMPARE WITH PASSIVE SLBS
	  JRST SCTCIR		;RESOURCE FAILURE
	MOVE P1,T1		;SAVE POINTER TO INCOMING CONNECT BLOCK

	CALL PRSCTX		;PARSE THE INCOMING CONNECT DATA IN CB
	 JRST [	MOVE T1,P1	;PTR TO NEW CONNECT BLK
		CALL DNFWDS	;FREE NEW CONNECT BLK
		CALLRET SCEIVM]	;--INVALID MESSAGE EVENT

	MOVE T1,P1		;GET THE INCOMING CONNECT BLOCK POINTER BACK
	CALL SLBMAT		;FIND PASSIVE SLB TO MATCH NEW SLB
	 JRST [	EXCH T1,P1	;Get pointer to new connect block and save
				; return code
		CALL DNFWDS	;Free the connect blk
		JUMPN P1,SCTCIT	;Return 'object too busy'
		CALLRET SCTCIN]	; otherwise unrecognized object error
;We have found matching SLB, lets set some parameters in the SLB and
;go into CR state.

	LOAD T1,SLCBP,(SL)	;POINT TO PASSIVE CB
	CALL DNFWDS		;FREE THAT UP
	SETZRO SLCBP,(SL)	;LOSE PTR TO PASSIVE CB WE JUST FREED

	LOAD T1,MBSRC,(MB)	;GET SOURCE NODE ADDRESS
	STOR T1,SLDNA,(SL)	; AND STORE AS DESTINATION OF THIS LINK
	STOR T1,CBNUM,(P1)	;  and store in connect block

SCTLC2:	STOR P1,SLCBP,(SL)	;PUT PTR TO ACTIVE CONNECT BLK IN SLB
				; FOR .NSFRI FUNCTION

	XMOVEI T1,CB.SRC(P1)	;Point to source PDB
	LOAD T1,PBOBJ,(T1)	;Get remote object type
	STOR T1,SLDOB,(SL)	; and store as destination PDB in SLB

	LOAD T1,MBAR1,(MB)	;GET THE NSPpid
	STOR T1,SLPID,(SL)	;STORE IT IN THE SLB

	LOAD T2,MBAR2,(MB)	;GET THE CONNECT CALL ARGUMENTS
	LOAD T1,IAFLO,+T2	;GET FLOW CONTROL TYPE FROM ARGUMENTS
	STOR T1,SLXFL,(SL)	; AND STORE IT IN SLB

	LOAD T1,SLDNA,(SL)	;Get destination address
	LOAD T2,IASIZ,+T2	;Get segment size specified by remote
	CALL SCTCSS		;Check what segment size we should use
	STOR T1,SLSIZ,(SL)	; and store the segment size ROUTER calculated

	NEWSTATE CR		;CALL SCSSTS TO PUT US IN .NSSCR STATE
				; AND WAKE THE USER
	TMNE MBPH2,(MB)		;IS THIS CONNECT FROM A PHASE II GUY?
	CALL CONBUF		;YES, SET UP FOR CONSERVATIVE BUFFERING

	CALL STRTMR		;START CONNECT TIMERS & FIX TIMER COUNTS
	CALLRET FREMSG		;DEALLOCATE CI MESSAGE, & RETURN TO NSP
;Here when we could not get enough resources to accept the Connect.

SCTCIR:	MOVX P2,RSNRES		;NO RESOURCES ERROR CODE
	JRST SCTCFN		;TO COMMON REJECT CODE

SCTCIT:	MOVX P2,RSNOTB		;Object too busy code
	JRST SCTCFN		; to common rejet code

SCTCIN:	MOVX P2,RSNURO		;UNRECOGNIZED OBJECT REASON CODE
SCTCFN:	TRACE SC,Rejecting Connect Initiate call
	LOAD P1,MBAR1,(MB)	;SAVE THE NSPpid
	MOVE T1,MB		;SET UP FOR DNMINI CALL
	MOVEI T2,3		;LENGTH OF USER DATA NEEDED
	CALL DNMINI		;RE-INITIALIZE THE MESSAGE BLOCK WE'RE USING
	  RET			;  Will never happen - but if, just return
	XMOVEI T1,UD.MSD(MB)	;POINT TO THE USER MSD
	CALL DNPINI		;INITIALIZE FOR PUTTING BYTES

	MOVE T1,P2		;GET REASON CODE
	CALL DNP2BY		;PLACE IT IN THE MESSAGE

	SETZ T1,		;NO DATA-CTL NEEDED
	CALL DNP1BY		;PLACE IT IN

	MOVE T1,P1		;SET UP THE NSPpid
	MOVX T3,NV.REJ		;REJECT THE CONNECT
	MOVE T4,MB		;SET UP POINTER TO MESSAGE BLOCK
	CALLRET NSP		;INFORM NSP OF FAILURE
	SUBTTL	LLINKS Calls -- The Vectored Call Entry Point SCTL

;SCTL - Here for all calls from NSP except Connect Initiate
;
; Call:
;	T1/ SLBid
;	T2/ Function specific argument
;	T3/ Function Code (SV.xxx)
;	T4/ Pointer to message block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

	XRESCD
SCTL:	SAVEAC <MB,MS,SA,SL>	;SAVE SOME ACS
	MOVE MB,T4		;SET UP MESSAGE BLOCK
	STOR T1,MBAR1,(MB)	;STORE THE SLBid
	STOR T2,MBAR2,(MB)	;STORE THE FUNCTION SPECIFIC ARGUMENT
	STOR T3,MBAR3,(MB)	;STORE THE FUNCTION CODE
	XMOVEI T1,SCTL1		;ADDRESS OF PROCESSOR TO CALL AFTER WE
	CALLRET SCTLCQ		; GET THE INTERLOCK

SCTL1:	CALL FNDSBI		;FIND THE SLB FROM THE SLBid
	  CALLRET FREMSG	;NO SUCH SLBid, IGNORE THIS MSG
	CALLRET @SCFNT(T3)	;DISPATCH BY FUNCTION TYPE
	SUBTTL	LLINKS Calls -- SCCCR - Connect Confirmed call from NSP

;SCCCR - Connect Confirmed call from NSP
;
; Call:
;	T2/ Connect Initiate arguments (see BEGSTR IA in D36PAR)
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCCCR:	TRACE SC,Connect confirmed call
	SAVEAC P1		;SAVE A PEA
	MOVE P1,T2		;SAVE ARGUMENT FROM NSP

	CALL STPTMR		;STOP CI TIMER & DECREMENT TIMER COUNTS

	LOAD T1,IAFLO,+P1	;GET FLOW CONTROL TYPE
	STOR T1,SLXFL,(SL)	;STORE TRANSMIT FLOW TYPE IN SLB

;We have to cater for a VMS crock here.  Assuming that the buffer size
; is 576 bytes, VMS announces 561 bytes while TOPS-20 would pick 559 bytes.
; The reason is that VMS never uses the piggy-back field while we do do.
;
;Now, if we are an endnode and we try to connect to a remote node and we run
; with big buffers, we will probably suggest 1450 bytes.  If the remote is
; not on the same NI and is a VAX it will return 561 bytes and we will accept
; that.... so we make a special test for if the remote buffer size is equal
; to our executor buffer size - %schdr + 2
	LOAD T1,IASIZ,+P1	;GET MAX BYTES ALLOWED IN MESSAGE SEGMENT
	MOVE T2,RTRBSZ		;Get router buffer size
	SUBI T2,<%SCHDR-2>	;Get 'VMS size'
	CAMN T1,T2		;Equal?
	SUBI T1,2		; -yes, fudge for VMS
	STOR T1,SLSIZ,(SL)	;STORE THAT

	TMNE MBPH2,(MB)		;WAS CC FROM A PHASE II NODE?
	CALL CONBUF		;YES, USE CONSERVATIVE BUFFERING

	STOR MB,SLCDM,(SL)	;SAVE CONNECT MSG FOR "NSFRC"

	NEWSTATE RN		;SET NEW STATE TO CONNECT RECEIVED
	SETONE SLCCB,(SL)	;HAVE TO CHECK CONNECT BLOCK NOW

;Now let's send the job's input quota worth of DRQs.

	TMNE SLPH2,(SL)		;SHOULD WE BE CONSERVATIVE WITH THIS FELLOW?
	RET			;LEAVE QUIETLY

	MOVEI T1,0		;DON'T NEED ANY USER DATA
	CALL DNGMSG		;GET A NEW MESSAGE BLOCK
	  RET			;  - resource error
	MOVE MB,T1		;POINT TO THE NEW MSG BLK

	SETZRO MBOTH,(MB)	;SEND OUT ON THE NORMAL SUBLINK
	SETZ T2,		;BUILD THE DATA REQUEST ARG BLOCK (QA) IN T2
	LOAD T1,SLINQ,(SL)	;GET THE INPUT QUOTA
	STOR T1,QACNT,+T2	; USE THAT AS THE DATA REQUEST COUNT
	STOR T1,SSRDO,+SL.NSL(SL) ;WE KEEP COUNT OF OUTGOING DATA REQUESTS

	LOAD T1,SLPID,(SL)	;GET THE NSPpid FOR NSP
	MOVX T3,NV.DRQ		;FUNCTION IS SEND DATA REQUESTS
	MOVE T4,MB		;POINT TO MESSAGE BLOCK
	CALLRET NSP		;CALL NSP AND RETURN EVENTUALLY
	SUBTTL	LLINKS Calls -- SCDIR - Disconnect Initiate received call

;SCDIR - Disconnect Initiate received call from NSP
;
; Call:
;	T2/ Reason Code
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T2

SCDIR:	TRACE SC,Disconnect Initiate received call
	SAVEAC P1		;SAVE A PEA

	STOR T2,SLRSN,(SL)	;ALSO STORE THE REASON FOR ERROR CODE
				; PROCESSING
	LOAD P1,SLSTA,(SL)	;GET THE STATE OF LINK
	IFSTATE P1,CS		;IF WE WERE IN CONNECT SENT STATE,
	  CALL STPTMR		; STOP CI TIMERS & ADJUST TIMER CNTRS

	OPSTR <SKIPE T1,>,SLCDM,(SL) ;ANY CONNECT DATA STILL THERE?
	  CALL DNFMSG		     ;YES, FREE IT NOW
	STOR MB,SLCDM,(SL)	;STORE DISCONNECT MESSAGE
	SETONE SLCCB,(SL)	;HAVE TO CHECK CONNECT BLOCK NOW

	IFNSTATE P1,CS,SCDIR1	;IF WE WERE IN CONNECT SENT,
	NEWSTATE RJ		; SET THE STATE TO RJ
	RET			; AND RETURN

;We differentiate between RJ and DR states here so that the user can
;tell from the state whether or not the link has ever been open.

SCDIR1:	NEWSTATE DR		;WE GOT INTO RN STATE, SO GO INTO DR STATE
	RET			;RETURN
	SUBTTL	LLINKS Calls -- SCDCR - Disconnect Confirm received call

;SCDCR - Disconnect Confirm received call from NSP
;
; Call:
;	T2/ Reason Code
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T2

SCDCR:	TRACE SC,Disconnect Confirm received call
	SAVEAC P1

;A DC is normally received in response to our sending out a DI.  In
;this case, there will be no interesting reason code and the link will
;not be in CS state, etc.  Phase II nodes, or those not yet updated to
;Phase III fully, will reject a CI with a DC instead of a DI and in
;this case the only reason code we get is on that DC message.

	STOR T2,SLRSN,(SL)	;ALSO STORE THE REASON FOR ERROR CODE
				; PROCESSING
	OPSTR <SKIPE T1,>,SLCDM,(SL) ;ANY CONNECT DATA STILL THERE?
	  CALL DNFMSG		     ;(T1) YES, FREE IT NOW
	SETZRO SLCDM,(SL)	;NO DISCONNECT MESSAGE FROM A DC

	LOAD P1,SLSTA,(SL)	;GET THE STATE OF LINK
	IFNSTATE P1,CS,SCDCR1	;IF WE WERE IN CONNECT SENT STATE,
	CALL STPTMR		; STOP CI TIMERS & ADJUST TIMER CNTRS
	NEWSTATE RJ		;GO INTO REJECT STATE
	CALLRET CHKABO		;(MB)CLOSE PORT IF NECESSARY
	   
;Here if link was not in CS state

SCDCR1: NEWSTATE DC		;GO INTO DISCONNECT CONFIRM STATE
	CALLRET CHKABO		;(MB)CLOSE PORT IF NECESSARY
	SUBTTL	LLINKS Calls -- SCODN - Output done call

;SCODN - Output done call from NSP
;SCOND - Output not done call from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T2

SCOND:!
SCODN:	TRACE SC,Output done call
	D36OFF			;PROTECT THIS CODE FROM LLINKS CALL TO SCTRIB
	OPSTRM <SOS T1,>,SLOTU,(SL) ;DECREMENT OUTPUT BUFFERS IN USE
	MOVX T2,-1		;ASSUME WE'RE NOT FREEING LAST OUTPUT BUFFER
	JUMPG T1,SCODN1		;JUMP IF THAT'S TRUE
	MOVX T2,-2		;WE ARE FREEING LAST OUTPUT, ASSUME NO INPUT
	TMNE SLINU,(SL)		;ANY INPUT BUFFERS ALLOCATED?
	MOVX T2,0		;YES, LEAVE AN OUTPUT BUFFER RESERVED
SCODN1:	ADDB T2,DCNRSB		;ADJUST THE RESERVED BUFFER COUNT
	D36ON			;END OF CRITICAL SECTION
	CALL FREMSG		;FREE THE MESSAGE BLOCK
	CALLRET CHKSTS		;GENERATE PSI INTERRUPT "OUTPUT-OK"
	SUBTTL	LLINKS Calls -- SCSEG - Segment received call

;SCSEG - Segment received call from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCSEG:	TRACE SC,Segment Received Call

	XMOVEI T4,SL.NSL(SL)	;ASSUME IT'S ON THE NORMAL SUBLINK
	TMNE MBOTH,(MB)		;IS IT THE OTHER SUBLINK?
	XMOVEI T4,SL.OSL(SL)	;YES, POINT TO OTHER AREA

	ENDQUE MB,SS.INQ(T4),MB.NXT,T1 ;QUEUE THE MESSAGE
	INCR SLPKR,(SL)		;Increment # of packets received
	CALLRET CHKSTS		;SEE IF THE USER NEEDS WAKING

;Note:  The count of input buffers used is incremented when the data
;requests are sent out in NSFxR, so we don't have to incr now.
	SUBTTL	LLINKS Calls -- SCDRQ - Data request received call

;SCDRQ - Data request received call from NSP
;
; Call:
;	T2/ DATA REQUEST call's arguments (see BEGSTR QA in D36PAR)
;	SL/ Pointer to Session Control Link Block
;	MB/ Pointer to the Message Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCDRQ:	TRACE SC,Data Request Received Call

	XMOVEI T4,SL.NSL(SL)	;ASSUME IT CAME FROM NORMAL SUBLINK
	TMNE MBOTH,(MB)		;DID IT?
	XMOVEI T4,SL.OSL(SL)	;NO, MUST BE FROM INTERRUPT SUBLINK

	LOADE T1,QACNT,+T2	;GET THE DATA REQUEST COUNT
	LOADE T3,SSXDO,(T4)	;GET # OF DRQS WE HAVE NOW OUTSTANDING
	ADD T3,T1		;NEW COUNT OF DATA REQUESTS
	STOR T3,SSXDO,(T4)	;STORE THE NEW DATA REQUEST COUNT

	CALL CHKSTS		;SET THE STATUS, POSSIBLY GENERATING PSI
	CALLRET FREMSG		;FREE THE MESSAGE BLOCK
	SUBTTL	LLINKS Calls -- SCNCF - No confidence in port call

;SCNCF - No confidence in port call from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCNCF:	TRACE SC,No confidence in port call

	NEWSTATE CF		;NEW STATE IS NO CONFIDENCE
	CALLRET CHKABO		;(MB)CLOSE PORT IF NECESSARY
	SUBTTL	LLINKS Calls -- SCNRS - No resources call

;SCNRS - No resources call from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCNRS:	TRACE SC,No resources call

	NEWSTATE NR		;SET THE NEW STATE TO "NO RESOURCES"
	CALLRET CHKABO		;(MB)CLOSE PORT IF NECESSARY
	SUBTTL	LLINKS Calls -- SCCLS - Close Completed call

;SCCLS - Close Completed call from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCCLS:	TRACE SC,Close Completed call

	SETONE SLFSL,(SL)	;FREE THE SLB ON NEXT TICK
	CALL SCTRFJ		;REQUEST SECOND SERVICE FOR THIS LINK
	CALLRET FREMSG		;FREE THE MESSAGE BLOCK
	SUBTTL	LLINKS Calls -- SCNLK - No link call

;SCNLK - No link call from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCNLK:	TRACE SC,No Link call

	NEWSTATE LK		;NEW STATE IS NO-LINK
	CALLRET CHKABO		;(MB)CLOSE PORT IF NECESSARY
	SUBTTL	LLINKS Calls -- SCNCM - No communication call

;SCNCM - No communication call from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCNCM:	TRACE SC,No communication call

	NEWSTATE CM		;NEW STATE IS NO COMMUNICATION
	CALLRET CHKABO		;(MB)CLOSE PORT IF NECESSARY
	SUBTTL	LLINKS Calls -- SCNRN - Not in Run State call

;SCNRN - Not in Run State call from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

SCNRN:	TRACE SC,Not in Run State call
				;NO NEW STATE TO GO INTO HERE
	CALLRET CHKABO		;(MB)CLOSE PORT IF NECESSARY
	SUBTTL	LLINKS Calls -- SCCAK - Got a Connect ACK

;SCCAK - Got a Connect ACK from NSP
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1
;
;NSP will call us here to report a connect ACK.  Using this call, we
;can determine that the CI has made it to the remote, while the remote
;user hasn't yet accepted the connect.  Maybe someday we'll figure out
;what to do with this.

SCCAK:	TRACE SC,Got a Connect ACK
	CALLRET FREMSG

	SUBTTL	Subroutines -- CHKABO - Check SLABO flag

;CHKABO - Check SLABO flag, close port if need be
;
; Call:
;	MB/ Pointer to a message block
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS, MESSAGE BLOCK FREED
;
; Uses: T1-T6
;

CHKABO:	TMNN SLABO,(SL)		;ARE WE TRYING TO CLOSE AFTER AN ABORT?
	CALLRET FREMSG		;NO, FREE MSG BLK AND LEAVE
	TMNE <SLFSL,SLLBC>,(SL)	;YES, HAVE WE ALREADY CLOSED LINK?
	CALLRET FREMSG		;YES, FREE MSG BLK AND LEAVE

	CALLRET RLSLNK		;(MB)RELEASE THE LINK AND RETURN
	SUBTTL	Subroutines -- CHKPPN - Check self PPN for validity

;CHKPPN - Check PPN in self port block
;
; Call:
;	T1/ Pointer to Port Block
;	SL/ Pointer to SLB
; Return
;	RET			;INVALID PPN AND NO PRIVS TO FAKE IT
;	RETSKP			;VALID


CHKPPN:
IFN FTOPS10,<
	LOAD T2,SLSJB,(SL)	;GET POINTER TO SJB
	JN SJPRV,(T2),RSKP	;IF PRIVED, ALWAYS VALID
IFN <PB.USR-PB.GRP>,<PRINTX PBUSR and PBGRP must be in same word>
	MOVE T1,PB.USR(T1)	;GET PPN WE CLAIM TO BE
	LOAD T2,SJJOB,(T2)	;GET JOB OUR JOB NUMBER
	CAME T1,JBTPPN##(T2)	;COMPARE OUR PPN WITH PPN WE CLAIM TO BE
	RET			;THAT'S NOT YOU. FAIL.
	RETSKP			;THAT'S YOU ALRIGHT. SUCCEED.
> ;END IFN FTOPS10
IFN FTOPS20,<
	RETSKP			;SUCCEED UNTIL WE DECIDE TO CHECK
> ;END IFN FTOPS20
	SUBTTL	Subroutines -- FREMSG - Free a message block

;FREMSG - Free a message block
;
; Call:
;	MB/ Pointer to Message Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

FREMSG:	MOVE T1,MB		;POINT TO THE MESSAGE BLOCK
	TRASH MB,		;CATCH A BUG
	CALLRET DNFMSG		;FREE THE MESSAGE BLOCK
	SUBTTL	Subroutines -- CONBUF - Invoke Conservative Buffering

;CONBUF - Invoke Conservative Buffering
;
; Call:
;	SL/ Pointer to SLB
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1,T2

;If we do support Phase II, then this routine will have to be updated
;to reserve a small number of buffers (eg, 1 in + 1 out) at open time,
;and then the rest of the 'reserve' routines will have to test for
;Phase II and promise never to release this link's reservations, and
;always to use the existing reservation when reallocating.

CONBUF:	BUG.(INF,SCLCBN,SCLINK,SOFT,<Phase-II buffering not implemented>,,<

Cause:	Conservative buffering is not yet implemented.
	We should never have a logical link open to a phase II node.

>)
	SETONE SLPH2,(SL)	;MARK LINK AS A PHASE II LINK
	MOVX T1,FCM.SG		;DO SEGMENT FLOW CONTROL ONLY
	STOR T1,SLRFL,(SL)	;SET THE RECEIVE FLOW CONTROL TYPE
	SETZRO SLGOL,(SL)	;SET THE GOAL TO ZERO
	MOVX T1,SCTP2Q		;GET THE PHASE II QUOTA
	STOR T1,SLINQ,(SL)	;STORE THAT AS THE INPUT QUOTA
	STOR T1,SLOTQ,(SL)	; AND AS THE OUTPUT QUOTA
	RET			;ONLY RETURN
	SUBTTL	Subroutines -- FNDSLB - Find SLB given a channel number

;FNDSLB - Find a SLB from a channel number
;
; Call:
;	T1/ Channel #
;	T2/ Pointer to Session Control Job Block
;
; Return:
;	RET			;COUDLN'T GET IT
;	RETSKP			;FOUND IT OK, SL POINTS TO IT
;
; Uses: T1,T2,T4,SL

FNDSLB:	JUMPLE T1,RTN		;IF NEGATIVE OR ZERO, RETURN FAILURE
	OPSTR <CAMLE T1,>,SJCHC,(T2) ;DO A QUICKIE RANGE CHECK
	RET			;OUT OF BOUNDS
	SOJ T1,			;OUR OFFSET IS ZERO, WHILE USER'S IS ONE
	LOAD T4,SJCHT,(T2)	;GET POINTER TO SLB TABLE
	ADD T4,T1		;FIND ENTRY FOR THIS CHANNEL
	SKIPN SL,(T4)		;GET THE POINTER TO IT
	RET			;NO THERE, GIVE BAD RETURN
	OPSTR <CAME SL,>,SLSLB,(SL) ;MAKE SURE IT POINTS TO AN SLB
	BUG.(CHK,SCLSPF,SCLINK,SOFT,<SLB self pointers messed up in FNDSLB>,<<T1,CHAN>,<T2,SJBPTR>>,<

Cause:	The DECnet data structures for this link are inconsistent.
	If this happens more than once, submit a SPR.

Data:	CHAN - The DECnet channel number
	SJBPTR - Pointer to the SJB

>,RTN)
	TMNE <SLFSL,SLLBC>,(SL)	;IS IT SCHEDULED FOR DESTRUCTION?
	RET			;YES, DON'T LET ON T