Google
 

Trailing-Edge - PDP-10 Archives - BB-M080K-SM - monitor-sources/latsrv.mac
There are 19 other files named latsrv.mac in the archive. Click here to see a list.
; *** Edit 7234 to LATSRV.MAC by PALMIERI on 27-Jan-86, for SPR #21077 (TCO 6.1.1572)
; CB not initialized if MAXIMUM ACTIVE CIRCUITS is exceeded. 
; *** Edit 7231 to LATSRV.MAC by MELOHN on 27-Jan-86 (TCO 6.1.1571)
; Change CB index to SB index for data waiting flag in RELSB
;------------------------- Autopatch Tape # 12 -------------------------
; UPD ID= 2305, SNARK:<6.1.MONITOR>LATSRV.MAC.61,  31-Jul-85 15:23:24 by MELOHN
;TCO 6.1.1503 - Fix flow control to DECserver 100s.
; UPD ID= 2298, SNARK:<6.1.MONITOR>LATSRV.MAC.60,  16-Jul-85 17:20:36 by MELOHN
;TCO 6.1.1485 - set up correct reason code when stopping circuit after LATIST.
; UPD ID= 2296, SNARK:<6.1.MONITOR>LATSRV.MAC.59,  15-Jul-85 15:43:33 by MELOHN
;TCO 6.1.1484 - At HRUNCB, range check the CB index.
;TCO 6.1.1483 - Fix Zero Counters to zero the correct number of words.
; UPD ID= 2293, SNARK:<6.1.MONITOR>LATSRV.MAC.58,  10-Jul-85 16:52:18 by MELOHN
;TCO 6.1.1480 - Fix NTINF% routine to return the properly when no server name.
; UPD ID= 2288, SNARK:<6.1.MONITOR>LATSRV.MAC.57,  28-Jun-85 15:08:06 by PALMIERI
; TCO 6.1.1476  Save address of receive buffer at GETRBF in case the attempt to
;post it to NISRV fails because we will then free it.
; UPD ID= 2287, SNARK:<6.1.MONITOR>LATSRV.MAC.56,  28-Jun-85 14:28:15 by NICHOLS
;Fix typo in 2217 (Thanks, Carl Appellof)
; UPD ID= 2284, SNARK:<6.1.MONITOR>LATSRV.MAC.55,  26-Jun-85 18:14:43 by NICHOLS
;TCO 6.1.2297 - If we receive a stop slot for a zero dest, ignore rather
; than complaining about illegal slot.  This bypasses a bug in LAT-11.
; UPD ID= 2217, SNARK:<6.1.MONITOR>LATSRV.MAC.54,  12-Jun-85 15:13:54 by MELOHN
;TCO 6.1.1445 - Correctly parse multiple slots in message at LSDMUX
;TCO 6.1.1431 - Fix MMVAZS to work with non-asciz strings.
; UPD ID= 2096, SNARK:<6.1.MONITOR>LATSRV.MAC.52,   3-Jun-85 14:52:19 by MCCOLLUM
;TCO 6.1.1406  - Update copyright notice.
; UPD ID= 2092, SNARK:<6.1.MONITOR>LATSRV.MAC.51,   3-Jun-85 14:48:58 by MELOHN
;More of last edit - fix off by one in bit flag sent to terminal.
; UPD ID= 2012, SNARK:<6.1.MONITOR>LATSRV.MAC.50,  24-May-85 16:04:29 by MELOHN
;TCO 6.1.1403 - Fix DATA_B slot formatting to send all XON/XOFF chars.
;TCO 6.1.1238 - Fix BUG. documentation
; UPD ID= 1660, SNARK:<6.1.MONITOR>LATSRV.MAC.49,  20-Mar-85 15:04:28 by MELOHN
;TCO 6.1.1280 - Reorder the CB BEGSTR so that correct info is returned by LATOP
; UPD ID= 1637, SNARK:<6.1.MONITOR>LATSRV.MAC.48,  14-Mar-85 15:36:41 by MELOHN
;TCO 6.1.1264 - Set the change flags when adding a new service to multi-cast.
; UPD ID= 1626, SNARK:<6.1.MONITOR>LATSRV.MAC.46,  12-Mar-85 19:02:38 by MELOHN
;TCO 6.1.1252 - Add LATIST buginf when received illegal slot in legal message.
;TCO 6.1.1251 - Make LATOP% defaults reasonable; fix host num to be set at INI.
;TCO 6.1.1250 - Make Retransmit timer use TODCLK instead of sched cycles.
; UPD ID= 1593, SNARK:<6.1.MONITOR>LATSRV.MAC.45,   5-Mar-85 23:05:26 by MELOHN
;TCO 6.1.1223 - Implement Host Keep Alive Timer 2 times server_keep_alive secs.
;TCO 6.1.1216 - HMSTOP - use local CB ID for received stop msgs instead of RID.
; UPD ID= 1540, SNARK:<6.1.MONITOR>LATSRV.MAC.44,  19-Feb-85 23:08:46 by MELOHN
;TCO 6.1.1214 - Fix Dynamic service ratings to be checked/updated at LATXMC.
; UPD ID= 1514, SNARK:<6.1.MONITOR>LATSRV.MAC.43,  13-Feb-85 01:32:59 by MELOHN
;TCO 6.1.1192 - there must be at least one offered service in multi-cast msg.
; don't allow users to clear the last offered service via LATOP%.
; UPD ID= 1507, SNARK:<6.1.MONITOR>LATSRV.MAC.42,  12-Feb-85 11:23:25 by MELOHN
; TCO 6.1.1191:
;always zero nslots at LAMUX so that we dont send bogus msgs to posideon -207-
; TCO 6.1.1190:
;don't send stop circuit when nslots becomes zero - server will stop it for us.
; UPD ID= 1380, SNARK:<6.1.MONITOR>LATSRV.MAC.41,  21-Jan-85 17:04:03 by MELOHN
;Change CHKXMC to check if KLNI is running before transmitting multicast.
; UPD ID= 1234, SNARK:<6.1.MONITOR>LATSRV.MAC.40,  20-Dec-84 19:54:21 by MELOHN
;Make a call to LATLSC during LATINI to make sure the KLNI is running so we
;don't exahust free space with multi-casts that are waiting to go out.
; UPD ID= 1207, SNARK:<6.1.MONITOR>LATSRV.MAC.39,  13-Dec-84 16:31:23 by MELOHN
;TCO 6.1.1088 - Fix HMSTRT to check for non-existent CB before calling LCLHLT.
; UPD ID= 1150, SNARK:<6.1.MONITOR>LATSRV.MAC.38,   5-Dec-84 10:25:57 by GLINDELL
;Add table of contents
; UPD ID= 1142, SNARK:<6.1.MONITOR>LATSRV.MAC.37,   3-Dec-84 17:29:48 by GLINDELL
;D36COM is now in XCDSEC
; UPD ID= 1089, SNARK:<6.1.MONITOR>LATSRV.MAC.36,  17-Nov-84 16:12:43 by MELOHN
;Initialize the Multi-Cast timer even if initial LAT state is off.
;Make LASDEF global so that it can be set via SMON%
; UPD ID= 952, SNARK:<6.1.MONITOR>LATSRV.MAC.34,   5-Nov-84 15:22:52 by WEISBACH
; Fix MTTSTP to behave correctly when the NI is not running.
;   Always use temp xmit buffer.  Always send message with UNUID of 0.
; Make MDLXBF loop through ALL buffers in ACK queue and unqueue them too.
; Remove XBFACK since work done in MDLXBF now.
; Fix XMTDON not to reference CB variables if there is no CB.
; Make RFXBFS return buffer header as well as buffer.
; Add entry point XMTMSN to XMTMSG routine to send a message
;   which will not be ACKed.
; UPD ID= 939, SNARK:<6.1.MONITOR>LATSRV.MAC.33,  29-Oct-84 15:55:46 by WEISBACH
;   Add check for wheel or operator capabilities to all functions which change
;    the data base.
;   Don't user XBLTUM since was conceived for BLT, not extended BLT. In fact
;    XBLTMU does not work with XBLT.
;   Lock data base when user in .LATOP% JSYS to permit only one user at a time.
;   Put TMPBLK in PSECT NRVAR.
; UPD ID= 936, SNARK:<6.1.MONITOR>LATSRV.MAC.32,  29-Oct-84 01:38:16 by PRATT
;More TCO 6.1.1022 - Remove unnecessary ENDSV. in previous edit
; UPD ID= 929, SNARK:<6.1.MONITOR>LATSRV.MAC.31,  28-Oct-84 11:08:40 by PRATT
;TCO 6.1.1022 - Add code to return node name / number for NTINF
; UPD ID= 923, SNARK:<6.1.MONITOR>LATSRV.MAC.30,  24-Oct-84 15:51:47 by WEISBACH
;   Length mismatch between  PARTB. macro and block lengths forTBEXEC,TBRNGE,
;     and TBFLGS. While at it, change defintions to exclude a parameter
;     function code of 0.
;   Put back line to store decremented timer in TMRCHK which accidentally
;    got taken out.
;   Correct length for info to return in routine LAS1C. 
;   Swap bits in group codes returned by the .LASCH LATOP% function.
; UPD ID= 908, SNARK:<6.1.MONITOR>LATSRV.MAC.29,  22-Oct-84 20:49:06 by WEISBACH
;   Add .LASTC and SET/CLEAR for .LPCOD parameter.
;   Fix TMRCHK, CBTIM not being reset properly when it expires but the
;    retransmit limit not exceeded because routine returns before T1
;    stored in CBTIM.
;   UPD 846 moved check for last buffer too far.  It must be done before
;    the message header is build otherwise the RRF flag will never get set.
;   If the LAT access state is off, refuse any incoming circuit connects
;    (in routine HMSTRT)
;   Change default group code to zero instead of 0-35
;   HNCON counter not being maintained. Add the incrementing/decrementing/
;    testing of HNCON.
;   In LATXMC,  testing for HN data base is not necessary since
;    LATXMC never is called until initialization is successful and complete.
;    Add call to routine CHKXMC to check if multicast should be transmitted.
;   The default LAT access state must be set before any multicast formatting
;    routines are called.   UPD 823 move the two lines which did this too far
;    into the initialization. 
;   In XSLDTB for DATA_B slots shift the credits returned from CRECHK to
;    proper position. This should have been part of UPD 801.
;   Change maximum circuits and connects in PARTB. macro to be their symbolic
;    values CBMAXI & NTTLAH.
; UPD ID= 849, SNARK:<6.1.MONITOR>LATSRV.MAC.28,   4-Oct-84 16:59:36 by WEISBACH
;   Remove LATOP% function .LPLOC since it is not used.
; UPD ID= 846, SNARK:<6.1.MONITOR>LATSRV.MAC.25,   2-Oct-84 13:44:18 by WEISBACH
;   Remove symbols for LATOP% which are now in MONSYM.
;   In the received message processing routines, change to release received
;    buffer after returning from each routine.  Buffer was not always being
;    returned to DLL.
;   IN HRUNCB, add defensive code for case that CBVECT entry is zero. Terminate
;    circuit with "invalid run message" error.
;   Call circuit stop routines (except LCLHLT) with JSP to record PC for
;    BUGINF.
;   The check for last transmit buffer in BLDMSL should be in MTTRUN.
; UPD ID= 835, SLICE:<6.1.MONITOR>LATSRV.MAC.24,  27-Sep-84 10:38:10 by WEISBACH
;   Transmit buffers released at wrong place. Release after ACK'd rather
;    than at transmit complete.
; UPD ID= 824, SLICE:<6.1.MONITOR>LATSRV.MAC.23,  25-Sep-84 09:34:39 by WEISBACH
;   INFSTP should branch to MTTSTP, not MTTSTR
; UPD ID= 823, SLICE:<6.1.MONITOR>LATSRV.MAC.22,  24-Sep-84 18:40:00 by WEISBACH
;   Put Reason Code for transmitting STOP messages into T1 correctly.
;   In TMRCHK, Load T1 with value to reset timer after call to XUNAKQ, not
;    before the call.
;   Add BUGINF for cases where host stops the circuit to LAT server.
;   Move initial setting of multicast timer in LATINI to the end of the
;    initialization process to prevent any chance of LATXMC being called
;    before initialization is complete.
; UPD ID= 801, SLICE:<6.1.MONITOR>LATSRV.MAC.21,  17-Sep-84 22:18:40 by WEISBACH
;   In XSLDTA, fix again continuation from page stop.  Error return from
;    TTSND must adjust the slot count.
;   CRECHK cannot update the remotes transmit credits since the message might
;    not get sent (e.g. no transmit buffers). Change to update the remote's
;    transmit credits only when it is definite that the message will be sent
;    (at XSLDA2).
; UPD ID= 794, SNARK:<6.1.MONITOR>LATSRV.MAC.20,  10-Sep-84 19:24:35 by WEISBACH
;   Mix SL.DXF, SL.EXF to violate the LAT spec the same way VMS does. Should
;    use the input XON/XOFF character recognition but other implementations
;    mistakenly used the output character recognition.  In the spec, "input"
;    or "output" here refers to the stream in which the XON/XOFF characters are
;    put, not the stream which is turned on or off.
;   Fix continuation from page stop.  Since TTSND returns non-skip if at page
;    stop, must check for this early otherwise might increment remote's
;    credits (CRECHK) but not send the slot.
;   Add call to GETRBF in LATXMC in case it is necessary to post additional
;    to the data link.
;   CBDACT should clear CBRID, not CBLID.
;   Correct the lenght requested for the HN block in LATINI.
;   CLRNID does not zero the identification count properly: SETZRO wrong.
;   LCP clear command does not set the change flags properly because tag
;    LASET0 is at the wrong place.
;   BMCRAT sets of wrong byte pointer to node name.  Off by one byte.
;   SSERVC clears wrong AC for assembling the change flags.  Should clear
;    P4 instead of T4
; UPD ID= 759, SNARK:<6.1.MONITOR>LATSRV.MAC.19,  27-Aug-84 14:49:47 by WEISBACH
;   FNDSRV sets up one word global pointer wrong.
;   GCBNAM does not loop thru circuit blocks properly because the pointer
;    to the user's service name is destroyed by SCMPTR.  Make SCMPTR preserve
;    Tn ACs.
;   In LATINI, T4 cannot be used as the base register to the service call
;    accross a call to MMVAZS.  Replace T4 with W1.
;   Replace all lengths of blocks in BEGSTR definitions with literals
;   Replace SNMMAX and SLOMAX with ML.SYS and ML.LOC
; UPD ID= 746, SNARK:<6.1.MONITOR>LATSRV.MAC.18,  20-Aug-84 17:40:01 by WEISBACH
;   Fix sendall by including the sendall buffer count in the output character
;    count for routine XSLDTA.  Create subroutine GTCHCT to compute the correct
;    count.
;   Set change flags for clearing of a service (CSERVC).
;   Fix setting of change bits in SSERVC. Replace a STKVAR variable in
;    same routine with an AC.
;   Move the setting of the byte pointer UNBFA for the multicast message
;    from initialization code to SENDMC so it gets set up each time since
;    it will be changed when the multicast message is rebuilt.
; UPD ID= 731, SNARK:<6.1.MONITOR>LATSRV.MAC.17,   8-Aug-84 13:22:49 by WEISBACH
;   In RELSB, don't call TTYDE0 if there is a controlling job for the 
;    terminal otherwise JOBCOF code will do it again and cause a
;    TTNAC7 BUGCHK.
; UPD ID= 729, SNARK:<6.1.MONITOR>LATSRV.MAC.16,   6-Aug-84 17:35:23 by WEISBACH
;   Clear the local slot block ID in routine XSLSTP before transmitting the
;    stop slot.  Create a new routine RLSBID to do this.
;   BUGCHK in LATCBR only for NISRV callback function codes which are out of
;    range.
;   In IGNSLT, set SLTCNT to zero so slot data is truly ignored.
;   XSLDTA decrements the transmit credits too soon in the case of page
;    stop.  Move the decrement to after the XSLDA1 loop.
;   Fix incarnation count so random number used properly at initialization.
;   Set description field length back to ^D64
; UPD ID= 695, SNARK:<6.1.MONITOR>LATSRV.MAC.15,  19-Jul-84 18:13:30 by WEISBACH
;   CC.LST, not CC.LEN, is the length to use when allocating storage for a CB!!
; UPD ID= 694, SNARK:<6.1.MONITOR>LATSRV.MAC.14,  18-Jul-84 18:15:19 by WEISBACH
;   Drop node description field length back to ^D16.
; UPD ID= 690, SNARK:<6.1.MONITOR>LATSRV.MAC.13,  17-Jul-84 10:20:42 by WEISBACH
;   Some LASCH and LASCO bug fixes.
;   Add setting of change flags for service rating or description change.
;     This must be done in SSERVC rather than in LASET.
;   Length of GBHID field of GB BEGSTR should be 13., not 16.
;   Raise node description field length maximum in multicast msg to ^D64
;   Not enough memory requested for circuit blocks
; UPD ID= 633, SNARK:<6.1.MONITOR>LATSRV.MAC.12,  19-Jun-84 05:56:03 by WEISBACH
;   LTSOF must be global.
; UPD ID= 631, SNARK:<6.1.MONITOR>LATSRV.MAC.11,  18-Jun-84 14:00:33 by WEISBACH
;   Add LATOP% JSYS
;   Halt all circuits when NI goes away.
;   In CRECHK, force fork wakeup if the remote has used up all credits and
;    we cannot give him more until he reads the input.
;   In RELSB, release the TDB after doing the carrier loss action.
;   After a slot session is rejected, release the slot.
;   Save the circuit stop reason properly in MTTSTP.
;   Save the circuit's remote ID properly for MTTSTP when a CB exists.
;   In CBINIT, clear CBFLG and CBSDC.  Change to to a BLT for the clear.
;   Fix RELSB to return right value in SB so that slot mux can continue
;    properly.
;   Clear TTPRM before calling NTYCOF so don't have to clobber W1 by using
;    is as a save register.
;   In BSLTHD, T2 cannot be used to save register because LAP2B0 clobbers it.
;   In CBDACT, T2 is not preserved by LAUNQ so don't assume it.
;   Fix CMNIAD. Its comparison of NI address never worked right.
;   Terminate a circuit if the number of slots active goes to zero.
;   In XSLSTP, release the slot (call RELSB).
;   Make LAP2Bx preserve the caller's T1.
;   Let LTHNGU do something -- kill the slot associated with the terminal.
;   Add device dependent routine for specifying LAT terminals as high speed
;    output terminal.
;   Fix RANDOM.
; UPD ID= 590, SNARK:<6.1.MONITOR>LATSRV.MAC.10,   1-Jun-84 18:02:02 by WEISBACH
;   Turn off interrupts earlier in LTSSDC so that scheduler level does not
;   take out the slot block from under process level if the LAT session
;   gets disconnected with output in progress.
; UPD ID= 588, SNARK:<6.1.MONITOR>LATSRV.MAC.9,   1-Jun-84 10:52:57 by WEISBACH
;   Change maximum slot size MXSLSI to 40 decimal to correspond to 9600
;    baud input assuming two credits.
;   One too may bytes in START message. NBR_DL_BUFS entered twice.
; UPD ID= 577, SNARK:<6.1.MONITOR>LATSRV.MAC.8,  29-May-84 14:52:19 by WEISBACH
;If the output TTY buffer count (TTOCT) is zero, always clear TTOTP
; when exiting routine XSLDTA.
;Fix method of determining the start of next slot in a multi-slot message
; when slot lengths not a multiple of 2.
;Use the new host multicast address.
; UPD ID= 564, SNARK:<6.1.MONITOR>LATSRV.MAC.7,  24-May-84 16:07:34 by WEISBACH
;Fix transmissions - transmit loop never terminated.
; UPD ID= 560, SNARK:<6.1.MONITOR>LATSRV.MAC.6,  24-May-84 15:03:25 by WEISBACH
;Add fairness to slot block processing in routine BLDMSL.
;On occurence of errors from NISRV, re-post receive buffers to NISRV in
;scheduler cycle instead of at NI interrupt level.  Also save NISRV's T3.
; UPD ID= 535, SNARK:<6.1.MONITOR>LATSRV.MAC.5,  21-May-84 14:04:19 by WEISBACH
;Fix GTCSTR so that it will skip over a string properly is it is not be
; be copied (count to move equals 0).
;In the SBFLG word, reverse to order of SBOUT and SBFOU so that output is
; always flushed before new data is sent.
;Rearrange the CB BEGSTR, getting rid of the SM BEGSTR by incorporating it
; directly into CB.
; UPD ID= 524, SNARK:<6.1.MONITOR>LATSRV.MAC.4,  17-May-84 23:47:29 by WEISBACH
;Clear RRF in LAMUX at start. Set RRF when transmitting last buffer.
;Store back adjusted byte pointer in HSDATB and IGNSLT.
;Fix in HDRDAT the skip around two instructions.
;Ensure that slots start on an even byte boundary and make sure we expect
; received slots to start on an even boundary.
;Correct the size required for transmit and receive buffers.
;In MMGTZW, call DNGWDZ, not DNGWDS.
;In BMCFXD set the preferred server circuit timer to be 80 ms because of
; a server bug which does not allow 0.
;In routine HSDATA, TTCHI has a skip return; it also preserves T2 so take
; setting of T2 outside the loop.
;Change all jumps to R to be jumps to RTN for benefit of TOPS-10
; UPD ID= 507, SNARK:<6.1.MONITOR>LATSRV.MAC.3,   7-May-84 14:10:04 by WEISBACH
;First Version

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


		SEARCH MACSYM,MONSYM,PROLOG,TTYDEF
		SEARCH NIPAR
		SALL

	TTITLE (LATSRV,,< -- LAT-20 Terminal Host>)
	SUBTTL	Table of Contents


;		Table of Contents for LATSRV
;
;
;			   Section			      Page
;   1. Definitions - Literals - Circuit Parameters. . . . . .    3
;   2. Definitions - Literals - Slot Parameters . . . . . . .    4
;   3. External References. . . . . . . . . . . . . . . . . .    5
;   4. Definitions - Data Structures. . . . . . . . . . . . .    7
;   5. CIRCUIT BLOCK data structure definitions . . . . . . .    8
;   6. SLOT BLOCK data struction definitions. . . . . . . . .   10
;   7. Miscellaneous Structure Definitions. . . . . . . . . .   11
;   8. Symbol Definitions
;        8.1.   Macros MIN.,MAX.,MAXENT . . . . . . . . . . .   12
;   9. LATOP% JSYS
;        9.1.   Definitions . . . . . . . . . . . . . . . . .   13
;        9.2.   Entry point . . . . . . . . . . . . . . . . .   15
;        9.3.   SET/CLEAR Functions . . . . . . . . . . . . .   16
;        9.4.   SET/CLEAR SERVICE . . . . . . . . . . . . . .   17
;        9.5.   SET LAT HOST NAME . . . . . . . . . . . . . .   19
;        9.6.   SET/CLEAR HOST IDENTIFICATION STRING. . . . .   20
;        9.7.   SET/CLEAR HOST GROUP CODES. . . . . . . . . .   21
;        9.8.   SET MAXIMUM CIRCUITS. . . . . . . . . . . . .   22
;        9.9.   SHOW CHARACTERISTIC HOST. . . . . . . . . . .   24
;        9.10.  SHOW TERMINAL CONNECTS. . . . . . . . . . . .   26
;        9.11.  SHOW SERVERS. . . . . . . . . . . . . . . . .   27
;        9.12.  SHOW/ZERO COUNTERS. . . . . . . . . . . . . .   28
;        9.13.  Utility Routines
;             9.13.1.    FNDSRV . . . . . . . . . . . . . . .   29
;             9.13.2.    GTSRVR . . . . . . . . . . . . . . .   30
;             9.13.3.    SCMPAR String Compare. . . . . . . .   31
;  10. LATINI - LAT Initialization. . . . . . . . . . . . . .   33
;  11. Multicast Transmitter. . . . . . . . . . . . . . . . .   36
;  12. Routines to Build the Host Multicast Message
;       12.1.   BMCFXD. . . . . . . . . . . . . . . . . . . .   37
;       12.2.   BMCFXD. . . . . . . . . . . . . . . . . . . .   38
;       12.3.   BMCNID. . . . . . . . . . . . . . . . . . . .   39
;       12.4.   BMCSRV. . . . . . . . . . . . . . . . . . . .   40
;       12.5.   BMCRAT. . . . . . . . . . . . . . . . . . . .   41
;  13. Multicast Transmit Check Routine . . . . . . . . . . .   42
;  14. Build the START Message Template . . . . . . . . . . .   43
;  15. LAINTR
;       15.1.   LAT DLL Callback Routinea . . . . . . . . . .   44
;       15.2.   Interrupt Level Virtual Circuit Message Receiver  45
;  16. LAINTX - Interrupt Level Virtual Circuit Message Receiver  46
;  17. LARSCH - Scheduler Level Virtual Circuit Message Receiver  47
;  18. LATLSC - Ethernet Link State Change Callback . . . . .   49
;  19. Message Receiver - HMSTRT. . . . . . . . . . . . . . .   50
;  20. HSTRCB . . . . . . . . . . . . . . . . . . . . . . . .   51
;  21. CBINIT - Circuit Block Initialization. . . . . . . . .   52
;  22. CMNIAD - Find CB base on NI address. . . . . . . . . .   53
;  23. HMRUN. . . . . . . . . . . . . . . . . . . . . . . . .   55
;  24. HSTRCB . . . . . . . . . . . . . . . . . . . . . . . .   56
;  25. HMSTOP . . . . . . . . . . . . . . . . . . . . . . . .   57
;  26. Message Transmit Routines. . . . . . . . . . . . . . .   59
;  27. Message Transmitter
;       27.1.   XMTMSG. . . . . . . . . . . . . . . . . . . .   67
;       27.2.   XMTDON. . . . . . . . . . . . . . . . . . . .   68
;  28. Receive Message Handling Routines. . . . . . . . . . .   69
;  29. Transmit Message Handling Routines . . . . . . . . . .   70
;  30. Slot Demultiplexor - LSDMUX. . . . . . . . . . . . . .   72
;  31. HSSTRT . . . . . . . . . . . . . . . . . . . . . . . .   73
;  32. Slot Processing Routines
;       32.1.   STOP Slots. . . . . . . . . . . . . . . . . .   74
;       32.2.   DATA_A,DATA_B Slots . . . . . . . . . . . . .   75
;  33. Slot Block Allocation and Deallocation Routines. . . .   77
;  34. Slot Multiplexor - Main Loop . . . . . . . . . . . . .   83
;  35. BLDMSL . . . . . . . . . . . . . . . . . . . . . . . .   85
;  36. Slot Formatting Routines
;       36.1.   XSLSTA. . . . . . . . . . . . . . . . . . . .   87
;       36.2.   XSLSTP. . . . . . . . . . . . . . . . . . . .   88
;       36.3.   XSLDTA. . . . . . . . . . . . . . . . . . . .   89
;       36.4.   XSLDTB. . . . . . . . . . . . . . . . . . . .   91
;  37. Queue Handling Routines. . . . . . . . . . . . . . . .   94
;  38. Buffer Handling Routines
;       38.1.   Transmit Buffers. . . . . . . . . . . . . . .   96
;       38.2.   Receive Buffers . . . . . . . . . . . . . . .  100
;  39. TTYSRV DEVICE DEPENDENT ROUTINES . . . . . . . . . . .  101
;  40. General Untility Routines. . . . . . . . . . . . . . .  102
	SUBTTL	Definitions - Literals - Circuit Parameters

;
;LAT Virtual Circuit Message Types
;
	MT.RUN==0		;RUN message
	MT.STA==1		;START message
	MT.STP==2		;STOP message
	MT.MCA==^D10		;Multicast message


;LAT Virtual Circuit States
;

	CS.HLT==0		;HALTED
	CS.STA==1		;STARTING
	CS.RUN==2		;RUNNING
	CS.TRN==-1		;In transition between HALTED and STARTING

;
;Circuit STOP message reason codes
;

	CE.NSL==1		;No slots connected on circuit
	CE.ILL==2		;Illegal message or slot format
	CE.HLT==3		;Circuit halted by local system
	CE.NPM==4		;No progress being made
	CE.TIM==5		;Time limit expired
	CE.LIM==6		;Retransmit limit exceeded
	CE.RES==7		;Insufficient resources
	CE.STO==10		;Server circuit timer out of range
	CE.SKW==11		;Protocol version skew
	CE.INV==12		;Invalid Message

;Circuit STOP reason codes of local significance only

	CE.NIH==200		;Local NI halted

	MMHDSI==^D8		;Minimum Message Header Size
	MSTMSI==^D16		;Minimum START Message Size(excluding header)
	SUBTTL	Definitions - Literals - Slot Parameters

;
;LAT Slot Types
;

	ST.DTA==0		;DATA_A slot
	ST.STA==11		;START slot
	ST.DTB==12		;DATA_B slot
	ST.ATT==13		;ATTENTION slot
	ST.REJ==14		;REJECT slot
	ST.STP==15		;STOP slot

;
;LAT Slot States
;

	SS.HLT==0		;Halted
	SS.RUN==1		;Running

;
; SLOT Reject and Stop Reason Codes
;

	SE.USR==1		;User requested disconnect
	SE.SHU==2		;System shutdown in progress
	SE.ISR==3		;Invalid slot received
	SE.SER==4		;Invalid service class
	SE.RES==5		;Insufficient resources available

;
; DATA_B Slot Control Flag Bit Definitions
;

	SL.EXF==5		;Enable XON/XOFF recognition in server
	SL.DXF==11		;Disable XON/XOFF recognition in server

;
; SLOT Size Definitions
;

	SLHDSI==4		;Slot header size
	ATTSIZ==SLHDSI		;ATTENTSION slot size
	DTBSIZ==SLHDSI+5	;DATA_B slot size
	NIPIA==DLSCHN		;Priority Interrupt Assignment for KLNI

;
;Miscellaneous Constants
;

	LAHPV==5		;Highest protocol version supported
	LALPV==5		;Lowest protocol version supported
	PROVER==5		;Our LAT protocol version
	PROECO==0		;Our LAT protocol ECO
	MXSLSI==^D40		;Maximum slot size we will receive
	MXSLTS==^D64		;Maximum slot sessions per circuit
	LMRFSI==^D1504		;Receive buffer size
	MXHSRV==10		;Maximum number of services offered
	LHPRID==6		;Product code type for TOPS-20 host
	SCITTY==1		;Interactive terminal service class
	MAXXBF==2		;Maximum number of transmit buffers/circuit
	CBMAXI==255		;Absolute maximum circuit id
	MINACB==^D20		;Minimum number of allocated circuit blocks.
	SLHDSZ==4		;Slot header size (bytes)
	MAXCRE==2		;Maximum number of credits to extend to remote.
	MXBALC==6		;Number of ticks to wait before sending
				; unsolicited message to server

	DLL.FL==400000		;"buffer-in-DLL" flag (in UNUID)
	MINXBF==^D46		;Minimum transmit buffer size


; UN Block Extension

BEGSTR	UE
	WORD LW0		;Queue Link Word for linking buffers to CB Qs
	WORD LW1		;Queue Link Word for linking buffers to NI Qs
ENDSTR

;
; Buffer offsets
;

	UNB.OF==UE.LEN		;Start of UN block for all buffers
	MDB.OF==UE.LEN+UN.LEN	;Start of MD block
	XBF.OF==UE.LEN+UN.LEN+MD.LEN
	SBF.OF==UE.LEN+UN.LEN

	SZMSTP==<XBF.OF+SZ.MHD+1>;STOP message size in words
	SZ.SHD==4		;Slot header size
	SZ.ATT==1		;Attention size
	SZ.REJ==0		;Reject slot size
	SZ.SDB==5		;DATA_B slot size
	SZ.SSP==0		;Stop slot size
	SZ.SST==6		;Start slot size
	SZ.XBF==<^D1500+3>/4
	SZ.MSP==^D10		;Start message size
	SZ.MHD==^D8		;Message header size

	ML.NNM==6		;Max length of node name
	ML.SYS==^D16		;Max length of system name
	ML.LOC==^D64		;Max length of location field
	ML.DSC==^D64		;Max length of description field
	ML.SNM==^D16		;Max length of service name
	ML.SID==^D64		;Max length of service identification field
	ML.SLN==^D16		;Max length of slot name

	ML.HMC==^D33+<1+ML.NNM>+<1+ML.DSC>+MXHSRV*<1+ML.SNM+1+ML.SID>+2
				;Host multicast message max length
	ML.HSM==^D12+<1+ML.NNM>+<1+ML.LOC>+<1+ML.SYS>+1
				;Host start message max length
	SUBTTL	Definitions - Data Structures

;

;Host Node Data Structures
;

BEGSTR	GB			;SERVICE BLOCK
	WORD RAT		;Service Rating.
	HWORD NC		;Count of bytes in service name.
	HWORD LC		;Count of bytes in service description
	WORD NAM,<<ML.SNM+4>/5>	;Storage for up to 16 bytes of service name.
	WORD HID,<<ML.SID+4>/5>	;Storage for up to 64 bytes of service id.
ENDSTR

BEGSTR	HN			;HOST NODE Data Base
	FIELD FLG,^D18
	 BIT RUN		;NI run state
	 BIT ANY		;Reconstruct of START msg necessary
	 BIT CIP		;Virtual circuit connect in progress
	FILLER ^D10
	FIELD CFL,^D8		;Multicast message change flags
	 BIT OTH		;Something other than above changed
	 BIT FIL
	 BIT CLS		;A host service class changed
	 BIT SVD		;A host service description changed
	 BIT SVR		;A host service rating changed
	 BIT SVN		;A host service name changed
	 BIT NDD		;Host Node Description changed
	 BIT ACS		;Access Codes changed
				;START OF PARAMETERS DISPLAYED BY  .LASCH 
	HWORD MXC		;Maximum allocatable circuit blocks
	HWORD NCC		;Number of currently allocated circuit blocks
	HWORD MAC		;Maximum number of active circuits
	HWORD NAC		;Number of currently active circuits
	HWORD MCO		;Maximum number of simultaneous connects
	HWORD CON		;Current number of active connects
	HWORD NUM		;Host number
	HWORD LAS		;LAT access state
	HWORD RLI		;Virtual circuit message retransmit limit
	HWORD TIM		;Virtual circuit timer initial value (ms)
	HWORD MTI		;Multicast timer initial value (sec)
				; END OF PARAMETERS DISPLAYED BY .LASCH 
	HWORD RAT		;Host node dynamic rating
	HWORD PRG		;Host progress timer
	HWORD NRB		;Number of receive buffers allocated
	WORD HST		;Address of state table
	WORD QAC,2		;Queue header for active circuit blocks
	WORD QIC,2		;Queue header for inactive circuit blocks
	WORD NIQ,2		;Interrupt level message queue
	WORD SCQ,2		;Scheduler level message queue
	WORD PID		;NI Portal ID
	HWORD NXI		;Next circuit block index to assign
	HWORD NSV		;Number of offered services
	WORD LOK		;Lock for HN data base
	HWORD NMC		;Host node name count
	HWORD IDC		;Host identification string count
	WORD NAM,2		;Host node name string
	WORD ID,<<ML.DSC+4>/5>	;Host identification string
	WORD SMT,<<<ML.HSM+3>/4>+SBF.OF>;Start message template
	WORD MCM,<<ML.HMC+3>/4>	;Copy of the multicast message.
	WORD SRV,<GB.LEN*MXHSRV>;Storage for service blocks
ENDSTR

BEGSTR	AC,HN.LST		;ACCESS CODES
	WORD LNG		;Access code string length in bytes
	WORD COD,^D32		;Storage for 256 bit bit-mask
ENDSTR

;LAT Circuit counters for all servers

BEGSTR	HC,AC.LST	
	WORD RCV		;Messages received
	WORD XMT		;Messages transmitted
	WORD RTR		;Messages retransmitted
	WORD SEQ		;Receive message sequence errors
	WORD IMR		;Illegal messages received
	WORD ISR		;Illegal slots received
	WORD RES		;Resource errors
	WORD MSK		;Illegal message error mask
ENDSTR
	SUBTTL	CIRCUIT BLOCK data structure definitions

BEGSTR	CB			;CIRCUIT BLOCK
	WORD LNK,2		;Queue Link words (must be first words)
	HWORD RID		;Circuit handle assigned by the remote
	HWORD LID		;Local circuit index
	FIELD FLG,3		;Virtual circuit flags
	 BIT RRF		;Reply requested flag
	 BIT MRS		;Must reply soon flag
	 BIT MRN		;Must reply now flag
	HWORD CSB		;Count since balanced
	HWORD SDC		;Number of slots with data waiting
	HWORD TSQ		;Next transmit sequence number
	HWORD RSQ		;Next expected receive sequence number
	HWORD LRA		;Sequence number of last message ack'd by remote node.
	WORD TIM		;Current value of circuit timer.
	HWORD RTC		;Current retransmit count
	HWORD KAV		;2*Server Keep-alive-timer in ms.
	WORD KAT		;TODCLK last time message receieved from server
	HWORD QUA		;Circuit quality
	HWORD ERR		;Reason code for last time circuit stopped
	HWORD DLL		;Number of transmit buffers in the DLL
	WORD XBQ,2		;Queue of free transmit buffers
	WORD AKQ,2		;Unacknowledged queue header
	WORD SBQ,2		;Circuit slot queue
;
;The following are returned as part of the LATOP Get Server information info.
;
	WORD DNI,2		;NI address of remote server
	HWORD MTF		;Maximum transmit frame size for circuit
	HWORD RPV		;Remote protocol version and ECO
	HWORD MSL		;Maximum slots allowed by remote
	HWORD NBF		;Additional transmit buffers allowed by remote
	HWORD CTI		;Value of remote's circuit timer
	HWORD KTI		;Value of remote's keep-alive timer
	HWORD PTC		;Product type code for remote node
	HWORD STA		;Virtual circuit state
	HWORD NUM		;Remote's system number
	HWORD RSC		;Remote's system name count
	HWORD RLC		;Remote's location text count
	WORD SNM,<<ML.SNM+4>/5>	;Remote's system name
	WORD LOC,<<ML.LOC+4>/5>	;Remote's location string	
ENDSTR
BEGSTR	CC,CB.LST		;CIRCUIT COUNTERS
	WORD RCV		;Messages received
	WORD XMT		;Messages transmitted
	WORD RTR		;Messages retransmitted
	WORD SEQ		;Receive message sequence errors
	WORD IMR		;Illegal messages received
	WORD ISR		;Illegal slots received
	WORD RES		;Resource errors
	WORD MSK		;Illegal message error mask
ENDSTR

;	Boundary definitions in the CB for CLEAR and SHOW
;	LAT Control Program functions.

	  CLRBEG==CB.FLG	;First clear field for .LACLR
	  CLREND==CB.DLL	;End of clear field for .LACLR
	  SASBEG==CB.DNI	;Start of .LASAS fields for single server
	  SASBG==CB.STA		;Start of .LASAS fields for multiple servers
	  SASEN==CB.SNM+<ML.SYS+4>/5 ;End of .LASAS fields for single server
	  SASEND==CB.LOC+<ML.LOC+4>/5 ;End of .LASAS fields for multiple servers
	SUBTTL	SLOT BLOCK data struction definitions

BEGSTR	SB
	WORD LNK,2		;Queue link word (must be first)
	FIELD FLG,^D18		;Flags
	  BIT SDP		;Slot data present (must be sign bit)
	  BIT REJ		;Send REJECT Slot
	  BIT STR		;Send START Slot
	  BIT FOU		;Flush output
	  BIT OUT		;Output data available
	  BIT FCC		;Flow control change
	  BIT STO		;Send STOP Slot(Must be last)
	HWORD ATS		;Maximum attention slot size
	HWORD MDS		;Maximum slot data size
	HWORD STA		;Slot state
	HWORD RID		;Remote slot id
	HWORD LID		;Local slot id
	HWORD XCR		;Transmit credits available to us
	HWORD RCR		;Receive credits still outstanding
	HWORD REA		;Reason code for stop or reject
	HWORD SNC		;Source slot name count
	WORD SNM,<<ML.SLN+4>/5>	;Source slot name (16 characters max)
	WORD TDB		;Terminal data block
	WORD CBA		;Circuit block address for this slot
ENDSTR
	SUBTTL Miscellaneous Structure Definitions

BEGSTR	QL			;Halfword Queue Link Word
	WORD FWD		;Next forward queue element
	WORD BWD		;Previous backward queue element
ENDSTR
	SUBTTL	External References


	EXT DNGWDZ
	EXT DNFWDS
	EXT DLLUNI
	EXT TTCHI

	EXT LATMCT	;CLK2TM table timer for multicast


	RESDT

LAHNDB:	BLOCK 1
CBVECT: BLOCK 1
SBVECT: BLOCK 1
LASDEF::	EXP LS.ON		;LAT Access Default State
NSLOTS:	BLOCK 1
	SUBTTL Symbol Definitions -- Macros MIN.,MAX.,MAXENT

;
; Macro MIN. (symbol,list) - Assigns minimum arithmetic value of list of items.
;

DEFINE MIN. (SYM,LIST),
	<IRP LIST,<..X==LIST	;;Assign first value in list
		   STOPI>
	 IRP LIST,<IFL <LIST-..X>,<..X==LIST>> ;;Assign lowest in list
	 SYM==:..X>		;;Generate value

;
; Macro MAX. (symbol,list) - Assigns maximum arithmetic value of list of items.
;

DEFINE MAX. (SYM,LIST),
	<IRP LIST,<..X==LIST	;;Assign first value in list
		   STOPI>
	 IRP LIST,<IFG <LIST-..X>,<..X==LIST>> ;;Assign highest in list
	SYM==:..X>		;;Generate value

DEFINE LHDSP. (TABLE,CODE,ROUTINE) <
	.ORG <TABLE+CODE>
	IFIW ROUTINE
	.ORG>

DEFINE LSLDT. (DTBL,LTBL,CODE,ROUTINE,LNGTST,LNGADD) <
	.ORG <DTBL+CODE>
	IFIW ROUTINE
	.ORG
	.ORG <LTBL+CODE>
	EXP <LNGTST,,LNGADD>
	.ORG>


DEFINE PARTB. (PRM,MAX,MIN,CLRVAL,INSTR,FLAGS) <

	.ORG <.LP'PRM+TBRNGE-1>
	EXP <MAX,,MIN>
	.ORG
	.ORG <.LP'PRM+TBFLGS-1>
	EXP <FLAGS!CLRVAL>
	.ORG
	.ORG <.LP'PRM+TBEXEC-1>
   IFB <INSTR>,<
	STOR T2,HN'PRM,(Q2)
   >
   IFNB <INSTR>,<
	INSTR
   >
	.ORG
   >
	SUBTTL	LATOP% JSYS -- Definitions
	
	LF%RNG==1B1		;Do range check on parameter value to set
	LF%LOK==1B2		;Lock the database before setting parameter
	LF%CLR==1B3		;Parameter may be cleared.

MAX. (MXSPAR,<.LPMAC,.LPMCO,.LPNUM,.LPLAS,.LPRLI,.LPTIM,.LPMTI,.LPCOD,.LPNNM,.LPNID,.LPSRV>)
MIN. (MNSPAR,<.LPMAC,.LPMCO,.LPNUM,.LPLAS,.LPRLI,.LPTIM,.LPMTI,.LPCOD,.LPNNM,.LPNID,.LPSRV>)

	MAX. (TMPLNG,<<GB.LEN+1>,ML.NNM,ML.DSC>)
	NR (TMPBLK, TMPLNG)
	TMPGB==TMPBLK+1

	SWAPCD

TBRNGE:	BLOCK <MXSPAR-MNSPAR+1>
TBFLGS:	BLOCK <MXSPAR-MNSPAR+1>
TBEXEC:	BLOCK <MXSPAR-MNSPAR+1>

	PARTB. (MAC,CBMAXI,1,^D10,,LF%RNG!LF%CLR)
	PARTB. (MCO,NTTLAH,1,NTTLAH,,LF%RNG!LF%CLR)
	PARTB. (NUM,177777,0,RTRADR,,LF%RNG!LF%CLR)
	PARTB. (LAS,LS.ON,LS.OFF,0,,LF%RNG!<HNOTH_^D18>)
	PARTB. (RLI,100,0,^D30,,LF%RNG!LF%CLR)
	PARTB. (TIM,^D100000,^D100,^D1000,,LF%RNG!LF%CLR)
	PARTB. (MTI,60,10,^D30,,LF%RNG!LF%CLR!<HNOTH_^D18>)
	PARTB. (COD,0,0,0,<JRST SETCOD>,LF%CLR!<HNACS_^D18>)
	PARTB. (NNM,0,0,0,<JRST SETNNM>,0)
	PARTB. (NID,0,0,0,<JRST SETNID>,LF%CLR!<HNNDD_^D18>)
	PARTB. (SRV,0,0,0,<JRST SSERVC>,LF%CLR)
	SUBTTL	LATOP% JSYS -- Entry point


.LATOP::MCENT
	SKIPN Q2,LAHNDB		;If there is no data base,
	ITERR (LATX03)		; then LAT is not operational
	UMOVE Q1,1		;Get the argument block address from user
	UMOVE P1,.LAACT(Q1)	;Count of the argument block
	UMOVE P2,.LAFCN(Q1)	;LAT function code.
	CAIL P2,MNLFCN		;Range check the function code
	CAILE P2,MXLFCN		; ...
	ITERR (ARGX02)		;Function code out of range
	NOINT			;Don't allow PSI while owning lock
	LOCK HN.LOK(Q2)		;Get the lock
	CALL @LAFDSP-MNLFCN(P2)	;Dispatch to perform the function
	 JRST [	UNLOCK HN.LOK(Q2);Release data base lock
		OKINT		; and allow interrupts
		ITERR ()]	;Error already reported
	UNLOCK HN.LOK(Q2)	;Release data base lock
	OKINT			; and allow interrupts
	MRETNG			;Return to user 

	MIN. (MNLFCN,<.LASET,.LACLR,.LASCH,.LASTC,.LASAS,.LASCO,.LAZCO>)
	MAX. (MXLFCN,<.LASET,.LACLR,.LASCH,.LASTC,.LASAS,.LASCO,.LAZCO>)

LAFDSP:	BLOCK <MXLFCN-MNLFCN+1>
	LHDSP. (LAFDSP,.LASET-MNLFCN,LASET) ;Set
	LHDSP. (LAFDSP,.LACLR-MNLFCN,LACLR) ;Clear
	LHDSP. (LAFDSP,.LASCH-MNLFCN,LASCH) ;Show characteristics of host
	LHDSP. (LAFDSP,.LASTC-MNLFCN,LASTC) ;Show terminal connects
	LHDSP. (LAFDSP,.LASAS-MNLFCN,LASAS) ;Show adjacent servers
	LHDSP. (LAFDSP,.LASCO-MNLFCN,LASCO) ;Show counters
	LHDSP. (LAFDSP,.LAZCO-MNLFCN,LAZCO) ;Zero counters
	SUBTTL	LATOP% JSYS -- SET/CLEAR Functions

;LASET/LACLR - Set/Clear LAT Host parameters.
;Call:	Q1/ User's argument block address
;	Q2/ Host node data base address
;	P1/ User's argument block count
;	P2/ Function code

LASET:	
LACLR:	MOVE T1,CAPENB		;To set and clear
	TXNN T1,SC%WHL!SC%OPR	; must have sufficient capabilities.
	RETBAD (CAPX1)		;Not enough.
	UMOVE T1,.LAPRM(Q1)	;Get the parameter number to set/clear.
	CAIL T1,MNSPAR		;Range check it
	CAILE T1,MXSPAR		; ...
	RETBAD (LATX06)		;Parameter number out of range
	CAIE P2,.LACLR
	IFSKP.
	  MOVX T3,LF%CLR	;Is this parameter clearable?
	  TDNN T3,TBFLGS-1(T1)	; ...
	  RETBAD (LATX07)	;No, return error
	  HRRZ T2,TBFLGS-1(T1)	;Get the value to clear to
	  JRST LASET0		; and go clear it.
	ENDIF.
	UMOVE T2,.LAVAL(Q1)	;Get the new value to set
	MOVX T3,LF%RNG		;Check if this parameter value should
	TDNN T3,TBFLGS-1(T1)	; be range checked.
	IFSKP.			;Yes
	  MOVE T3,TBRNGE-1(T1)	;Get the upper and lower ranges.
	  HRRZ T4,T3		;Lower bound
	  HLRZS T3		;Upper bound
	  CAML T2,T4
	  CAMLE T2,T3
	  RETBAD (LATX02)
	ENDIF.
LASET0:	HLRZ T3,TBFLGS-1(T1)	;Change flags to set.
	ANDI T3,377
	IORM T3,HN.FLG(Q2)	;Set them
	XCT TBEXEC-1(T1)	;Do the store.
	RETSKP
	SUBTTL	LATOP% JSYS -- SET/CLEAR SERVICE

;SSERVC - Set LAT Service

;Call:	Q1/ User's parameter buffer
;	Q2/ Host node data base address
;	P2/ LATOP% function code
;Return:
;	RET -	Error return
;	RETSKP - Sucess

SSERVC:	SAVEAC <P3,P4>
	CALL FNDSRV		;(T1-T4/T1)Find the service to be set/cleared.
	 RETBAD ()
	MOVEM T1,P3		;Save the service block address
	CAIN P2,.LACLR		;Clearing rather than setting?
	JRST CSERVC		;Yes.
	SETZ P4,0		;P4 will be used to assemble the change flags
	UMOVE T1,.LAQUA(Q1)	;Get the flags and rating
	TXNN T1,LA%RAT		;Set the rating?
	IFSKP.			;Yes.
	  TXO P4,HNSVR		;Set rating change flag	  
	  HRRE T2,T1
	  MOVE T3,T2
	  AOS T2
	  CAIL T2,0		;Range check the rating.
	  CAILE T2,^D256	; ...
	  RETBAD ()		;Out of range
	  STOR T3,GBRAT,+TMPGB	;Store the rating
	ENDIF.
	TXNN T1,LA%DSC		;Set the description?
	IFSKP.
	  TXO P4,HNSVD		;Description change flag
	  UMOVE T1,.LADSC(Q1)	;Get the user's pointer to description string
	  MOVX T2,<POINT 7,GB.HID+TMPGB>;Where to put it.
	  MOVEI T3,ML.SID	;Maximum string count
	  CALL UMVAZS		;(T1-T3/T3)Move the string
	   RETBAD (LATX06)
	  STOR T3,GBLC,+TMPGB	;Store the description length
	ENDIF.
	SKIPN TMPBLK		;Did we add a new service?
	IFSKP.
	  OPSTRM <AOS>,HNNSV,(Q2);Yes.
	  TXO P4,HNSVN		;Flag that number of services changed
	ENDIF.
	IORM P4,HN.FLG(Q2)	;Enter the assebled flags
	MOVEI T1,GB.LEN		;Move the new service
	XMOVEI T2,TMPGB		; to the service block
	MOVE T3,P3		; supplied by FNDSRV.
	XBLT. T1
	RETSKP

;CSERVC - Clear LAT Service

;Call:	Q1/ User's parameter buffer
;	Q2/ Host node data base address
;	T1/ Address of service block to clear
;RET -	Error return
;RETSKP - Sucess

CSERVC:	MOVE T3,T1		;Start of overlay
	LOAD T1,HNNSV,(Q2)	;Number of services before clear
	CAIN T1,1		;There must always be at least one service
	RETBAD(LATX07)		;So return an error.
	IMULI T1,GB.LEN		;Total length of service blocks
	XMOVEI T4,HN.SRV(Q2)	;Service block start address
	ADD T1,T4		;Address of first word outside service block
	XMOVEI T2,GB.LEN(T3)	;First address to move down
	SUB T1,T2		;Number of words to move down
	SKIPG T1		;Is there a service block above?
	JRST CSRVC0		;No so nothing to move
	XBLT. T1		;Compress all blocks downward
CSRVC0:	OPSTRM <SOS>,HNNSV,(Q2)	;One service less.	
	MOVEI T1,HNCLS!HNSVD!HNSVR!HNSVN
	IORM T1,HN.FLG(Q2)	;Indicate service changed.
	RETSKP
	SUBTTL	LATOP% JSYS -- SET LAT HOST NAME

;SETNNM - Set LAT host name

;Call:	Q1/ User's parameter buffer
;	Q2/ Host node data base address
;RET -	Error return
;RETSKP - Sucess

SETNNM:	OPSTR <SKIPE>,HNNMC,(Q2);Has one already been set?
	RETBAD (LATX09)		;Yes, illegal to reset.
	UMOVE T1,.LABFA(Q1)	;Node name string address from the user.
	MOVX T2,<POINT 7,TMPBLK>;Pointer to where to build the string.
	MOVEI T3,ML.NNM		;Maximum node name count.
	CALL UMVAZS		;Move the ASCIZ string.
	  RETBAD (LATX06)
	STOR T3,HNNMC,(Q2)	;Store the actual string count
	MOVEI T1,<ML.NNM+4>/5	;Maximum number of words in the string
	XMOVEI T2,TMPBLK		;Move the string to
	XMOVEI T3,HN.NAM(Q2)	; the host node data base
	XBLT. T1		; ...
	RETSKP
	SUBTTL	LATOP% JSYS -- SET/CLEAR HOST IDENTIFICATION STRING

;SETNID/CLRNID - Set/Clear host identification string.

;Call:	Q1/ User's parameter buffer
;	Q2/ Host node data base address
;RET -	Error return
;RETSKP - Sucess

SETNID:	CAIN P2,.LACLR		;Clearing rather than setting?
	JRST CLRNID		;Yes.
	UMOVE T1,.LABFA(Q1)	;Node description string address from the user.
	MOVX T2,<POINT 7,TMPBLK>;Pointer to where to build the string.
	MOVEI T3,ML.DSC		;Maximum node description count.
	CALL UMVAZS		;Move the ASCIZ string.
	  RETBAD (LATX06)
	STOR T3,HNIDC,(Q2)	;Store the actual string count
	MOVEI T1,<ML.DSC+4>/5	;Maximum number of words in the string
	XMOVEI T2,TMPBLK
	XMOVEI T3,HN.ID(Q2)
	XBLT. T1
	RETSKP

CLRNID:	SETZRO HNIDC,(Q2)	;Clear the string count
	RETSKP
	SUBTTL	LATOP% JSYS -- SET/CLEAR HOST GROUP CODES

; SETCOD/CLRCOD - Set/clear access codes
;Call:	Q1/ User's parameter buffer
;	Q2/ Host node data base address
;	P2/ .LACLR or .LASET
;RETSKP - always, no errors.

SETCOD:	SAVEAC <P1>
	STKVAR <USRPTR,MSKPTR>
	SETZM TMPBLK		;Zero out the storage where mask to be built.
	XMOVEI T2,TMPBLK	;Zero the first workd
	XMOVEI T3,1(T2)		;Address of second word
	MOVEI T1,^D7		;Number of words to zero.
	XBLT. T1		;
	UMOVE T1,.LABFA(Q1)	;Group code address in user space.
	HRLI T1,441000		;Make an 8-bit byte pointer
	MOVEM T1,USRPTR		;Save it as pointer to user code string.
	MOVX T1,<POINT 8,TMPBLK>;Pointer to temporary storage for mask
	MOVEM T1,MSKPTR		;Save it too.
	MOVEI P1,^D32		;Number of bytes to do
	DO.
	  XCTBU [ILDB T1,USRPTR];Get a user group code byte
	  CALL BITSWP		;Swap the bits
	  IDPB T1,MSKPTR	;Put into mask words
	  SOJG P1,TOP.		;Continue for full count
	ENDDO.
	MOVEI T1,^D8		;32 bytes is 8 words
	XMOVEI T2,AC.COD+7(Q2)	;Address of last group code word in database
	DO.
	  MOVE T3,TMPBLK-1(T1)	;Get a mask word, going backwards
	  CAIN P2,.LACLR	;Clearing of Setting?
	  IFSKP.
	    IORM T3,(T2)	;Set the bits	    
	  ELSE.
	    ANDCAM T3,(T2)	;Clear the bits
	  ENDIF.
	  SOS T2
	  SOJG T1,TOP.
	ENDDO.
	RETSKP			;Return success.

;BITSWP - Swap all bits in an 8-bit byte
;Call:	T1/ Byte to swap
;Returns:
;	T1/ Swapped byte

BITSWP:	SAVEAC <P1>
	JUMPE T1,RTN		;If no bits, don't bother
	MOVEI T3,1		;Bit to test
	MOVEI T4,200		;Mirror bit
	SETZ P1,		;Initialize result
	DO.
	  TRNE T1,(T3)		;Is the bit set?
	  TRO P1,(T4)		;Yes, set mirror bit in result
	  LSH T3,1		;Shift bit to test
	  LSH T4,-1		; and mirror bit
	  JUMPN T4,TOP.		;Done?
	ENDDO.
	MOVE T1,P1		;Return swapped byte in T1
	RET
	SUBTTL	LATOP% JSYS -- SET MAXIMUM CIRCUITS

;SETMAC - Set Maximum Circuits
;Call:	T2/ New Maximum Circuits value
;	Q2/ Host node data base address
;RETSKP - always

SETMAC:	STOR T2,HNMAC,(Q2)	;Store the new value
	ADDI T2,MINACB		;HNMXC must be at least HNMAC+MINACB
	OPSTR <CAILE T2,>,HNMXC,(Q2)
	STOR T2,HNMXC,(Q2)
	RETSKP
;UMVAZS - Get User ASCIZ String
;Call:	T1/ User's ASCIZ Pointer
;	T2/ Destination Pointer
;	T3/ Maximum string count permitted
;RET -	Error, string longer than maximum
;RETSKP	Success
;	T3/ Actual string count

	RESCD

UMVAZS:	STKVAR <MAXCNT>
	MOVEM T3,MAXCNT		;Save the maximum count
	SETZ T3,		;Clear the string count
	TLC T1,-1		;Make an ASCII pointer to the
	TLCN T1,-1		; user's buffer
	HRLI T1,(<POINT 7,0>)	; if necessary.
UMVAZ0:	XCTBU [ILDB T4,T1]	;Get a character from his string.
	JUMPE T4,RSKP		;Null found so string ended
	CAML T3,MAXCNT		;If we are at the maximum, read no more.
	RET
	IDPB T4,T2		;Deposit character in temp storage.
	AOJA T3,UMVAZ0		;Increment string count

	ENDSV.

;MMVAZS - Move ASCIZ string within monitor context
;Call:	T1/ Source ASCIZ Pointer
;	T2/ Destination Pointer
;	T3/ Maximum string count permitted
;RET -  If string longer than maximum, truncated.
;	T3/ Actual string count

MMVAZS:	STKVAR <MAXCNT>
	MOVEM T3,MAXCNT		;Save the maximum count
	SETZ T3,		;Clear the string count
MMVAZ0:	ILDB T4,T1		;Get a character from his string.
	JUMPE T4,RTN		;Null found so string ended
	CAML T3,MAXCNT		;If we are at the maximum, read no more.
	RET
	IDPB T4,T2		;Deposit character at destination
	AOJA T3,MMVAZ0		;Increment string count

	ENDSV.

	SWAPCD
	SUBTTL	LATOP% JSYS -- SHOW CHARACTERISTIC HOST


;LASCH - Show LAT Host Characteristics
;Call:	Q1/ User's parameter buffer
;	Q2/ Host node data base address
;Return:
;	RET - error
;	RETSKP - success

LASCH:	SAVEAC <P1,P2,P3>
	UMOVE T3,.LABFA(Q1)	;User buffer address

;Check to see if the user provided buffer is large enough for all the data

	LOAD T1,HNNSV,(Q2)	;Compute the total buffer size 
	IMULI T1,GB.LEN		; required.
	ADDI T1,<HN.MXC-HN.MTI+<ML.LOC+3>/4+<ML.DSC+3>/4+<ML.NNM+3>/4+^D16>
	XCTU [HRLM T1,.LABCT(Q1)];Tell user.
	XCTU [HRRZ T2,.LABCT(Q1)];Get his buffer count.
	CAMGE T2,T1		;Is the user's buffer big enough?
	RETBAD (LATX01)		;User buffer too small

;Move fixed length dynamic parameters from the host node database (HN) to the 
;user's buffer.

	MOVEI T1,<HN.MTI-HN.MXC+1>;Number words to move
	XMOVEI T2,HN.MXC(Q2)	;Address of first HN parameter to move
	XCT 1,[XBLT. T1]

;Move static parameters to the user's buffer.

	MOVEI T1,5		;Number of words to move
	XMOVEI T2,[EXP <LAHPV,,LALPV>;Static parameter address
		   EXP <PROECO,,PROVER>
		   EXP <MXSLSI,,MXSLTS>
		   EXP <LMRFSI,,MXHSRV>
		   EXP <LHPRID,,0>]
	XCT 1,[XBLT. T1]

;Move the access code words

	MOVEI P3,^D32		;Number of bytes for the access codes
	XMOVEI P2,AC.COD(Q2)	;Address of the access codes
	TXO P2,<OWGP. 8,0>	;Make global pointer
	MOVE P1,T3		;Current address in user's buffer.
	TXO P1,<OWGP. 8,0>	;Make a byte pointer
	DO.
	  ILDB T1,P2		;Get a byte for group codes
	  CALL BITSWP		;Swap the bits
	  XCTBU [IDPB T1,P1]	;Deposit swapped byte into user buffer.
	  SOJG P3,TOP.		;Do all bytes
	HRRZI T3,1(P1)		;Make user pointer an address again.	  
	ENDDO.

;Move Host node name and host node description

	MOVE T1,HN.NMC(Q2)	;[Name count,,description count]
	MOVEI T1,<<ML.NNM+4>/5+<ML.DSC+4>/5+1>
	XMOVEI T2,HN.NMC(Q2)	;Address of node name and description
	XCT 1,[XBLT. T1]
	;...

	;...

;Move the service blocks

	LOAD T1,HNNSV,(Q2)	;Number of services currently defined
	UMOVEM T1,(T3)		;Move to user buffer
	AOS T3			;Advance buffer address
	LOAD T1,HNNSV,(Q2)	;Get the size of the
	IMULI T1,GB.LEN		; service blocks.
	XMOVEI T2,HN.SRV(Q2)	;Address of service blocks
	XCT 1,[XBLT. T1]
	RETSKP
	SUBTTL 	LATOP% JSYS -- SHOW TERMINAL CONNECTS
;
; LASTC - SHOW TERMINAL CONNECTS
;
;Call:	Q1/ address of user's arg list
;	Q2/ Address of Host node database
;Return:
;	+1 error
;	+2 success
;

LASTC:	SAVEAC <P1,P2>
	STKVAR <LTTCNT>
	LOAD T1,HNCON,(Q2)	;How many do we think are connected?
	MOVE P1,T1		;Save count
	IMULI T1,<<ML.SYS+4>/5> ;Multiply by size of returned block
	CALL LABFCK		;Is his buffer big enough?
	 RETBAD()		;No
	MOVEI T1,NTTLAH		;Get size of SBvect
	MOVEM T1,LTTCNT		;Save in local for decrementing
	UMOVE T2,.LABFA(Q1)	;Get user's buffer address
	MOVE P2,SBVECT		;Point to table of SBs
LASTCL:	SKIPE T1,(P2)		;Is there a slot block there?
	CALL LAS1C		;Yes, move data for it to user's buffer
	 JRST LASTCN		;No, go for next one
	SOJLE P1,LASTCD		;Have we given the user as many as we said?
LASTCN:	SOSLE LTTCNT		;Or have we exhausted SBVECT?
	AOJA P2,LASTCL		;No, just go for next data block
LASTCD:	UMOVE T1,.LABFA(Q1)	;Done, get address of show buffer again
	SUB T2,T1		;Calculate number of words moved
	XCTU [HRLM T2,.LABCT(Q1)] ;and store it.
	RETSKP			;And give good return

;
;	Routine to move one connect block to user's show buffer
;Call:
;	T1/ Slot Block address
;	T2/ Next address in user's buffer
;
;Return: +1 no attached terminal or circuit block now
;	 +2 Success. Data moved to user's buffer
;
;	T2/ Updated next address to fill in user's buffer (in either case)
;
LAS1C:	MOVE T3,T2		;User's address where need it later.
	SKIPE T2,SB.TDB(T1)	;Any LDB associated with slot?
	SKIPN T4,SB.CBA(T1)	;Any circuit block?
	  RET			;No, skip this one
	OPSTR <HRLZ T1,>,CBRSC,(T4) ;Yes,get Server name count
	DYNST			;Yes, get TTY line number
	UMOVEM T2,(T3)		;Give to user
	UMOVEM T1,1(T3)		;Store counts
	XMOVEI T2,CB.SNM(T4)	;and point to name string
	MOVEI T1,<<ML.SYS+4>/5>	;Combined length of strings
	ADDI T3,2		; +2 for the words we have filled
	XCT 1,[XBLT. T1]	;Fill in the user's buffer
	MOVE T2,T3		;Point P1 to the next free place in buffer
	RETSKP			;and give good return
	SUBTTL	LATOP% JSYS -- SHOW SERVERS

;LASAS - Show All Known Servers
;Call:	Q1/ User's parameter buffer
;	Q2/ Host node data base address
;Return:
;	RET - error
;	RETSKP - success

LASAS:	CAIGE P1,5		;Is the user's arg block the right size?
	RETBAD (ARGX04)		;No
	UMOVE T1,.LAQUA(Q1)	;User's pointer to server name
	XCTU [SKIPN T1,.LAQUA(Q1)] ;Specific server wanted?
	IFSKP.			;Yes.
	  CALL MAKPTR		;Make a valid ASCIZ pointer.
	  CALL GTSRVR		;Find circuit block based on server name.
	   RETBAD ()		;Unknown server
	  MOVE T4,T1		;Save the circuit block address
	  MOVEI T1,<SASEND-SASBEG>;# words returned for single server
	  XCTU [HRLM T1,.LABCT(Q1)];Tell user.
	  XCTU [HRRZ T2,.LABCT(Q1)];Get his buffer count.
	  CAMGE T2,T1		;Is the user's buffer big enough?
	  RETBAD (LATX01)	;User buffer too small
 	  UMOVE T3,.LABFA(Q1)	;Get his buffer address 
	  XMOVEI T2,SASBEG(T4)	;Address of source
	  XCT 1,[XBLT. T1]	;Move to user space
	  RETSKP		;Return successfully.
	ENDIF.
	LOAD T1,HNNCC,(Q2)	;Get the number of allocated circuit blocks
	IMULI T1,<<<ML.SYS+4>/5>+3>;Compute space needed in user's buffer
	XCTU [HRLM T1,.LABCT(Q1)];Tell user
	XCTU [HRRZ T2,.LABCT(Q1)];Get his buffer count.
	CAMGE T2,T1		;Has user allocated large enough buffer?
	RETBAD (LATX01)		;No
	UMOVE T3,.LABFA(Q1)	;Get user buffer address
	MOVE T4,HN.QAC(Q2)	;First element on the active queue
	CALL SASLP		;Get all those on the active queue
	MOVE T4,HN.QIC(Q2)	;Do the same for
	CALL SASLP		; the inactive queue.
	RETSKP			;Return successfully.

SASLP:	JUMPE T4,RTN		;No more circuit blocks on this queue
	MOVE T1,CB.RSC(T4)	;get <number,,name count>
	UMOVEM T1,(T3)		;Give it to user
	ADDI T3,1		;Bump destination address
	MOVEI T1,<<ML.SYS+4>/5>	;There are 4 words of name
	XMOVEI T2,CB.SNM(T4)	;Address of first to copy
	XCT 1,[XBLT. T1]	;Copy to user's space
	DMOVE T1,CB.DNI(T4)	;Get Ethernet address
	XCTU [DMOVEM T1,(T3)]	;Give it to the user
	ADDI T3,2		;Account for those words
	LOAD T4,QLFWD,+CB.LNK(T4);Get the next CB
	JRST SASLP		;And continue looping thru queue
	SUBTTL	LATOP% JSYS -- SHOW/ZERO COUNTERS

;LASCO - Show LAT Counters
;CALL:	Q1/ User argument block address
;	Q2/ LAT host node data base address

LASCO:	CAIGE P1,5		;Is the user's arg block the right size?
	RETBAD (ARGX04)		;No
	UMOVE T1,.LAQUA(Q1)	;Get server name requested
	CALL GTLCOB		;Get the counter block based on server name.
	 RET			;Error, already reported
	XCTU [HRLM T1,.LABCT(Q1)];Tell user
	XCTU [HRRZ T3,.LABCT(Q1)];Get his buffer count.
	CAMGE T3,T1		;Has user allocated large enough buffer?
	RETBAD (LATX01)		;No
	UMOVE T3,.LABFA(Q1)	;Get his buffer address 
	XCT 1,[XBLT. T1]
	RETSKP

;LAZCO - Zero LAT Counters
;CALL:	Q1/ User argument block address
;	Q2/ LAT host node data base address

LAZCO:	MOVE T1,CAPENB		;To zero counters,
	TXNN T1,SC%WHL!SC%OPR	; must have sufficient capabilities.
	RETBAD (CAPX1)		;Not enough.
	CAIGE P1,5		;Is the user's arg block the right size?
	RETBAD (ARGX04)		;No
	UMOVE T1,.LAQUA(Q1)	;Get server name requested
	CALL GTLCOB		;Get the counter block bases on server name.
	 RET			;Error, already reported
	SETZM (T2)		;Zero the first counter
	XMOVEI T3,1(T2)		;
	SOSLE T1		;Compute number of words to zero
	XBLT. T1		;Zero the block
	RETSKP			;Return successfully.

;GTLCOB - Get LAT Counter Block.  Get the length and address of the LAT
; counter block based on server number.
;
;CALL:	T1/ Pointer to server name or [0,,-1]
;RET:	Error - server unknown
;RETSKP:Success with
;	T1/ Counter block length in words
;	T2/ Counter block address

GTLCOB:	CAIN T1,0		;Zero local host counters?
	IFSKP.			;Zero a particular server's counters
	  CALL MAKPTR		;Make a valid pointer
	  CALL GTSRVR		;Find circuit block based on server name
	   RETBAD ()		;Unknown server
	  XMOVEI T2,CC.BEG(T1)	;Server counter block address
	  MOVEI T1,CC.LEN	;Server counter block length
	ELSE.			;Local host counters wanted
	  MOVEI T1,HC.LEN	;Host counter block length
	  XMOVEI T2,HC.BEG(Q2)	;Host counter block address
	ENDIF.
	RETSKP
	SUBTTL	LATOP% JSYS -- Utility Routines -- FNDSRV

;FNDSRV - find the service block requested by the user based on the
; service name provided.

;Call:	no arguments
;Return:
;	RET - error
;	RETSKP - success, T1/ address of service block

FNDSRV:	SAVEAC <P1,P2>
	MOVEI T1,TMPLNG-1	;Zero the storage for assembling user's
	XMOVEI T2,TMPBLK	; service name.
	XMOVEI T3,TMPBLK+1	; ...
	SETZM TMPBLK		; ...
	XBLT. T1		; ...

;Get the Service Name string from the user and put it into the service
;block build area.

	UMOVE T1,.LABFA(Q1)	;Pointer to user's service name string
	CALL MAKPTR		;Make a valid ASCIZ pointer
	XMOVEI T2,TMPGB+GB.NAM	;Pointer of where to put it
	TXO T2,<OWGP. 7,0>	; ...
	MOVEI T3,<ML.SNM+4/5>	;Maximum length
	CALL UMVAZS		;Move the string to exec space
	 RETBAD (LATX07)		;Service name too long
	STOR T3,GBNC,+TMPGB	;Store the count of service name from user

;Loop through all service blocks looking for a name match.

	XMOVEI P2,HN.SRV(Q2)	;Address of start of service blocks
	OPSTR <SKIPN P1,>,HNNSV,(Q2);Number of currently defined services
	JRST FSFALS		;There are none defined.
	DO.
	  XMOVEI T1,GB.NAM(P2)	;Address of service block's service name
	  TXO T1,<OWGP. 7,0>
	  LOAD T2,GBNC,(P2)	;Count of service block's service name
	  XMOVEI T3,TMPGB+GB.NAM;Address of user's service name
	  TXO T3,<OWGP. 7,0>
	  LOAD T4,GBNC,+TMPGB	;Count of user's service name
	  CALL SCMPAR		;Compare the strings
	  IFSKP.		;MATCH FOUND
	    MOVEI T1,.LASET	;If a SET...
	    XCTU [CAME T1,.LAFCN(Q1)]
	    IFSKP.
	      MOVEI T1,GB.LEN	; then copy entire service block to the
	      XMOVEI T2,(P2)	; temporary block so that new values
	      XMOVEI T3,TMPGB	; overlay the old.
    	      XBLT. T1
	    ENDIF.
	    MOVE T1,P2		;In both cases (SET/CLEAR) return service
	    RETSKP		; block address.
	  ENDIF.
	  ADDI P2,GB.LEN	;No match here so
	  SOJG P1,TOP.		; advance to next service block
	ENDDO.

;Service block not found.  If function is SET, see if there is room for
;a new service.  If function is CLEAR, nothing further required.

FSFALS:	MOVEI T1,.LASET		;If service name not found and the function
	XCTU [CAME T1,.LAFCN(Q1)]; request is not a SET,
	RETBAD (LATX07)		; then return an error. Otherwise,
	XMOVEI T1,<GB.LEN*MXHSRV+HN.SRV>(Q2); get end of service block address
	CAML P2,T1		; and compare with first free block address
	RETBAD (LATX08)		;There is no room for new service.
	SETOM TMPBLK		;There is room.  Flag to increment # services.
	MOVE T1,P2		;Return service block address for new entry.
	RETSKP
	SUBTTL	LATOP% JSYS -- Utility Routines -- GTSRVR


;GTSRVR - Routine to get the circuit block address for a particular 
; server based on server name.
;
;CALL:	T1/ Pointer to server name supplied by user
;	Q2/ LAT host node data base address
;RET - 	Error, no circuit block with the requested server name
;RETSKP	Success,
;	T1/ Circuit block address for requested server

GTSRVR:	SAVEAC Q1		;Get an AC for circuit block address.
	MOVX T2,<POINT 7,TMPBLK>;Pointer to where to move name string.
	MOVEI T3,ML.DSC		;Maximum server name string length.
	CALL UMVAZS		;(T1-T3/T3)Move the ASCIZ string.
	  RETBAD (LATX04)
	MOVX T1,<POINT 7,TMPBLK>;Pointer to user's name string in local storage
	MOVE T2,T3		;Length of the user's name string.
	LOAD Q1,HNQAC,(Q2)	;Get first CB on active queue
	CALL GCBNAM		;See if desired server there
	IFSKP.
	  MOVE T1,Q1		;Return the CB address on successful
	  RETSKP		; match.
	ENDIF.
	LOAD Q1,HNQIC,(Q2)	;Get first CB on inactive queue
	CALL GCBNAM
	IFSKP.
	  MOVE T1,Q1		;Return the CB address on successful
	  RETSKP		; match.
	ENDIF.
	RETBAD (LATX04)		;Not anywhere, return error.

;GCBNUM - searches a particular queue of circuit blocks looking for a match
; on the server name.

GCBNAM: SAVEAC <T1,T2>		;Preserve string pointer and count.
	JUMPE Q1,RTN		;No more circuit blocks on this queue
	DO.
	  XMOVEI T3,CB.SNM(Q1)	;Address of server name
	  TXO T3,<OWGP. 7,0>	;Make a pointer
	  LOAD T4,CBRSC,(Q1)	;Count in name
	  CALL SCMPAR		;Do the compare
	   SKIPA		;No match
	  RETSKP		;Match
	  OPSTR <SKIPN Q1,>,QLFWD,+CB.LNK(Q1)
	  RET			;No match for any CBs on queue.
	  JRST TOP.		;Try next CB.
	ENDDO.
	SUBTTL	LATOP% JSYS -- Utility Routines -- SCMPAR String Compare

;SCMPAR - String Compare
;Call:	T1/ First string byte pointer
;	T2/ First string count
;	T3/ Second string byte pointer
;	T4/ Second string count
;RET 	Mismatch
;RETSKP Match

SCMPAR:	SAVEAC <T1,T2,T3,T4>	;Preserve the byte pointers.
	STKVAR <PTR1,PTR2>
	CAME T2,T4		;If lengths don't match then obviously
	RET			; the strings don't either.
	MOVEM T1,PTR1		;Save the byte pointers
	MOVEM T3,PTR2		; to the strings.
	DO.
	  ILDB T1,PTR1		;Get next character from
	  ILDB T2,PTR2		; each string.
	  CALL UPPERC		;Uppercase one.
	  EXCH T1,T2
	  CALL UPPERC		;Uppercase the other.
	  CAME T1,T2		;Compare the result.
	  RET			;Different so return failure.
	  SOJG T4,TOP.		;Match, so continue with the next.
	ENDDO.
	RETSKP			;Full match, return success.

	ENDSV.

;UPPERC - Uppercase a character
;Call:	T1/ Character
;RET	Always with T1/ uppercased character

UPPERC:	CAIL T1,"a"
	CAILE T1,"z"
	SKIPA
	SUBI T1,40
	RET

;MAKPTR- Make a valid ASCIZ pointer if not already

MAKPTR:	MOVE T2,T1		;Make a proper pointer in case the user
	TLC T2,-1		; supplied one of the form
	TLCN T2,-1		; -1,,address
	HRLI T1,440700		; ...
	RET

;LABFCK - Routine to check if user supplied buffer large enough
;Call:	T1/ Minimum size required of user buffer
;	Q1/ User's argument block address
;Return +1/ Too small
;	+2/ Large enough

LABFCK:	XCTU [SKIPGE T1,.LABCT(Q1)]
	RETBAD (LATX01)
	RETSKP
	SUBTTL	LATINI - LAT Initialization

;LATINI - LAT Initialization
;
;Call:	CALL LATINI
;	  Error return - resource failure
;	Normal Return
;

	RESCD

LATINI::ACVAR <HN,XB,W1,W2>	;Reserve AC for Host Node data base address
	MOVEI T1,HC.LST		;Get memory for the Host Node (HN) tables
	CALL MMGTZW		;Get zeroed memory
	 JRST INIRES		;Resource failure. Should not happen.
	MOVE HN,T1		;Base address for host node database references
	MOVEM T1,LAHNDB		;Store the data base address
	MOVEI T1,CBMAXI		;Get storage for CBVECT
	CALL MMGTZW		;
	  JRST INIRES		;Resource failure.
	MOVEM T1,CBVECT		;Save the address
	MOVEI T1,NTTLAH		;Get storage for SBVECT
	CALL MMGTZW
	 JRST INIRES		;Resource failure.
	MOVEM T1,SBVECT		;Save the address
	DMOVE T1,OURNAM		;Get the local host name
	DMOVEM T1,HN.NAM(HN)	;Store in HN block
	MOVE T1,OURCNT		;Host name length in characters
	STOR T1,HNNMC,(HN)	;Store in HN block
	MOVX T1,<POINT 7,SVN##>	;Pointer to MONNAM.TXT
	XMOVEI T2,HN.ID(HN)	;Where to put it.
	TXO T2,<OWGP. 7,0>	; ...
	MOVEI T3,ML.DSC		;Maximum length allowed
	CALL MMVAZS		;Move the string
	  TRN			;Don't care, take truncated string
	STOR T3,HNIDC,(HN)	;Store real count
	MOVE T1,LASDEF		;Get the default LAT Access State
	STOR T1,HNLAS,(HN)	; and store it
	MOVE T1,RTRADR		;Get the default host number
	STOR T1,HNNUM,(HN)
	MOVEI T1,^D30		;Default Host message retransmit limit
	STOR T1,HNRLI,(HN)
	MOVEI T1,^D1000		;Default host virtual circuit timer(ms)
	STOR T1,HNTIM,(HN)
	MOVEI T2,^D30		;Default host multicast timer (secs)
	STOR T2,HNMTI,(HN)
	MOVEI T1,MINACB		;Default maximum allocatable circuit blocks
	STOR T1,HNMXC,(HN)
	;...

	;...
	MOVEI T1,^D20		;Default maximum active circuits
	STOR T1,HNMAC,(HN)
	MOVEI T1,CBMAXI		;Get a random number in the range or 1 to
	CALL RANDOM		; maximum value of circuit block index
	STOR T1,HNNXI,(HN)	;Store as next CB index to assign	
	MOVEI T1,NTTLAH		;Default max number of simultaneous connects
	STOR T1,HNMCO,(HN)
	MOVEI T1,^D32		;Number of access code bytes
	STOR T1,ACLNG,(HN)
	MOVX T1,1B7		;Default to group code 0
	MOVEM T1,AC.COD(HN)	;Store as first byte of access codes
	MOVEI T1,1
	STOR T1,HNNSV,(HN)	;Default number of host services offered
	XMOVEI W1,HN.SRV(HN)	;Address of the host service blocks
	STOR T1,GBRAT,(W1)
	MOVE T1,OURCNT		;Default service name is node name so
	STOR T1,GBNC,(W1)	; store the count
	DMOVE T1,OURNAM		; and the name
	DMOVEM T1,GB.NAM(W1)
	MOVX T1,<POINT 7,SVN##>	;Pointer to MONNAM.TXT
	XMOVEI T2,GB.HID(W1)	;Make it the service identification
	TXO T2,<OWGP. 7,0>	; ...
	MOVEI T3,ML.DSC		;Maximum length allowed
	CALL MMVAZS		;Move the string
	  TRN			;Don't care, take truncated string
	STOR T3,GBLC,(W1)	;Store real count
	XMOVEI XB,HN.MCM(HN)	;Address of the multicast message block
	MOVE T1,[BYTE (8)11,0,53,0,(4)0];Store multicast address
	MOVE T2,[BYTE (8)0,17,0,0]; ...
	OPSTRM <DMOVEM T1,>,UNDAD,+UNB.OF(XB);09-00-2B-00-00-0F
	SETO T1,0		;Use random number for incarnation count
	CALL BMCFXD		;Build the first (fixed) ^D12 bytes
	CALL BMCACS		;Enter the host access code string (fixed)
	CALL BMCNID		;Enter the host node name and description
	CALL BMCSRV		;Build the services
	SETONE HNLOK,(HN)	;Initialize database lock
	STOR XB,UNRID,+UNB.OF(XB);Store for xmit complete processing
	MOVEI T1,^D255		;Get a random number in range 1-255 for
	CALL BLDSTM		;Now build the START message template
	MOVSI T1,-NSBWDS	;Initialize free slot block words to
INISBN:	SETOM NFRSBQ(T1)	; all slots free (all bits 1's)
	AOBJN T1,INISBN		; ...
	;...

	;...
	SETZRO UNCHN,+UNB.OF(XB);For now, later must read channel list
	SETZRO UNPAD,+UNB.OF(XB);We use padding
	MOVEI T1,^B0110000000000100;Protocol ID is 60-04
	STOR T1,UNPRO,+UNB.OF(XB);
	XMOVEI T1,LATCBR	;Callback routine address
	STOR T1,UNCBA,+UNB.OF(XB)
	MOVX T1,NU.OPN		;NISRV Open function
	XMOVEI T2,UNB.OF(XB)	;Use this for open as well
	CALL DLLUNI
	IFSKP.
	  XMOVEI T2,UNB.OF(XB)	;Load address of UN block
	  CALL LATLSC		;Check to see if NI is running
	  LOAD T1,UNPID,+UNB.OF(XB) ;Get the PID return from the DLL
	  STOR T1,HNPID,(HN)	; and store for later calls to the DLL.
	  LOAD T1,HNLAS,(HN)	;Get the access state.
	  LOAD T2,HNMTI,(HN)	;Get multicast timer value
 	  IMULI T2,^D1000	;Multicast in millisecs
	  MOVEM T2,LATMCT	;Initialize scheduler long timer word
	  CALL GETRBF		;Get a buffer for receives
	   TRN			;Will have to get buffers later
	ENDIF.
	RET
INIRES:	
	BUG. (CHK,LATINE,LATSRV,SOFT,<LATINI failed to initialize>,,<

Cause:	Could not obtain sufficient memory for the LAT host databases.
	
Action:	Determine why there is so little resident memory available at system
	startup.

Data:	HN.LST words for the host node database, CBMAXI words for CBVECT,
	and NTTLAH words for SBVECT.

>)
	SKIPE T1,LAHNDB		;Start with a clean slate when we try
	CALL MMFREE		; to initialize later...
	SKIPE T1,CBVECT		; ...
	CALL MMFREE		; ...
	SKIPE T1,SBVECT		; ...
	CALL MMFREE		; ...
	RET

	ENDAV.
	SUBTTL Multicast Transmitter

;
;LATXMC	- Transmit Multicast Message
;
;CALL:	CALL LATXMC
;	Normal Return

LATXMC::ACVAR <HN,XB,W1,W2>
	MOVE HN,LAHNDB		;Get the HN data base address
	LOAD T1,HNMTI,(HN)	;Reset the
	IMULI T1,^D1000		; multicast
	MOVEM T1,LATMCT		; timer
	CALL CHKXMC		;Should be transmit the multicast this time?
	 RET			;No.
	CALL GETRBF		;Post more receive buffers if necessary.
	 TRN			;Not necessary.
	CALL DYNRAT		;Check to see if dynamic rating needs update.
	XMOVEI XB,HN.MCM(HN)	;Get the address of the multicast msg
	OPSTR <SKIPE W1,>,HNCFL,(HN);If nothing changed in HN database
	OPSTR <SKIPL>,HNLOK,(HN); or something changed by db id locked
	JRST SNDMNC		; then send the old image of mc msg
	CAIE W1,HN%SVR		;Did only the ratings change?
	JRST LATXM0		;No, something else changed
	CALL BMCRAT		;Go update the ratings
	JRST LATXM2		; and send the mc msg
LATXM0:	SETZ T1,0		;So BMCFXD will increment incarnation count
	TRNE W1,HN%OTH		;Did something in fixed part of mc change?
	CALL BMCFXD		;Yes, go rebuild fixed part of mc msg
	TRNE W1,HN%ACS		;Did any access codes change?
	CALL BMCACS		;Yes, go rebuild access code field
	TRNE W1,HN%NDD		;Did the host node description change?
	CALL BMCNID		;Rebuild the node description
	TRNN W1,HN%NDD		; and the service fields
LATXM1:	TRNE W1,HN%SVN!HN%SVD!HN%SVR;Did anything in any service change?
	CALL BMCSRV		;Yes, reconstruct all service fields
LATXM2:	SETZRO HNCFL,(HN)	;Clear data base change flags
SENDMC:	MOVX T1,<POINT 8,<SBF.OF+1>(XB),23>;Pointer msg change flags
	LDB T2,T1		;Message incarnation field
	AOS T2			;Add one
	DPB T2,T1		; and put back into msg
	ILDB T2,T1		;Load current change flag settings from msg
	TDC T2,W1		;Complement those bits which represent change
	DPB T2,T1		; and put back into msg
SNDMNC:	XMOVEI T1,<SBF.OF>(XB) ;Set up one word global to buffer for
	TXO T1,<OWGP. 8,0>	; NISRV and
	STOR T1,UNBFA,+UNB.OF(XB); store in UN block
	LOAD T1,HNPID,(HN)	;Get the PID for the DLL
	STOR T1,UNPID,+UNB.OF(XB)
	MOVX T1,NU.XMT		;Set NI function to transmit
	XMOVEI T2,UNB.OF(XB)	;Address of arguement block for NISRV
	CALL DLLUNI		;Call NI service
	 TRN			;Ignore error
	RET			;Return
	SUBTTL	Routine to check/compute the Dynamic Rating

;
;DYNRAT - Routine to check/compute the Dynamic Rating
;

DYNRAT:	MOVE T1,RJAV+2          ;15 minute load average
	FSC T1,2		;Multiply by 4
	FIXR T1,T1		;and convert/round to integer
	SUBI T1,^D255           ;Subtract from 255 to give negative rating.
	SKIPLE T1               ;Assure rating
	MOVX T1,-1              ; is at least 1
	MOVMS T1                ;Make rating positive
	OPSTR <CAMN T1,>,HNRAT,(HN) ;Compare with current dynamic rating
	RET			;No change; return.
	STOR T1,HNRAT,(HN)	;Store new rating in HN block
	SETONE HNSVR,(HN)	;Indicate rating has changed.
	RET
	SUBTTL	Routines to Build the Host Multicast Message -- BMCFXD

;
;BMCFXD - Build fixed portion of the multicast message
;
;Call:	T1/ Incarnaction count flag
;	HN/ Host Node Data Base Address
;	XB/ Address of the multicast message block in HN
;	CALL BMCFXD
;
; This routine builds the first ^D12 bytes of the multicast message.  It is
; called either to build a new message or to update the fixed portion if any
; of the parameters in the fixed portion have changed since the last multicast
; message was transmitted.  This routine does not modify the message count.

BMCFXD:	STKVAR <INCFLG>		;Save the incarnaction
	MOVEM T1,INCFLG		; flag.
	MOVX T1,<POINT 8,<SBF.OF>(XB)> ;Byte pointer to start of msg
	STOR T1,UNBFA,+UNB.OF(XB); is needed to fill in the msg header
	MOVEI T1,MT.MCA_2	;Message type for multicast message
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB)	
	MOVEI T1,^D8		;Set 80ms preferred server circuit timer
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB)
	MOVEI T1,LALPV_^D8+LAHPV;Highest and lowest permitted protocol version
	CALL LAP2B0
	MOVEI T1,PROECO_^D8+PROVER ;Enter current protocol version and ECO
	CALL LAP2B0
	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(XB);Get old incarnation count and assume
	AOS T1			; we just want to increment it.
	SKIPN INCFLG		;If this is initialization, we want a "random"
	IFSKP.			; incarnation count instead.
	  MOVEI T1,^D255	;Maximum value incarnation count can have.
	  CALL RANDOM		; the initial message incarnation count.
	ENDIF.
	OPSTRM <DPB T1,>,UNBFA,+UNB.OF(XB); and put back
	LOAD T1,HNCFL,(HN)	;Get the change flags
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB)
	MOVEI T1,LMRFSI+^D14	;Receive buffer size (including E-net header)
	CALL LAP2B0
	LOAD T1,HNMTI,(HN)	;Get the multicast timer interval
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB)
	MOVEI T1,0		;Set status indicating accepting new sessions.
	LOAD T2,HNLAS,(HN)	;Get the current LAT access state
	CAIE T2,LS.ON		;Is it on?
	MOVEI T1,1		;No, indicate not accepting new sessions.
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB)
	RET
	SUBTTL	Routines to Build the Host Multicast Message -- BMCFXD

;
;BMCACS - Enter Access Codes into the Multicast Message - BMCACS
;
;Call:	HN/ Host Node Data Base Address
;	XB/ Address of the multicast message block in HN
;	CALL BMCACS
;	Normal Return
;
; This routine builds ^D32 bytes of host access codes.  It is called either to
; build a new message or to update the fixed length access code field if any of
; the access codes have changed since the last multicast message was
; transmitted.  This routine does not modify the message count.

BMCACS:
	MOVX T1,<POINT 8,<SBF.OF+^D12/4>(XB)> ;ACs start at a fixed location
	STOR T1,UNBFA,+UNB.OF(XB);Store as byte pointer to message buffer
	LOAD T1,ACLNG,(HN)	;Count of the access code string
	MOVX T2,<POINT 8,AC.COD(HN)> ;Source string byte pointer
	CALL PTCST0		;Transfer the string to the message
	RET
	SUBTTL	Routines to Build the Host Multicast Message -- BMCNID

;
;BMCNID - Enter Access Codes into the Multicast Message - BMCNID
;
;Call:	HN/ Host Node Data Base Address
;	XB/ Address of the multicast message block in HN
;	CALL BMCNID
;	Normal Return
;
; This routine enters the host node name and descriptor strings into the 
; multicast message block.  It is called either to build a new message or to
; update the fixed length access code field if the node name or descriptor 
; has changed since the last multicast message was transmitted.  If this
; routine is called, the rest of the multicast message must be rebuilt.  This
; routine does not modify the message count.

BMCNID:
	MOVX T1,<POINT 8,<SBF.OF+^D12/4+^D32/4>(XB),7> ;Node name starts here
	STOR T1,UNBFA,+UNB.OF(XB);Store as byte pointer to message buffer
	LOAD T1,HNNMC,(HN)	;Host name count
	MOVX T2,<POINT 7,HN.NAM(HN)> ;Source string byte pointer
	CALL PTCST0		;Transfer the host node name to m.c.msg
	LOAD T1,HNIDC,(HN)	;Count of host node description string
	MOVX T2,<POINT 7,HN.ID(HN)> ;Move the host node id string to the
	CALL PTCST0		; m.c. message
	RET
	SUBTTL	Routines to Build the Host Multicast Message -- BMCSRV

;
;BMCSRV - Build the Available Host Services Data - BMCSRV
;
;Call:	HN/ Host Node Data Base Address
;	XB/ Address of the multicast message block in HN
;	CALL BMCSRV
;	Normal Return
;
; This routine enters all service blocks and the service classes into the
; multicast message block.  It is called either to build a new message or to
; update the message when any service name or description has changed since
; the last multicast message was transmitted.  This routine does modify the
; message count.

BMCSRV:	SAVEAC <W1,W2>
	LOAD W1,HNNSV,(HN)	;Get the total number of offered services
	SKIPN W1		;Must be at least one service to offer	
	RET			;Nope.
	MOVEI T4,^D12+^D33+1	;Initialize msg count
	MOVX T3,<POINT 8,<SBF.OF+^D12/4+^D32/4>(XB),7>;Get pointer to node name
	ILDB T1,T3		;Get node name count
	ADDI T4,1(T1)		;Include in total msg count
	ADJBP T1,T3		;Adjust to node descriptor
	ILDB T3,T1		;Get the node description count
	ADDI T4,1(T3)		;Include in total msg count
	ADJBP T3,T1		;Adjust pointer to start of Service Block
	STOR T4,UNBSZ,+UNB.OF(XB);Store msg count so far
	STOR T3,UNBFA,+UNB.OF(XB);Current message byte pointer
	OPSTRM <IDPB W1,>,UNBFA,+UNB.OF(XB);Already counted
	XMOVEI W2,HN.SRV(HN)	;Address of start of service blocks
SRVLP:	OPSTR <SKIPGE T1,>,GBRAT,(W2) ;Get the service rating
	LOAD T1,HNRAT,(HN)	;If negative, load current host dynamic rating.
	OPSTRM <AOS>,UNBSZ,+UNB.OF(XB);Account for rating in the byte count
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB);Store in mc msg
	LOAD T1,GBNC,(W2)	;Get the service name count
	MOVX T2,<POINT 7,GB.NAM(W2)> ;Pointer to service name
	CALL PTCSTR		;Move name string to mc msg
	LOAD T1,GBLC,(W2)	;Get the service description count
	MOVX T2,<POINT 7,GB.HID(W2)>;Pointer to the service description string
	CALL PTCSTR		;Move to mc msg
	ADDI W2,GB.LEN		;Address of next block
	SOJN W1,SRVLP		;Loop through all services
	MOVEI T1,1_^D8+1	;Service class 1 only
	CALL LAP2BY
	RET
	SUBTTL	Routines to Build the Host Multicast Message -- BMCRAT

;
;BMCRAT - Update the MC Msg with the current service ratings - BMCRAT
;
;Call:	HN/ Host Node Data Base Address
;	XB/ Address of the multicast message block in HN
;	CALL BMCRAT
;	Normal Return
;
; This routine loops though all the service blocks and updates the 
; corresponding service rating in the multi-cast message. This routine does
; NOT modify the message count itself.

BMCRAT:	SAVEAC <W1,W2>
	MOVX T3,<POINT 8,<SBF.OF+^D12/4+^D32/4>(XB),7>;Get pointer to node name
	ILDB T1,T3		;Get node name count
	ADJBP T1,T3		;Adjust to node descriptor
	ILDB T3,T1		;Get the node description count
	ADJBP T3,T1		;Adjust pointer to start of Service Block
	ILDB W1,T3		;Get number of services from msg
	XMOVEI W2,HN.SRV(HN)	;Address of start of service blocks
RATLP:	OPSTR <SKIPGE T1,>,GBRAT,(W2) ;Get the service rating
	LOAD T1,HNRAT,(HN)	;If negative, load current host dynamic rating.
	IDPB T1,T3		;Store updated rating in mc msg
	LOAD T1,GBNC,(W2)	;Get the service name count
	OPSTR <ADD T1,>,GBLC,(W2);Add the service description count
	ADDI T1,2		;Account for the 2 count fields
	ADJBP T1,T3		;Adjust the pointer
	MOVE T3,T1		;Put adjusted pointer back where we want it
	ADDI W2,GB.LEN		;Address of next block
	SOJN W1,RATLP		;Loop through all services
	RET
	SUBTTL	Multicast Transmit Check Routine

;CHKXMC - Check for Transmit of Multicast Message
;Call:	HN/ Address of the Host Node Data Base
;RET -	Don't transmit
;RETSKP	Transmit

CHKXMC:	LOAD T1,HNLAS,(HN)	;Transmit only if LAT access state is ON and
	LOAD T2,HNNMC,(HN)	; there is a host name defined.
	CAIN T1,LS.ON		; is ON.
	 SKIPN T2
	  RET
	TMNE HNRUN,(HN)		;Is the NIA running?
	 RET			;No, don't send any messages.
	RETSKP
	SUBTTL	Build the START Message Template

;BLDSTM - Build the START Message Template
;
;Call:	HN/ Address of the Host Node Data Base
;	CALL BLDSTM
;	Normal Return
;
; This routine builds the "static" portions of the start message:  those
; fields which do not change very frequently. Fields which change frequently
; are set up by the transmitting routines.  This routine also sets up the
; message size in UNBSZ of the NISRV UN block.

BLDSTM:	SAVEAC <W1>
	XMOVEI XB,HN.SMT(HN)	;Address of START message template
	MOVX T1,<POINT 8,<SBF.OF+<SZ.MHD>/4>(XB)>;Point beyond the msg header
	STOR T1,UNBFA,+UNB.OF(XB); since it will change with each transmission.
	MOVEI W1,^D12		;Count of fixed fields (including count fields)
	MOVEI T1,LMRFSI+^D14	;Minimum receive buffer size
	CALL LAP2B0		;Store in msg
	MOVEI T1,PROECO_^D8+PROVER ;Enter current LAT protocol version
	CALL LAP2B0		; and ECO
	LOAD T1,HNMCO,(HN)	;System-wid maximum LAT connects allowed
	CAILE T1,MXSLTS		; compared to max slots per virtual circuit
	MOVEI T1,MXSLTS		;Get the smaller of the two
	CALL LAP2B0
	MOVEI T1,0		;Timers are non-zero when from server
	CALL LAP2B0		; only.
	LOAD T1,HNNUM,(HN)	;Get the host number
	CALL LAP2B0		; and store as 2 bytes in msg
	MOVEI T1,LHPRID		;LAT Host product ID is TOPS-20 Host
	CALL LAP2B0		; and store as 2 bytes in msg
	LOAD T1,HNNMC,(HN)	;Host Node Name count
	ADDI W1,1(T1)		;Add to the total message byte count
	MOVX T2,<POINT 7,HN.NAM(HN)>;Host node name source string
	CALL PTCST0		;Put into start message as NODE NAME
	LOAD T1,HNNMC,(HN)	;Host Node Name count
	ADDI W1,1(T1)		;Add to the total message byte count
	MOVX T2,<POINT 7,HN.NAM(HN)>;Host node name source string
	CALL PTCST0		;Put into start message as SYSTEM NAME
	LOAD T1,HNIDC,(HN)	;Count of description string
	ADDI W1,1(T1)		;Add to the total message byte count
	MOVX T2,<POINT 7,HN.ID(HN)>;Description string source pointer
	CALL PTCST0		;Put into start message
	MOVEI T1,0		;There are no parameters
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB); is zero
	ADDI W1,1		;Include in total count
	CAIG W1,MINXBF		;Send at least the minimum 
	MOVEI W1,MINXBF		; size.
	STOR W1,UNBSZ,+UNB.OF(XB);Store message byte count
	STOR XB,UNRID,+UNB.OF(XB);Store buffer address for completion routines.
	RET

	ENDAV.
	SUBTTL	LAINTR -- LAT DLL Callback Routinea

;LATCBR - dispatch to routine to handle DLL callback
;
;Call:	T1/ NU.RCV
;	T2/UN block address
;	CALL LATCBR
;	Normal return
;

LATCBR:	ACVAR <HN,XB,RB>	;Reserve some registers
	MOVE HN,LAHNDB		;Get the host node data base address
	MOVE T4,T3		;Save NISRV's error register
	CAIL T1,MINCBF		;Validate the callback function code
	CAIG T1,MAXCBF
	 CALLRET @CBRDSP-MINCBF(T1);Valid, call the proper routine
	BUG. (CHK,LATICB,LATSRV,SOFT,<LATCBR called from NISRV with illegal callback function code>,<<T1,CODE>>,<

Cause:	NISRV has called the LATSRV callback routine with an invalid function
	code.

Data:	CODE - Function code

>)
ILLCBR:	
	RET

	MIN. (MINCBF,<NU.XMT,NU.RCV,NU.RCI,NU.SCA,NU.RCC>) ;Minimum callback function
	MAX. (MAXCBF,<NU.XMT,NU.RCV,NU.RCI,NU.SCA,NU.RCC>) ;Maximum callback function

CBRDSP:	BLOCK <MAXCBF-MINCBF+1>
	LHDSP. (CBRDSP,NU.XMT-MINCBF,LAINTX) ;Datagram transmit complete
	LHDSP. (CBRDSP,NU.RCV-MINCBF,LAINTR) ;Datagram received
	LHDSP. (CBRDSP,NU.RCI-MINCBF,LATLSC) ;Link state change
	LHDSP. (CBRDSP,NU.SCA-MINCBF,ILLCBR) ;Illegal callback, not used by LAT
	LHDSP. (CBRDSP,NU.RCC-MINCBF,ILLCBR) ;Illegal callback, not used by LAT
	SUBTTL	LAINTR -- Interrupt Level Virtual Circuit Message Receiver

;LAINTR - Processes a circuit message at NI interrupt level
;
;Call:	T1/ NU.RCV
;	T2/UN block address
;	T4/NISRV's T3
;	CALL LAINTR
;	Normal return
;

LAINTR:	LOAD RB,UNRID,(T2)	;Get address of message block
	MOVEI T1,UN.LEN	;Length of the block to copy
	XMOVEI T3,UNB.OF(RB)	;Address of where to copy the UN block
	XBLT. T1		;Copy the UN block to buffer header
	MOVEI T1,NU.RCV		;Scheduler needs to know buffer type.
	MOVE T2,RB		;Address of element to put onto queue
	CALL LAINT0		;Do common actions
	RET			;And return to NISRV dismissing interrupt
	SUBTTL	LAINTX - Interrupt Level Virtual Circuit Message Receiver

;LAINTX - Processes a circuit message at NI interrupt level
;
;Call:	T1/ NU.XMT
;	T2/UN block address
;	T4/NISRV's T3
;	CALL LAINTX
;	Normal return
;

LAINTX:	LOAD XB,UNRID,(T2)	;Get the buffer address
	LOAD T3,UNDAD,+UNB.OF(XB);Get first 4 bytes (low order) of NI address
	TXNE T3,1B7		;Is the multicast bit on?
	RET			;Yes, nothing to do
	MOVX T3,<POINT 8,<SBF.OF>(XB)>;Point to the msg header
	ILDB T2,T3		; and get the message type
	LSH T2,-2		;Shift off M and RRF bits
	CAIE T2,MT.STA		;If a START message,
	IFSKP.			; then release lock on the START message
	  SETZRO HNCIP,(HN)	; template and return.
	  RET
	ENDIF.
	MOVE T2,XB
	CALL LAINT0		;Do common actions
	RET

; Routine to perform interrupt level actions common to both receive data and
; transmit complete.

LAINT0:	STOR T1,UNCBA,+UNB.OF(T2);Store callback type for scheduler processing
	STOR T4,UNSPI,+UNB.OF(T2);Save NISRV's error return
	OPSTR <SKIPN T3,>,HNNIQ,+1(HN);Is the Q currently empty?
	IFSKP.			;NO
	  STOR T2,UELW1,(T3)	;New forward for old Q tail element
	  SETZRO UELW1,(T2)	;New tail's forward link is zero.
	ELSE.			;YES
	  STOR T2,HNNIQ,(HN)	;New queue header forward pointer
	ENDIF.
	STOR T2,HNNIQ,+1(HN)	;New queue header tail pointer
	RET

	ENDAV.
	SUBTTL LARSCH - Scheduler Level Virtual Circuit Message Receiver

;LARSCH - Complete circuit message processing at scheduler level

LARSCH::ACVAR <HN,XB,RB,CB,SB,W1,W2,MD>;Get a set of dedicated ACs
	TRVAR <MSGDID,MSGSID,MSGACK,STPCOD>;Message hdr variable, etc.
;**; [7234]	Add 1 line after LARSCH:+1	HMP	24-Jan-86
	SETZ CB,		;[7234] No circuit block yet
	SKIPN HN,LAHNDB		;Get the host node database address
	RET			;If none, not initialized yet.
	CALL MOVNIQ		;Move all messages from NI to Scheduler queue
MSGNXT:	OPSTR <SKIPN T2,>,HNSCQ,(HN);Get first element on the scheduler Q
	CALLRET LAMUX		;No more so call the slot multiplexor
	OPSTR <SKIPN T3,>,UELW1,(T2);Get new head or queue if any
	STOR T3,HNSCQ,+1(HN)	;None, so clear the queue header tail pointer
	STOR T3,HNSCQ,(HN)	;Set new queue header forward pointer
	SETZRO UELW1,(T2)	;Clear link word
	LOAD T1,UNCBA,+UNB.OF(T2)	
	CAIE T1,NU.XMT		;Is this a transmit complete?
	IFSKP.			;Yes...
	  CALL XMTDON		;Go do scheduler level transmit done stuff
	  JRST MSGNXT		; and continue with next message
	ENDIF.
	MOVE RB,T2		;Message address obtained
	SKIPE T1,UN.SPI+UNB.OF(RB);Was there an error?
	JRST MSGDON		;Re-post and ignore
	OPSTRM <AOS>,HCRCV,(HN)	;Increment count of received messages
	OPSTR <ILDB W1,>,UNBFA,+UNB.OF(RB);Get the message type from the buffer
	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);Get number of slots in message
	MOVEM T1,NSLOTS
	MOVX T1,-2		;Reduce the residual byte count by 2 for
	OPSTRM <ADDM T1,>,UNBSZ,+UNB.OF(RB); type and slots fields
	CALL LAG2BY		;Get the destination ID from msg (2 bytes)
	MOVEM T1,MSGDID
	CALL LAG2BY		;Get the source id from the msg
	MOVEM T1,MSGSID
	CALL LAG2BY		;Get the ACK/SEQ field from msg
	MOVEM T1,MSGACK
	MOVE T1,W1
	LSH T1,-2		;Shift off the M and RRF bits
	CAIL T1,MINMTY		;Range check message type
	CAILE T1,MAXMTY		;...
	IFNSK.
	  BUG. (CHK,LATIMT,LATSRV,SOFT,<LAT Illegal Message Type>,<<T1,MSGTYP>>,<

Cause:	The LAT virtual circuit message was received with a message type out  
	of range.

Data:	MSGID - Message type

>)
	ELSE.
	  TRNN W1,2		;Test the Master/Slave bit
	  JRST MSGDON		;This a message from a host. 
	  CALL @MSGDSP-MINMTY(T1) ;Dispatch to process the message
	ENDIF.
MSGDON:	CALL RELRBF		;Ignore for now
	JRST MSGNXT


	MIN. (MINMTY,<MT.RUN,MT.STA,MT.STP>) ;Minimum message type code
	MAX. (MAXMTY,<MT.RUN,MT.STA,MT.STP>) ;Maximum message type code

MSGDSP:	BLOCK <MAXMTY-MINMTY+1>
	LHDSP. (MSGDSP,MT.RUN-MINMTY,HMRUN) ;RUN message received
	LHDSP. (MSGDSP,MT.STA-MINMTY,HMSTRT) ;START message received
	LHDSP. (MSGDSP,MT.STP-MINMTY,HMSTOP) ;STOP message received

;MOVNIQ - Move all messages on the NI queue to the Scheduler queue
;
;Call:	T1/ Pointer to head of NIQ 
;	T2/ Pointer to tail of NIQ
;	CALL MOVNIQ
;	Normal return
;
;Uses: T3,T4 

MOVNIQ:	
	CHNOFF NIPIA		;Turn off NI interrupts
	DMOVE T1,HN.NIQ(HN)	;Get the header contents
	SETZM HN.NIQ(HN)	;Clear the queue begin pointer
	SETZM HN.NIQ+1(HN)	;Clear the end pointer
	CHNON NIPIA		;Turn NI back on
	OPSTR <SKIPN T3,>,HNSCQ,+1(HN);Is there anything on the queue?
	IFSKP.			;YES
	  STOR T1,UELW1,(T3)	;Add new elements at the queue tail.
	ELSE.			;NO
	  STOR T1,HNSCQ,(HN)	;New queue header forward pointer
	ENDIF.
	STOR T2,HNSCQ,+1(HN)	;New queue header tail pointer.
	RET
	SUBTTL	LATLSC - Ethernet Link State Change Callback

;LATLSC - Process an asynchronous link state change
;
;Call:	T1/ NU.RCI
;	T2/UN block address
;	Normal return
;

LATLSC:	MOVX T3,HNRUN
	ANDCAM T3,HN.FLG(HN)	;Assume NI is up
	TMNN UNRUN,(T2)		;Is the NI running?
	IORM T3,HN.FLG(HN)	;No
	RET
	SUBTTL	Message Receiver - HMSTRT

;HMSTRT - Process a Received START Message
;
;Call:	RB/ Address of Message Block
;	CALL HMSTRT
;	 Error Return - error processing completed, message processing aborted.
;	Normal Return

HMSTRT:	TMNN HNCIP,(HN)		;If the START message template is locked
	SKIPN MSGSID		;or source's message handle is 0
	RET			; ignore since server will persist.
	LOAD T1,HNNAC,(HN)	;If the addition of this circuit brings total
	OPSTR <CAML T1,>,HNMAC,(HN); over the maximum number of active circuits
;**; [7234]	Change 1 line at HMSTRT:+5	HMP	24-Jan-86
	 JSP T2,NOCRE0		;[7234] then reject it.
	CALL HSTRCB		;Find the CB associated with this circuit.
	 RET			;None.  Error action already taken.
	LOAD T1,HNLAS,(HN)	;If LAT access state is not ON, 
	CAIE T1,LS.ON		;then
	CALLRET LCLHLT		;stop the circuit.
	CALL GETXBH		;Get the necessary number of transmit buffer
	 SKIPA			; headers (+MSD pointers)
	CALL GETRBF		;Go see if we need more receive buffers
	 JSP T2,NOCRES		;Could not get the necessary buffer quota
	LOAD T1,CBSTA,(CB)	;Get the current circuit state
	CAIN T1,CS.RUN		;If the state is running, 
	CALL RELSBS		; release all current slots before continuing
	CALL CBINIT		;Initialize all host CB parameters
	 RET			;START message format bad. Error action done.
	CALL MTTSTR		;Send a START message
	MOVEI T1,CS.STA		;Put the circuit into the
	STOR T1,CBSTA,(CB)	; STARTING state.
	RET			;Return the receive buffer.
	SUBTTL 	HSTRCB

;HSTRCB - Find the circuit block for a received START msg on a LAT host.
;
;Call:	RB/ address of received message block
;	CALL HSTRCB
;	 Error Return - CB not found
;	Normal Return
;	CB/ Circuit Block address

HSTRCB:	XMOVEI T1,HN.QIC(HN)	;Search the inactive CB queue first
	CALL CMNIAD		;Look for NI address match
	 SKIPA			;Not found on the inactive CB queue
	JRST CBIFND		;Correct CB found on inactive CB queue
NFND1:	XMOVEI T1,HN.QAC(HN)	;Not on inactive Q, try active Q
	CALL CMNIAD		;Look for NI address match
	 SKIPA			;Not on active queue either
	JRST CBAFND		;Active CB has been found
NFND2:	LOAD T2,HNNAC,(HN)	;Get number of currently active CBs
	OPSTR <CAML T2,>,HNMXC,(HN) ;Compare with maximum allowed
	IFSKP.			;Not at maximum number of CBs allowed
	  MOVEI T1,CC.LST	;The number of words to get for a CB
	  CALL MMGTZW		;Go get them
	   JSP T2,NOCRES	;Failed
	  MOVE CB,T1		;This is the new CB address
	  MOVEI T1,CS.HLT	;Don't assume that the halted state
	  STOR T1,CBSTA,(CB)	; is zero.
	  OPSTRM <AOS>,HNNCC,(HN);Increment number of allocated CBs.
	  JRST CBALNW		;Allocated new CB successfully
	ENDIF.
	OPSTR <SKIPN CB,>,QLBWD,+HN.QIC(HN);Check inactive Q for free CB
	JSP T2,NOCRES		;None, so can't reuse an inactive CB
CBIFND:	MOVE T2,CB		;(T1)Remove from inactive CB queue
	CALL LAUNQ
CBALNW:	CALL CBGNIX		;Get next assignable index for the CB
	XMOVEI T1,HN.QAC(HN)	;Put at the front of the active
	MOVE T2,CB		; CB
	CALL LAQUE		; queue
	OPSTRM <AOS>,HNNAC,(HN)	;New active circuit. Count it.
	SETONE CBSTA,(CB)	;Set state to non-HALTED since now active
	OPSTRM <AOS>,CCRCV,(CB)	;Increment per server count
	OPSTR <DMOVE T1,>,UNSAD,+UNB.OF(RB);Move the source NI address from
	OPSTRM <DMOVEM T1,>,CBDNI,(CB)  ; message to the CB
CBAFND:	RETSKP			;Return success
	SUBTTL	CBINIT - Circuit Block Initialization

;
;CBINIT - Initialize a Circuit Block from the Contents of a START Message
;
;Call:	CB/ Address of Circuit Block
;	RB/ Address of Message Block
;	CALL CBINIT
;	 Error Return - START Message content in error.
;	Normal Return

CBINIT:	MOVEI T1,MSTMSI		;Check to see if the message is at least
	OPSTR <CAMLE T1,>,UNBSZ,+UNB.OF(RB); the minimum size for a START msg
	JSP T2,ILLMES		;No, too short.
	SETZM CLRBEG(CB)	;Clear the region which must be initialized
	MOVEI T1,<CLREND-CLRBEG>; to zero when a circuit block is 
	XMOVEI T2,CLRBEG(CB)	; re-used.
	XMOVEI T3,CLRBEG+1(CB)	; ...
	XBLT. T1		; ...
	MOVE T1,MSGSID		;Move source's circuit ID from message
	STOR T1,CBRID,(CB)	; to circuit block
	CALL LAG2BY		;Get the maximum transmit frame size which
	STOR T1,CBMTF,(CB) ; the remote will allow
	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);LAT protocol version used by remote
	CAIL T1,LALPV		;Check if in the range of version supported
	CAILE T1,LAHPV		; by this node
	JSP T2,LVSKEW		;Out of range, return error
	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Remote's protocol ECO level
	LSH T2,^D9		;Shift protocol version
	IOR T1,T2		;OR version and ECO level
	STOR T1,CBRPV,(CB) ;	; and store as single unit
	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);Maximum number of slots in VC message
	STOR T1,CBMSL,(CB)	; permitted by remote
	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);Number of additional receive buffers
	STOR T1,CBNBF,(CB)	; queued by remote for this circuit
	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);Remote's circuit timer value
	STOR T1,CBCTI,(CB)	;
	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);Remote's keep-alive timer value
	STOR T1,CBKTI,(CB) 	;
	IMULI T1,^D2000		;Convert to ms and multiply by 2 (fudge factor)
	STOR T1,CBKAV,(CB)	;and store in keep alive value field.
	MOVX T1,-^D6		;Decrement residual message count by what we
	OPSTRM <ADDM T1,>,UNBSZ,+UNB.OF(RB); have read so far.
	CALL LAG2BY		;Get the "facility number"
	STOR T1,CBNUM,(CB)
	CALL LAG2BY		;Get the product type code
	STOR T1,CBPTC,(CB)
	MOVEI T1,0		;Skip the circuit name
	CALL GTCSTR
	 JSP T2,ILLMES		;Illegally formatted message
	MOVEI T1,ML.SYS		;Maximum length of system's name string
	XMOVEI T2,CB.SNM(CB)	;Where copy the name string
	TXO T2,<OWGP. 7,0>
	CALL GTCSTR
	 JSP T2,ILLMES		;Illegally formatted message
	STOR T1,CBRSC,(CB)	;Save the length of the name string
	MOVEI T1,ML.LOC		;Maximum length of the system's location string
	XMOVEI T2,CB.LOC(CB)	;Where to copy the location string
	TXO T2,<OWGP. 7,0>
	CALL GTCSTR
	 JSP T2,ILLMES		;Illegally formatted message
	STOR T1,CBRLC,(CB) 	;Save the length of the location string
	RETSKP
	SUBTTL	CMNIAD - Find CB base on NI address

;CMNIAD - search the CBs on a CB queue looking for one which has the same
;	NI address as the source NI address in the incoming message.
;
;Call:	T1/ Queue header of CB queue to search
;	RB/ Address of the circuit message
;	CALL CMNIAD
;	 Not found return
;	Success return
;	T1/ unchanged
;	CB/ circuit block address if found, 0 otherwise

CMNIAD:	LOAD CB,QLFWD,(T1)	;Get the first queue entry
CMLOOP:	JUMPE CB,RTN		;Is this last entry?
	LOAD T3,CBDNI,(CB)	;First 4 bytes of remote NI address from CB
	LOAD T4,CBDNI,+1(CB)	;Last 2 bytes
	OPSTR <CAMN T3,>,UNSAD,+UNB.OF(RB);Compare with message destination adr
	OPSTR <CAME T4,>,UNSAD,+<UNB.OF+1>(RB) ;...
	IFNSK.
	  LOAD CB,QLFWD,+CB.LNK(CB); Get next forward CB
	  JRST CMLOOP		;Try again
	ENDIF.
	RETSKP			;Return success
		

;
;CBGNIX - Get the next assignable index for a CB
;
;Call:	CB/ address of circuit block
;	CALL CBGNIX
;	Normal Return
;
; This routine is called each time a CB is to be used for a new LAT
; virtual circuit.  The old index vector (CBVECT) pointer to the CB
; is deleted and a new index is generated.

CBGNIX:	MOVE W1,CBVECT		;Get the address of the CB index vector
	OPSTR <SKIPN T2,>,CBLID,(CB);Get the old circuit index, if any.
	IFSKP.
	  ADD T2,W1		;Get the address of the pointer to this CB
	  SETZM (T2)		; in the CB index vector and clear it.
	ENDIF.
	LOAD T2,HNNXI,(HN)	;Get the next available index to assign
	STOR T2,CBLID,(CB)	;Set the new index in the CB
	MOVE T1,W1		;Address of teh CB index vector
	ADD T1,T2		;Offset into this vector for new index
	MOVEM CB,(T1)		;Set the new pointer to point to new CB
CBGNX0:	AOS T2			;Increment the next available index
	AOS T1			; and the corresponding index vector pointer
	CAIG T2,CBMAXI		;Time to wrap around?
	IFSKP.			;Yes
	  MOVEI T2,1		;Always start at 1
	  MOVE T1,W1
	ENDIF.
	SKIPE (T1)		;Is this vector entry free?
	JRST CBGNX0		;No, try next
	STOR T2,HNNXI,(HN)	;Store new "next assignable index"	
	RET
	SUBTTL HMRUN

;HMRUN - Process a Host RUN Message
;
;Call:	
;	CALL HMRUN
;	 Error return
;	Normal return

HMRUN:	CALL HRUNCB		;Get the circuit block address
	 RET			;None, error action already comleted
	SKIPE T2,MSGDID		;Destination and source circuit IDs from
	SKIPN T1,MSGSID		; the message may not be zero
	JSP T2,ILLMES		;If so, it is illegal
	LOAD T3,CBRID,(CB)	;Get the remote's circuit index from the CB
	LOAD T4,CBLID,(CB)	; and our circuit index from the CB must match
	CAMN T1,T3		; the source id and
	CAME T2,T4		; the destination id from the msg.
	JSP T2,INVRUN		;Invalid RUN msg, go terminate the circuit.
	MOVE T1,TODCLK		;Get current time
	STOR T1,CBKAT,(CB)	;Save time for keep-alive process
	MOVEI T1,CS.RUN		;Put in RUNNING state if not there already
	STOR T1,CBSTA,(CB)	;Store state in CB
	MOVE T1,MSGACK		;Get the SEQ/ACK number
	MOVE W1,T1		;
	ANDI T1,377		;Received message sequence number
	LSH W1,-^D8		;Last message ack'd by remote
	LOAD T3,CBRSQ,(CB)	;Previous recv SEQ number
	AOS T3			;Increment
	ANDI T3,377		;Modulo 256
	CAME T1,T3		;Is is the expected sequence number
	IFSKP.
	  STOR T1,CBRSQ,(CB)	;Update expected sequence number for next time
	ELSE.
	  SETZM NSLOTS		;No, ignore any slots in this message
	  OPSTRM <AOS>,HCSEQ,(HN);Increment Host's count
	  OPSTRM <AOS>,CCSEQ,(CB);Increment per-server count
	ENDIF.
	LOAD T3,CBLRA,(CB)	;Previous "last ACK from remote"
	STOR W1,CBLRA,(CB)	;New "last ACK from remote"
	CAMGE W1,T3		;Is new ACK less than old ACK?
	ADDI W1,^D256		;Yes, new ACK has wrapped around
	SUB W1,T3		;Number of messages ACK'd by this msg
	CAILE W1,MAXXBF		;Range check it and if out of range
	JSP T2,ILLMES		; treat as an illegal message
	SKIPN W1		;If anything got ACK'd
	IFSKP.
	  SETZRO CBRTC,(CB)	
	ENDIF.
NXTACK:	JUMPE W1,ACKDON		;No new messages ACK'd
	MOVE XB,CB.AKQ(CB)	;Sneak look at buffer at head of Q
	CALL RELXBF		;Free large MSD buffer.
	XMOVEI T1,CB.AKQ(CB)	;Unqueue the now acknowledged buffers
	CALL UNQ1WF		; from the ACK wait queue
	  JSP T2,ILLMES	;Should not happen
	XMOVEI T1,CB.XBQ(CB)	;Queue  them back to the transmit buffer
	CALL QUE1WB		; queue.
	SOJA W1,NXTACK
ACKDON:	OPSTR <SKIPN>,CBAKQ,(CB);If all messages ack'd
	SETZRO CBTIM,(CB)	; clear the circuit timer.
	SETONE CBMRS,(CB)	;Indicate we must respond to this msg "soon"
	CALL LSDMUX		;Go process all slots in the RUN message
	IFNSK.			;Invalid slot type returned.
	  BUG. (INF,LATIST,LATSRV,SOFT,<LAT Illegal Slot Type>,<<T2,SLTID>>,<

Cause:	LAT Slot received with Slot type out of range.

Data:	SLTID - Slot ID

>)
	  MOVEI T1,CE.ILL	;Set up stop reason code.
	  CALL MTTSTP		;Illegal slot, kill the circuit
	ENDIF.
	RET			;All slots processed, return receive buffer
	SUBTTL 	HSTRCB

;
;HRUNCB - Find the circuit block for a received RUN/STOP msg on a LAT host.
;
;Call:	RB/ address of received message block
;	CALL HRUNCB
;	 Error Return - CB not found
;	Normal Return
;	CB/ Circuit Block address
;

HRUNCB:	SKIPN CB,MSGDID		;Index to CB from received message
	JSP T2,ILLMS0		;Zero message destination index is illegal	
	CAIG CB,CBMAXI		;If CB index is greater than our max
	IFSKP.
	  SETZ CB,		;it's also illegal, so flag no circuit
	  JSP T2,ILLMS0		;and go send a stop message
	ENDIF.
	ADD CB,CBVECT		;Add address of vector of pointers to CBs
	SKIPN CB,(CB)		;The real circuit block address
	JSP T2,INVRUN		;Circuit probably went away.
	OPSTR <DMOVE T3,>,CBDNI,(CB);All 6 bytes of remote NI address from CB
	OPSTR <CAMN T3,>,UNSAD,+UNB.OF(RB);Compare with message destination adr
	OPSTR <CAME T4,>,UNSAD,+<UNB.OF+1>(RB) ;...
	 SKIPA			;Not the right CB
	JRST HRNFND			;Found the CB	
	XMOVEI T1,HN.QAC(HN)	;Try searching the active Q
	CALL CMNIAD
	 SKIPA			;Not there either
	JRST HRNFND		;Found the CB
	XMOVEI T1,HN.QIC(HN)	;Try searching the inactive Q
	CALL CMNIAD
	 JSP T2,ILLMES		;Non-existent CB. Treat as illegal message.
HRNFND:	OPSTRM <AOS>,CCRCV,(CB)	;Increment per-server count
	RETSKP			;Found the CB
	SUBTTL HMSTOP

HMSTOP:	CALL HRUNCB		;Get the CB for this STOPS message
	 RET			;None, error action already done
	SKIPE MSGSID		;Is the source ID in MSG non-zero?
	 JSP T2,ILLMES		;Yes, that's not legal
	LOAD T1,CBLID,(CB)	;Get our remote ID for circuit. It must be
	LOAD T2,CBSTA,(CB)	;Current circuit state
	CAME T2,CS.HLT		;Is it halted or
	CAME T1,MSGDID		; an invalid STOP message?
	  SKIPA
	CALL CBDACT		;Move the CB from the active to inactive Q
	RET

; Routines to terminate a circuit because of some error.  T1 contains
; the error to be entered into the STOP message.

ILLMES: OPSTRM <AOS>,CCIMR,(CB)	;Increment per-server count
ILLMS0:	OPSTRM <AOS>,HCIMR,(HN)	;Increment host count
	MOVEI T1,CE.ILL		;Illegally formatted message received
	JRST INFSTP		;Join common code to kill off circuit

LVSKEW:	MOVEI T1,CE.SKW		;Version skew between server and host,
	JRST INFSTP		; cannot support connection.

NOCRES: OPSTRM <AOS>,CCIMR,(CB)	;Increment per-server count
NOCRE0:	OPSTRM <AOS>,HCIMR,(HN)	;Increment host count
	MOVEI T1,CE.RES		;Insufficient resources to support new circuit.
	JRST INFSTP

XMTLIM: MOVEI T1,CE.LIM		;Retransmit limit exceeded. Server probably
	JRST INFSTP		; has gone away.

IDLHLT:	MOVEI T1,CE.NSL		;Circuit halted because there are no more slots
	JRST MTTSTP

LCLHLT: MOVEI T1,CE.HLT		;Circuit halted by local system. Either stopped
	JRST MTTSTP		; by operator or last slot has disconnected.

NIHALT:	MOVEI T1,CE.NIH		;NI halted
	SETZ T2,0		;PC meaningless
	JRST INFSTP

INVRUN:	MOVEI T1,CE.INV		;Invalid message
	JRST INFSTP

INFSTP:	BUG. (INF,LATNSC,LATSRV,SOFT,<LAT Host node stopped circuit>,<<T1,CODE>,<T2,PC>>,<

Cause:	LAT Host node stopped the circuit.

Action: Look at the Reason Code in T1 and the PC in T2.  This error, if
	relatively infrequent is nothing to be concerned about.  If it occurs
	frequently, use the CODE and PC to determine further action.

Data:	CODE - Reason code
	PC - PC
>)
	JRST MTTSTP
	SUBTTL	Message Transmit Routines

;MTTSTR - transmit a START message
; 
;Call:	CALL MTTSTR
;	Normal Return
;
; This routine builds the START message header in the template from the host
; node data base and transmits it. It sets the interlock so that the template
; cannot be changed at process level by the LATOP% JSYS.  The message count
; is not changed since it is already correct (hence LAP2B0 is called instead of
; LAP2BY).  Cannot fail after this point since sending a STOP will clobber
; the START msg template.

MTTSTR:	SETONE HNCIP,(HN)	;Set interlock on the msg template
	XMOVEI XB,HN.SMT(HN)	;Address of start message template
	MOVX T1,<POINT 8,<SBF.OF>(XB)>;Set pointer to the message header
	STOR T1,UNBFA,+UNB.OF(XB); for BMSGHD.
	MOVEI T1,<MT.STA_2>	;Build a START
	CALL BMSGHD		; message header
	XMOVEI T1,<SBF.OF>(XB)	;Build the one word global
	TLO T1,^D44_^D12	; byte pointer.
	STOR T1,UNBFA,+UNB.OF(XB);
	OPSTR <DMOVE T1,>,CBDNI,(CB);
	OPSTRM <DMOVEM T1,>,UNDAD,+UNB.OF(XB);
	LOAD T1,HNPID,(HN)	;Get the Portal ID for NISRV
	STOR T1,UNPID,+UNB.OF(XB)
	MOVX T1,NU.XMT		;Go transmit but don't
	XMOVEI T2,UNB.OF(XB)	; use the usual routine
	CALL DLLUNI		; ...
	IFSKP.
	  OPSTRM <AOS>,HCXMT,(HN);Increment host count
	  OPSTRM <AOS>,CCXMT,(CB);Increment per-server count
	ELSE.
	  SETZRO HNCIP,(HN)	;Release the lock
	ENDIF.
	RET

;CBDACT - Deactivate CB by moving to inactive queue
;
;Call:	CB/ address of the circuit block
;	CALL CBDACT
;	Normal return

CBDACT:	JUMPE CB,RTN		;If there is no CB, don't do the following
	SETZRO CBRID,(CB)	;Clear remote circuit index
	MOVEI T1,CS.HLT		;If the current circuit state is not halted
	OPSTR <CAMN T1,>,CBSTA,(CB); move CB from active to inactive queue
	RET			; otherwise CB is already on inactive queue
	STOR T1,CBSTA,(CB)	;Set the state to halted now
	XMOVEI T1,HN.QAC(HN)	;Remove 
	MOVE T2,CB		; the circuit block
	CALL LAUNQ		; from the active queue
	XMOVEI T1,HN.QIC(HN)	; and queue to 
	MOVE T2,CB		;Need the CB address again.
	CALL LAQUE		; the inactive queue
	OPSTRM <SOS>,HNNAC,(HN)	;Reduce the number of active circuits
	RET
;MDLXBF - Remove all buffers from ACK Wait queue, and mark them
;		for deletion at transmit complete.
;
;Call:	CB/ circuit block address
;	CALL MDLXBF
;	Normal return
;

MDLXBF:	SETZRO CBDLL,(CB)	;No buffers in DLL now
MDLXB1:	XMOVEI T1,CB.AKQ(CB)	;Address of the ACK wait queue
	CALL UNQ1WF		;Unqueue next buffer on Q
	 RET			;Done
	MOVEI T1,0		;Mark buffer for freeing at xmit done
	EXCH T1,UN.UID+UNB.OF(T2) ;and get current "DLL" flag
	JUMPL T1,MDLXB1		;If in DLL, it will be freed at XMTDON
	MOVE XB,T2		; else we have to free it now.
	CALL RELXBH
	JRST MDLXB1

;MTTSTP - transmit a STOP message ;
;Call:	T1/ stop reason code
;	XB/ address of transmit buffer
;	RB/ address of recieve buffer
;	CB/ address of circuit block (may be 0)
;	CALL MTTSTP
;	Normal return
;

MTTSTP:	MOVEM T1,STPCOD		;Save the stop reason.
	JUMPE CB,MTTSP0		;If there is not CB, just send stop msg.
	STOR T1,CBERR,(CB)	;Store reason for the stop in the CB
	LOAD T1,CBRID,(CB)	;Get the id to which to send the STOP
	MOVEM T1,MSGSID		; and put where we want it below
	CALL RELSBS		;Release all slots asssociated with this CB
	CALL RFXBFS		; Release all buffers on
	CALL MDLXBF		; free queue and mark all those in DLL
	CALL CBDACT		; and remove the CB from the active queue
;
;	Here to actually send a stop message
;	Always get a temp xmit buffer.
;	Then send the message with a 0 in UNUID so that
;	XMTDON will always just release the buffer.
;
MTTSP0:	MOVX T1,HNRUN		;If the NI
	TDNE T1,HN.FLG(HN)	; is no longer running
	 RET			;We're done.
	MOVEI T1,SZMSTP	;There will be no buffers so get one
	CALL MMGTZW
	 RET			;Couldn't. Assume we did.
	MOVE XB,T1		;Transmit buffer for STOP message
	MOVX T1,<POINT 8,<XBF.OF>(XB)>;Byte pointer to start of msg
	STOR T1,UNBFA,+UNB.OF(XB);Store byte ptr to use building STOP msg
	MOVEI T1,MT.STP_2	;Message type (STOP) and number of slots (=0)
	CALL LAP2B0		; to message
	MOVE T1,MSGSID		;Get the source ID from the received message
	CALL LAP2B0		; and enter as destination ID to msg to send.
	MOVEI T1,0		;Get a 0 source ID
	CALL LAP2B0		; and enter into msg to send
	CALL LAP2B0		;Ack and sequence number must be zero also.
	MOVE T1,STPCOD		;Enter Disconnect reason and Reason text byte
	CALL LAP2B0		; (=0 since text field not used) to msg.
	XMOVEI T1,<XBF.OF>(XB)	;Set up 1 word global byte pointer
	TLO T1,(<OWGP. 8,0>)	; to the transmit data for NISRV
	STOR T1,UNBFA,+UNB.OF(XB); and store in UN block
	MOVEI T1,MINXBF		;Initialize to zero
	STOR T1,UNBSZ,+UNB.OF(XB); the message count
	CALLRET XMTMSN		;Go give the message to the DLL.
				; It will be freed at XMTDON

;MTTRUN - transmit a RUN message
;Call:	XB/ address of the transmit buffer
;	CALL MTTRUN
;	Normal return

MTTRUN:	JUMPE XB,RTN		;No message to transmit
	XMOVEI T1,CB.XBQ(CB)	;Unqueue the transmit buffer
	CALL UNQ1WF		; from the free Q
	 NOP			;Would not be here if queue empty.
	OPSTR <SKIPE>,CBXBQ,(CB);Is this our last transmit buffer?
	IFSKP.
	  SETONE CBRRF,(CB)	;Yes, set the reply requested flag
	  LOAD T1,HNTIM,(HN)	;Value for circuit timer
	  ADD T1,TODCLK		;Plus current time
	  STOR T1,CBTIM,(CB)	;Store in circuit timer
	ENDIF.
	XMOVEI T1,CB.AKQ(CB)	; and queue to the end
	CALL QUE1WB		; of the ACK queue
	LOAD T1,CBTSQ,(CB)	;Get the transmit sequence number
	AOJ T1,			; and increment it by one before
	STOR T1,CBTSQ,(CB)	; using it in the next transmitted msg.
	MOVX T1,<POINT 8,<XBF.OF>(XB)>;Set message pointer to point to
	STOR T1,UNBFA,+UNB.OF(XB); the RUN message header.
	MOVEI T1,MT.RUN_2	;Message type for the header.
	CALL BMSGHD		;Build the message header
	MOVEI T1,SZ.MHD		;Store the message header length
	OPSTR <SKIPN>,MDNXT,+MDB.OF(XB); which if there is no slot data
	MOVEI T1,MINXBF		; must be the minimum NI datagram size.
	STOR T1,MDBYT,+MDB.OF(XB); in the appropriate place
	SETZRO UNBSZ,+UNB.OF(XB);MBZ for MSD type transmits
	XMOVEI T1,<MDB.OF>(XB)	;Address of the first MSD block
	STOR T1,UNBFA,+UNB.OF(XB);stored where usually byte pointer goes.
	CALL XMTMSG		;No, go transmit a single buffer XB
	RET

;TMRCHK - Host circuit timer check routine.  This routine performs actions
; base on the state of the circuit timer.
;Call:	CB/ circuit block address
;Return:
;	RET - no further action required for this circuit
;	RESKSP - continue processing

TMRCHK:	OPSTR <SKIPN T1,>,CBTIM,(CB);Is the circuit timer running?
	RETSKP			;No, continue processing circuit
	SUB T1,TODCLK		;Account for current time
	SKIPL T1		;Any time still left?
	IFSKP.			;Circuit timer has expired.
	  LOAD T1,HNRLI,(HN)	;Get the retransmit limit.
	  OPSTR <CAMG T1,>,CBRTC,(CB);Have we reached the limit?
	  IFSKP.		;No, so
	    CALL XUNAKQ		; retransmit the unack queue
	    LOAD T1,HNTIM,(HN)	; and reset the timer
	    ADD T1,TODCLK	; from now.
	    STOR T1,CBTIM,(CB)	;Restore reset timer to CB
	  ELSE.
	    JSP T2,XMTLIM	; otherwise, terminate the circuit
	  ENDIF.		; cause communication broken with remote.
	  RET
	ENDIF.
	RETSKP
	

;XUNAKQ - Transmit all messages on the unacknowledged queue

XUNAKQ:	OPSTR <SKIPN>,CBDLL,(CB);Don't retransmit if already transmitting
	OPSTR <SKIPN XB,>,QLFWD,+CB.AKQ(CB) ;First msg to retransmit
	RET			;Should be
	OPSTRM <AOS>,HCRTR,(HN)	;Increment host count
	OPSTRM <AOS>,CCRTR,(CB)	; and per-server count
	OPSTRM <AOS>,CBRTC,(CB)	;Increment retransmit count
XMTALL:	OPSTRM <AOS>,CBDLL,(CB)	;Increment buffers in DLL
	MOVX T1,NU.XMT		;Transmit function
	XMOVEI T2,UNB.OF(XB)	;UN block
	CALL DLLUNI
	IFNSK.
	  OPSTRM <SOS>,CBDLL,(CB)
	ENDIF.
	SKIPE XB,UE.LW0(XB)	;Next XB on queue to (re)transmit
	JRST XMTALL		;Still more, continue to give buffers to DLL
	RET

; BMSGHD - Build a LAT Virtual Circuit Message Header

;Call:	T1/ <Message type>_2
;	XB/ Message buffer address
;	CB/ Address of the Circuit Block

BMSGHD:	MOVE T2,NSLOTS		;Number of slots in message
	LSH T2,^D8		;Shift to left byte position
	IOR T1,T2		;Or in message type (shifted)
	OPSTR <SKIPE>,CBRRF,(CB);If the Reply Request Flag is set
	IORI T1,1		; set it in message header
	CALL LAP2B0		;Enter as 1st 2 bytes of header
	LOAD T1,CBRID,(CB)	;Get the remote's circuit index
	CALL LAP2B0		; and enter as 2 byte field
	LOAD T1,CBLID,(CB)	;Get our circuit index
	CALL LAP2B0		; and enter as 2 byte field
	LOAD T1,CBRSQ,(CB)	;ACK number
	LSH T1,^D8		;Shift to left byte position
	LOAD T2,CBTSQ,(CB)	;Send Seq Number
	ANDI T2,377		;Modulo 256
	IOR T1,T2
	CALL LAP2B0
	RET	
	SUBTTL	Message Transmitter -- XMTMSG

;
;XMTMSG - Transmit a LAT Message
;
;Call:	CB/ 0 or Circuit Block Address
;	XB/ Address of the UN block/Transmit Buffer
;	RB/ Address of Receive Buffer if XB/0
;	UNBSZ/ Message byte count
;	CALL XMTMSG
;	Normal Return
;
; This routine sets up the UN block for NISRV and issues the transmit
; function.  Only UNBSZ must be set by the caller.  If there is no CB,
; the information is taken from the receive buffer, otherwise from the
; circuit block.
;
;If there is no possibility of an ACK (such as sending a STOP message),
; then call XMTMSN, which will set UNUID to 0 so that the buffer will
; just be returned to free memory after NISRV has sent it.
;

XMTMSN:	TDZA T1,T1		;Enter here if you just want to free the buffer
				;  when transmit is done.
XMTMSG:	MOVE T1,CB
	TLO T1,DLL.FL		;set "buffer-in-DLL" flag
	STOR T1,UNUID,+UNB.OF(XB);Save the CB address for completion action
	JUMPE CB,XMNOCB		;Case with no assigned circuit block
	LOAD T1,HNPID,(HN)	;Portal ID
	OPSTR <DMOVE T2,>,CBDNI,(CB) ;Get destination NI adr from CB
	OPSTRM <AOS>,CBDLL,(CB) ;Increment # buffers in the DLL
	JRST XMTMS0		;Go do common code
XMNOCB:	LOAD T1,UNPID,+UNB.OF(RB);Get the portal ID
	OPSTR <DMOVE T2,>,UNSAD,+UNB.OF(RB) ;Source NI address from recvd msg
XMTMS0:	STOR T1,UNPID,+UNB.OF(XB);Store in xmit buffer
	OPSTRM <DMOVEM T2,>,UNDAD,+UNB.OF(XB) ; to the xmit buffer
	STOR XB,UNRID,+UNB.OF(XB);Remember the buffer address
	MOVEI T1,UNA.EV
	STOR T1,UNADS,+UNB.OF(XB);Tell NISRV we are using EXEC virtual memory
	MOVX T1,NU.XMT		;NI transmit function code
	XMOVEI T2,UNB.OF(XB)	;UN block address
	CALL DLLUNI
	IFSKP.
	  OPSTRM <AOS>,HCXMT,(HN)
	  JUMPE CB,RTN
	  OPSTRM <AOS>,CCXMT,(CB)
	ELSE.
	  MOVSI T1,DLL.FL	;get "buffer-in-DLL" flag
	  ANDCAB T1,UN.UID+UNB.OF(XB) ;clear it in message
	  SKIPN T1
	  CALL RELXBH
	  JUMPN CB,RTN
 	  OPSTRM <SOS>,CBDLL,(CB);Failed so put back count where is was
	ENDIF.
	RET
	SUBTTL	Message Transmitter -- XMTDON

;XMTDON - Message Transmitter Transmit Done Scheduler Level Routine
;
;Call:	T1/ NU.XMT
;	T2/ UN block address
;	CALL XMTDON
;	Normal return
;
;  If the UNRID is zero, there is no longer a CB for this buffer because the
; circuit went away and we don't care what happens at transmit complete.  Just
; release the buffer.  If the CB state is stopping, complete the stopping of
; the circuit which could not be done earlier because all xmit buffers were in
; the DLL.

XMTDON:	SAVEAC <CB>
	MOVE XB,T2		;Move transmit buffer adr to conventional AC.
	LOAD CB,UNUID,+UNB.OF(T2);Get the CB address
	TLZ CB,DLL.FL		;Clear the "buffer-in-DLL" flag
	STOR CB,UNUID,+UNB.OF(T2); and store back this way
	JUMPE CB,RELXBH	;If there is no CB, release buffer and header and return
	OPSTRM <SOS>,CBDLL,(CB)	;One less buffer in the DLL
	RET
	SUBTTL Receive Message Handling Routines

;LAG2BY - obtains two bytes from the message which is assumed to be a 16-bit
; quantity.
;
;Call:	MA/ Message base address
;	CALL LAG2BY
;	Normal Return
;Changes T2
;	T1/ 16-bit quantity

LAG2BY:	MOVX T1,-2		;Amount to decrement the count by
	OPSTRM <ADDM T1,>,UNBSZ,+UNB.OF(RB);Update message count
	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);Get low order byte
	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Get high order byte
	LSH	T2,^D8		;Shift high order byte left
	IOR	T1,T2		;Make a 16-bit quantity
	RET			;Return with result

;GTCSTR - Move Counted String from a Message
;
;Call:	T1/ Maximum count to move
;	T2/ Destination byte pointer for string
;	RB/ Message base address
;Changes T1,T2,T3,T4

GTCSTR:	SAVEAC <W1,W2>		;Get two work registers
	OPSTR <SKIPN T3,>,UNBSZ,+UNB.OF(RB);Byte count left in message
	RET			;Return error. Message unexpectedly truncated
	OPSTR <ILDB W1,>,UNBFA,+UNB.OF(RB);Count of the counted string
	SUBI T3,1(W1)		;Calculate new residual message count
	JUMPL T3,RTN		;Message truncated
	STOR T3,UNBSZ,+UNB.OF(RB);Store back into message header
	SKIPE W1		;A null string?
	IFSKP.
	  MOVEI T1,0		;Yes, return zero count
	  RETSKP		; successfully
	ENDIF.
	MOVE T3,W1		;Copy of the actual string count
	CAMLE T3,T1		;Get the minimum of (actual string count,
	MOVE T3,T1		; maximum string count) for copy loop index
	MOVEM T3,T1		; and store as actual count copied
	LOAD T4,UNBFA,+UNB.OF(RB);Save initial value of byte pointer
	JUMPE T1,GTCDON		;Skipping over this field
GTCSLP: OPSTR <ILDB W2,>,UNBFA,+UNB.OF(RB);Fetch a source byte
	IDPB W2,T2		;Store as destination byte
	SOJN T3,GTCSLP		;Loop till all bytes copied
GTCDON:	ADJBP W1,T4		;Adjust byte pointer properly in case less than
	STOR W1,UNBFA,+UNB.OF(RB); the full string was copied
	RETSKP			;Return success, T1/ count of string fetched
	SUBTTL Transmit Message Handling Routines

;LAP2BY - Put 2 bytes to message
;
;Call:	XB/ Address of the transmit buffer
;	T1/ 16-bit quantity to ouput
;	CALL LAP2BY or CALL LAP2B0
;	Normal return
;T1 perserved, T2 used
;
; This routine enters a 16-bit quantity as two bytes from T1 into the transmit
; buffer with the least significant entered first.  It assumes that UNBFA is
; set up with the proper byte pointer to the message buffer. The alternate
; entry LAP2B0 differs from LAP2BY in that LAP2B0 does not increment the
; message byte count whereas LAP2BY does.

LAP2BY:	MOVEI T2,2		;Amount to increment the message count by
	OPSTRM <ADDM T2,>,UNBSZ,+UNB.OF(XB);Update the count in message header
LAP2B0:	MOVE T2,T1		;In order to return T1 preserved.
	OPSTR <IDPB T2,>,UNBFA,+UNB.OF(XB);Low order byte entered first
	LSH T2,-^D8		;Shift hi byte into position
	OPSTR <IDPB T2,>,UNBFA,+UNB.OF(XB);High order byte goes to b buffer
	RET

;
;PTCSTR - put counted string to message buffer
;
;Call:	XB/ Address of the transmit buffer
;	T1/ Count to enter into buffer
;	T2/ Source string byte pointer

PTCSTR:	MOVEI T3,1(T1)		;Increment to include count byte
	OPSTRM <ADDM T3,>,UNBSZ,+UNB.OF(XB);Increment count in buffer
PTCST0:	OPSTR <IDPB T1,>,UNBFA,+UNB.OF(XB);Put the string count to buffer
	MOVE T3,T1		;Save copy of count for decrementing
PTCSTL:	JUMPE T3,RTN		;If count exhausted, done with copying
	ILDB T4,T2		;Get the next source string byte
	OPSTR <IDPB T4,>,UNBFA,+UNB.OF(XB);Put it to buffer
	SOJA T3,PTCSTL		;Continue til string count zero

;
;PTUSTR - put uncounted string to message buffer
;
;Call:	XB/ Address of the transmit buffer
;	T1/ Count to enter into buffer
;	T2/ Source string byte pointer

PTUSTR:	OPSTRM <ADDM T1,>,UNBSZ,+UNB.OF(XB);Increment count in buffer
	MOVE T3,T1		;Save copy of count for decrementing
PTUSTL:	JUMPE T3,RTN		;If count exhausted, done with copying
	ILDB T4,T2		;Get the next source string byte
	OPSTR <IDPB T4,>,UNBFA,+UNB.OF(XB);Put it to buffer
	SOJA T3,PTUSTL		;Continue til string count zero

;RBPOST - Return Receive Buffer to DLL
;
;Call:	T1/ Message block address
;	CALL RBPOST
;	Normal Return
;

RBPOST:	MOVEI T2,LMRFSI		;Size of the buffer
	STOR T2,UNBSZ,+UNB.OF(T1);Store in UN block for NISRV
	XMOVEI T2,<SBF.OF>(T1) ;Set up 1 word global byte pointer
	TLO T2,^D44_^D12	; to the transmit data for NISRV
	STOR T1,UNRID,+UNB.OF(T1);Store the buffer address for callback
	STOR T2,UNBFA,+UNB.OF(T1); and store in UN block
	LOAD T2,HNPID,(HN)	;Get the Portal ID for NISRV
	STOR T2,UNPID,+UNB.OF(T1)
	MOVEI T2,UNA.EV
	STOR T2,UNADS,+UNB.OF(T1);Tell NISRV we are using EXEC virtual memory
	XMOVEI T2,UNB.OF(T1)	;UN block address
	MOVX T1,NU.RCV		;Post receive buffer function code
	CALL DLLUNI
	IFNSK.
	  BUG. (CHK,LAPRBF,LATSRV,SOFT,<Specify Receive Buffer Failure>,<<T1,DLLERC>>,<

Cause:	LATSRV received an error from NISRV while attempting to post a
	receive buffer.

Data:	DLLERC - Error code returned by NISRV

>)
	  RET
	ENDIF.
	RETSKP
	SUBTTL	Slot Demultiplexor - LSDMUX

;
;LSDMUX - Process all slots in a received RUN message
;
;Call:	RB/ Address of RUN message
;	CALL LSDMUX
;	 Error return - illegal slot encountered
;	Normal return

	MIN. (MINSTY,<ST.DTA,ST.STA,ST.STP,ST.DTB,ST.ATT,ST.REJ>) ;Minimum slot type code
	MAX. (MAXSTY,<ST.DTA,ST.STA,ST.STP,ST.DTB,ST.ATT,ST.REJ>) ;Maximum slot type code

LSDMUX:	TRVAR <SLTDID,SLTSID,SLTCNT,SLTTYP,SLTEPT> ;Assign some variables on stack
	SKIPN T1,NSLOTS		;Any slots left to process?
	RETSKP			;No, return successfully
NXTSLT:	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Get the destination id
	MOVEM T2,SLTDID
	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Get the source id
	MOVEM T2,SLTSID
	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Get the slot count
	MOVEM T2,SLTCNT
	LOAD T3,UNBSZ,+UNB.OF(RB);Get the current message count
	AOS T1,T2		;Round up to the nearest even number
	TRZ T1,1		;since they may be 1 pad byte in slot
	SUBI T3,SLHDSZ(T1)	;Subtract the total slot size
	STOR T3,UNBSZ,+UNB.OF(RB);Store back in msg header as updated msg count
	JUMPL T3,ILLSLT		;Error if message has ended prematurely
	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Get the slot type/credits/reason
	MOVEM T2,SLTTYP
	ADJBP T1,UN.BFA+UNB.OF(RB);Point to next slot
	MOVEM T1,SLTEPT		;in case we need to skip later
	LSH T2,-4		;Shift off the credits field
	CAIL T2,MINSTY		;Range check slot type
	CAILE T2,MAXSTY
	CALLRET ILLSLT		;Slot ID out of range, illegal slot
	CALL @SLTDSP-MINSTY(T2)	;Dispatch to proper routine
	 JRST ILLSLT
	SOSG NSLOTS		;Indicate one less slot to process
	RETSKP			;Count went to zero, so done.
	MOVE T1,SLTEPT		;Get pointer to next slot's data
	MOVEM T1,UN.BFA+UNB.OF(RB);and force pointer to there
	JRST NXTSLT		;Still some left, go do next

SLTDSP:	BLOCK <MAXSTY-MINSTY+1>
	LHDSP. (SLTDSP,ST.DTA-MINSTY,HSDATA)
	LHDSP. (SLTDSP,ST.DTB-MINSTY,HSDATB)
	LHDSP. (SLTDSP,ST.STA-MINSTY,HSSTRT)
	LHDSP. (SLTDSP,ST.ATT-MINSTY,IGNSLT)
	LHDSP. (SLTDSP,ST.REJ-MINSTY,ILLSLT)
	LHDSP. (SLTDSP,ST.STP-MINSTY,HSSTOP)
 	SUBTTL HSSTRT

;HSSTRT  Process a received START slot
;
;Call:
;	CALL HSSTRT
;	 Error Return - Terminate slot loop
;	Normal Return - Continue to next slot
;

HSSTRT:	SKIPE SLTSID		;Source ID 0 is illegal
	SKIPE SLTDID		;Destination ID of non-0 is illegal
	CALLRET ILLSLT		;Report an illegal slot received
	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Get the service class requested
	CAIE T2,SCITTY		;Only interactive terminal class permitted
	CALLRET ILLSLT		;If not, slot is illegal.
	CALL SBALOC		;Allocate a slot block
	 RETSKP			;Ignore since server should persist and we
				; assume we should almost always get an SB
	OPSTRM <AOS T1,>,HNCON,(HN);Increment the number of connects.
	OPSTR <CAMLE T1,>,HNMCO,(HN);Check if maximum exceeded.
	JRST SLRESF		;No additional sessions permitted.
	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Attention slot size maximum
	STOR T2,SBATS,(SB)
	OPSTR <ILDB T2,>,UNBFA,+UNB.OF(RB);Data slot size maximum
	STOR T2,SBMDS,(SB)
	CALL GETTDB		;Get a terminal data block and start a job
	 JRST SLRESF		;No terminal data blocks available
	MOVX T1,SBSTR		;Flag this slot as waiting to send
	CALL SSBDAV		; a START slot when MUX runs.
	RETSKP			;Return success
SLRESF:	MOVEI T2,SE.RES		;Indicate insufficient resource as reject code
	STOR T2,SBREA,(SB)	; and save for later when REJ slot built
	MOVX T1,SBREJ		;Flag this slot as waiting to send
	CALL SSBDAV		; a REJECT slot when MUX runs
	RETSKP			;Return success
	SUBTTL Slot Processing Routines -- STOP Slots

;HSSTOP - Process a STOP Slot.
;
;Call:	SB/ Slot block address
;	CALL HSSTOP
;	 Error return - 
;	Normal return

HSSTOP:	SKIPE SLTSID		;Source Id must be zero
	CALLRET ILLSLT          ; else slot is illegal
	SKIPN T2,SLTDID		;Destination ID should not be zero
	CALLRET IGNSLT          ; else slot is useless
	CAILE T2,NTTLAH		;Is the SBVECT index in range?
	CALLRET ILLSLT		;No. Slot is illegal
	MOVE T1,SBVECT		;Get the slot block address from
	ADD T1,T2		; the destination slot id
	SKIPE SB,(T1)		;If the SB address is zero
	OPSTR <CAME CB,>,SBCBA,(SB); or CB address has changed then
	JRST IGNSLT		; ignore the slot since already gone.
	CALL RELSB		;Release all slot resources	
	RETSKP			;Return success
	SUBTTL	Slot Processing Routines -- DATA_A,DATA_B Slots

;HSDATA - Process a DATA_A Slot
;
;Call:  SB/ Slot block address
;	CALL HSDATA
;	 Error return - illegal slot
;	Normal return

HSDATA:	CALL HDRDAT		;Go process data slot header
	 RET			;Illegal slot, error return
	SKIPN W1,SLTCNT		;Get the number of characters in the slot
	RETSKP			;None, return
	LOAD T2,SBTDB,(SB)	;Terminal dynamic data base
	DYNST			;Get the line number
NXSLCH:	OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);Get a character.
	CALL TTCHI		;Call TTYSRV to enter into input buffer
	 TRN			;Ignore error
	SOJG W1,NXSLCH		;If still more, continue
	RETSKP			;Otherwise return success

HSDATB:				;Process received DATA_B slot (NOP)
HDRDAT:	SKIPE T2,SLTDID		;Destination ID cannot be zero
	CAILE T2,NTTLAH		; or greater than maximum slot index
	CALLRET ILLSLT		;Illegal slot
	MOVE T3,SLTSID		;Source ID from the slot
	MOVE T1,SBVECT		;Get the address of the SB
	ADD T1,T2		; based on the remote slot id from 
	SKIPN SB,(T1)		; the message 
	CALLRET IGNSLT		;No slot block.
	OPSTR <CAME T3,>,SBRID,(SB) ;Is slot source ID equal to SB remote ID?
	CALLRET IGNSLT		;No, ignore the slot
	LOAD T3,SBSTA,(SB)	;Get the slot state
	CAIN T3,SS.RUN		;If the slot state is not running or
	OPSTR <CAME CB,>,SBCBA,(SB) ; the circuit block address has changed
	CALLRET IGNSLT		; then slot disappeared already so ignore.
	MOVE T3,SLTTYP		;See if remote extended us any transmit
	ANDI T3,17		; credits. If so, add them to what we already
	OPSTRM <ADDM T3,>,SBXCR,(SB); have.
	SKIPN SLTCNT		;Do remote credit check only if there was
	RETSKP			; data in the slot.
	OPSTR <SOSGE T3,>,SBRCR,(SB) ;Decrement the receive credits
	JRST ILLSLT		;Illegal, server sent data with no credits	
	STOR T3,SBRCR,(SB)	;Store back updated credits count
	SKIPE T3		;If receive credits have gone to zero,
	IFSKP.			; then indicate we need to grant more credits
	  MOVX T1,SBOUT		; by setting setting data available
	  CALL SSBDAV		; for MUX when it runs next time.
	ENDIF.
	RETSKP
	SUBTTL	Slot Block Allocation and Deallocation Routines

;SBALOC - Slot Block Allocation Routine
;
;Call:	CB/ circuit block address
;	CALL SBALOC
;	 Error Return
;	Normal Return
;	SB/ Address of allocated slot block
;
; This routine allocates and initializes a new slot block.

SBALOC:	MOVEI T1,SB.LEN		;Number of words to allocate for a slot blk
	CALL MMGTZW		;Try to get them
	  RET			;Can't
	MOVE SB,T1		;Put address in proper AC
	MOVSI T4,-NSBWDS	;Look for a free slot block index
SBANXT:	SKIPE T1,NFRSBQ(T4)	; by examining the free slot block words
	JFFO T1,SBAGO		; for the first 1 bit.  This represents
	AOBJN T4,SBANXT		; the first free slot block index
	  RET			;No slot block available
SBAGO:	HRRZS T4		;Clear out left half
	MOVE T3,BITS(T2)	;Clear the bit since the index is
	ANDCAM T3,NFRSBQ(T4)	; no longer free
	IMULI T4,^D36		;Compute the slot block index
	ADDI T2,1(T4)		;Cannot be zero
	STOR T2,SBLID,(SB)	;Store in SB as local slot index
	MOVE T1,SBVECT		;Now store the address of the slot block
	ADD T1,T2		; in the vector of slot block address, which
	MOVEM SB,(T1)		; is indexed by slot block index
	XMOVEI T1,CB.SBQ(CB)	;Queue the SB to the circuit block's 
	MOVE T2,SB		; queue of slot blocks.
	CALL LAQUE		;
	MOVEI T1,CS.STA		;Set the slot state
	STOR T1,SBSTA,(SB)	; to starting.
	STOR CB,SBCBA,(SB)	;Store the circuit block address in SB
	MOVE T1,SLTSID		;Get the remote's slot id
	STOR T1,SBRID,(SB)	;Store as our remote id
	MOVE T1,SLTTYP		;Get the Slot type/credits from recv's slot
	ANDI T1,17		;Keep only the number of credits extended to us
	STOR T1,SBXCR,(SB)	;Store is slot block as our transmit credits
	MOVEI T1,MAXCRE		;Maximum credits to extend to remote
	STOR T1,SBRCR,(SB)	; to current number of remote credits
	RETSKP			;Return successfully

;SSBDAV - Set Slot BLock Data Available
;
;Call:	T1/ Bit to set in SB.FLG
;	SB/ Slot block address
;	CALL SSBDAV
;	Normal Return

SSBDAV:	SKIPGE T2,SB.FLG(SB)	;Is there already data available?
	IFSKP.			;No, so set it now
	  OPSTRM <AOS>,CBSDC,(CB);Increment number of slots waiting
	  TLO T2,400000		; and indicate this slot now waiting	 
	ENDIF.
	IOR T2,T1		;OR in new reason for waiting
	MOVEM T2,SB.FLG(SB)	; and store back
	RET			;Return

;CSBDAV - Clear Slot BLock Data Available
;
;Call:	T1/ Bit to clear in SB.FLG
;	SB/ Slot block address
;	CALL SSBDAV
;	Normal Return

CSBDAV:	SKIPL T2,SB.FLG(SB)	;Is "data available" already clear?
	RET			;Yes
	ANDCM T2,T1		;Clear the desired bit
	TLNE T2,377777		;Are all bits off now?
	IFSKP.
	  OPSTRM <SOS>,CBSDC,(CB)
	  MOVEI T2,0
	ENDIF.
	HLLM T2,SB.FLG(SB)
	RET

;RELSBS - Release all Slot Blocks and their Resources for a virtual circuit
;
;Call:	CB/ Address of the circuit block
;	CALL RELSBS
;	Normal Return
;

RELSBS:	JUMPE CB,RTN		;No slots to release if no circuit block
	OPSTR <SKIPN SB,>,QLFWD,+CB.SBQ(CB);Get the address of first SB on Q
	RET			;If zero, there are no more on the queue
	CALL RELSB		;Release the slot block
	JRST RELSBS		;Go do the next
	RET

;RELSB - Release a Slot Block and its Resources
;
;Call:	CB/ Address of the circuit block for slot
;	SB/ Address of the slot block to release
;	CALL RELSB
;	Normal Return
;	SB/ address of previous slot or listhead

RELSB:	OPSTR <SKIPN T2,>,SBTDB,(SB) ;Is there a terminal data base
	JRST RELSB0		;No, go just release the SB resources
	SETZM TTDEV(T2)		;Clear the slot block address in the TDB.
	SETZRO TTPRM,(T2)	;TDB no longer permanent
	SETZRO TTOTP,(T2)	;Clear output in progress so sched tests work.
	DYNST			;Get the static line number
	CALL NTYCOF		;Go detach the job
	LOAD T2,SBTDB,(SB)	;Get the TDB address again.
	LOAD T1,TCJOB,(T2)	;If there is a controlling job, don't release
	CAIE T1,-1		; the TDB since this will be done
	JRST RELSB0		; later by JOBCOF.
	DYNST			;No controlling job so we must release the
	CALL TTYDE0		; the dynamic data base here.
	  TRN			;TTYSRV already reports this.
RELSB0:	OPSTR <XMOVEI T1,>,CBSBQ,(CB) ;Point to the CB SBQ header
	MOVE T2,SB		; and 
	CALL LAUNQ		; release the SB from this queue
;**;[7231] Change 1 line at RELSB0+3	WCM	23-Jan-85
	SKIPL SB.FLG(SB)	;Did this SB have any data waiting?
	IFSKP.			;Yes
	  OPSTRM <SOS>,CBSDC,(CB) ;Reduce number of slots with data waiting
	ENDIF.
	OPSTRM <SOS>,HNCON,(HN)	;Reduce number of active connects.
	CALL RLSBID		;Release the SB local id.
	MOVE T1,SB
	OPSTR <SKIPN SB,>,QLBWD,(SB);Set up so the slot mux gets the correct
	XMOVEI SB,CB.SBQ(CB)	; next slot.
	CALL MMFREE		;Free the memory
	RET

;RLSBID - Release a Slot Block's local id.
;
;Call:	SB/ Address of the SB
;	CALL RLSBID
;	Normal Return
;

RLSBID:	OPSTR <SKIPN T1,>,SBLID,(SB);Get the SB local index
	RET			;Already zero. Nothing to do.
	MOVE T2,SBVECT		;Clear the pointer to SB
	ADD T2,T1		; in the vector of SB addresses
	SETZM (T2)		; ...
	SOS T1			;Normalize to zero
	IDIVI T1,^D36		;Now clear the bit to indicate that the
	MOVE T3,BITS(T2)	; slot block index is now free
	IORM T3,NFRSBQ(T1)	; ...
	SETZ T1,		;Clear the local
	STOR T1,SBLID,(SB)	; SB ID.
	RET

;GETTDB - get terminal dynamic data base
;
;Call:	SB/ slot block address
;	CALL GETTDB
;	 Error return
;	Normal return
;	


GETTDB:
	SAVEAC <W1,W2>
	MOVE W1,TT1LIN+TT.LAH
GTTDB0:	LOAD T1,TTSTY,(W1)	;Get the line type
	CAIE T1,TT.LAH
	 RET
	MOVE T2,W1
	CALL STADYN
	 JUMPE T2,GTTDB2
GTTDB1:	AOJA W1,GTTDB0		;Try next line
GTTDB2:	MOVE T2,W1
	CALL TTYASC
	 JRST GTTDB1
	CALL STADYN
	 JRST GTTDB1
	STOR T2,SBTDB,(SB)
	MOVEM SB,TTDEV(T2)
	SETONE TCJOB,(T2)
	SETONE TTPRM,(T2)
	MOVEI T1,.CHCNC
	MOVE T2,W1
	CALL TTCHI
	  NOP
	RETSKP

;LHRRH - LAT device dependent "return remote host" code
;
; Given the line #, returns the originating hostname, line and
; network type. Places this info in the users NTINF% .NWRRH 
; argument block.  NTINF has already checked the user arguments 
; for validity.
;
; Call with T1/ address of internal arg block
;
;   ARG+.NWABC/ # of bytes available for host name
;   ARG+.NWFNC/ not used
;   ARG+.NWNNP/ byte pointer to store hostname string
;   ARG+.NWLIN/ address of dynamic data for line
;   ARG+.NWTTF/ flags, and network and terminal types
;   ARG+.NWNNU/ node # word 1
;   ARG+.NWNU1/ node # word 2
;
; Returns + 1 on error with T1/ error code
;	  + 2 on success

LHRRH::	SASUBR <UAB>
	MOVEM T1,UAB		;SAVE OUTPUT POINTER
	MOVX T4,NW%LAT		;SET NETWORK TYPE
	DPB T4,[POINT 9,.NWTTF(T1),17] ;STORE NETWORK TYPE
	MOVE T2,.NWLIN(T1)	;GET DYNAMIC DATA ADR
	NOSKED			;PROTECT SB/CB FROM CHANGING BENEATH US
	MOVE T2,TTDEV(T2)	;GET SB ADDRESS
	JUMPE T2,LHRRH3		
	LOAD T3,SBCBA,(T2)	;GET CB ADDRESS
	JUMPE T3,LHRRH3		
	LOAD T4,CBDNI,(T3)	;GET NI ADDRESS (WORD 1)
	MOVEM T4,.NWNNU(T1)
	LOAD T4,CBDNI,+1(T3)	;GET NI ADDRESS (WORD 2)
	MOVEM T4,.NWNNU+1(T1)
	MOVE T2,T3		;SAVE CB ADDRESS IN T2
	LOAD T3,CBRSC,(T3)	;GET COUNT OF CHARACTERS 
	JUMPE T3,LHRRH3		;IF ZERO - NO HOST NAME
	CAML T3,.NWABC(T1)	;MAKE SURE NO MORE THAN MAX
	MOVE T3,.NWABC(T1)	;ENFORCE IT
	MOVE T1,.NWNNP(T1)	;GET POINTER TO OUTPUT STRING
	OPSTR <XMOVEI T2,>,CBSNM,(T2) ;GET ADR OF REMOTE NAME STRING
	TXO T2,<OWGP. 7,>	;FORM BYTE POINTER TO BUFFER
	EXCH T1,T2		;PUT POINTER WHERE MMVAZS WANTS THEM
	CALL MMVAZS		;COPY THE STRING
LHRRH2:	SETZ T1,
	IDPB T1,T2		;DEPOSIT A NULL
	MOVE T1,UAB		;GET POINTER TO USER ARG BLOCK
	MOVX T2,NW%NNN		;GET "NO NODE NAME KNOWN" FLAG
	SKIPN T3 		;GOT A NODE NAME ?
	IORM T2,.NWTTF(T1)	;NO - SET THE "NO NODE NAME KNOWN" FLAG
	OKSKED			;DONE WITH SB/CB
	RETSKP

LHRRH3:	MOVE T2,.NWNNP(T1)	;GET POINTER TO OUTPUT STRING
	SETZ T3,		;NO NODE NAME
	CALLRET LHRRH2		;UPDATE HOSTNAME STRING, AND RETURN
	SUBTTL	Slot Multiplexor - Main Loop

;LAMUX Slot Multiplexor Main Loop
;
;Call:	CALL LAMUX
;	Normal return
;

LAMUX:	LOAD CB,QLFWD,+HN.QAC(HN) ;Get the first CB on the active queue
LANXCB:	JUMPE CB,RTN		;All CBs processed, return
	MOVX T1,HNRUN		;Is the NI still up?
	TDNE T1,HN.RUN(HN)	; ...
	JRST [	CALL NIHALT	;No, so deallocate all resources
		JRST MXNXCB]	;
	SETZM NSLOTS		;No slots in message yet
	LOAD T1,CBSTA,(CB)	;Consider only those circuits which are
	CAIE T1,CS.RUN		; in the running state
	JRST MXNXCB		; ...
	OPSTR <SKIPN>,CBSBQ,(CB);If there are no slots for this circuit or
	JRST [	CALL IDLHLT	; terminate the circuit.
		JRST MXNXCB]
	LOAD T1,HNLAS,(HN)	;Get the current access state.
	CAIN T1,LS.OFF		; the access state is now off,
	  JRST MXNXC2		;kill this circuit and look at next.
	SETZRO CBRRF,(CB)	;No RRF set yet
	CALL TMRCHK		;Check for circuit timer related functions.
	 JRST MXNXCB		;Continue with next circuit.
	TMNN CBMRN,(CB)		;Must we go balanced (reply to server) now?
	JRST LMNMRN		;No

; We have received a message from the server and must respond message now.  If
; there is a free transmit buffer and there is slot data waiting to be sent,
; then build the slots in a large MSD transmit buffer and send it.  If the MSD
; buffer is not available or there is no slot data waiting, just send a RUN
; message header.  If there are no free transmit buffers, retransmit the queue
; of unacknowledged messages.

	MOVX T1,CBMRS!CBMRN!CBRRF;Clear all flags
	ANDCAM T1,CB.FLG(CB)	; since we will reply now.
	SETZRO CBCSB,(CB)	;Clear count since balanced
	SKIPN XB,CB.XBQ(CB)	;Is there a transmit buffer header free?
	JRST LMNOXB		;No, go transmit unACK'd Q
	OPSTR <SKIPE >,CBSDC,(CB);There is a buffer, is there data waiting?	
	CALL BLDMSL		;Everything there so build slots in buffer
	  NOP			;No slots, but don't care
	CALL MTTRUN		;Transmit either hdr only or full message
	JRST MXNXCB		;Go to next circuit block
LMNOXB:	CALL XUNAKQ		;Transmit all messages on the unack'd Q
	JRST MXNXCB

; Check to see if we just received a message from the server.  If so,
; flag to force a reply at the next sheduler cycle.  We wait one cycle in
; case user programs are controlling echoing in the hopes that response
; will be smoother.

LMNMRN:	TMNN CBMRS,(CB)		;Must reply soon?
	JRST LMUNSL
	SETONE CBMRN,(CB)	;Yes, set to force reply on next cycle	
	JRST MXNXCB

; We are in balanced mode, i.e. the response to the latest server message
; has already been sent.  Check here to see if we ought to send an unsolicited
; message.  We are allowed one.  We wait at least MXBALC scheduler cycles since
; last going balanced.  If after that period there is slot data waiting, we
; send an unsolicited message to the server.
; If we have no data to send, wait another MXBALC cycles before trying again.

LMUNSL:	LOAD T1,CBCSB,(CB)	;Number of cycles to wait before sending
	CAIL T1,MXBALC		;Have we waited long enough?
	IFSKP.			;No, so
	  AOJ T1,		; increment the count
	  STOR T1,CBCSB,(CB)	; and wait
	  JRST MXNXCB		; some more.
	ENDIF.

; We have waited long enough. If there is slot data waiting and resouces
; available, send an unsolicited message to the server

	SKIPN XB,CB.XBQ(CB)	;Is there a transmit buffer header free?
	JRST MXNXCB		;No, so don't send anything
	SETZRO CBCSB,(CB)	;We might send data; reset the ticker.
	OPSTR <SKIPE >,CBSDC,(CB);Is there slot data waiting?
	CALL BLDMSL		;Build a message full of slots
	  JRST MXNXC1		;If no slots, do keep-alive timing.
	CALL MTTRUN		;There are slots in the message so send it.
MXNXCB:	LOAD CB,QLFWD,(CB)	;Index of the next CB on active queue
	JRST LANXCB		;Continue looping
	RET

; We don't have any unsolicited data to send, check if we have heard
; from the server within the last few SERVER_KEEP_ALIVE_TIMER seconds.
; If not, we will assume the server died, and we will kill the circuit.

MXNXC1:	MOVE T1,TODCLK			;Get the current time
	LOAD T2,CBKAT,(CB)		;Get the time last msg received
	OPSTR <ADD T2,>,CBKAV,(CB)	;Add the keep-alive time
	CAML T1,T2			;Have we reached the limit yet?
MXNXC2:	CALL LCLHLT			;Yes, consider the server dead and
	JRST MXNXCB			;stop the circuit.
	SUBTTL BLDMSL
;BLDMSL - Build RUN Message Slots
;
;Call:	CB/ Address of Circuit Block
;	CALL BLDMSL
;	Normal return
;
; This routine scans through all slots associated with a LAT virtual circuit
; and builds RUN message slots for those which have data waiting to be sent 
; to the remote node.  SBFLGS contains a bits mask which indicates which
; type of slot must be built for the slot block. More than one bit may be set.

BLDMSL:	OPSTR <SKIPN SB,>,QLFWD,+CB.SBQ(CB);Get addr of first slot for this CB
	RET			;There are none
	SETZRO UNBSZ,+UNB.OF(XB); and message count
	SETZ MD,		;No transmit buffer yet.
BLSLOP:	SKIPL W1,SB.FLG(SB)	;Does this slot have data waiting?
	JRST MXNXSL		;No, loop to next slot
	HLLZS W1		;Clear all but flags
	TLZ W1,400000
BLNXSL:	JFFO W1,BLSDSP		;Find first flags bit which is lit.
MXNXSL:	OPSTR<SKIPE SB,>,QLFWD,(SB);Get next slot's index and
	JRST BLSLOP		; continue with the next slot
BLSLCP:	SKIPN NSLOTS		;Check to see if any slots entered at all
	RET			;None.

;If we couldn't get through all slots, rotate this circuit's slot queue so
;that we resume next time with the slot we did not complete this time.

	SKIPN SB		;Did we get through the whole queue?
	IFSKP.			;NO
	  OPSTR <CAMN SB,>,QLFWD,+CB.SBQ(CB);Is slot already at the Q head?
	  IFSKP.		;NO
	    LOAD T3,QLFWD,+CB.SBQ(CB);Get old queue head
	    LOAD T4,QLBWD,+CB.SBQ(CB);Get old queue tail
	    STOR T3,QLFWD,(T4)	;Joint them together
	    STOR T4,QLBWD,(T3)	; ...
	    LOAD T3,QLBWD,(SB)	;Get new Q tail from new Q head
	    SETZRO QLFWD,(T3)	;Clear new tail's forward ptr
	    SETZRO QLBWD,(SB)	;Clear new head's backward ptr
	    STOR SB,QLFWD,+CB.SBQ(CB);New head
	    STOR T3,QLBWD,+CB.SBQ(CB);New tail
	  ENDIF.
	ENDIF.

;Compute final byte count padding to minimum NI datagram size if necessary.

	LOAD T1,UNBSZ,+UNB.OF(XB);Store the larger of the final real message
	CAIG T1,MINXBF-MMHDSI	; count and the minimum count as the message
	MOVEI T1,MINXBF-MMHDSI	; count.
	STOR T1,MDBYT,(MD)	;Store in MSD
	RETSKP

; Check first to see if adding a new slot causes the number of slots in a
; message to exceed the maximum permitted by remote.

BLSDSP:	LOAD T3,CBMSL,(CB)	;Get the maximum permitted by remote
	CAMG T3,NSLOTS		; and compare with number of slots so far.
	JRST BLSLCP		;At maximum, stop processing this circuit.

; Check to see if there is enough room remaining in the transmit buffer to
; include this slot.

	HLRZ T1,SLLNTB-1(W2)	;Minimum bytes required for this slot type.
	LOAD T3,UNBSZ,+UNB.OF(XB);Current number of bytes in the buffer.
	ADD T1,T3		;Count if we include this new slot.
	CAILE T1,SZ.XBF		;Compare with total transmit buffer capacity.
	JRST BLSLCP		;Will not fit so stop processing this circuit.

; Call the routine to build the slot.  It may or may not be entered into the
; message. At this point however it is always marked as having been processed.

	MOVE T1,BITS(W2)	;Get the mask for the bit
	ANDCM W1,T1		;Indicate that this slot has been processed.
	CALL @SLBDSP-1(W2)	;Build the slot.
	  JRST BLNXSL		;Slot not entered. Try next.

; Slot successfully entered into the transmit buffer. Update the buffer counts.
; Terminal character count has already been added.

	HRRZ T1,SLLNTB-1(W2)	;Get the count for the slot header.	
	OPSTR <ADD T1,>,UNBSZ,+UNB.OF(XB);Get the message count so far
	TRNN T1,1		;Is the count even?
	IFSKP.			;No, must pad to even number
	  OPSTR <IDPB T1,>,UNBFA,+UNB.OF(XB);Put fill byte into message
	  AOS T1		;Increment total in message
	ENDIF.
	STOR T1,UNBSZ,+UNB.OF(XB);
	AOS NSLOTS		;Increment number of slots in the message.
	JRST BLNXSL		;Get next slot request.

	MIN. (MINSFL,<^L<SBREJ>,^L<SBSTR>,^L<SBSTO>,^L<SBOUT>,^L<SBFOU>,^L<SBFCC>>)
	MAX. (MAXSFL,<^L<SBREJ>,^L<SBSTR>,^L<SBSTO>,^L<SBOUT>,^L<SBFOU>,^L<SBFCC>>)


SLBDSP:	BLOCK <MAXSFL-MINSFL+1>
SLLNTB:	BLOCK <MAXSFL-MINSFL+1>
	LSLDT. (SLBDSP,SLLNTB,^L<SBREJ>-1,XSLREJ,<SZ.SHD+SZ.REJ>,<SZ.SHD+SZ.REJ>)
	LSLDT. (SLBDSP,SLLNTB,^L<SBSTR>-1,XSLSTA,<SZ.SHD+SZ.SST>,<SZ.SHD+SZ.SST>)
	LSLDT. (SLBDSP,SLLNTB,^L<SBSTO>-1,XSLSTP,<SZ.SHD+SZ.SSP>,<SZ.SHD+SZ.SSP>)
	LSLDT. (SLBDSP,SLLNTB,^L<SBOUT>-1,XSLDTA,<SZ.SHD>,<SZ.SHD>)
	LSLDT. (SLBDSP,SLLNTB,^L<SBFOU>-1,XSLATT,<SZ.SHD+SZ.ATT>,<SZ.SHD+SZ.ATT>)
	LSLDT. (SLBDSP,SLLNTB,^L<SBFCC>-1,XSLDTB,<SZ.SHD+SZ.SDB>,<SZ.SHD+SZ.SDB>)
	SUBTTL	Slot Formatting Routines -- XSLSTA

;XSLSTA - Format a START Slot in the Transmit Buffer
;
;Call:	SB/ Slot Block Address
;	CB/ Circuit Block Address
;	XB/ Transmit Buffer Address
;	CALL XSLSTA
;	Normal Return
;

XSLSTA:	CALL XBFCHK		;Do we have a transmit buffer yet?
	  RET			;No, and can't get any.
	MOVEI T1,SS.RUN		;Put the slot in the
	STOR T1,SBSTA,(SB)	; RUNNING state.
	MOVEI T1,<<ST.STA_^D12>!<MAXCRE_^D8>!SZ.SST>;COUNT/TYPE/CREDITS fields
	CALL BSLTHD		;Build the slot header	
	MOVEI T1,SCITTY		;Only available service class currently
	OPSTR <IDPB T1,>,UNBFA,+UNB.OF(XB)
	MOVEI T1,MXSLSI		;Maximum slot size for all slot types
	OPSTR <IDPB T1,>,UNBFA,+UNB.OF(XB);As maximum attention slot size
	OPSTR <IDPB T1,>,UNBFA,+UNB.OF(XB);As maximum data slot size
	MOVEI T1,0
	CALL LAP2B0		;Zero slot names
	OPSTR <IDPB T1,>,UNBFA,+UNB.OF(XB);No parameters either
	MOVX T1,SBSTR		;Indicate that this slot request successfully
	CALL CSBDAV		; and entirely completed.
	RETSKP

;XSLREJ - Format a REJECT Slot in the Transmit Buffer
;
;Call:	SB/ Slot Block Address
;	CB/ Circuit Block Address
;	XB/ Transmit Buffer Address
;	CALL XSLREJ
;	Normal Return
;

XSLREJ:	CALL XBFCHK		;Do we have a transmit buffer yet?
	  RET			;No, and can't get any.
	LOAD T1,SBREA,(SB)	;Get the reject reason code
	LSH T1,^D8
	MOVEI T1,<ST.REJ_^D12!SZ.REJ>(T1) ;COUNT/TYPE/REASON fields
	CALL BSLTHD		;Go build the slot header
	MOVX T1,SBREJ		;Indicate that this slot request successfully
	CALL CSBDAV		; and entirely completed.
	CALL RELSB		;Release the slot.
	RETSKP			;That's all there is to a REJECT slot.
	SUBTTL	Slot Formatting Routines -- XSLSTP

;XSLSTP - Format a STOP Slot in the Transmit Buffer
;
;Call:	SB/ Slot Block Address
;	CB/ Circuit Block Address
;	XB/ Transmit Buffer Address
;	CALL XSLSTP
;	Normal Return
;

XSLSTP:	CALL XBFCHK		;Do we have a transmit buffer yet?
	  RET			;No, and can't get any.
	CALL RLSBID		;Release the local slot block ID.
	LOAD T1,SBREA,(SB)	;Get the stop reason code
	LSH T1,^D8
	MOVEI T1,<ST.STP_^D12!SZ.SSP>(T1) ;COUNT/TYPE/REASON fields
	CALL BSLTHD		;Go build the slot header
	MOVX T1,SBSTO		;Indicate that this slot request successfully
	CALL CSBDAV		; and entirely completed.
	CALL RELSB		;Now go release the slot
	RETSKP

;XSLATT - Format a ATTENTION Slot in the Transmit Buffer
;
;Call:	SB/ Slot Block Address
;	CB/ Circuit Block Address
;	XB/ Transmit Buffer Address
;	CALL XSLATT
;	Normal Return
;

XSLATT:	CALL XBFCHK		;Do we have a transmit buffer yet?
	  RET			;No, and can't get any.
	MOVEI T1,<ST.ATT_^D12!SZ.ATT>;COUNT/TYPE fields
	CALL BSLTHD		;Go build the slot header
	MOVEI T1,1B30
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB);Deposit control flags
	MOVX T1,SBFOU		;Indicate that this slot request successfully
	CALL CSBDAV		; and entirely completed.
	RETSKP
	SUBTTL	Slot Formatting Routines -- XSLDTA

;XSLDTA - Format a DATA_A Slot in the Transmit Buffer
;
;Call:	W1/ Slot Block Flags
;	SB/ Slot Block Address
;	CB/ Circuit Block Address
;	XB/ Transmit Buffer Address
;	CALL XSLDTA
;	Normal Return

; If we have transmit credit available, compute the maximum number of terminal
; output characters which may be entered into the slot.  If we no longer have
; credit, set the character count to zero.

XSLDTA:	SAVEAC <W1,W2>
	SETZ W1,		;Assume character count is zero.
	OPSTR <SKIPN>,SBXCR,(SB);Do we still have any transmit credit?
	IFSKP.
 	  LOAD T2,SBTDB,(SB)	;Get address of TDB
	  TMNE <TTSFG,TTRXF>,(T2);Check if at a page stop.  If so, character
	  IFSKP.		; stays 0 (cannot call TTSND).
	    MOVEI W1,SZ.XBF-SZ.SHD;Get the residual buffer (minus slot header)
	    OPSTR <SUB W1,>,UNBSZ,+UNB.OF(XB); count.
	    CALL GTCHCT		;(T2/T1)Get total character count
	    CAMLE W1,T1		;Smaller of residual count - 4 and TTOCT+TSALC 
	    MOVE W1,T1		;TTOCT+TSALC was smaller
	    OPSTR <CAMLE W1,>,SBMDS,(SB) ;Compare with maximum DATA_A slot size
	    LOAD W1,SBMDS,(SB)	;Maximum allowed by remote is less.
	  ENDIF.
	ENDIF.

; Determine if the remote needs more credits and if we can give him any.  If
; he doesn't or we can't and there is no terminal output data to be sent(count
; from above is 0) , exit without building the slot since there is no need to
; send one.  Otherwise at least a DATA_A slot header will be sent.  If it is 
; extending credit, there may or may not be output data.

	CALL CRECHK		;Check if we can grant more credit remote
	 SKIPE W1		;Can't or don't need to so if there is no data
	SKIPA			; either, no need to send a slot. Otherwise,
	JRST XSLDA3		; send at least empty slot granting new credit.
	MOVE W2,T1		;Save CRECHK result
	CALL XBFCHK		;Do we have a transmit buffer yet?
	  JRST XSLDA3		;No, and can't get any.
	MOVE T1,W2		;Retrieve CHECHK results
	LSH T1,^D8		;Shift to proper position
	IORI T1,<ST.DTA_^D12>(W1);Fill in the rest of last 2 slot header bytes.
	CALL BSLTHD		;Go build the slot header
	JUMPE W1,XSLDA2		;No slot data and no credits used.
	MOVNS W1		;Set up to be used in an AOBJN
	HRLZS W1		;... [-count,,0]
XSLDA0:	LOAD T2,SBTDB,(SB)	;Dynamic TT data base
	CALL TTSND		;Get a character from the buffer
	 JRST [	HRRZS W1
		MOVEI T1,1(W1)
		MOVNS T1
		OPSTR <ADJBP T1,>,UNBFA,+UNB.OF(XB)
		DPB W1,T1		
		JRST XSLDA1]
	OPSTRM <IDPB T1,>,UNBFA,+UNB.OF(XB);Enter character into buffer
	AOBJN W1,XSLDA0		;Continue if more characters
XSLDA1:	LOAD T1,SBXCR,(SB)	;Get our transmit credit count
	SOS T1			;Decrement since we may consume one credit now.
	STOR T1,SBXCR,(SB)	; send data so store decremented credit count.

; Determine whether to slot request was FULLY completed.  If not, don't
; clear the request bit since it will have to be completed next time around.

XSLDA2:	OPSTRM <ADDM W1,>,UNBSZ,+UNB.OF(XB);Update the total message count.
	OPSTRM <ADDM W2,>,SBRCR,(SB);Update his credit count.
	LOAD T2,SBTDB,(SB)	;Get the TDB address back
	CALL GTCHCT		;(T2/T1)Get remaining character count. If more,
	JUMPN T1,RSKP		; return and do not clear request bit.
	SETZRO TTOTP,(T2)	;Clear TTY output active if not more data.
	OPSTR <SKIPN>,SBRCR,(SB);If remote has zero credit for transmits 
	RETSKP			; return successfully without clearint request.
	MOVX T1,SBOUT		; Otherwise mark this request as complete.
	CALL CSBDAV
	RETSKP

XSLDA3:	LOAD T2,SBTDB,(SB)	;Get the TDB address
	CALL GTCHCT		;(T2/T1)Get remaining character count.
	JUMPN T1,RTN
	SETZRO TTOTP,(T2)	;There is no more terminal output, clear
	RET			; the TTOTP bit and return.
	SUBTTL	Slot Formatting Routines -- XSLDTB

;XSLDTB - Format a DATA_B Slot in the Transmit Buffer
;
;Call:	SB/ Slot Block Address
;	CB/ Circuit Block Address
;	XB/ Transmit Buffer Address
;	CALL XSLDTB
;	Normal Return
;

XSLDTB:	OPSTR <SKIPE>,SBXCR,(SB);Do we still have any transmit credit?
	CALL XBFCHK		;Check if we have a transmit buffer yet.
	 RET			; Didn't and can't get one.
	LOAD T1,SBXCR,(SB)	;Current number of transmit credits available
	SOS T1			;Decrement since this slot will consume one.
	STOR T1,SBXCR,(SB)	;Store back updated count.
	CALL CRECHK		;Does remote need credits?
	 NOP			;Dont't care if he did and we can't provide
	LSH T1,^D8		;Shift to proper position.
	IORI T1,<ST.DTB_^D12>!SZ.SDB;Fill in rest of last 2 header bytes.
	CALL BSLTHD		;Go build the slot header
	LOAD T2,SBTDB,(SB)	;Get the terminal data base address
	MOVEI T1,SL.DXF		 ;Assume disabling XOFF recognition	
	TMNE TT%PGM,TTFLGS(T2)	
	MOVEI T1,SL.EXF		 ;No, enabling	
	OPSTR <IDPB T1,>,UNBFA,+UNB.OF(XB);Enter control flags to buffer	
	MOVEI T1,<XONC_^D8!XOFFC> ;XON and XOFF characters
	CALL LAP2B0		;Enter output XON/OFF chars into buffer
	CALL LAP2B0		;Also input XON/XOFF characters
	MOVX T1,SBFCC
	CALL CSBDAV
	RETSKP

BSLTHD:	MOVE T3,T1		;Save input arg
	LOAD T1,SBLID,(SB)	;Remote slot ID
	LSH T1,^D8		;Shift to second byte
	OPSTR <IOR T1,>,SBRID,(SB) ;Local slot ID into first byte
	CALL LAP2B0		;(Clobbers T2 only.)
	MOVE T1,T3		;Get original argument back
	CALL LAP2B0		;Enter count/slot type/credits
	RET

;CRECHK - Remote Credit Check Routine
;
;Call:	SB/ Slot Block Address
;	CALL CRECHK
;	 No-Grant return
;	Grant return
;	T1/ Number to grant

; If the remote has less than the maximum number of transmit credits, try to
; compute the number of additional credits to grant him and return in T1, 
; shifted by one byte (for convenience of caller).  We can only grant credit
; if there is room for a non-zero integral number of full slot buffers (of
; length MXSLSI).

CRECHK:	MOVEI T1,MAXCRE		;Maximum number of credits remote may have.
	OPSTR <SUB T1,>,SBRCR,(SB);His deficiency.
	JUMPE T1,RTN		;Can't grant him more-- he already has limit.
	MOVE T3,T1		;Save remote's credit deficiency.
	LOAD T4,SBTDB,(SB)	;Get the terminal dynamic pointer
	LOAD T1,TIMAX,(T4)	;Maximum that will fit in an input buffer
	SUB T1,TTICT(T4)	;Subtract current input count to get whats left
	IDIVI T1,MXSLSI		;Compute number of slot buffer that would fit
	JUMPE T1,[SETONE TTFWK,(T4);None, so can't grant more so force
		  JRST RTN]	; the process to wake.
	CAMLE T1,T3		;Make sure he never gets a total of more than
	MOVE T1,T3		; the maximum allowed.
	RETSKP

IGNSLT:	SETZM SLTCNT		;Force data to be ignored.
	RETSKP			;This is a good return.

ILLSLT:	OPSTRM <AOS>,HCISR,(HN)	;Increment host count
	OPSTRM <AOS>,CCISR,(CB)	; and the per-server count
	RET			;Give a bad return
	SUBTTL Queue Handling Routines

;LAQUE - Add an element to the front of a queue
;
;Call:	T1/ Address of Queue Header
;	T2/ Address of Element to Add
;	CALL LAQUE
;	Normal Return
; This routine is used to queue either CBs or SBs.  The queue headers
; and link words are always two words, and are described by the BEGSTR QL.
; All elements are linked in both forward and backward directions.

LAQUE:	MOVE T4,T1		;Save queue header address
	OPSTR <SKIPE T3,>,QLFWD,(T1)	;Get old first queue element if any.
	MOVE T1,T3		; Old first element or Q header backward
	STOR T2,QLBWD,(T1)	; pointer points to new element
	STOR T2,QLFWD,(T4)	;Store new first queue element
	SETZRO QLBWD,(T2)	;Zero its backward pointer
	STOR T3,QLFWD,(T2)	;Set up its forward pointer
	RET

;LAUNQ - Dequeue a queue element
;
;Call:	T1/ Address of queue header
;	T2/ Address of queue element to be unqueued
;	CALL LAUNQ
;	Normal return
;
;Uses:	T3,T4

LAUNQ:	LOAD T3,QLBWD,(T2)	;Get elements backward pointer
	LOAD T4,QLFWD,(T2)	;Get its forward ptr also.
	SKIPN T2,T4		;Remove the element from the backward list
	XMOVEI T2,QL.FWD(T1)	; by updating the backward
	STOR T3,QLBWD,(T2)	; ptr of either next Q element or Q head
	SKIPE T3		;If backward ptr 0 update Q head forward ptr
	XMOVEI T1,QL.FWD(T3)	; otherwise  update that of the previous Q
	STOR T4,QLFWD,(T1)	; element
	RET
	

;QUE1WB - Routine to queue and element to the back of a 1 directional queue.
;
;Call:	T1/ address of Q header block
;	T2/ address of queue element to add to queue
;	CALL QUE1WB
;	Normal return

QUE1WB:	OPSTR <SKIPN T3,>,QLBWD,(T1);Address of first element on Q
	MOVE T3,T1		;Zero, Q header fwd ptr points to new element
	STOR T2,QLFWD,(T3)	;Update forward ptr of Q hdr or old Q tail
	SETZRO QLFWD,(T2)	;Zero new elements forward ptr
	STOR T2,QLBWD,(T1)	;New tail ptr
	RET

;UNQ1WF - Routine to remove an element from the front of a 1 directional queue.
;
;Call:	T1/ address of the Q header block
;	CALL UNQ1WF
;	 Error return - queue emtpy	
;	Normal return
;	T2/ address of element

UNQ1WF:	OPSTR <SKIPN T2,>,QLFWD,(T1);Address of first element on Q
	RET			;Zero, so queue is empty
	OPSTR <SKIPN T3,>,QLFWD,(T2);If only 1 element on Q then update
	STOR T3,QLBWD,(T1)	; the Q headers tail ptr (set to 0)
	STOR T3,QLFWD,(T1)	;There is a new head of the Q (or 0)
	RETSKP			;Return with the Q element in T2
	SUBTTL Buffer Handling Routines -- Transmit Buffers

;GETXBH - Get the necessary number of transmit buffer headers
;
;Call:	CB/ Address of the circuit block
;	CALL GETXBH
;	  Error return - insufficient resources
;	Normal return

GETXBH:	MOVSI W1,-MAXXBF	;Number of buffers to get
GTXNXT:	MOVEI T1,<<MINXBF+3>/4+XBF.OF>;Buffer length to get
	CALL MMGTZW		;Go get the storage
	 JRST GTXFL		;Failed to get all that are needed
	MOVX T2,<POINT 8,0>
	STOR T2, MDAUX,+MDB.OF(T1);
	MOVEI T2,SZ.MHD		;The message header always has 
	STOR T2,MDBYT,+MDB.OF(T1); 8 bytes.
	MOVEI T2,VMC.XC		;Always use exec virtual paging
	STOR T2,MDVMC,+MDB.OF(T1)		; ...
	XMOVEI T2,<XBF.OF>(T1)	;
	STOR T2,MDALA,+MDB.OF(T1);Address of start of header
	MOVE T2,T1		;Now queue to circuit blocks queue of free
	XMOVEI T1,CB.XBQ(CB)	; transmit buffers
	CALL QUE1WB		; ...
	AOBJN W1,GTXNXT		;Get as many as required
	RETSKP

; Failed to get the nececessary number of buffers. Must free those
; which we did get.

GTXFL:	XMOVEI T1,CB.XBQ(CB)	;Unqueu each from the transmit free Q
	CALL UNQ1WF		; ...
	 RET			;No more there so return with error
	MOVE T1,T2		;Give back the buffer to the memory
	CALL MMFREE		; manager
	JRST GTXFL		; and continue looping til all returned

;GETXBF - Get one long transmit buffer for real data
;
;Call:	CB/ Address of the circuit block
;	XB/ Address of the 1st MSD (header)	
;	CALL GETXBH
;	  Error return - insufficient resources
;	Normal return
;	MD/ MSD address

GETXBF:	MOVEI T1,<MD.LEN+SZ.XBF>;Buffer length to get
	CALL MMGTZW		;Go get the storage
	  RET			;No luck!
	MOVE MD,T1
	SETZ T1,
	MOVX T2,<POINT 8,0>
	STOR T2,MDAUX,(MD);Byte pointer to message
	MOVEI T1,SZ.XBF
	STOR T1,MDBYT,(MD);Zero length
	MOVEI T1,VMC.XC		;Always use exec virtual paging
	STOR T1,MDVMC,(MD); ...
	XMOVEI T1,<MD.LEN>(MD)	;
	STOR MD,MDNXT,+MDB.OF(XB);Link header MSD to this
	STOR T1,MDALA,(MD);Message address
	RETSKP

;XBFCHK - Check for Transmit buffer
;
;Call:	XB/
;	CALL XBFCHK
;	 Error Return
;	Normal Return

XBFCHK:	JUMPN MD,RSKP		;If we already have one, return.
	CALL GETXBF		;Get an MSD transmit buffer
  	  RET			;None available, return failure
	MOVX T1,<POINT 8,MD.LEN(MD)>; and
	STOR T1,UNBFA,+UNB.OF(XB); message byte pointer.
	RETSKP

;RFXBFS - Release all free transmit buffers
;
;Call:	CB/ circuit block address
;	CALL RFXBFS
;	Normal return
;

RFXBFS:	XMOVEI T1,CB.XBQ(CB)	;Address of the free xmit buffer queue
	CALL UNQ1WF		;Unqueue the head of the Q
	 RET
	MOVE XB,T2		;Put buffer address where RELXBH wants it
	CALL RELXBH		;Go release it
	JRST RFXBFS		;Get next

;RELXBF - Release Transmit Buffer
;
;Call:	XB/ Address of buffer header
;	CALL RELXBF
;	Normal return

RELXBF:	OPSTR <SKIPN T1,>,MDNXT,+MDB.OF(XB);If there is no MSD style buffer
	RET			; then just return
	SETZRO MDNXT,+MDB.OF(XB); otherwise clear the link to it in header MSD
	CALL MMFREE		; and free the MSD's storage.
	RET

;RELXBH - Release Transmit Buffer Header and Buffer
;
;Call:	XB/ Address of buffer header
;	CALL RELXBH
;	Normal return

RELXBH:	CALL RELXBF		;Release the buffer first
	MOVE T1,XB		;Now the header
	CALL MMFREE
	RET
	SUBTTL Buffer Handling Routines -- Receive Buffers

;GETRBF - Get a Receive buffer
;
;Call:	CALL GETRBF
;	 Error return - no receive buffers
;	Normal Return
;
; This routine checks to see if enough receive buffers have been posted
; to the NI DLL to support the number of currently active circuits plus
; a new one.  If  buffers are needed and can be obtained, they are posted
; directly to the NI DLL.  If some are needed, and cannot be obtained,
; the error return is taken.

GETRBF:	STKVAR <BUFADR>
	LOAD T1,HNNRB,(HN)	;Do check to see if we need more buffers.
	LOAD T2,HNNAC,(HN)
	CAIL T1,1(T2)
	  RETSKP		;No additional are needed. Return success.
	MOVEI T1,<<LMRFSI+3>/4+UN.LEN+UE.LEN>;Receive buffer word size
	CALL MMGTZW		;Get the number of required buffers
	  RET			;Can't, return error.
	MOVEM T1,BUFADR		;Save address in case we can't post buffer
	CALL RBPOST		;Post the receive buffer to NISRV
	IFSKP.
	  OPSTRM <AOS>,HNNRB,(HN);Increment the number of receive buffers
	  RETSKP		;Return success
	ENDIF.
	MOVE T1,BUFADR		;Get address of buffer
	CALLRET MMFREE		;Couldn't post it so free it and
				; return with error.

;RELRBF - Release a Receive buffer
;
;Call:	RB/ Address of the buffer to release
;	CALL RELRBF
;	Normal Return
;
; This routine frees a receive buffer if there is already a sufficient number
; posted to the NI DLL otherwise the buffer is re-posted to the NI DLL.

RELRBF:	LOAD T1,HNNRB,(HN)	;Do we still need this buffer?
	LOAD T2,HNNAC,(HN)
	CAILE T1,1(T2)
	 JRST RELRB0		;No, so free it
	MOVE T1,RB		;Yes, so
	CALL RBPOST		; re-post it to NISRV
	 JRST RELRB0		;NISRV could not accept it?! Release it.
	RET			;Successfully re-posted. Return.
RELRB0:	MOVE T1,RB		;Here to free the buffer.
	CALL MMFREE
	OPSTRM <SOS>,HNNRB,(HN)	;Reduce the number of receive buffers
	RET

	ENDAV.
	SUBTTL	TTYSRV DEVICE DEPENDENT ROUTINES

LTSTRO::MOVX T1,SBOUT
	SETONE TTOTP,(T2)	;Set output active
	JRST LTSSDC
LTCOBF::MOVX T1,SBFOU
	JRST LTSSDC
LTEXF::	MOVX T1,SBFCC
LTSSDC:	ACVAR <HN,XB,RB,CB,SB,W1,W2>
	NOSKD1
	CHNOFF DLSCHN
	SKIPN SB,TTDEV(T2)	;Get the slot data base from TDB if not 0
	JRST LTSSD0
	MOVE CB,SB.CBA(SB)	;Get the circuit data base from slot block.
	SAVEAC <T2>		;TTYSRV needs T2 returned intact
	CALL SSBDAV
LTSSD0:	CHNON DLSCHN
	OKSKD1
	RET

; TTYSRV Device Dependent Routine to see if SENDAL should be done on this
; line.  
; Call:	T2/ Line Number
;	Non-skip Return/ Skip SENDALL to this Line
;	Skip Return    / Do SENDALL to this Line

LTSALL::SKIPG TTACTL(T2)	;Is line fully active?
	RET			;No, don't do SENDALL
	RETSKP			;Yes, do the SENDALL

; TTYSRV device dependent routine to perform hang-up action
;
;Call:	T2/ Line Number

LTHNGU::SAVEAC <T2>		;Caller wants this back intact
	CALL STADYN		;Get the dynamic data base
	 RET			;There is none
	MOVX T1,SBSTO		;Mark to send STOP slot if necessary
	CALL LTSSDC		;(CALLRET won't work here)
	RET

; TTYSRV device dependent routine to see if output is possible on a line
;
; Call:	T2/ TDB Address
;	Non-skip Return/ Output not possible
;	Skip Return    / Output possible

LTTCOU::RETSKP

; TTYSRV device dependent routine to set LAT Host terminal lines as 
; high speed lines.
;
;RETSKP always with:
;	T3/ ^D9600 to simulate high speed output
;	T4/ IBFRC1 the high speed terminal characteristics word

LTSOF::	MOVEI T3,^D9600		;Fake 9600 baud
	MOVE T4,IBFRC1		;High speed buffer word
	RETSKP			;Always
				
	SUBTTL	General Untility Routines

;
;RANDOM - get a "random" number
;
;Call:	T1/ Maximum number allowed
;	CALL RANDOM
;	Normal Return
;	T1/ Random Number

RANDOM:	MOVE T4,T1		;Save for modulo
	CALL LGTAD		;Get internal time and date
	ANDI T1,777777		;Time only
	IDIV T1,T4
	SKIPN T1,T2		;Zero not allowed
	AOS T1
	RET

	ENDAV.
	

;GTCHCT - Get character count for terminal output.
;Call:	T2/ TDB address
;Return:
;	T1/ Total character count

GTCHCT:	SETZ T1,0
	TMNN TTSAL,(T2)		;Is there a send all in progress?
	IFSKP.			;Yes, must include count of the
	  LOAD T1,TSALC,(T2)	; send all buffer.
	ENDIF.
	ADD T1,TTOCT(T2)	;Add the terminal buffer count.
	RET

	LIT

MMGTZW:	XCALLRET (XCDSEC,DNGWDZ)

MMFREE:	XCALLRET (XCDSEC,DNFWDS)

	END