Google
 

Trailing-Edge - PDP-10 Archives - BB-H311D-RM - monitor-sources/scsjsy.mac
There are 20 other files named scsjsy.mac in the archive. Click here to see a list.
; UPD ID= 5057, SNARK:<6.MONITOR>SCSJSY.MAC.84,   6-Nov-84 14:38:27 by HAUDEL
;Change SCSDEQ code to RETSKP and RET so that it will not do a RET on
;both a good return and an error return.
;
; UPD ID= 5055, SNARK:<6.MONITOR>SCSJSY.MAC.83,   6-Nov-84 09:42:03 by HAUDEL
;Changes to SCSQUE and SCSPSI so that blocks are now doubly linked and
;none are lost when SCSPSI removes them from the list. Also, a couple of
;changes to previous edit and error returns.
;
; UPD ID= 5053, SNARK:<6.MONITOR>SCSJSY.MAC.82,   5-Nov-84 08:10:30 by HAUDEL
;Many changes - Add STKVAR entries in SCSLIS so that they match those of 
;SCSCON; add OKSKED to error return in SCSRMG; add OKINT to error return in 
;SCSCRD; add ENDSV. in UMPERR; change ENDSV. to ENDTV. in ONTBLD; add JFCL 0
;in GDEERR after call to SCSLFQ.
;
; UPD ID= 5048, SNARK:<6.MONITOR>SCSJSY.MAC.81,  31-Oct-84 16:09:01 by HAUDEL
;Change SCSCSP code to handle a null byte pointer; do not return data
;if the byte pointer is zero.

; UPD ID= 5045, SNARK:<6.MONITOR>SCSJSY.MAC.80,  31-Oct-84 15:23:18 by HAUDEL
;Add code to SCSCSP to return the destination node number for the .SSCSP
;SCS% function.

; UPD ID= 5044, SNARK:<6.MONITOR>SCSJSY.MAC.79,  31-Oct-84 10:48:20 by HAUDEL
;Delete a NOSKED in RXNMOR code, one was done before a JRST to this code.

; UPD ID= 5029, SNARK:<6.MONITOR>SCSJSY.MAC.78,  29-Oct-84 08:11:54 by GRANT
;In SCSRCD, minor fixes for destination hardware version
; UPD ID= 4999, SNARK:<6.MONITOR>SCSJSY.MAC.77,  24-Oct-84 20:05:40 by GRANT
;In SCSRCD, return the remote node name and all the words of hardware version
; UPD ID= 4981, SNARK:<6.MONITOR>SCSJSY.MAC.76,  23-Oct-84 14:46:24 by HAUDEL
;Fix code in SCSLXN so that DMA buffer names are added to the fork's linked 
;list.
;
; UPD ID= 4941, SNARK:<6.MONITOR>SCSJSY.MAC.75,  16-Oct-84 09:31:22 by HAUDEL
;Correct problems with the handling of SCS's BSDs - when linking a new
;user virtual address into a BSD, checked for chained BSDs; update pointers
;in the BSD when removing a user virtual address.
;
; UPD ID= 4834, SNARK:<6.MONITOR>SCSJSY.MAC.74,  17-Sep-84 11:22:43 by PURRETTA
;Update copyright notice
;
; UPD ID= 4790, SNARK:<6.MONITOR>SCSJSY.MAC.73,   5-Sep-84 15:49:56 by HAUDEL
;Change some comments. Change the SCSMAP code to use bytes for high density
;and add 2 entries to the STKVAR. Change a call of DATADR to DATRET in the
;ACCERR code(this is more of UPD ID 4690 that prevent ILLUUOs).
;
; UPD ID= 4746, SNARK:<6.MONITOR>SCSJSY.MAC.72,  24-Aug-84 11:22:55 by CDUNN
;Make SCSCBD delete the connect block from the owning fork's CB list. Prevents
;block from being reaped before SCSJSY is done with it.
;
; UPD ID= 4724, SNARK:<6.MONITOR>SCSJSY.MAC.71,  21-Aug-84 14:57:43 by CDUNN
;Fix typo in UPD ID= 4593. Make SCSDEQ referance .MEANC and .MEAPC rather than
;.MEAPC twice. Also fix SCSKIL (KILCHK) to update FLINK and BLINK pointers 
;after emptying the fork CB queue.
;
; UPD ID= 4690, SNARK:<6.MONITOR>SCSJSY.MAC.70,  14-Aug-84 16:22:06 by CDUNN
;Move CID check before data check in SCSACC. Prevents ILLUUO. Also force 
;minimum send credit for JSYS connection to be 5 (temporary fix to make
;diagnostics to HSC work, real solution is to allow user to specify min
;send credit).
;
; UPD ID= 4673, SNARK:<6.MONITOR>SCSJSY.MAC.69,  10-Aug-84 17:14:12 by CDUNN
;Add function .SSRPS (Return path status information)
;
; UPD ID= 4659, SNARK:<6.MONITOR>SCSJSY.MAC.68,   7-Aug-84 16:17:21 by CDUNN
;Do correct cleanup of DMA buffers on connection delete.
;
; UPD ID= 4651, SNARK:<6.MONITOR>SCSJSY.MAC.67,   7-Aug-84 10:26:02 by CDUNN
;Fix handling of .SSPBC callback since AC usage has changed.
;
; UPD ID= 4593, SNARK:<6.MONITOR>SCSJSY.MAC.66,  26-Jul-84 22:26:18 by CDUNN
;Allow forks without connect blocks to get online/offline interrupts. Also
;update the table of contents.
;
; UPD ID= 4501, SNARK:<6.MONITOR>SCSJSY.MAC.65,  12-Jul-84 19:56:15 by LOMARTIRE
;Rework of callback handling.  Replace .SSCBD with .SSDDG.
;Code for AC change in .SSDGR/.SSMGR callbacks.
;Remove .SSMNT and .SSNWO callback handlers.
;Fix SBLIST searching in SINNCO since SBLIST is now sparse.
;Remove EXTERN references to SC.SMD and SC.RMD
;
; UPD ID= 4456, SNARK:<6.MONITOR>SCSJSY.MAC.64,  12-Jul-84 10:46:29 by CDUNN
;Fix SCSKIL to not smash any ACs so it can be called easily from KSELF and 
;FLOGO. Also add required NOINTs to SCAMPI calls.
;
; UPD ID= 4428, SNARK:<6.MONITOR>SCSJSY.MAC.63,   3-Jul-84 14:29:28 by LOMARTIRE
;Remove reference to CBACSY and replace with .CBADR
;Replace references to CBFLG with .CBFLG
;
; UPD ID= 4409, SNARK:<6.MONITOR>SCSJSY.MAC.62,  29-Jun-84 11:29:03 by CDUNN
;Set correct flag bits in CB on end of connection events. Also remove 
;referances to CBFABT. Also fix CB delete on active side of disconnect.
;
; UPD ID= 4296, SNARK:<6.MONITOR>SCSJSY.MAC.61,   4-Jun-84 14:29:11 by HAUDEL
;Change SCSMAP to use new form of SC.MAP
;
; UPD ID= 4284, SNARK:<6.MONITOR>SCSJSY.MAC.60,   1-Jun-84 12:21:26 by LOMARTIRE
;Remove SC.SBI references and use new CBDNOD instead of old CBDSBI.
;
; UPD ID= 4258, SNARK:<6.MONITOR>SCSJSY.MAC.59,  30-May-84 14:18:48 by CDUNN
;TCO 6.2073 - Fix SCSDEQ to correctly delete the first entry from a queue
;with more than one entry.
;
; UPD ID= 4199, SNARK:<6.MONITOR>SCSJSY.MAC.58,   9-May-84 13:19:09 by LOMARTIRE
;Remove reference to SC.ABT.
;
; UPD ID= 4190, SNARK:<6.MONITOR>SCSJSY.MAC.57,   9-May-84 00:34:06 by CDUNN
;Fix referances to CBSBI and .CBSBI to use correct LOAD/STOR name... 
;
; UPD ID= 4169, SNARK:<6.MONITOR>SCSJSY.MAC.56,   2-May-84 03:11:58 by CDUNN
;Change SCSRMG/SCSRDG to zero leftover. bytes in an industry compatable packet.
;
; UPD ID= 4159, SNARK:<6.MONITOR>SCSJSY.MAC.55,  30-Apr-84 23:46:21 by CDUNN
;Fix the moving of process names from user to monitor pad the name with spaces.
;
; UPD ID= 4110, SNARK:<6.MONITOR>SCSJSY.MAC.54,  23-Apr-84 18:36:36 by CDUNN
;Fix SCSRMG/SCSRDG to correctly convert from bytes to words. Also report 
;correct byte size to user. Also fix SCSEVT to correctly account for the
;size of the overhead area in event blocks when moving data.
;
; UPD ID= 4052, SNARK:<6.MONITOR>SCSJSY.MAC.53,   5-Apr-84 15:00:51 by CDUNN
;General work on interrupt routines. Merge SINMGR and SINDGR. Also fix
;update of datagram and message counts.
;
; UPD ID= 4045, SNARK:<6.MONITOR>SCSJSY.MAC.52,   4-Apr-84 10:45:40 by CDUNN
;Remove bad edit history entires added by last edit.
;
; UPD ID= 4044, SNARK:<6.MONITOR>SCSJSY.MAC.51,   4-Apr-84 10:38:02 by CDUNN
;Merge SCSRDG and SCSRMG into one routine with two entry points. Fixes to 
;SCSKIL and SCSQRD/SCSQRM. Also get SCSEVT to use SCSDMU rather than BLTMU.
;Also disallow use of previous delete bits. Code still needs to be removed,
;only bit check was removed.
;
; UPD ID= 3990, SNARK:<6.MONITOR>SCSJSY.MAC.50,  28-Mar-84 01:06:58 by CDUNN
;Fix PSI channel check in SCSAIC. Add functions .SSGLN and .SSRBS. Fix SCSEVT
;to use ENDSV and not ENDTV.
;
; UPD ID= 3953, SNARK:<6.MONITOR>SCSJSY.MAC.49,  21-Mar-84 12:44:24 by LOMARTIRE
;Remove old PI ring buffer.  This has been replaced by RG.PIT jacket routine.
;Also remove PIDBG conditional.
;
; UPD ID= 3875, SNARK:<6.MONITOR>SCSJSY.MAC.48,   7-Mar-84 19:05:38 by CDUNN
;Many, many cosmetic changes. Remove leading spaces on some instructions,
;remove SAVEAC's from top level JSYS code. Change TRVARs to STKVARs. Also
;insure calls to SCA have NOINT's as they are needed.
;
; UPD ID= 3778, SNARK:<6.MONITOR>SCSJSY.MAC.47,  28-Feb-84 13:08:23 by CDUNN
;Fix missed test in previous edit.
;
; UPD ID= 3769, SNARK:<6.MONITOR>SCSJSY.MAC.46,  27-Feb-84 11:12:49 by CDUNN
;Make SCSSMG and SCSSDG the same routine with two entry points.
;
; UPD ID= 3753, SNARK:<6.MONITOR>SCSJSY.MAC.45,  25-Feb-84 07:59:20 by HALL
;SCAMPI's names changed for maintenance functions.
;
; UPD ID= 3716, SNARK:<6.MONITOR>SCSJSY.MAC.44,  21-Feb-84 13:03:33 by CDUNN
;More TCO 6.1127 - Convert SCSCON,SCSLIS, and SCSACC into a batch of 
;subroutines
;
; UPD ID= 3573, SNARK:<6.MONITOR>SCSJSY.MAC.43,  28-Jan-84 01:41:23 by MCLEAN
;EDITS TO REMOVE P1 FROM CONNECT BLOCK DEFSTRS
;
; UPD ID= 3560, SNARK:<6.MONITOR>SCSJSY.MAC.42,  26-Jan-84 14:38:16 by MCLEAN
;ADD NEW PARAMETER TO SC.MAP CALL (ALWAYS 1 FOR NOW)
;
; UPD ID= 3521, SNARK:<6.MONITOR>SCSJSY.MAC.41,  24-Jan-84 12:44:34 by HALL
;Create SCSRET for returning a buffer to the correct pool
;In SINPSC call SCSRET on both success and failure
;In SCSSDG, set JH%DGB in .JHFLG word (it was being set only in the AC)
;
; UPD ID= 3518, SNARK:<6.MONITOR>SCSJSY.MAC.40,  23-Jan-84 23:49:58 by MCLEAN
;FIX TYPO
;
; UPD ID= 3517, SNARK:<6.MONITOR>SCSJSY.MAC.39,  23-Jan-84 23:06:13 by MCLEAN
;FIX PLACES WHERE SC.RLD AND SC.RBF CALLS ARE WRONG
;ALSO MANY OTHER RANDOM FIXES
;
; UPD ID= 3508, SNARK:<6.MONITOR>SCSJSY.MAC.38,  23-Jan-84 08:49:32 by LOMARTIRE
;More TCO 6.1127 - Use correct AC to store receive credit at SINCIA 
;
; UPD ID= 3493, SNARK:<6.MONITOR>SCSJSY.MAC.37,  20-Jan-84 11:12:30 by CDUNN
;More TCO 6.1127 - Fix SCSGDE to user correct interlock in error path (GDEFRK).
;Fix wrong kind of interlock used in error recovery in a number of routines.
;Also update call to SC.LIS to new calling sequence.
;
;UPD ID= 3473, SNARK:<6.MONITOR>SCSJSY.MAC.36,  17-Jan-84 15:36:37 by LOMARTIRE
;More TCO 6.1127 - Place count of datagram buffers in .CBDGJ not .CBDGR
;
;UPD ID= 3470, SNARK:<6.MONITOR>SCSJSY.MAC.35,  16-Jan-84 15:23:42 by LOMARTIRE
;More TCO 6.1127 - Use correct AC when getting next BSD at GUBTNB
;
; UPD ID= 3303, SNARK:<6.MONITOR>SCSJSY.MAC.34,  13-Dec-83 13:17:01 by CDUNN
;More TCO 6.1127 - Fix random bugs in SCSMAP and DMA support in general
;
; UPD ID= 3256, SNARK:<6.MONITOR>SCSJSY.MAC.33,   5-Dec-83 19:28:14 by CDUNN
;More TCO 6.1127 - Fix SCSRDG/SCSRMG to better test addresses handed to SCA.
;Force SCSLUB to zero the forward link of the last entry in the user buffer
;database. Add the code to support the maintainace data transfer functions.
;
; UPD ID= 3186, SNARK:<6.MONITOR>SCSJSY.MAC.32,  17-Nov-83 16:20:20 by CDUNN
;More TCO 6.1127 - Remove all code supporting the mapping if user data into
;monitor space for packet sends. Do the copy instead.
;
; UPD ID= 3138, SNARK:<6.MONITOR>SCSJSY.MAC.31,  11-Nov-83 10:47:49 by CDUNN
;More TCO 6.1127 - Fix confusion over interlocks in SCSRDG
;
; UPD ID= 2855, SNARK:<6.MONITOR>SCSJSY.MAC.30,  22-Aug-83 13:55:04 by CDUNN
;More TCO 6.1127 - Fix get a datagram (SCSRDG) to offset to user text before
;doing the BLT. Also some random fixes for bad AC usage in calculating packet
;lengths.
;
; UPD ID= 2825, SNARK:<6.MONITOR>SCSJSY.MAC.29,  11-Aug-83 15:33:36 by CDUNN
;More TCO 6.1127  Pass 2 on code cleanup. More CION/CIOFF fixes as well as
;general bug fixes.
;
; UPD ID= 2742, SNARK:<6.MONITOR>SCSJSY.MAC.28,  22-Jul-83 15:20:22 by CDUNN
;More TCO 6.1127 - Do all kinds of code cleanup and beefed up error recovery.
;Also remove all cases of CIOFF appearing in swapable code... Change CIOFF
;to be a routine accessable to the whole world
;
; UPD ID= 2610, SNARK:<6.MONITOR>SCSJSY.MAC.27,  20-Jun-83 17:09:50 by HALL
;TCO 6.1689 - Move fork tables to extended section
;	Reference FKPGS and FKINT via DEFSTR
;
; UPD ID= 2557, SNARK:<6.MONITOR>SCSJSY.MAC.26,   3-Jun-83 17:14:58 by CDUNN
;More TCO 6.1127 - Fix returns from JSYS interrupt routines...
;
; UPD ID= 2533, SNARK:<6.MONITOR>SCSJSY.MAC.25,  26-May-83 18:11:13 by CDUNN
;More TCO 6.1127 - Set abort in SCSPSI after DISCONNECT has been reported to
;fork.
;
; UPD ID= 2494, SNARK:<6.MONITOR>SCSJSY.MAC.24,  19-May-83 11:15:43 by MURPHY
;Put EPGMAP in extended section.
;
; UPD ID= 2479, SNARK:<6.MONITOR>SCSJSY.MAC.23,  17-May-83 13:16:54 by CDUNN
;More TCO 6.1127 - Fix referances to send datagram and send message argument
;block to use new symbol defs.
;
; UPD ID= 2462, SNARK:<6.MONITOR>SCSJSY.MAC.22,  12-May-83 17:14:50 by CDUNN
;More TCO 6.1127 - Dont try to poke the PSB of a deleted fork.
;
; UPD ID= 2454, SNARK:<6.MONITOR>SCSJSY.MAC.21,  10-May-83 16:48:17 by CDUNN
;More TCO 6.1127 - Change SINMGR and SINDGR to conform to new packer length
;scheme.
;
; UPD ID= 2323, SNARK:<6.MONITOR>SCSJSY.MAC.19,  22-Apr-83 01:40:33 by CDUNN
;More TCO 6.1127 - Add return of packet length to SCSRDG and SCSRMG. Support
;also in SINDGR and SINMGR.
;
; UPD ID= 2301, SNARK:<6.MONITOR>SCSJSY.MAC.18,  18-Apr-83 05:29:37 by GRANT
;TCO 6.1127 - In SCSRCD, add +1 return to CALL LOCPRT
;
; UPD ID= 2253, SNARK:<6.MONITOR>SCSJSY.MAC.17,  12-Apr-83 20:07:39 by CDUNN
;More TCO 6.1127 - Finish SCSMAP
;
; UPD ID= 2172, SNARK:<6.MONITOR>SCSJSY.MAC.16,   6-Apr-83 03:59:45 by CDUNN
;More TCO 6.1127 - Fix PSIBER to observe the entry type and use the correct
;space return routine.
;
; UPD ID= 2162, SNARK:<6.MONITOR>SCSJSY.MAC.15,   4-Apr-83 20:51:29 by CDUNN
;More TCO 6.1127 - Change default for PIDBG to be off...
;
; UPD ID= 2138, SNARK:<6.MONITOR>SCSJSY.MAC.14,   3-Apr-83 20:36:26 by CDUNN
;More TCO 6.1127 
;1. Add PIRNG to collect info about the CIOFF's and CION's we do.
;2. Add page offset to user address when sending essage or datagrams.
;3. Make the credit thresholds be zero on connect and listen
;4. Fix SCSCON to restore the user arg address before doing user buffer
;stuff...
;
; UPD ID= 2117, SNARK:<6.MONITOR>SCSJSY.MAC.13,  29-Mar-83 17:17:47 by CDUNN
;More TCO 6.1127 -  Create SCSCLK to queue interrupts for forks which have
;entries on the SCS work queue. Also remove call to PSIGR from SCSQUE. Make
;SCSQUE zero SCSTIM, and have SCSCLK set SCSTIM to infinite. Also replace
;PION/PIOFF with CION/CIOFF.
;
; UPD ID= 2070, SNARK:<6.MONITOR>SCSJSY.MAC.12,  23-Mar-83 18:57:30 by CDUNN
;More TCO 6.1127 - Make SCSQRD add count of new buffers to CB. Also add
;routine to support DMA functions.
;
; UPD ID= 2057, SNARK:<6.MONITOR>SCSJSY.MAC.11,  22-Mar-83 00:52:02 by CDUNN
;More TCO 6.1127 - Move certain routines into RESCD since they must go
;CIOFF and hence cannot cause page faults. Also fix SCSFUB to do the right
;things with the queue ends. Also fix SCSRMG to return the CID in question
;to the user.
;Make SCSPSI check validity of CID before linking entries
;onto fork queue. Check to see if block still around and if it has abort or
;reap bits set.
;
; UPD ID= 2014, SNARK:<6.MONITOR>SCSJSY.MAC.8,  17-Mar-83 03:13:35 by CDUNN
;More TCO 6.1127 - Correctly handle linking of dont care listeners onto
;fork CB queues.
;
; UPD ID= 1990, SNARK:<6.MONITOR>SCSJSY.MAC.7,  15-Mar-83 02:00:55 by CDUNN
;More TCO 6.1127 - Fix SCSDEQ to use correct address wen deleting last entry
;on a queue while not the target fork.
;Also remove $PION and $PIOFF, replace them with CION and PIOFF
;
; UPD ID= 1956, SNARK:<6.MONITOR>SCSJSY.MAC.6,  10-Mar-83 00:37:59 by CDUNN
;More TCO 6.1127 - TEMPORARILY! turn on RS%SE0 in calls to ASGRES until
;all work is doen in FREE.
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY  BE  USED
;OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT  (C)  DIGITAL  EQUIPMENT  CORPORATION  1982, 1984.
;ALL RIGHTS RESERVED.


	SEARCH SCAPAR,PROLOG,PHYPAR

	TTITLE (SCSJSY,,<- The SCS% JSYS>)


; Note:
; Some abbreviations appear EVERYWHERE in this module. They are:
;
; CB - Connection block
; SB - System block
; SBI - System block index
; CID - Source connect ID
; PPD - Physical port driver
; WQ  - Working queue used by SCA to remember flow control messages
; MBZ - Must be zero
; BDT - Buffer descriptor table (xlation for JSYS buffers to monitor buffers)
;
;

	EXTERN SC.CSC,SC.ABF,SC.RBF,SBLIST,SC.CRM,CIDTAB
	EXTERN SCSTDQ,SCSBDQ,SCSTMQ,SCSBMQ,SCSTEQ,SCSBEQ,SCSTXQ,SCSBXQ
	EXTERN SCSTCQ,SCSBCQ,SCSPS0,SCSPS1,SCSTXN,SCSBXN
	EXTERN SC.ALD,SC.RLD,SC.RCB,SC.UMP,SC.MAP,SC.SND,SC.REQ
	EXTERN SETCPT,RELCPT,LOCPRT,SC.ACB,SC.LCB
	EXTERN PSITR1,PSIGR
	EXTERN FRKTAB,FRKTLN
	SUBTTL	Table of Contents


;		Table of Contents for SCSJSY
;
;
;			   Section			      Page
;   1. Init code. . . . . . . . . . . . . . . . . . . . . . .    4
;   2. Entry to JSYS. . . . . . . . . . . . . . . . . . . . .    5
;   3. Function code handlers
;        3.1.   Connect (SCSCON). . . . . . . . . . . . . . .    6
;        3.2.   Listen (SCSLIS) . . . . . . . . . . . . . . .    7
;        3.3.   Receive datagram (SCSRDG) . . . . . . . . . .    8
;        3.4.   Read port counters (SCSRPC) . . . . . . . . .    9
;        3.5.   Reject (SCSREJ) . . . . . . . . . . . . . . .   10
;        3.6.   Disconnect (SCSDIS) . . . . . . . . . . . . .   11
;        3.7.   Send message/datagram (SCSSMG/SCSSDG) . . . .   12
;        3.8.   Queue mess buffers (SCSQRM) . . . . . . . . .   13
;        3.9.   Cancel DG buffers (SCSCRD). . . . . . . . . .   14
;        3.10.  Connect state poll (SCSCSP) . . . . . . . . .   15
;        3.11.  Return connect data (SCSRCD). . . . . . . . .   16
;        3.12.  Status of connect (SCSSTS). . . . . . . . . .   17
;        3.13.  Map a buffer (SCSMAP) . . . . . . . . . . . .   18
;        3.14.  Unmap a buffer (SCSUMP) . . . . . . . . . . .   19
;        3.15.  Send/Request a buffer (SCSSND/SCSREQ) . . . .   20
;        3.16.  Maint. data send/rec (SCSMDS/SCSMDR). . . . .   21
;        3.17.  Start a remote system . . . . . . . . . . . .   22
;        3.18.  Reset a remote system . . . . . . . . . . . .   22
;        3.19.  Add interrupt channel (SCSAIC). . . . . . . .   23
;        3.20.  Accept connection (SCSACC). . . . . . . . . .   24
;        3.21.  Get data queue entry (SCSGDE) . . . . . . . .   25
;        3.22.  Get event Q entry (SCSEVT). . . . . . . . . .   26
;        3.23.  SCSGLN (Get local node number). . . . . . . .   27
;        3.24.  SCSRBS (Return buffer sizes). . . . . . . . .   28
;   4. SCS% error handlers. . . . . . . . . . . . . . . . . .   29
;        4.1.   SERIAA (Invalid argument block address) . . .   29
;        4.2.   SERIBP (Invalid byte pointer) . . . . . . . .   29
;   5. SCA call handlers
;        5.1.   Main entry point. . . . . . . . . . . . . . .   30
;        5.2.   SCSONT (Handle online/offline interrupts) . .   30
;        5.3.   SINDGR. . . . . . . . . . . . . . . . . . . .   31
;        5.4.   SINDMA. . . . . . . . . . . . . . . . . . . .   32
;        5.5.   SINMSC. . . . . . . . . . . . . . . . . . . .   33
;        5.6.   SINPBC. . . . . . . . . . . . . . . . . . . .   34
;        5.7.   SINCTL. . . . . . . . . . . . . . . . . . . .   35
;        5.8.   SINCRA. . . . . . . . . . . . . . . . . . . .   36
;        5.9.   SINOSD. . . . . . . . . . . . . . . . . . . .   37
;        5.10.  SINRID. . . . . . . . . . . . . . . . . . . .   38
;        5.11.  SINCIA. . . . . . . . . . . . . . . . . . . .   39
;        5.12.  SINPSC. . . . . . . . . . . . . . . . . . . .   40
;        5.13.  SINLCL. . . . . . . . . . . . . . . . . . . .   41
;        5.14.  SINNWO. . . . . . . . . . . . . . . . . . . .   42
;   6. Support routines
;        6.1.   SCSDMU (Data monitor to user) . . . . . . . .   43
;        6.2.   SCSRUB (Remove user buffer) . . . . . . . . .   44
;        6.3.   SCSSUM (Move string from user to monitor) . .   45
;        6.4.   SCSDUM (Move data from user to monitor) . . .   46
;        6.5.   SCSCUB (Count user buffers) . . . . . . . . .   47
;        6.6.   SCSXUB (Link buffer chain into database). . .   48
;        6.7.   SCSUNM (User name move) . . . . . . . . . . .   49
;        6.8.   SCSUDM (User connection data move). . . . . .   50
;        6.9.   SCSCBI (Connect block data init). . . . . . .   51
;        6.10.  SCSABT/SCSRBT (Add/remove bit from table) . .   52
;        6.11.  SCSGNB (Get next bit) . . . . . . . . . . . .   53
;        6.12.  SCSW2B (Word/bit offset to bit number conv) .   54
;        6.13.  SCSAFT (Add fork to table). . . . . . . . . .   55
;        6.14.  SCSRFT (Remove fork from table) . . . . . . .   56
;        6.15.  SCSGNF (Get next fork). . . . . . . . . . . .   57
;        6.16.  SCSQMD (Queue entry on maint. data Q) . . . .   58
;        6.17.  SCSDMD (Dequeue maint. data queue entry). . .   59
;        6.18.  SCSLFQ (Link packet to front of queue's). . .   60
;        6.19.  SCSAER (ASGRES error handler) . . . . . . . .   61
;        6.20.  SCSLCB (Link CB onto fork CB queue) . . . . .   62
;        6.21.  SCSKIL (Clean up SCS% data on a CLZFF). . . .   63
;        6.22.  SCSCBD (JSYS CB data delete). . . . . . . . .   64
;        6.23.  SCSGPR (General packet return). . . . . . . .   65
;        6.24.  SCSCLK (Clock service routine). . . . . . . .   66
;        6.25.  SCSPSI (Add entry to fork queue). . . . . . .   67
;        6.26.  SCSQUE (Queue a block for list queueing). . .   68
;        6.27.  SCSMBS (Make another BSD) . . . . . . . . . .   69
;        6.28.  SCSDEQ (Dequeue a buffer from fork+CB Q's). .   70
;        6.29.  SCSLUB (Link on user message buffer addr) . .   71
;        6.30.  SCSGUB (Get user buffer address). . . . . . .   72
;        6.31.  SCSRET (Return buffer to correct pool). . . .   73
;   7. Storage. . . . . . . . . . . . . . . . . . . . . . . .   74
	SUBTTL	Init code

	RESCD

; This routine initializes the SCS% JSYS SYSAP.
;
SCSINI::BLCAL. (SC.SOA,<<.,SCSONT>>) ;Tell SCA about this address
	 BUG. (CHK,SCSNOI,SCSJSY,SOFT,<SCA: SCS% cannot receive node online/offline interrupts>,,<

Cause:	SCA has told the JSYS SYSAP that there are to many SYSAPs and the JSYS
	is not allowed to see online/offline interrupts. The system can run
	but many diagnostics will get upset as will anything that uses the 
	JSYS.
>)
	RET			;All done...
	SUBTTL	Entry to JSYS

	SWAPCD

; SCS% JSYS
;                T1/ Function code
;                T2/ Address of argument block
;                SCS%
;                return +1: success,
;                  illegal instruction trap on failure
;
;
; What we do here is rather simple. Get the users function code from users
;T2. Range check the argument, and dispatch to the correct routine to handle
;that function code.
;
.SCS::	MCENT			;Enter monitor context
	MOVE T1,CAPENB		;Get the callers enabled capabilities
	TXNN T1,<SC%WHL!SC%MNT!SC%NWZ> ;Is he wheel,maintainance, or net wizard
	ITERR (SCSNEP)		;Nope, not enough privs
	UMOVE T1,T1		;Get the function code from user T1
	CAIL T1,.SSCON		;Lower than lowest???
	CAILE T1,.SSLST		;Higher than highest???
	ITERR (SCSBFC)		;Out of range, flunk with bad function code
	UMOVE T2,T2		;Get the address of the argument block
	CALL @FCNTAB(T1)	;Call the routine to handle the function code
	 ITERR ()		;Fail, passing along the error code
	MRETNG			;Return from JSYS country...
	SUBTTL Function code handlers -- Connect (SCSCON)

; This routine handles the connect function code.
;	Check all the args and then setup to do the SC.CON or SC.LIS call.
;Also store the fork number of the fork that opened this connection.
;
; Assumes:
;	T1/	Function code
;	T2/	User virtual address of arg block
;
; *** Waring ***
; The STKVAR at the top of this routine must have a set of common elements
;with the STKVAR atop SCSLIS. Error recovery routines are shared by these
;two routines and STKVAR locations are used. Hence these offsets must be
;kept in common.
;
SCSCON::SAVEAC <Q1>
	STKVAR <SPN,DPN,CID,ERRCOD,STRRET,DATRET,STRADR,DATA,MSGBUF,DGBUF>
	MOVE Q1,T2		;Setup perm AC for user addr of args
	SETZM DPN		;Zero the pointer to destination process name
	SETZM DATA		;Insure null pointer to connection data
	SETZM STRADR		;Zero pointer to string storage
	XCTU [HRRZ T3,.SQLEN(Q1)] ;Get the first word out of the arg block
	ERJMP SERIAA		;Bad address, call the error handler
	CAIGE T3,.LBCON		;Is the arg block long enough???
	ITERR (SCSBTS)		;Nope, cause an illegal instruction trap
	XCTU [HLRE T1,.SQSYS(Q1)] ;Get the SBI word from the users arg block
	ERJMP SERIAA		;Handle page fault badness
	SKIPL T1		;Is it negative???
	CAILE T1,C%SBLL-1	;  or greater than the largest SBI???
	ITERR (SCSISB)		;Bad SBI, fail here...
	SKIPN SBLIST(T1)	;Is there a system for this SBI???
	ITERR (SCSISB)		;Nope, fail pls...
	XCTU [SKIP .SQRCI(Q1)]	;Is the end of the block available too???
	ERJMP SERIAA		;Nope, cause an illegal instruction trap

; Move the source name, destination name, and user data into monitor space.

	NOINT			;SCSUNM allocated resouces, don't loose them
	CALL SCSUNM		;Get the source and destination names moved
	 ITERR (<>,<OKINT>)	;Failed, report error code
	MOVEM T1,SPN		;Save the pointer to the source name
	MOVEM T2,DPN		;  and the pointer to the destination name
	MOVEM T3,STRADR		;  and the base addr of the free space used
	MOVEM T4,STRRET		;  and the routine addr to return free space

CONDAT: UMOVE T1,.SQCDT(Q1)	;Get user pointer to connection data
	ERJMP LISER1		;Handle page faults
	SKIPN T1		;Is there any user data pointer???
	JRST CONMCN		;No,finished moving data
	CALL SCSUDM		;Move the user data into monitor space
	 JRST LISERR		;Handle failure please...
	MOVEM T1,DATA		;Save the address of the connect data
	MOVEM T2,DATRET		;  and the addr of the routine to return it

; Here to count the number of buffers desired by the user so we can queue 
;them on connect.

CONMCN:	SETZM MSGBUF		;Zero the message buffer count
	UMOVE T1,.SQAMC(Q1)	;Get user pointer to initial message buffers
	ERJMP LISER1		;Handle nastygram page faults
	JUMPE T1,CONDGC		;ANY MESSAGES? (IF ZERO GO TRY DATAGRAMS)
	MOVX T2,C%MGSZ		;YES. Get the size of the message buffers
	CALL SCSCUB		;Count number of buffer and check intergrity
	 JRST LISERR		;Handle a bad buffer chain
	MOVEM T3,MSGBUF		;Store number of buffers on the chain

CONDGC:	SETZM DGBUF		;Zero the datagram count
	UMOVE T1,.SQADC(Q1)	;Get user pointer to initial datagram buffers
	ERJMP LISER1		;Handle bad addresses
	SKIPN T1		;Are there any datagram buffers to count???
	JRST CONCAL		;No, do the connect call
	MOVX T2,C%DGSZ		;Get the size of the buffers
	CALL SCSCUB		;Count and check the buffer chain
	 JRST LISERR		;Handle a bad chain
	MOVEM T3,DGBUF		;Save the count of buffers

; Here when we are ready to do the connect call

CONCAL: UMOVE T2,.SQSYS(Q1)	;Get the system word from the block
	ERJMP LISER1		;Handle page fault badness
	HLRZM T2,T3		;Move the SBI to its own AC
	HRRZS T2		;Be sure the CID bits are in their own AC
	NOSKED			;Don't allow packet processing until after
				;  CB has been filled with JSYS data
	BLCAL. (SC.CON,<SPN,DPN,T3,[5],[0],<.,SCSINT>,T2,DATA,MSGBUF,DGBUF>)
	IFNSK.
	 OKSKED			;Allow other processes again
	 JRST LISERR		;  and handle call failure
	ENDIF.
	UMOVEM T1,.SQRCI(Q1)	;Return the connect ID
	ERJMP CONERR		;Handle write locked pages and friends
	MOVEM T1,CID		;Save the returned connect ID
	SKIPE T1,DATA		;Get the address of the connection data
	CALL @DATRET		;If there was any, return its space
	MOVE T1,STRADR		;Get the address of the string space
	CALL @STRRET		;Return the space the process names live in

; Here to store needed info about the fork and job in the CB

CONSTO:	$LDCID P1,CID		;Load the CB address
	CALL SCSCBI		;Init the connect block with JSYS data
	CALL SCSLCB		;Link this CB onto my fork CB queue
	OKSKED			;Allow other processes again

; Here to store data about the users queued buffers.

	SKIPN MSGBUF		;Are there any message buffer to handle???
	JRST CONDGB		;No, go try for datagrams
	UMOVE T1,.SQAMC(Q1)	;Get user pointer to initial message buffers
	ERJMP LISER1		;Handle nastrygram page faults
	MOVX T2,.BDFMG		;Put it on the message buffer list
	CALL SCSXUB		;Link this chain of buffer into database
	 JRST LISERR		;Handle chain badness

CONDGB:	SKIPN DGBUF		;Are there any datagram buffers to queue???
	JRST CONSPC		;No, return
	UMOVE T1,.SQADC(Q1)	;Get user pointer to arg block address
	ERJMP LISER1		;Handle bad arg block address
	MOVX T2,.BDFDG		;Point buffers to the datagram queue
	CALL SCSXUB		;Link datagram chain into database please
	 JRST LISERR		;Report failure

CONSPC:	OKINT			;Allow interrupts again
	RETSKP			;All done, return

; Here when we have to go OKSKED before joining common error recovery code.
;We ERJMP here.

CONERR:	OKSKED			;Allow other processes again
	JRST LISER1		;  and join the common error handler

	ENDSV.
	SUBTTL Function code handlers -- Listen (SCSLIS)

; This routine handles the listen function code...
;
; Expects:
;	T2/	User addr of arg block
;
; *** Waring ***
; The STKVAR at the top of this routine must have a set of common elements
;with the STKVAR atop SCSCON. Error recovery routines are shared by these
;two routines and STKVAR locations are used. Hence these offsets must be
;kept in common.
;
SCSLIS::SAVEAC <Q1>
	STKVAR <SPN,DPN,CID,ERRCOD,STRRET,DATRET,STRADR,DATA,MSGBUF,DGBUF>
	MOVE Q1,T2		;Setup perm AC with addr of user args
	XCTU [HRRZ T3,.SQLEN(Q1)] ;Get the first word out of the arg block
	ERJMP SERIAA		;Bad address, call the error handler
	CAIGE T3,.LBLIS		;Is the arg block long enough???
	ITERR (SCSBTS)		;Nope, cause an illegal instruction trap
	XCTU [SKIP .SQLCI(Q1)]	;Is the end of the block available too???
	ERJMP SERIAA		;Nope, cause an illegal instruction trap
	XCTU [HLRE T1,.SQSYS(Q1)] ;Get the SBI we want to talk to
	ERJMP SERIAA		;Handle bad page faults
	CAME T1,[-1]		;If -1 its ok, its a don't care
	CAIG T1,C%SBLL-1	;Is it within range???
	$SKIP			;Yes, go on...
	ITERR (SCSISB)		;Not good, fail wth bad SBI pls...
	SKIPN SBLIST(T1)	;Is there an entry for this SBI???
	ITERR (SCSISB)		;No, fail with bad SBI
	
; Addresses look OK. Start moving data so SCA can get at it.

	SETZM DATA		;Insure null pointer to connection data
	SETZM ERRCOD		;Init the error code

; Move the source name, destination name, and user data into monitor space.

	NOINT			;SCSUNM allocates free space, don't loose it
	CALL SCSUNM		;Move user process names into monitor space
	 ITERR (<>,<OKINT>)	;Return failure
	MOVEM T1,SPN		;Save the address of the source name
	MOVEM T2,DPN		;Save the address of the destination name
	MOVEM T3,STRADR		;Save the address of the user free space
	MOVEM T4,STRRET		;  and the address of the routine to return it

; Here when we are ready to make the call.

LISCAL:	UMOVE T2,.SQSYS(Q1)	;Get the system word from the block
	ERJMP LISER1		;Handle bad addresses
	HLRE T3,T2		;Move the SBI to its own AC
	HRRZS T2		;Be sure the CID bits are in their own AC
	BLCAL. (SC.LIS,<SPN,DPN,T3,<.,SCSINT>,T2,[0],[0]>)
	 JRST LISERR		;Handle failing SCA calls
	UMOVEM T1,.SQLCI(Q1)	;Return the connect ID
	ERJMP LISER1		;Handle write locked pages and friends
	MOVEM T1,CID		;Save the connect ID
	MOVE T1,STRADR		;Get the address of the string space
	CALL @STRRET		;Return it

; Here to store needed data about the fork and job in the CB
;Also link the CB onto fork's CB queue

LISSTO:	$LDCID P1,CID		;Get the CB address from the CID
	CALL SCSCBI		;Init the connect block please
	CALL SCSLCB		;Link this new CB onto fork CB queue
	OKINT			;Allow user interrupts again
	RETSKP			;All done, return

; Here on failure.
;
; LISERR
; T1/	Error code
;
; LISER1
; No error code, failure is bad page access
;
LISER1:	MOVX T1,SCSIAB		;Get the appropriate error code
LISERR:	MOVEM T1,ERRCOD		;Save the error code
	SKIPE T1,STRADR		;Is there any string space to return???
	CALL @STRRET		;Yes, call the routine to return it
	SKIPE T1,DATA		;Is there any connection data to return
	CALL @DATRET		;Yes, return the space it came in
	OKINT			;Allow user interrupts again
	MOVE T1,ERRCOD		;Get the error code back
	ITERR ()		;Return failure

	ENDSV.
	SUBTTL Function code handlers -- Receive datagram (SCSRDG)

	RESCD			;We do CIOFF's here

; This routine handles the receive datagram function code.
;
; Expects:
;	T2/	User addr of arg block
;
SCSRDG::TDZA T1,T1		;Indicate we are doing datagrams
SCSRMG::SETO T1,		;Indicate we are doing messages
	SAVEAC <Q1,Q2,P1>
	STKVAR <PAKADR,USRBUF,CID,MSGFLG,ERRCOD,LEN>
	MOVEM T1,MSGFLG		;Remember how we entered this routine
	MOVE Q1,T2		;Put users addr of args in perm AC
	XMOVEI Q2,MSG		;Assume we are doing messages
	SKIPN MSGFLG		;A good assumption???
	XMOVEI Q2,DG		;No, get the addr of the datagram header block
	SETZM USRBUF		;Be sure these are zero inited since error
	SETZM PAKADR		;  recovery expects zero if unused

	UMOVE T1,.SQLEN(Q1)	;Get the length of the block provided
	ERJMP SERIAA		;Handle bad page faults
	CAIGE T1,.LBRDG		;Is the block long enough to return all data in
	ITERR (SCSBTS)		;No, fail with block to short
	UMOVE T1,.SQCID(Q1)	;Get the connect ID from arg block
	ERJMP SERIAA		;Handle bad addresses
	NOSKED			;Interlock the fork datagram queue
	CAMN T1,[-1]		;Do we want first for fork or connection???
	IFNSK.
	 SKIPN T1,@.TOPFQ(Q2)	;Is there a packet waiting for this fork???
	 ITERR (SCSQIE,<OKSKED>);No, fail with queue is empty
	 LOAD P1,MECID,(T1)	;Get the destination connect ID
	 UMOVEM P1,.SQCID(Q1)	;Return it to user right away
	 ERJMP [OKSKED
		JRST SERIAA]	;BAD ADDRESS
	 MOVEM T1,CID		;Save connect ID of the packet we found
	 $LDCID P1,P1		;Turn connect ID into a CB address
	ELSE.
	 CALL SC.CSC		;Sanity check the connect ID
	  ITERR (SCSIID,<OKSKED>) ;Nope, not a good one, fail now pls...
	 SKIPN T1,@.TOPCQ(Q2)	;Are there datagrams queued for this connect???
	 ITERR (SCSQIE,<OKSKED>) ;Nothing on the queue, fail with queue empty
	ENDIF.

; Here with:
;	T1/	Address of desired packet buffer
;	Q1/	Address of user arguments
;	Q2/	Address of 4 word header block pointing to list headers
;	P1/	Address of connect block
;
RDGHDG:	SKIPG @.JBUFF(Q2)	;Are there datagram buffers queued???
	IFNSK.
	 MOVX T1,SCSNMQ		;Assume we are doing messages
	 SKIPN MSGFLG		;Good assumption???
	 MOVX T1,SCSNDQ		;No, show error code for datagrams
	 ITERR (<>,<OKSKED>)	;Now, fail with correct error code
	ENDIF.
	MOVEM T1,PAKADR		;Save the monitor address of the DG
	MOVE T2,Q2		;Point to the datagram list pointers
	CALL SCSDEQ		;Dequeue the buffer from the fork and CB queues
	ITERR (<>,<OKSKED>)	;RETURN ERROR
	NOINT			;We still need to finish this unintertupted
	OKSKED			;  but we can allow other forks now
	MOVX T1,.BDFMG		;Assume we are doing messages
	SKIPN MSGFLG		;A good assumption???
	MOVX T1,.BDFDG		;No, point at datagrams instead
	CALL SCSGUB		;Get a user datagram buffer address
	 JRST RDGERR		;Handle the buffer we need to return
	MOVEM T1,USRBUF		;Store user buffer addr
	UMOVEM T1,.SQARB(Q1)	;Return the address of the DG data
	ERJMP RDGER1		;Handle this particular write error

	MOVE T4,PAKADR		;Addr of datagram in monitor space
	LOAD T1,MELEN,(T4)	;Get the length of the datagram
	MOVX T2,F.SPM		;Get the high density flag
	TDNE T2,.METYP(T4)	;Is this a high density packet???
	SUBI T1,.MHLNW		;Yes, account for the packet header
	TDNN T2,.METYP(T4)	;Is this an industry compatable packet???
	SUBI T1,.MHLNB		;Yes, account for the packet header
	MOVEM T1,LEN		;Save the corrected length of the packet
	UMOVEM T1,.SQLRP(Q1)	;Tell user how long the datagram is
	ERJMP RDGER1		;Handle write locked user pages and such
	TDNN T2,.METYP(T4)	;Is this an industry compatable mode packet???
	IFNSK.
	 MOVEI T1,3(T1)		;Yes, round up to next nearest word
	 IDIVI T1,4		;Word count = Byte count/4
	 MOVE T2,LEN		;Get the corrected length of the packet
	 IDIVI T2,4		;Convert from bytes to words
	 JUMPE T3,RDGNOB	;If even conversion, no bytes to zero

	 ; Here to zero extra bytes in same word as last valid data byte
	 ADD T2,PAKADR		;Offset to the packet
	 ADDI T2,.MHUDA		;Now offset to user data area
	 SUBI T3,4		;Get number of bytes to zero from
	 MOVMS T3		;  the number of extra valid data bytes
	 IMULI T3,^D8		;Convert from bytes to bits
	 ADDI T3,4		;Account for the low order bits
	 MOVNS T3		;We want a shift right
	 MOVE T4,(T2)		;Get the word in question
	 LSH T4,(T3)		;Shoft out the undesirable bits
	 MOVMS T3		;Now we want a shift left
	 LSH T4,(T3)		;Restore bit position of the word
	 MOVEM T4,(T2)		;Put the word back into the packet
	ENDIF.

RDGNOB:	MOVE T2,PAKADR		;Get the source address
	ADDI T2,.MHUDA		;Don't move SCA/PPD headers
	MOVE T3,USRBUF		;Get the address of te users buffer
	CALL SCSDMU		;Move the data to user space
	 JRST RDGERR		;Handle data move error

	MOVX T1,.SQDFL		;Get the number of words processed
	XCTU [HRLM T1,.SQLEN(Q1)] ;Return the number of words processed
	ERJMP RDGER1		;Handle bad addresses, write locked pages, etc.
	MOVE T1,PAKADR		;Get the datagram buffer address again
	LOAD T3,MEFLG,(T1)	;Get the flags from the entry
	XCTU [HRLM T3,.SQDFL(Q1)] ;Return the flags
	ERJMP RDGER1		;Handle nasty page faults
	SKIPN MSGFLG		;Are we doing messages???
	CALL SC.RLD		;No, return the datagram buffer
	SKIPE MSGFLG		;Are we doing datagrams???
	CALL SC.RBF		;No, return the message buffer
	OKINT			;Allow user interrupts again

	LOAD T2,CBDNOD,(P1)	;Now get the remote node number for this 
	XCTU [HRRM T2,.SQDFL(Q1)] ;Store node number for the user
	ERJMP SERIAA		;Handle a bad address
	RETSKP			; and return all happy

; Here when an ERJMP takes. In this case we have a user datagram address that
;needs to be placed back into the database and a datagram that should be
;linked back onto the FRONT of the fork and connection datagram queues.
;
;Note:
;
; It is assumed that we are NOINT when we get here...
;
; Expects:
;	P1/	CB addr
;	USRBUF/	User virtual address that goes back into database or zero
;	PAKADR/	Monitor virtual address of datagram buffer or zero
;	
RDGER1:	MOVX T1,SCSIAB		;Fill in the error code for bad address
RDGERR:	MOVEM T1,ERRCOD		;Save the error code
	SKIPN T1,USRBUF		;Is there a user buffer to worry about???
	JRST RDGER2		;No, try for the packet

	MOVX T2,.BDFMG		;Assume we are doing messages
	SKIPN MSGFLG		;A good assumption???
	MOVX T2,.BDFDG		;No, get BSD offset for datagrams
	CALL SCSLUB		;Link this user address back into database
	IFNSK.
	 MOVE T2,FORKX		;Say who we are
	 MOVE T3,USRBUF	;  and what buffer we tried for
	 BUG. (INF,SCSUBL,SCSJSY,SOFT,<SCSJSY: User buffer lost during error recovery>,<<T1,ERRCOD>,<T2,CURFRK>,<T3,BUFADR>>,<

Cause:	Bad access to user memory or a failing routine caused SCS to try to 
	place the currently owned user buffer back on the buffer list. The
	attempt failed and the buffer address has been lost. Note that there
	is no memory loss, the monitor has just forgotten one user buffer
	address.
>);End of BUG. SCSUBL
	ENDIF.			;Fall into packet return code please...

; Here to return the packet (if we have one)

RDGER2:	SKIPN T1,PAKADR		;Do we have a packet to return???
	JRST RDGER3		;No, exit please
	MOVE T2,Q2		;Point to the 4 word list header block
	CALL SCSLFQ		;Link this packet onto the front of the q's
	IFNSK.
	 MOVE T1,ERRCOD
	 BUG. (INF,SCSDGL,SCSJSY,SOFT,<SCSJSY: User packet address lost>,<<T1,ERRCOD>,<FORKX,CURFRK>,<JOBNO,CURJOB>>,<

Cause:	In trying to link a packet buffer back onto the datagram queue after
	determining we could not continue, we failed.

Action:	None. The fork will have lost a datagram, but the fork shoudl not hang 
	and when the fork goes away so does the problem.
>)
	 MOVE T1,PAKADR		;Get the datagram buffer address
	 CALL SC.RLD		;Return the datagram buffer
	ENDIF.

; Here for the final exit

RDGER3:	OKINT			;Allow user interrupts again
	MOVE T1,ERRCOD		;Get the error code back
	ITERR ()		;Now yell at the user

	ENDSV.
	SUBTTL Function code handlers -- Read port counters (SCSRPC)

	SWAPCD

; This routine handles the read port counters function code.
;
SCSRPC::RET
	SUBTTL Function code handlers -- Reject (SCSREJ)

; This routine handles the reject function code.
;
; Expects:
;	T2/	User address of arg block
;
SCSREJ::MOVE Q1,T2		;Save the address of the user args
	UMOVE T1,.SQLEN(Q1)	;Get the length word from the user args
	ERJMP SERIAA		;Handle an address that doesn't exist
	HRRZS T1		;Get just the length part
	CAIGE T1,.LBREJ		;Is the block long enough for all args???
	ITERR (SCSBTS)		;Nope, fail with block to short
	UMOVE T1,.SQCID(Q1)	;Get the CID from the user
	ERJMP SERIAA		;Handle bad page faults
	SAVEAC <P1>		;Save the AC we kill here
	CALL SC.CSC		;Is it a valid CID???
	 ITERR (SCSIID)		;Nope, die with invalid CID
	UMOVE T2,.SQREJ(Q1)	;Get the reject reasons form the user
	ERJMP SERIAA		;Handle the address not being there
	NOINT			;Be sure SCA completes with no user interrupts
	BLCAL. (SC.REJ,<T1,T2>)	;Do the reject pls...
	 ITERR (<>,<OKINT>)	;Fail with code from SCA
	CALL SCSCBD		;All done with connect, delete JSYS data
	OKINT			;Allow user interrupts again
	RETSKP			;Return OK
	SUBTTL Function code handlers -- Disconnect (SCSDIS)

; This routine handles the disconnect function code.
;
; Expects:
;	T2/	User virtual address of arguments
;
SCSDIS::MOVE Q1,T2		;Save the user address of args
	UMOVE T1,.SQLEN(Q1)	;Get the length word
	ERJMP SERIAA		;Handle the address not being there
	HRRZS T1		;Get just the length part
	CAIGE T1,.LBDIS		;Is the block long enough
	ITERR (SCSBTS)		;Nope, fail JSYS call with block to short
	UMOVE T1,.SQCID(Q1)	;Get the CID from the user
	ERJMP SERIAA		;Handle the address not being there
	CALL SC.CSC		;Is this a valid CID???
	 ITERR (SCSIID)		;Nope, fail with bad CID
	UMOVE T2,.SQDIS(Q1)	;Get the disconnect reasons from the user
	ERJMP SERIAA		;Handle that address not being there
	NOINT			;Be sure SCA code completes with user intertupt
	BLCAL. (SC.DIS,<T1,T2>)	;Do the disconnect
	 ITERR (<>,<OKINT>)	;Fail with code form SCA
	CALL SCSCBD		;(P1/) All done with JSYS data, release it
	OKINT			;Allow user interrupts again
	RETSKP			;Succeed
	SUBTTL Function code handlers -- Send message/datagram (SCSSMG/SCSSDG)

	RESCD

; Routine to send a message/datagram.
;
; Expects:
;	T2/	User address of args
;
SCSSDG::TDZA T1,T1		;Indicate we are doing datagrams
SCSSMG::SETO T1,		;Indicate we are doing messages

	SAVEAC <Q1,P1>
	STKVAR <BUFADR,CID,MSGFLG,ERRCOD>
	MOVEM T1,MSGFLG		;Save the entry point indicator
	SETZM BUFADR		;Init the buffer address as zero

	MOVE Q1,T2		;Save the user address of the message text
	XCTU [HRRZ T1,.SQLEN(Q1)] ;Get the length word from the block
	ERJMP SERIAA		;Handle nasty page faults
	CAIGE T1,.LBSDG		;Is the block long enough???
	ITERR (SCSBTS)		;No, fail with block to short
	XCTU [SKIP .SQFLG(Q1)]	;Touch the last word in the block
	ERJMP SERIAA		;Handle case of block overflows to bad page
	NOINT			;Insure no change of CID
	UMOVE T1,.SQCID(Q1)	;Get the connect ID
	ERJMP SDGER1		;Handle bad addresses
	CALL SC.CSC		;Is the ID ok???
	 ITERR (SCSIID,<OKINT>)	;No, fail with invalid ID
	MOVEM T1,CID		;Stash the connect ID for later

	MOVX T1,1		;We wish one packet buffer please
	SKIPN MSGFLG		;Are we doing messages???
	IFNSK.
	 CALL SC.ALD		;Get a monitor buffer for the data
	  ITERR (<>,<OKINT>)	;No buffer available, fail here
	 MOVX T2,JH%DGB		;Get the datagram buffer bit
	 IORM T2,.JHFLG(T1)	;Light the datagram flag in packet
	ELSE.
	 CALL SC.ABF		;Get a monitor buffer for the data
	  ITERR (<>,<OKINT>)	;None available, fail please
	 MOVX T2,JH%DGB		;Get the datagram buffer flag
	 ANDCAM T2,.JHFLG(T1)	;Zero the datagram flag in the JSYS header
	ENDIF.
	MOVEM T1,BUFADR		;Save the buffer address

	UMOVE T2,.SQAPT(Q1)	;Get the user address of the packet text
	ERJMP SDGER1		;Ooops, back out gracfully please
	MOVEM T2,.JHAUB(T1)	;Store the address of the user buffer

	UMOVE T1,.SQLPT(Q1)	;Get the length of the packet text
	ERJMP SDGER1		;Bad address? Back out pls...
	UMOVE T2,.SQFLG(Q1)	;Get the flags word from user space
	ERJMP SDGER1		;Handle bad user addresses
	TXNN T2,SC%MOD		;Is this a high density packet???
	IFNSK.
	 ADDI T1,3		;Round up byte count
	 IDIVI T1,4		;Yes, turn the byte count into a word count
	ENDIF.
	MOVX T3,<C%MGSZ-.MHUDA>	;Assume messages
	SKIPN MSGFLG		;A good assumption???
	MOVX T3,<C%DGSZ-.MHUDA>	;No, get the size for datagrams
	CAMLE T1,T3		;Is the text too long???
	IFNSK.
	 MOVX T1,SCAPTL		;Yes, get the appropriate error code
	 JRST SDGERR		;  and back out of this routine
	ENDIF.

	MOVE T3,BUFADR		;Get the address of the monitor buffer
	MOVE T2,.JHAUB(T3)	;Get user buffer address again
	ADDI T3,.MHUDA		;Offset monitor space text area in buffer
	CALL SCSDUM		;Move the data from user to monitor space
	 JRST SDGERR		;Failed, report error

	MOVX T2,F.RTB		;Start with just the return buffer flag
	UMOVE T3,.SQFLG(Q1)	;Get the users flag word
	ERJMP SDGER1		;Handle bad addresses
	TXNE T3,SC%MOD		;Is a high density packet requested???
	TXO T2,F.SPM		;Yes, light flag for SCA

	LOAD T3,SC%OPS,T3	;Now keep just the optional path spec
	CAIL T3,.SSLOW		;Path spec too low???
	CAILE T3,.SSHGH		;  or too high???
	IFNSK.
	 MOVX T1,SCSIPS		;Yes, fail with invalid path spec.
	 JRST SDGERR		;.	.	.
	ENDIF.

	UMOVE T4,.SQLPT(Q1)	;Get the length of the packet text from user
	ERJMP SDGER1		;Handle a bad address

	SKIPN MSGFLG		;Are we doing messages???
	IFNSK.
	 BLCAL. (SC.SDG,<CID,T2,T4,BUFADR,[2],T3>) ;Have SCA send the datagram
	  JRST SDGERR		;Fail with code from SCA
	ELSE.
	 BLCAL. (SC.SMG,<CID,T2,T4,BUFADR,[2],[0],T3>) ;Have SCA send message
	  JRST SDGERR		;Fail with code from SCA
	ENDIF.

	OKINT			;Allow user interrpts again
	RETSKP			;All done...

; Here when an ERJMP takes or when we get a non-skip return from a routine.
;
; Usage:
;	SDGERR -- Here when we have an error code
;
;	SDGER1 -- ERJMP here on a user address error. We must fill in the error
;			code...
;
SDGER1: MOVX T1,SCSIAB		;Get the correct error code for address error
SDGERR:	MOVEM T1,ERRCOD		;Save the error code
	SKIPN T1,BUFADR		;Is there a buffer owned???
	JRST SDGER2		;No, exit please
	SKIPN MSGFLG		;Datagram?
	CALL SC.RLD		;Return the buffer to the SCA free pool
	SKIPE MSGFLG		;Message?
	CALL SC.RBF		;Yes return the message buffer

SDGER2:	OKINT			;Allow interrupts again
	MOVE T1,ERRCOD		;Put the error code where it belongs
	ITERR ()		;  and fail please

	ENDSV.
	SUBTTL Function code handlers -- Queue mess buffers (SCSQRM)

	SWAPCD

; This routine handles the queue receive message buffers function code.
;
; Assumes:
;	T2/	User virtual address of arg block
;
SCSQRD::TDZA T1,T1		;Set the flag that says we are in datagram code
SCSQRM::SETO T1,		;Claim that we are in message code
	STKVAR <QRMFLG,CID>
	MOVEM T1,QRMFLG		;Save message/datagram queue flag
	MOVE Q1,T2		;Set up perm AC for user arg block addr
	UMOVE T1,.SQLEN(Q1)	;Get the length of the users arg block
	ERJMP SERIAA		;Handle nasty page faults in the monitor
	HRRZS T1		;Get just the length part
	CAIGE T1,.LBQRM		;Is the block long enough???
	ITERR (SCSBTS)		;No, fail on block to short
	XCTU [SKIP .SQAFB(Q1)]	;Is the end of the block touchable???
	ERJMP SERIAA		;No, handle the nasty page fault
	UMOVE T1,.SQCID(Q1)	;Get the connect ID from the user
	ERJMP SERIAA		;Handle bad addresses
	CALL SC.CSC		;Sanity check the connect ID
	 ITERR (SCSIID)		;Bad CID, fail pls...
	MOVEM T1,CID		;Save the CID for later please
	UMOVE T1,.SQAFB(Q1)	;Get the address of the first buffer
	MOVX T2,C%MGSZ		;Assume message buffers
	SKIPN QRMFLG		;A good assumption???
	MOVX T2,C%DGSZ		;No, get the size for datagrams
	CALL SCSCUB		;Count buffer in chain and check for good chain
	 ITERR ()		;Fail with bad pointer

; Here to start looping over buffers and adding them to the BSD for this
;connection.

	UMOVE Q1,.SQAFB(Q1)	;Get the user addr of a buffer
	ERJMP SERIAA		;Handle bad addresses
	SETZM Q2		;Zero the count of buffers queued

QRMBLP:	AOS Q2			;Increment count of buffers
	MOVE T1,Q1		;Get user buffer address
	MOVX T2,.BDFMG		;Assume its a message
	SKIPN QRMFLG		;Was this a good assumption???
	MOVX T2,.BDFDG		;No, this is a datagram buffer queueing
	CALL SCSLUB		;Link this buffer addr into database
	 ITERR ()		;Handle errors from below
	XCTU [MOVE Q1,(Q1)]	;Try for the next buffer
	ERJMP SERIAA		;Hmm, handle errors touching bad address
	SKIPE Q1		;If zero we have reached the end of our rope
	JRST QRMBLP		;Else loop for more buffer on the chain

; Here when the buffer addresses have all been recorded and we can queue some
;real buffers.
;
	SKIPN QRMFLG		;Are we in datagram code???
	JRST QRMQDG		;Yes, do datagram queue
	NOINT			;Be sure the call completes
	BLCAL. (SC.RMG,<CID,Q2,[0]>) ;Queue buffer for this connection
	 ITERR (<>,<OKINT>)	;Fail with error code from SCA
	OKINT			;Allow interrupts again
	RETSKP			;  or return success

QRMQDG:	NOINT			;Be sure the call completes
	BLCAL. (SC.RDG,<CID,Q2,[0]>) ;Queue datagram buffers for this connect
	 ITERR	(<>,<OKINT>)	;Fail with error code from SCA
	OKINT			;Allow interrupts again
	RETSKP			;  and return

	ENDSV.
	SUBTTL Function code handlers -- Cancel DG buffers (SCSCRD)

; This routine handles the cancel datagram buffers function code.
;
SCSCRM::TDZA T1,T1		;Show we are no doing datagrams
SCSCRD::SETO T1,		;Show we are doing datagrams
	STKVAR <CRDFLG,CID>
	MOVEM T1,CRDFLG		;Save the state of the datagram code flag
	MOVE Q1,T2		;Save the address of the user buffer
	UMOVE T2,.SQLEN(Q1)	;Get the users first word of arg block
	ERJMP SERIAA		;Handle a bad address
	HRRZS T2		;Get just the length part
	CAIGE T2,.LBCRD		;Is the block long enough???
	ITERR (SCSBTS)		;Bomb with block to short
	XCTU [SKIP .SQADB(Q1)]	;Can we get to the whole arg block???
	ERJMP SERIAA		;Nope, die on a bad address

	UMOVE T1,.SQCID(Q1)	;Get the CID
	ERJMP SERIAA		;Handle nasty page faults
	CALL SC.CSC		;Is there such a thing???
	 ITERR (SCSNSC)		;Nope, tell the user we dont like this CID
	MOVEM T1,CID		;Save the connect ID
	NOINT			;About to diddle counts and data
	SOS .CBDGJ(P1)		;Decrement the JSYS datagram count
	UMOVE T1,.SQADB(Q1)	;Get the addr of the user buffer to return
	ERJMP [OKINT
	       JRST SERIAA]	;BAD ADDRESS
	CALL SCSRUB		;Remove the buffer from the database
	 ITERR (SCSNSB,<OKINT>)	;Return no such buffer
	SKIPN CRDFLG		;Are we doing datagrams???
	JRST CRDMSG		;No, cancel a message

	BLCAL. (SC.CRD,<CID,[1]>) ;Cancel a datagram pls...
	 ITERR (<>,<OKINT>)	;Fail with code from SCAMPI
	OKINT			;Allow user interrupts again
	RETSKP			;Else return OK

CRDMSG:	BLCAL. (SC.CRM,<CID,[1]>) ;Cancel a message please
	 ITERR (<>,<OKINT>)	;Fail with code from SCAMPI
	OKINT			;Allow user interrupts again
	RETSKP			;Else return OK...	

	ENDSV.
	SUBTTL Function code handlers -- Connect state poll (SCSCSP)

; This routine handles the connect state poll function code.
;
SCSCSP::STKVAR <BYTEP>
	MOVE Q1,T2		;Save the user arg block address
	UMOVE T1,.SQLEN(Q1)	;Get the length word
	ERJMP SERIAA		;Handle nasty page fault from monitor
	CAIGE T1,.LBCSP		;Is the block big enough???
	ITERR (SCSBTS)		;No, fail with block to small
	XCTU [SKIP .SQREA(Q1)]	;Touch the last word in the block
	ERJMP SERIAA		;Its not there, complain
	UMOVE T1,.SQCID(Q1)	;Get the CID from the users arg block
	ERJMP SERIAA		;Handle bad user addresses
	CALL SC.CSC		;Sanity check the connect ID
	 ITERR (SCSIID)		;Bad connect ID. Fail pls...
	LOAD T1,CBCNST,(P1)	;Get the connection state
	UMOVEM T1,.SQCST(Q1)	;Store for the user
	ERJMP SERIAA		;Handle bad pages...
	LOAD T1,CBDCID,(P1)	;Get the destination CID
	UMOVEM T1,.SQDCI(Q1)	;Store this for the user as well
	ERJMP SERIAA		;Handle bad addresses
	LOAD T1,CBDNOD,(P1)	;GET THE DESTINATION NODE NUMBER
	UMOVEM T1,.SQSBI(Q1)	;STORE IN USERS BLOCK
	ERJMP SERIAA		;HANDLE BAD ADDRESS
	LOAD T1,CBSDRE,(P1)	;Get the source disconnect reasons
	XCTU [HRLZ T1,.SQREA(Q1)] ;Store in users block
	ERJMP SERIAA		;Handle bad addresses
	LOAD T1,CBDDRE,(P1)	;Get the destination disconnect reasons
	XCTU [HRRM T1,.SQREA(Q1)] ;Store this as well
	ERJMP SERIAA		;Handle bad addresses
	UMOVE T1,.SQBDN(Q1)	;Get the users byte pointer
	ERJMP SERIAA		;Handle bad addresses
	SKIPN T1		;IS THE POINTER ZERO?
	IFSKP.			;NO, THEN GET THE NAME.
	  TLC T1,-1		;Compliment left half of byte pointer
	  TLCN T1,-1		;Is it a generic byte pointer???
	  HLL T1,[POINT 7,]	;Yes, turn it into a real one
	  MOVEM T1,BYTEP		;Save the byte pointer for later use
	  MOVX T4,<POINT 8,.CBDPN(P1)> ;Get a byte pointer to destination name
	  MOVX T3,C%PNMN		;Get the length of the string
CSPNLP:	  ILDB T2,T4		;Get a char
	  XCTBU [IDPB T2,BYTEP]	;Store a byte of the destination name
	  ERJMP SERIBP		;Handle bad byte instructions
	  SOJG T3,CSPNLP	;Loop for all bytes in the string
	  MOVE T1,BYTEP		;Get the updated byte pointer
	  UMOVEM T1,.SQBDN(Q1)	;Update the users byte pointer...
	  ERJMP SERIAA		;Handle bad addresses
	ENDIF.
	RETSKP			;Return...

	ENDSV.
	SUBTTL Function code handlers -- Return connect data (SCSRCD)

; This routine handles the RCD function code.
;
SCSRCD::MOVE Q1,T2		;Save the address of the user arg block
	UMOVE T1,.SQLEN(Q1)	;Get the block length
	ERJMP SERIAA		;Handle bad addresses from monitor
	CAIGE T1,.LBRCD		;Is it long enough for the job???
	ITERR (SCSBTS)		;No, fail on block to short
	XCTU [SKIP .SQLPN(Q1)]	;Can we get to the last word???
	ERJMP SERIAA		;Nope, handle nasty page faults from monitor
	XCTU [MOVE T1,.SQCID(Q1)] ;Is there a CID word in user args???
	ERJMP SERIAA		;Handle bad addresses
	JUMPE T1,RCDSBI		;No, use the SBI field
	CALL SC.CSC		;Sanity check the connect ID
	 ITERR (SCSIID)		;Not a good CID, fail
	LOAD T1,CBDNOD,(P1)	;Else load the node we are talking about
	JRST RCDGIN		;  and go give the info the world wants

; Here when the SBI field is to be used.
;
RCDSBI:	UMOVE T1,.SQOSB(Q1)	;Get the SBI from the arg block
	ERJMP SERIAA		;Handle bad addresses
	SKIPL T1		;Is it a negative SBI???
	CAILE T1,C%SBLL-1	;Or within the values of reason
	ITERR (SCSISB)		;No, invalid SBI

; Here when we have an SBI in T1 and can start the long march of data into
;Hilbert space...
;
RCDGIN:	SKIPN SBLIST(T1)	;Is this a valid SBI???
	ITERR (SCSISB)		;Nope, fall over pls...
	UMOVEM T1,.SQOSB(Q1)	;Store the SBI for the user
	ERJMP SERIAA		;Handle bad addresses
	MOVE T1,SBLIST(T1)	;Get the SB addr from the SBI

	LOAD T2,SBVCST,(T1)	;Get the virtual circuit state
	XCTU [HRLZM T2,.SQVCS(Q1)] ;Store the virtual circuit state for user
	ERJMP SERIAA		;Oh nasty user, handle bad arg address

	LOAD T2,SBDPA,(T1)	;Load the destination port number
	XCTU [HRRM T2,.SQVCS(Q1)] ;Store for user
	ERJMP SERIAA		;Handle bad addresses
	
	DMOVE T2,.SBDSS(T1)	;Get the destination system address
	XCTU [DMOVEM T2,.SQSAD(Q1)] ;Store
	ERJMP SERIAA		;Handle bad arg addresses

	LOAD T2,SBMXMG,(T1)	;Get the maximum message size
	LOAD T3,SBMXDG,(T1)	;  and the max datagram size
	XCTU [DMOVEM T2,.SQMDD(Q1)] ;Store in user block
	ERJMP SERIAA		;Handle bad argument addresses

	DMOVE T2,.SBDST(T1)	;Get the software type and version
	XCTU [DMOVEM T2,.SQDST(Q1)] ;Store
	ERJMP SERIAA		;Handle bad argument addresses

	DMOVE T2,.SBDSE(T1)	;software edit level
	XCTU [DMOVEM T2,.SQDSE(Q1)] ;Store it
	ERJMP SERIAA		;Handle bad argument addresses

	MOVE T2,.SBDHT(T1)	;GET DESTINATION HARDWARE TYPE
	UMOVEM T2,.SQDHT(Q1)	;STORE IT
	ERJMP SERIAA		;HANDLE BAD ADDRESS

	DMOVE T2,.SBDHV(T1)	;GET FIRST 2 WORDS OF HARDWARE VERSION
	XCTU [DMOVEM T2,.SQDHV(Q1)] ;STORE THEM
	ERJMP SERIAA		;Handle bad argument addresses
	MOVE T2,.SBDHV+2(T1)	;GET 3RD WORD OF HARDWARE VERSION
	UMOVEM T2,.SQDHV+2(Q1)	;STORE IT
	ERJMP SERIAA		;Handle bad argument addresses

	DMOVE T2,.SBNNM(T1)	;GET DESTINATION PORT NAME
	XCTU [DMOVEM T2,.SQNNM(Q1)] ;GIVE IT TO USER
	ERJMP SERIAA		;Handle bad argument addresses

	MOVE T2,.SBDPC(T1)	;GET PORT CHARACTERISTICS
	UMOVEM T2,.SQPCW(Q1)	;STORE IT
	ERJMP SERIAA		;Handle bad argument addresses

	MOVX T1,KLPRH2		;Get the channel number of the KLIPA
	UMOVEM T1,.SQLPN(Q1)	;Store for the user
	ERJMP SERIAA		;Handle bad argument addresses

	RETSKP			;All done
	SUBTTL Function code handlers -- Status of connect (SCSSTS)

; This routine handles the quick status function code.
;
SCSSTS::MOVE Q1,T2		;Save the user address of the user arg block
	UMOVE T1,.SQLEN(Q1)	;Get the length word
	ERJMP SERIAA		;Handle nasty page faults from monitor
	CAIGE T1,.LBSTS		;Is the block long enough???
	ITERR (SCSBTS)		;No, fail with block to short
	XCTU [SKIP .SQSBR(Q1)]	;Is the whole block available???
	ERJMP SERIAA		;No, handle nasty page faults
	UMOVE T1,.SQCID(Q1)	;Get the users proposed CID
	ERJMP SERIAA		;Handle bad addresses
	CALL SC.CSC		;Sanity check the connect ID
	 ITERR (SCSIID)		;Fail with invalid connect ID
	LOAD T1,CBCNST,(P1)	;Get the connection state
	XCTU [HRRZM T1,.SQFST(Q1)] ;Store for the user
	ERJMP SERIAA		;Handle bad argument addresses
	LOAD T1,CBDNOD,(P1)	;Get the node number of the remote
	XCTU [HRRZM T1,.SQSBR(Q1)] ;Store
	ERJMP SERIAA		;Handle bad argument addresses
	SETZ T1,		;Clear a flag AC
	SKIPE .CBTMQ(P1)	;Are there any messages pending???
	TXO T1,SC%MSA		;Yes, light the messages bit
	SKIPE .CBTXQ(P1)	;Is there a data request pending???
	TXO T1,SC%DTA		;Yes, light the data available bit
	SKIPE .CBTDQ(P1)	;Is there a datagram available???
	TXO T1,SC%DGA		;Yes, light the datagram bit
	SKIPE .CBTEQ(P1)	;Is there an event pending???
	TXO T1,SC%EVA		;Yes, light the event bit
	XCTU [ORM T1,.SQFST(Q1)] ;Set the appropriate flag bits
	ERJMP SERIAA		;Handle bad argument addresses
	RETSKP			;And we are all done
	SUBTTL Function code handlers -- Map a buffer (SCSMAP)

	SWAPCD

; This routine handles the map a DMA buffer function code.
;
;THE STKVAR ELEMENTS ARE
;	USRADR - THE USER SPACE VIRTUAL ADDRESS OF THE JSYS'S ARGUMENT BLOCK
;	USRLEN - LENGTH OF THE .SQBLN/.SQBAD PAIRS IN THE JSYS'S ARGUMENT BLOCK
;	FBADDR - MONITOR VIRTUAL ADDRESS OF BUFFER FOR THE MAP DESCRIPTOR BLOCK
;		 THAT IS USED BY MAPBUF.
;	PGSTK  - MONITOR VIRTUAL ADDRESS OF BUFFER THAT STORES THE USER PAGES
;		 THAT HAVE BEEN LOCKED FOR THIS TRANSFER. FIRST WORD IS THE
;		 COUNT OF THE PAGES THAT HAVE BEEN LOCKED.
;	ERRCOD -
;	MODE   - THE FORMATTING MODE THAT HAS BEEN SPECIFIED 
;	WRDLEN - THE NUMBER OF WORDS NEEDED FOR THE NUMBER OF BYTES
;		 THAT HAVE BEEN SPECIFIED IN THE JSYS'S ARGUMENT BLOCK.

SCSMAP::STKVAR <USRADR,USRLEN,FBADDR,PGSTK,ERRCOD,MODE,WRDLEN>
	SETZM PGSTK		;Clear address of page stack page
	MOVEM T2,USRADR		;Save user virtual addr of args
	XCTU [HRRZ T1,.SQLEN(T2)] ;Get the length of the arg block
	ERJMP SERIAA		;Handle bad user arg address
	CAIGE T1,.SQBNA+.SQBAD+2;Is block long enough for return +1 buffer?
	ITERR (SCSBTS)		;No, fail with block to short
	MOVEI T1,-<.SQBNA+1>(T1) ;Get the user length minus the overhead
	MOVEM T1,USRLEN		;Store for later generations
	CAILE T1,<C%DGSZ-2>/<.SQBAD+1> ;More segments than we can handle???
	ITERR (SCSTMS)		;Yes, fail with to many buffer segments

	MOVX T1,1		;Start with a single buffer
	NOINT			;We are getting a resource...
	CALL SC.ALD		;(T1/T1,T2,T3) Allocate a dataram buffer FOR
				;THE MAP DESCRIPTOR BLOCK.
	 ITERR (,<OKINT>)	;Fail with resource allocation failure
	MOVEM T1,FBADDR		;Store first block address
	MOVE Q1,T1		;Put addr of scratch buffer here for use

; Quick sanity check:
;
;	T1-T4 -- Temp since MLKCP will smash them
;
;	Q1/	ADDRESS OF THE MAP DESCRIPTOR BLOCK USED BY MAPBUF
;	Q2/	Current USER CONTEXT VIRTUAL ADDRESS POINTER TO
;		JSYS ARGUMENT BLOCK ENTRY
;	Q3/	User CONTEXT VITUAL ADDRESS OF JSYS ARGUMENT BLOCK
;
;	P4/	Count of LOCKED pages IN page stack
;	P5/	Stack pointer for locked pages stack
;
	MOVE Q2,USRADR		;Get the user arg block address again
	ADDI Q2,.SQBNA+1	;Offset into the user block to first desc blk
	MOVE Q3,USRADR		;Keep the user arg address here
	SETZ P4,		;Zero the page stack counter

	UMOVE T1,.SQXFL(Q3)	;Get the users flags
	ERJMP MAPER2		;Bad addr, yell at the user
	ANDX T1,MD%FLG		;Keep only the defined flags
	TXO T1,SQ%WRT		;Set the writable bit for compatibility with
				;the way code in SCAMPI use to work.
	MOVEM T1,.MDFLG(Q1)	;Store flags/mode in MAP DESCRIPTOR BLOCK
	LOAD T2,MD%DMD,T1	;Get the buffer mode
	CAIE T2,MD%ILL		;Is it the dissallowed value???
	IFSKP.
	 MOVX T1,SCSIDM		;Yes, get the appropriate error code
	 JRST MAPER1		;   and yell at the user
	ENDIF.
	MOVEM T2,MODE		;SAVE THE MODE

	MOVX T1,1		;We only desire one buffer pls...
	CALL SC.ALD		;Get a datagram buffer pls...
	 JRST MAPER1		;Handle failure pls...
	MOVEM T1,PGSTK		;Save the base address of the page stack
	AOS P5,T1		;Get the address of the page stack +1
	ADDI Q1,.MDSSD		;Offset mon buffer addr to first desc blk

; Top of the loop. Here to check for valid segment length.
;
MAPELP:	UMOVE T3,.SQBLN(Q2)	;Length from user block
	ERJMP MAPER2		;Arg block addr was bad, fail
	JUMPE T3,MAPCAL		;Zero length, end of block, start the map call
	MOVEM T3,.MDLEN(Q1)	;Store length in block for SCA
				
				;CALCULATE NUMBER OF WORDS NEEDED FOR
				;THE SPECIFIED NUMBER OF BYTES.
	MOVE T1,MODE		;GET THE MODE. 
	CAIE T1,MD%DIC		;IS IT INDUSTRY COMPATABLE
	IFSKP.
	  LSH T3,-2		;YES. DIVIDE BY 4 (4 BYTES PER WORD).
	ELSE.
	  LSH T3,1		;HIGH DENSITY. MULTIPY BY 2/9 (4.5 PER WORD)
	  IDIVI T3,11
	  SKIPE T4		;REMAINDER?
	  AOS T3		;YES. ADD ONE TO THE WORD COUNT
	ENDIF.
	MOVEM T3,WRDLEN		;SAVE THE NUMBER OF WORDS NEEDED.
	CAILE T3,PGSIZ		;Is the block not longer than one page???
	IFNSK.
	 MOVEI T1,SCSSTL	;Yes, error code for segment to long
	 JRST MAPER1		;Go fail...
	ENDIF.

; Here to check for valid address.
;
	UMOVE T1,.SQBAD(Q2)	;Get the users segment address
	ERJMP MAPER2		;Bad arg address, handle page fault
	XCTU [MOVES (T1)]	;Be sure the page is writable
	ERJMP MAPER2		;Handle bad address here too
	XCTU [MAP T1,(T1)]	;Get the physical address from user address
	ANDX T1,C%PHAD		;Keep just the address part
	MOVE T2,T1		;Copy of the physical address
	ANDI T2,777		;Keep only the page offset
	ADD T2,T3		;Add the length of the segment
	CAIG T2,1000		;Are we still on the same page???
	IFSKP.
	 MOVX T1,SCSSCP		;No! Get the appropriate error code
	 JRST MAPER1		;Die here please...
	ENDIF.
	MOVEM T1,.MDADR(Q1)	;Store segment address in monitor buffer
	LSH T1,^D-9		;Turn address into page number
	PUSH P5,T1		;Save the page on the page stack
	AOS P4			;Count the page on the stack
	CALL MLKCP		;Lock the physical page pls...

	ADDI Q1,.MDLSD		;Point Q1 at next descriptor length pair
	ADDI Q2,.SQBAD+1	;Offset to next entry in user space
	MOVX T3,-<.SQBAD+1>	;Minus length of one segment descriptor
	ADDM T3,USRLEN		;Update the remaining block length
	MOVMS T3		;Get positive length of entry
	CAMG T3,USRLEN		;Is there room in user block for another blk?
	JRST MAPELP		;Yes, loop for another user block

MAPCAL:	MOVEM P4,@PGSTK		;Save the number of pages on the stack
	BLCAL. (SC.MAP,<FBADDR>) ;Do the work of mapping the buffer
	 JRST MAPER1		;Hmmm, bomb with error code from SCA
	MOVE Q1,T1		;Save the buffer name
	MOVE T2,FORKX		;Associate this fork with the buffer
	MOVE T3,PGSTK		;Get the base address of the page stack
	CALL SCSAXN		;Add the name to the fork's database
	 JRST MAPER1		;Handle failure
	MOVE T1,FBADDR		;Now the scratch buffer address
	CALL SC.RLD		;  which get returned as well
	OKINT			;Allow user interrupts again
	UMOVEM Q1,.SQBNA(Q3)	;Tell user what name we gave him
	ERJMP SERIAA		;Handle nasty page faults
	RETSKP			;All done

; Here when an ERJMP takes or we get a plus one return from a routine.
;
; MAPERR -- We are NOINT, and FBADDR has the addr of a DG buffer we must 
;		return to the SCA free pool
; MAPER1 -- As above but we have an error code in T1 and the page stack needs
;		to be unwound, also PGSTK must be checked for a DG buffer
;		which needs to be returned to SCA free pool
; MAPER2 -- As MAPER1 but no error code in T1
;
MAPER2:	MOVX T1,SCSIAB		;Error code for bad addresses
MAPER1:	MOVEM T1,ERRCOD		;Save the error code
	JUMPE P4,MAPERA		;Anything on the page stack, if not go on...

	POP P5,T1		;Get the physical page number to be unlocked
	CALL MULKCR		;Unlock the page please
	SOJG P4,.-2		;More pages to do? If so unlock them...

MAPERA:	SKIPE T1,PGSTK		;A page stack DG buffer to be returned???
	CALL SC.RLD		;Yes, return it please...

MAPERR:	MOVE T1,FBADDR		;Get the address of the scratch DG buffer
	CALL SC.RLD		;Resturn it to the SCA free pool
	OKINT			;Allow user interrupts again
	MOVE T1,ERRCOD		;Get the error code back please
	ITERR ()		;Yes, use it please...

	ENDSV.
	SUBTTL Function code handlers -- Unmap a buffer (SCSUMP)

; This routine handles the unmap a buffer function code.
;
SCSUMP::STKVAR <BUFNAM>
	XCTU [HRRZ T1,.SQLEN(T2)] ;Get user length of block
	ERJMP SERIAA		;Handle nasty page faults
	CAIGE T1,.LBUMP		;Is the block long enough???
	ITERR (SCSBTS)		;No, fail with block to short
	NOINT			;Be sure there are no data alterations
	UMOVE T1,.SQNAM(T2)	;Get the users buffer name
	ERJMP UMPERR		;Handle addr being bad
	MOVEM T1,BUFNAM		;Save the buffer name
	MOVE T2,FORKX		;Look for the buffer in current fork
	CALL SCSFXN		;See if we have such a buffer name???
	 ITERR (<>,<OKINT>)	;Fail with appropriate error code please...
	BLCAL. (SC.UMP,<T1>)	;Unmap the buffer please
	 ITERR (<>,<OKINT>)	;Fail with error code from port driver
	MOVE T1,BUFNAM		;Get the buffer name back
	MOVE T2,FORKX		;  and we own it
	CALL SCSDXN		;Remove it from the fork database please
	 ITERR (<>,<OKINT>)	;Failed? Report failure
	OKINT			;Allow user interrupts again
	RETSKP			;Return OK

; Here on a bad user supplied address

UMPERR:	OKINT			;Allow user interrupts again
	JRST SERIAA		;Now go handle like any other bad address
	ENDSV.
	SUBTTL Function code handlers -- Send/Request a buffer (SCSSND/SCSREQ)

; This routine handles the send a buffer, and request a buffer function codes.
;
SCSREQ::TDZA T1,T1		;Show us in request data code
SCSSND::SETO T1,		;Indicate we want a SEND_DATA

	STKVAR <SNDFLG>
	MOVEM T1,SNDFLG		;Save code type flag
	MOVE Q1,T2		;Save address of users arg block
	XCTU [HRRZ T1,.SQLEN(Q1)] ;Get user arg block length
	ERJMP SERIAA		;Handle bad addresses
	CAIGE T1,.LBSND		;Is the block long enough???
	ITERR (SCSBTS)		;No, fail with block to short
	UMOVE T1,.SQCID(Q1)	;Get users CID
	ERJMP SERIAA		;Handle bad addresses
	CALL SC.CSC		;Is the CID valid???
	 ITERR (SCSIID)		;No fail with invalid CID
	UMOVE T1,.SQSNM(Q1)	;Name of buffer to send
	ERJMP SERIAA		;Handle bad addresses
	UMOVE T3,.SQRNM(Q1)	;Name of receiving buffer
	ERJMP SERIAA		;Handle bad addresses
	XCTU [HLRZ T4,.SQOFS(Q1)] ;Xmit offset
	ERJMP SERIAA		;Handle bad addresses
	XCTU [HRRZ Q2,.SQOFS(Q1)] ;Receive offset
	ERJMP SERIAA		;Handle bad addresses
	NOINT			;Be sure SCA completes with user interrupt
	SKIPN SNDFLG		;Are we doing a data send???
	JRST SNDREQ		;No, do a request data instead
	BLCAL. (SC.SND,<<.CBSCI(P1)>,T1,T3,T4,Q2>) ;Send the data
	 ITERR (<>,<OKINT>)	;Fail with error from SCA
	OKINT			;Allow user interrupts again
	RETSKP			;Return all well

SNDREQ:	BLCAL. (SC.REQ,<<.CBSCI(P1)>,T1,T3,T4,Q2>) ;Request the data
	 ITERR (<>,<OKINT>)	;Fail with code from SCA
	OKINT			;Allow user interrupts again
	RETSKP			;Return OK...

	ENDSV.
	SUBTTL Function code handlers -- Maint. data send/rec (SCSMDS/SCSMDR)

REPEAT 0,<

This function is now performed by the DIAG% JSYS

; This routine handles the sending/receiving of maintainance data.
;
; Expects:
;	T2/	User address of arg block
;
SCSMDS::TDZA T1,T1		;Clear the "receive" flag
SCSMDR::SETO T1,		;Set the "receive" flag
	STKVAR <RECFLG,SBA,RESADR,NODNUM>
	MOVEM T1,RECFLG		;Save value of the "recieve" flag
	MOVE Q1,T2		;Put user addr of args where it is safe
	UMOVE T1,.SQLEN(Q1)	;Get the claimed length of the arg block
	ERJMP SERIAA		;Handle bad addresses
	CAIGE T1,.LBMDS		;Is the block long enough???
	ITERR (SCSBTS)		;Nope, fail with block to short
	UMOVE T1,.SQMDT(Q1)	;Now for the target node number
	ITERR SERIAA		;Handle bad addresses
	CAILE T1,C%SYMX		;A reasonable node number???
	ITERR (SCSISB)		;Nope, fail here please
	MOVEM T1,NODNUM		;Save the node number
	SKIPN T1,SBLIST(T1)	;Is this a good node number???
	ITERR (SCSISB)		;Nope, fail please
	MOVEM T1,SBA		;Save the system block address for now
	SKIPN RECFLG		;Is this a send or receive???
	IFSKP.
	 UMOVE T1,.SQMSR(Q1)	;A receive, get the locally generated name
	 ERJMP SERIAA		;Address badness
	ELSE.
	 UMOVE T1,.SQMSS(Q1)	;A send, get locally generated name
	 ERJMP SERIAA		;Address is bad...
	ENDIF.
	MOVE T2,FORKX		;Get the owning fork number
	NOINT			;We are about to own a resource...
	MOVE T3,NODNUM		;Get the node number back again
	CALL SCSQMD		;Add entry to system maintainace data Q
	 ITERR (<>,<OKINT>)	;Fail with error from T1
	MOVEM T1,RESADR		;Store the address of the block for error recov

	MOVE T1,NODNUM		;Get the target node number again
	MOVX T2,.LBMDS		;Get the expected length of the block
	XCTU [HRLM T2,.SQLEN(Q1)] ;Store "words processed" for user
	ERJMP MDRERR		;Handle bad page access
	UMOVE T2,.SQMSS(Q1)	;Get the source buffer name
	ERJMP MDRERR		;Handle address badness
	UMOVE T3,.SQMSR(Q1)	;Get the destinaton buffer name
	ERJMP MDRERR		;Bomb on bad addresses please
	XCTU [HLRZ T4,.SQMOF(Q1)] ;Get the xmit offset
	ERJMP MDRERR
	XCTU [HRRZ Q1,.SQMOF(Q1)] ;   and the receive offset
	ERJMP MDRERR
	SKIPE RECFLG		;Is this a receive or send data???
	JRST MDRREC		;Its a receive, go do it
	BLCAL. (SC.SMD,<T1,T2,T3,T4,Q1,<.,SINMSC>>) ;Do the call please
	 JRST MDRER1		;Fail with code from SCA please
	OKINT			;Allow interrupts again
	RETSKP			;All went well... Return

;Here when this is a receive, not a send.
;
MDRREC:	BLCAL. (SC.RMD,<T1,T2,T3,T4,Q1,<.,SINMSC>>) ;Do the receive please
	 JRST MDRER1		;Fail with code from SCA please
	OKINT			;Allow interrupts again
	RETSKP			;All went well... Return.

; Here on errors:
;
; MDRERR -- Here when an ERJMP takes on failure to access user space
;		RESADR/	Address of block to take off system maint Q
;
; MDRER1 -- Here on a routine failure or detected error.
;		T1/	Error code to fail with
;		RESADR/	Address of block to remove from system Q
;
MDRERR:	MOVX T1,SCSIAB		;Get the error code to fail with for bad addr
MDRER1:	MOVE Q1,T1		;Save the liver
	MOVE T1,RESADR		;Get the addr of the maint block we just Q'ed
	CALL SCSDMD		;Remove it again
	CALL RELRES		;Release the space please
	OKINT			;Allow interrupts again
	MOVE T1,Q1		;Get the error code again
	RETBAD ()		;Fail with arranged error code pls...

	ENDSV.

> ; End REPEAT 0
	SUBTTL Function code handlers -- Start a remote system (SCSSRS)

REPEAT 0,<

These functions are now performed by the DIAG% JSYS

; This routine calls SCA to start a remote system.
;
; Expects:
;	T2/	Address of args in user space
;
SCSSRS::RETBAD (MONX04)


	SUBTTL Function code handlers -- Reset a remote system (SCSRRS)

; This routine calls SCA to reset a remote system.
;
; Expects:
;	T2/	Address of args in user space
;
SCSRRS::RETBAD (MONX04)

> ;End REPEAT 0
	SUBTTL Function code handlers -- Add interrupt channel (SCSAIC)

; This routine handles the add an interrupt channel function code.
;
; Expects:
;	T2/	User address of argument block
;
SCSAIC::STKVAR <ARGLEN,CHAN,CODE>
	MOVE Q1,T2		;Save the user address of arg block
	UMOVE T1,.SQLEN(Q1)	;Get the users arg length word
	ERJMP SERIAA		;Handle nasty page faults
	CAIGE T1,.SQLEN+1	;Is block long enough for at least one chan???
	ITERR (SCSBTS)		;No, fail with block to short
	SOS T1			;Make up for length word of arg block
	MOVEM T1,ARGLEN		;Save the block length

; Loop over the entries in the block
;
AICEL:	MOVE T2,ARGLEN		;Get the current pointer into the arg block
	ADD T2,Q1		;Get the user addr of the word we want
	UMOVE T1,(T2)		;Get the user arg word
	ERJMP SERIAA		;Handle a nasty page fault

; Note: If you change the channel number AC, be sure to change
;the XCT table (PSBPSI) as well since it assume T2 contains the channel number.
;
	HRRZ T2,T1		;Get the channel number
	CAIE T2,-1		;Is this a halfword -1 (Channel cancel)???
	CAIG T2,.ICLST		;Within range of reasonable PSI channels???
	$SKIP			;Yes, PSI channel is OK, go on
	ITERR (SCSIPC)		;No, fail with bad channel number
	HLRZS T1		;Now get just interrupt type code
	CAILE T1,.SIHGH		;It this within range of reasonable codes???
	ITERR (SCSIST)		;No, fail
	MOVEM T1,CODE		;Save the interrupt code
	MOVEM T2,CHAN		;Save the current channel number as well
	XCT PSBPSI(T1)		;Store the current channel in the PSB

; Check the interrupt code. If events are being turned on, add this fork to
;FRKTAB. If events are being turned off, remove fork from FRKTAB. For non-event
;interrupt codes ignore FRKTAB.

	CAIE T1,.SIPAN		;Are we doing events???
	JRST AICCON		;No, nothing needs to be done with FRKTAB
	MOVE T1,FORKX		;Get number of fork doing this SCS% JSYS
	CAIE T2,-1		;Are we disabling event interrupts???
	IFNSK.
	 CALL SCSAFT		;No, add this fork to FRKTAB
	 NOP			;We don't care if the bit was already lit
	ELSE.
	 CALL SCSRFT		;Yes, events being disabled, remove from FRKTAB
	 NOP			;We don't care if the bit was already off
	ENDIF.

AICCON:	SKIPN P1,SCSTCQ		;Are there any CB's that need to be updated???
	JRST AICELP		;No, try for another PSI code
	MOVE T1,CODE		;Be sure we have the right interrupt code
	MOVE T2,CHAN		;Get the channel number back
AICCBL:	XCT CBPSI(T1)		;Update the PSI code for this CB
	SKIPN P1,.CBJNB(P1)	;Is there another CB on this chain???
	JRST AICELP		;Nope, try for anothe PSI code
	JRST AICCBL		;Yes, loop to update the PSI code

AICELP:	SOSG ARGLEN		;Are there more words to do???
	RETSKP			;Nope, return
	JRST AICEL		;Yes, handle more PSI words pls...

	ENDSV.
	SUBTTL Function code handlers -- Accept connection (SCSACC)

; This routine handle the acceptance of a connection.
;
;	Call
;	T2/	Address of users argument block (in user space)
;
SCSACC::STKVAR <DATADR,DATRET,ERRCOD>
	MOVE Q1,T2		;Setup perm AC with addr of user args
	SETZM DATADR		;Zero address of user connection data
	XCTU [HRRZ T1,.SQLEN(Q1)] ;Get the length word of users arg block
	ERJMP SERIAA		;Bad address, handle smoothly
	CAIGE T1,.LBACC		;Is the block long enough for all data needed??
	ITERR (SCSBTS)		;Fail with a block too short
	UMOVE T1,.SQCID(Q1)	;Get the connect ID from the user
	ERJMP SERIAA		;Handle bad addresses
	CALL SC.CSC		;Is this a valid CID???
	 ITERR (SCSIID)		;Nope, bomb the JSYS here...
	UMOVE T1,.SQCDA(Q1)	;Get the address of the conn data
	ERJMP SERIAA		;It was bad afterall, fail pls...
	JUMPN T1,ACCDTA		;Handle user data
	NOINT			;Be sure ^C does not stop SCA call
	BLCAL. (SC.ACC,<T1,DATADR,[0],[0]>) ;Do the accept call
	 ITERR (<>,<OKINT>)	;Didnt make it, bomb with error from SCA
	OKINT			;Allow user interrupts again
	RETSKP			;Else return happy

ACCDTA:	NOINT			;SCSUDM allocates space, don't loose it
	CALL SCSUDM		;Move the user connect data to monitor space
	 ITERR (<>,<OKINT>)	;Fail please
	MOVEM T1,DATADR		;Save the address of the connect data
	MOVEM T2,DATRET		;  and thre address of the space return routine

	UMOVE T1,.SQCID(Q1)	;Get the connect ID from the user
	ERJMP ACCER1		;Handle bad addresses pls...
	CALL SC.CSC		;Is this a valid CID???
	IFNSK.
	 MOVX T1,SCSIID		;Get the error code
	 JRST ACCERR		;Handle the error please
	ENDIF.
	BLCAL. (SC.ACC,<T1,DATADR,[0],[0]>) ;Do the accept call
	 JRST ACCERR		;Handle the failure from SCA
	SKIPE T1,DATADR		;Get the data address back again
	CALL @DATRET		;Release the free space we obtained
	OKINT			;Allow user interrupts again
	RETSKP			;  and return all ok...

; Here on failure. We are NOINT and own resident free space.
;
; ACCER1
; No error code. We ERJMP here on a bad address or access failure.
;
; ACCERR
; T1/	Error code

ACCER1:	MOVX T1,SCSIAB		;We ERJMP here, report bad address
ACCERR:	MOVEM T1,ERRCOD		;Save the error code
	SKIPE T1,DATADR		;Get the data address back again
	CALL @DATRET		;Release the free space we obtained
	OKINT			;Allow user interrupts again
	MOVE T1,ERRCOD		;Get the error code back
	ITERR ()		;Report failure

	ENDSV.
	SUBTTL Function code handlers -- Get data queue entry (SCSGDE)

	RESCD			;We do CIOFF's here

; This routine handle getting entries from the data transfer complete queue.
;
; Expects:
;	T2/	User address of arg block
;
SCSGDE::STKVAR <BLKADR>
	MOVE Q1,T2		;Save address of user args
	XCTU [HRRZ T1,.SQLEN(Q1)] ;Get the length word from user
	ERJMP SERIAA		;Handle bad addresses
	CAIGE T1,.LBGDE		;Is the block long enough???
	ITERR (SCSBTS)		;Nope, fail with block to short
	UMOVE T1,.SQCID(Q1)	;Now get the users connect ID
	ERJMP SERIAA		;Handle bad addresses
	CAMN T1,[-1]		;Does he want the next for the fork???
	JRST GDEFRK		;Yes, go get an entry from the fork queue

	CALL SC.CSC		;We have a real CID, check it
	 ITERR (SCSIID)		;Bad CID, yell at the user
	NOSKED			;Don't allow queue changes
	SKIPN T1,.CBTXQ(P1)	;Is there anything on the queue for this CB???
	ITERR (SCSQIE,<OKSKED>) ;Nope, fail with empty queue

; Here when we have the address of a DMA complete buffer in T1.
;
GDEHBF:	XMOVEI T2,XFER		;Point to the block type
	CALL SCSDEQ		;Dequeue the buffer
	ITERR (<>,<OKSKED>)	;RETURN ERROR
	NOINT			;We can release queue interlock but
	OKSKED			;  we still own resources, hence to user inter
	MOVE T2,.DMNAM(T1)	;Get the buffer name from the block
	UMOVEM T2,.SQBID(Q1)	;Give it to the user
	ERJMP GDEERR		;Handle bad page writes
	CALL RELRES		;Return the free space we used
	OKINT			;Allow user interrupts again
	RETSKP

; Here when we want the next DMA for THIS fork.
;
GDEFRK:	NOSKED			;Interlock fork and CB queues for deletion
	SKIPN T1,SCSTXQ		;Is there anything on the fork queue???
	ITERR (SCSQIE,<OKSKED>) ;Nope, restore channel inter and yell at user
	$LDCID P1,.MECID(T1)	;Setup P1 with CB addr
	JRST GDEHBF		;Else go handle this buffer

; Here when an ERJMP takes while we have resources outstanding...
;
GDEERR:	XMOVEI T2,XFER		;Point to the block type
	CALL SCSLFQ		;Link it back onto the front of the queue
	JFCL 0			;SCSLFQ ALWAYS SKIP RETURNS
	OKINT			;Allow user interrupts again
	ITERR (SCSIAB)		;Now yell at user for bad addresses
	ENDSV.
	SUBTTL Function code handlers -- Get event Q entry (SCSEVT)

; This routine handles getting an entry from the event queue.
;
; Expects:
;	T2/	User address of arg block
;
SCSEVT::STKVAR <EVTADR,EVTCOD,USRLEN,ERRCOD>
	MOVE Q1,T2		;Save user arg addr in perm AC
	XCTU [HRRZ T1,.SQLEN(Q1)] ;Get the length word from the arg block
	ERJMP SERIAA		;Oops, bad arg address, back out pls...
	MOVEM T1,USRLEN		;Stash for later
	UMOVE T1,.SQCID(Q1)	;Get the users connect ID
	ERJMP SERIAA		;Handle a nasty page fault, if any
	CAMN T1,[-1]		;Are fork or connect events being requested???
	JRST EVTFRK		;Fork events being requested, go to it....
	CALL SC.CSC		;Is this a valid connect ID???
	 ITERR (SCSIID)		;No, fail JSYS with bad CID
	NOSKED			;Allow no further queue deletes until done here
	SKIPN T1,.CBTEQ(P1)	;Is there anything on the event queue
	ITERR (SCSQIE,<OKSKED>) ;No, fail with queue is empty
	JRST EVTPTE		;Yes, go process it

; Here to get event entries on a per fork basis.
;
EVTFRK:	NOSKED			;No queue deleteions until we get done here
	SKIPN T1,SCSTEQ		;Is there something on the forks event queue???
	ITERR (SCSQIE,<OKSKED>) ;No, return with Q is empty

;Here when we have the address of the event block we desire in T1. Please to
;process it...
;
EVTPTE:	MOVEM T1,EVTADR		;Save the address of the event block free space
	LOAD T3,EBLEN,(T1)	;Get the length field from the event block
	SUBI T3,.EBDAT		;Don't count the overhead area
	CAML T3,USRLEN		;Is there room in user block for this event???
	ITERR (SCSBTS,<OKSKED>) ;No, fail with block to short
	XMOVEI T2,EVT		;Point to the EVT list end pointers
	LOAD P1,MECID,(T1)	;Get the CID from the event block
	CAMN P1,[-1]		;Is this a special block (no CB for it)???
	IFNSK.
	 SETO P1,0		;Yes, a special block, show this to SCSDEQ
	ELSE.
	 $LDCID P1,P1		;Get the addr of the CB in question
	 LOAD T4,CBDNOD,(P1)	;Get the node number of remote on this connect
	 UMOVEM T4,.SQESB(Q1)	;Store remote node number for user
	 ERJMP EVTER1		;Handle non-writeable pages
	ENDIF.
	CALL SCSDEQ		;Remove this entry from the fork and CB queues
	ITERR (<>,<OKSKED>)	;RETURN ERROR
	NOINT			;We can allow queue deletes now but we
	OKSKED			;  own a resource, must not allow user inter
	LOAD T4,EBCOD,(T1)	;Get the event code from the block
	MOVEM T4,EVTCOD		;Save the event code
	UMOVEM T4,.SQEVT(Q1)	;Give the user the event code
	ERJMP EVTER1		;Handle non-writeable pages
	LOAD T4,MECID,(T1)	;Get the CID from the event block
	UMOVEM T4,.SQCID(Q1)	;Store for the user...
	ERJMP EVTER1		;Handle non-writeable pages
	LOAD T3,EBLEN,(T1)	;Get the length field from the event block
	SUBI T3,.EBDAT		;Dont count the overhead area
	JUMPE T3,EVTLST		;If there is no data to move, finish up
	MOVE T2,T1		;Source address for XBLT to user space
	MOVE T1,T3		;Set length of data to move
	MOVE T3,Q1		;Get destination address in user space
	ADDI T2,.EBDAT		;Offset source to just event data
	ADDI T3,.SQDTA		;Add offset to data area in user block
	CALL SCSDMU		;Move data to user space
	 JRST EVTERR		;Handle bad addresses from user

; See if we need to clean up after a connection termination event.
;
EVTLST:	JUMPL P1,EVTPNT		;If a special block, no CB to light bits in
	MOVE T1,EVTCOD		;Get the event code back

	CAIN T1,.SEPBC		;Did the VC get closed???
	CALL SCSCBD		;Yes, clean up JSYS connection data

	CAIE T1,.SERID		;Is this a remote disconnect???
	CAIN T1,.SECRR		;   or a connect rejection???
	CALL SCSCBD		;Yes, clean up JSYS connection data

EVTPNT:	MOVE T1,EVTADR		;Get the event address back
	CALL RELRES		;Release the space we used for the event block
	OKINT			;Allow user interrupts again
	RETSKP			;All done...

; Here for error recovery, I.E. when an ERJMP takes.
;
;	EVTER1 -- We are NOINT and EVTADR is the address of the event block.
;
EVTER1:	MOVX T1,SCSIAB		;Get error code for bad address
EVTERR:	MOVEM T1,ERRCOD		;Save the error code
	MOVE T1,EVTADR		;Get the address of the event block
	XMOVEI T2,EVT		;Show addr of four word list header block
	CALL SCSLFQ		;Link it back onto the front of the queue
	IFNSK.
	 BUG. (INF,SCSQEB,SCSJSY,SOFT,<SCSJSY: Event block linking failure>,<<T1,ERRCOD>,<FORKX,CURFRK>,<JOBNO,CURJOB>>,<

Cause:	After detecting an error while fetching an event for the user, we 
	failed to relink the event back onto the event queue.

Action:	None. This will not hang the fork, and when the fork goes away so does
	the problem. When you see this BUGINF, the offending fork has lost
	an event...
>)
	 MOVE T1,EVTADR		;Get the address of the event block
	 CALL RELRES		;Resturn the free space
	ENDIF.
	OKINT			;Restore user interrupts
	MOVE T1,ERRCOD		;Get the error code again
	ITERR ()		;Now go yell at the user

	ENDSV.
	SUBTTL Function code handlers -- Get local node number (SCSGLN)

; This function code returns the local node number.
;
; Expects:
;	T2/	Address of user args
;
SCSGLN::MOVE Q1,T2		;Put user arg addr in perm AC
	XCTU [HRRZ T1,.SQLEN(Q1)] ;Get the length from user
	ERJMP SERIAA		;Handle bad addresses
	CAIGE T1,.LBGLN		;Is the block long enough???
	ITERR (SCSBTS)		;No, fail with block to short
	NOINT			;Allow SCA to finish
	CALL SC.PRT		;Get the local port number
	 ITERR (SCSNKP,<OKINT>)	;No KLIPA on this system, fail please
	OKINT			;SCA is finshed
	UMOVEM T1,.SQLNN(Q1)	;Tell the user who we are
	ERJMP SERIAA		;Handle unwritable pages
	RETSKP			;All done return to user
	SUBTTL Function code handlers -- Return buffer sizes (SCSRBS)

; This routine returns the minimum sizes for message and datagram buffers.
;
; Expects:
;	T2/	Addr of user args
;
SCSRBS::MOVE Q1,T2		;Save user arg address in perm AC
	XCTU [HRRZ T1,.SQLEN(Q1)] ;Get the length from the user
	ERJMP SERIAA		;Handle bad addresses
	CAIGE T1,.LBRBS		;Is the block long enough???
	ITERR (SCSBTS)		;No, fail with block to short
	MOVX T1,<C%MGSZ-.MHUDA>	;Get the size of a buffer
	UMOVEM T1,.SQLMG(Q1)	;Tell user what size messages should be
	ERJMP SERIAA		;Handle unwritable areas
	MOVX T1,<C%DGSZ-.MHUDA>	;Get the size of datagram buffers
	UMOVEM T1,.SQLDG(Q1)	;Tell user about DG lengths
	ERJMP SERIAA		;Handle bad addresses
	RETSKP			;All done
	SUBTTL Function code handlers -- Return path status info (SCSRPS)

; Routine to return information about the status of a wire to a particular
;remote.
;
;	Expects:
;	T2/	Address of user arguments.
;
SCSRPS::XCTU [HRRZ T1,.SQLEN(T2)] ;Get the length of the users arg block
	CAIGE T1,.LBRPS		;Is it long enough???
	ITERR (SCSBTS)		;No, fail with block to short
	UMOVE T1,.SQRPN(T2)	;Get target node number
	ERJMP SERIAA		;Handle bad user arg addresses
	SKIPL T1		;Is node number negative???
	CAILE T1,MAXNDS		;  or to big???
	ITERR (SCSISB)		;Invalid node number
	LOAD T3,IDPAO,(T1)	;Get the status of path A
	XCTU [STOR T3,SQRPA,(T2)] ;Store path status for the user
	ERJMP SERIAA		;Handle bad addresses
	LOAD T3,IDPBO,(T1)	;Get the status of path B
	XCTU [STOR T3,SQRPB,(T2)] ;Stash the info for the user
	ERJMP SERIAA		;Handle bad addresses
	RETSKP			;All done, return
	SUBTTL	SCS% error handlers

	SUBTTL	SCS% error handlers -- A2MHLT (PSB mapping bug)

A2MHLT:	 BUG. (HLT,SCSA2M,SCSJSY,SOFT,<SCSJSY: Attempt to map second PSB>,<<MPSFRK,OWNFRK>,<T1,CURFRK>>,<

Cause:	Some routine mapped a PSB but did not release it, or did not use the 
	correct interlock. The net result was that we are trying to map another
	PSB while we still have the first one mapped. MPSFRK contains the 
	number of the fork that did the first map, T1 ontains the fork doing 
	the second lock.
>)
	SUBTTL	SCS% error handlers -- SERIAA (Invalid argument block address)

; This routine is called when the user supplied argument block address is
;bad. This handy label exists as a conveniant place to ERJMP/ERCAL after 
;various instructions that touch the users arg block.
;
SERIAA::ITERR (SCSIAB)		;Return to user with illegal instruction trap

	SUBTTL	SCS% error handlers -- SERIBP (Invalid byte pointer)

; Here when an ERJMP after a byte instruction takes. This is the destination
;address for any ERJMP in SCSJSY that follows a byte instruction. Simply
;return with an illegal instruction trap...
;
SERIBP:	ITERR (SCSIBP)		;Return to user with invalid byte pointer
	SUBTTL SCA call handlers -- Main entry point

	RESCD			;Called at interrupt level

; This routine is the entry point to the SYSAP from SCA.
;Note that this code is not in process context, but rather is at interrupt
;level for the CI.
;
SCSINT::SAVEP			;Save the ACs we will kill here
	JRST @CALTAB(T1)	;Goto the handler routine for SCA calls


	SUBTTL SCA call handlers -- SCSONT (Handle online/offline interrupts)

; This routine handles anline/offline interrupts from SCA. We are only called
;here when SCAMPI is scanning NOTTAB for addresses to notify. Hence we get 
;called here once per system and with no connect ID.
;
; Expects
;	T1/	.SSPBC or .SSNCO
;
;	T3/	Node number for .SSPBC
;	  or
;	T2/	Node number for .SSNCO
;
;	Return (+1) Always
;	No data returned
;
SCSONT:	TRVAR <NOD,CODE,FORK>
	CAIN T1,.SSPBC		;Did the node go offline???
	MOVX T4,.SEPBC		;Yes, get the event code for node offline
	CAIN T1,.SSNCO		;Did the node come online???
	IFNSK.
	  MOVX T4,.SENCO	;Yes, get the event code for node online
	  MOVEM T2,T3		;Reposition node number
	ENDIF.
	MOVEM T4,CODE		;Save the code we must give the user
	MOVEM T3,NOD		;Save the node that just changed state
	
; Loop over FRKTAB looking for forks the need to be poked.

	SETO T1,0		;Make the AOS get fork zero
ONTLOP:	AOS T1			;Increment to the next fork
	CALL SCSGNF		;Get the next fork with PSI's enabled
	 RET			;No more forks enabled, all done
	CALL ONTBLD		;Build and queue a block for this fork
	 $SKIP			;Failed, BUGCHK and loop again
	JRST ONTLOP		;All went OK, loop for more forks

	BUG. (CHK,SCSPBF,SCSJSY,SOFT,<SCSJSY: PSI block build failure>,<<T1,ERRCOD>>,<

Cause:	The routine to build an event block failed. The error code is in T1.
	It is very likely that ASGRES did not have the space available.
>,ONTLOP)

; ONTBLD
;
; This routine is a support routine for SCSONT. It builds an online/offline
;event block and queues it.
;
; Usage:
;	Call
;	T1/	Fork number
;	CODE/	Event code to use
;	NOD/	Node number that changed state
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Fork number
;
ONTBLD:	MOVEM T1,FORK		;Save the fork we are building for
	MOVX T1,<XWD .RESP2,C%PBCL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get space for the event block
	 RETBAD ()		;Handle error, note - call does not return here
	MOVE T2,FORK		;Get the fork number
	STOR T2,MEFRK,(T1)	;Store the target fork number
	SETONE MECID,(T1)	;Set CID to special case. 
	MOVX T2,C%PBCL		;Get the block length
	STOR T2,EBLEN,(T1)	;Store the event length
	MOVE T2,CODE		;Now the event code
	STOR T2,EBCOD,(T1)	;Store the event code
	MOVE T2,NOD		;Get the target node number
	MOVEM T2,.EBDAT(T1)	;Store the desired data
	MOVX T2,.ETEVT		;Get the entry type for events
	CALL SCSQUE		;Queue the block and return
	MOVE T1,FORK		;Get the fork number back again
	RETSKP			;All done, all went well

	ENDTV.
	SUBTTL SCA call handlers -- SINDGR

; This routine handles a message/datagram receive.
;
; Expects:
;	T1/	.SSDGR
;	T2/	Connect ID
;	T3/	Packet address
;	T4/	Flags from the port driver
;
SINDGR::TDZA T1,T1		;Show we are not doing messages
SINMGR::SETO T1,		;Set the flag that says we are doing datagrams
	SAVEAC <Q1>		;Save the CB addr AC
	MOVX Q1,.ETMSG		;Assume we are doing messages
	SKIPN T1		;Was this a good assumption???
	MOVX Q1,.ETDG		;No, set up for datagrams

	MOVE T1,T3		;Put the datagram buffer address in right place
	MOVE P1,.MHPKL(T1)	;Get the packet length
	STOR P1,MELEN,(T1)	;Store the packet length in the header
	STOR T2,MECID,(T1)	;Store the CID in the block
	$LDCID P1,T2		;Get the CB address
	LOAD T3,CBFORK,(P1)	;Get owning fork number
	STOR T3,MEFRK,(T1)	;Store in the block
	ANDX T4,C%FLGM		;Be sure we have just the flags
	IORM T4,.METYP(T1)	;Store in block for SYSAP
	MOVE T2,Q1		;Name the block type
	CALLRET SCSQUE		;Queue it for the fork and return
	SUBTTL SCA call handlers -- SINDMA

; This routine handles the DMA operation complete callback from SCA.
;
; Expects:
;
;	T2/	Connect ID of connection over which DMA has been completed
;	T3/	32 bit buffer name of DMA buffer
;
SINDMA::STKVAR <CID,BUFNAM>
	MOVEM T2,CID		;Save the CID
	MOVEM T3,BUFNAM		;Save the buffer name for later...
	MOVX T1,<XWD .RESP2,.DMLEN> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get space for the event block
	 CALL SCSAER		;Handle error, note - call does not return here
	MOVE T2,CID		;Get the CID back
	STOR T2,MECID,(T1)	;Store in DMA block
	MOVE T2,BUFNAM		;Get the buffer name
	MOVEM T2,.DMNAM(T1)	;  which goes in the block as well...
	MOVX T2,.DMLEN		;Get the length of this block
	STOR T2,MELEN,(T1)	;  which also goes into the block
	MOVX T2,.ETDMA		;Get the block type
	CALLRET SCSQUE		;Queue the buffer and return

	ENDSV.

REPEAT 0,<
	SUBTTL SCA call handlers -- SINMSC

; Here to handle SCA having a mainatainace data complete for us...
;
; Note: This routine expects to always run at interrupt level.
;
; Expects:
;
;	T2/	Buffer name for completed transfer
;
SINMSC:	SAVEAC <Q1>
	SKIPN T1,SCATMQ		;Is there anything on the queue???
	JRST MSCERR		;Nope, yell and then ignore the whole mess
MSCLOP:	CAMN T2,.MQBUF(T1)	;Is this the entry we are looking for???
	JRST MSCOK		;Yes, go process it please
	SKIPE T1,.MQNXT(T1)	;Is there a next entry???
	JRST MSCLOP		;Can't find an entry for this name...
	JRST MSCERR		;Nope, die here please...

; Here when we have found the entry we are looking for.
;
; Expects:
;	T1/	Base address of the entry we desire
;	T2/	Buffer name for completed xfer
;
MSCOK:	MOVE P1,.MQCBA(T1)	;Setup the CB address
	MOVE Q1,.MQBUF(T1)	;Save the address of the entry
	CALL SCSDMD		;Dequeue this entry from the maint Q
	 JRST MSCER1		;Handle bizzare failures
	CALL RELRES		;Return the entry's free space

	MOVX T1,<XWD .RESP2,C%MDCL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get space for the event block
	 CALL SCSAER		;Handle ASGRES failure
	MOVEM Q1,.EBDAT(T1)	;Store the buffer name in event entry
	LOAD T2,CBSCID,(P1)	;Get the "CID" from this connect
	STOR T2,MECID,(T1)	;Store in event entry
	MOVX T2,.ETEVT		;Get the entry code for events
	STOR T2,METYP,(T1)	;Store the event type in the block
	MOVX T2,C%MDCL		;Get the length of the block
	STOR T2,EBLEN,(T1)	;Store in the event block
	MOVX T2,.SEMDC		;The event code
	STOR T2,METYP,(T1)	;   which goes in the event block as well
	SETZRO MEFLG,(T1)	;Zero the flags
	CALLRET SCSQUE		;Place this entry on the appropriate q's

; Here when we cannot find the buffer name SCA has just given us...
;
MSCERR:	BUG. (CHK,SCSCFB,SCSJSY,SOFT,<SCSJSY: Can't find maintainance buffer name>,<<T2,BFRNAM>>,<

Cause:	SCA called us with a buffer name for a completed maintainance data 
	transfer. Upon trying to find this name in the database of outstanding 
	JSYS maint xfers, no match was found. The fork which performed the 
	maint data xfer request will never be notified of the completion of 
	the request.

Action: Try to find the buffer name in the port driver database of BSD/BHD's
	and see if this is a valid buffer name. If so, check the consistancy
	of the SCA maint Q. Other than this, not much can be done...
>)
	RETBAD ()


; Here on a bizzare failure.
;
; Expects:
;	T1/	Error code
;
MSCER1:	BUG. (CHK,SCSBIZ,SCSJSY,SOFT,<SCSJSY: Maintainance data transfer Q smashed>,<<T1,ERRCOD>>,<

Cause:	A routine which should not return (+1), did. The error code is in T1.
>)
	RETBAD ()
>				;End of REPEAT 0
	SUBTTL SCA call handlers -- SINPBC

;This routine handle the port breaking a connection. We get here from SCA once
;per connection.
;
SINPBC::STKVAR <CID>
	MOVEM T2,CID		;Save the connect ID
	$LDCID P1,T2		;Get the CB address from the CID
	MOVX T1,<XWD .RESP2,C%PBCL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get space for the event block
	 CALL SCSAER		;Handle error, note, call does not return here
	MOVX T2,.SEPBC		;Get the event type
	STOR T2,EBCOD,(T1)	;Store the event code in the event block
	MOVX T2,C%PBCL		;Get the length of this block
	STOR T2,EBLEN,(T1)	;Store this in the block as well
	MOVE T2,CID		;Get the connect ID
	STOR T2,MECID,(T1)	;  and store this in the block as well
	MOVX T2,.ETEVT		;Name the entry type
	CALLRET SCSQUE		;Queue the event block and return

	ENDSV.
	SUBTTL SCA call handlers -- SINCTL

;This routine handles a connect to our listen.
;
;	Call
;	T1/	.SSCTL
;	T2/	Connect ID
;	T3/	Pointer to connection data from remote system
;
; Build the event queue entry, place it on the event queue for the connection,
;and interrupt the owning fork if need be...
;
SINCTL::STKVAR <CID,CDATA>
	MOVEM T2,CID		;Save the connect ID
	MOVEM T3,CDATA		; and the connection data
	MOVX T1,<XWD .RESP2,C%CTLL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get some swapable space for it
	 CALL SCSAER		;Handle error, note, call does not return here
	MOVX T2,.SECTL		;Get the event code type
	STOR T2,EBCOD,(T1)	;Store the event code in the block
	MOVX T2,C%CTLL		;Now get the length of the block
	STOR T2,EBLEN,(T1)	;Store this in the blcok as well
	MOVE T2,CDATA		;Get the pointer to connection data back
	MOVE T3,T1		;Make the block address the destination addr
	PUSH P,T3		;Save this address
	ADDI T3,.EBDAT		;Add the offset to the data portion
	MOVX T1,<<C%DTAL+3>/4>	;Get the number of words to move
	EXTEND T1,[XBLT]	;Move the connection data into the event block
	POP P,T1		;Get the address of the block back
	MOVE T2,CID		;Get the CID back again
	STOR T2,MECID,(T1)	;Get back and store the connect ID
	SETZM (T1)		;Insure we are calling this the Q end
	MOVX T2,.ETEVT		;Name the block type
	CALLRET SCSQUE		;Queue the event block and return

	ENDSV.
	SUBTTL SCA call handlers -- SINCRA

; This routine handles a connect response becoming available.
;
;	Call
;	T1/	.SSCRA
;	T2/	Connect ID
;	T3/	-1 for accepted, 0 for rejected
;	T4/	Reject reason or pointer to connection data
;
; Determine which event block need to be built (based on whether connect was
;accepted or rejected) and add the block to the event queue. If this is the
;first entry on the queue, then cause a PSI interrupt for the owning fork.
;
SINCRA::STKVAR <CID,RJTADR>
	MOVEM T2,CID		;Save the connect ID
	MOVEM T4,RJTADR		;  and the reject code
	JUMPE T3,CRAREJ		;Handle the rejected connections...
	MOVX T1,<XWD .RESP2,C%CRAL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get the event block from swapable free space
	 CALL SCSAER		;Handle error, note, call does not return here
	MOVX T2,C%CRAL		;Get the length of the block
	STOR T2,EBLEN,(T1)	;Store the length in the block
	MOVX T2,.SECRA		;Get the event code
	STOR T2,EBCOD,(T1)	;Store this in the block as well
	MOVE T3,CID		;Get the connect ID back, and into the block
	STOR T3,MECID,(T1)	;Store in the event block
	MOVE T3,RJTADR		;Get the pointer to connection data back
	MOVEM T1,T4		;Set up the XBLT destination address
	ADDI T4,.EBDAT		;Offset it to the data part of the block
	MOVX T2,<<C%DTAL+3>/4>	;Number of words of connect data to move
	EXTEND T2,[XBLT]	;Move the connect data to the block
	MOVX T2,.ETEVT		;Name the block type
	CALLRET SCSQUE		;Queue the event block and return

;Here to handle the rejected connections...
;
CRAREJ:	MOVX T1,<XWD .RESP2,C%CRRL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get the swapable space for the event block
	 CALL SCSAER		;Handle error, note, call does not return here
	MOVE T2,RJTADR		;Get the CID again
	MOVEM T2,.EBDAT(T1)	;Restore and place the reject reason in block
	MOVX T2,.SECRR		;Get the event code for what we just saw
	STOR T2,EBCOD,(T1)	;Store in the event block
	MOVX T2,C%CRRL		;Get the length of the event block
	STOR T2,EBLEN,(T1)	;Store that as well
	MOVE T2,CID		;Get the connect ID back
	STOR T2,MECID,(T1)	;Store the connect ID in the block
	MOVX T2,.ETEVT		;Name the block type
	CALLRET SCSQUE		;Queue the event block and return

	ENDSV.
	SUBTTL SCA call handlers -- SINOSD

; This routine handles the SCA callback that tells SYSAPs a connection
;is now fully open and data may be sent.
;
; Build an event block, queue it, and cause an interrupt if need be...
;
SINOSD::STKVAR <CID>
	MOVEM T2,CID		;SAve the connect ID
	MOVX T1,<XWD .RESP2,C%OSDL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get some space for the block
	 CALL SCSAER		;Handle error, note, call does not return here
	MOVE T2,CID		;Get the CID again
	STOR T2,MECID,(T1)	;Pop the connect ID into the event block
	MOVX T2,.SEOSD		;Get the event code
	STOR T2,EBCOD,(T1)	;Store in the event block
	MOVX T2,C%OSDL		;And the length of the event block
	STOR T2,EBLEN,(T1)	;   gets stored in the block as well
	MOVX T2,.ETEVT		;Name the event type
	CALLRET SCSQUE		;Queue the event block and return

	ENDSV.
	SUBTTL SCA call handlers -- SINRID

; The routine handles a remote disconnecting from one of the JSYS connections.
;
; This is the other case where the event queue is flushed and only this entry
;is left on it. When we return from this routine, SCA will wipe out the data
;about this connection. Hence all other entries on the event queue are 
;worthless...
;
SINRID::STKVAR <CID>
	MOVEM T2,CID		;Save the connect ID
	$LDCID P1,T2		;Get the CB address from the CID
	MOVX T1,<XWD .RESP2,C%RIDL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Assign swapable space for the block
	 CALL SCSAER		;Handle error, note, call does not return here
	MOVX T2,.SERID		;Get the event type
	STOR T2,EBCOD,(T1)	; and store it in the event block
	MOVX T2,C%RIDL		;Get the length of the block
	STOR T2,EBLEN,(T1)	; which goes in the block as well
	MOVE T2,CID		;Get the connect ID back again
	STOR T2,MECID,(T1)	; which goes in the block too
	MOVX T2,.ETEVT		;Name the event type
	CALLRET SCSQUE		;Queue the event block and return

	ENDSV.
	SUBTTL SCA call handlers -- SINCIA

; This routine handles the "credit is available" callback...
;
; Build an event block, queue it, and cause a PSI interrupt if one is required.
;
SINCIA::STKVAR <CID,RCDT,SCDT>
	MOVEM T2,CID		;Save the connect ID
	MOVEM T3,SCDT		; and the send credit
	MOVEM T4,RCDT		; and the receive credit
	MOVX T1,<XWD .RESP2,C%CIAL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get that much swapable space for the block
	 CALL SCSAER		;Handle error, note, call does not return here
	MOVX T2,.SECIA		;Get the event code
	STOR T2,EBCOD,(T1)	;Store the event type in the block
	MOVX T2,C%CIAL		;Get the length of this block
	STOR T2,EBLEN,(T1)	;Store this in the block as well
	MOVE T2,CID		;Get the CID again
	STOR T2,MECID,(T1)	; and store it in the event block
	MOVE T2,SCDT		;Get the send credit back again
	MOVEM T2,.EBDAT(T1)	;Store as the first word of data
	MOVE T2,RCDT		;Get the receive credit again
	MOVEM T2,.EBDAT+1(T1)	;Store this as the second word of data
	MOVX T2,.ETEVT		;Name the event type
	CALLRET SCSQUE		;Queue the event block and return

	ENDSV.
	SUBTTL SCA call handlers -- SINPSC

;This routine handles a packet send complete.
;
SINPSC::STKVAR <CID,BUFADR>
	MOVEM T2,CID		;Save the connect ID
	MOVEM T3,BUFADR		;And the buffer address
	$LDCID P1,T2		;Get the CB address
	MOVX T1,CBFRAP		;Get the reap bit
	TDNE T1,.CBFLG(P1)	;Is this an aborted conenction???
	JRST PSCABT		;Yes, handle an aborted connect

	MOVX T1,<XWD .RESP2,C%MSCL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get the space required for the block
	 CALL SCSAER		;Handle error, NOTE, CALL DOES NOT RETURN HERE!
	MOVX T2,.SEMSC		;Get the event type
	STOR T2,EBCOD,(T1)	;Store it in the block
	MOVX T2,C%MSCL		;And get the length of the block
	STOR T2,EBLEN,(T1)	;Which also goes in the block
	MOVE T2,CID		;Get the pertinant connect ID
	STOR T2,MECID,(T1)	;Which goes in the block...
	MOVE T3,BUFADR		;Get the buffer address again
	MOVE T2,.JHAUB(T3)	;Get the address of the user buffer completed
	MOVEM T2,.EBDAT(T1)	;Store user addr of packet text in EVT block
	MOVX T2,.ETEVT		;Name the event type
	CALL SCSQUE		;Queue the event block

; Here directly from above when the connection has been aborted...
;
PSCABT:	MOVE T1,BUFADR		;Get the monitor buffer address again
	CALLRET SCSRET		;Return the buffer please

	ENDSV.
	SUBTTL SCA call handlers -- SINLCL

; This routine handles little credit being left.
;
; Build an event block, queue it, and check if an interrupt is necessary...
;
SINLCL::STKVAR <CID,SHRTBF>
	MOVEM T2,CID		;Save the connect ID
	MOVEM T3,SHRTBF		;Save the number of buffers needed
	MOVX T1,<XWD .RESP2,C%LCLL> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get the space required for the event block
	 CALL SCSAER		;Handle error, note, call does not return here
	MOVX T2,.SELCL		;Get the event code
	STOR T2,EBCOD,(T1)	;Store in the block
	MOVX T2,C%LCLL		;Now the length of the block
	STOR T2,EBLEN,(T1)	;Which goes in the block too...
	MOVE T4,SHRTBF		;Get the number of needed buffers back
	MOVEM T4,.EBDAT(T1)	;Put the number of buffers required in the blk
	MOVE T4,CID		;Get the CID back
	STOR T4,MECID,(T1)	;  and store it in the event block header
	MOVX T2,.ETEVT		;Name the event type
	CALLRET SCSQUE		;Queue the event block and return

	ENDSV.

REPEAT 0,<
	SUBTTL SCA call handlers -- SINNWO

;Here to handle the callback for node went offline.
;
; Usage:
;	Call
;	T1/	.SSNWO
;	T2/	CID for your connect to remote
;
;  Note that if you have more than one connect to the remote that has gone
;away, you get one callback for each connect.
;
SINNWO::STKVAR <CID>
	MOVEM T2,CID		;Save CID of dead connect
	MOVX T1,<XWD .RESP2,C%NWOL> ;Priority,,length of free space block
	MOVX T2,.RESGP		    ;  from the general pool please
	CALL ASGRES		;Get free space for event block
	 CALL SCSAER		;Handle error, note we dont come back from call
	MOVX T2,C%NWOL		;Get the length of this block
	STOR T2,EBLEN,(T1)	;Store in event block
	SETZRO MEFLG		;No flags for this one
	MOVE T2,CID		;Get the connect ID again
	STOR T2,MECID,(T1)	;Store CID in event block
	MOVX T2,.ETEVT		;Get the block type
	CALLRET SCSQUE		;Queue the block and return

	ENDSV.
>				;End of REPEAT 0
	SUBTTL Support routines -- SCSDMU (Data monitor to user)

; This routine moves data from monitor space into user space. Checks are made
;to insure that the page we will write to is accessable to the monitor. 
;
;Note:
; This routine assumes the data being moved is less than one page long. If the
;length is more than one page, the page access checks will be wrong...
;
; Usage:
;	Call
;	T1/	Length in words of block to move
;	T2/	Source address in monitor space
;	T3/	Destination adddress in user space
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	No data returned
;
SCSDMU:	XCTU [MOVES (T3)]	;Try to write the page
	ERJMP DMUER1		;Failed, back out pls...
	MOVE T4,T3		;Get the base address in user space
	ADD T4,T1		;Build the last address we move things into
	XCTU [MOVES (T4)]	;Can we write the end of the block???
	ERJMP DMUER1		;Nope, back out
	CALL BLTMU		;Move the data into user space
	RETSKP			;All done, return success

; Here when an ERJMP takes on page access failure.

DMUER1:	RETBAD (SCSIAB)		;Fail with error code please...
	SUBTTL Support routines -- SCSRUB (Remove user buffer)

	RESCD

; This routine removes a user buffer entry from the BSD set for a connection.
;
; Usage:
;	T1/	User buffer address entry to be removed
;	T2/	Offset into BSD for buffer type
;	P1/	Address of CB
;
;	Return (+1)
;	No entry found
;
;	Return (+2)
;	Entry has been removed
;
SCSRUB:	CIOFF			;Do this alone please
	SKIPN T3,.CBTBQ(P1)	;Is there a BSD on this connection???
	JRST RUBERR		;Nope, return failure to find buffer entry
	SAVEAC <Q1>
	SETZ Q1,		;Be sure initial back pointer is zero
	MOVE T4,T3		;Build offset to first buffer
	ADD T4,T2		;    by adding offset to base address of BSD
	SKIPN T4,(T4)		;Is there an address of the first entry
	JRST RUBNXB		;Nope, try the next BSD
RUBCLP:	CAMN T1,.BBUVA(T4)	;Is this the entry we want???
	JRST RUBWIN		;Yes, put the entry on the free queue and ret
	MOVE Q1,T4		;Save the address of the current buffer
	SKIPE T4,.BBNXT(T4)	;Is there a next entry in this BSD
	JRST RUBCLP		;Yep, loop for it pls...

; Here to do the next BSD.

RUBNXB:	SKIPN T3,.BDNXT(T3)	;Is there a next BSD???
	JRST RUBERR		;Nope, we loose, no entry found
	MOVE T4,T3		;Build the addr of the queue FLINK
	ADD T4,T2		;  by adding the offset to the base address
	SKIPN T4,(T4)		;Is there a first entry here???
	JRST RUBNXB		;Nope, get the next BSD pls...
	SETZ Q1,		;Be sure back pointer is zero
	JRST RUBCLP		;Yup, check it pls...

; Here when an entry matches. Delete it from the queue its currently on, and
;add it to the free list.
;
; Expects:
;	T2/	Offset into BSD for queue FLINK
;	T3/	Base addr of BSD
;	T4/	Addr of current entry (one to be deleted)
;	Q1/	Address of previous entry

RUBWIN:	SETZM .BBUVA(T4)	;Zero the user virtual address
	SKIPN Q1		;Is there a previous entry???
	SKIPE T1,.BBNXT(T4)	;   or a next entry???
	TRNA			;Yes, queue is not empty
	JRST RUBQIE		;No previous or next, queue is empty
	ADD T2,T3		;Build the address of the queue FLINK
	SKIPN Q1		;Is there a previous entry???
	MOVEM T1,(T2)		;No previous, update FLINK to show new first
	SKIPN T1		;Is there a next entry???
	MOVEM Q1,.BDF2B(T2)	;No next, update BLINK for new last entry
	SKIPE Q1		;Is there a previous entry???
	MOVEM T1,.BDNXT(Q1)	;Yes, point it to the next entry

; Here when the buffer has been deleted from the appropriate queue and is now
;ready to be added to the free queue.

RUBMBF:	MOVEM T4,@.BDLFD(T3)	;Link this buffer onto the free queue
	MOVEM T4,.BDLFD(T3)	;  and update the BLINK
	CION			;Restore CI interrupts
	RETSKP			;  and return all OK

; Here when we are deleting the last entry on a BSD queue.

RUBQIE:	MOVE T1,T3		;Get base addr of BSD
	ADD T1,T2		;Build it into address of queue FLINK
	MOVEM T1,.BDF2B(T1)	;Store FLINK address in BLINK
	SETZM (T1)		;Init FLINK as zero
	JRST RUBMBF		;Now go put the entry on the free queue

; Here on an error exit

RUBERR:	CION			;Restore the channel
	RET			;   and fail return
	SUBTTL Support routines -- SCSSUM (Move string from user to monitor)

	SWAPCD			;These routines need not be resident

; This routine moves a string from user space to monitor space. It handles
;the odd cases of the addresses or byte pointer being bad. It also allows
;the byte pointer from the user to be a generic byte pointer.
;
; Usage:
;	Call
;	T1/	Byte pointer to string in user space
;	T2/	Byte pointer to destination string in monitor space
;	T3/	Max length of the string (in bytes) or zero.
;		  If zero -- No max length, string terminates on null byte
;		  If non-zero -- String terminates on this length or null byte
;			and the string is padded to this length with spaces
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Updated user byte pointer
;	T2/	Updated monitor byte pointer
;
SCSSUM:	SAVEAC <Q3>
	MOVE Q3,T3		;Save max length of string
	TLC T1,-1		;Compliment left half bits to determine if
	TLCN T1,-1		;  this a generic byte pointer???
	HLL T1,[POINT 7,]	;Yes, fill in a real byte pointer left half
SUMLOP:	XCTBU [ILDB T4,T1]	;Get a byte from the user string
	ERJMP SUMBER		;Handle bad instructions
	JUMPE T4,SUMFIL		;Null byte, see if any padding is needed
	IDPB T4,T2		;Load byte into monitor space string
	JUMPE Q3,SUMLOP		;If no initial count then loop until null byte
	SOJG T3,SUMLOP		;Have a byte count, loop if more bytes allowed
	RETSKP			;No more bytes allowed, hence no padding to do

; Here on a null source byte. Handle any padding as needed.

SUMFIL:	JUMPE Q3,RSKP		;If no initial count, then no padding needed
	JUMPE T3,RSKP		;Return if no padding required
	MOVX T4," "		;Get the pad character (space)
	IDPB T4,T2		;Add the pad character to the string
	SOJG T3,.-1		;Keep adding padding chars as needed
	RETSKP			;No more padding needed, return

; Here when the ERJMP on the byte instruction takes...

SUMBER:	RETBAD (SCSIBP)		;Return saying bad byte pointer
	SUBTTL Support routines -- SCSDUM (Move data from user to monitor)

; This routine moves a block of words from user space into monitor space.
;Checks on the validity of the user address specified are done.
;
; Usage:
;	Call
;	T1/	Length in words of block
;	T2/	Address of source block in user space
;	T3/	Address of destination block in monitor space
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	No data, block has been moved
;
; *** Warning ***
; This routine assumes that the length of the data block being moved is less 
;than one page. If the block is longer than one page the checks for page
;accesability are wrong...
;
SCSDUM:	XCTU [SKIP (T2)]	;Does the user address exist???
	ERJMP DUMERR		;Nope, yell about a bad address
	MOVE T4,T2		;Get the base user address of the block
	ADD T4,T1		;Calculate end address of user block
	XCTU [SKIP (T4)]	;Does the whole block exist???
	ERJMP DUMERR		;Nope, handle this as well
	CALL BLTUM		;Move data words from user to monitor
	RETSKP			;Data moved, all done

; Here when the ERJMP taks on a bad address

DUMERR:	RETBAD (SCSIAB)
	SUBTTL Support routines -- SCSCUB (Count user buffers)

; This routine will chase a buffer chain and count the number of buffers
;in it. It will also validate the start and end addresses of the buffers in
;the chain.
;
; Note:
;	This routine always assumes that we are counting buffers for the
;current fork. USECTB is checked to be sure that the section the buffers
;are in exists.
;
; Usage:
;	Call
;	T1/	Address of first user buffer
;	T2/	Size of each buffer in the chain
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Address of first buffer
;	T2/	Size of each buffer in chain
;	T3/	Count of buffers in chain
;
SCSCUB:	SAVEAC <Q1,Q2>
	SETZ T3,		;Zero the buffer count
	MOVE Q1,T1		;Start with the first buffer

CUBLOP:	TXNE Q1,SECMSK		;Are there any bad section bits on???
	JRST CUBERR		;Yes, fail
	HLRZ T4,Q1		;Get the section part of buffer address
	SKIPN USECTB(T4)	;Does this section exist???
	JRST CUBERR		;No, fail
	XCTU [MOVES (Q1)]	;Touch the start address of the buffer
	ERJMP CUBERR		;Address does not have reasonable access
	MOVE Q2,Q1		;Get a copy of the current buffer address
	ADD Q2,T2		;Calculate the end address of the buffer
	HLRZ T4,Q2		;Get the section number of the end address
	SKIPN USECTB(T4)	;Does this section exist???
	JRST CUBERR		;No, fail
	XCTU [MOVES (Q2)]	;Yes, touch the end of the buffer
	ERJMP CUBERR		;Failed, report bad page access
	AOS T3			;Count the buffer
	CAIL T3,C%MXBF		;Have we exceeded a reasonable number of buf?
	 IFNSK.
	 MOVX T1,SCSENB		;Yes, get the error code for too many buffers
	 RETBAD ()		;Report failure
	 ENDIF.
	XCTU [MOVE Q1,(Q1)]	;Get the addr of the next buffer
	ERJMP CUBERR		;No such address, fail
	SKIPE Q1		;Is there a next buffer???
	JRST CUBLOP		;Yes, loop to check it please
	RETSKP			;No, all done

; Here when an ERJMP takes on a user referance or on a detected bad address.

CUBERR:	RETBAD (SCSIAB)		;Return with bad address error code
	SUBTTL Support routines -- SCSXUB (Link buffer chain into database)

; This routine links a user buffer chain into the JSYS translation database.
;It is assumed that the buffer chain has been previously checked for
;accesibility and length (by routine SCSCUB).
;
; Usage:
;	Call
;	T1/	Address of first buffer in user space
;	T2/	Offset into BSD for linking these buffers
;	P1/	Address of connect block
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	P1/	Address of connect block
;
SCSXUB: SAVEAC <Q1,Q2>
	MOVE Q1,T1		;Start with the first buffer please
	MOVE Q2,T2		;Save the offset into the BSD

XUBLOP:	CALL SCSLUB		;Link buffer into data structure pls...
	 RETBAD ()		;Fail with error code in T1
	XCTU [MOVE Q1,(Q1)]	;Get pointer to next buffer
	ERJMP XUBERR		;Handle bad address chain
	SKIPN Q1		;Is there really a next buffer???
	RETSKP			;No more buffers, return now
	MOVE T1,Q1		;Point SCSLUB at the current buffer
	MOVE T2,Q2		;  and where in the BSD they go
	JRST XUBLOP		;Loop for the next buffer please

XUBERR:	RETBAD (SCSIAB)		;Address failure, report failure
	SUBTTL Support routines -- SCSUNM (User name move)

; This routine moves the source and destination process names from user
;space into monitor space. Note that it assumes a particualt argument block
;format used for the connect and listen functions.
;
; Note:
;	This routine assumes that it is being called NOINT. This prevents
;^C races while we own resources.
;
; Usage:
;	Call
;	Q1/	Addr of args in user space
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Address of source process name
;	T2/	Address of destination process name or zero if none
;	T3/	Address of free space block used
;	T4/	Address of routine to call to return free space
;
SCSUNM:	SAVEAC <P5>
	STKVAR <SPN,DPN,ERRCOD>
	SETZM DPN		;Init byte pointer to destination name
	MOVX T1,<XWD .RESP2,<C%PNLW*2>> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get the swapable space for the event block
	 RETBAD ()		;Fail with
	MOVE P5,T1		;Setup perm AC with base address of free space
	UMOVE T1,.SQSPN(Q1)	;Get source process name argument
	ERJMP UNMER1		;Handle bad page fault
	JUMPN T1,UNMSNM		;If there is a source name, move it
	MOVX T1,SCSNSN		;Otherwise, get the correct error code
	JRST UNMERR		; and error exit

UNMSNM:	XMOVEI T2,(P5)		;Get address of free space
	MOVEM T2,SPN		;Save addr of source process name
	MOVX T2,<POINT 8,(P5)>	;Now build byte pointer we can use
	MOVX T3,C%PNMN		;The maximum length of the string
	CALL SCSSUM		;Move the string
	 JRST UNMERR		;Handle string move failure

	UMOVEM T1,.SQSPN(Q1)	;Store the updated string for the user
	ERJMP UNMER1		;Handle bad page faults
	UMOVE T1,.SQDPN(Q1)	;Get the destination process name
	ERJMP UNMER1		;Handle page faults
	JUMPE T1,UNMEXI		;If no destination name, default no string

	XMOVEI T2,<C%PNMN+3>/4(P5) ;Build address of destination name
	MOVEM T2,DPN		;Save the pointer to the destination name
	MOVX T2,<POINT 8,<<C%PNMN+3>/4>(P5)> ;Byte ptr we use
	MOVX T3,C%PNMN		;Max length of the string
	CALL SCSSUM		;Move the string please
	 JRST UNMERR		;Handle string move failure
	UMOVEM T1,.SQDPN(Q1)	;Store updated byte pointer for user
	ERJMP UNMER1		;Handle page faults

UNMEXI:	MOVE T1,SPN		;Get promised addr of source name
	MOVE T2,DPN		;Get promised addr of destination name
	MOVE T3,P5		;Put addr of free space where promised
	XMOVEI T4,RELRES	;  and get addr of routine to return space
	RETSKP			;All is well, return

; Here on failure. Be sure the free space gets returned and the error code 
;reported.
;
; UNMER1
;	ERJMP here on bad address. Fill in error code for that.
;
; UNMERR
;	T1/	Error code
;
UNMER1:	MOVX T1,SCSIAB		;Get the error code for bad address
UNMERR:	MOVEM T1,ERRCOD		;Save the error code
	MOVE T1,P5		;Get the address of the free space
	CALL RELRES		;Return the free space to resident pool
	MOVE T1,ERRCOD		;Get the error code back
	RETBAD ()		;Report our failure

	ENDSV.
	SUBTTL Support routines -- SCSUDM (User connection data move)

; This routine moves connection data from user space to monitor space.
;The data is stored in a resident free sace block that the caller is
;responsible for returning. The caller does not need to know what kind
;of space was used since the base address of the block and the address of
;the space return routine are provided.
;
; Usage:
;	Call
;	T1/	Base address of connection data in user space
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Monitor address of the connection data
;	T2/	Address of the routine to return the free space used
;
; Note:
;	It is assumed that this routine is called NOINT since it will
;allocate resources. Rather than return NOINT when the success return
;is taken, the caller must be NOINT at the time of the call.
;
SCSUDM:	STKVAR <DATADR,USRDAT,ERRCOD>
	MOVEM T1,USRDAT		;Save the address of the user conn data
	MOVX T1,<XWD .RESP2,C%DTLW> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get the resident space for the block
	 RETBAD ()		;Return with an error from ASGRES
	MOVEM T1,DATADR		;Store the address of the free space used
	MOVE T3,T1		;Make this address the destination address
	MOVX T1,C%DTLW		;Get the length of the block to move
	MOVE T2,USRDAT		;Now the address of the block in user space
	CALL SCSDUM		;Move data from user space to monitor space
	 IFNSK.
	 MOVEM T1,ERRCOD	;Save the error code
	 MOVE T1,DATADR		;Get the free space address back
	 CALL RELRES		;Return the free space please
	 MOVE T1,ERRCOD		;Get the error code back again
	 RETBAD ()		;   and report failure
	 ENDIF.
	MOVE T1,DATADR		;Return the address of the data as promised
	XMOVEI T2,RELRES	;Also report addr of free space return
	RETSKP			;  and return

	ENDSV.
	SUBTTL Support routines -- SCSCBI (Connect block data init)

; This routine initializes the connect block with the data required
;by the JSYS.
;
; Note:
;	This routine assumes that it is being called to add data to a connect 
;block owned by the current fork. PSB cells (SCSPS0,SCSPS1) are accessed and
;it is assumed that the current PSB is the desired one.
;
; Usage:
;	Call
;	P1/	Address of connect block
;
;	Return (+1) Always
;	P1/	Address of connect block
;
; Note:
;	Today the PSB cells used here do not have a MSKSTR associated with
;them for use with LOAD/STOR. Hence the MOVE/MOVEM is used to move the
;PSI information around. These PSB cells should be defined and LOAD/STOR
;used to move the data around.
;
SCSCBI:	SETONE CBFJSY,(P1)	;Set the "CB is for JSYS" flag
	MOVE T1,FORKX		;Get my fork number
	STOR T1,CBFORK,(P1)	;Store in CB as owning fork
	MOVE T1,JOBNO		;Get my job number as well
	STOR T1,CBJOB,(P1)	;  which is also stored in the CB
	MOVE T1,SCSPS0		;Get the first word of fork PSI channels
	MOVEM T1,.CBPS0(P1)	;Store in the CB
	MOVE T1,SCSPS1		;Now get the second word of PSI channels
	MOVEM T1,.CBPS1(P1)	;Store these as well
	RET			;All done...
	SUBTTL Support routines -- SCSABT/SCSRBT (Add/remove bit from table)

;SCSABT
; This routine adds a bit to the table who's address is specified.
;If the bit was already lit, the (+1) return is taken. If the bit was off
;it is turned on and the (+2) return is taken.
;
;SCSRBT
; This routine turns off a bit in a bit table whos base address is given.
;If the bit was already off then the (+1) return is taken. If the bit
;was on then it is cleared and the (+2) return is taken.
;
; Usage (SCSABT and SCSRBT):
;
;	Call
;	T1/	Bit number
;	T2/	Base address of table
;
;	Return (+1)
;	SCSABT: Bit was already on
;	SCSRBT: Bit was already off
;
;	Return (+2)
;	SCSABT: Bit was not on, but is now
;	SCSRBT: Bit was not off, but is now
;
SCSRBT:	TDZA T3,T3		;Show we are in RBT code
SCSABT:	SETO T3,		;Show we are in ABT code
	SAVEAC <T1>
	STKVAR <ABTCOD>
	MOVEM T3,ABTCOD		;Save entry point info

	MOVE T3,T1		;Get the bit number
	IDIVI T3,^D36		;Turn bit number into word + bit offset
	ADD T3,T2		;Add table offset to table base addr
	MOVE T4,BITS(T4)	;Light the bit from the bit number
	MOVE T1,(T3)		;Save the origional contents of the table

	SKIPN ABTCOD		;Are we adding the bit???
	IFNSK.
	 ANDCAM T4,(T3)		;Turn off the bit please
	 TDNE T4,T1		;No, we are removing the bit
	 RETSKP			;Bit was on before, say so
	 RET			;Bit was off before, say so
	ENDIF.
	IORM T4,(T3)		;Yes, turn the bit on please
	TDNN T4,T1		;We are adding the bit, was it on before?
	RETSKP			;No, return it was off
	RET			;Yes, return it was on...

	ENDSV.
	SUBTTL Support routines -- SCSGNB (Get next bit)

	RESCD			;Called at interrupt level

; This routine finds the next bit lit in a bit table. An origin bit is
;specified. The first bit tested is the first bit to the right of the
;origin bit.
;
; Usage:
;	Call
;	T1/	Origin bit
;	T2/	Base address of bit table
;	T3/	Length of table in words
;
;	Return (+1)
;	All bits after origin bit to end of table are zero
;
;	Return (+2)
;	T1/	Bit number of next lit bit in table
;
SCSGNB:	STKVAR <BASADR,ADDR,LEN>
	MOVEM T2,BASADR		;Save the base address for later
	MOVEM T2,ADDR		;  as well as for computation
	IDIVI T1,^D36		;(T1) = word offset (T2) = bit offset into word
	SUB T3,T1		;Offset table length to current word offset
	JUMPL T3,R		;If neg, we are off the end of the table...
	MOVEM T3,LEN		;Save the corrected length of the table
	ADDB T1,ADDR		;Convert word offset to address in table

	MOVE T1,(T1)		;Get the first word to be considered
	LSH T1,(T2)		;Punt any bits left of the origin bit
	MOVNS T2		;Now put the bits back
	LSH T1,(T2)		;   where they came from
	$SKIP			;Don't smash what we just did

GNBLOP:	MOVE T1,@ADDR		;Get the next word to be considered
	JFFO T1,GNBEND		;If there are bits set, we are done
	SOSG LEN		;Have we goen off the end of the table???
	RET			;Yes, all done, no more bits let
	AOS ADDR		;Otherwise point to the next word
	JRST GNBLOP		;  and test it...

; Here with:
;	ADDR/	Address of desired bit
;	T2/	Bit position of desired bit
;
GNBEND:	MOVE T1,ADDR		;Get the address of the desired bit
	SUB T1,BASADR		;Turn it into a table index
	IMULI T1,^D36		;  and then into a bit number
	ADD T1,T2		;Add the offset into the word
	RETSKP			;All done, report bit number in T1

	ENDSV.

	SWAPCD 	
	SUBTTL Support routines -- SCSAFT (Add fork to table)

; This is a jacket routine (for SCSABT) to add forks to the interrupt bit 
;table. There is one bit for each possible fork in the system. When the bit 
;is lit the fork has enabled interrupts for configuration changes on the CI.
;
; Usage:
;	Call
;	T1/	Fork number
;
;	Return (+1)
;	Fork was already enabled for SCS interrupts
;
;	Return (+2)
;	Fork was not enabled for SCS interrupts but is now
;
SCSAFT:	XMOVEI T2,FRKTAB	;Get the address of the fork bit table
	CALLRET SCSABT		;Add the bit to the table
	SUBTTL Support routines -- SCSRFT (Remove fork from table)

; This routine removes a fork from the SCS interrupts fork table. It is a
;jacket routine for SCSRBT.
;
; Usage:
;	Call
;	T1/	Fork number
;
;	Return (+1)
;	Fork was not enabled for SCS interrupts
;
;	Return (+2)
;	Fork was enabled for SCS interrupts but isn't now
;
SCSRFT:	XMOVEI T2,FRKTAB	;Get the base address of the SCS% fork table
	CALLRET SCSRBT		;Remove the bit from the table
	SUBTTL Support routines -- SCSGNF (Get next fork)

	RESCD			;Called at interrupt level

; This routine obtains the next fork with SCS% configuration change interrupts
;enabled.
;
; Usage:
;	Call
;	T1/	Origin fork number
;
;	Return (+1)
;	No fork after origin fork is enabled for SCS% interrupts
;
;	Return (+2)
;	T1/	Fork number of fork with SCS% PSIs enabled
;
SCSGNF:	XMOVEI T2,FRKTAB	;Get the address of the fork table
	MOVE T3,FRKTLN		;  and the length of the table in words
	CALLRET SCSGNB		;Find the appropriate bit and return

	SWAPCD
	SUBTTL Support routines -- SCSQMD (Queue entry on maint. data Q)

	RESCD			;These routines do CIOFF's, must be resident

; This routine adds an entry to the system wide maintainance data queue.
;
; Usage:
;	Call
;	T1/	Buffer name
;	T2/	Owning fork number
;	T3/	Target node number
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Address of queued block
;
SCSQMD:	SAVEAC <P1,P4>
	STKVAR <BUFNAM,OWNFRK,NODNUM>
	MOVEM T1,BUFNAM		;Save the buffer name
	MOVEM T2,OWNFRK		;  and the owning fork number
	MOVEM T3,NODNUM		;  and the target node number

; Here to set up the temporary CB. Same format as regular CB. Just no real
;connect associated with it. Hence connect state is closed.
;
	MOVE P4,SBLIST(T3)	;Fetch the system block address please...
	SETZ T1,		;No bits for the CID please
	NOINT			;We are about to own a resource
	CALL SC.ACB		;Allocate a connect block
	 RETBAD (<>,<OKINT>)	;Fail with error from SCA
	MOVX T1,.CSCLO		;Get the closed state
	STOR T1,CBCNST,(P1)	;Store as the circuit state
	MOVX T1,CBFMDC		;Get the maint data CB flag
	MOVEM T1,.CBFLG(P1)	;Init the CB flags to be just the MDC bit
	MOVEM P4,.CBADR(P1)	;Setup the system block address
	MOVE T1,NODNUM		;Get the node number again
	STOR T1,CBDNOD,(P1)	;Store in the CB please...
	SETZM .CBANB(P1)	;Be sure the CB links are zero
	SETZM .CBAPB(P1)	;.	.	.
	CALL SC.LCB		;Link this connect onto block for target node

	MOVX T1,<XWD .RESP2,.MQLEN> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get the swapable space for the event block
	 JRST QMDERR		;Recover from the error please
	MOVE T2,BUFNAM		;Get the buffer name again
	MOVEM T2,.MQBUF(T1)	;Save it in the new Q entry
	MOVE T2,OWNFRK		;Get the owning fork again
	STOR T2,CBFORK,(P1)	;Store in the CB
	SETZM .MQNXT(T1)	;Be sure the pointer to next is zero
	MOVEM P1,.MQCBA(T1)	;Store the addr of the connect block

	CIOFF			;Lock out oter access to the queue please
	MOVEM T1,@SCABMQ	;Link this entry onto the end of the queue
	MOVEM T1,SCABMQ		;  and update the queue BLINK
	CION			;Allow access to the queue again
	OKINT			;Allow interrupts again
	RETSKP			;  and return OK...

; Here when the call to ASGRES fails. Dequeue the false CB we have made and
;then return failure with error code in T1.
;
QMDERR:	MOVE P4,T1		;Save the error code
	CALL SC.RCB		;Release the connect block we started here
	MOVE T1,P4		;Get the error code back again
	OKINT			;Allow interrupts again
	RETBAD ()		;Fail with error code in T1

	ENDSV.
	SUBTTL Support routines -- SCSDMD (Dequeue maint. data queue entry)

; This routine removes an entry from the system wide maintainance data queue.
;
; Usage:
;	Call
;	T1/	Address of entry to dequeue
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Address of entry from maintainance data queue
;
SCSDMD:	CIOFF			;Lock out other access to queue
	SETZ T2,		;Be sure address of previous is zero at start
	SKIPN T3,SCATMQ		;Is there an entry to look at here???
	RETBAD (MONX03,<CION>)	;Nope, internal confusion, return badness
DMDTRY:	CAMN T1,T3		;Do we have a match???
	JRST DMDMCH		;Yes, go dequeue it please
	SKIPN T2,.MQNXT(T3)	;Is there a next buffer to try???
	RETBAD (MONX03,<CION>)	;Nope, internal confusion, return badness
	MOVE T3,T2		;Setup address compare AC
	JRST DMDTRY		;Yes, loop for the next entry

; Here on a match.
;
; Expects:
;	T1/	Address of entry to be deleted
;	T2/	Address of previous entry or zero if first
;
DMDMCH:	MOVE T3,.MQNXT(T1)	;Address of entry after one to be deleted
	SKIPE T2		;Was this the first entry???
	MOVEM T3,.MQNXT(T2)	;No, store as addr of next in previous entry
	SKIPN T2		;Was this the first entry???
	MOVEM T3,SCSTMQ		;Yes, update the queue FLINK

	XMOVEI T3,SCATMQ	;Get the address of the queue FLINK pointer
	SKIPN SCATMQ		;Did we delete the last entry???
	MOVEM T3,SCABMQ		;Yes, update the queue BLINK as addr of FLINK
	CION			;Allow access to queue again
	RETSKP			;Return all OK
	SUBTTL Support routines -- SCSLFQ (Link packet to front of queue's)

; This routine links a monitor packet onto the front of the fork and
;CB queues. This routine is mainly used by error recovery code to put
;buffers back onto queues after an error has been detected. Note that this
;routine assumes the packet is for the current fork. I.E. the currently
;mapped PSB contains the correct pointers.
;
; Usage:
;	Call
;	T1/	Address of packet to be linked
;	T2/	Address of four word list header block
;	P1/	Address of CB
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	All calling AC's preserved, packet has been linked onto the front of 
;		the fork and CB queues.
;
SCSLFQ:	CIOFF			;Do this alone please
	MOVE T3,@.TOPFQ(T2)	;Get the addr of the first buffer on the fork Q
	MOVEM T3,.MEANF(T1)	;Link new buffer at the front
	SETZM .MEAPF(T1)	;Make backward link be zero (first on Q)
	MOVEM T1,@.TOPFQ(T2)	;  and update the queue FLINK
	MOVE T3,@.TOPCQ(T2)	;Get addr of first buffer on CB Q
	MOVEM T3,.MEANC(T1)	;Link new packet onto front of queue
	SETZM .MEAPC(T1)	;Zero the backward link (first on queue)
	MOVEM T1,@.TOPCQ(T2)	;  and update the queue FLINK
	CION			;Turn on CI interrupts again
	RETSKP			;Return ok...
	SUBTTL Support routines -- SCSAER (ASGRES error handler)

	SWAPCD

; This routine handles an allocation failure from ASGRES. It causes a
;BUGINF with enough data to try to figure out what has happened from just
;the BUGINF data.
;
; Usage
;	Call
;	T1/	Error code from ASGRES
;
;	Return (+1) Always
;	No data
;
; Note:
;	This routine is branched to with a PUSHJ P,. This is only to
;obtain the PC of the caller. The routine POPs this PC off the stack and
;returns to the stack loc before the call to this routine. Hence you cannot
;have and explicit PUSH's pending before the call to this routine...
;
SCSAER:	POP P,T2		;Get the callers PC back
	TXZ T2,<777740,,0>	;Keep just the PC part
	BUG. (INF,SCSACF,SCSJSY,SOFT,<SCA: A JSYS call to ASGRES failed>,<<T1,ERRCOD>,<T2,CALLPC>>,<

Cause:	A call to ASGRES (by the JSYS) has failed. With the error code and 
	callers PC given by the BUGINF, figuring out why it failed should
	be easy enough...
>)
	RETBAD			;Return to callers caller with error code in T1
	SUBTTL Support routines -- SCSLCB (Link CB onto fork CB queue)

	RESCD

; This routine links a CB onto the CB queue of a fork (whether or not we are
;the current fork).
;
; Usage:
;	Call
;	P1/	Address of the CB to be linked
;		  Note that at present, this routine is only called for the
;		   current fork. Hence we make the assumption that we are
;		   always the current fork. We also assume that we are ALWAYS
;		   in process context...
;
;	Return (+1) Always
;	P1/	Address routine was called with
;
SCSLCB:	CIOFF			;Interlock the connect block linking
	MOVE T1,SCSBCQ		;Get addr of last entry on the queue
	XMOVEI T2,SCSTCQ	;  and the addr of the top of queue pointer
	CAME T1,T2		;Is BLINK a pointer to the FLINK???
	IFNSK.
	 MOVEM T1,.CBJPB(P1)	;No, update previous pointer of new CB
	 ADDI T1,.CBJNB		;  and offset to the forward pntr for last CB
	ENDIF.
	MOVEM P1,(T1)		;Link new buffer onto the fork CB queue
	MOVEM P1,SCSBCQ		;  and update the queue BLINK
	CION			;Release CB interlock
	RET			;  and return all OK
	SUBTTL Support routines -- SCSKIL (Clean up SCS% data)

	SWAPCD

; Routine to call to release all SCS% resources owned by a fork. Currently
;the only way here is from KSELF, FLOGO, KFORK% JSYS and the CLZFF% JSYS.
;
; Note: No ACs are smashed
;
; Usage:
;	Call
;	T1/	Fork number of fork being killed
;
;	Return (+1) Always
;	No data returned
;
; Note:
;	Most callers of SCSKIL/SCSLGO assume that NO ACs are smashed.
;
SCSLGO::			;Alternate entry name for FLOGO (MEXEC)
SCSKIL::SAVEAC <T1,T2,T3,T4,P1>	;Save ALL ACs used here
	TRVAR <FRKN,PSBADR,FLNK,BLNK>
	MOVEM T1,FRKN		;Save the fork number for later
	CALL SCSRFT		;Remove this fork from the PSI table
	 NOP			;I don't care if it wasn't in the table
	NOSKED			;Interlock the use of SCSMPS
	CALL SCSMPS		;Map PSB of target fork
	 CALL A2MHLT		;PUSHJ to a common BUGHLT
	MOVEM T2,PSBADR		;Save the PSB base address

	MOVEI T1,SCSTCQ		;Get 18 bits of addr for top of the event Q
	ANDI T1,777		;Keep just the offset
	ADD T1,T2		;Offset into the PSB page
	MOVEM T1,FLNK		;Save the address of the queue FLINK

	MOVEI T3,SCSBCQ		;Get 18 bits of queue BLINK address
	ANDI T3,777		;Keep just the page offset
	ADD T3,T2		;Add page address to get addr of queue BLINK
	MOVEM T3,BLNK		;Save the address of the queue BLINK

	SKIPE P1,(T1)		;Are there any connections for this fork???
	CALL KILCLP		;Yes, loop to kill them

	MOVE T1,PSBADR		;Get the fork number back again
	CALL SCSCXN		;Delete any DMA resources owned by target fork
	CALL SCSUPS		;All done, release the mapped PSB page
	OKSKED			;  and allow other forks again
	RET			;All done, return

; Here to loop over all connection opened by a fork.
;
;	P1/	Pointer to the first CB in the list
;	FLNK/	Address of queue FLINK
;	BLNK/	Address of queue BLINK
;
; Note: KILCHK assumes that the error recovery from the failing SC.DIS call
;will eventually loop back to get the rest of the connections. It assumes the
;queue is always empty.
;
KILCLP:	SETONE CBFKIL,(P1)	;Set the fork is dead flag in CB flag word
	CALL SCSCBD		;Delete JSYS CB data from this CB
	MOVE T1,.CBSCI(P1)	;Get the CID of the current CB
	MOVE P1,.CBJNB(P1)	;Save the addr of the next block
	NOINT			;Be sure SCA gets a chance to finish
	BLCAL. (SC.DIS,<T1,[0]>) ;Abort the connection
	 JRST KILERR		;Failed??? Check the error code
	OKINT			;SCA is done, allow interrupts again

KILCHK:	JUMPN P1,KILCLP		;If more blocks left, delete them
	XMOVEI T1,SCSTCQ	;Get extended address of queue FLINK
	MOVEM T1,@BLNK		;Re-init headers for now empty queue
	SETZM @FLNK		;Zero the queue FLINK
	RET			;No more connect blocks, all done


; Here when SC.DIS fails.
;
KILERR:	 OKINT			;SCA is done, allow interrupts again
	 CAIN T1,SCSIID		;Is the error, bad CID
	 JRST KILCHK		;Yes, then connect is already gone, no problem
	 BUG. (CHK,SCSABF,SCSJSY,SOFT,<SCSJSY: Connection abort failure on fork delete>,<<T1,ERRCOD>>,<

Cause:	During the deletion process for a fork we tried to abort the 
	connections it had open. We failed in the attempt. 
>)
	JRST KILCHK		;No continue looping for connects

	ENDTV.
	SUBTTL Support routines -- SCSCBD (JSYS CB data delete)

; ***** CROCK ALERT *****
;This code needs to be updated to reflect the correct scheme for deletion
;of connection data.
;
; This routine deletes JSYS data from a connect block. Buffers on the port 
;queues will be returned by SC.RAP. Only the buffer database is handled here.
;
; Usage:
;	Call
;	P1/	Connect block address
;
;	Return (+1) Always
;	P1/	Connect block address
;
; Note:
;	This routine should only be called from process context!
;
SCSCBD:	NOSKED			;Interlock connect block JSYS queues

; Clean up entries on the message available queue

	XMOVEI T1,.CBTMQ(P1)	;Point to top of the queue
	XMOVEI T2,.CBBMQ(P1)	;  and the bottom of the queue
	XMOVEI T3,SC.RBF	;  and the routine to call to return the space
	XMOVEI T4,MSG		;  and the 4 word block for messages
	CALL SCSGPR		;Return the packets

; Clean up entries on the datagram available queue

	XMOVEI T1,.CBTDQ(P1)	;Point to the top of the queue
	XMOVEI T2,.CBBDQ(P1)	;  and the bottom of the queue
	XMOVEI T3,SC.RLD	;  and the routine to return the space
	XMOVEI T4,DG		;  and the 4 word block for datagrams
	CALL SCSGPR		;Return the packets

; Clean up entries on the DMA transfer complete queue

	XMOVEI T1,.CBTXQ(P1)	;Point to the top of the queue
	XMOVEI T2,.CBBXQ(P1)	;  and the bottom of the queue
	XMOVEI T3,RELRES	;  and the routine to return the space
	XMOVEI T4,XFER		;  and the 4 word block for DMA
	CALL SCSGPR		;Return the blocks on the queue

; Clean up entries on the event queue

	XMOVEI T1,.CBTEQ(P1)	;Point to the top of the queue
	XMOVEI T2,.CBBEQ(P1)	;  and the bottom of the queue
	XMOVEI T3,RELRES	;  and the routine to return the space
	XMOVEI T4,EVT		;  and the 4 word block for events
	CALL SCSGPR		;Return the blocks on the queue

; Clean up any BSD's that are around

	XMOVEI T1,.CBTBQ(P1)	;Point to the top of the queue
	XMOVEI T2,.CBBBQ(P1)	;  and the bottom of the queue
	CALL SCSCBS		;Clean up any BSD's that are around

; Now remove this connect block from the owning fork CB queue

	CALL SCSRCB		;Remove current CB from owning fork CB queue
	 CALL CBDCHK		;Failed, BUGCHK and continue

	OKSKED			;Release JSYS Q interlock

; ***** CROCK ALERT *****
;This is a temporary fix to get SCA to delete the block when it wants to...

	SETZRO CBFJSY,(P1)	;Turn off the JSYS flag bit
	TMNE CBFPTC,(P1)	;Is protocol complete lit???
	IFNSK.
	 SETONE CBFRAP,(P1)	;Yes, light reap as well
	ENDIF.
	RET			;All done

CBDCHK:	BUG. (CHK,SCSCDC,SCSJSY,SOFT,<SCSJSY: Cannot delete connect block from fork queue>,<<T1,ERRCOD>>,<

Cause:	We tried to remove a connect block from the owning fork's list of 
	connect blocks. T1 contains why this did not succeed. The most likely
	failure is a +1 return from SCSMPS. This fails only when we map a
	PSB but do not unmap it.
>,R)
	SUBTTL Support routines -- SCSGPR (General packet return)

; This routine returns a set of packets/blocks linked with a standard queue
;header. The header is the one used by SCS% to queue up EVERYTHING it hangs off
;the connect block.
;
; Usage:
;	Call
;	T1/	Queue FLINK
;	T2/	Queue BLINK
;	T3/	Address of routine to return packet/block space
;		This routine assumes that the routine address given expects
;		nothing more than the space address in T1.
;	T4/	Address of 4 word list header block (if there is one)
;
; Note:
;	This routine will try to use the address of the 4 word list header
;block (T4) first. If this address is zero it will use just the FLINK and BLINK
;specified in T1/T2. The FLINK and BLINK must be specified, even when a 4 word
;header block address is specified.
;
; Note:
;	This routine assumes that the caller will provide whatever interlock
;is necessary for the queue. This allows this routine to be used by callers 
;that require different interlocks.
;
;	Return (+1) Always
;	No data returned
;
SCSGPR:	SAVEAC <Q1>
	STKVAR <RTNADR,BLKADR>
	MOVEM T3,RTNADR		;Save the return routine address
	MOVEM T4,BLKADR		;   and the 4 word block address
	MOVE Q1,T1		;Save the queue FLINK

	SKIPN Q1,(Q1)		;Anything at all on the queue???
	RET			;No, all done, return
GPRBLK:	MOVE T1,Q1		;Get the address of the current entry
	MOVE Q1,(Q1)		;Get the addr of the current packet/block
	SKIPE T2,BLKADR		;Is there a 4 word block address???
	CALL SCSDEQ		;Yes, dequeue the packet/block please
	SKIPA			;ERROR, BUT WHAT ELSE CAN BE DONE. MAY WANT
				;A BUGCHK HERE.
	CALL @RTNADR		;Call the routine to return the space
	JUMPN Q1,GPRBLK		;Loop until the queue is empty
	RET

	ENDSV.
	SUBTTL Support routines -- SCSCBS (Clean a BSD queue)

; Routine to clean up the entries on a BSD queue. Note that this routine
;should be merged with a general queue cleaning routine. For the moment however
;it solves a point problem.
;
; Usage:
;	Call
;	P1/	Address of connect block
;
;	Return (+1) Always
;	P1/	Address of connect block
;
SCSCBS:	SAVEAC <Q1>
	NOSKED			;Interlock the queue
	SKIPE T1,.CBTBQ(P1)	;Is there really something here???
	JRST CBSLOP		;Yes, go process it
	OKSKED			;No, return now
	RET			;All done...

CBSLOP:	LOAD Q1,BDNXT,(T1)	;Get the address of the next entry
	CALL SC.RLD		;Release the buffer
	SKIPE T1,Q1		;Is there another enty here???
	JRST CBSLOP		;Yes, loop to return it
	XMOVEI T1,.CBTBQ(P1)	;Get a pointer to the queue top
	STOR T1,CBBBQ,(P1)	;Init BLINK as pointer to FLINK
	SETZRO CBTBQ,(P1)	;Init queue FLINK as zero
	OKSKED			;Release queue interlock
	RET			;All done
	SUBTTL Support routines -- SCSRCB (Remove CB from owning fork CB queue)

; Routine to remove a CB from the owning forks list of CB's.
;
; Usage:
;	Call
;	P1/	Address of connect block
;
;	Return (+1) 
;	T1/	Error code
;
SCSRCB:	NOSKED			;Interlock queue pointers
	LOAD T1,CBFORK,(P1)	;Get the owning fork number
	CALL SCSMPS		;Map the target forks PSB
	 RETBAD (<>,<OKSKED>)	;Failed?!?

	MOVEI T1,SCSTCQ		;Get 18 bit addr of queue FLINK
	ANDI T1,777		;Keep just the offset
	ADD T1,T2		;Offset to mapped PSB
	MOVE T3,T1		;Save the address of the queue FLINK

	MOVEI T1,SCSBCQ		;Get 18 bit addr of queue BLINK
	ANDI T1,777		;Keep just the offset
	ADD T1,T2		;Offset to mapped PSB
	MOVE T4,T1		;Save the address of the queue BLINK

	MOVE T1,.CBJNB(P1)	;Get forward pointer from CB
	SKIPN T2,.CBJPB(P1)	;Is there a previous pointer???
	SKIPE T1		;   or a forward pointer???
	JRST RCBMOQ		;There are other entries, work on them

; Here to delete the last entry on the queue. Reinit the pointers

	XMOVEI T1,SCSTCQ	;Get addr of queue FLINK
	MOVEM T1,(T4)		;Reinit BLINK as pointer to FLINK
	SETZM (T3)		;Reinit FLINK as zero
	CALL SCSUPS		;Unmap the target PSB
	OKSKED			;Release queue interlock
	RETSKP			;All done

; Here when there are other entries on the queue.
;
; T1/	Pointer to next CB
; T2/	Pointer to previous CB
; T3/	Address of queue FLINK
; T4/	Address of queue BLINK

RCBMOQ:	SKIPN T1		;Is there a next CB???
	MOVEM T2,(T4)		;No, update Q BLINK with pointer to previous
	SKIPE T1		;Is there a next CB???
	MOVEM T2,.CBJPB(T1)	;Yes, update its previous pointer
	SKIPN T2		;Is there a previous CB???
	MOVEM T1,(T3)		;No, update queue FLINK
	SKIPE T2		;Is there a previous CB???
	MOVEM T1,.CBJNB(T2)	;Yes, update its next pointer
	CALL SCSUPS		;Unmap the target PSB
	OKSKED			;Release queue interlock
	RETSKP			;All done
	SUBTTL Support routines -- SCSAXN (Add DMA buffer name)

; Routine to add a name to the DMA name database for a fork.
;
; Usage:
;	Call
;	T1/	DMA buffer name
;	T2/	Owning fork number
;	T3/	Address of datagram buffer with page stack in it
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	DMA buffer name
;	T2/	Owning form number
;	T3/	Address of page stack
;
SCSAXN:	SAVEAC <Q1>
	STKVAR <FRKN,BUFNAM,PAGSTK,ERRCOD>
	MOVEM T1,BUFNAM		;Save the fork number
	MOVEM T2,FRKN		;  and the buffer name
	MOVEM T3,PAGSTK		;  and the address of the page stack DG buffer
	NOSKED			;Interlock the PSB mapping
	MOVE T1,T2		;Get the fork number for SCSMPS
	CALL SCSMPS		;Map the PSB of the target fork
	 RETBAD (<MONX03>,<OKSKED>) ;Return badness
	MOVE Q1,T2		;Save the base addr of the PSB
	MOVX T1,<XWD .RESP2,.XNLEN> ;Priority,,length of block
	MOVX T2,.RESGP		;Resident pool number
	CALL ASGRES		;Get the swapable space for the event block
	 JRST AXNERR		;Recover from the error please
	MOVE T2,BUFNAM		;Get the buffer name back
	STOR T2,XNNAM,(T1)	;Store the name in the entry
	MOVE T2,PAGSTK		;Get the address of the page stack
	STOR T2,XNSTK,(T1)	;Store in the entry
	MOVE T2,Q1		;Get the base address of the PSB
	CALL SCSLXN		;Link the entry onto the fork queue
	CALL SCSUPS		;Unmap the target PSB
	OKSKED			;We can allow interrupts again
	MOVE T1,BUFNAM		;Get the buffer name
	MOVE T2,FRKN		;  as well as the fork number
	RETSKP			;All done...

; Here on a failure from ASGRES.
;
AXNERR:	MOVEM T1,ERRCOD		;Save the error code
	CALL SCSUPS		;Unmap the PSB
	OKSKED			;Allow interrupts again
	MOVE T1,ERRCOD		;Get the error code back again
	RETBAD ()		;  and return

	ENDSV.
	SUBTTL Support routines -- SCSDXN (Delete DMA buffer name)

; Routine to delete a DMA buffer name from the fork database. Note that this
;routine  unmaps the buffer.
;
; Usage:
;	Call
;	T1/	Buffer name
;	T2/	Fork number
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Buffer name
;	T2/	Fork number
;
SCSDXN:	STKVAR <FRKN,BUFNAM,PSBADR>
	MOVEM T1,BUFNAM		;  as well as the buffer name
	MOVEM T2,FRKN		;Save the fork number
	NOSKED			;Interlock the PSB mapping
	MOVE T1,T2		;Get the fork number for MPS
	CALL SCSMPS		;Map the target PSB please
	 RETBAD (<MONX03>,<OKSKED>) ;Fail please
	MOVEM T2,PSBADR		;Save the PSB base address
	MOVEI T1,SCSTXN		;Get 18 bit address of queue FLINK
	ANDX T1,777		;Keep just the page offset
	ADD T1,T2		;Add PSB base addr to get FLINK addr
	MOVE T2,BUFNAM		;Get the buffer name back
DXNLOP:	SKIPN T1,(T1)		;Is there another entry on the queue???
	RETBAD (<SCSIBN>,<OKSKED>) ;No, fail please
	LOAD T3,XNNAM,(T1)	;Get the name from this entry
	CAME T2,T3		;Do we have a match???
	JRST DXNLOP		;No, loop for the next entry
	MOVE T2,PSBADR		;Get the PSB base address again
	CALL SCSRXN		;Remove the entry from the queue
	CALL SCSUPS		;Unmap the target PSB
	OKSKED			;Release PSB mapping interlock
	MOVE T1,BUFNAM		;Get the buffer name back
	MOVE T2,FRKN		;   as well as the fork number
	RETSKP			;We are all done...

	ENDSV.
	SUBTTL Support routines -- SCSFXN (Find a buffer name)

; Routine to determine if a buffer name is currently owned by a fork.
;
; Usage:
;	Call
;	T1/	Buffer name
;	T2/	Fork number
;
;	Return (+1)
;	Buffer name is not owned by designated fork
;
;	Return (+2)
;	Buffer name is owned by designated fork
;
SCSFXN:	SAVEAC <T1,T2>
	STKVAR <FRKN,BUFNAM>
	MOVEM T1,BUFNAM		;Save the buffer name
	MOVEM T2,FRKN		;  and fork number
	NOSKED			;Interlock the PCB mapping
	MOVE T1,T2		;Get the fork number for MPS
	CALL SCSMPS		;Map the PSB of the target fork
	 RETBAD (<MONX03>,<OKSKED>) ;Failed, report please...
	MOVEI T1,SCSTXN		;Get 18 bit addr of queue FLINK
	ANDX T1,777		;Keep just the page offset
	ADD T1,T2		;Get the address of the FLINK word
	MOVE T2,BUFNAM		;Get the buffer name again

FXNLOP:	SKIPN T1,(T1)		;Is there a next entry???
	IFNSK.
	 CALL SCSUPS		;Unmap the PSB
	 OKSKED			;Release PSB mapping interlock
	 RETBAD (SCSIBN)	;Fail please
	ENDIF.
	LOAD T3,XNNAM,(T1)	;Get the name from the entry
	CAME T2,T3		;Doe we have a match???
	JRST FXNLOP		;No, try for the next entry
	CALL SCSUPS		;Yes, unmap the target PSB
	OKSKED			;Release PSB mapping interlock again
	MOVE T1,BUFNAM		;Restore the buffer name
	MOVE T2,FRKN		;   and fork number
	RETSKP			;Return success...

	ENDSV.
	SUBTTL Support routines -- SCSCXN (Clean a buffer name list)

; This routine cleans the buffer name list for a given fork.
;
; Usage:
;	Call
;	T1/	Fork number
;	T2/	Base address of PSB
;
;	Return (+1) Always
;	No data returned
;
SCSCXN:	SAVEAC <Q1>
	STKVAR <PSBADR>
	MOVEM T1,PSBADR		;  and the PSB base address
	MOVEI T1,SCSTXN		;Get 18 bit addr of queue FLINK
	ANDX T1,777		;Keep just the page offset
	ADD T1,PSBADR		;Offset in PSB to top of queue

	NOSKED			;Interlock the fork's queue
	SKIPN Q1,(T1)		;Is there anything on the queue???
	JRST CXNEXI		;No, we are ready to leave
CXNLOP:	MOVE T1,Q1		;Get the entry address
	LOAD Q1,XNNXT,(Q1)	;Get the address of the next entry
	MOVE T2,PSBADR		;  and the PSB base address
	CALL SCSRXN		;Remove the entry from the queue
	JUMPN Q1,CXNLOP		;Loop if there are more entries

; Here when we have finished cleaning the queue

CXNEXI:	OKSKED			;Release the queue interlock
	RET			;All done...

	ENDSV.
	SUBTTL Support routines -- SCSLXN (Link entry onto fork DMA name queue)

; Routine to add an entry to a forks DMA buffer name list.
;
; Usage:
;	Call
;	T1/	Entry address
;	T2/	Base address of target PSB
;
;	Return (+1) Always
;	T1/	Entry address
;	T2/	Base address of target PSB
;
SCSLXN:	
	MOVEI T3,SCSTXN		;GET THE FLINK IN THE PSB
	ANDI T3,777		;GET ONLY THE WORD OFFSET.
	ADD T3,T2		;CALCULATE THE ADDRESS.
	NOSKED
	SKIPE (T3)		;IS THERE ANYTHING ALREADY ON THE LINKED LIST?
	IFSKP.			;NO.
	  MOVEM T1,(T3)		;SET UP THE FLINK (SCSTXN).
	  MOVEM T1,1(T3)	;SET UP THE BLINK (SCSBXN).
	  SETZRO XNPRV,(T1)	;CLEAN OUT ANTHING IN THE NAMED BUFFER LIST BLK.
	  SETZRO XNNXT,(T1)
	ELSE.			;YES.
	  MOVE T4,1(T3)		;GET THE ADDRESS OF THE LAST NAMED BUF. LIST BLK
	  MOVEM T1, .XNNXT(T4)	;SET UP POINTER IN PREVIOUS NAMED BUF. LIST BLK
	  MOVEM T1,1(T3)	;SET UP BLINK (SCSBXN).
	  SETZRO XNNXT,(T1)	;CLEAN UP ANTHING IN THE NAMED BUF. LIST BLK
	  STOR T4, XNPRV,(T1)	;SET UP BLINK IN NAMED BUF. LIST BLK.
	ENDIF.
	OKSKED
	RET
	SUBTTL Support routines -- SCSRXN (Remove entry from fork DMA nam list)

; Routine to remove an entry from a forks DMA name list.
;
; Usage:
;	Call
;	T1/	Entry address
;	T2/	PSB base address
;
;	Return (+1) Always
;	T1/	Entry address
;	T2/	PSB base address
;
SCSRXN:	SAVEAC <Q1>
	STKVAR <ENTADR,PSBADR>
	MOVEM T1,ENTADR		;Save the entry address
	MOVEM T2,PSBADR		;  as well as the PSB address
	LOAD T1,XNNAM,(T1)	;Get the name of the buffer we are killing
	NOINT			;Be sure SCA gets a chance to finish
	BLCAL. (SC.UMP,<T1>)	;Unmap the buffer please
	 RETBAD (<>,<OKINT>)	;Fail please
	MOVE T1,ENTADR		;Get the entry address back again
	CALL SCSULK		;Unlock all the pages in the buffer
	OKINT			;SCA has finished...
	MOVEI T1,SCSTXN		;Get the 18 bit addr of the queue FLINK
	ANDX T1,777		;Keep just the page offset
	ADD T1,PSBADR		;Get the address of the queue FLINK
	MOVEI T2,SCSBXN		;Get 18 bit address of queue BLINK
	ANDX T2,777		;Keep just the page offset
	ADD T2,PSBADR		;Get the address of the queue BLINK
	MOVE Q1,ENTADR		;Get the entry address back again

; Quick sanity check:
;
; Setup already:
; T1/	Address of queue FLINK
; T2/	Address of queue BLINK
; Q1/	Address of entry to be deleted
;
; Used:
; T3/	Forward link from entry
; T4/	Back link from entry
;
	NOSKED			;Interlock the queue
	LOAD T3,XNNXT,(Q1)	;Get the forward link
	LOAD T4,XNPRV,(Q1)	;Get the previous link
	SKIPN T3		;Is there a forward link???
	SKIPE T4		;  and no previous link???
	JRST RXNMOR		;There is forward or back link, not last entry
	SETZM (T1)		;Last entry on the queue, zero the list FLINK
	XMOVEI T3,SCSTXN	;Get a pointer to the list FLINK
	MOVEM T3,(T2)		;Init the BLINK as pointer to FLINK
	NOINT			;We own a resource now, be sure to finish
	OKSKED			;Release the queue interlock
	MOVE T1,Q1		;Get the address of the entry
	CALL RELRES		;Release the free space
	OKINT			;We no longer own the resource, all int again
	RET			;All done

; Here when this is not the last entry on the list.
;
				;SHOULD BE NOSKED WHEN ENTERING RXNMOR.
RXNMOR:	SKIPN T3		;Is there a forward link???
	MOVEM T4,(T2)		;No, deleting last entry, update queue BLINK
	SKIPE T3		;Is there a forward link???
	STOR T4,XNPRV,(T3)	;Yes, update previous pointer of next entry
	SKIPN T4		;Is there a previous entry???
	MOVEM T3,(T1)		;No, deleteing first entry, update queue FLINK
	SKIPE T4		;Is there a previous entry???
	STOR T3,XNNXT,(T4)	;Yes, update the forward pointer of next entry
	NOINT			;We own the resource, be sure we keep it
	OKSKED			;Release queue interlock
	MOVE T1,Q1		;Get the entry address
	CALL RELRES		;Return the free space
	OKINT			;We no longer own the resource, allow int
	RET			;All done

	ENDSV.
	SUBTTL Support routines -- SCSULK (Unlock pages in a DMA buffer)

; This routine unlocks the pages associated with a DMA buffer name. SCSMAP
;logged all of the pages it locked in a datagram buffer as a stack. The address
;of this stack is stored in the DMA name queue entry.
;
; Usage:
;	Call
;	T1/	Entry address
;
;	Return (+1) Always
;	No data returned
;
; Note: This routine returns the datagram buffer being used as a page stack.
;
SCSULK:	SAVEAC <Q1,Q2>
	STKVAR <STKADR>
	SKIPN Q1,.XNSTK(T1)	;Get the address of the stack
	RET			;What stack... Return now
	MOVE T1,Q1		;Get the address of the stack
	MOVEM T1,STKADR		;Save the stack address
	SKIPN Q1,(Q1)		;Is there anything on the stack???
	JRST ULKEXI		;Stack is empty, release the buffer
	AOS Q2,T1		;Point to the base of the stack
	ADD Q2,Q1		;Now point to the end of the stack

ULKLOP:	POP Q2,T1		;Get the page number to unlock
	NOINT			;Make sure this finishes
	CALL MULKCR		;Unlock it please
	OKINT			;We are done...
	SOJG Q1,ULKLOP		;Loop for all locked pages

ULKEXI:	MOVE T1,STKADR		;Get the entry address again
	CALL SC.RLD		;RElease it please
	RET			;All done

	ENDSV.
	SUBTTL Support routines -- SCSCLK (Clock service routine)

	RESCD

; This routine is called by the scheduler whenever SCSTIM is zero. Here we
;look down the work queue for JSYS events and set the SCS% bit in FKINT
;for forks that have events queued.
;
; Usage
;	Call
;	No arguments
;
;	Return (+1) Always
;	No arg returned
;
SCSCLK::SAVEAC <Q1>
	NOSKED			;Dont allow other queue deletes while here
	SKIPN Q1,SCATWQ		;Anything on the events queue???
	JRST CLKEXI		;Nope, return now
CLKFLP:	LOAD T2,MEFRK,(Q1)	;Get the target fork number
	MOVX T1,PSISC%		;Get the bit to light in FKINT (if needed)
	TMNN FKISC,(T2)		;Has this fork been poked already???
	CALL PSIGR		;No, request an interrupt for this fork
	SKIPE Q1,.MEANC(Q1)	;Is there a next entry???
	JRST CLKFLP		;Yes, loop for the next entry pls...

CLKEXI:	MOVX T1,<1B1>		;Get a VERY large positive number
	MOVEM T1,SCSTIM		;Turn our clock off pls...
	OKSKED			;Allow other queue deletes again
	RET			;  and return
	SUBTTL Support routines -- SCSPSI (Add entry to fork queue)

; This routine is called by SCHED at PIRQ when PSISC% is on in FKINT. We
;need to look down the queue of packets and see if there is one for this
;fork.
;
;	Call
;	No arguments
;
;	Return (+1) Always to PIRQR
;	No data returned
;
;
;THE .MEANC OFFSET OF EACH QUEUE ENTRY IS USED AS A FORWARD POINTER
;TO THE NEXT ENTRY. THE .MEAPC OFFSET IS USED AS A BACKWARD POINTER.
;THIS IS TRUE ONLY BETWEEN THE TIMES WHEN THE ENTRY
;IS QUEUED (VIA QUEFRK) AND WHEN IT IS REMOVED (VIA SCSPSI). AFTER THE
;ENTRY IS REMOVED FROM THE SCATWQ LIST, THE .MEANC AND .MEAPC OFFSETS
;ARE USED FOR ANOTHER PURPOSE.
;
SCSPSI::SKIPN SCATWQ		;Is there anything at all on the queue???
	RET			;No, nothing to do here
	SAVEAC <Q1,Q2,Q3,P1>	;Save the ACs we will need here
	STKVAR <BLKADR,EVTTYP,NXTENT>	;Address of the current event block 
				;NXTENT IS THE NEXT ENTRY ON
				;THE SCATWQ QUEUE.

PSIELP:	CIOFF			;We need to be alone for this...
	SKIPN T1,SCATWQ		;Is there another entry???
	JRST PSIEXI		;No, bail out here
	JRST PSIFRK		;GO CHECK IF ENTRY IS FOR THIS FORK.
PSINXT:	CIOFF
	SKIPN T1,NXTENT		;GET ADDRESS OF NEXT ENTRY.
	JRST PSIEXI		;NONE. FINISHED FOR THIS FORK.
PSIFRK:	LOAD T2,MEFRK,(T1)	;Get the target fork number
	CAMN T2,FORKX		;Is this one for us???
	IFSKP.
	 SKIPN T1,(T1)		;Get the addr of the next entry
	 JRST PSIEXI		;No next entry, fall out here
	 JRST PSIFRK		;Test the next entry
	ENDIF.
	MOVE T2,.MEANC(T1)	;GET ADDRESS OF THE NEXT ENTRY
	MOVEM T2,NXTENT		;SAVE IT.

				;NOW DELETE THIS ENTRY FROM THE SCATWQ LIST.
	SKIPE T2		;IS THERE A NEXT ENTRY?
	IFSKP.			;NO.
	  SKIPE T2,.MEAPC(T1)	;IS THERE PREVIOUS ENTRY?
	  IFSKP.		;NO. THEN THIS IS THE ONLY ENTRY.
	    SETZM SCATWQ	;RESET THE SCATWQ AND SCABWQ POINTERS.
	    XMOVEI T2,SCATWQ
	    MOVEM T2,SCABWQ
	  ELSE.			;YES. THEN THIS IS THE LAST ENTRY.
	    SETZM .MEANC(T2)	;ZERO THE FORWARD POINTER IN PREVIOUS ENTRY.
	    MOVEM T2,SCABWQ	;RESET THE POINTER FOR NEXT TIME AROUND.
	  ENDIF.
	ELSE.			;YES, THERE IS A NEXT ENTRY.
	  SKIPE T3,.MEAPC(T1)	;IS THERE A PREVIOUS ENTRY ON THE QUEUE?
	  IFSKP.		;NO, THEN THIS IS THE FIRST ENTRY.
	    SETZM .MEAPC(T2)	;SETUP BACKWARD POINTER.
	    MOVEM T2,SCATWQ	;SET UP SCATWQ
	  ELSE.			;YES, THERE IS PREVIOUS ENTRY.
	    MOVEM T3,.MEAPC(T2)	;SET NEXT TO POINT TO PREVIOUS.
	    MOVEM T2,.MEANC(T3)	;SET PREVIOUS TO POINT TO NEXT.
	  ENDIF.
	ENDIF.			;ENTRY NOW DELETED FROM SCATWQ. CONTINUE ON.

	CION			;Allow world access again
	MOVEM T1,BLKADR		;Save the event block address
	LOAD T1,MECID,(T1)	;Get the CID from the event block
	CAMN T1,[-1]		;Is this a special block with no CID???
	IFNSK.
	 SETO P1,0		;Yes, special block, show address of CB as -1
	 JRST PSINDL		;Yes, no CB work to do
	ENDIF.

; Here to see if this event requires bits to be set in the connect block.

	CALL SC.CSC		;Sanity check the CID
	 JRST PSIBER		;Stale CID, do not queue this block
	MOVX T1,CBFRAP		;Get reap bit
	TDNE T1,.CBFLG(P1)	;Is this still a valid CB???
	JRST PSIBER		;No, loop for next entry

; Here to put the new entry onto the fork and connection queues.

	MOVE T1,BLKADR		;Get the event block address again
	$LDCID P1,.MECID(T1)	;Now for the CB address
	LOAD T2,METYP,(T1)	;Get the entry type
	MOVEM T2,EVTTYP		;Save the event type
	MOVE T2,PSITAB(T2)	;Now the address of the list pointer block

; Add the entry to the CB queue.

	MOVE Q1,.TOPCQ(T2)	;Pointer to CB queue top pointer
	XMOVEI Q1,@Q1		;Turn index into CB into a real address
	MOVE Q2,.BOTCQ(T2)	;Pointer to CB queue bottom pointer
	XMOVEI Q2,@Q2		;Turn index into CB into a real address
	TXO Q2,C%IND		;Turn on the indirect bit for this address
	SETZM .MEANC(T1)	;There is no next buffer since we are last
	SETZM .MEAPC(T1)	;  and for now assume there is no previous
	CIOFF			;Time to play the queue game again
	MOVE T3,(Q2)		;Get the current last entry
	CAME T3,Q1		;Is this a pointer to the q head pointer???
	MOVEM T3,.MEAPC(T1)	;No, update the previous pointer of new entry
	MOVEM T1,@Q2		;Link this entry onto the end of the queue
	MOVEM T1,(Q2)		;  and update the tail pointer
	CION			;Restore world access again

; Here to put the new entry onto the fork queues.

PSINDL:	MOVE T1,BLKADR		;Get the entry address
	LOAD T2,METYP,(T1)	;Get the entry type
	MOVEM T2,EVTTYP		;Save the event type
	MOVE T2,PSITAB(T2)	;Now the address of the list pointer block

; Add the entry to the fork queue

	MOVE Q1,.TOPFQ(T2)	;Get addr of the fork queue FLINK
	MOVE Q2,.BOTFQ(T2)	;Get addr of the fork queue BLINK
	SETZM .MEANF(T1)	;No next on fork queue since we are last
	SETZM .MEAPF(T1)	;  and for now, assume no previous
	CIOFF			;Time once again, to play with queue pointers
	MOVE T4,@Q1		;Save the current state of the queue
	MOVE T3,@Q2		;Get addr of current last entry
	CAMN T3,Q1		;Is this a pointer to the FLINK???
	IFNSK.
	 MOVEM T1,(Q1)		;Yes, update the fork Q FLINK
	ELSE.
	 MOVEM T3,.MEAPF(T1)	;No, update link to previous of new entry
	 MOVEM T1,.MEANF(T3)	;Update next field of previous entry
	ENDIF.
	MOVEM T1,(Q2)		;In any case, update the fork queue BLINK
	CION			;Restore world access
	JUMPN T4,PSINXT		;If there were entries before, don't give a PSI
	MOVE T4,EVTTYP		;Get the event type
	XCT PSIXCT(T4)		;Fetch the PSI channel from the PSB
	CAIN T1,-1		;Is it the null channel???
	JRST PSINXT		;Yes, don't give a PSI
	MOVE T3,BLKADR		;Get the event block address again
	LOAD T2,MEFRK,(T3)	;Get the fork to interrupt
	CALL PSIRQ		;Queue a PSI interrupt for the fork
	JRST PSINXT		;Loop for more entries

; Here on finding an old CB. Return the free space and loop for the next
;entry.

PSIBER:	MOVE T1,BLKADR		;Get the free space addr back
	LOAD T2,METYP,(T1)	;Get the entry type
	CAILE T2,.ETDMA		;Check entry type
PSIBUG:	BUG. (CHK,SCSBDE,SCSJSY,SOFT,<SCA: BAD ENTRY TYPE FOUND>,<<T2,TYE>,<T1,BLKADR>>,<
Cause: An illegal type of message buffer was attempted to be returned.
       it is now lost.
>,PSINXT)
	JUMPE T2,PSIBUG		;Check for type of 0
	CALL @[	IFIW!SC.RBF	;Free correct data type
		IFIW!SC.RLD
		IFIW!RELRES
		IFIW!RELRES]-1(T2)
	JRST PSINXT		;Loop for more entries

; Here to leave this routine

PSIEXI:	CION			;Restore CI interrupts
	RET			;  and return

	ENDSV.
	SUBTTL Support routines -- SCSQUE (Queue a block for list queueing)

; This routine places blocks on a system wide queue for the scheduler.
;This is the routine called by interrupt level to get a block moved up to
;the user. The route is as follows:
;
; 1. Interrupt level calls SCSQUE which places blocks on a system wide queue
;	and sets off a timer.
; 2. The timer goes off and the scheduler runs SCSCLK. SCSCLK loops over
;	the system wide queue placing an interrupt for the indicated target 
;	forks.
; 3. The interrupt happens (at SCSPSI) and in the target fork's context we 
;	take entries off the system wide queue and place them on the fork's 
;	queue. We also queue a PSI interrupt for the fork if needed.
; 
; Note:
;	This routine assumes that if the CID in the block is -1 the block
; is a special fork only block. Hence the target fork number has already been
; filled in and there is no connect block to retrieve data from.
;
; Usage:
;	Call
;	T1/	Addr of block to be queued
;	T2/	Block type code
;
;	Return (+1) Always
;	T1/	Addr of block which was queued
;	T2/	Block type code
;
SCSQUE:	SAVEAC <P1>		;We need the CB addr AC here...
	SETZM .MEANC(T1)	;Zero any old link pointers
	SETZM .MEAPC(T1)	;.	.	.
	SETZM .MEANF(T1)	;.	.	.
	SETZM .MEAPF(T1)	;.	.	.
	STOR T2,METYP,(T1)	;Store the block type in the event block

	LOAD P1,MECID,(T1)	;Get the CID from the event block
	CAMN P1,[-1]		;Is this a special fork only block???
	JRST QUEFRK		;Yes, do no CID checking
	$LDCID P1,P1		;Now the CB addr
	LOAD T3,CBFORK,(P1)	;Get the fork this block applies to
	STOR T3,MEFRK,(T1)	;Save in the block


				;BETWEEN THE TIME THE ENTRY IS QUEUED (VIA
				;QUEFRK) AND REMOVED FROM THE SCATWQ LIST,(VIA
				;SCSPSI)THE .MEANC OFFSET OF THE ENTRY WILL BE
				;USED AS A FORWARD LINK AND THE .MEAPC 
				;OFFSET WILL BE USED AS A BACKWARD LINK.
				;A ZERO IN EITHER ENTRY INDICATES
				;THE END OF THE CHAIN IN THAT PARTICULAR
				;DIRECTION.

QUEFRK:	CIOFF			;We need to be alone to diddle queues
	SKIPN SCATWQ		;ANY ENTRIES ON THE QUEUE
	IFSKP.			;YES.
	  MOVE T3,SCABWQ	;GET ADDRESS OF LAST ENTRY
	  MOVEM T3,.MEAPC(T1)	;UPDATE THE BACKWARD LINK IN ENTRY TO BE ADDED.
	ENDIF.
	MOVEM T1,@SCABWQ	;UPDATE THE FORWARD LINK OR SCATWQ
	MOVEM T1,SCABWQ		;UPDATE SCABWQ
	CION			;Restore the PI pls...
	SETZM SCSTIM		;Tell scheduler that its time to run SCSCLK
	RET			;All done...
	SUBTTL Support routines -- SCSMBS (Make another BSD)

;This routine creates a BSD and links it onto the list of BSD's for a 
;connection.
;
; Note: This routine needs to be protected from user interrupts. SC.ALD must
;be allowed to complete and the obtained resource must not be lost.
;
;	Call
;	P1/	Address of connection block
;
;	Return (+1)
;	T1/	Error code
;
;	Return (+2)
;	T1/	Address of the new BSD
;	P1/	Address of the connection block
;
SCSMBS::MOVX T1,1		;We only need one buffer thank you.
	CALL SC.ALD		;Allocate a long datagram buffer
	 RETBAD ()		;No buffers, we loose, return badness
	XMOVEI T2,.BDFMG(T1)	;Addr of first message pointer
	MOVEM T2,.BDLMG(T1)	;Init message tail as pointer to head
	SETZM .BDFMG(T1)	;Zero the message entry head
	XMOVEI T2,.BDFDG(T1)	;Addr of first datagram pointer 
	MOVEM T2,.BDLDG(T1)	;Init tail as pointer to head
	SETZM .BDFDG(T1)	;Init head as zero
	MOVX T4,C%NBSD		;Get the number of entries in a BSD
	MOVEM T1,T2		;A copy we can play with
	ADDI T2,.BDBDB		;Offset this to the first slot address
	MOVEM T2,.BDFFD(T1)	;Store as the addr of the first free entry
	MOVEM T2,T3		;Dont smash the pointer to last entry
MBSLOP:	ADDI T3,.BBLEN		;Add the offset to the next entry
	MOVEM T3,(T2)		;Link on this next entry
	MOVE T2,T3		;Move to the next buffer
	SOJG T4,MBSLOP		;Loop for the entire BSD
	MOVEM T3,.BDLFD(T1)	;Store the list tail pointer
	SETZM (T3)		;Insure a null last entry pointer
	CIOFF			;Interlock the BSD list on this connection
	MOVEM T1,@.CBBBQ(P1)	;Link this new buffer onto the end of the queue
	MOVEM T1,.CBBBQ(P1)	;  and update the tail pointer
	CION			;Release BSD queue interlock
	RETSKP			;Return all happy...
	SUBTTL Support routines -- SCSDEQ (Dequeue a buffer from fork+CB Q's)

; This routine dequeues a packet from the fork and connection lists it is on.
;
; Note:
;	May only be called from process context.
;
;	Call
;	T1/	Address of packet
;	T2/	Address of 4 word list header block
;	P1/	Address of CB or -1, if -1 there is no CB entry to delete
;
;	Return (+1) Error
;	T1/	Error code
;
;
;	Return (+2) No error
;	T1/	Address of packet
;
SCSDEQ::SAVEQ
	STKVAR <PAKADR,BLKADR>
	MOVEM T1,PAKADR		;Save the packet address
	MOVEM T2,BLKADR		;  as well as the addr of the list header block
	JUMPL P1,DEQFRK		;This a fork only entry? If yes, skip CB stuff
	CIOFF			;Turn off PI while entry addr's are in AC's
	MOVE T3,.MEANC(T1)	;Get next connection entry
	SKIPN T4,.MEAPC(T1)	;Is there another entry
	SKIPE T3		;   on the queue???
	JRST DEQMEL		;No, there are entries left on the queue
	XMOVEI T3,@.TOPCQ(T2)	;Get the addr of the top of the queue
	MOVEM T3,@.BOTCQ(T2)	;Init tail pointer as pointer to head
	SETZM @.TOPCQ(T2)	;Init head pointer as zero
	CION			;Allow world access again
	JRST DEQFRK		;Now do the fork list

DEQMEL:	SKIPE T3		;Is there a next entry???
	MOVEM T4,.MEAPC(T3)	;Yes, update previous entry pointer in nxt buff
	SKIPN T3		;Is there a next entry???
	MOVEM T4,@.BOTCQ(T2)	;No, update the tail pointer in the CB
	SKIPE T4		;Is there a previous entry???
	MOVEM T3,.MEANC(T4)	;Yes, update next pointer of previous
	SKIPN T4		;Is there a previous entry???
	MOVEM T3,@.TOPCQ(T2)	;No, update the head pointer in the CB
	CION			;Allow world access again

; Here to remove the entry from the fork list
;
DEQFRK:	SKIPL P1		;Is this fork only case???
	IFNSK.
	 MOVX T3,CBFKIL		;No, see if fork with connect is dead
	 TDNE T3,.CBFLG(P1)	;Is the fork already gone???
	 RETSKP			;Yes, then don't try to delete fork Q entries.
	ENDIF.

	LOAD T1,MEFRK,(T1)	;Get the owning fork number
	NOSKED			;Interlock the ownership of a mapped PSB
	CALL SCSMPS		;Map the PSB of the target fork
	 RETBAD (,<OKSKED>)
	MOVE T3,BLKADR		;Get well as the header block addr
	MOVE Q1,.TOPFQ(T3)	;Get the addr of the fork Q top
	ANDX Q1,777		;Keep only the page offset
	ADD Q1,T2		;Offset into PSB page
	MOVE Q2,.BOTFQ(T3)	;Get the addr of the fork Q bottom pointer
	ANDX Q2,777		;Keep only the page offset
	ADD Q2,T2		;Offset into PSB page
	MOVE T1,PAKADR		;Restore the packet address

	MOVE T3,.MEANF(T1)	;Get the previous entry
	SKIPN T4,.MEAPF(T1)	;Is there no next entry
	SKIPE T3		;   and no previous entry (I.E. last entry)???
	JRST DEQMFE		;No, there are entries left on the queue

; Deleting last entry on queue.
;
	MOVE T3,BLKADR		;Get header block address again
	MOVE T3,.TOPFQ(T3)	;Now the real top of queue pointer address
	MOVEM T3,(Q2)		;Init tail pointer as pointer to head
	SETZM (Q1)		;Init head pointer as zero
	JRST DEQEXI		;All done, exit please...

; Here when there are other fork entries.
;
DEQMFE:	SKIPE T3		;Is there a next entry???
	MOVEM T4,.MEAPF(T3)	;Yes, update previous entry in next buffer
	SKIPN T3		;Is there a next entry???
	MOVEM T4,(Q2)		;No, update the tail pointer in the PSB
	SKIPE T4		;Is there a previous entry???
	MOVEM T3,.MEANF(T4)	;Yes, update next pointer of previous
	SKIPN T4		;Is there a previous entry???
	MOVEM T3,(Q1)		;No, update the head pointer in the PSB

DEQEXI:	CALL SCSUPS		;Unmap the PSB
	OKSKED			;Allow world access
	MOVE T1,PAKADR		;Restore the entry address
	RETSKP			;All done

	ENDSV.
	SUBTTL Support routines -- SCSMPS (Map a forks PSB)

; Jacket routine for SETCPT. Maps the target forks PSB if the target is
;not the current fork. Also not that it is expected that multiple routines
;will try to map the PSB for the same fork at the same time. This is a result
;of more than one routine needing access to the PSB to perform its function.
;It is not normal however to try to map the PSB of two different forks at the
;same time. This would indicate that someone neglected an interlock.
;
; Usage:
;	Call
;	T1/	Fork number
;
;	Return (+1)
;	T1/	Fork number
;
;	Return (+2)
;	T1/	Fork number
;	T2/	Base address of target fork's PSB
;
; Note:
;	It is the responsibility of the caller to provide an interlock while
;we own the PSB.
;
SCSMPS:	SKIPL T2,MPSFRK		;Have we got a fork mapped already???
	IFNSK.
	 CAME T1,T2		;Yes, are we mapping the same fork again???
 	 RETBAD (MONX03)	;NO! Someone missed an interlock, fail
	 AOS MPSCNT		;Increment the nesting count
	 AOS MPSNST		;Count a nested fork map event
	 MOVE T2,MPSADR		;Get the address we reported the last time
	 RETSKP			;  and return as if we did the work again
	ENDIF.

	MOVEM T1,MPSFRK		;Save the fork number of last fork mapped

	CAMN T1,FORKX		;Are we the target fork???
	IFNSK.
	 AOS MPSCNT		;Increment the nesting level
	 AOS MPSFKX		;Count a mapped ourselves occurance
	 XMOVEI T2,PSBPGA	;Point to the base of our PSB
	 MOVEM T2,MPSADR	;Save the last reported address
	 RETSKP			;  and return
	ENDIF.

	STKVAR <FRKN>
	AOS MPSCPT		;Count a use of SETCPT for fork mapping
	MOVEM T1,FRKN		;Save the fork number
	LOAD T1,FKPS%,(T1)	;Get the SPT index for the target PSB
	HRLZS T1		;Put the SPT slot where SETCPT wants it
	CALL SETCPT		;Map the PSB of the target fork
	MOVE T1,FRKN		;Restore fork number
	AOS MPSCNT		;Increment the nesting count
	XMOVEI T2,CPTPGA	;Show the base addr of the PSB
	MOVEM T2,MPSADR		;Save the last reported address
	RETSKP			;All done

	ENDSV.
	SUBTTL Support routines -- SCSUPS (Unmap a forks PSB)

; Routine to unmap the PSB mapped with SCSMPS.
;
; Usage:
;	Call
;	No arguments required
;
;	Return (+1) Always
;
; Note:
;	It is the responsibility of the caller to provide an interlock while
;we own the PSB.
;
SCSUPS:	SOSLE MPSCNT		;Have we reached the base nesting level???
	RET			;No, nothing to do yet
	MOVE T1,FORKX		;Get the current fork
	CAME T1,MPSFRK		;Did we map the current fork???
	CALL RELCPT		;No, release the mapped PSB page
	SETOM MPSFRK		;Show we have no PSB mapped
	RET			;All done
	SUBTTL Support routines -- SCSLUB (Link on user buffer addr)

; This routine links a user buffer address into a BSD for the current
;connection.
;
; Usage:
;	Call
;	T1/	User address of message buffer
;	T2/	Offset in BSD to desired FLINK (indicates desired queue)
;	P1/	CB addr
;
;	Return (+1)
;	T1/	Error codes
;
;	Return (+2)
;	Entry is linked, T1-T4 ARE SMASHED!
;
SCSLUB:	STKVAR <BUFADR,BSDOFS>
	CIOFF			;We need to do this alone please...
	MOVEM T1,BUFADR		;Save the user buffer address
	MOVEM T2,BSDOFS		;  and the BSD offset
	SKIPE T3,.CBTBQ(P1)	;Are there any BSD's on this connect???
	JRST LUBAHD		;Yes, dont create one

LUBMBS:	CALL SCSMBS		;Create the BSD
	 RETBAD (<>,<CION>)	;Return badness with error code in T1
	MOVE T3,T1		;Put the BSD addr where expected
	MOVE T2,BSDOFS		;Restore the BSD offset
	MOVE T1,BUFADR		;  and the user address of message buffer

LUBAHD:	SKIPE T4,.BDFFD(T3)	;Is there a free entry in the BSD???
	IFSKP.			;NO.
	  SKIPE T3,.BDNXT(T3)	;IS THERE ANOTHER BSD ON THE CHAIN?
	  JRST LUBAHD		;YES. GO FIND THE FIRST FREE ENTRY IN THIS BSD.
	  JRST LUBMBS		;NO.  GO CREATE A NEW BSD.
	ENDIF.
	ADD T2,T3		;Calculate the addr of the FLINK
	MOVEM T1,.BBUVA(T4)	;Store user addr in BSD entry
	MOVE T1,.BBNXT(T4)	;Get the addr of the next buffer
	MOVEM T1,.BDFFD(T3)	;Delete this entry from the free queue
	SETZM .BBNXT(T4)	;Zero the forward link of the new entry
	JUMPN T1,LUBNLF		;If next entry don't do Q end code

	XMOVEI T1,.BDFFD(T3)	;Get the addr of the free queue FLINK
	MOVEM T1,.BDLFD(T3)	;Init BLINK as pointer to FLINK

LUBNLF:	MOVEM T4,@.BDF2B(T2)	;Link this buffer onto the end of the queue
	MOVEM T4,.BDF2B(T2)	;  and update the queue BLINK
	MOVE T2,BSDOFS		;Get the BSD offset
	CAIN T2,.BDFDG		;Was this a new datagram???
	AOS .CBDGJ(P1)		;Yes, increment the count of JSYS DG buffers
	CAIN T2,.BDFMG		;Was this a new message???
	AOS .CBMGJ(P1)		;Yes, update the JSYS message buffer count

	CION			;Restore CI interrupts
	RETSKP			;Return all OK

	ENDSV.
	SUBTTL Support routines -- SCSGUB (Get user buffer address)

; This routine obtains the address of a buffer in user space.
;
; (This routine is expected to remove the buffer from the BSD)
;
;	Call
;	T1/	Offset into BSD indicating packet type
;	P1/	Address of connection block
;
;	Return (+1)
;	T1/	Error code
;	P1/	Address of connection block
;	(No buffers available)
;
;	Return (+2)
;	T1/	Address of buffer in user space
;	P1/	Address of connection block
;
SCSGUB:	LOAD T2,CBTBQ,(P1)	;Get the address of the first BSD
	JUMPE T2,[RETBAD (SCSNSB)]	;If no BSD then no message buffers
	SAVEQ			;Save addr ACs
	MOVE Q1,T2		;Save the addr of the BSD
	MOVE Q2,T2		;Get the BSD addr again
	MOVE Q3,T1		;Save the BSD offset indicated
	ADD Q2,T1		;Build this into the FLINK address
	CIOFF			;Interlock this BSD
GUBELP:	SKIPN T4,(Q2)		;Get the address of the first packet entry
	 JRST GUBTNB		;None in this BSD, try the next one
					;NOW THAT AN ENTRY HAS BEEN FOUND
					;FOR A DG OR MSG, UPDATE THE FREE
					;QUEUE POINTERS OF THE BSD AND THE
					;DG OR MSG QUEUE POINTERS OF THE BSD.

					;FIRST, UPDATE THE DG OR MSG POINTERS.
	SKIPE .BBNXT(T4)		;ONLY DG OR MSG ENTRY?
	IFSKP.				;YES.
	  SETZM (Q2)			;SET THE DG OR MSG FIRST ENTRY TO ZERO
	  MOVEM Q2,.BDF2B(Q2)		;SET THE DG OR MSG LAST POINTER TO POINT
					;TO THE FIRST POINTER.
	ELSE.				;NO.
	  MOVE T1,.BBNXT(T4)		;GET THE ADDRESS OF THE SECOND DG OR MSG
	  MOVEM T1,(Q2)			;MAKE IT THE FIRST.
	ENDIF.

					;NOW, UPDATE THE FREE QUEUE POINTERS.
	SKIPE .BDFFD(Q1)		;ANY ENTRIES IN FREE QUEUE OF BSD?
	IFSKP.				;NO. 
	  MOVEM T4,.BDLFD(Q1)		;UPDATE LAST FREE ENTRY POINTER BECAUSE
					;IT POINTS TO FIRST FREE QUEUE POINTER
					;WHEN FREE QUEUE IS EMPTY.
	  SETZM .BBNXT(T4)		;SET END OF FREE QUEUE ENTIES TO ZERO.
	ELSE.				;YES.
	  MOVE T1,.BDFFD(Q1)		;GET THE ADDRESS OF THE FIRST ENTRY.
	  MOVEM T1,.BBNXT(T4)		;MAKE NEW FREE QUEUE ENTRY POINT TO
					;THE PREVIOUS FIRST FREE QUEUE ENTRY.
	ENDIF.
	MOVEM T4,.BDFFD(Q1)		;UPDATE THE FIRST FREE ENTRY POINTER
		
	MOVE T1,.BBUVA(T4)	;Get the user address from this entry
	CAIN Q3,.BDFDG		;Did we get a datagram???
	DECR CBDGJ,(P1)		;Yes, update the JSYS buffer count
	CAIN Q3,.BDFMG		;Did we get a message???
	DECR CBMGJ,(P1)		;Yes, update the JSYS buffer count
	CION			;Release BSD interlock
	RETSKP			;Return all happy

;Here to try the next BSD...
;
GUBTNB:	SKIPE Q1,.BDNXT(Q1)	;Get the address of the next BSD
	 JRST GUBNXT		;We have the next BSD, look for DG entries
	CION			;No next BSD, loose here...
	RETBAD (SCSNSB)		;Fail please

GUBNXT:	MOVE Q2,Q1		;Get the new BSD addr
	ADD Q2,T1		;Add the offset to the FLINK
	JRST GUBELP		;  and loop for more datagrams
	SUBTTL Support routines -- SCSRET (Return buffer to correct pool)

; Common routine to return buffers to SCA pool.
;
;Usage:
;
;	Call
;	T1/ Address of buffer
;
;
;	Returns (+1) Always
;
;Note: Buffer must have been initialized so that .JHFLG indicates the
;kind of buffer
;
;Note: Also, this routine must be protected from user interrupts such that
;the SCA calls are guarenteed to complete.
;
SCSRET:	MOVX T2,JH%DGB		;Get the datagram buffer bit
	TDNN T2,.JHFLG(T1)	;Is this a datagram buffer???
	CALLRET SC.RBF		;No, return the message buffer please
	CALLRET SC.RLD		;yes, return this datagram buffer
	SUBTTL	Storage

	.PSECT RSDAT

FCNTAB:	$SCSFC			;Function code dispatch table for SCS JSYS 

CALTAB:	$CALTB			;Dispat for SCA to SYSAP calls

CBPSI:	$BUILD (.SIHGH+1)	;Block for storing PSI codes into CB's
	$SET (.SIDGA,<>,<HRRM T2,.CBPS0(P1)>);Instruction to store DG int chan
	$SET (.SIMSA,<>,<HRLM T2,.CBPS0(P1)>);Instruction to store MSG int chan
	$SET (.SIDMA,<>,<HRLM T2,.CBPS1(P1)>);Instruction to store DMA int chan
	$SET (.SIPAN,<>,<HRRM T2,.CBPS1(P1)>);Instruction store EVNT int chan
	$EOB

PSBPSI:	$BUILD (.SIHGH+1)	;Block for storing PSI codes into PSB's
	$SET (.SIDGA,<>,<HRRM T2,SCSPS0>);Instruction to store DG int chan
	$SET (.SIMSA,<>,<HRLM T2,SCSPS0>);Instruction to store MSG int chan
	$SET (.SIDMA,<>,<HRLM T2,SCSPS1>);Instruction to store DMA int chan
	$SET (.SIPAN,<>,<HRRM T2,SCSPS1>);Instruction store EVNT int chan
	$EOB

MSG::	$BUILD (QLEN)
	$SET (.TOPCQ,<>,<<IFIW>!<.CBTMQ(P1)>>) ;Top of CB message queue
	$SET (.BOTCQ,<>,<<IFIW>!<.CBBMQ(P1)>>) ;Bot of CB message queue
	$SET (.TOPFQ,<>,<MSEC1,,SCSTMQ>) ;Top of fork message queue
	$SET (.BOTFQ,<>,<MSEC1,,SCSBMQ>) ;Bot of fork message queue
	$SET (.JBUFF,<>,<IFIW!.CBMGJ(P1)>) ;Count of JSYS MSG buffers
	$EOB

DG::	$BUILD (QLEN)
	$SET (.TOPCQ,<>,<<IFIW>!<.CBTDQ(P1)>>) ;Top of CB datagram queue
	$SET (.BOTCQ,<>,<<IFIW>!<.CBBDQ(P1)>>) ;Bot of CB datagram queue
	$SET (.TOPFQ,<>,<MSEC1,,SCSTDQ>) ;Top of fork datagram queue
	$SET (.BOTFQ,<>,<MSEC1,,SCSBDQ>) ;Bot of fork datagram queue
	$SET (.JBUFF,<>,<IFIW!.CBDGJ(P1)>) ;Count of JSYS DG buffers
	$EOB

XFER::	$BUILD (QLEN)
	$SET (.TOPCQ,<>,<<IFIW>!<.CBTXQ(P1)>>) ;Top of CB xfer queue
	$SET (.BOTCQ,<>,<<IFIW>!<.CBBXQ(P1)>>) ;Bot of CB xfer queue
	$SET (.TOPFQ,<>,<MSEC1,,SCSTXQ>) ;Top of fork xfer queue
	$SET (.BOTFQ,<>,<MSEC1,,SCSBXQ>) ;Bot of fork xfer queue
	$EOB

EVT::	$BUILD (QLEN)
	$SET (.TOPCQ,<>,<<IFIW>!<.CBTEQ(P1)>>) ;Top of CB event queue
	$SET (.BOTCQ,<>,<<IFIW>!<.CBBEQ(P1)>>) ;Bot of CB event queue
	$SET (.TOPFQ,<>,<MSEC1,,SCSTEQ>) ;Top of fork event queue
	$SET (.BOTFQ,<>,<MSEC1,,SCSBEQ>) ;Bot of fork event queue
	$EOB

PSITAB:	$BUILD (4+1)		;Table of queue pointers,4 types, but no type 0
	$SET (.ETMSG,<>,MSG)	;Pointer message list pointer block
	$SET (.ETDG,<>,DG)	;Pointer to datagram list pointer block
	$SET (.ETEVT,<>,EVT)	;Pointer to event list pointer block
	$SET (.ETDMA,<>,XFER)	;Pointer to DMA list pointer block
	$EOB

;Warning, do not change the loaded AC without changing SCSPSI as well...
;
PSIXCT:	0			;No code 0
	HLRZ T1,SCSPS0		;Load the message available channel
	HRRZ T1,SCSPS0		;Load the datagram avail channel
	HRRZ T1,SCSPS1		;Load the event channel
	HLRZ T1,SCSPS1		;Load the DMA channel

	.ENDPS RSDAT

; Random locations

	RSI (SCATWQ,<0>,1)		 ;Top pointer for SCS% working queue
	RSI (SCABWQ,<<MSEC1,,SCATWQ>>,1) ;Bottom pointer for SCS% working queue

	RSI (SCATMQ,<0>,1)		 ;FLINK for maint xfer Q
	RSI (SCABMQ,<<MSEC1,,SCATMQ>>,1) ;BLINK for maint xfer Q

	RSI (MPSFRK,<-1>,1)	;Last fork mapped by SCSMPS
	RSI (MPSCNT,<0>,1)	;Nesting level count for SCSMPS
	RSI (MPSADR,<-1>,1)	;Last address reported by SCSMPS
	RSI (MPSFKX,<0>,1)	;Count of times we asked to map ourselves
	RSI (MPSCPT,<0>,1)	;Count of times we used SETCPT to map PSB
	RSI (MPSNST,<0>,1)	;Count of times we did a nested MPS call

; Support location for CION/CIOFF
;
	RS (PIFLAG)		;-1 -- Last real CIOFF did a channel off
				; 0 -- Don't channel on at last CION in nest
	END