Google
 

Trailing-Edge - PDP-10 Archives - BB-M080V-SM_1990 - monitor-sources/sclink.mac
There are 20 other files named sclink.mac in the archive. Click here to see a list.
; Edit= 8948 to SCLINK.MAC on 25-Aug-88 by RASPUZZI, for SPR #22244
;Prevent CFCLDP BUGHLTs and DECnet freespace lossage by having the session
;control layer check a link number before it dishes it out to the job. If too
;large, then cleanup and take an error path back so SCJSYS does the right
;thing from there.
; Edit= 8934 to SCLINK.MAC on 23-Aug-88 by GSCOTT
;Update BUG. documentation. 
; UPD ID= 8617, RIP:<7.MONITOR>SCLINK.MAC.8,  11-Feb-88 18:31:38 by GSCOTT
;TCO 7.1218 - Update copyright date.
; UPD ID= 8452, RIP:<7.MONITOR>SCLINK.MAC.7,   4-Feb-88 17:13:28 by GSCOTT
;More of TCO 7.1210 - Correct spelling in SCLVAS
; UPD ID= 8445, RIP:<7.MONITOR>SCLINK.MAC.6,   4-Feb-88 16:35:02 by GSCOTT
;TCO 7.1210 - Set SCLVAS normally not dumpable.
; UPD ID= 285, RIP:<7.MONITOR>SCLINK.MAC.5,  11-Nov-87 12:20:56 by MCCOLLUM
;TCO 7.1130 - Don't use SL in SCTNIX if it is zero.
; UPD ID= 148, RIP:<7.MONITOR>SCLINK.MAC.4,  29-Sep-87 18:19:21 by MCCOLLUM
;More of TCO 7.1063 - Fix up the table of contents.
; UPD ID= 138, RIP:<7.MONITOR>SCLINK.MAC.3,  24-Sep-87 10:37:15 by MCCOLLUM
;More of TCO 7.1063 - Rename STRTMR to STRTIM to avoid conflict with PROLOG
; *** Edit 7318 to SCLINK.MAC by MCCOLLUM on 11-Jun-86, for SPR #21246
; Alter PARAMETER macro to store values in full words and change some network
; management parameters to accept values in millseconds 
; *** Edit 7302 to SCLINK.MAC by PALMIERI on 11-Jun-86 (TCO 6.1.1596)
; Don't drop connects if Session Version is newer than ours. 
; Edit 7175 to SCLINK.MAC by PALMIERI on 28-Oct-85 (TCO 6.1.1543)
; Fix COMMMS BUGHLTs. 
; UPD ID= 2183, SNARK:<6.1.MONITOR>SCLINK.MAC.93,   5-Jun-85 11:04:46 by MCCOLLUM
;TCO 6.1.1406  - Update copyright notice.
; UPD ID= 2001, SNARK:<6.1.MONITOR>SCLINK.MAC.92,  23-May-85 17:00:12 by NICHOLS
;Add SCLINK interface documentation from MONUSR.RNO so it doesn't get lost.
;Fix up the description of no flow control mode.
; UPD ID= 1939, SNARK:<6.1.MONITOR>SCLINK.MAC.91,   8-May-85 12:27:03 by MCCOLLUM
;TCO 6.1.1238 - Fix more BUG. documentation
; UPD ID= 1910, SNARK:<6.1.MONITOR>SCLINK.MAC.90,   6-May-85 16:43:24 by GLINDELL
;TCO 6.1.1365 - allocate enough memory in RETOAR
; UPD ID= 1829, SNARK:<6.1.MONITOR>SCLINK.MAC.89,  25-Apr-85 16:50:53 by MCCOLLUM
;TCO 6.1.1238 - Fix BUG. documentation
; UPD ID= 1765, SNARK:<6.1.MONITOR>SCLINK.MAC.88,  18-Apr-85 00:20:39 by GLINDELL
;  TCO 6.1.1329 - must check node address existing too before adding node
; UPD ID= 1762, SNARK:<6.1.MONITOR>SCLINK.MAC.87,  17-Apr-85 13:52:30 by GLINDELL
;  Need to be NOINT in LOKNOD
; UPD ID= 1760, SNARK:<6.1.MONITOR>SCLINK.MAC.86,  17-Apr-85 11:34:11 by GLINDELL
;  TCO 6.1.1323 - Make node database dynamic.  Call ASGVAS when we run out
;  of buckets.  Supercede TCO 6.1.1315.  QAR #838204.
;
; UPD ID= 1741, SNARK:<6.1.MONITOR>SCLINK.MAC.85,   9-Apr-85 13:07:25 by GLINDELL
;  TCO 6.1.1315 - Move node database size parameters to STG.
; UPD ID= 1734, SNARK:<6.1.MONITOR>SCLINK.MAC.84,   8-Apr-85 16:06:37 by PALMIERI
;TCO 6.1.1313  Notice if DNCB2M did not copy all bytes when in 36-bit byte
; mode and set flag (P1) accordingly
; UPD ID= 1634, SNARK:<6.1.MONITOR>SCLINK.MAC.83,  13-Mar-85 18:04:53 by PALMIERI
;TCO 6.1.1258  Notice if DNCB2M does not transfer all of the bytes that were
;requested and send them in the following message.
;  Always use default buffer size when establishing connection to a loop node
;
; UPD ID= 1549, SNARK:<6.1.MONITOR>SCLINK.MAC.82,  20-Feb-85 16:17:03 by GLINDELL
;  Fix one BUG. text
;
; UPD ID= 1484, SNARK:<6.1.MONITOR>SCLINK.MAC.81,   5-Feb-85 17:25:57 by GLINDELL
;  TCO 6.1.1173 - LOOP NODE
;  Bug in SCTANL - LSCTN2A does not preserve T1 (of course) and bad code
;   when removing a loop node
;  Also NMXCKL didnt return an error code.
; UPD ID= 1458, SNARK:<6.1.MONITOR>SCLINK.MAC.80,   1-Feb-85 15:01:33 by GLINDELL
;Add PADRC - "dont range check" for the appropriate parameters
; UPD ID= 1457, SNARK:<6.1.MONITOR>SCLINK.MAC.79,   1-Feb-85 14:51:56 by GLINDELL
;Add SLPAS (passive SLB if set) and use that in SLBMAT
; UPD ID= 1386, SNARK:<6.1.MONITOR>SCLINK.MAC.78,  22-Jan-85 11:55:21 by GLINDELL
;  Change name from CSSEON to EV96.0
; UPD ID= 1383, SNARK:<6.1.MONITOR>SCLINK.MAC.77,  22-Jan-85 11:48:58 by GLINDELL
;  Update all BUG. macros
; UPD ID= 1379, SNARK:<6.1.MONITOR>SCLINK.MAC.76,  21-Jan-85 15:00:20 by GLINDELL
;  Make generation of the CSSE event dependent on flag CSSEON
; UPD ID= 1375, SNARK:<6.1.MONITOR>SCLINK.MAC.75,  21-Jan-85 14:44:11 by GLINDELL
;  Add error codes to the node name/number routines
; UPD ID= 1357, SNARK:<6.1.MONITOR>SCLINK.MAC.74,  17-Jan-85 14:46:05 by GLINDELL
;Fix previous edit
; UPD ID= 1342, SNARK:<6.1.MONITOR>SCLINK.MAC.73,  16-Jan-85 11:19:07 by GLINDELL
;Remove TSTS6 tests
; UPD ID= 1305, SNARK:<6.1.MONITOR>SCLINK.MAC.72,  10-Jan-85 17:24:21 by GLINDELL
;Fix buffer negotiating problem caused by VMS not using piggy-back ACK
; UPD ID= 1236, SNARK:<6.1.MONITOR>SCLINK.MAC.71,  26-Dec-84 10:43:15 by NICHOLS
;Fix typo in connect accept segment size negotiation
; UPD ID= 1210, SNARK:<6.1.MONITOR>SCLINK.MAC.70,  14-Dec-84 13:12:33 by GLINDELL
;Yet another typo...
; UPD ID= 1206, SNARK:<6.1.MONITOR>SCLINK.MAC.69,  13-Dec-84 15:14:44 by GLINDELL
;It was a little premature to remove SCTCSS....
; UPD ID= 1205, SNARK:<6.1.MONITOR>SCLINK.MAC.68,  13-Dec-84 10:41:11 by GLINDELL
;Fix SCTCSS (i.e. remove it)
; UPD ID= 1198, SNARK:<6.1.MONITOR>SCLINK.MAC.67,  12-Dec-84 15:45:59 by PALMIERI
;Typo in previous edit
; UPD ID= 1195, SNARK:<6.1.MONITOR>SCLINK.MAC.66,  12-Dec-84 15:00:47 by GLINDELL
;Fix up SLSOB and SLDOB
;Clean up error codes from SCLINK
;Improve 'large buffer' scheme - call RTRCBS on incoming CI
; UPD ID= 1168, SNARK:<6.1.MONITOR>SCLINK.MAC.65,   7-Dec-84 17:51:31 by GLINDELL
;Give correct abort code when an incoming CI is rejected because of resources
;Return 'object too busy' appropriately
; UPD ID= 1162, SNARK:<6.1.MONITOR>SCLINK.MAC.64,   5-Dec-84 17:33:39 by GLINDELL
;Make CHKSTS calculate Output-OK status bit correctly when output buffer quota
;is one.
;Remove inter-section transfers where possible
; UPD ID= 1110, SNARK:<6.1.MONITOR>SCLINK.MAC.63,  20-Nov-84 15:24:57 by GLINDELL
;Merge from TOPS-10: wrong error code when function code was out of range
;Merge from TOPS-10: conversion bytes to words were wrong
; UPD ID= 1095, SNARK:<6.1.MONITOR>SCLINK.MAC.62,  19-Nov-84 10:28:12 by GLINDELL
;Move scheduler test back to section 1
;Need not save any AC's in SCTINI
; UPD ID= 1078, SNARK:<6.1.MONITOR>SCLINK.MAC.61,  14-Nov-84 13:38:31 by GLINDELL
;Fix all IFIW's.
;Clean up jiffy service - resend DRQ's is no longer needed.
;Change jiffy service to be a once-a-second service
; UPD ID= 1002, SNARK:<6.1.MONITOR>SCLINK.MAC.60,   7-Nov-84 15:53:12 by GLINDELL
;TCO 6.1.1035 - Support big buffers on the NI
; UPD ID= 927, SNARK:<6.1.MONITOR>SCLINK.MAC.59,  26-Oct-84 12:24:10 by GLINDELL
;New node name table hashsize - courtesy of Joe Martin. We should now be able
; to store 4534 nodes at a minimum.
; UPD ID= 900, SNARK:<6.1.MONITOR>SCLINK.MAC.58,  19-Oct-84 14:40:19 by GLINDELL
;Move definition of BEGSTR LN to here from D36PAR
;NSPINI does not take arguments any more
; UPD ID= 887, SNARK:<6.1.MONITOR>SCLINK.MAC.57,  15-Oct-84 17:55:44 by GLINDELL
;Put in locks for the node database
;Use DNGWDP/DNGWZP where appopriate
;Change to 3 nodes per bucket
;Update table of contents
; UPD ID= 868, SNARK:<6.1.MONITOR>SCLINK.MAC.56,  10-Oct-84 18:06:34 by GLINDELL
;Move SCLINK to extended code
; UPD ID= 844, SLICE:<6.1.MONITOR>SCLINK.MAC.52,   1-Oct-84 17:09:34 by GLINDELL
;Inserted on behalf of Jim:
;Fix bug in RETOAR, I introduced in edit 768.  Changed the way it calculated
;the numbers of words it needed for the KNOWN NODE bit table.
;
; UPD ID= 833, SLICE:<6.1.MONITOR>SCLINK.MAC.51,  26-Sep-84 17:35:28 by PALMIERI
;Handle -1 error code from NSP OPEN function when LLINKS can't get memory.
; UPD ID= 832, SLICE:<6.1.MONITOR>SCLINK.MAC.50,  26-Sep-84 15:00:36 by NICHOLS
;Rename SLSOB and SLROB (Sender and Receiver Obj Type) to SLDOB and SLSOB
; (Dest and Source Obj Type)
;Clean up TOPS10/TOPS20 Feature Test code in NSFEA
; UPD ID= 789, SNARK:<6.1.MONITOR>SCLINK.MAC.49,   5-Sep-84 11:45:54 by GLINDELL
;Use NOSKED/OKSKED instead of D36OFF/D36ON in SCTANL
; UPD ID= 778, SNARK:<6.1.MONITOR>SCLINK.MAC.48,  31-Aug-84 14:35:35 by GLINDELL
;Remove unused code (node name/number mapping old version)
; UPD ID= 768, SNARK:<6.1.MONITOR>SCLINK.MAC.47,  31-Aug-84 10:53:46 by HALPIN
;Use %RTMXN to allocate space for 'other-area' table instead of IBMXA
;Use IBBLK value instead of RTRMXN to rangecheck node number in CHKADR
;WRTNOD saves the Byte Pointer to the User Buffer in the NFWBLK when
;it exits.
;Put in a "Resource Error" return in RETOAR when it runs out of space
;in the user buffer. This will cause NMLT20 to retry with a larger
;buffer.
;Changed LOAD macro to a LOADE in NMXRET to get correct value of NFSEL
;Changed CAI instructions in NMXRET to CAX's
;
; UPD ID= 709, SNARK:<6.1.MONITOR>SCLINK.MAC.46,  25-Jul-84 17:32:24 by GLINDELL
;Use macro RNMXND when there is no data present on network mgmt requests
; UPD ID= 678, SNARK:<6.1.MONITOR>SCLINK.MAC.45,  12-Jul-84 17:40:46 by HALPIN
;Remove all references to the NF.REX Network management function code.
;
; UPD ID= 668, SNARK:<6.1.MONITOR>SCLINK.MAC.44,   5-Jul-84 16:57:31 by GLINDELL
;Do not generate error return if executor node name/# is changed to itself
; UPD ID= 658, SNARK:<6.1.MONITOR>SCLINK.MAC.43,   2-Jul-84 16:55:33 by GLINDELL
;Log CSSE event
;Include NSP layer definition in EVENT macro
;Support counts SLPKS,SLPKR,SLBYS and SLBYR
; UPD ID= 640, SNARK:<6.1.MONITOR>SCLINK.MAC.42,  27-Jun-84 16:32:26 by GLINDELL
;Correct AC usage in PARAMETER macro
;Rearrange network management part of module
; UPD ID= 621, SNARK:<6.1.MONITOR>SCLINK.MAC.41,  13-Jun-84 09:38:14 by GLINDELL
;Pick up default flow control from IB block
; UPD ID= 615, SNARK:<6.1.MONITOR>SCLINK.MAC.40,  11-Jun-84 13:35:49 by GLINDELL
;Use RNMXER instead of RETBAD
; UPD ID= 612, SNARK:<6.1.MONITOR>SCLINK.MAC.39,   8-Jun-84 17:29:46 by GLINDELL
;More network management - include PARAMETER and COUNTER tables
; UPD ID= 572, SNARK:<6.1.MONITOR>SCLINK.MAC.38,  28-May-84 15:58:37 by GLINDELL
;More network management - implement "check loopback node" and "return list
; of entity ids"
; UPD ID= 539, SNARK:<6.1.MONITOR>SCLINK.MAC.37,  22-May-84 13:22:19 by GLINDELL
;More of previous edit
; UPD ID= 538, SNARK:<6.1.MONITOR>SCLINK.MAC.36,  21-May-84 20:46:52 by GLINDELL
;More of previous edit
; UPD ID= 527, SNARK:<6.1.MONITOR>SCLINK.MAC.35,  18-May-84 09:23:09 by GLINDELL
;New node name/number mapping routines and a swappable database
;New initialization
; UPD ID= 452, SNARK:<6.1.MONITOR>SCLINK.MAC.34,  25-Apr-84 09:30:26 by GLINDELL
;Remove calls to SCTA2N and SCTN2A in preparation for swappable node name/#
; routines. Make "enter active" read loopback circuit ID from connect block.
; UPD ID= 433, SNARK:<6.1.MONITOR>SCLINK.MAC.32,   6-Apr-84 13:36:25 by GUNN
;Remove UPD ID=408 Until I can do it right and test it.
; UPD ID= 278, SNARK:<6.1.MONITOR>SCLINK.MAC.30,  16-Dec-83 11:08:56 by GLINDELL
;Fix NSFSQ to default goal correctly, and adjust argument count tests
; UPD ID= 259, SNARK:<6.1.MONITOR>SCLINK.MAC.29,  22-Nov-83 11:23:39 by NICHOLS
;Add SCLRIB BUG. at SCTRIB
; UPD ID= 258, SNARK:<6.1.MONITOR>SCLINK.MAC.28,  21-Nov-83 18:22:35 by NICHOLS
;Remove edit 248, it was wrong
; UPD ID= 257, SNARK:<6.1.MONITOR>SCLINK.MAC.27,  21-Nov-83 12:48:17 by MCINTEE
;More of previous edit
; UPD ID= 256, SNARK:<6.1.MONITOR>SCLINK.MAC.26,  18-Nov-83 08:40:09 by MCINTEE
;Correction to SLUID & SAUID edit.
; UPD ID= 252, SNARK:<6.1.MONITOR>SCLINK.MAC.25,  16-Nov-83 18:02:38 by PALMIERI
;Add minimal Phase IV suppport
; UPD ID= 250, SNARK:<6.1.MONITOR>SCLINK.MAC.24,  16-Nov-83 15:20:24 by MCINTEE
;Add time stamp to SLB and SAB to detect staleness - fields SLUID & SAUID
; UPD ID= 248, SNARK:<6.1.MONITOR>SCLINK.MAC.23,  15-Nov-83 14:52:21 by NICHOLS
;Add reserved buffer update at FRESLB
; UPD ID= 246, SNARK:<6.1.MONITOR>SCLINK.MAC.22,  13-Nov-83 18:50:12 by GLINDELL
;Fix NSFRQ to return correct input percentage
; UPD ID= 236, SNARK:<6.1.MONITOR>SCLINK.MAC.21,   8-Nov-83 08:34:55 by MCINTEE
;MERGE FROM -10
; UPD ID= 224, SNARK:<6.1.MONITOR>SCLINK.MAC.20,  26-Oct-83 10:58:04 by MCINTEE
;Typo in previous
; UPD ID= 220, SNARK:<6.1.MONITOR>SCLINK.MAC.19,  21-Oct-83 12:21:14 by MCINTEE
;Bug fixes - Race condition in the lock mechanism, bug in NSFSQ
; UPD ID= 217, SNARK:<6.1.MONITOR>SCLINK.MAC.18,  17-Oct-83 07:49:15 by MCINTEE
;Bug in EDIT 211
; UPD ID= 214, SNARK:<6.1.MONITOR>SCLINK.MAC.17,  10-Oct-83 08:20:50 by MCINTEE
;Treat a DC received in CS state as a DI plus a DC to support remote
; nodes which still reject with DCs instead of DIs as in Phase II days.
; UPD ID= 213, SNARK:<6.1.MONITOR>SCLINK.MAC.16,   7-Oct-83 15:36:58 by MCINTEE
;Remove privilege check for ENTER ACTIVE for TOPS20
; UPD ID= 211, SNARK:<6.1.MONITOR>SCLINK.MAC.15,   7-Oct-83 13:59:46 by MCINTEE
;Set up local node name in SCTINI.
; UPD ID= 204, SNARK:<6.1.MONITOR>SCLINK.MAC.14,  30-Sep-83 08:37:19 by MCINTEE
;DNET:SCLINK.MAC[10,36,MON,NEW], 05-Oct-1983 04:04:28, Edit by TARL
;MCO 10994 - Fix loop node stuff.
;DNET:SCLINK.MAC[10,36,MON,NEW], 22-Sep-1983 20:55:00, Edit by TARL
;MCO 10971 - Grand Re-merge of the -10/-20 sources

;	COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION 1984, 1988.
;	ALL RIGHTS RESERVED.
;
;	THIS SOFTWARE IS FURNISHED UNDER A  LICENSE AND MAY BE USED AND  COPIED
;	ONLY IN  ACCORDANCE  WITH  THE  TERMS OF  SUCH  LICENSE  AND  WITH  THE
;	INCLUSION OF THE ABOVE  COPYRIGHT NOTICE.  THIS  SOFTWARE OR ANY  OTHER
;	COPIES THEREOF MAY NOT BE PROVIDED  OR OTHERWISE MADE AVAILABLE TO  ANY
;	OTHER PERSON.  NO  TITLE TO  AND OWNERSHIP  OF THE  SOFTWARE IS  HEREBY
;	TRANSFERRED.
;
;	THE INFORMATION IN THIS  SOFTWARE IS SUBJECT  TO CHANGE WITHOUT  NOTICE
;	AND SHOULD  NOT  BE CONSTRUED  AS  A COMMITMENT  BY  DIGITAL  EQUIPMENT
;	CORPORATION.
;
;	DIGITAL ASSUMES NO  RESPONSIBILITY FOR  THE USE OR  RELIABILITY OF  ITS
;	SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
;TITLE SCLINK - Session Control Layer Control for DECnet-36
	SUBTTL V.Brownell & W.Nichols/Tarl
	SEARCH D36PAR,SCPAR,MACSYM
	SALL

	ENTRY SCTL,SCTINI

IFN FTOPS10,<
.CPYRT<
COPYRIGHT (C) 1988 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
>
> ;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. SCLINK Interface Document (RUNOFF source)  . . . . . .   6
;    2. Definitions
;        2.1    Internal References  . . . . . . . . . . . . .  39
;        2.2    External References  . . . . . . . . . . . . .  40
;        2.3    Accumulators . . . . . . . . . . . . . . . . .  42
;        2.4    Node name/number database  . . . . . . . . . .  43
;        2.5    Loopback node block  . . . . . . . . . . . . .  44
;    3. Macros
;        3.1    Code-Generating
;            3.1.1    IFxSTATE . . . . . . . . . . . . . . . .  45
;            3.1.2    NEWSTATE . . . . . . . . . . . . . . . .  46
;    4. SCTINI - Session Control Initialization  . . . . . . .  47
;    5. SCTPSQ - Grant a PSI interrupt . . . . . . . . . . . .  48
;    6. SCTSTM - Check Active Connect Timers for Expiration  .  49
;    7. SCTSEC - Once-a-second service for SCLINK  . . . . . .  50
;    8. SCTRFJ - Request Second Service for a Link . . . . . .  51
;    9. SCTL's Interlock
;        9.1    Queuing Version  . . . . . . . . . . . . . . .  52
;        9.2    Waiting Version  . . . . . . . . . . . . . . .  53
;        9.3    Flag Version . . . . . . . . . . . . . . . . .  55
;   10. SCTULK - SCTL's Interlock
;       10.1    Unlock Checks  . . . . . . . . . . . . . . . .  56
;   11. NSP.
;       11.1    Function Dispatch Table  . . . . . . . . . . .  57
;       11.2    Wait Check Tables
;           11.2.1    Definitons . . . . . . . . . . . . . . .  58
;           11.2.2    "Before" NSP. table  . . . . . . . . . .  59
;           11.2.3    "After" NSP. table . . . . . . . . . . .  60
;           11.2.4    Routines . . . . . . . . . . . . . . . .  61
;       11.3    SCTNSF - The Entry to SCLINK from SCMUUO . . .  62
;       11.4    NSFRE - REset all links  . . . . . . . . . . .  69
;       11.5    NSFEA - Enter Active State . . . . . . . . . .  70
;       11.6    NSFEP - Enter Passive State  . . . . . . . . .  73
;       11.7    NSFRI - Read Connect Information . . . . . . .  74
;       11.8    NSFAC - Accept the Connect . . . . . . . . . .  75
;       11.9    NSFRJ - Reject the Connect . . . . . . . . . .  77
;       11.10   NSFRC - Read Connect Confirm Information . . .  78
;       11.11   NSFSD - Synchronous Disconnect . . . . . . . .  79
;       11.12   NSFAB - Abort and Release  . . . . . . . . . .  81
;       11.13   NSFRD - Read Disconnect Data . . . . . . . . .  82
;       11.14   NSFRL - Release the Channel  . . . . . . . . .  83
;       11.15   NSFRS - Read the Channel Status  . . . . . . .  84
;       11.16   NSFIS - Send Interrupt Data  . . . . . . . . .  85
;       11.17   NSFIR - Receive Interrupt Data . . . . . . . .  87
;       11.18   NSFDS - Send Normal Data . . . . . . . . . . .  89
;       11.19   NSFDR - Receive Normal Data  . . . . . . . . .  93
;       11.20   NSFSQ - Set Quotas and Goals . . . . . . . . .  95
	Subttl	Table of Contents (page 2)

;		     Table of Contents for SCLINK
;
;				  Section		      Page
;
;
;       11.21   NSFRQ - Read Quotas and Goals  . . . . . . . .  96
;       11.22   NSFJS - Set Job Quotas and Goals . . . . . . .  97
;       11.23   NSFJR - Read Job Quotas and Goals  . . . . . .  98
;       11.24   NSFPI - Set PSI Reason Mask  . . . . . . . . .  99
;   12. RETFLW - Return current flow controls  . . . . . . . . 100
;   13. SNDDRQ - Send Data Requests  . . . . . . . . . . . . . 101
;   14. RLSLNK - Release a Link  . . . . . . . . . . . . . . . 102
;   15. LLINKS Calls
;       15.1    Entry Vector Table . . . . . . . . . . . . . . 103
;       15.2    SCTRIB - Reserve Input Buffer  . . . . . . . . 104
;       15.3    SCTUCG - Uncongestion Call . . . . . . . . . . 106
;       15.4    SCTLCI - Connect Initiate Call . . . . . . . . 107
;       15.5    The Vectored Call Entry Point SCTL . . . . . . 110
;       15.6    SCCCR - Connect Confirmed call from NSP  . . . 111
;       15.7    SCDIR - Disconnect Initiate received call  . . 112
;       15.8    SCDCR - Disconnect Confirm received call . . . 113
;       15.9    SCODN - Output done call . . . . . . . . . . . 114
;       15.10   SCSEG - Segment received call  . . . . . . . . 115
;       15.11   SCDRQ - Data request received call . . . . . . 116
;       15.12   SCNCF - No confidence in port call . . . . . . 117
;       15.13   SCNRS - No resources call  . . . . . . . . . . 118
;       15.14   SCCLS - Close Completed call . . . . . . . . . 119
;       15.15   SCNLK - No link call . . . . . . . . . . . . . 120
;       15.16   SCNCM - No communication call  . . . . . . . . 121
;       15.17   SCNRN - Not in Run State call  . . . . . . . . 122
;       15.18   SCCAK - Got a Connect ACK  . . . . . . . . . . 123
;   16. Subroutines
;       16.1    CHKABO - Check SLABO flag  . . . . . . . . . . 124
;       16.2    CHKPPN - Check self PPN for validity . . . . . 125
;       16.3    FREMSG - Free a message block  . . . . . . . . 126
;       16.4    CONBUF - Invoke Conservative Buffering . . . . 127
;       16.5    FNDSLB - Find SLB given a channel number . . . 128
;       16.6    FNDSBI - Find SLB from SLBid . . . . . . . . . 129
;       16.7    FRESLB - Deallocate a SLB  . . . . . . . . . . 130
;       16.8    FRECBP - Free Connect Block  . . . . . . . . . 133
;       16.9    CDMCBP - Copy User Data from Message Block . . 134
;       16.10   CPMSCB - Copy User Data from Message Block . . 135
;       16.11   FRESJB - Deallocate a SJB  . . . . . . . . . . 136
;       16.12   MAKSLB - Create a SLB, filling in defaults . . 137
;       16.13   MAKSJB - Create a SJB, filling in defaults . . 139
;       16.14   SCSSTS/CHKSTS - Set Status & Inform PSISER . . 140
;       16.15   SCTWKQ - Queue a Link for later call to SCTPSQ 142
;       16.16   SLBMAT - Pattern match connect to passive SLBs 143
;       16.17   CDBMAT - Match Two Connect Blocks  . . . . . . 144
;       16.18   STRMAT - Pattern Matcher . . . . . . . . . . . 147
;       16.19   STPTMR - Stop the Connect Initiate Timer . . . 150
;       16.20   STRTIM - Start the Connect Initiate Timer  . . 151
	Subttl	Table of Contents (page 3)

;		     Table of Contents for SCLINK
;
;				  Section		      Page
;
;
;       16.21   TMRREJ - Send a Reject with Reason RSNNRO  . . 152
;       16.22   BLDCTX - Build connect message . . . . . . . . 153
;       16.23   PRSCTX - Parse a connect message . . . . . . . 157
;       16.24   CPYS2M - Copy string block to message segment  161
;       16.25   CPYM2S - Copy message data to string block . . 162
;       16.26   SCTGSS - Get segment size for a destination  . 163
;       16.27   SCTCSS - Check segment size for a destination  164
;   17. Node name/number database
;       17.1    Initialize . . . . . . . . . . . . . . . . . . 165
;       17.2    Lock/unlock database . . . . . . . . . . . . . 167
;       17.3    Add a node . . . . . . . . . . . . . . . . . . 168
;       17.4    Address to name  . . . . . . . . . . . . . . . 169
;       17.5    Name to address  . . . . . . . . . . . . . . . 171
;       17.6    Name to NO block . . . . . . . . . . . . . . . 172
;       17.7    Default area # . . . . . . . . . . . . . . . . 173
;       17.8    Hash routine . . . . . . . . . . . . . . . . . 174
;       17.9    Get a bucket . . . . . . . . . . . . . . . . . 175
;       17.10   SCTANL - Add a loopback node name  . . . . . . 176
;       17.11   SCTN2L - Convert name to loop circ . . . . . . 177
;       17.12   SCTL2N - Convert loopback circuit  . . . . . . 178
;       17.13   SCTCKL - Check for loopback node . . . . . . . 179
;   18. Network management
;       18.1    Dispatch . . . . . . . . . . . . . . . . . . . 180
;       18.2    SET/CLEAR/READ parameter . . . . . . . . . . . 181
;       18.3    Return list of entity ids  . . . . . . . . . . 182
;       18.4    Map node address to node name  . . . . . . . . 183
;       18.5    Map node name to node address  . . . . . . . . 184
;       18.6    Check loopback node name . . . . . . . . . . . 185
;       18.7    Event
;           18.7.1    Invalid message  . . . . . . . . . . . . 186
;           18.7.2    CSSE event . . . . . . . . . . . . . . . 187
;   19. Local SCTL Storage . . . . . . . . . . . . . . . . . . 188
;   20. End of SCLINK  . . . . . . . . . . . . . . . . . . . . 189
	SUBTTL SCLINK Interface Document (RUNOFF source)

comment `

.chapter Conventions

.hl1 Register Definitions

Since DECnet-36 is designed to be used in both TOPS-10 and TOPS-20, it must
fail to use the standard register definitions of at least one host operating
system.  Therefore, DECnet-36 uses its own register definitions.  The register
names have been chosen to fit in as best as is reasonable with the two target
operating systems.

The names are as follows:

.lit

Register        DECnet-10       TOPS-10         DECnet-20      TOPS-20

0               FREE0           S               FREE0           0
1               P               P               T1              T1
2               FREE1           J               T2              T2
3               CX              R               T3              T3
4               MS              F               T4              T4
5               MB              U               T5              Q1
6               T1              T1              T6              Q2
7               T2              T2              MB              Q3
10              T3              T3              P1              P1
11              T4              T4              P2              P2
12              T5              W               MS              P3
13              T6              M               FREE1           P4
14              P1              P1              FREE2           P5
15              P2              P2              FREE3           P6
16              FREE2           P3              CX              CX
17              FREE3           P4              P               P
.end lit

These assignments are subject to change.

The DECnet-36 registers FREE1, FREE2 and FREE3 are renamed to different names
by the different DECnet-36 modules.  All DECnet-36 registers other than T1
through T6 are treated as preserved registers.  DECnet-36 does not use register
zero (FREE0).  DECnet-36 defines six temporary registers in order to use them
for EXTEND instructions such as MOVSLJ.
.hl1 Data Structure Definitions

All data structures in DECnet-36 are defined with the BEGSTR macro set.  See
D36UNV.MAC for detailed documentation of the macros.

BEGSTR macros all resolve to MSKSTR macros as defined in the standard TOPS-20
universal MACSYM.  Thus all fields defined by BEGSTR can be referenced by the
standard TOPS-20 LOAD and STOR class of macros.

BEGSTR macros define fields in a data structure with names made up of a
two-character structure name ("ss") and up to three characters of field name
("fff").

BEGSTR defines several symbols for each field:
.list
.le;ssfff - this is the symbol by which the LOAD and STOR class of macros
reference a field.  It is also the name of a mask which defines the field's
size and position within a word.
.le;_$ssfff - this is the offset within the data structure of the word in which
the field is defined.
.le;ss.fff - for historical reasons, this is the same as _$ssfff.
.end list

DECnet-36 uses both the LOAD and STOR class of macros and the BEGSTR
definitions separately where such use will not impare the flexibility of the
definitions.
.chapter Session Control Interface
.hl1 The DECnet-36 Call

The basic form of the DECnet-36 call is:
.lit

        XMOVEI T1,addr          ;Argument block pointer (extended)
        PUSHJ P,SCTNSF          ;Call Session Control to do it
        Only Return             ;Error code in argument block (SAERR)

.end lit

T1 points to a Session Control Argument Block (SAB).  The SAB contains a
function code and all the arguments to the function, see the data base section
for details.

.test page 45
.hl2 Summary List of Functions

The DECnet-36 call has the following functions.  The function code is always
in SAAFN.  The DECnet-36 channel number is always in SAACH.

All functions return the after-function status of the link in SAAST.  All
functions ignore SAAST when reading arguments from the user.

The interpretation of the argument words varies with the function.
.lit

Function      Action                 Arg1           Arg2        Arg3

.NSFRE    Reset
.NSFEA    Enter Active State      Connect block   Segsize    Flow-cont
.NSFEP    Enter Passive State     Connect block
.NSFRI    Read Connect Info       Connect block   Segsize    Flow-cont
.NSFAC    Accept Connect          User Data       Segsize    Flow-cont
.NSFRJ    Reject the Connect      User Data
.NSFRC    Read Confirm Info       User Data       Segsize    Flow-cont
.NSFSD    Synchronous Disconnect  User Data
.NSFAB    Abort and Release       User Data
.NSFRD    Read Disconnect Data    User Data       Reason
.NSFRL    Release Channel            --
.NSFRS    Read Channel Status     Segsize         Flow-cont
.NSFIS    Send Interrupt Data     User Data
.NSFIR    Receive Interrupt Data  User Data
.NSFDS    Send Normal Data        Byte-count      Byte-pntr
.NSFDR    Receive Normal Data     Byte-count      Byte-pntr
.NSFSQ    Set Quotas              Link-Quota      %Input     Goal
.NSFRQ    Read Quotas             Link-Quota      %Input     Goal
.NSFPI    Set PSI Reasons         Channel         Reason Mask
.end lit

There are two flags that a user can set when calling DECnet-36.
.list,"."
.le
SAWAI indicates that the user wants to wait for the function to complete
before the monitor call returns.  If this flag is set, the SAHBA address
(q.v.) must be filled in.
.le
SAEOM on a normal data send indicates that an end of message bit is to be sent
with a message.  SAEOM is returned as appropriate on a data read.  If SAEOM is
set on a normal data read call, DECnet-36 will truncate data that overflows
the buffer presented.
.end list
.test page 35
.hl2 DECnet-36 Status

.hl3 DECnet-36 Status Variable

The DECnet-36 status variable is returned in SAAST by all DECnet-36 functions
and is presented to the _@SAWKA (q.v.) routine.

The format of the DECnet-36 status variable is:
.skip
.left margin 10.tab stop 10
.i-10
NSSTA	6-bit field that contains the state of the connection. (one of the
DECnet state values listed below.)
.i-10
NSIDA	Single bit.  If set, there is interrupt data available to be read.
.i-10
NSIDR	Single bit.  If set, then the user may send interrupt data. (data
request and output quota are available.)  DECnet-36 may not be able to send a
message immediately even if this flag is set if there are no message blocks
available in the system.
.i-10
NSNDA	Single bit.  If set, there is normal data available to be read.
.i-10
NSNDR	Single bit.  If set, normal data may be sent (data requests and output
quota are available.)  DECnet-36 may not be able to send a message immediately
even if this flag is set if there are no message blocks available in the
system.
.left margin 0
.sk
The four data and data request available flags are only set if the link is in
an appropriate state for the indicated functions.

.hl3 Port States

The state of the port is important, because certain DECnet functions are legal
only if the port is in certain states and because PSI interrupts due to
changes in state are the best means by which a program using asynchronous I/O
can learn of changes in the state of the logical link.

The DECnet-36 port states and their mnemonic values are:
.left margin 10.tab stop 10
.sk.indent -10
_.NSSCW	Connect Wait
.br;The user has performed a .NSFEP (Enter Passive) function and
is awaiting the receipt of a connect initiate message.
.sk.indent -10
_.NSSCR	Connect Received.
.br;The user has received a connect initiate message and must now
read the connect data and either accept or reject the message.
.sk.indent -10
_.NSSCS	Connect Sent.
.br;The user has performed an .NSPEA (Enter Active) function which
sent a connect initiate message and is now awaiting either a connect confirm
(and entry into the .NSSRN state) or a connect reject (and entry into the
_.NSSDR state).
.sk.indent -10
_.NSSRJ	Reject.
.br;The remote node has rejected this node's Connect Initiate attempt.
The user should read the disconnect data and release the channel.
.sk.indent -10
_.NSSRN	Run.
.br;The link is up and may be used for the transfer of data.
.sk.indent -10
_.NSSDR	Disconnect Received.
.br;The user has received either a connect reject or a disconnect
message.  The user should read the disconnect data and release
the channel.
.sk.indent -10
_.NSSDS	Disconnect Sent.
.br;The user has performed a _.NSFSD (Synchronous Disconnect) function and
is awaiting a disconnect confirm.  During this time, the user must be
prepared to read data from the link (the other end having not yet
received the disconnect), but may not use the link for the
transmission of new data.
.sk.indent -10
_.NSSDC	Disconnect Confirmed.
.br;This state is entered from _.NSSDS when the disconnect is
finally confirmed.  At this point, the only legal operations are release
(.NSFRL) and read status (.NSFRS).
.sk.indent -10
_.NSSNR	No Resources.
.br;This state is enterred from _.NSSCS (Connect Sent) when a No Resources
message is received.
.sk.indent -10
_.NSSCF	No Confidence
.br;DECnet-36 has no confidence in this logical link: the remote node is
not acknowleging messages.  The confidence timer is set by Network Management.
.sk.indent -10
_.NSSLK	No Link
.br;There is No Link: the logical link has been broken by the remote node.
.sk.indent -10
_.NSSCM	No Communication
.br;There is No Communication between this node and the remote node: a
Connect Initiate cannot succeed because there is no communication with
the requested node.
.lm0

.hl3 Channel Numbers

DECnet-36 assigns channel numbers densely starting with channel one.  If a
channel is closed, the channel number will be reused when the process requests
a new channel.  Each set of channel numbers is kept in an SJB (q.v.).

This method of assigning channel numbers allows the user to use the channel
number as an index into a table of links.  This is expected to be used when a
_@SAWKA call (q.v.) is received by a process with several channels open at the
same time.

Please note that after the user releases a channel, either with the release
function of the DECnet-36 call or with a RESET, there is some delay before the
channel number can be reused.  Eg, the first channel number assigned after a
RESET may not be channel 1 if DECnet-36 is still in the process of closing
channels open by a previous program.  DECnet-36 will, however, use the lowest
numbered channel number available.

.test page 45
.hl2 Argument Blocks _& Values

All addresses occupy full words to allow extended addressing.

.test page 45
.hl3 Connect Block Format

The connect block is used to describe the information present in the
NSP connect initiate message.  The format of an internal connect block
is defined in BEGSTR CB with the following fields:
.list,">"
.le;CBNAM: Destination node name (enter active only), up to 6 8-bit bytes.
.le;CBNCT: Node name byte count
.le;CBDST: The destination process block, see process block definition below.
.le;CBSRC: The source process block, see process block definition below.
.le;CBUID: (optional) Source user identification, up to 39 8-bit bytes.
.le;CBUCT: User Id byte count
.le;CBPSW: (optional) Password, up to 39 8-bit bytes.
.le;CBPCT: Password byte count
.le;CBACC: (optional) Account data, up to 39 8-bit bytes.
.le;CBACT: Account data byte count
.le;CBUDA: (optional) User Connect Data, up to 16 8-bit bytes.
.le;CBCCT: User Connect Data byte count
.end list
.tp 45
.hl3 Process Descriptor Blocks

A "process block" is used to name local and remote processes.
The format for a process descriptor block is defined in BEGSTR PB with
fields as follows:
.list,">"
.le;PBLEN: Length field in words.  The process block must be at least
3 (PBL.NM) words long and at most 5 (PBL.MX) words long.
.le;PBFOR: Format type, types are enumerated below.
.le;PBOBJ: Object type.
.le;PBNAM: Up to 16 8-bit bytes of end user name.
.le;PBNCT: Byte count of name in PBNAM field.
.le;PBGRP: Up to 16 bits of group code, the only 16 bits of this
half-word field are sent across the net according to the architecture.
.le;PBUSR: Up to 16 bits of user code, the only 16 bits of this
half-word field are sent across the net according to the architecture.
.end list

The format type is defined by the DECnet architecture:
.list,"."
.le;Format Type 0

The user must specify only a non-zero object type, the other fields must be
zero or have a zero length.
.le;Format Type 1

The user must not specify an object type; the PBOBJ field must be zero.
The user must supply a process name up to 16 bytes long in the PBNAM field.
.le;Format Type 2

The user must not specify an object type; the PBOBJ field must be zero.  The
user must fill in the PBGRP and PBUID fields.  Note that only the low-order 16
bits of each half of a PPN are transmitted.  The user must supply a process
name up to 14 bytes long in the PBNAM field (DECnet uses the other 2 bytes for
the GRP and UID fields).
.end list
Refer to the NSP 3.2 specification for more information about the format types.
.test page 45
.hl3 Flow Control Options

NSP supports three types of flow control.  These are:
.list
.le;_.NSFC0	No flow control (default).
.le;_.NSFCS	Segment flow control.
.le;_.NSFCM	Message flow control (only used for testing).
.els
Each DECnet node choses the flow control mode to be used on the receiving side
of the logical link between them.  DECnet-36 allows the user to chose the flow
control mode to be used on received messages.  No user is expected to use
message flow control mode for production, since it cannot be guaranteed to
work properly in extreme cases, given DECnet-36's buffering methods.
.hl1 Data Base

This section will describe the DECnet-36 data base visible to the user
process.

.hl2 The SJB

One Session Control Job Block (SJB) is kept for each set of channel numbers in
which the caller is interested.  In DECnet-10, one SJB is kept for each Job.
The SJB has job-wide quota information and initial values for some logical
link variables.

The SJB is defined as the BEGSTR structure SJ in SCTPRM.UNV.

All calls to DECnet-36 require that a pointer to an SJB be passed.

The monitor user process is responsible for calling the DECnet-36 subroutine
MAKSJB to create an SJB before calling any other DECnet-36 routine.  See the
subroutines section for more on MAKSJB.

Many fields in the SJB are for DECnet-36's use only and should not be
referenced by the caller in order to preserve modularity.

The fields of the SJB are:
.list,"."
.le;SJGOL - Default input data request goal for logical links.
.le;SJINQ - Default input quota for logical links.
.le;SJOTQ - Default output quota for logical links.
.le;SJSAB - Pointer to current SAB.  Caller is expected to use this to keep
track of allocated SABs.
.le;SJPRV - Job is privileged if this flag is set.
.le;SJRST - RESET in progress for job if this flag is set.  Caller is expected
to set this when initiating a RESET and to clear it when a RESET completes.
Before calling SCTNSF, caller is expected to check this flag.  If it is set,
caller is expected to try the RESET again to see if it succeeds.
.le;SJMUU - Save MUUO word here.  Temp area for the caller.

.le;SJNXT - For DECnet-36 only.  Pointer to the next SJB in the system.
.le;SJCHT - For DECnet-36 only.  Pointer to the SLB table.
.le;SJCHC - For DECnet-36 only.  Highest numbered open channel.
.le;SJPSJ - For DECnet-36 only.  Pointer to system's SJB list header.
.le;SJJSV - For DECnet-36 only.  Job needs jiffy service.
.le;SJCTA - For DECnet-36 only.  Number of Connect Initiate timers active.
.le;SJSLT - For DECnet-36 only.  Initial SLB table with 4 entries.
.le;SJTXQ - For DECnet-36 only.  Input transaction queue.
.le;SJPSQ - For DECnet-36 only.  The PSI queue.
.le;SJINU - For DECnet-36 only.  Buffers used toward input quota.
.le;SJOTU - For DECnet-36 only.  Buffers used toward output quota.
.end list
.hl2 The SAB

The SAB is defined as the BEGSTR structure SA in SCTPRM.UNV.

Its fields are:
.list,"-"
.le;SAWAI: 1 bit

If this flag is not set, the function will return immediately, even if it
cannot do the function requested.  This is asynchronous I/O.  If SAWAI is set,
the function will call the hibernate routine (q.v.) to block until the function
can be completed.  This is synchronous I/O.

.le;SAEOM: 1 bit

This flag only applies to normal data read and normal data send (q.v.).
Specifies whether end of message is to be asserted.  

.le;SABOM: 1 bit

This flag only applies to normal data send (q.v.).
It is not used by DECnet-36, it is simply passed on to the remote node.

.le;SASAT: 1 bit

The "satisfied" flag.  Used internally in DECnet-36 if SAWAI is set to
determine whether to block.

.le;SAERR: half-word

The error code is returned here.  If it is zero, there was no error.

.le;SANAG: half-word

Number of arguments supplied to the function in SAAA1, SAAA2 and SAAA3.
This number does not include the function code or the channel word.

.le;SAAFN: half-word

The function code.  The symbols for the functions are defined in D36UNV as
_.NSFxx, where xx is the two-letter name listed in the function descriptions
below.

.le;SAAST: half-word

The status variable (q.v.)  returned after any function.  Session Control
ignores this field on all calls.

.le;SAACH: half-word

The channel number.  For the Enter Active and Enter Passive functions, this
field is ignored.  For all other functions, it must hold the channel number.

.le;SASJB: full-word

Pointer to the Session Control job block.  This is the block which defines
which set of channel numbers SAACH refers to.
.le;SAAA1, SAAA2 and SAAA3: full-words

Function dependent arguments, see function descriptions below.

.le;SASBP: full-word

Pointer to String Block passed either as an argument to a function or as a
receptacle for a returned value.  This pointer could be placed in one of the
SAAAx variables; it is separated to make deallocation of memory simple for the
caller.  There are no functions which take more than one String Block as
argument.

.le;SAEVA: 1 bit

If set, the buffer described by SASBP is in Executive Virtual Address space,
else the buffer is in User Virtual Address space.

.le;SACBP: full-word

Pointer to a Connect Block passed either as an argument to a function or as a
receptacle for a returned value.  This pointer could be placed in one of the
SAAAx variables; it is separated to make deallocation of memory simple for the
caller.  There are no functions which take more than one Connect Block as
argument.

.le;SABPT: full-word

The byte pointer for a byte-count/byte-pointer pair.  Work area for caller to
use when checking and resolving buffer descriptors.

The caller guarantees that there will be no page faults or other problems when
Session Control tries to access any part of the buffer so described.  Session
Control will access this buffer under the interlock, and thus must not be made
to block.

.le;SABCT: full-word

The byte count for a byte-count/byte-pointer pair.  See SABPT.

.le;SAHBA: full-word

Caller always fills in the address of a routine which Session Control will
call when it wants to block (Hibernate).  See subroutines section, below.

.le;SAWKA: full-word

Caller always fills in the address of a routine which Session Control will
call when it wants to wake a requestor (a job, fork or other entity which is
interested in some event).  See subroutines section, below.

.le;SASLB: full-word

For Session Control's use only; the caller is not expected to reference the
Session Control Link Block at any time.
.end list

The length of the SA block defined above is available as _$SALEN from the
BEGSTR macro.

.tp 55
.hl1 Description of SCTNSF's Functions

When a user process wants to do a DECnet function, it calls SCTNSF with one
argument:
.list
.le
In register T1, a pointer to an SA block.  The function code and further
arguments are in the SA block.  The logical link is specified by a channel
number in SAACH and a pointer to a Session Control Job Block in SASJB.
.end list

.tp 25
.hl2 .NSFRE - Reset

The only argument is a pointer to an SJB in T1.

Operation:

The _.NSFRE function releases all logical links attached to an SJB.  It does
not wait until the closes have completed.  This means that if a job issues a
_.NSFRE and then immediately opens a DECnet channel, it may not get channel
one, for the _.NSFRE may not have finished closing an old incarnation of
channel one.

The _.NSFRE function will succeed unless it was not able to get sufficient
resources to close all of the channels.  In that case it will return a
non-zero error code and the caller is expected to try again after a time.

Given that the caller must retry the function in order that it work properly,
the caller should not give non-monitor users direct access to this function.
The caller should provide an intervening routine which will block and do the
retries if necessary.
.tp 25
.hl2 .NSFEA - Enter Active

Enter Active

Argument list format:
.lit

     SAAFN     EXP .NSFEA
     SAACH     Channel Number
     SAAST     Status Variable returned
     SACBP     Connect block pointer
     SAAA2     Segment size (optional; default: maximum allowed)
     SAAA3     Flow control (optional, privileged; default:  Segment)

.EL

Operation:

This function creates and assigns a new channel.  The channel number assigned
by the package is stored in the SAACH word of the argument block.  See the
note about the assignment of channel numbers under the section on channel
numbers above.

A connect message, as specified by the connect block pointed to by SACBP, is
sent and the link enters the "Connect Sent" state (.NSSCS).
.list,"."
.le
If the SAWAI bit is set in the flags field of SAAFN, the UUO will block
until either a connect accept or connect reject is received.  The UUO will
give a skip return if the connect is accepted and a fail return if the connect
is rejected or if there is any other error detected.
.le
If the SAWAI bit is not set, the UUO will return immediately.  The UUO will
give a skip return unless the arguments passed by the user contained errors.
In this case, the user may determine that the connect has been accepted or
rejected by polling the link for state information (.NSFRS function), or by
enabling the PSI system and waiting for a PSI interrupt.
.end list

Note that the segment size parameter is only passed from the caller to DECnet,
not returned.  In order to find the actual segment size used on a link, use
the .NSFRS (Read Status) function once the link is running.
.tp 25
.hl2 _.NSFEP - Enter Passive
Enter Passive

Argument list format:
.lit

     SAAFN     EXP .NSFEP
     SAACH     Channel number (assigned by this call)
     SAAST     Status Variable returned
     SACBP     Connect block pointer
     SAAA2     Reserved for DEC
     SAAA3     Reserved for DEC

.EL

This call assigns a new NSP channel and stores the channel number into the
SAACH location.  See the note about the assignment of channel numbers under
the section on channel numbers above.

The link is put into the "Connect Wait" (.NSSCW) state and remains in this
state until a matching connect is received or the link is released.
.list,"."
.le
If the SAWAI bit is set in the flags field of SAAFN, the UUO will block
until a connect initiate message is received.  The UUO will give a skip return
if a connect initiate is received and a fail return if any error is detected.
.le
If the SAWAI bit is not set, the UUO will return immediately.  The UUO will
give a skip return unless the arguments passed by the user contained errors.
In this case, the user may determine the state of the port by polling the link
for state information (.NSFRS function), or by enabling the PSI system and
waiting for a PSI interrupt.
.end list

The connect block specified by SAAA1 is used as a pattern for incoming
connect initate messages.  Fields that are zero in the pattern block are
considered to be wild and will match anything.  The only field that must be
specified is the destination process descriptor (.NSCDD).

The object type is a number between 0-377 (octal).  Values between 1
and 177 (octal) can only be set by privileged programs.

When a connect initiate message is received by DECnet-36, it is matched
against the connect blocks of seccessive passive links until a match is found.
This pattern match works in the following way:
.list
.le
The destination process fields of the connect message is matched against the
destination process descriptor in the connect block.  This match takes place
as follows:
.list,"."
.le
The object types must match exactly.
.le
The PPN fields must match exactly except that zero in either the project or
programmer field means that anything will match.
.le
The incoming process name is matched against the process name pattern.  In the
pattern, all characters except "*", "?" and CTRL-V match only themselves.  The
"*" character matches any number of characters, while the "?" will match any
single character.  CTRL-V is used to quote the next character.
.els
.LE
If the destination process matches, the source process descriptor is matched
in the same manner.
.LE
If the source process descriptor matches, the rest of the fields in the
connect message are string matched as described above.
.end list
When a match succeeds, the link is put in the "connect received" (.NSSCR)
state, and control is returned to the user.  At this point the user can read
the connect data and accept or reject the connection.
.tp 45
.hl2 .NSFRI - Read Connect Data

Read connect initiate data

Argument list format:
.lit

     SAAFN     EXP .NSFRI
     SAACH     Channel Number
     SAAST     Status Variable returned
     SACBP     Connect block pointer
     SAAA2     Segment size (optional, returned by UUO)
     SAAA3     Transmit Flow Control on the proposed link.

.EL

Operation:

If the port is in "connect received" state, this UUO reads the data from the
connect initiate message into the connect block.  The user may then examine
the fields before accepting or rejecting the connect.

If the port is not in "connect received" or "idle" state, the UUO fails.  Note
that this means that a logical link's connect data cannot be read after an
accept or reject is issued for that logical link.

If the port is in the idle state and SAWAI was not set in the SAAFN word,
the UUO fails.  If the port is in the idle state and SAWAI was set in the
SAAFN word, the UUO waits for a Connect Initiate message and then returns
connect data when one arrives.
.tp 25
.hl2 .NSFAC - Accept Connect
Accept the connect initiate

Argument list format:
.lit

     SAAFN     EXP .NSFAC
     SAACH     Channel Number
     SAAST     Status Variable returned
     SASBP     Pointer to user data or zero
     SAAA2     Segment Size (optional; default: maximum allowed)
     SAAA3     Flow-Control (optional, privileged; default: segment)

.EL
The user data must not be longer than 16 bytes.

Operation:

If the port is in "connect received" state a connect confirm message
is sent, with data from the user string block.  The port is left in
the "Running" state (.NSSRN).

If the port is not in "connect received" state, the UUO fails.

Note that the segment size parameter is only passed from the caller to DECnet,
not returned.  In order to find the actual segment size used on a link, use
the .NSFRS (Read Status) function once the link is running.
.tp 25
.hl2 .NSFRJ - Reject Connect
Reject the connect

Argument list format:
.lit

     SAAFN     EXP .NSFRJ
     SAACH     Channel Number
     SAAST     Status Variable returned
     SASBP     String Block Pointer to User Data (optional)
     SAAA2     Reason code if greater than zero
     SAAA3     Reserved for DEC

.EL
The user data must not be longer than 16 bytes.

If the port is in "connect received" state (.NSSCR) state, a connect
reject is sent and the channel is released.  Otherwise, the UUO fails.
.tp 25
.hl2 .NSFRC - Read Connect Confirm Data

Read Connect Confirm Data

Argument list format:
.lit

     SAAFN     EXP .NSFRC
     SAACH     Channel Number
     SAAST     Status Variable returned
     SASBP     String Block Pointer to User Data (optional)
     SAAA2     Segment Size
     SAAA3     Transmit Flow Control Mode

.EL
The maximum length of a user data string is 16 bytes, so the count field of
the string string block passed should show at least 16 bytes of space
available.

If the link is not in "Connect Sent" (.NSSCS) or "Running" (.NSSRN) state, the
UUO fails.

If the link is in "Connect Sent" state and
.list,"."
.le;SAWAI is not set, the UUO fails.
.le;SAWAI is set, the UUO waits until a Connect Confirm message is
received, then it returns the Connect Confirm data.
.end list
If the link is in the "Running" state and the user has issued no Read Data
or Write Data functions to this link, the UUO returns the Connect Confirm
data.

If the user issues a Read Data or Write Data function DECnet-36 will discard
any Connect Confirm data which has not yet been read.
.hl2 .NSFSD - Synchronous Disconnect
Synchronous disconnect

Argument list format:
.lit

     SAAFN     EXP .NSFSD
     SAACH     Channel Number
     SAAST     Status Variable returned
     SASBP     String Block Pointer to Disconnect Data (optional)
     SAAA2     Reason code if greater than zero
     SAAA3     Reserved for DEC

.EL
The Disconnect Data must not be longer than 16 bytes.

If the port is in "running" state (.NSSRN), a disconnect initiate is sent to
the remote process and the link is put in the "disconnect sent" state (.NSSDS)
state.  While the link is in this state, the user may not transmit any data,
but must be prepared to read data sent by the other process before it receives
the disconnect.  When the disconnect is confirmed, the link will be placed in
"disconnect confirmed" state (.NSSDC).

If the port is not in "running" state, the UUO fails.

After issuing the Synchronous Disconnect function, the user can wait for a
Disconnect Confirm by issuing the Read Normal Data function with SAWAI set.
This function will return an error when a Disconnect Confirm is received.  If
the user is enabled for DECnet PSI interrupts, DECnet-36 will give a PSI
interrupt when a Disconnect Confirm arrives since the DECnet state will
change.
.tp 25
.hl2 .NSFAB - Abort and Release
Abort and release the link

Argument list format:
.lit

     SAAFN     EXP .NSFAB
     SAACH     Channel Number
     SAAST     Status Variable returned
     SASBP     Pointer to disconnect data (optional)
     SAAA2     Reason code if greater than zero
     SAAA3     Reserved for DEC

.EL
The Disconnect Data must not be longer than 16 bytes.

If the port is in the "running" (.NSSRN) state, this function sends a
disconnect initiate message to the remote process and the link is
released.  In all other port states, this function is illegal.

After the Abort function, the logical link is released: no further functions
are allowed for that logical link.
.tp 25
.hl2 .NSFRD  - Read Disconnect Data
Read disconnect data

Argument list format:
.lit

     SAAFN     EXP .NSFRD
     SAACH     Channel Number
     SAAST     Status Variable returned
     SASBP     Pointer to string block to receive the data
     SAAA2     Reason Code
     SAAA3     Reserved for DEC

.EL

If the port is in "disconnect received" (.NSSDR) state, the data sent
with the disconnect initiate message is returned to the user in the
specified string block.  The state of the link is not changed.  If the
port is in any other state, this function is illegal.
.tp 25
.hl2 .NSFRL - Release Channel
Release the channel

Argument list format:
.lit

     SAAFN     EXP .NSFRL
     SAACH     Channel Number
     SAAST     Status Variable returned
     SAAA1     Reserved for DEC
     SAAA2     Reserved for DEC
     SAAA3     Reserved for DEC

.EL

If the port is in "connect wait" (.NSSCW), "disconnect received"
(.NSSDR) or "disconnect confirmed" (.NSSDC) state, the channel is
released.  Otherwise, the function is illegal.
.tp 25
.hl2 .NSFRS - Read Status
Read Status

Argument list format:
.lit

     SAAFN     EXP .NSFRS
     SAACH     Channel Number
     SAAST     Status Variable returned
     SAAA1     Segment Size for this Link
     SAAA2     XWD rmt-flow-control,local flow control
     SAAA3     Reserved for DEC

.EL

This function, like all others, always returns the DECnet-36 status (q.v.) of
the port in _.NSACH.

The segment size, flow control options will be filled in with the information
available.  If the port is in _.NSSRN (Run) or _.NSSDS Disconnect Sent states,
all values should be known.  Any fields not (yet) known are returned with zero.
.tp 25
.hl2 .NSFIS - Send Interrupt Data
Send interrupt data

Argument list format:
.lit

     SAAFN     EXP .NSFIS
     SAACH     Channel Number
     SAAST     Status Variable returned
     SASBP     Pointer to string block containing the data
     SAAA2     Reserved for DEC
     SAAA3     Reserved for DEC

.EL
The maximum length of the data sent in an interrupt message is 16 bytes.

If the port is in "running" (.NSSRN) state, and there are outstanding
interrupt data requests available, the specified data is sent as an interrupt
message.  Otherwise, the error return is taken with an error code in AC.

The Send Interrupt Data function will never segment a message, even if SAWAI
is not set.  It will either send the whole message or none of it.
.tp 25
.hl2 .NSFIR - Read Interrupt Data
Read interrupt data

Argument list format:
.lit

     SAAFN     EXP .NSFIR
     SAACH     Channel Number
     SAAST     Status Variable returned
     SASBP     Pointer to a string block to receive data
     SAAA2     Reserved for DEC
     SAAA3     Reserved for DEC

.EL
The maximum length of the data sent in an interrupt message is 16 bytes.

If the port is in "running" (.NSSRN) or "disconnect sent" (.NSSDS) state, and
there is interrupt data available, the data is copied into the string block
specified in SAAA1.  If not, an error is given.

The Receive Interrupt Data function will never segment a message, even if
SAWAI is not set.  It will
either receive the whole message or none of it.
.tp 25
.hl2 .NSFDS - Send Normal Data
Send normal data

Argument list format:
.lit

     SAAFN     EXP .NSFDS
     SAACH     Channel Number
     SAAST     Status Variable returned
     SAAA1     EXP byte-count
     SAAA2     EXP byte-pointer to data
     SAAA3     Second word of optional 2-word byte pointer

.EL

This function is legal only if the port is in "running" (.NSSRN) state.

The caller guarantees that the buffer described by SAAA1, SAAA2 and possibly
SAAA3 is entirely paged in and otherwise directly addressable by the monitor.
The byte pointer is expected to have all indexing and indirection resolved.
If the buffer so described is in executive virtual address space then SAEVM
must be 1, else the buffer is read from previous (user) context.  If the
buffer is to be found in a non-zero section, the byte pointer may be 1- or
2-word global, occupying SAAA2 and SAAA3.

Data is copied from the memory indicated by the caller to monitor buffers.  As
the data is copied, the byte-count is decremented and the byte-pointer
advanced.

Unless the SAWAI bit is set in the flags field of SAAFN, the UUO will return
when the users quota of network buffers is exhausted.  In this case, the byte
count will still be non-zero.
.note Please Note
Both the _.NSFDR (receive) and _.NSFDS (send) functions, when used with the
SAWAIt bit off (asynchronous I/O), will give a SUCCESS return whether or not
they have actually sent (received) any data.  It is the responsibility of the
caller to check the byte count field to determine the amount of data sent
(received).  A transient condition such as no data requests available or no
data available is not considered an error for asynchronous I/O.
The error (non-skip) return is only taken on a permanent error.
.end note

A message will be sent across the network whenever any of the follow happen:
.ls
.LE
The SAEOM bit is set in the flags field of SAAFN.
.LE
The number of bytes already given to the monitor exceeds the segment size of
the link.
.els

Segmentation of messages is always left to the monitor.  The user controls the
sending of complete messages by setting the SAEOM bit in the argument block
when using this function.

The byte pointer (SAAA2) is a normal DECsystem-36 byte pointer.  Bytes are
fetched as if with an LDB instruction and either left zero filled or truncated
to 8-bit bytes.
.tp 25
.hl2 .NSFDR - Receive Normal Data
Receive normal data

Argument list format:
.lit

     SAAFN     EXP .NSFDR
     SAACH     Channel Number
     SAAST     Status Variable returned
     SAAA1     EXP Maximum Number of Bytes
     SAAA2     Byte Pointer to Data Buffer
     SAAA3     Second word of optional word byte pointer
.EL

This function is legal if the port is in "Running" (.NSSRN) or "Disconnect
Sent" (.NSSDS) state.

This function reads an incoming normal data message.

The caller guarantees that the buffer described by SAAA1, SAAA2 and possibly
SAAA3 is entirely paged in and otherwise directly addressable by the monitor.
The byte pointer is expected to have all indexing and indirection resolved.
If the buffer so described is in executive virtual address space then SAEVM
must be 1, else the buffer is read from previous (user) context.  If the
buffer is to be found in a non-zero section, the byte pointer may be 1- or
2-word global, occupying SAAA2 and SAAA3.

When the call returns, SAAA1 (byte count) word is decremented by the number of
bytes transferred, and the SAAA2/SAAA3 word in advanced by the number of bytes
transferred.  To determine the number of bytes returned, the caller must
subtract the value returned by the call from the value originally specified in
the call.
.note Please Note
Both the _.NSFDR (receive) and _.NSFDS (send) functions, when used with the
SAWAIt bit off (asynchronous I/O), will give a SUCCESS return whether or not
they have actually sent (received) any data.  It is the responsibility of the
caller to check the byte count field to determine the amount of data sent
(received).  A transient condition such as no data requests available or no
data available is not considered an error for asynchronous I/O.
The error (non-skip) return is only taken on a permanent error.
.end note

This function will never read data from more than one message in a single call.

The function's behavior is governed by the SAWAI and SAEOM flags in the
SAAFN word.
.list,"."
.le;SAWAI

If the user sets the SAWAI flag, DECnet-36 will wait until either an entire
message is copied into the user's buffer or until the user's buffer is full
before returning control to the user.  The SAEOM flag will be set
if the buffer contains the End of Message, else it will be cleared.

If the user clears the SAWAI flag, DECnet-36 will return to the user
immediately.  Any data queued for the user and which fits in the user's buffer
will be returned to the user.  The SAEOM flag will be set appropriately.
.le;SAEOM

If the user clears the SAEOM flag in the arguments passed to this function,
DECnet-36 is allowed to return part of a message in the buffer offered.  If
the received data will fit, the SAEOM flag is set on return, else it will be
cleared and further _.NSFDR calls will be required to read the rest of the
message.  No data will be discarded if the SAEOM flag is cleared by the user.

If the user sets the SAEOM flag in the arguments passed to this function,
DECnet-36 will try to fit a whole message into the user buffer offered.  If
the message received is too big to fit, the excess data will be discarded.

If data is discarded, DECnet-36 will set the buffer count to negative the
number of bytes discarded; thus a returned buffer count of zero or less
implies that the buffer is full.

It is illegal to call this function with the SAEOM flag set and the SAWAI
flag clear.  This is to avoid the deadlock which would be possible if the
remote user were to send a message larger than the user's monitor buffer
quota: the user would not be able to read data until the end of message
arrived, but the end of message would never arrive because there is no room
for it.
.end list
.tp 25
.hl2 _.NSFSQ - Set Quotas and Goals

Argument list format:
.sk
.lm 12.tab stop 12
.i-8;SAAFN	Flags+XWD _.NSFSQ,3
.i-8;SAACH	Channel Number
.i-8;SAAST	Status Variable returned
.i-8;SAAA1	Link Quota
.br;Any user may set this argument.  The quota of commit buffers for this
logical link.  The total of all the link quotas for a single job may exceed
the job quota, but the monitor will never assign more buffers than the job
quota allows.  This allows the user to overbook the job quota as an airline
would overbook plane seats.  The default is infinity: ie, use only the job
quota.  Any values set by this function are ignored for links to a Phase II
node.
.i-8;SAAA2	% Input
.br;Any user may set this argument.  Percent of quota allocated to input.
This is stated as a percent so that the user can default the quota and still
affect the proportion of input to output monitor buffers for this logical
link.  Range is 0 to 100, default is 50%.
.i-8;SAAA3	Input Goal
.br;Only a privileged user (JACCT, [1,2] or Poke) user may set this value.
Number of input data requests NSP is to keep outstanding to the remote node.
See below for discussion of goals.  The default is set by the system
administrator.
.i-8;	Job Quota can be read/set with the _.NSFJR/.NSFJS functions.
(Privileged functions)

.i-12;Operation
.i-8;State	######Action
.i-8;any	Set those variables the user is privileged to change.
If any of the arguments has a value of -1 or is beyond the end of the argument
block passed, it is defaulted.  The default is the value set for the link by
Session Control or the value last stored by the _.NSFSQ function.  We cannot
use zero for the defaulting value, because some of the arguments can
legitimately be zero.
.lm 0

The following discussion deals with flow control.  There are three flow
control modes in DECnet: segment, message and no flow control.  Flow is
controlled as follows: a node which wants to send data (the source) is not
allowed to send a message segment until it has a data request from the
intended receiver.  In segment flow control mode, the receiver must request
each message segment separately.  In message flow control mode, the receiver
requests each message (which may be broken into an arbitrary number of
segments for transmission across the network).  In no flow control mode, no
data requests are asked or given.

When a logical link is opened, each receiver specifies the flow control mode
to be used for messages sent to it.  DECnet-36 will always specify segment
flow control, though it will be able to handle all the flow control modes for
transmission.  Only segment flow control is acceptable to DECnet-36's input
buffering mechanism.

In order for the network to keep message segments flowing as smoothly as
possible, DECnet-36 can be asked to send data requests before the user issues
a read.  DECnet-36 is prepared to queue message segments which arrive before
the user issues a read for them.

The input goal controls the number of data requests DECnet-36 will try to keep
outstanding at the remote node.  In the simple case, this means that whenever
DECnet-36 receives a message segment it will send enough data requests to the
remote node to bring the total outstanding data requests to the goal.
Actually, DECnet-36 will not acknowledge a received message segment until it
fits into the job or link quota (whichever is smaller).  DECnet-36 will not
send another data request until a received message is acknowleged
("committed").

If the input goal is larger than the appropriate quota, this creates a pool of
"cached" messages which have been received but not yet "committed".  DECnet
allows cached messages to be discarded at any time, since the source node will
resend them after a timeout.

DECnet-36 expects the system administrator to set job quotas so that it is
very unlikely that the system will run out of free message buffers when all or
almost all jobs are using their full quota.  Were this conservative rule not
modified, this could result in inefficient use of system memory, so DECnet-36
offers the cache feature so that jobs can borrow from the system buffer pool.
The system administrator can adjust the job quotas and input goals in such a
way that system memory is used reasonably efficiently, but the cached messages
will seldom have to be discarded.
.tp 25
.hl2 _.NSFRQ - Read Quotas and Goals

The argument list format is exactly the same as that for the _.NSFSQ
(Set Quota) function.  The length of the argument block determines
which values will be returned.  Values passed in the argument list are
ignored.
.hl2 _.NSFPI - Set PSI Reason Mask

Argument list format:
.lit
     SAAFN     EXP .NSFPI
     SAACH     Channel Number
     SAAST     Status Variable returned
     SAAA1     XWD reason mask,0 (same fields as status variable)
.end lit

The default PSI reason mask is all ones, i.e., all reasons are enabled for that
logical link.  Thus, when the user issues a PISYS_. UUO for the _.PCNSP
condition, any "interesting change" (see below) will cause a PSI interrupt.

The PSI reason mask is an 18-bit quantity with fields which correspond to the
fields in the DECnet-36 status variable.

When the _.NSFPI function is executed, DECnet will simulate a status change
from zero to the current status for the affected logical link.  This will
cause a call the the _@SAWKA routine with an old status of zero and a new
status copied from the current status of the affected logical link.  The
changes argument will also be a copy of the current status.  This is to allow
users who have newly set a PSI mask to rescan the current status in light of
the new PSI mask.
.hl1 Subroutines Provided by DECnet-36

All pointers used by DECnet-36 are full words to handle extended addressing.
Most byte pointers are standard local byte pointers which use indexes to
access other sections, rather than two-word or one-word global byte pointers.

.hl2 MAKSJB - Make a New SJB

The SJB is described in the Data Base section of this document.

The arguments to MAKSJB are passed in T1 and defined in the BEGSTR structure
PD.
.list
.le;PDGOL (12 bits), default goal for logical links on this SJB.
.le;PDDQT (12 bits), default quota for logical links on this SJB.
.le;PDIPR (12 bits), default input percentage for logical links on this SJB.
.end list
MAKSJB takes the non-skip return if it cannot get enough free memory for an
SJB. 

MAKSJB takes the skip return on success, with a pointer to the new SJB in T1.
.hl2 SCTPSQ - Read PSI Queue

SCTPSQ returns the channel number and link status for the next logical link on
the DECnet-36 PSI queue.

The DECnet-36 PSI queue is expected to be used as in this sequence:
.list
.le
An input-done interrupt arrives, the message is queued in DECnet-36.
DECnet-36 will call the user's wake routine (_@SAWKA).
.le
If the user's wake routine finds that the interrupt is interesting, it will
call SCTWKQ (q.v.) to queue the logical link, and then wake the user process
(be it a real user or a monitor process).
.le
Some time later, the interested process will be scheduled and will want to
know what happened.  It then calls SCTPSQ to find out which logical links have
become interesting.
.end list

The caller is expected to put the address of an SJB in register T1 as an
argument to SCTPSQ.

SCTPSQ only takes a non-skip return.

SCTPSQ returns data described by the BEGSTR structure PS in SCTPRM.UNV.
It is returned in T1 and T2.  T3 and T4 are changed unpredictably.
.list,">"
.le;PSMOR: If this flag is set, there are more logical links on the PSI queue
for this process.
.le;PSPSM: Half-word of PSI mask, see _.NSFPI for description.
.le;PSSTS: The DECnet-36 status half-word for this logical link.
.le;PSCHN: The channel number relative to this process's SJB.
.end list

A side effect of calling SCTPSQ is that the logical link dequeued by the call
is made eligible again for wake calls to _@ SAWKA (q.v.).

.hl2 Node Name Manipulators

DECnet-36's SCLINK module offers several subroutines for manipulating node
names and node addresses:
.list
.le;SCTAND - Add NoDename

The caller places the node address (a binary number) in T1, a byte count in T2
and a byte pointer to a node name string (8-bit bytes) in T3.  SCTAND gives a
skip return if successful in adding the node name to the data base.

.le;SCTRND - Remove NoDename

Remove node name indicated by a byte count in T1 and a byte pointer in
T2.

.le;SCTA2N - Translate Address to Name

The caller places a node address in T1.  SCTA2N will take the skip return if
successful with a byte count in T1 and a byte pointer in T2.

.le;SCTN2A - Translate Name to Address

The caller places a byte count in T1 and a byte pointer to a name string in
T2.  If successful, SCTN2A gives a skip return with a node address in T1.

.le;SCTLNN - List Node Names

This subroutine is intended for Network Management's NMXCON module.
The caller places the full-word address of a routine to be called once for
each node name in the DECnet-36 data base.

.end list
.hl2 Initialization

When the host operating system is ready to start DECnet-36, it must call
SCTINI to initialize DECnet-36.  SCTINI will call the initialization routines
in the other layers.  In T1, the caller passes our local node number, in T3
the caller passes a flag (XP%PH2) which says that this copy of DECnet-36 is
capable of handling Phase II messages.  If we ever decide to have multiple NSP
layers running on top of a single Transport layer, then only one of the
NSP/Session Control pairs would get the XP%PH2 flag set in its initialization
call.

.hl1 Subroutines Provided by Caller

.hl2 _@SAHBA

DECnet-36 will call the subroutine whose address is in SAHBA (q.v.) when it
is called upon to block.  This would happen only when the caller has set the
SAWAIt bit (q.v.) and the conditions necessary for the function to complete
are not present.  Eg, a _.NSFDR (data read) function with SAWAI set will block
if there are no messages in the input queue.

The address of the SJB is passed in register T1.

DECnet-36 will have given up its interlock before calling this routine.

The routine is expected to save all ACs except Zero and T1 through T6 (see AC
definition section).

Only a non-skip return is expected.

.hl2 _@SAWKA

DECnet-36 will call the subroutine whose address is in SAWKA (q.v.) when:
.list,"."
.le
The logical link state changes.  Eg, The logical link changes from Connect
Wait state to Run state.
.le
One of the four status bits changes from a zero (false) to a one (true).
Eg, Normal Data Available changes from false to true, but not vice versa.
.le
The logical link in question is NOT queued on the PSI queue described below.
If the logical link is already queued, it is assumed that the caller is
already aware of the wake request.  We need this test to avoid a race.
.end list

Session Control will NOT have given up its interlock before calling this
routine.  Therefore, this routine may not call DECnet-36 at any entry point
other than SCTWKQ described below.

The routine is expected to save all ACs except Zero and T1 through T6 (see AC
definition section).  

When Session Control calls the wake routine, it will pass:
.list,"."
.le;T1: XWD <old status>,<PSI mask>
.le;T2: XWD <new status>,<channel number>
.le;T3: Pointer to the SJB
.le;T4: Link Identifier to be passed to SCTWKQ
.end list

The PSI mask is set for a channel by the _.NSFPI (set PSI) function.  It is
presented so that we wake routine can decide whether or not to cause a PSI
interrupt for the user.  It is not used by SCLINK.

If the caller elects to use the SAWAI flag and thus the blocking facility of
Session Control, then the wake routine must be able to undo the effect of the
hibernate routine whose address is passed in SAHBA.

The routine always takes a non-skip return.

Even if the caller never expects Session Control to block on its behalf, this
routine should be provided.  It may be null.

A special routine called SCTWKQ is provided in DECnet-36 to be called only
from the _@SAWKA routine.  It is called if the user would like to queue this
logical link for later processing, perhaps in a different context.  The
subroutine SCTPSQ will dequeue the logical link.  See SCTPSQ for more details.

To call SCTWKQ, move the logical link identifier from T4 to T1 and CALL
SCTWKQ.  SCTWKQ will only take a non-skip return.

`;End of comment around interface document
	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

;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		;   "         "       "       "        "
	EXT TIMBAS		;[7318]Fractions of a second used for time
				;[7318] computation
;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 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 %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.

	SJB=P3			;[8948] SJB points to session control job block
	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 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:	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.  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.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.
>)
	  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
	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,<
	CALL INTLVL##		;ARE WE AT UUO LEVEL?
	  CALL 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
	JUMPLE SL,SCTNX1	;[7.1130]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.  To solve this, find who called SCTNIE
	with T1/ 0 and correct the caller's behavior.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.
>)
	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
	SKIPN 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 STRTIM		;[7.1063]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>
	SKIPGE FORKX		;[8948] Must be fork context
	IFSKP.			;[8948] If so,
	  OPSTR <SKIPN >,SJMXP,(SJB) ;[8948] Does SJB have a maximum?
	  IFSKP.		;[8948] If so, check channel limit
	    OPSTR <CAMLE T1,>,SJMXP,(SJB) ;[8948] Too many channels already?
	    SCERR %NERES,NSFEPC,<Resource failure, all channels in use> ;[8948]
	  ENDIF.		;[8948]
	ENDIF.			;[8948]
	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
	OPSTR <SKIPGE T1,>,SAAA1,(SA) ;GET THE LENGTH OF THE USER'S BUFFER
	  SCERR %NEADE,RTN,<Address Error> ;DON'T ALLOW NEGATIVE COUNT
	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

;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

	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.  Find out what is in LLINK's ELSCB and
	why its not an SLB pointer.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.

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 STRTIM		;[7.1063]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.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.
>)
	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. 

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.

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 THAT ITS HERE
	RETSKP			;RETURN WITH SUCCESS
	SUBTTL Subroutines -- FNDSBI - Find SLB from SLBid

;FNDSBI - Find SLB from a SLBid
;
; Call:
;	T1/ SLBid
;
; Return:
;	RET			;FAILED
;	RETSKP			;SUCCESS WITH SL POINTING TO SLB
;
; Uses: T1

FNDSBI:	TMNE SLFSL,(T1)		;SLB SCHEDULED FOR DESTRUCTION?
	RET			;YES, TELL CALLER IT DOESN'T EXIST
	SKIPE SL,T1		;EASY ISN'T IT?
	AOS (P)			;WE'RE OK
	RET			;BLEW IT
	SUBTTL Subroutines -- FRESLB - Deallocate a SLB

;FRESLB - Deallocate a SLB
;
; Call:
;	SL/ Pointer to SLB
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6

FRESLB:	SAVEAC <P1,P2>
	OPSTR <SKIPN P2,>,SLSJB,(SL) ;LOAD UP THE JOB BLK POINTER
	BUG.(CHK,SCLSLB,SCLINK,SOFT,<SLB bad at FRESLB>,<<SL,SLBPTR>>,<

Cause:	There is no Session Control Job Block (SJB) for this Session Control
	Link Block (SLB).  This error could have happened at any time during
	the life of the link after it was actively transferring data.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.

Data:	SLBPTR/ pointer to the SLB that lacked a SJB pointer

>,RTN)
	LOAD T1,SLCHN,(SL)	;GET THE CHANNEL NUMBER OF LINK
	LOAD T4,SJCHT,(P2)	;GET POINTER TO SLB TABLE IN THE SJB
	ADDI T4,-1(T1)		;FIND THE ENTRY FOR THIS CHANNEL
	CAME SL,(T4)		;MAKE SURE THERE'S ONE THERE
	BUG.(CHK,SCLTFS,SCLINK,SOFT,<Tried to free wrong SLB>,<<SL,SLBPTR>>,<

Cause:	The channel table entry didn't point to the correct SLB.  There is an
	internal inconsistency in the DECnet data structures for this link.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.

Data:	SLBPTR - Pointer to the bad SLB

>,RTN)
	SETZM (T4)		    ;ZERO CHANNEL TABLE ENTRY
	XMOVEI P1,SS.INQ+SL.NSL(SL) ;POINT TO NORMAL SUBLINK'S INPUT Q
	CALL CLNQUE		    ;GET RID OF ALL MESSAGES ON THE QUEUE
	XMOVEI P1,SS.INQ+SL.OSL(SL) ;SAME FOR OTHER SUBLINK'S INPUT Q
	CALL CLNQUE
	OPSTR <SKIPE T1,>,SLOTM,(SL) ;IF WE HAVE A PART FILLED OUTPUT MSG
	CALL DNFMSG		     ; THEN FREE IT
	OPSTR <SKIPE T1,>,SLCBP,(SL) ;IF WE HAVE AN INTERNAL CONNECT BLK
	CALL DNFWDS		     ; THEN FREE IT
	OPSTR <SKIPE T1,>,SLCDM,(SL) ;IF WE HAVE A DISCONNECT MSG BLK
	CALL DNFMSG		     ; THEN FREE IT

;Here we remove any buffer reservations attached to this SLB.

	D36OFF			;#KEEP LLINKS (SCTRIB CALL) OUT OF THIS
	LOAD T1,SLOTU,(SL)	;#GET OUTPUT BUFFERS IN USE
	LOAD T2,SLINU,(SL)	;#GET INPUT BUFFERS IN USE
	SKIPG T3,T1		;#ANY INPUT BUFFERS RESERVED?
	JUMPE T2,FRESL1		;#NO, NO RESERVATIONS IF NEITHER IN USE
	ADD T3,T2		;#GET TOTAL BUFFERS WHICH WERE IN USE
	SKIPE T1		;#WERE THERE ANY INPUTS IN USE?
	SKIPN T2		;#YES, ANY OUTPUTS?
	AOS T3			;#NO OR NO, ONE MORE WAS RESERVED FOR
	MOVNS T3		;# OTHER DIRECTION
	ADDM T3,DCNRSB		;#UPDATE RESERVED BUFFER COUNT
FRESL1:	D36ON			;#END CRITICAL SECTION
;Here we remove this SLB from the PSQ if it had a PSI waiting.

	TMNN SLPSI,(SL)		;ON THE PSI Q?
	JRST FRESL2		;NO, NO WORRY NOW
	D36OFF			;#PSI Q DOESN'T RESPECT SCT INTERLOCK
	RMVQUE SL,SJ.PSQ(P2),SL.NXP,T1 ;#REMOVE SLB FROM PSI Q
	D36ON			;#FINISHED WITH THE CRITICAL SECTION
FRESL2:

;Here we remove the SLB from the second request Q if need be

	RMVQUE SL,SCTJFQ,SL.JFQ,T1 ;THIS IS INTERLOCKED

;Here we remove the SLB from the All SBLs Queue

	RMVQUE SL,SCTASQ,SL.ASQ,T1 ;THIS IS INTERLOCKED

;Here we clean out the SLB's serial number

	SETZRO SLUID,(SL)

;Here we free up the SLB's memory

	MOVE T1,SL		;SET UP FOR CALL TO DNFWDS
	SETZ SL,		;JUST FOR KICKS
	CALLRET DNFWDS		;FREE THE WORDS AND RETURN


;Routine to clean out a queue of message blks pointed to by P1.

CLNQUE:	DEQUE T1,(P1),MB.NXT,RTN ;GET NEXT MB, RETURN IF Q IS EMPTY
	CALL DNFMSG		;FREE MESSAGE POINTED TO BY T1
	JRST CLNQUE		;LOOP FOR NEXT ONE
	SUBTTL Subroutines -- FRECBP - Free Connect Block

;FRECBP - Free Connect Block
;
; Call:
;	SL/ Pointer to SLB
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T2

;We try to free up connect data blocks in decending order of importance.
;First, we try to get rid of a message blocks which interrupt level may have
;left for process level by copying them to connect blocks.  (Interrupt level
;can't copy for fear of failing to get a connect block from free memory.)
;Whether or not we can get rid of the resulting connect block, we have freed a
;quota-controlled message block.
;Then we try to free any connect block which may be waiting, subject to the
;user's request that we keep it.

FRECBP:	SETZRO SLCCB,(SL)	;DON'T NEED TO CHECK CONNECT BLK ANY MORE
	TMNN SLCDM,(SL)		;ANY WAITING DIS/CONNECT DATA MESSAGE?
	JRST FRECB1		;NO
	CALL CDMCBP		;YES, FREE UP MESSAGE BY COPYING IT TO CBP
	  JFCL			;COULDN'T WE'LL TRY AGAIN NEXT TIME
FRECB1:	TMNN SLCBP,(SL)		;IS THERE A CONNECT BLOCK?
	RET			;NONE THERE, LEAVE NOW
	TMNE SLKCB,(SL)		;USER WANT US TO KEEP CONNECT BLOCK?
	RET			;YES, DON'T RELEASE IT.
	LOAD T2,SLSTA,(SL)	;GET LINK'S STATE
	IFNSTATE T2,RN,RTN	;IF NOT RUN STATE, MIGHT BE DISCON DATA
	LOAD T1,SLCBP,(SL)	;GET POINTER TO CONNECT BLOCK
	CALL DNFWDS		;(T1) FREE THE BLOCK
	SETZRO SLCBP,(SL)	;REMEMBER WE'VE LOST IT
	RET			;THAT'S ALL
	SUBTTL Subroutines -- CDMCBP - Copy User Data from Message Block

;CDMCBP - Copy User Data from @SLCDM to @SLCBP
;
; Call:
;	SL/ Pointer to SLB
;
; Return:
;	RET			;IF INVALID MESSAGE, EVENT ALREADY CALLED
;	RETSKP			;ON SUCCESS
;
; Uses: T1-T2

CDMCBP:	SAVEAC <MB,MS>
	OPSTR <SKIPN MB,>,SLCDM,(SL) ;ANY DISCONNECT MESSAGE?
	RETSKP			;NO, NOTHING TO COPY
	TMNE SLCBP,(SL)		;YES, IS THERE A CONNECT BLOCK
	JRST CDMCB1		;YES, ONE OF THEM TOO, COPY THE GOODS NOW
	MOVEI T1,CB.LEN		;NO, ALLOCATE ONE FIRST
	CALL DNGWDZ		;GET A ZEROED BLOCK
	  SCERR %NEALF,RTN,<Allocation failure>
	STOR T1,SLCBP,(SL)	;STORE CONNECT BLOCK POINTER IN SLB

CDMCB1:	MOVE T1,MB		;POINT TO THE MESSAGE BLOCK
	CALL DNGINI		;SET UP TO GET SOME BYTES OUT

	SETZRO SLCDM,(SL)	;REMEMBER WE FREED IT
	CALL CPMSCB		;COPY USER DATA TO CONNECT BLOCK
	  RETSKP		;INVALID MESSAGE, WE'VE DONE BEST WE CAN
	CALL FREMSG		;DONE WITH THE DISCONNECT MESSAGE NOW
	RETSKP			;SUCCESS RETURN
	SUBTTL Subroutines -- CPMSCB - Copy User Data from Message Block

;CPMSCB - Copy User Data from Message Block to Existing Connect Block
;
; Call:
;	SL/ Pointer to SLB, SLCBP points to Connect Block
;	MB/ Pointer to Message Block
;	MS/ Set up for D36COM message block routines to copy user data
;
; Return:
;	RET			;IF INVALID MESSAGE, EVENT ALREADY CALLED
;	RETSKP			;ON SUCCESS
;
; Uses: T1-T2

CPMSCB:	CALL DNG1BY		;GET FIRST BYTE = LENGTH OF USER DATA
	  SETZ T1,		;ZERO LENGTH IF NO LENGTH BYTE PRESENT
	CAIG T1,^D16		;LONGER THAN ARCHITECTURAL LIMIT OF 16?
	JRST CPMSC1		;NO, ITS OK
	MOVEI T1,^D16		;YES, TOO LONG, TRUNCATE TO 16 BYTES
	CALL CPMSC1		; COPY WHAT WE CAN BEFORE SCEIVM FREES MSG
	  RET			; OOPS, INVALID MESSAGE EVENT ALREADY GIVEN
	CALLRET SCEIVM		; GIVE INVALID MESSAGE EVENT

CPMSC1:	LOAD T4,SLCBP,(SL)	;T4 POINTS TO CONNECT BLOCK
	STOR T1,CBCCT,(T4)	;STORE LENGTH OF USER DIS/CONNECT DATA
	JUMPE T1,RSKP		;SUCCESS NOW IF NO USER DATA
	MOVX T2,<<POINT 8,>!1B12> ;2-WORD BYTE POINT PTR TO CONNECT BLOCK'S
	XMOVEI T3,CB.UDA(T4)	  ; USER-CONNECT-DATA BLOCK
	CALL DNCM2B		;(T1,T2,T3,MS)COPY MSG BLK TO MONITOR BUFFER
	  CALLRET SCEIVM	;TOO SHORT, GIVE INVALID MESSAGE EVENT
	RETSKP			;JUST RIGHT, SUCCESS
	SUBTTL Subroutines -- FRESJB - Deallocate a SJB

;FRESJB - Deallocate a SJB
;
; Call:
;	T1/ Pointer to SJB
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T2

	XRENT FRESJB

	SAVEAC <P1,T5,T6>
	SKIPN P1,T1		;POINTER TO SJB
	RET			;JUST CHECKING
	LOAD T1,SJCHT,(P1)	;GET POINTER TO THE SLB TABLE
	LOAD T2,SJCHC,(P1)	; AND THE COUNT OF ENTRIES
	JUMPLE T2,FRESJ2	;DON'T CHECK IF COUNT IS ZERO
FRESJ1:	SKIPE (T1)		;IS THERE AN ENTRY THERE?
	BUG.(CHK,SCLTFJ,SCLINK,SOFT,<Freeing SJB with SLB entries existing>,<<P1,SJBPTR>>,<

Cause:	FRESJB was called to free up a SJB.  However, there are still active
	links in use for this SJB.  This should never happen, and there is an
	internal inconsistency in the DECnet data structures.  Submit a SPR if
	this happens more than once.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.

Data:	SJBPTR - Pointer to the SJB

>)
	SOSE T2			;SEE IF THERE ARE ANY MORE ENTRIES
	AOJA T1,FRESJ1		;THERE ARE, TEST THEM

FRESJ2:	D36OFF			   ;#PROTECT THE SMP
	RMVQUE P1,SCTSJQ,SJ.NXT,T1 ;#REMOVE SJB FROM LIST OF SJBS
	D36ON			   ;#END OF CRITICAL SECTION
	MOVE T1,P1		;SET UP TO FREE SOME WORDS
	CALLRET DNFWDS		;FREE THE WORDS THE SJB IS USING
	SUBTTL Subroutines -- MAKSLB - Create a SLB, filling in defaults

;MAKSLB - Create a SLB, filling in defaults
;
; Call:
;	T1/ Channel #
;	SA/ Pointer to argument block from SCTNSF caller
;
; Return:
;	RET			;ON FAILURE DUE TO ALLOCATION
;	RETSKP			;SUCCESS WITH SL SET UP WITH THE SLB POINTER
;				; AND T1 CONTAINING THE CHANNEL NUMBER
;
; Uses: T1-T6

	CHTFAC==2		;FACTOR BY WHICH NEW CHT IS LARGER THAN OLD

MAKSLB:	SAVEAC <P1,P2>		;CHT POINTER, SJB POINTER
MAKSL1:	LOAD P2,SASJB,(SA)	;GET POINTER TO SJB
	LOAD P1,SJCHT,(P2)	;GET POINTER TO THE SLB TABLE
	LOAD T3,SJCHC,(P2)	; AND THE COUNT OF ENTRIES IN THE TABLE

MAKSL2:	SKIPN (P1)		;IS THERE A FREE ENTRY?
	JRST MAKSL3		;YES, GO USE IT
	SOSE T3			;ARE ANY MORE ENTRIES LEFT?
	AOJA P1,MAKSL2		;YES, LOOK AT THE NEXT ONE

;Here when we have to expand the SLB table in the SJB.  We will try to make
;it twice as large as it is currently.

	LOAD T1,SJCHC,(P2)	;GET THE CURRENT SIZE OF SLB TABLE
	IMULI T1,CHTFAC		;CALC NEW, LARGER SIZE
				;WE'LL STORE SIZE LATER, AFTER COPY
	CALL DNGWDZ		;GET THAT MANY WORDS
	  RET                   ;ALLOCATION FAILURE

	MOVE T2,T1		;SET UP DEST POINTER FOR DNCPYW
	OPSTRM <EXCH T1,>,SJCHT,(P2) ;STORE NEW CHT PTR, LOAD OLD CHT PTR
	LOAD T3,SJCHC,(P2)	;GET THE OLD COUNT
	PUSH P,T1		;SAVE POINTER TO OLD CHT
	CALL DNCPYW		;COPY THE OLD WORDS
	POP P,T4		;RESTORE POINTER TO OLD CHT FOR DEALLOCATION

	LOAD T2,SJCHC,(P2)	;GET OLD CHT LENGTH
	IMULI T2,CHTFAC		;CALC NEW LENGTH AGAIN
	STOR T2,SJCHC,(P2)	;STORE NEW CHT LENGTH

;Deallocate the old block if it was outside of the SJB.

	XMOVEI T2,SJ.SLT(P2)	;POINT TO THE SLB TABLE IN THE SJB
	CAME T2,T4		;IS THE SLB TABLE IN THE SLB?
	JRST [MOVE T1,T4	;NO, JUST FREE THE BLOCK
	      CALL DNFWDS	;FREE THE WORDS
	      JRST MAKSL1]	;MAKE THE NEW ENTRY UP
	MOVE T1,T2		;ZERO THE
	ADDI T2,SLT.LN-1	; ENTRIES IN THE SJB
	SETZ T3,		; JUST TO BE SAFE
	CALL DNSWDS		;SMEAR THEM
	JRST MAKSL1		; AND GO INTO THE OTHER CODE
;Here to make the new SLB.

MAKSL3:	MOVX T1,SL.LEN		;LENGTH OF A SLB
	CALL DNGWDZ		;GET THAT MANY WORDS
	  RET			;ALLOCATION FAILURE
	MOVE SL,T1		;SET UP SL WITH THE POINTER
	STOR SL,SLSLB,(SL)	;STORE POINTER TO OURSELVES FOR CHECKING
	STOR P2,SLSJB,(SL)	; AND POINTER TO SJB

;Initialize some of the variables.

	SETONE SSOTH,+SL.OSL(SL) ;SET "OTHER SUBLINK" BIT IN OTHER SUBLINK

	LOAD T1,IBFCM,+IBBLK	;Get default flow control from IB block
	STOR T1,SLXFL,(SL)	;SET THE TRANSMIT FLOW CONTROL TO SEGMENT
	STOR T1,SLRFL,(SL)	; AND THE RECEIVE FLOW TYPE

	SETZRO SLGOL,(SL)	;START THE GOAL AT ZERO
	MOVX T1,1
	STOR T1,SSXDO,+SL.OSL(SL) ;SET XMT DRQS ON OTHER SUBLINK TO ONE
	STOR T1,SSRDO,+SL.OSL(SL) ; ALONG WITH THE RCV DRQS

	LOAD T1,SJGOL,(P2)	;GET THE JOB'S GOAL
	STOR T1,SLGOL,(SL)	;STORE IT

	LOAD T1,SJINQ,(P2)	;GET THE JOB'S INPUT QUOTA
	STOR T1,SLINQ,(SL)	;STORE IT IN THE SLB
	LOAD T1,SJOTQ,(P2)	;SAME FOR THE OUTPUT QUOTA
	STOR T1,SLOTQ,(SL)	;STORE THAT IN THE SLB ALSO

	MOVE T1,RTRBSZ		;Get executor bytes size
	SUBI T1,%SCHDR		; and subtract size of headers below SC
	STOR T1,SLSIZ,(SL)	;SET THAT UP ALSO

	SETZRO SLRSN,(SL)	;ZERO THE REASON FIELD

	LOAD T1,SAWKA,(SA)	;GET ADDRESS OF WAKEUP ROUTINE
	STOR T1,SLWKA,(SL)	;STORE IN THE SLB

	SETZRO SLPSM,(SL)	;START WITH PSI MASK DISABLED

	SETONE <SLBSY,SLEOM>,(SL) ;INITIALLY THE SLB IS BUSY, SEE SCTNIA
				  ; SLEOM INITIALLY TRUE FOR FIRST BOM

   IFN FTOPS20,<
	MOVE T1,TODCLK		;SET UP THE 
	STOR T1,SLUID,(SL)	; SLB'S SERIAL NUMBER
   >

;After all errors have been avoided, store pointer in SLB table

	ENDQUE SL,SCTASQ,SL.ASQ,T1 ;PUT NEW SLB ON ALL SLB QUEUE

	MOVEM SL,(P1)		;SAVE POINTER TO US IN SLB TABLE
	OPSTR <SUB P1,>,SJCHT,(P2) ;CALC NEW CHANNEL NUMBER
	AOS T1,P1		;MAKE CHANNEL NUMBERS START AT ONE
	STOR P1,SLCHN,(SL)	;STORE IT IN THE SLB
	RETSKP			; AND RETURN CHANNEL # TO USER IN T1
	SUBTTL Subroutines -- MAKSJB - Create a SJB, filling in defaults

;MAKSJB - Create a SJB, filling in defaults
;
; Call, PC in section 1 if KL paging:
;	T1/ DECnet "PDB" word (See PD Structure in D36PAR)
;
; Return:
;	RET			;ON ALLOCATION FAILURE
;	RETSKP			;OK, WITH T1 SET UP WITH SJB POINTER
;
; Uses: T1-T6

	XRENT MAKSJB

	SAVEAC <P1,P2,T5,T6>
	MOVE P1,T1		;SAVE THE ARGUMENT
	MOVX T1,SJ.LEN		;GET THE LENGTH OF A SJB
	CALL DNGWDZ		;GET THAT MANY WORDS
	 RET			;ALLOCATION FAILURE
	MOVE P2,T1		;SET UP THE SJB POINTER IN P2

	D36OFF			;INTERLOCK FOR SMP
	ENDQUE P2,SCTSJQ,SJ.NXT,T1 ;PUT IT ON THE QUEUE OF SJB'S
	D36ON			;END CRITICAL SECTION
	MOVX T1,SLT.LN		;LENGTH OF VIRGINAL SLB TABLE
	STOR T1,SJCHC,(P2)	;STORE THAT IN THE COUNT
	XMOVEI T1,SJ.SLT(P2)	;MAKE THE SLB TABLE POINTER POINT
	STOR T1,SJCHT,(P2)	; TO THE VIRGIN TABLE IN THE SJB

	LOAD T1,PDGOL,+P1	;GET THE JOB'S GOAL
	STOR T1,SJGOL,(P2)	;STORE IN THE SJB
	LOAD T1,PDDQT,+P1	;GET THE DEFAULT QUOTA
	OPSTR <IMUL T1,>,PDIPR,+P1 ;MULT BY INPUT % = INPUT QUOTA * 100
	IDIVI T1,^D100		; AND DIVIDE BY 100 TO FIND INPUT QUOTA
	SKIPG T1		;ASSURE INPUT
	MOVEI T1,1		; GETS AT LEAST ONE
	STOR T1,SJINQ,(P2)	;STORE THE INPUT QUOTA IN SJB
	LOAD T2,PDDQT,+P1	;GET THE TOTAL QUOTA BACK
	SUB T2,T1		;SUBTRACT INPUT QUOTA
	SKIPG T2		;ASSURE OUTPUT
	MOVEI T2,1		; GETS AT LEAST ONE
	STOR T2,SJOTQ,(P2)	;TO GET THE OUTPUT QUOTA

	MOVE T1,P2		;RETURN SJB POINTER IN T1
	RETSKP			;RETURN SUCCESS
	SUBTTL Subroutines -- SCSSTS/CHKSTS - Set Status & Inform PSISER

;SCSSTS/CHKSTS - Set Status & Inform PSISER
;
; Call:
;	T1/ New State		;FOR SCSSTS ONLY, NO ARGS FOR CHKSTS
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6
;
;NSFDS and NSFIS use the NSNDR and NSIDR bits to determine whether they
;should send a msg, so CHKSTS is the repository of quota information.

SCSSTS:	STOR T1,SLSTA,(SL)	;STORE NEW STATE (NOT PART OF SLSST)
CHKSTS:				;ALTERNATE ENTRY TO CHECK BITS ONLY

;Now build a new status word from the data contained in the SLB.  The
;new status gets built in T6.

	LOAD T6,SLSTA,(SL)	;GET STATE FOR STATUS HALF-WORD
	MOVE T4,T6		;SAVE THE STATE FOR LATER

;Check for input available
	TMNE QHBEG,+SL.NSL+SS.INQ(SL) ;ANY NORMAL INPUT DATA AVAILABLE?
	TXO T6,NSNDA		;YES, SET NORMAL DATA AVAILABLE
	TMNE QHBEG,+SL.OSL+SS.INQ(SL) ;ANY OTHER INPUT DATA AVAILABLE?
	TXO T6,NSIDA		;YES, SET INTERRUPT DATA AVAILABLE

;Check for output quota
	IFNSTATE T4,<RN>,CHKST4 ;IF WE'RE NOT RUNNING, DON'T ALLOW SENDS
	D36OFF			;PROTECT FROM SCTRIB CALL FROM LLINKS
	LOAD T2,SLOTU,(SL)	;GET # OUTPUT BUFFERS CURRENTLY IN USE
	TMNE SLOTM,(SL)         ;Is there an output message being filled?
	SOS T2                  ;Yes, allow output to continue
	SKIPE DCNCON		;WE ARE CONGESTED,
	JUMPG T2,CHKST3		; ALLOW ONLY 1 OUTPUT/LINK
	OPSTR <CAML T2,>,SLOTQ,(SL) ;ARE WE WITHIN THE QUOTA?
	JRST CHKST3		;NO, DON'T ALLOW SENDS
	MOVEI T1,1		;ASSUME WE'RE ALREADY USING AN OUTPUT BUFFER
	JUMPG T2,CHKST1		;JUMP IF THAT'S TRUE
	MOVEI T1,2		;WE HAVE NO OUTPUT NOW, ASSUME NO INPUT
	TMNE SLINU,(SL)		;ANY INPUT BUFFERS ALLOCATED?
	JRST CHKST2		;YES, AN OUTPUT BUFFER ALREADY RESERVED
CHKST1:	MOVE T2,DCNTSB		;GET TOTAL SYSTEM BUFFERS
	SUB T2,DCNRSB		;SUBTRACT THOSE ALREADY USED
	CAMGE T2,T1		;ENOUGH ROOM FOR A NEW REQUEST?
	JRST CHKST3		;NO, DON'T ALLOW OUTPUT

;Check for output data requests
CHKST2:	LOAD T1,SLXFL,(SL)	;GET THE TRANSMIT FLOW CONTROL OPTION
	TMNN SSXDO,+SL.NSL(SL)	;NORMAL DATA REQUESTS AVAILABLE?
	CAIN T1,FCM.NO		;NO, PUNT UNLESS NO FLOW CONTROL
	TXO T6,NSNDR		;YES, SET NORMAL DATA REQUESTED
	TMNE SSXDO,+SL.OSL(SL)	;OTHER DATA REQUESTS AVAILABLE?
	TXO T6,NSIDR		;YES, SET INTERRUPT DATA REQUESTED
CHKST3:	D36ON			;END CRITICAL SECTION
CHKST4:
;Fall through with new status in T6
;From previous page with new status in T6

;Note that NSFPI calls the wake routine also, and any argument changes
;must be reflected there too.

	LOAD T5,SLSST,(SL)	;GET OLD STATUS
	STOR T6,SLSST,(SL)	;STORE NEW STATUS
	SKIPN T2,T5		;COPY OLD STATUS
	RET			;FIRST CHANGE IS NEVER "INTERESTING"
	TXZ T5,^-NSSTS		;LEAVE ONLY THE STATUS BITS
	IOR T5,T6		;BITS (NEW ! OLD) + NEW STATE
	XOR T5,T2		;REVEAL INTERESTING CHANGES AS ONES IN T5
	JUMPE T5,RTN		;LEAVE NOW IF NO INTERESTING CHANGES

;SLPSI is no longer in the following test incase we are e blocked at PSI level.
;SCTWKQ won't signal twice.

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

;Here if the status has changed in an interesting way
;Pick up the args for the caller's wake routine

	LOAD T1,SLPSM,(SL)	;THE PSI MASK FOR THIS LINK
	HRL T1,T2		;T1 HOLDS OLD STATUS,,MASK
	LOAD T2,SLCHN,(SL)	;THE LINK NUMBER
	HRL T2,T6		;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?
	CALLRET 0(T6)		;(T1,T2,T3,T4,T5)YES, CALL WAKE ROUTINE
	RET			;NO, JUST RETURN
	SUBTTL Subroutines -- SCTWKQ - Queue a Link for later call to SCTPSQ

;SCTWKQ - Queue a Link for later call to SCTPSQ
;
; Call:		T1/ Link Identifier (SLB ptr) from @SLWKA call above
;
; Return:
;		RET		;Only return
;T1 = 0 on return if SLB already queued for PSI, non-zero altered
;This routine is only called by the routine whose address is in SAWKA.

	XRENT SCTWKQ		;Entry point

	LOAD T3,SLSJB,(T1)	;LOAD UP SJB POINTER FOR ENDQUE BELOW
	MOVX T2,SLPSI		;GET THE "QUEUED ON PSQ" FLAG
	D36OFF			;#THE DEQUE ROUTINE IS NOT INTERLOCKED
	TDNE T2,SL.PSI(T1)	;#IS THIS SLB ALREADY QUEUED?
	JRST [SETZ T1,
	      JRST SCTWKX]
	IORM T2,SL.PSI(T1)	;#WELL, IT IS QUEUED NOW
	ENDQUE T1,SJ.PSQ(T3),SL.NXP,T2 ;#QUEUE SLB FOR SCTPSQ
SCTWKX:	D36ON			;#INTERRUPTS BACK ON AGAIN
	RET			;ALL DONE
	SUBTTL Subroutines -- SLBMAT - Pattern match connect to passive SLBs

;SLBMAT - Pattern match a connect over all passive SLBs
;
; Call:
;	T1/ Pointer to internal connect block (see BEGSTR CB in SCPAR)
;
; Return:
;	RET			;COULND'T FIND MATCHING PASSIVE SLB
;	RETSKP			;FOUND ONE, POINTER TO IT IN SL
;
; Uses: T1-T6

SLBMAT:	SAVEAC <P1,P2>
	MOVE P1,T1		;SAVE THE POINTER TO THE CONNECT BLOCK
	SETZ P2,		;No matches so far (used to distinguish
				; between 'no such object' and 'object too
				; busy'
	LOAD SL,QHBEG,+SCTASQ	;GET PTR TO FIRST SLB ON ALL SLBs QUEUE
SLBMA1:	JUMPE SL,[MOVE T1,P2	;If no more SLB's, load return code
		  RET]		; and fail return
	JN <SLFSL,SLLBC>,(SL),SLBMA2 ;IF LINK IS BEING CLOSED, IGNORE IT

;We try to match the connect no matter wait the link state is.
; If we get a success return we check if the state is CW. If so, all is well.
; If not, we know that we had an 'object too busy' reject instead of a
; 'no such object'.

	CALL SLBMAS		;COMPARE SLB WITH RECEIVED CONNECT BLK
	  JRST SLBMA2		;NO MATCH
	LOAD T1,SLSTA,(SL)	;GET THE SLB'S STATE
	IFSTATE T1,CW,RSKP	;IF IT'S IN CONNECT WAIT, THEN MATCH
	TMNE SLPAS,(SL)		;Was this a passive task?
	SETO P2,		; -yes, flag 'object too busy'

SLBMA2:	LOAD SL,SLASQ,(SL)	;STEP TO NEXT SLB ON ALL SLBs Q
	JRST SLBMA1		;AND TRY IT

;Check a SLB

SLBMAS:	SAVEAC <T3,T4>		;SAVE T3 AND T4 FOR A WHILE
	MOVE T2,P1		;POINT TO THE INCOMING CONNECT BLOCK
	LOAD T1,SLCBP,(SL)	;POINT TO THE SLB'S CONNECT BLOCK
	JUMPN T1,CDBMAT		;If there is a connect block, then go
				; and check it out.  CDBMAT returns +1 or +2.
;No connect block - link is not in CW state.  Check if the object numbers
; match.
	XMOVEI T2,CB.DST(T2)	;Point to the dest pb source
	LOAD T2,PBOBJ,(T2)	;Get dest obj number
	OPSTR <CAME T2,>,SLSOB,(SL) ;The same object numbers?
	  RET			; -no, return failure
	RETSKP			;-yes, return success
	SUBTTL Subroutines -- CDBMAT - Match Two Connect Blocks

;CDBMAT - Match two internal connect blocks
;
; Call:
;	T1/ Pointer to passive SLB's connect block
;	T2/ Pointer to incoming connect block
;
; Return:
;	RET			;THEY DON'T MATCH
;	RETSKP			;FOUND A MATCH
;
; Uses: T1-T6

CDBMAT:	SAVEAC <P1,P2>		;SAVE SOME PEAS
	DMOVE P1,T1		;SAVE THE POINTERS TO THE CONNECT BLOCKS

;Try to match the destination end names with each other.

	XMOVEI T5,CB.DST(P1)	;POINT TO THE DEST PB PATTERN (PASSIVE)
	XMOVEI T6,CB.DST(P2)	;POINT TO THE DEST PB SOURCE
	CALL CDBMTP		;COMPARE THE DEST END NAME DESCRIPTOR
	RET			;NOW GIVE ERROR RETURN

;Both of the end descriptors have matched.  Now let's try to match the
;remainder of the fields in the connect data.  First:

;RQSTRID

	LOAD T1,CBUCT,(P1)	;GET THE PASSIVE RQSTRID COUNT
	XMOVEI T2,CB.UID(P1)	;POINT TO THE WORD ALIGNED RQSTRID STRING
	LOAD T3,CBUCT,(P2)	;GET THE SOURCE RQSTRID COUNT
	XMOVEI T4,CB.UID(P2)	;POINT TO THE WORD ALIGNED STRING
	CALL STRMAT		;TRY TO MATCH THE STRING
	RET			;A MISMATCH

;PASSWRD

	LOAD T1,CBPCT,(P1)	;GET THE PASSIVE PASSWRD COUNT
	XMOVEI T2,CB.PSW(P1)	;POINT TO THE WORD ALIGNED PASSWRD STRING
	LOAD T3,CBPCT,(P2)	;GET THE SOURCE STRING COUNT
	XMOVEI T4,CB.PSW(P2)	;POINT TO THE WORD ALIGNED STRING
	CALL STRMAT		;TRY TO MATCH THEM
	RET			;MISMATCH

;ACCOUNT

	LOAD T1,CBACT,(P1)	;GET THE PASSIVE ACCOUNT COUNT
	XMOVEI T2,CB.ACC(P1)	;POINT TO THE WORD ALIGNED ACCOUNT STRING
	LOAD T3,CBACT,(P2)	;GET THE SOURCE STRING COUNT
	XMOVEI T4,CB.ACC(P2)	;POINT TO THE WORD ALIGNED STRING
	CALL STRMAT		;TRY TO MATCH THEM
	RET			;MISMATCH

;USRDATA

	LOAD T1,CBCCT,(P1)	;GET THE PASSIVE USER CONNECT DATA COUNT
	XMOVEI T2,CB.UDA(P1)	;POINT TO THE WORD ALIGNED USER DATA STRING
	LOAD T3,CBCCT,(P2)	;GET THE SOURCE STRING COUNT
	XMOVEI T4,CB.UDA(P2)	;POINT TO THE WORD ALIGNED STRING
	CALLRET STRMAT		;TRY TO MATCH THE STRINGS AND RETURN
;CDBMTP - Subroutine to match process descriptor blocks
;
;Call:
;	T5/ Pointer to the passive (or pattern) connect block
;	T6/ Pointer to the source (or incoming) connect block

CDBMTP:	LOAD T1,PBOBJ,(T5)	;GET THE PASSIVE GUY'S OBJECT TYPE
	OPSTR <CAME T1,>,PBOBJ,(T6) ;MUST BE THE SAME AS THE SOURCE'S
	RET			;MISMATCH

;Get the format type, do a range check and dispatch.

	LOAD T1,PBFOR,(T5)	;GET THE PASSIVE FORMAT TYPE
	OPSTR <CAME T1,>,PBFOR,(T6) ;DOES IT MATCH?
	RET			;NO, WE HAVE A MISMATCH
	CAIL T1,FRM.0		;RANGE CHECK THE
	CAILE T1,FRM.MX		; FORMAT TYPE
	RETSKP			;  - should never happen, just RSKP
	CALLRET @.+1(T1)	;DISPATCH ON FORMAT TYPE
	   IFIW <RSKP&777777>	;FORMAT TYPE 0
	   IFIW <CDBMP1&777777>	;FORMAT TYPE 1
	   IFIW <CDBMP2&777777>	;FORMAT TYPE 2

;Here to match format 1s. We already matched the object type, so just match
;the NAME field.

CDBMP1:	LOAD T1,PBNCT,(T5)	;GET THE PASSIVE COUNT
	XMOVEI T2,PB.NAM(T5)	;POINT TO THE WORD ALIGNED STRING
	LOAD T3,PBNCT,(T6)	;GET THE SOURCE STRING COUNT
	XMOVEI T4,PB.NAM(T6)	;POINT TO THE WORD ALIGNED STRING
	CALLRET STRMAT		;TRY TO MATCH THE STRING

;Here to match format 2s. We have already matched the object type. Compare
;the GRPCODE and USRCODE fields, allowing zeroes in either field to act as
;wildcards.

CDBMP2:

IFN <PB.USR-PB.GRP>,<
	IF2,<PRINTX ?GRPCODE and USRCODE must be in the same word>>

	MOVE T1,PB.USR(T5)	;GET THE PASSIVE USRCODE AND GRPCODE
	MOVE T2,PB.USR(T6)	; AND THE SOURCE ONE
	TLNE T1,-1		;A ZERO IN THE LEFT OF PASSIVE
	TLNN T2,-1		; OR LEFT OF SOURCE?
	HLL T1,T2		;YES, MAKE BOTH THE SAME
	TRNE T1,-1		;A ZERO IN THE RIGHT OF PASSIVE
	TRNN T2,-1		; OR RIGHT OF SOURCE?
	HRR T1,T2		;YES, MAKE BOTH THE SAME
	CAME T1,T2		;ARE THEY THE SAME?
	RET			;MISMATCH
;Now try to match the DESCRPT fields.

	LOAD T1,PBNCT,(T5)	;GET THE PASSIVE COUNT
	XMOVEI T2,PB.NAM(T5)	;POINT TO THE WORD ALIGNED STRING
	LOAD T3,PBNCT,(T6)	;GET THE SOURCE STRING COUNT
	XMOVEI T4,PB.NAM(T6)	;POINT TO THE WORD ALIGNED STRING
	CALLRET STRMAT		;TRY TO MATCH THE STRING
	SUBTTL Subroutines -- STRMAT - Pattern Matcher

;STRMAT - Routine to perform a pattern match
;
; Call:
;	T1/ Length of pattern
;	T2/ Pointer to start of word aligned pattern string (not a BP)
;	T3/ Length of source string
;	T4/ Pointer to start of word aligned source string (not a BP)
;
; Return:
;	RET			;FAILURE TO MATCH OR TOO MANY "*"S IN PATTERN
;	RETSKP			;PATTERN MATCHED STRING
;
; Uses: T1-T6
;
;The special "pattern-matching" characters are:
;
;	?	Match the next source character.
;	*	Match zero or more next source characters.
;	^V	(Control-V) Treat the next pattern character as
;		a normal character even if it is a "?" or a "*".
;
;Note: This routine uses four words of stack for every "*" contained
;within the pattern.  Also note that we are assuming one word byte
;pointers.  This is the same algorithm that TSKSER (in TOPS-10) uses.
;Also note that a null pattern will match anything.

	P0==SL			;WE NEED ONE MORE AC
	STRMAX==^D4		;MAXIMUM LEVEL OF RECURSION

STRMAT:	JUMPE T1,RSKP		;NULL PATTERN MATCHES EVERYTHING
	SAVEAC <P0,P1,P2,P3>
	MOVE P1,T2		;KEEP WORD ALIGNED POINTER TO PATTERN IN P1
	MOVE P2,T4		; TO SOURCE STRING IN P2
	SETZ P3,		;USE P3 AS A RECURSION COUNTER
	MOVX T2,<POINT 8,(P1)>	;SET UP INDEXED BYTE POINTER TO PATTERN
	MOVX T4,<POINT 8,(P2)>	; TO SOURCE STRING ALSO
	MOVE P0,P		;SAVE THE STACK POINTER
	ADJSP P0,4		;THIS IS ZERO LEVEL
STRMT1:	ADJSP P,4		;SAVE THE
	DMOVEM T1,-3(P)		; SOURCE AND
	DMOVEM T3,-1(P)		; PATTERN DESCRIPTORS
	AOJ P3,			;INCREMENT THE RECURSION COUNTER
	CAILE P3,STRMAX		;ARE WE TOO FAR DOWN?
	JRST STRMTF		;WE FAILED

STRMT2:	SOJL T1,[SOJL T3,STRMTS	;IF BOTH STRINGS ARE NULL, RETURN SUCCESS
		 JRST STRMT3]	;IF SOURCE STRING TOO LONG, BACK UP
	ILDB T5,T2		;GET THE NEXT PATTERN CHARACTER
	CAIN T5,"*"		;SEE IF IT'S A STAR
	JRST STRMT1		;IT IS, GO RECURSE
	CAIN T5,"?"		;SEE IF IT'S A WILDCARD CHARACTER
	JRST [SOJL T3,STRMTF	;IF SO, COUNT ONE SOURCE CHAR, FAIL IF NONE
	      IBP T4		;SKIP OVER NEXT SOURCE CHAR
	      JRST STRMT2]	; AND CONTINUE MATCHING WITH NEXT CHARACTER
	CAIN T5,"V"-100		;IS IT A ^V?
	JRST [SOJL T1,STRMTF	;IF SO, COUNT OFF ON PATTERN CHARACTER
	      ILDB T5,T2	;GET THE NEXT "QUOTED" CHARACTER
	      JRST .+1]		; AND CONTINUE WITH THE MATCH
	SOJL T3,STRMTF		;COUNT OFF SOURCE CHARACTER, FAIL IF NONE
	ILDB T6,T4		;GET THE NEXT SOURCE CHARACTER
	CAIL T6,"A"+40		;IS IT
	CAILE T6,"Z"+40		; LOWER CASE?
	CAIA			;NO
	SUBI T6,40		;YES, MAKE UPPER CASE
	CAIL T5,"A"+40		;IS IT
	CAILE T5,"Z"+40		; LOWER CASE?
	CAIA			;NO
	SUBI T5,40		;YES, MAKE UPPER CASE
	XORI T5,(T6)		;COMPARE THE CHARACTERS
	TRNN T5,177		; BUT ONLY LOOK AT LOW 7 BITS
	JRST STRMT2		;IF THEY MATCH, COMPARE NEXT CHARACTERS
;Here when characters don't match.

STRMT3:	CAMN P,P0		;ARE WE AT LEVEL ZERO?
	JRST STRMTF		;NO, MATCH FAILED
	DMOVE T1,-3(P)		; PATTERN AND THE
	DMOVE T3,-1(P)		; SOURCE
	SOJL T3,STRMTF		;WE ARE PROCESSING A "*", EAT ONE MORE
	IBP T4			; SOURCE CHARACTER
	DMOVEM T3,-1(P)		;REMEMBER THE UPDATED SOURCE
	JRST STRMT2		;NO TRY TO COMPARE AGAIN

;STRMTF on failure, STRMTS on success

STRMTF:	SETO T6,		;WHEN T6 IS NON-ZERO, WE FAILED
	TRNA			;MERGE WITH OTHER SUCCESS
STRMTS:	 SETZ T6,		;SIGNAL SUCCESS
	ASH P3,2		;MULTIPLY RECURSION COUNTER BY FOUR
	MOVNS P3		;NEGATE IT
	ADJSP P,(P3)		;GO BACK TO TOP LEVEL
	ADJSP P0,-4		;BACK TO TOP LEVEL FROM ZERO LEVEL
  IFN FTDEBUG <
	CAME P,P0		;I GET PARANOID ABOUT STACKS
	BUG.(CHK,SCLSMS,SCLINK,SOFT,<STRMAT messed up the stack pointer>,,<

Cause:	At the end of routine STRMAP, the stack pointer should have reset to
	the value in P0.   It did not.  This BUGCHK will not be present in
	a production monitor.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.
>,RTN)
  >
	JUMPN T6,RTN		;GIVE BAD RETURN ON FAILURE
	RETSKP			; AND GIVE SUCCESS RETURN
	SUBTTL Subroutines -- STPTMR - Stop the Connect Initiate Timer

;STPTMR - Stop the Connect Initiate Timer, adjusting timer counts
;
; Call:
;	SL/ Pointer to the SLB
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1,T2
;
;This is called even when the CI timer isn't running.

STPTMR: JE SLCTM,(SL),RTN	;IF TIMER ISN'T GOING, JUST RETURN
	SETZRO SLCTM,(SL)	;ZERO THE CONNECT TIMER
	SOSGE SCTCTA		;DECREMENT THE GLOBAL TIMER COUNT
	SETZM SCTCTA		; - went negative, should never happen, fix
	LOAD T2,SLSJB,(SL)	;POINT TO THE SJB
	OPSTRM <SOS T1,>,SJCTA,(T2) ;DECREMENT THE JOB'S TIMER COUNT
	JUMPGE T1,RTN		;RETURN IF COUNT IS OK
	SETZRO SJCTA,(T2)	; -should never happen, fix
	RET
	SUBTTL Subroutines -- STRTIM - Start the Connect Initiate Timer

;STRTIM - Start the Connect Initiate Timer, adjusting all timer counts
;
; Call:
;	SL/ Pointer to SLB
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1,T2
;
;It is illegal to start a CI timer that has already been started.

STRTIM:	CALL DNGTIM		;[7.1063]GET THE CURRENT TIME
	STOR T1,SLCTM,(SL)	;STORE THE CURRENT TIME
	AOS SCTCTA		;INCREMENT THE GLOBAL TIMER COUNT
	LOAD T1,SLSJB,(SL)	;GET POINTER TO SJB
	INCR SJCTA,(T1)		;INCREMENT THE JOB'S TIMER COUNT
	RET			;RETURN
	SUBTTL Subroutines -- TMRREJ - Send a Reject with Reason RSNNRO

;TMRREJ - Reject a Connect with reason RSNNRO
;
; Call:
;	SL/ Pointer to Session Control Link Block
;
; Return:
;	RET			;ON ALLOCATION FAILURE - TRY AGAIN LATER
;	RETSKP			;ON SUCCESS - STOP THE TIMER
;
; Uses:	T1-T6

TMRREJ:	SAVEAC <MB,MS>		;SAVE SOME ACS
	TRACE SC,<Rejecting connect due to lack of response from object>
	MOVX T1,2		;WE NEED ONLY TWO BYTES
	CALL DNGMSG		;GET A MESSAGE BLOCK
	  RET			;COULDN'T GET IT, TRY AGAIN LATER
	MOVE MB,T1		;POINT TO THE MESSAGE BLOCK

	XMOVEI T1,UD.MSD(MB)	;POINT TO THE USER DATA AREA
	CALL DNPINI		;INITIALIZE TO PUT BYTES IN MESSAGE

	MOVX T1,RSNNRO		;REASON IS "NO RESPONSE FROM OBJECT"
	STOR T1,SLRSN,(SL)	;TELL USER THAT REASON AND
	CALL DNP2BY		; PUT IT IN MESSAGE TOO

	LOAD T1,SLPID,(SL)	;GET THE NSPpid
	MOVX T3,NV.REJ		;FUNCTION IS REJECT
	MOVE T4,MB		;POINT TO THE MESSAGE BLOCK
	CALL NSP		;CALL NSP TO REJECT THE CONNECT
	NEWSTATE LK		;GO INTO NO LINK STATE
	SETZRO SLPID,(SL)	;NSP HAS FORGOTTEN THIS LINK DUE TO REJECT
	RETSKP			;RETURN SUCCESS
	SUBTTL Subroutines -- BLDCTX - Build connect message

;BLDCTX - Build the connect message from an internal connect block
;
; Call:
;	T1/ Pointer to the internal connect block (See BEGSTR CB in SCPRM)
;
; Return:
;	RET			;RANGE CHECKING FOUND ILLEGAL CONNECT INFO
;	RETSKP			;EVERYTHING'S ALRIGHT
;
; Uses: T1-T6
;
;Note: We are assuming that DNPINI has already been called and that we can
;just do DNPxBYs to dump bytes into the message.

BLDCTX:	SAVEAC <P1,P2>		;SAVE TWO PEAS
	MOVE P1,T1		;SAVE THE POINTER TO THE CONNECT BLOCK
	XMOVEI P2,CB.DST(P1)	;BUILD THE
	CALL BLDCTP		; DESTINATION END USER NAME
	 RET			;BAD FORMAT
	XMOVEI P2,CB.SRC(P1)	;BUILD THE
	CALL BLDCTP		; SOURCE END USER NAME
	 RET			;BAD FORMAT

;Here to build the MENUVER word.

	SETZB T3,T4		;BUILD MENUVER IN T4, FLAG FOR MNRPA IN T3
	OPSTR <IOR T3,>,CBUCT,(P1) ;ANY RQSTRID?
	OPSTR <IOR T3,>,CBPCT,(P1) ;ANY PASSWRD?
	OPSTR <IOR T3,>,CBACT,(P1) ;ANY ACCOUNT?
	SKIPE T3		;SHOULD WE SAY THOSE FIELDS ARE INCLUDED?
	TXO T4,MNRPA		;YES, SET BIT
	TMNE CBCCT,(P1)		;ANY USRDATA?
	TXO T4,MNUSR		;YES, SET THE BIT TO INDICATE THIS

	MOVX T1,SC.VER		;GET THE VERSION OF SESSION CONTROL
	STOR T1,MNVER,+T4	;STORE IT IN THE VERSION FIELD
	MOVE T1,T4		;PUT IT IN T1 FOR DNPEBY
	CALL DNPEBY		;PLACE THE MENUVER EXTENSIBLE BYTE IN MESSAGE

	TXNN T4,MNRPA		;WAS RQSTRID, PASSWRD OR ACCOUNT SET?
	JRST BLDCT1		;NO, DON'T INCLUDE THEM
;Now put in the User ID, Password, etc.

	LOAD T1,CBUCT,(P1)	;GET THE RQSTRID BYTE COUNT
	XMOVEI T2,CB.UID(P1)	;POINT TO THE STRING
	CALL PUTSTR		;PUT THE STRING IN THE MESSAGE

	LOAD T1,CBPCT,(P1)	;GET THE PASSWRD BYTE COUNT
	XMOVEI T2,CB.PSW(P1)	;POINT TO THE PASSWORD
	CALL PUTSTR		;PUT IT IN THE MESSAGE

	LOAD T1,CBACT,(P1)	;GET THE ACCOUNT BYTE COUNT
	XMOVEI T2,CB.ACC(P1)	;POINT TO THE ACCOUNT STRING
	CALL PUTSTR		;PUT IT IN THE MESSAGE

BLDCT1:	TXNN T4,MNUSR		;IS THERE ANY USRDATA?
	RETSKP			;NO, JUST RETURN

	LOAD T1,CBCCT,(P1)	;GET THE USRDATA BYTE COUNT
	XMOVEI T2,CB.UDA(P1)	;POINT TO THE ACCOUNT STRING
	CALL PUTSTR		;PUT IT IN THE MESSAGE
	RETSKP			; AND RETURN
;BLDCTP - Subroutine for BLDCTX to build end user name from process block
;
; Call:
;	P2/ Pointing to the process descriptor block

BLDCTP:	LOAD T1,PBFOR,(P2)	;GET THE FORMAT TYPE
	CAIL T1,FRM.0		;DO SOME
	CAILE T1,FRM.MX		; RANGE CHECKING
	RET			;BAD CONNECT BLOCK
	MOVE T4,T1		;SEMI-PRESERVE THE FORMAT
	CALL DNP1BY		;PLACE THE BYTE IN THE MESSAGE
	CALL @[IFIW <BLDCP1&777777> ;DISPATCH BY THE FORMAT TYPE
	       IFIW <BLDCP2&777777> ;
	       IFIW <BLDCP4&777777>](T4) ;
	  RET			;ERROR RETURN
	RETSKP			;SUCCESS RETURN

;Here to process a end user name format 0.  This just consists
;of a non-zero one byte object type.

BLDCP1:	OPSTR <SKIPN T1,>,PBOBJ,(P2) ;MUST HAVE AN OBJECT TYPE
	RET			;DIDN'T - ILLEGAL CONNECT BLOCK
	CALL DNP1BY		;PLACE THE OBJECT TYPE IN
	RETSKP			; AND WE'RE DONE

;Here to process a end user name format 1.  This format is a
;zero object type followed by a I-16 name field.

BLDCP2:	JN PBOBJ,(P2),RTN	;OBJECT TYPE MUST BE ZERO
	SETZ T1,		;GET A ZERO FOR THE OBJECT TYPE
	CALL DNP1BY		;STORE OBJECT TYPE IN MESSAGE
	MOVX T3,^D16		;MAX LENGTH NAME IS 16 BYTES

BLDCP3:	LOAD T1,PBNCT,(P2)	;GET THE COUNT OF NAME BYTES
	CAMLE T1,T3		;RANGE CHECK THE LENGTH
	MOVE T1,T3		;OOPS, OVERFLOWED, USE MAXIMUM ALLOWED LENGTH
	XMOVEI T2,PB.NAM(P2)	;POINT TO THE STRING
	CALL PUTSTR		;PLACE THE STRING IN THE MESSAGE
	RETSKP			;RETURN SUCCESS

;Here to process a end user name format 2.  This format is a zero
;object type followed by GRPCODE (1B), USRCODE (1B) and DESCRPT (I-12).

BLDCP4:	JN PBOBJ,(P2),RTN	;OBJECT TYPE MUST BE ZERO
	SETZ T1,		;GET A ZERO FOR THE OBJECT TYPE
	CALL DNP1BY		;STORE OBJECT TYPE IN MESSAGE
	LOAD T1,PBGRP,(P2)	;GET THE GROUP CODE
	CALL DNP2BY		;PLACE IT IN THE MESSAGE
	LOAD T1,PBUSR,(P2)	;GET THE USER CODE
	CALL DNP2BY		;PLACE IT IN THE MESSAGE
	MOVX T3,^D12		;MAXIMUM LENGTH FOR THIS GUY IS 12 BYTES
	JRST BLDCP3		;MERGE WITH THE COMMON CODE
;PUTSTR - Subroutine to put a string from an internal block into a message
;
; Call:
;	T1/ Length of the string
;	T2/ Pointer to word aligned start of string

PUTSTR:	SAVEAC <P1,P2,T4>	;THIS IS A REALLY LOCAL ROUTINE
	MOVE P1,T2		;PUT THE POINTER IN P1
	MOVE T4,T1		;PRESERVE THE LENGTH FOR A WHILE
	CALL DNP1BY		;PLACE THE IMAGE COUNT IN THE STRING
	MOVX P2,<POINT 8,(P1)>	;BYTE POINTER TO 8 BIT STRING
	SOJGE T4,[ILDB T1,P2	;GET THE BYTE FROM CONNECT BLOCK
		  CALL DNP1BY	;PLACE THE BYTE IN THE MESSAGE
		  JRST .]	;KEEP GOING UNTIL DONE
	RET			; AND THEN RETURN
	SUBTTL Subroutines -- PRSCTX - Parse a connect message

;PRSCTX - Parse a connect message into an internal connect block
;
; Call:
;	T1/ Pointer to internal connect block (See BEGSTR CB in SCPRM)
;	MB/ Pointer to the Message Block
;	MS/ Pointer to the current MSD
;
; Return:
;	RET			;ON MESSAGE FORMAT ERROR AFTER REPORTING EVENT
;	RETSKP			;SUCESS, INTERNAL CONNECT BLOCK IS FILLED
;
; Uses: T1-T6
;
;Note: We are assuming that DNGINI has already been called and that we can
;just do DNGxBYs to get bytes from the message.

PRSCTX:	SAVEAC <P1,P2>		;SAVE A COUPLE OF PEAS
	MOVE P1,T1		;PRESERVE THE POINTER TO THE CONNECT BLOCK
	XMOVEI P2,CB.DST(P1)	;POINT TO THE DESTINATION PROCESS BLOCK
	CALL PRSCTP		;PARSE DSTNAME PORTION OF MESSAGE
	  RET			;OOPS
	XMOVEI P2,CB.SRC(P1)	;POINT TO THE SOURCE PROCESS BLOCK
	CALL PRSCTP		;PARSE SRCNAME PORTION OF MESSAGE
	  RET			;COULDN'T DO IT

;Here to look at the MENUVER field and see what to parse.

	CALL DNG1BY		;GET MENUVER
	  RET			;ERROR, CALLER WILL CALLRET SCEIVM
	MOVE P2,T1		;PRESERVE IT
	LOAD T1,MNVER,+P2	;GET THE VERSION NUMBER
	CAIGE T1,SC.VER		;[7302] Same or greater than ours?
	RET			; - error, caller will SCEIVM
	TXNN P2,MNRPA		;ARE RQSTRID, PASSWRD AND ACCOUNT COMING?
	JRST PRSCT1		;NO, CHECK FOR USRDATA

;Now read the RQSTRID, PASSWRD and the ACCOUNT.

	CALL DNG1BY		;GET THE LENGTH OF THE RQSTRID
	  RET			;ERROR, CALLER WILL CALLRET SCEIVM
	CAILE T1,^D39		;IS LENGTH OK?
	RET			;ERROR, CALLER WILL CALLRET SCEIVM
	STOR T1,CBUCT,(P1)	;STORE THE BYTE COUNT
	XMOVEI T2,CB.UID(P1)	;PUT THE TEXT HERE
	CALL GETSTR		;DO IT
	 RET			;[7175] Error - Caller will handle
	CALL DNG1BY		;GET THE LENGTH OF THE PASSWRD
	  RET			;ERROR, CALLER WILL CALLRET SCEIVM
	CAILE T1,^D39		;IS LENGTH OK?
	RET			;ERROR, CALLER WILL CALLRET SCEIVM
	STOR T1,CBPCT,(P1)	;STORE THE BYTE COUNT
	XMOVEI T2,CB.PSW(P1)	;STORE THE TEXT IN
	CALL GETSTR		; THE CONNECT BLOCK
	 RET			;[7175] Error - Caller will handle

	CALL DNG1BY		;GET THE LENGTH OF THE ACCOUNT STRING
	  RET			;ERROR, CALLER WILL CALLRET SCEIVM
	CAILE T1,^D39		;IS LENGTH OK?
	RET			;ERROR, CALLER WILL CALLRET SCEIVM
	STOR T1,CBACT,(P1)	;STORE THE BYTE COUNT
	XMOVEI T2,CB.ACC(P1)	;STORE THE ACCOUNT STRING
	CALL GETSTR		; IN THE CONNECT BLOCK
	 RET			;[7175] Error - Caller will handle

;Now check for the USRDATA field.

PRSCT1:	TXNN P2,MNUSR		;IS THERE ANY USER TEXT?
	RETSKP			;NO, WE'RE DONE

	CALL DNG1BY		;GET THE LENGTH OF THE USRDATA
	  RET			;ERROR, CALLER WILL CALLRET SCEIVM
	CAILE T1,^D16		;IS LENGTH OK?
	RET			;ERROR, CALLER WILL CALLRET SCEIVM
	STOR T1,CBCCT,(P1)	;STORE THE BYTE COUNT
	XMOVEI T2,CB.UDA(P1)	;PUT THE TEXT HERE
	CALL GETSTR		;DO IT
	 RET			;[7175] Error - Caller will handle
	RETSKP			;RETURN SUCCESS
;PRSCTP - Subroutine for PRSCTX to parse end user name portion of message
;
; Call:
;	P2/ Pointing to the process descriptor block

PRSCTP:	CALL DNG1BY		;GET THE FORMAT TYPE
	  RET			;ERROR, CALLER WILL RET TO CALLRET SCEIVM
	CAIL T1,FRM.0		;RANGE CHECK
	CAILE T1,FRM.MX		;  THE FORMAT
	RET			;ERROR, CALLER WILL RET TO CALLRET SCEIVM
	STOR T1,PBFOR,(P2)	;STORE IT IN THE PD BLOCK
	MOVE T4,T1		;SORT OF PRESERVE THE FORMAT
	CALL DNG1BY		;GET THE OBJECT TYPE
	  RET			;ERROR, CALLER WILL RET TO CALLRET SCEIVM
	JRST @.+1(T4)		;DISPATCH BY FORMAT TYPE
	   IFIW <PRSCP1&777777>	;FORMAT TYPE 0
	   IFIW <PRSCP2&777777>	;FORMAT TYPE 1
	   IFIW <PRSCP3&777777>	;FORMAT TYPE 2

;Here to parse the end user name format 0.

PRSCP1:	SKIPN T1		;IS THERE A NON-ZERO OBJECT TYPE?
	RET			;ERROR, CALLER WILL RET TO CALLRET SCEIVM
	STOR T1,PBOBJ,(P2)	;YES, STORE IT
	RETSKP			; AND WE'RE DONE

;Here to parse the end user name format 1.

PRSCP2:	CALL DNG1BY		;GET THE IMAGE COUNT
	  SETZ T1,		;ASSUME NO TEXT IF NO COUNT FIELD
	CAILE T1,^D16		;IS IT OF ACCEPTABLE LENGTH?
	RET			;ERROR, CALLER WILL RET TO CALLRET SCEIVM
	STOR T1,PBNCT,(P2)	;STORE THE COUNT OF BYTES IN NAME
	JUMPE T1,RSKP		;SUCCESS NOW IF NO STRING TO GET
	XMOVEI T2,PB.NAM(P2)	;POINT TO WERE TO PUT THE STRING
	CALL GETSTR		;GET THE STRING INTO THE PB
	 RET			;[7175] Error - Caller will handle
	RETSKP			; AND WE'RE DONE
;Here to parse the end user name format 2.

PRSCP3:	CALL DNG2BY		;GET THE GRPCODE
	  RET			;ERROR, CALLER RETS TO CALLRET SCEIVM
	STOR T1,PBGRP,(P2)	;STORE IT IN THE PB
	CALL DNG2BY		;GET THE USRCODE
	  RET			;ERROR, CALLER RETS TO CALLRET SCEIVM
	STOR T1,PBUSR,(P2)	;STORE IT IN THE PB
	CALL DNG1BY		;GET THE IMAGE LENGTH
	  SETZ T1,		;NO LENGTH FIELD, ASSUME ZERO LENGTH
	CAILE T1,^D14		;IS LENGTH OK?
	RET			;ERROR, CALLER RETS TO CALLRET SCEIVM
	STOR T1,PBNCT,(P2)	;STORE THE BYTE COUNT
	JUMPE T1,RSKP		;SUCCESS NOW IF NO MORE STRING
	XMOVEI T2,PB.NAM(P2)	;POINT TO THE NAME AREA IN THE PB
	CALL GETSTR		;PLACE THE STRING IN THE PB
	 RET			;[7175] Error - Caller will handle
	RETSKP			; AND RETURN SUCCESSFULLY
;GETSTR - Get a string from the message and place it in the connect block
;
;	T1/ Byte count
;	T2/ Pointer to the string area in the internal connect block

GETSTR:	SAVEAC <P1,P2,FREE0>
	DMOVE P1,T1		;SAVE THE BYTE COUNT AND POINTER
	MOVX FREE0,<POINT 8,(P2)> ;BYTE POINTER USING FULLWORD IN P2
	SOJGE P1,[CALL DNG1BY	;GET THE BYTE
		   RET		;[7175] Return the error
		  IDPB T1,FREE0	;STORE IN CONNECT BLOCK
		  JRST .]	; AND CONTINUE UNTIL WE RUN OUT
	RETSKP			;RETURN

;GETSTP performs the same function but takes the data from a string.
;
; Call:
;	T1/ Count
;	T2/ Fullword Pointer
;	T3/ String Block Address (word aligned)
;
; Return:
;	RET			;WITH STRING IN BLOCK

GETSTP:	SAVEAC <P1,P2>		;SAVE FOR BYTE POINTERS
	DMOVE P1,[POINT 8,0(T2)
		  POINT 8,0(T3)];BYTE POINTERS FOR FULL WORD POINTERS
	SOJGE T1,[ILDB T4,P1	;GET A BYTE FROM STRING
		  IDPB T4,P2	;PLACE IT IN THE STRING BLOCK
		  JRST .]	; AND CONTINUE UNTIL DONE
	RET			;RETURN TO SENDER
	SUBTTL Subroutines -- CPYS2M - Copy string block to message segment

;CPYS2M - Copy string block to message segment
;
; Call:
;	T1/ Pointer to SBLOCK to copy into message
;	MS/ Pointer to current message segment
;
; Return:
;	RET			;ALWAYS
;
; Uses: T1-T6
;
;Note: CPYS2I is identical except that it doesn't put a image count into
;the message (used for interrupt data).

CPYS2M:	SAVEAC P1		;SAVE A PEA
	SKIPE P1,T1		;SAVE STRING BLOCK POINTER
	LOAD T1,SBCNT,(P1)	;GET COUNT OF BYTES IN STRING
	CALL DNP1BY		;PUT IMAGE COUNT IN THE MESSAGE
	JUMPE P1,RTN		;IF NO STRING BLOCK, RETURN NOW

CPYS21:	LOAD T1,SBCNT,(P1)	;GET THE BYTE COUNT AGAIN
	MOVX T2,<<POINT 8,>!1B12> ;MAKE UP 2-WORD BYTE POINTER
	XMOVEI T3,SB.DAT(P1)	;POINT TO THE DATA
	CALLRET DNCB2M		;COPY STRING TO THE MESSAGE

;Here to copy interrupt data into the message block.

CPYS2I:	SAVEAC P1		;SAVE A PEA
	JUMPE T1,RTN		;IF THERE'S NO DATA TO COPY, JUST RETURN
	MOVE P1,T1		;PRESERVE POINTER TO THE SBLOCK
	CALLRET CPYS21		;MERGE WITH OTHER CODE
	SUBTTL Subroutines -- CPYM2S - Copy message data to string block

;CPYM2S - Copy message data to string block
;
; Call:
;	T1/ Pointer to String Block to copy into
;	MS/ Pointer to current message segment
;	MB/ Pointer to current message block
;
; Return:
;	RET			;ON A FAILURE
;	RETSKP			;SUCCESSFUL
;
; Uses: T1-T6
;
;Note: CPYI2S performs the same function, expcept it gets the data
;length from the remaining length of the message (used for interrupt
;data).

CPYM2S:	SAVEAC <P1,P2>		;SAVE A PEA
	MOVE P1,T1		;SAVE STRING BLOCK POINTER
	MOVE T1,MB		;POINT TO MESSAGE BLOCK
	CALL DNLENG		;GET LENGTH OF THE MESSAGE
	SOS P2,T1		;SAVE LENGTH OF DATA IN MSG
	CALL DNG1BY		;GET THE IMAGE COUNT
	  JRST [SETZ T1,	   ;IF THERE WAS NO COUNT
	        STOR T1,SBCNT,(P1) ; WE SHOULD RETURN A ZERO AS
	        RETSKP]		   ; BYTE COUNT IN SBLOCK
	CAMLE T1,P2		;THAT LENGTH LOOK OK?
	RET			;NO, CALLER WILL CALLRET SCEIVM

CPYM21:	STOR T1,SBCNT,(P1)	;STORE THE REAL COUNT
	JUMPE T1,RSKP		;IF NOTHING THERE, JUST RETURN
	MOVX T2,<<POINT 8,>!1B12> ;MAKE UP 2-WORD BYTE POINTER
	XMOVEI T3,SB.DAT(P1)	;POINT TO THE TEXT PORTION
	CALLRET DNCM2B		;COPY IT TO BUFFER AND RETURN

;Here to copy interrupt data from a message block to a string block.

CPYI2S:	SAVEAC P1		;SAVE A PEA
	MOVE P1,T1		;SAVE SBLOCK POINTER
	MOVE T1,MB		;POINT TO MESSAGE BLOCK
	CALL DNLENG		;GET LENGTH OF THE MESSAGE
	CAILE T1,^D16		;LEGAL INTERRUPT MSG?
	RET			;NO, CALLER WILL CALLRET SCEIVM
	CALLRET CPYM21		;MERGE WITH OTHER CODE
	SUBTTL Subroutines -- SCTGSS - Get segment size for a destination

;SCTGSS - get the segment size we are willing to use to a specific destination.
; Used to implement 'big buffers on the NI'.
;
; Call:
;	T1/ 16-bit destination node address
;	CALL SCTGSS
;
; Return:
;	RET always with T1/ segment size to use

SCTGSS:	CALL RTRGBS		;Get ROUTER buffer size
	SUBI T1,%SCHDR		; and subtract max headers below SC
	RET
	SUBTTL Subroutines -- SCTCSS - Check segment size for a destination

;SCTCSS - check the segment size for a specific destination.
; Used to implement 'big buffers on the NI'
;
; Call:
;	T1/ 16-bit destination node address
;	T2/ the segment size the remote is willing to use
;	CALL SCTCSS
;
; Return:
;	RET with T1/ the segment size to use

SCTCSS:	ADDI T2,%SCHDR		;Get the right value for ROUTER to compare with
	CALL RTRCBS		;Ask ROUTER for value
	SUBI T1,%SCHDR		; and remove header length
	RET
	SUBTTL Node name/number database -- Initialize

;SCTNIN - Initialize node database
;	CALL SCTNIN
;
; Return:
;	RET	Failed to initialize
;	RETSKP	Success.

	XSWAPCD
SCTNIN:

;The node name/number database use swappable extended section memory.
; ASGVAS is used to allocate space.
;
;At initialization time (below) the hash table and the address table
; are allocated.  The layout of these are:
;
;  HSHTAB	ADRTAB
;    !		  !
;  ------------------------------
;  ! SCNHSZ	! RTRMXN+1      !
;  ------------------------------
;
;As needed, space for the node name buckets are allocated, 1 page at
; a time.  QHBUCK points to the head of free buckets:
;
;  QHBUCK ---)  BUNXT  ---) BUNXT ---) 0
;
	MOVEI T1,SCNHSZ		;Hash table size
	OPSTR <ADD T1,>,IBMXA,+IBBLK ;Add max # of nodes in area
	AOJ T1,			; and an extra word to offset from 0
	BLCAL.(<@[MSEC1,,ASGVAS]>,<[0],T1>) ;Get unlocked extended virtual mem
	  BUG.(CHK,SCLVAS,SCLINK,SOFT,<SCLINK - Couldn't get memory>,,<

Cause:	SCLINK called ASGVAS to assign virtual address space for the node
	name/address database. Since the requested memory is non-resident,
	this should always succeed. However, ASGVAS gave a fail return.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.
>,RTN,<DB%NND>)			;[7.1210] and return error
;Success return from ASGVAS, T1 has address of first location

	MOVEM T1,HSHTAB		;Save hash table address
	ADDI T1,SCNHSZ		;Add hash table size
	MOVEM T1,ADRTAB		;Save node address table address

;No buckets yet
	SETZM QHBUCK		;Clear bucket header

;Unlock the node database
	UNLOCK NODLOK

;Insert executor node name and address into database
	LOAD T1,IBNAM,+IBBLK	;Get sixbit executor name
	LOAD T2,IBADR,+IBBLK	; and executor address
	CALLRET SCTAND		;Insert them into database and return
	SUBTTL Node name/number database -- Lock/unlock database

;LOKNOD - Lock/unlock node database
;
;This is a coroutine (like a SAVEAC) that locks the node database
;
; Call:	JSP CX,LOKNOD
;
; Return: forwards a +1 or +2 return
;
; Must be called in process context, without already having the lock.

LOKNOD:	PUSH P,CX		;Save PC for a little while
	LOCK NODLOK		;Lock the database, wait if necessary
	NOINT			;No user interrupts while we have lock
	CSKED			;Go CSKED since we have a system resource
	POP P,CX		;Retrieve PC
	CALL 0(CX)		;Call processing routine
	IFNSK.			; +1 return
	  UNLOCK NODLOK		;  Unlock,
	  OKINT			;   allow interrupts
	  ECSKED		;    and exit critical section
	  RET			;     and pass on +1 return
	ELSE.			; +2 return
	  UNLOCK NODLOK		;  Unlock
	  OKINT			;   allow interrupts
	  ECSKED		;    and exit critical section
	  RETSKP		;     and pass on +2 return
	ENDIF.
;Can never get here
	SUBTTL Node name/number database -- Add a node

;SCTAND - Add a node name to the database
;
; Call:	T1/ node name (in sixbit). Zero to clear
;	T2/ Node address. If area # is not present, it will be
;	    to local area.
;	CALL SCTAND
;
; Return:
;	RET	Name already taken, or node address out of range
;	RETSKP	Node name and address added to the database

	XSWAPCD
SCTAND:	SAVEAC <T6,P1,P2>
	JSP CX,LOKNOD		;Lock node database
	DMOVE P1,T1		;Save node name and address in P1,P2
	CALL CHKADR		;Check address, and default area if needed
	  RET			; -bad address, pass on error code

;P2 now contains a node address with area #, T6 is non-zero if area = home area

;Delete option?
	JUMPE P1,SCTDND		;Go delete a node

;Check if the node already exists in the database
	CALL NAM2NO		;Map node name to NO pointer
	JRST SCTAN1		; -no, go check address before adding node

;Here to change a node, T1 points to NO block
;
;Are we changing executor (not allowed unless changing to same..)
	LOAD T2,NOADR,(T1)	;Get node address
	OPSTR <CAME T2,>,IBADR,+IBBLK ;Executor?
	IFSKP.			;  -yes,
	  LOAD T2,NONAM,(T1)	;   Get node name
	  OPSTR <CAME T2,>,IBNAM,+IBBLK	; Same as before?
	  RETBAD (NF.CWS)	;     -no, not allowed, component in wrong st.
	  RETSKP		;     -yes, say its ok
	ENDIF.

;Is the node number we are replacing in the local area. If so, delete pointer
; from ADRTAB
	LDAREA T3,T2		;Get old area number
	CAME T3,RTRHOM		;IS it local area?
	IFSKP.			; -yes, must delete old pointer in ADRTAB
	  LDNODE T3,T2		;  Get local node index
	  ADD T3,ADRTAB		;   and add in ADRTAB base address
	  SETZM (T3)		;    and clear the link
	  SOS SCTNDC		;     and one less local node
	ENDIF.

;Now write the new node address into the tables
	STOR P2,NOADR,(T1)	;Store new node address

;If we are adding to local area, update ADRTAB
	SKIPN T6		;Home area?
	IFSKP.			; -yes,
	  LDNODE T2,P2		;  Get new local node index
	  ADD T2,ADRTAB		;   and add address table address
	  MOVEM T1,(T2)		;    and store NO pointer in address table
	  AOS SCTNDC		;  Increment # of local nodes defined
	ENDIF.

;All done, return
	RETSKP

;SCTAN1 - node name is not in database
SCTAN1:	SKIPN T6		;Home area?
	IFSKP.			; -yes,
	  LDNODE T2,P2		;  Get node index
	  ADD T2,ADRTAB		;   and get pointer into address table
	  SKIPE (T2)		;  Entry non-zero?
	  RETBAD (NF.CWS)	;   -yes, not allowed
	ENDIF.

;Find the hash index for this node name
	CALL HSHNAM		;Get the hash index
	ADD T1,HSHTAB		; and make address of hash table entry
	SKIPN (T1)		;Is there a hash chain already?
	JRST SCTAN2		; -no, go add a hash chain

;There is a hash chain, search buckets for an empty slot, or end of chain
	MOVE T1,(T1)		;Point to first bucket
	DO.			;LOOP over buckets
	  XMOVEI T2,BU.NO1(T1)	;  Point to first NO block in bucket
	  MOVX T3,NRNOPB	;  # of NO blocks/bucket
	  DO.			;  LOOP over NO blocks
	    OPSTR <SKIPE>,NONAM,(T2) ;Is node name clear?
	    IFSKP.		;    -yes,
	      STOR P1,NONAM,(T2) ;    store name
	      STOR P2,NOADR,(T2) ;     and address
	      JRST SCTAN3	;     Go join common code to update ADRTAB
	    ENDIF.
	    ADDI T2,NO.LEN	;    Move to next NO block
	    SOJG T3,TOP.	;    And loop back if more to do
	  ENDDO.
	  OPSTR <SKIPN T2,>,BUNXT,(T1) ;Go to next bucket, if any
	  EXIT.			;      -no, exit with T1/ last bucket
	  MOVE T1,T2		;      -yes, bucket pointer in T1
	  LOOP.			;        and loop back
	ENDDO.

;Come here of there are no more buckets, T1 points to last one

;SCTAN2 - add a hash chain, T1 has address of hash entry
SCTAN2:	ASSUME BU.NXT,EQ,0
	MOVE T4,T1		;"Save" bucket pointer in T4
	CALL GETBUC		; and get a bucket
	  RETBAD (NF.RES)	;  -failed (!!)
	XMOVEI T2,BU.NO1(T1)	;Point to first NO block in bucket
	STOR P1,NONAM,(T2)	; and store node name
	STOR P2,NOADR,(T2)	;  and node address
	STOR T1,BUNXT,(T4)	;Link new bucket into chain
;	JRST SCTAN3		;Go join common code to update ADRTAB

;SCTAN3 - update address table with a new node. NO pointer in T2
SCTAN3:	JUMPE T6,RSKP		;No more to do if not home area
	LDNODE T1,P2		;Get local node index
	ADD T1,ADRTAB		; and add address table address
	MOVEM T2,(T1)		;Store NO pointer
	AOS SCTNDC		;Increment # of local nodes known
	RETSKP			; and done

;SCTDND - delete a node
SCTDND:	OPSTR <CAMN P2,>,IBADR,+IBBLK ;Deleting executor?
	RETBAD (NF.CWS)		; -not allowed
	MOVE T1,P2		;Get node address
	CALL LSCTA2N		; and map it to a name, use locked entry point
	 RETSKP			;  -not found, no more to do

;If home area, clear pointer from ADRTAB to NO block
	SKIPN T6		;Home area?
	IFSKP.			; -yes,
	  LDNODE T1,P2		;  Get local node index
	  ADD T1,ADRTAB		;   and add address table address
	  SETZM (T1)		;    and clear entry
	  SOS SCTNDC		;  Decrement # of local nodes
	ENDIF.

;Now clear node name in NO block
	SETZRO NONAM,(T2)	;Clear
	SETZRO NOADR,(T2)	; Might as well clear address too
	RETSKP			;  and done
	SUBTTL Node name/number database -- Address to name

;SCTA2N - Do node address to name mapping
;
; Call:	T1/ Node address. If an area # is not present, it will be
;	    defaulted to local area.
;	CALL SCTA2N
;
; Return:
;	RET	The address does not have a name set
;	RETSKP	Node name in sixbit returned in T1, NO pointer in T2

	XNENT SCTA2N

	JSP CX,LOKNOD		;Lock node database
LSCTA2N:			;Entry point if already locked
	SAVEAC <T6,P1,P2>
	MOVE P2,T1		;P2 has node address
	CALL CHKADR		;Check address, and default area if needed
	  RET			; -bad address, pass on error code

;P2 now has node address with area #, T6 is non-zero if area = home area
	JUMPE T6,SCTA21		;If not local area, do it the cumbersome way..

;Verify that the node address is in range
	LDNODE T1,P2		;Get local node index
	CAMLE T1,RTRMXN		;Compare with max nodes in area
	RETBAD (NF.IPV)		; -bad address

;Get NO pointer from ADRTAB
	ADD T1,ADRTAB		;Add address of ADRTAB
	SKIPN T2,(T1)		;Is there a NO pointer
	RETBAD (NF.URC)		; -no, node is not known

  IFN FTDEBUG <
;Do a sanity check
	OPSTR <CAME P2,>,NOADR,(T2) ;Node addresses match?
	BUG.(CHK,SCLA2N,SCLINK,SOFT,<Node database inconsistent>,<<T1,NOD>>,<

Cause:	The node database SCLA2N failed an internal consistency check.

Action:	If this bug is reproducible, set it dumpable and send in an SPR along
	with how to reproduce the problem.

Data:	NOD - NO pointer in ADRTAB

>,RTN)
  >
	LOAD T1,NONAM,(T2)	;Get node name
	RETSKP			; and return with T1 and T2
;SCTA21 - map non-local node address
SCTA21:

;T6 can be reused, not needed as flag any more
; Use T6 as hash table index
	SETZ T6,		;Start at index 0
	DO.			;LOOP over hash table
	  CAIL T6,SCNHSZ	;  Done all of table?
	  RETBAD (NF.URC)	;  -yes, no match
	  MOVE T1,T6		;  Move hash index to T1
	  ADD T1,HSHTAB		;  And make hash table address
	  SKIPN T1,(T1)		;  Is there a hash bucket chain?
	  AOJA T6,TOP.		;  -no, loop back to next index
	  DO.			;  LOOP over hash bucket chain
	    XMOVEI T2,BU.NO1(T1) ;   Pointer to first NO block for this bucket
	    MOVX T3,NRNOPB	;    and # of NO blocks in this bucket
	    DO.			;    LOOP over NO blocks in bucket
	      OPSTR <CAME P2,>,NOADR,(T2) ;Node addresses match?
	      IFSKP.		;       -yes,
		LOAD T1,NONAM,(T2) ;     Get node name
		RETSKP		;         and return (T1 and T2)
	      ENDIF.
	      ADDI T2,NO.LEN	;       -no match, go to next NO block
	      SOJG T3,TOP.	;        -if there is any
	    ENDDO.
	    OPSTR <SKIPE T1,>,BUNXT,(T1) ;Move to next bucket
	    LOOP.		;        and loop back
	  ENDDO.
	  AOJA T6,TOP.		; and move to next hash table entry
	ENDDO.
;Will never get here
	SUBTTL Node name/number database -- Name to address

;SCTN2A - Do node name to address mapping
;
; Call: T1/ Node name (in sixbit).
;	CALL SCTN2A
;
; Return:
;	RET	No such name
;	RETSKP	Node address returned in T1

	RESCD
SCTN2A:	TOXSWAPCD
	JSP CX,LOKNOD		;Lock node database
LSCTN2A:			; Already locked entry point
	SAVEAC <T6,P1,P2>
	MOVE P1,T1		;P1 contains node name
	CALL NAM2NO		;Map node name to NO block
	RETBAD (NF.URC)		; -node name not in database
	LOAD T1,NOADR,(T1)	;Get node address
	RETSKP			; and return it successfully
	SUBTTL Node name/number database -- Name to NO block

;NAM2NO - map a node name to NO block
;
; Call:	P1/ node name (in sixbit)
;	CALL NAM2NO
;
; Return:
;	RET	Node name not in database
;	RETSKP	T1/ NO pointer

NAM2NO:	CALL HSHNAM		;Get a hash value from name
	ADD T1,HSHTAB		;Get pointer to hash table entry
	SKIPN T2,(T1)		;Get pointer to first bucket
	RET			; -none, return error

;Hash table entry (in T1) has address of first bucket in chain
	DO.			;LOOP over hash chain
	  XMOVEI T1,BU.NO1(T2)	;  Point to first NO block in bucket
	  MOVX T3,NRNOPB	;  # of nodes/bucket
	  DO.			;  LOOP over NO blocks
	    OPSTR <CAMN P1,>,NONAM,(T1)	;Node name matches?
	    RETSKP		;    -yes, return with T1 setup

	    ;No match, go to next NO block in bucket
	    ADDI T1,NO.LEN	;     Move T1
	    SOJG T3,TOP.	;      and do next NO block
	  ENDDO.

	  ;Come here when one bucket is exhausted, go to next if there is any
	  OPSTR <SKIPE T2,>,BUNXT,(T2) ;Move to next
	  LOOP.			;   -yes, loop and do next bucket
	  RET			;   -no more in this chain, fail return
	ENDDO.
;Will never get here
	SUBTTL Node name/number database -- Default area #

;CHKADR - check node address, and default area
;
; Call:	P2/ node address
;	CALL CHKADR
;
; Return:
;	RET	Bad node address
;	RETSKP	P2/ node address with area #, T6 non-zero if home area

CHKADR:	TXNE P2,RN%ARE		;Is there an area #?
	IFSKP.			; -no, default it
	  MOVE T1,RTRHOM	;  Get ROUTER home area
	  STAREA T1,P2		;   and store it
	ELSE.
	  LDAREA T1,P2		;  Get area # if there is one
	ENDIF.
	SETZ T6,		;Assume not home area
	CAME T1,RTRHOM		;Is it?
	RETSKP			; -yes, all done
	SETO T6,		;It is home area
	LDNODE T1,P2		;Get local node index
	OPSTR <CAMLE T1,>,IBMXA,+IBBLK ;In range?
	RETBAD (NF.IPV)		; -no, return failure
	RETSKP			;-yes, all ok
	SUBTTL Node name/number database -- Hash routine

; Call:	T1/ Node name (in sixbit)
;	CALL HSHNAM
;
; Return:
;	RET	Index into hash table in T1 [0..SCNHSZ-1]
;
; Note:
;	May not touch T4....


	XSWAPCD
HSHNAM:	MOVM T1,P1		;We know first character is alphanumeric..
	IDIVI T1,SCNHSZ		;Divide by table size (prime #)
	MOVE T1,T2		;Use the modulo value
	RET
	SUBTTL Node name/number database -- Get a bucket

;GETBUC - get a bucket off free bucket list
;
; Call:	CALL GETBUC
;
; Return:
;	RET	No bucket available (!!)
;	RETSKP	T1 contains address of bucket

	XSWAPCD
GETBUC:	SKIPE T1,QHBUCK		;Get first free bucket
	IFSKP.			; -none, get more space
	  BLCAL.(<@[MSEC1,,ASGVAS]>,<[0],[PGSIZ]>) ;Get one page for buckets
	    RETBAD (NF.RES)	;  !! Couldn't get any more space
	  MOVEM T1,QHBUCK	;  Save pointer to first bucket
	  ASSUME <PGSIZ/BU.LEN>,GT,0 ;Verify bucket size is reasonable
	  MOVX T2,<PGSIZ/BU.LEN> ; Get # of buckets per page
	  MOVE T3,T1		;  Get bucket pointer into right AC for loop
	  DO.			;  LOOP over buckets
	    SOJLE T2,ENDLP.	;    Exit if all buckets linked
	    MOVE T1,T3		;    Copy pointer of current bucket
	    ADDI T3,BU.LEN	;     and move to next bucket
	    STOR T3,BUNXT,(T1)	;    Link new bucket into old
	    LOOP.		;     and loop
	  ENDDO.
	  MOVE T1,QHBUCK	;  Get bucket header again (now non-zero)
	ENDIF.
	LOAD T2,BUNXT,(T1)	;Get link to next free bucket
	MOVEM T2,QHBUCK		; and make that the new first bucket
	SETZRO BUNXT,(T1)	;Clear link to next word
	RETSKP
	SUBTTL Node name/number database -- SCTANL - Add a loopback node name

;SCTANL - Add a loopback node name to table
;
; Call
;	T1/ Node name (in sixbit). Zero to clear.
;	T2/ Circuit id
;
; Return:
;	RET			;NAME ALREADY TAKEN
;	RETSKP			;LOOPBACK NODE CREATED

	XSWAPCD
SCTANL:	JSP CX,LOKNOD		;Lock node database
	SAVEAC <P1,P2>		;STORAGE SPACE
	DMOVE P1,T1		;SAVE ARGUMENTS
	JUMPE T1,SCTAL5		;CLEARING?
	CALL LSCTN2A		;FIND OUT IF THIS NAME ALREADY EXISTS, locked
	IFNSK.			; -no,
	  MOVE T1,P1		;  maybe loopback node name?
	  CALL LSCTCKL		;  so check that
	    RETBAD (NF.NRM)	;   -yes, return "name already taken"
	ENDIF.
	MOVE T1,SCTLNL		;GET POINTER TO LIST OF LOOPBACK NODES
SCTAL1:	JUMPE T1,SCTAL2		;IF END OF LIST, ADD IT IN
	OPSTR <CAMN P2,>,LNCIR,(T1) ;MAKE SURE CIRCUIT ID DOESN'T MATCH
	RETBAD (NF.NRM)		;CIRCUIT ALREADY HAS A LOOPBACK NODE
	LOAD T1,LNNXT,(T1)	;GET POINTER TO NEXT LOOPBACK NODE
	JRST SCTAL1		;AND LOOP

SCTAL2:	MOVX T1,LN.LST		;GET SIZE OF BLOCK WE NEED
	CALL DNGWDP		;ALLOCATE IT
	  RETBAD (NF.RES)	;OUT OF RESOURCES
	STOR P1,LNNAM,(T1)	;SAVE LOOPBACK NAME
	STOR P2,LNCIR,(T1)	;SAVE CIRCUIT ID
	MOVE T2,SCTLNL		;#GET POINTER TO HEAD OF LIST
	STOR T2,LNNXT,(T1)	;#STORE AS NEXT
	MOVEM T1,SCTLNL		;#AND STORE POINTER TO THIS AS HEAD
	RETSKP			;AND RETURN SUCCESS

SCTAL5:	MOVE T1,T2		;Get circuit ID
	MOVE T2,SCTLNL		;GET POINTER TO LOOPBACK NODE LIST
	MOVE T3,[MSEC1,,SCTLNL]	;PLACE TO BACK UP TO
SCTAL6:	SKIPN T2		;Skip if there is such circuit
	RETBAD (NF.URC)		; -no such circuit
	OPSTR <CAMN T1,>,LNCIR,(T2) ;Is this the circuit we are interested in?
	JRST SCTAL7		;YES
	MOVE T3,T2		;SAVE POINTER TO THIS BLOCK
	LOAD T2,LNNXT,(T2)	;LOOK AT NEXT loop node
	JRST SCTAL6		;AND LOOP

SCTAL7:	LOAD T1,LNNXT,(T2)	;GET POINTER TO NEXT BLOCK
	STOR T1,LNNXT,(T3)	;SAVE IN PREDECESSOR, DELINKING THIS BLOCK
	MOVE T1,T2		;POINTER TO THIS BLOCK
	CALL DNFWDS		;RETURN THE CORE
	RETSKP			;AND DONE
	SUBTTL Node name/number database -- SCTN2L - Convert name to loop circ

;SCTN2L - Convert name to loopback circuit
;
; Call
;	T1/ Node name (in sixbit).
; Return:
;	RET			;NO SUCH LOOPBACK NODE
;	RETSKP, T1/ Circuit ID

	RESCD
SCTN2L:	TOXSWAPCD
	JSP CX,LOKNOD		;Lock node database
	MOVE T2,SCTLNL		;GET POINTER TO HEAD OF LOOPBACK NODE LIST
	DO.
	  SKIPN T2		;Skip if another node
	  RETBAD (NF.URC)	; -no such name
	  OPSTR <CAME T1,>,LNNAM,(T2) ;IS THIS THE NODE WE WANT?
	  IFSKP.		; -yes,
	    LOAD T1,LNCIR,(T2)	;  Get circuit ID
	    RETSKP
	  ENDIF.
	  LOAD T2,LNNXT,(T2)	;NOPE, TRY FOR THE NEXT
	  LOOP.
	ENDDO.
;Can never get here
	SUBTTL Node name/number database -- SCTL2N - Convert loopback circuit

;SCTL2N - Check for existance of a loopback node name associated with a
;circuit.
;
; Call:
;	T1/ circuit id (LI format)
;
; Return:
;	RET	No loopback name associated with circuit
;	RETSKP	T1/ sixbit loopback node name

	XSWAPCD
SCTL2N:	JSP CX,LOKNOD		;Lock node database
	MOVE T2,SCTLNL		;Get pointer to header of loopback node list
	DO.			;LOOP over list
	  SKIPN T2		;  More items in list?
	  RETBAD (NF.URC)	;   -no, end of list, no such name
	  OPSTR <CAME T1,>,LNCIR,(T2) ;Circuits match?
	  IFSKP.		;  -yes,
	    LOAD T1,LNNAM,(T2)	;   Get loopback node name
	    RETSKP		;    and return
	  ENDIF.
	  LOAD T2,LNNXT,(T2)	; -no, go to next loopback node
	  LOOP.			;  and loop back
	ENDDO.
;Will never get here
	SUBTTL Node name/number database -- SCTCKL - Check for loopback node

;SCTCKL - Check for existance of loopback node. Meant to be used with SCTN2A,
;	and thus has a reversed return sequence to indicate existance.
;
;Call
;	T1/ Node name (in sixbit).
;Return
;	RET			;NODE EXISTS
;	RETSKP			;NODE DOESN'T EXIST

	XSWAPCD
SCTCKL:	JSP CX,LOKNOD		;Lock node database
LSCTCKL:			;Entry point if already locked
	MOVE T2,SCTLNL		;GET POINTER TO HEAD OF LOOPBACK NODE LIST
	DO.			;LOOP through list
	  JUMPE T2,RSKP		;  Skip return at end of list
	  OPSTR <CAMN T1,>,LNNAM,(T2) ;IS THIS THE NODE WE WANT?
	  RET			;YES, DECLARE THAT IT EXISTS
	  LOAD T2,LNNXT,(T2)	;GET POINTER TO NEXT
	  LOOP.			;Do next node
	ENDDO.
;Can never get here
	SUBTTL Network management -- Dispatch

;SCLNMX is the entry point for all network management calls to SCLINK.
;
; Call:
;	T1/ function code
;	T2/ NF block address
;
; Return:
;	RET	Network management error code in T1
;	RETSKP	Operation succeded

	XSWAPCD			;All of network management is swappable

;Define dispatch table

NMXFNT:

INIDSP NF.,NMX,0
	DSP SET			;SET parameter
	DSP CLR			;CLEAR parameter
	DSP RED			;READ parameter
	DSP COU			;SHOW counters
	DSP SZC			;SHOW and ZERO counters
	DSP RET			;Return list of entity ids
	DSP A2N			;Map Node address to Node name
	DSP N2A			;Map Node name to node address
	DSP CET			;Check entity Id
	DSP CKL			;Check loopback node
ENDDSP

	ASSUME NF.CKL,EQ,NF.MAX	;Verify our dispatch table

;Network management parameter table
NODPAR:

PARAMETER(^D500,<PANRD!PADRC>,,,0,<CALL SET500>,JFCL,<CALL CLR500>,<Name>)
PARAMETER(^D501,PADRC,,,,<CALL SET501>,<CALL RED501>,<CALL CLR501>,<Circuit>)
PARAMETER(^D502,<PANRD!PADRC>,,,0,<CALL SET502>,JFCL,<CALL CLR502>,<Address>)
PARAMETER(^D510,,^D65535*TIMBAS,^D1000,%SCINT,<MOVEM T2,SCTINT>,<MOVE T2,SCTINT>,<MOVEM T2,SCTINT>,<Incoming timer>) ;[7318]
PARAMETER(^D511,,^D65535*TIMBAS,^D1000,%SCOTT,<MOVEM T2,SCTOTT>,<MOVE T2,SCTOTT>,<MOVEM T2,SCTOTT>,<Outgoing timer>) ;[7318]

NRNPAR==.-NODPAR

CIRPAR:

PARAMETER(^D400,PANST!PANCL,,,,JFCL,<CALL RED400>,JFCL,<Loopback name>)

NRCPAR==.-CIRPAR

	XSWAPCD
SCLNMX:	CAIL T1,NF.SET		;Range check
	CAILE T1,NF.MAX		; the function code
	RNMXER (NF.UFO)		;"Unrecognized function or option"
	SAVEAC <P1,P2>		;Save preserved ACs
	MOVE P1,T2		;Put NF block in P1
	JRST @NMXFNT(T1)	;Go to processing routine

;Unsupported function codes come here

;Session control does not maintain any counters
NMXCOU:
NMXSZC:

;Check entity ID should never be called
NMXCET:
	RNMXER (NF.MPE)		;"Management program error"
	SUBTTL Network management -- SET/CLEAR/READ parameter

;NMXSET - SET a network management paramater
;
; Call:
;	T1/ function code
;	P1/ NF block
;	NFEID/ entity ID
;	NFPRM/ parameter #
;	NFETY/ entity type
;	NFBFF/ zero
;	NFBUF/ parameter value

NMXSET:
NMXCLR:
NMXRED:
	MOVE P2,T1		;Save function code
	LOAD T1,NFETY,(P1)	;Get entity type
	CAIE T1,.NTNOD		;NODE?
	IFSKP.			; -yes,
	  XMOVEI T1,NODPAR	;  Load node table
	  MOVEI T2,NRNPAR	;   and its length
	ELSE.			; -not NODE
	  CAIE T1,.NTCKT	;  Is it circuit?
	  IFSKP.		;  -yes,
	    XMOVEI T1,CIRPAR	;   Load circuit table
	    MOVEI T2,NRCPAR	;    and its length
	  ELSE.			;  -neither NODE nor CIRCUIT
	    RNMXER (NF.URC)	;   -so return error
	  ENDIF.
	ENDIF.
	MOVE T3,P2		;Retrieve function code
	CALLRET NTPARM		;Call NTPARM to do more work

;RED400 - READ circuit parameter 400
; Call:
;	P1/ points to NF block
;	NFEID/ circuit ID (LI format)
;
; Return:
;	RET	No loopback name associated with circuit
;	RETSKP	T1/ 0 and T2/ loopback name (sixbit)

RED400:	SAVEAC <T3,T4>		;This coroutine may not touch T3 and T4
	LOAD T1,NFEID,(P1)	;Get circuit ID
	CALL SCTL2N		;Convert loopback circuit to name
	  RNMXND		;  -not there, return 'no data'
	MOVE T2,T1		;Put it in T2
	RNMXOK			; and return success

;SET500 - set node parameter 500 (NAME)
;CLR500 - read  "      "      "     "
;
; Call:
;	NFEID/	Node address
;	T2/ sixbit node name in SET case, 0 in CLEAR
;
SET500:
CLR500:	SAVEAC <T3,T4>		;Must be preserved
	MOVE T1,T2		;Move node name into place
	LOAD T2,NFEID,(P1)	; and node address
	CALL SCTAND		;Insert/remove into table
	RNMXER ()		;Pass error code back up
	RNMXOK			;Success return

;SET501 - set node parameter 501 (CIRCUIT)
;
; Call:
;	NFEID/ node name
;	T2/ circuit ID
;
SET501:	SAVEAC <T3,T4>		;Must be preserved
	LOAD T1,NFEID,(P1)	;Get loopback node name
	CALL SCTANL		;Add loopback node name
	 RNMXER (NF.NRM)	; -error
	RNMXOK			;Return success

;CLR501 - clear parameter 501 (CIRCUIT)
;
; Call:
;	NFEID/ node name
;	T2/ zero
;
CLR501:	SAVEAC <T3,T4>
	LOAD T1,NFEID,(P1)	;Get loopback node name to clear
	XCALL (MSEC1,SCTN2L)	;Convert it to loopback circuit
	 RNMXND			; -not there, return 'no data'
	MOVE T2,T1		;Get circuit ID into T2
	SETZ T1,		; and clear loopback node name
	CALL SCTANL		;Clear loopback node name
	 RET			; -pass on error code
	RNMXOK			;Return success

;RED501 - read parameter 501 (CIRCUIT)
;
; Call:
;	NFEID/ loopback node name
;
RED501:	SAVEAC <T3,T4>		;Must be preserved
	LOAD T1,NFEID,(P1)	;Get node name
	XCALL (MSEC1,SCTN2L)	;Convert to circuit
	 RNMXER (NF.NRM)	; -error
	MOVE T2,T1		;Result to T2
	RNMXOK			; and return success

;SET502 - set node parameter 502 (ADDRESS)
;
; Call:
;	NFEID/ node name
;	T2/ node address on SET
;
SET502:	SAVEAC <T3,T4>
	LOAD T1,NFEID,(P1)	;Get node name
	CALL SCTAND		;Add node name
	 RNMXER ()		; Pass on error code
	RNMXOK			;Return success

;CLR502 - clear node parameter 502 (ADDRESS)
;
; Call:
;	NFEID/ node name
;	T2/ 0
;
CLR502:	SAVEAC <T3,T4>		;Must be preserved
	LOAD T1,NFEID,(P1)	;Get node name
	XCALL (MSEC1,SCTN2A)	;Map it to a node address
	IFSKP.			; -ok,
	  MOVE T2,T1		;  Move node address to T2
	  SETZ T1,		;  Clear node name to indicate clear
	  CALL SCTAND		;  Clear
	   RNMXER ()		;   Pass on error code
	ENDIF.
	RNMXOK			;Return success
	SUBTTL Network management -- Return list of entity ids

;NMXRET - return list of entity IDs
;
; SCLINK maintains two entity types: KNOWN NODES and LOOP NODES.
; The KNOWN nodes fall into two categories: home area and not home
; area. The arguments to NMXRET are different in each case.
;
; Common parameters are:
;	P1/ address of NF block
;	NFSEL/ .NTKNO or .NTLOP
;	NFEID/ if NFSEL is .NTKNO then NFEID has area #

NMXRET:	LOADE T1,NFSEL,(P1)	;Get selector
	CAXN T1,.NTLOP		;Is it LOOP nodes?
	JRST RETLOP		; -yes, go return LOOP nodes
	CAXE T1,.NTKNO		;then it should be KNOWN nodes
	RNMXER (NF.MPE)		; -no, management program error
	LOAD T1,NFEID,(P1)	;Get area number
	CAME T1,RTRHOM		;Is it local area
	JRST RETOAR		; -no, return other area
;	JRST RETHAR		;-yes, return home area

;RETHAR - return home area
;
; SCLINK is handed a buffer that is indexed by local node number
; The local node number is filled in if the node is KNOWN, i.e. in
; session controls node database.
;
; Call:
;	NFBFF/ set
;	NFUBF/ clear
;	NFBUF/ buffer address
;	NFBLN/ buffer length
;
; Return:
;	RET/	error occurred
;	RETSKP/ succeeded

RETHAR:

;Do a couple of sanity checks
	LOAD T1,NFBFF,(P1)	;Get buffer flag
	LOAD T2,NFUBF,(P1)	; and user flag
	SKIPE T1		;Verify buffer flag is set
	SKIPE T2		; and user buffer flag is clear
	RNMXER (NF.MPE)		;  otherwise "management program error"

;P2 will contain buffer address
	LOAD P2,NFBUF,(P1)

;Loop from node 1 to node NFBLN, checking ADRTAB along the way
	JSP CX,LOKNOD		;Lock database
	LOAD T1,NFBLN,(P1)	;T1 has node index
	LOAD T2,IBMXA,+IBBLK	;Get the Maximum Nodes in Area
	CAMLE T1,T2		;Is Max Area .LE. Buffer Length??
	 MOVE T1,T2		;If so use Max area as index
	DO.			;LOOP
	  MOVE T2,ADRTAB	;  Get address of address table
	  ADD T2,T1		;   and add in current local node index
	  SKIPN T3,(T2)		;  Is any pointer there?
	  IFSKP.		;   -yes, that node is KNOWN
	    LOAD T3,NOADR,(T3)	;    Get node address (now 16-bit format)
	    MOVE T4,T1		;    Get local node index
	    ADD T4,P2		;     and add destination buffer address
	    MOVEM T3,(T4)	;      and store the node address
	  ENDIF.
	  SOJG T1,TOP.		;  Loop back if more nodes to do
	ENDDO.			; -no, all nodes done, return successfully
	RETSKP

;RETOAR - return list of KNOWN nodes in areas other than home area
;
; RETOAR will write directly to a user buffer. Each node within the given
; area should be written in NICE format, i.e. 16-bit node address in two
; bytes followed by a zero byte to indicate no node name following.
;
; Since the node name/number database is organized in a way that makes
; the search for a single node number outside home area very expensive,
; another measure is taken to speed up the processing.
;
; RETOAR will process the entire area in one pass through the database,
; setting a bit in a temporary table if a specific node is there.
;
; Call:
;	NFEID/ area #
;	NFUBF/ set
;	NFBPT/ user byte pointer
;	NFBLN/ # of user bytes
;
; Return:
;	RET	Some error occurred
;	RETSKP	Success with
;			NFBLN/ # of bytes written

RETOAR:	SAVEAC <T5,T6>		;Need these two also

;Verify user buffer bit is set
	TMNN NFUBF,(P1)		;Is it set?
	RNMXER (NF.MPE)		; -no, "management program error"

;Allocate a temporary buffer to store the bit mask of KNOWN nodes in
	MOVX T1,<<RN%NOD+^D36>/^D36> ;Room for all nodes in an area
	CALL DNGWZP		;Get zeroed space
	 RNMXER (NF.RES)	; "resource failure"
	MOVE P2,T1		;Keep bit mask address in P2

;Lock node database so it doesnt change under our feet
	S1XCT <LOCK NODLOK>

;Now go through the entire node name/number database, and check for all nodes
; in the specified area
	SETZ T6,		;T6 is hash table index
	DO.			;LOOP over hash table
	  CAIL T6,SCNHSZ	;  Done all of table?
	  EXIT.			;  -yes, pass 1 done
	  MOVE T1,T6		;  Hash index to T1
	  ADD T1,HSHTAB		;   and make hash table address
	  SKIPN T1,(T1)		;  Is there a bucket chain?
	  AOJA T6,TOP.		;   -no, go to next hash index
	  DO.			;  LOOP over hash bucket chain
	    XMOVEI T2,BU.NO1(T1) ;   Pointer to first NO block in this bucket
	    MOVX T3,NRNOPB	;     and # of NO blocks per bucket
	    DO.			;    LOOP over bucket
	      LOAD T4,NOADR,(T2) ;     Get node address
	      LDAREA T5,T4	;       and extract area number
	      OPSTR <CAME T5,>,NFEID,(P1) ;Do areas match?
	      IFSKP.		;          -yes, found a KNOWN node
	        LDNODE T5,T4	;           Get the local node index
		CALL INSNOD	;            and set the corresponding bit
	      ENDIF.
	      ADDI T2,NO.LEN	;      Go to next NO block
	      SOJG T3,TOP.	;       -if there is any!
	    ENDDO.
	    OPSTR <SKIPE T1,>,BUNXT,(T1) ;Move to next bucket
	    LOOP.		;         and loop back
	  ENDDO.
	  AOJA T6,TOP.		;  and go to next hash index
	ENDDO.

;Come here after 1st pass.
; The node database can now be unlocked
	UNLOCK NODLOK

;The bit table has now been set up.
;
;Loop over all entries in the bit table and write corresponding node number
; to the user buffer
	LOAD T1,IBMXA,+IBBLK	;Get maximum # of nodes in an area
	ADDI T1,^D35		;One bit
	IDIVI T1,^D36		; per node
	MOVE T6,T1		;  gives word count in T6
	SETZ T1,		;Begin at word 0
	DO.			;LOOP over all words
	  MOVE T2,T1		;  Move index to T2
	  ADD T2,P2		;   and make bit table address
	  MOVE T3,(T2)		;  Get contents of that word
	  JFFO T3,RETOA1	;  Find first bit set
	;Come here if no bit was set, go to next word
	  AOJ T1,		;  Increment index
	  CAMGE T1,T6		;  Done all?
	  LOOP.			;   -no, loop back
	  EXIT.			;  -yes, exit loop

RETOA1:	;Come here if some bit was set, bit # in T4
	  TDZ T3,BITS(T4)	;  Clear bit in word
	  MOVEM T3,(T2)		;   and write it back to bit mask
	  MOVE T3,T1		;  Get index
	  IMULI T3,^D36		;   and make into
	  ADD T3,T4		;    node number
	  CALL WRTNOD		;     in order to write it to user space
	  IFNSK.
	   MOVE T1,P2		;Get the buffer address
	   CALL DNFWDS		;Return space
	   RNMXER (NF.RES)	;And return a "Resource Error"
	  ENDIF.
	  LOOP.			;  Loop back with old T1 to take care of
				;   next set bit in word, if any.
	ENDDO.

;At this point, release the temporary buffer and return successfully
	MOVE T1,P2		;Get buffer address
	CALL DNFWDS		;Return space
	RETSKP			;Done successfully

;INSNOD - insert a node in bit table
;
;	T5/ local node index
;	P2/ bit mask address
;
;No ACs may be touched

INSNOD:	SAVEAC <T1,T2>
	MOVE T1,T5		;Local node index to T1
	IDIVI T1,^D36		;Word index to T1, bit number to T2
	ADD T1,P2		;Make buffer word address
	MOVE T2,BITS(T2)	;Get bit mask for node index
	IORM T2,(T1)		; and OR in the node
	RET			;Done

;WRTNOD - write a node to user buffer
;
;	T3/ local node index
;	P1/ NF block
;	CALL WRTNOD
;	+1 return if no more user space available
;	+2 return if all is OK
;
;No ACs may be touched

WRTNOD:	SAVEAC <T1,T2>
	LOAD T1,NFBLN,(P1)	;Is there room
	SUBI T1,3		; for 3 more bytes
	SKIPGE T1		;  ?
	RET			;   -no, return +1 to indicate that
	STOR T1,NFBLN,(P1)	;Store new updated count
	LOAD T1,NFEID,(P1)	;Get area number
	STAREA T1,T3		; and put it into node number

	TOSWAPCD		;Transfer into section 1 to do PXCT
	LOAD T1,NFBPT,(P1)	;Get user byte pointer
	LDB T2,[POINT 8,T3,35]	;Get 1st byte
	XCTBU [IDPB T2,T1]	;Give it to him
	LDB T2,[POINT 8,T3,27]	;Get 2nd byte
	XCTBU [IDPB T2,T1]	;Give it to him
	SETZ T2,		;And a zero length of node name
	XCTBU [IDPB T2,T1]	;Give it to him
	TOXSWAPCD		;Exit section 1 after PXCT

	STOR T1,NFBPT,(P1)	;Save the updated Byte Pointer
	RETSKP			;All done


;RETLOP - return list of LOOP nodes
;
; Call:
;	NFBFF/ set
;	NFUBF/ clear
;	NFBUF/ buffer address
;	NFBLN/ # of words in buffer
;
; Return:
;	RET	Some error occurred
;	RETSKP	Succes with
;			NFBLN/ # of words written
;			buffer contains sixbit loop node names

RETLOP:	
	JSP CX,LOKNOD		;Lock node database

;Sanity checks
	LOAD T1,NFBFF,(P1)	;Get buffer flag
	LOAD T2,NFUBF,(P1)	; and user buffer flag
	SKIPE T1		;Verify buffer flag is set
	SKIPE T2		; and user buffer flag is clear
	RNMXER (NF.MPE)		;  otherwise "management program error"

;Step through list of loopback nodes
	LOAD P2,NFBUF,(P1)	;Buffer address to P2
	SETZ T1,		;No loop nodes found yet
	MOVE T2,SCTLNL		;Get pointer to head of loopback nodes
	DO.			;LOOP over chain
	  SKIPN T2		;  Is there any more LOOP nodes?
	  EXIT.			;  -no, done
	  LOAD T3,LNNAM,(T2)	;  -yes, get loopback node name
	  MOVE T4,T1		;   Get count word of nodes found
	  ADD T4,P2		;    and add in destination address
	  MOVEM T3,(T4)		;   Store the loopback name
	  AOJ T1,		;   One more loopback node found
	  LOAD T2,LNNXT,(T2)	;   Go to next loopback node
	  LOOP.			;  and loop back
	ENDDO.
;Come here when there are no more loopback nodes
	STOR T1,NFBLN,(P1)	;Store # of loopback nodes returned
	RETSKP			; and return successfully
	SUBTTL Network management -- Map node address to node name

;NMXA2N - convert from node address to node name
;
; Call:
;	T1/ function code
;	P1/ NF block
;	NFEID/ Entity ID (node address in 16-bit format)
;	NFETY/ .NTNOD
;	NFBUF/ zero
;
; Return:
;	RET	Network management error code in T1
;	RETSKP	NFBUF/ Sixbit node name
	SUBTTL Network management -- Map node name to node address

;NMXN2A - convert node name to node number
;
; Call:
;	T1/ function code
;	P1/ NF block
;	NFEID/ entity ID (sixbit node name)
;	NFETY/ .NTNOD
;	NFBUF/ zero
;
; Return:
;	RET	Network management error code in T1
;	RETSKP	NFBUF/ 16-bit node address

NMXN2A:	
NMXA2N:	LOAD T2,NFETY,(P1)	;Get entity type
	LOAD T3,NFBUF,(P1)	; and buffer word
	CAIN T2,.NTNOD		;Verify that it is a node
	SKIPE T3		; and that buffer word is zero
	RNMXER (NF.MPE)		;  "Management program error"
	CAIE T1,NF.A2N		;Is it address to name?
	IFSKP.			; -yes
	  LOAD T1,NFEID,(P1)	;Get 16-bit node address
	  XCALL (MSEC1,SCTA2N)	;Convert it
	    RNMXER (NF.URC)	;  "Unrecognized component"
	ELSE.			; -no
	  LOAD T1,NFEID,(P1)	;Get sixbit node name
	  XCALL (MSEC1,SCTN2A)	;Convert it
	    RNMXER (NF.URC)	;  "Unrecognized component"
	ENDIF.
	STOR T1,NFBUF,(P1)	;Store node name/address
	RETSKP
	SUBTTL Network management -- Check loopback node name

;NMXCKL - Check loopback node name
;
; Call:
;	P1/ NF block address
;	NFETY/ .NTNOD
;	NFEID/ sixbit node name
;
; Return:
;	RET	Node is not a loopback node
;	RETSKP	Node is a loopback node

NMXCKL:	LOAD T1,NFETY,(P1)	;Get entity type
	CAIE T1,.NTNOD		;Verify that it is a node
	RNMXER (NF.MPE)		; otherwise "management program error"
	LOAD T1,NFEID,(P1)	;Get entity ID
	CALL SCTCKL		;Check if it is a loopback node
	RETSKP			; SCTCKL returns "wrong way" (to be fixed)
	RNMXER (NF.URC)		;  -no, return "unrecognized component"

	XRESCD
	SUBTTL Network management -- Event -- Invalid message

;SCEIVM - Report Invalid Message Event
;
; Call:
;	MB/ Pointer to Message Block (which will be freed)
;
; Return:
;	RET			;ALWAYS (WITH AN ERROR CODE IN T1)
;
; Uses: T1-T6

SCEIVM:	EVENT(NSP,MSG,Invalid Msg in SCTL) ;BAD MESSAGE EVENT TYPE
	CALL FREMSG		;MUSTN'T FREE UNTIL AFTER EVENT MACRO
	SCERR %NEIMF,RTN,<Illegal message format detected>

;T1 set up with error code by SCERR macro, which also returns via RTN
	SUBTTL Network management -- Event -- CSSE event

;LEVT.0 - parameter 0 for CSSE event

	XRESCD
LEVT.0:

;Fall through to LEP.0
;	CALLRET LEP.0		;Parameter 0 (all data)

;LEP.0 - LCG event paramater 0
;	CM-6
;	DU-2	destination object type
;	DU-2	destination node address
;	DU-2	packets sent
;	DU-2	packets received
;	DU-2	bytes sent
;	DU-2	bytes received

LEP.0:	SETZ T1,		;Parameter #0
	CALL EVP2BT		; and output
	MOVEI T1,306		;CM-6
	CALL EVPBYT
	MOVEI T1,002		;DU-2
	CALL EVPBYT		; and output
	LOAD T1,SLDOB,(SL)	;Get destination object #
	CALL EVP2BT		; and output
	MOVEI T1,002		;DU-2
	CALL EVPBYT		; and output
	LOAD T1,SLDNA,(SL)	;Get destination node address
	CALL EVP2BT		; and output
	MOVEI T1,002		;DU-2
	CALL EVPBYT		; and output
	LOAD T1,SLPKS,(SL)	;Get # of packets sent
	CALL EVP2BT		; and output
	MOVEI T1,002		;DU-2
	CALL EVPBYT		; and output
	LOAD T1,SLPKR,(SL)	;Get # of packets received
	CALL EVP2BT		; and output
	MOVEI T1,002		;DU-2
	CALL EVPBYT		; and output
	LOAD T1,SLBYS,(SL)	;Get # of bytes sent
	CALL EVP2BT		; and output
	MOVEI T1,002		;DU-2
	CALL EVPBYT		; and output
	LOAD T1,SLBYR,(SL)	;Get # of bytes received
	CALLRET EVP2BT		; and output and return
	SUBTTL Local SCTL Storage

	RESDT			;IMPURE STORAGE (non-zeroed on boot)

IFN FTOPS20,<			;TOPS10 HAS THESE IN COMMON
;Schedular level routines do "SKIPL SCTLOK/RET" before proceeding
SCTLOK:	EXP -1			;SCTL INTERLOCK, USED BY SCTLCQ & SCTLCW
SCTLKO:	EXP 0			;INTERLOCK OWNER
>
SCTJFF:	BLOCK 1			;NON-ZERO: SCTULK SHOULD CALL SECOND SERVICE
SCTUCF:	BLOCK 1			;NON-ZERO: SCTULK SHOULD CALL SCIUCG

SCTCTA:	BLOCK 1			;GLOBAL COUNT OF CI TIMERS ACTIVE
SCTSJQ:	BLOCK QH.LEN		;QUEUE OF ALL SJBS

IFN FTOPS10 <

SCTMXN::%RTMXN*2		;MAXIMUM NODE NUMBER ALLOWED
SCTNDT::BLOCK 1			;POINTER TO NODE NAME TABLE
SCTNNT::BLOCK 1			;POINTER TO NODE ADDRESS TABLE
SCTNDC::BLOCK 1			;COUNT OF NODE NAMES DEFINED

>

	SWAPVR
HSHTAB:	BLOCK 1			;30-bit address of node name hash table
ADRTAB:	BLOCK 1			;30-bit address of node number table
QHBUCK:	BLOCK 1			;30-bit address of first free bucket
SCTNDC:	BLOCK 1			;# of local nodes known by SCLINK (for JNTMAN)
	RESDT

NODLOK:	BLOCK 1			;Lock word for node database

SCTLNL:	BLOCK 1			;LOOPBACK NODE LIST
SCTP2Q:	%SCP2Q			;PHASE II QUOTA
SCTINT:	%SCINT			;INCOMING TIMER EXPIRATION VALUE
SCTOTT:	%SCOTT			;OUTGOING TIMER EXPIRATION VALUE
SCTJFQ:	BLOCK QH.LEN		;QUEUE OF SLBS NEEDING SECOND SERVICE
SCTPRQ:	BLOCK QH.LEN		;SCTL TRANSACTION QUEUE HEADER

STRCNT:	BLOCK 1			;RECURSION COUNT FOR PATTERN MATCHER

SCTASQ:	BLOCK QH.LEN		;ALL-SLBs QUEUE HEADER

	XRESCD			;BACK TO PURE STORAGE
	SUBTTL End of SCLINK

IFN FTOPS10, .XCMSY             ;XCREF THE MACSYM SYMBOLS
IFN FTOPS20, TNXEND		;TOPS-20 ENDING

	END