Trailing-Edge
-
PDP-10 Archives
-
bb-bt99l-bb
-
latser.x18
There is 1 other file named latser.x18 in the archive. Click here to see a list.
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) 1985,1986 BY
;DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
;ALL RIGHTS RESERVED.
IFNDEF FTOPS20,<FTOPS20==0>
IFNDEF FTOPS10,<FTOPS10==-1>
IFN FTOPS20,< SEARCH MACSYM,MONSYM,PROLOG,TTYDEF>
IFN FTOPS10,< SEARCH F,S,D36PAR,MACSYM
.CPYRT<1985,1986>
SUBTTL TOPS10 redefinitions of TOPS20 things
OPDEF NOP[TRN]
DEFINE RESCD <$HIGH> ;TOPS-10 EQUIVALENT
DEFINE SWAPCD <$HIGH>
DEFINE RESDT <$LOW>
DEFINE NOSKD1 <> ;DON,T HAVE TO WORRY ABOUT THESE
DEFINE OKSKD1 <> ; IN TOPS10
DEFINE MCENT <> ; or this one either
EXTERN BITTBL
DEFINE BITS,<BITTBL>
; XCTU is used for "normal" instructions which move data from User to eXec
; and eXec to User
;
DEFINE XCTU <PXCT 4,> ;For all "normal" instructions
DEFINE XCTBU <PXCT 3,> ;To load from and deposit to user
;with byte pointer in eXec
SUBTTL Definitions - KS10 conditionals
IFE FTXMON,<
;
; Redefine some stuff if we running non-extended (KS10 especially)
;
DEFINE OWGP. (SS,ADR,POS),<POINT SS,ADR,POS>
DEFINE XBLT.(AC<T1>),<
IFN <AC>-T1,<PRINTX ? CAN'T XBLT VIA AC, USE T1>
PUSHJ P,XBLTA##
>
>; END IFN FTXMON
SUBTTL Symbol purging macros
;
; Define some macros which will help purge all symbols of
; the form ..nnnn at the end of assembly. This will get rid
; of about 200 useless symbols generated by .IFSKP and friends
define eq(a,b),<a==b>
define highest(sym,%tg),< ;;Macro to find highest generated symbol
....==sixbit/%tg/&000077777777 ;;Isolate the nnnn part of ..nnnn
eq(sym,\'....)
purge ....>
define prgnum(n),< ;;Macro to purge symbol named ..nnnn
....==sixbit/n/ ;;given a number n
ifl <n-1000>,<....==<..../100>!sixbit/0/> ;;make leading zero fill
ifl <n-100>,<....==<..../100>!sixbit/0/>
ifl <n-10>,<....==<..../100>!sixbit/0/>
prgsym(..,\'....)
purge ....>
define prgsym(sy1,sy2),<
purge sy1'sy2
>
define prgall,< ;;End product: macro to purge all symbols
if2,< ;;of the form ..nnnn
highest XX ;;Use this just before END statement
nn==XX
repeat XX,<prgnum(\nn)
nn==nn-1>
nn==.npac
repeat .npac,<prgsym(.fpac,\nn) ;;get rid of .fpacN symbols
nn==nn-1>
purge nn,XX,.npac>
>
;
;
; Redefine TRVAR to allocate static storage on TOPS-10
; This is so we don't have to have a frame pointer in TOPS-10
; If we ever run into a TRVAR in code, we will die
;
DEFINE TRVAR(LIST),<
IRP LIST,<
LIST: BLOCK 1
>>
DEFINE ACDEF(N,AC),<.FPAC'N==AC>
DEFINE FREEAC(AC),<
.NPAC==0
IRP AC,<
ACDEF(\.NPAC,AC)
.NPAC==.NPAC+1
>>
FREEAC <W,M,P1,P2,P3,P4,J> ;These are all the ACs available for ACVAR
;Explicitly do NOT use "U" since it is used
;for the normal TOPS-10 function to point to
;an LDB throughout the code
DEFINE ACVAR (LIST)<
..NAC==0 ;;INIT NUMBER OF ACS USED
IRP LIST,<
.ACV1 (LIST,\..NAC) ;;PROCESS ITEMS
..NAC==..NAC+1>
SAVEAC (<LIST>) ;;SAVE ACS USED (QUICK AND DIRTY)
DEFINE ENDAV.<.ENAV1 <LIST>>>
DEFINE .ACV1 (ITEM,N)<
.ACV2 (ITEM,N)> ;;PEEL OFF ANGLEBRACKETS IF ANY
DEFINE .ACV2 (NAM,N)<
IFDEF NAM,<.IF NAM,SYMBOL,<PRINTX ACVAR NAM ALREADY DEFINED>>
NAM==.FPAC'N ;;DEFINE VARIABLE
$'NAM==NAM ;;FOR DDT
>
DEFINE .ENAV1 (ARGS)<
IRP ARGS,<
.ENAV2 (ARGS)>>
DEFINE .ENAV2 (ARG)<
.ENAV3 (ARG)>
DEFINE .ENAV3 (NAM,SIZ)<
PURGE NAM,NAM
>
XOFFC=="S"-100 ;XOFF CHARACTER
XONC=="Q"-100 ;XON CHARACTER
>;end IFN FTOPS10
IFN FTOPS20,< SEARCH NIPAR>
IFN FTOPS10,< SEARCH ETHPRM>
SALL
IFN FTOPS20,< TTITLE (LATSRV,,< -- LAT-20 Terminal Host>)>
IFN FTOPS10,< TITLE LATSER -- LAT-10 Terminal Host
$RELOC
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
XP VLATSER,1 ;PUT VERSION NUMBER IN STORAGE MAP AND GLOB
>;end IFN FTOPS10
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
CS.SPG==10 ;STOPPING
;
;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
; Low order bit always set to enable "HOSTSYNC" flow control ala VAX
; Other bits Enables/Disables "TTSYNC" in VAX terms.
SL.EXF==^B0101 ;Enable XON/XOFF recognition in server
SL.DXF==^B1001 ;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
;
;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
IFN FTOPS20,< LHPRID==6 ;Product code type for TOPS-20 host>
IFN FTOPS10,< LHPRID==7 ;Product code type for TOPS-10 host
>
SCITTY==1 ;Interactive terminal service class
MAXXBF==2 ;Maximum number of transmit buffers/circuit
CBMAXI==255 ;Absolute maximum circuit id
IFN FTOPS20,<MAXCIR==^D20> ;Maximum number of simultaneous circuits
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)
LS.OFF==0 ;LAT Access State OFF
LS.ON==1 ;LAT Access State ON
LS.SHT==2 ;LAT Access State SHUT
MINXBF==^D46 ;Minimum transmit buffer size
SUBTTL MONSYM/UUOSYM Definitions
;LATOP./LATOP% Function Code Definitions
.LASET==0 ;Set
.LACLR==1 ;Clear
.LASCH==2 ;Show characteristics of host
.LASTC==3 ;Show terminal connects
.LASAS==4 ;Show adjacent servers
.LASCO==5 ;Show counters
.LAZCO==6 ;Zero counters
.LAACT==0 ;Argument block count
.LAFCN==1 ;LATOP% function code
.LAPRM==2 ;SET/CLEAR parameter number
.LAVAL==3 ;SET/CLEAR new parameter value
.LABCT==2 ;Buffer count
.LABFA==3 ;Buffer Address
.LAQUA==4 ;Qualifier
.LADSC==5 ;
LA%RAT==1B0
LA%DSC==1B1
.LPMAC==1
.LPMCO==2
.LPNUM==3
.LPLAS==4
.LPRLI==5
.LPTIM==6
.LPMTI==7
.LPCOD==10
.LPNNM==11
.LPNID==12
.LPSRV==13
.LPLOC==14
IFN FTOPS10,<
;
; Allocate some ACs for use in LATOP. UUO
; Use W, since we don't use it for anything else.
; Use M, since it is W+1 for 7.03
; We don't bother saving W, since it is useless.
; We save M at start of LATOP. UUO and restore just before end
; because it contains the actual UUO and AC field to store to
;
Q1==W ;redefine some TOPS-20 ACs
Q2==M ;( = W+1)
DEFINE ITERR (ERRCOD),<
IFB <ERRCOD>,<JRST ERRRTN>
IFNB <ERRCOD>,<JRST [MOVEI T1,ERRCOD
JRST ERRRTN]>>
DEFINE RETBAD (ERRCOD),<
IFB <ERRCOD>,<RET>
IFNB <ERRCOD>,<JRST [MOVEI T1,ERRCOD
RET]>>
DEFINE .ERRT (A,B,C,D) <
B==A
C==A> ;internal and external name
.ERRT (00,LATX01,LABTS%,<Buffer size too small for available data>)
.ERRT (01,LATX02,LAVOR%,<LAT parameter value out of range>)
.ERRT (02,LATX03,LALNO%,<LAT is not operational>)
.ERRT (03,LATX04,LASVR%,<Invalid or unknown LAT server name>)
.ERRT (04,LATX05,LAIPN%,<Invalid LAT parameter>)
.ERRT (05,LATX06,LAIPV%,<Invalid LAT parameter value>)
.ERRT (06,LATX07,LASVC%,<Invalid or unknown LAT service name>)
.ERRT (07,LATX08,LAILR%,<Insufficient LAT Resources>)
.ERRT (10,LATX09,LAHAS%,<LAT Host name already set>)
.ERRT (11,ARGX02,LAIVF%,<Function code out of range>)
.ERRT (12,ARGX04,LAABS%,<Argument list too small>)
.ERRT (13,LATXAC,LAADC%,<Address check for argument list>)
.ERRT (14,LATXPV,LAPRV%,<No privilege for attempted operation>)
>;END IFN FTOPS10
SUBTTL External References
EXT DLLUNI
IFN FTOPS20,<
EXT DNGWDZ
EXT DNFWDS
EXT TTCHI
EXT LATMCT ;CLK2TM table timer for multicast
>
IFN FTOPS10,<
EXTERN LAHNDB
EXTERN GETEWZ,GIVEWS
EXTERN LDBPAG,SAVE4,GETWDU,GETWD1,RTZER,STOTAC
EXTERN RTN,SETCHP,NTTLAH,MAXCIR,CONFIG,STANAM
EXTERN NSBWDS,NFRSBQ,.CPSK0,RECPTY,GETLDB,DETLDB,FRELDB
EXTERN LDRREM,LDBDCH,LDBISR,LDBQUH,LDBLAT
EXTERN LTLLAT,LDBTTW,APCLAT,LDPAPC,TTFGRT,APCUNK
EXTERN XMTCHR,TTIWRN,LDBTIC
EXTERN LDBECC,TOTAKE,L1RCHP,LDBBYT
EXTERN DATE,TPOPJ
EXTERN LDLIDL,M.LIDL,SCNADT
>
; 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.PNM==^D16 ;Max length of port 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
MW.SYS==<ML.SYS+4>/5 ;Max length of system name in words
MW.LOC==<ML.LOC+4>/5 ;Max length of location field in words
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 (must be sign bit)
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 retransmit timer (ms)
HWORD MTI ;Multicast timer initial value (sec)
; END OF PARAMETERS DISPLAYED BY .LASCH
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.
HWORD TIM ;Current value of circuit timer.
HWORD RTC ;Current retransmit count
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
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
WORD KAF ;host keep-alive timer
;<initial-value>,,<current value>
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
RBFSIZ==<<LMRFSI+3>/4+UN.LEN+UE.LEN>;Receive buffer size
XBFSIZ==<MD.LEN+SZ.XBF>;Transmit buffer length
NBUFS==<1+<MAXXBF+1>>
; 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
HWORD OPC ;Object port name count
HWORD SPC ;Subject port name count
WORD SNM,<<ML.SLN+4>/5> ;Source slot name (16 characters max)
WORD OPN,<<ML.PNM+4>/5> ;Object port name
WORD SPN,<<ML.PNM+4>/5> ;Subject port name
WORD TDB ;Terminal data block
WORD CBA ;Circuit block address for this slot
IFN FTOPS10,<
FIELD REM,^D18 ;More flags for TOPS-10
BIT TTO ;"Character in SBCHR" bit, must be sign bit
BIT XNF ;"XON/XOF processing enabled
HWORD CHR ;space to hold saved character
>;end ifn FTOPS10
ENDSTR
SUBTTL Miscellaneous Structure Definitions
BEGSTR QL ;Halfword Queue Link Word
WORD FWD ;Next forward queue element
WORD BWD ;Previous backward queue element
ENDSTR
asuppress
RESDT
IFN FTOPS10,<
OURNAM: BLOCK 2
OURCNT: BLOCK 1
LDPXNF: POINT 1,LDBPAG(U),1;POINTER TO XOFFON BIT
>;end IFN FTOPS10
IFN FTOPS20,<
LAHNDB: BLOCK 1
> ;end IFN FTOPS20
RTXTIM: BLOCK 1 ;Retransmit timer in ticks
CBVECT: BLOCK 1
SBVECT: BLOCK 1
LASDEF: EXP LS.ON ;LAT Access Default State
NSLOTS: BLOCK 1
IFN FTOPS10,<
LATRTQ: BLOCK 2 ;QUEUE FOR LAT LDBS
LATFRE: BLOCK QL.LEN ;QUEUE FOR LAT FREE CORE BLOCKS
LATLOC:: BLOCK 1 ;START OF LAT FREE CORE (SET UP AT ONCE TIME)
LATMCT: BLOCK 1 ;Multicast timer (in seconds for TOPS-10)
LATNFB: BLOCK 1 ;Number of free buffers in pool
LATWFB: BLOCK 1 ;Number of free buffers wanted for new circuits
LATRFB: BLOCK 1 ;Number of buffers requested on last cycle
LASBAD::BLOCK 2 ;Ethernet address of sender of last bad msg
TRVAR <MSGDID,MSGSID,MSGACK,STPCOD> ;Message header variable storage
TRVAR <SLTDID,SLTSID,SLTCNT,SLTTYP,SLTEPT>
>;end IFN FTOPS10
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>
;
; Temp storage block for LATOP.
;
MAX. (TMPLNG,<<GB.LEN+1>,ML.NNM,ML.DSC>)
TMPBLK: BLOCK TMPLNG
TMPGB==TMPBLK+1
RESCD
IFN FTOPS10,<
SUBTTL LDBISR Dispatch Table
LATDSP::RET ;( 0)ILLEGAL NOW. DON'T USE!!
RET ;( 1)MODEM CONTROL
JRST LATSEC ;( 2)ONCE A SECOND CALL (NOT PER TTY)
RET ;( 3)INITIALIZE
JRST SETCHP ;( 4)CHANGE HARDWARE PARMS (let SCNSER do it)
RET ;( 5)LINE PARM CONTROL
RET ;( 6)SET TERMINAL ELEMENT
JRST LATREM ;( 7)STUFF FOR REMOTE TERMINALS
RETSKP ;(10)IS LINE DEFINED ON STATION (YES)
DEFINE PARTB. (PRM,MAX,MIN,CLRVAL,INSTR,FLAGS) <
.ORG <.LP'PRM+TBRNGE>
EXP <MAX,,MIN>
.ORG
.ORG <.LP'PRM+TBFLGS>
EXP <FLAGS!CLRVAL>
.ORG
.ORG <.LP'PRM+TBEXEC>
IFB <INSTR>,<
STOR T2,HN'PRM,(Q2)
>
IFNB <INSTR>,<
INSTR
>
.ORG
>
SUBTTL LATOP% JSYS
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,.LPLOC>)
MIN. (MNSPAR,<.LPMAC,.LPMCO,.LPNUM,.LPLAS,.LPRLI,.LPTIM,.LPMTI,.LPCOD,.LPNNM,.LPNID,.LPSRV,.LPLOC>)
SWAPCD
TBRNGE: BLOCK <MXSPAR-MNSPAR+1>
TBFLGS: BLOCK <MXSPAR-MNSPAR+1>
TBEXEC: BLOCK <MXSPAR-MNSPAR+1>
PARTB. (MAC,377,1,^D10,,LF%RNG!LF%CLR)
PARTB. (MCO,NTTLAH,1,^D10,,LF%RNG!LF%CLR)
PARTB. (NUM,177777,0,0,,LF%RNG!LF%CLR)
PARTB. (LAS,LS.ON,LS.OFF,0,,LF%RNG!<HNOTH_^D18>)
PARTB. (RLI,100,0,^D60,,LF%RNG!LF%CLR)
PARTB. (TIM,^D100000,^D100,^D1000,<JRST SETRTX>,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)
IFN FTOPS10,< SUBTTL NETOP. FUNCTIONS
;NETDIL - tell user the node and port where his TTY is connected
;
;Call: (in section 1 already)
;
; U/ LDB of LAT terminal
; M/ Address of user's NETOP. arg list
;Return:
; ECDX? depending on error
; RETSKP node name stored in string block pointed to by user's arg list
;
;Uses P1,P2 (already saved by higher routine), T1-T4, M
NETDIL::MOVE P1,M ;SAVE ADDRESS OF ARG LIST
EXCTUX <SKIPN M,5(P1)> ;POINT TO NODE NAME STRING BLOCK
JRST NTDIL1 ;NO STRING BLOCK FOR NAME
PUSHJ P,CHKSTB## ;CHECK STRING BLOCK FOR ADDRESSABILITY
JRST NOPADC## ;ADDRESS CHECK
MOVE T3,LDBLAT##(U) ;GET ADDRESS OF SB BLOCK
MOVE T3,SB.CBA(T3) ;GET ADDRESS OF CORRESPONDING CB BLOCK
LOAD T1,CBRSC,(T3) ;GET BYTE COUNT FOR NAME
XMOVEI T3,CB.SNM(T3) ;GET ADDRESS OF NODE NAME
PUSHJ P,MOV7T8 ;MOVE THE STRING
JRST NOPADC## ;NOT ENOUGH ROOM
NTDIL1: EXCTUX <SKIPN M,6(P1)> ;POINT TO PORT NAME STRING BLOCK
RETSKP ;BELIEVE IT OR NOT, WE'RE DONE
PUSHJ P,CHKSTB## ;CHECK THE STRING BLOCK
JRST NOPADC## ;NOT THERE
MOVE T3,LDBLAT##(U) ;GET ADDRESS OF SB BLOCK AGAIN
LOAD T1,SBSPC,(T3) ;GET BYTE COUNT OF PORT NAME
JUMPE T1,RSKP ;Nothing to store
XMOVEI T3,SB.SPN(T3) ;GET ADDRESS OF PORT NAME
PUSHJ P,MOV7T8
JRST NOPADC## ;NOT ENOUGH ROOM
RETSKP
;
;MOV7T8 - ROUTINE TO MOVE A MONITOR 7 BIT STRING TO A USER 8 BIT STRING BLOCK
;
;CALL: M/ ADDRESS OF STRING BLOCK
; T1/ COUNT OF SOURCE STRING
; T3/ ADDRESS OF SOURCE STRING
; T4/ COUNT OF DESTINATION STRING
;
;RETURN: +1 ADDRESS CHECK
; +2 SUCCESS
;
; USES T1-T4
;
MOV7T8:
IFN FTXMON,<PUSHJ P,SSPCS##> ;PRESERVE PCS FOR CALLER
PUSH P,T1 ;SAVE COUNT FOR A BIT
MOVE T1,M ;COPY POINTER TO STRING BLOCK
PUSHJ P,SXPCS## ;SETUP PCS
JRST TPOPJ## ;I THOUGHT WE KNEW IT WOULD FIT?
POP P,T1 ;RESTORE BYTE COUNT
EXCTXU <HRLM T1,(M)> ;STUFF BYTE COUNT INTO STRING BLOCK
CAILE T1,(T4) ;ENOUGH SPACE TO STORE?
RET ;NO
MOVSI T2,(<POINT 7,>!1B12) ;MAKE GLOBAL BYTE POINTER
PUSH P,T4+1 ;SAVE A COUPLE OF ACS
PUSH P,T4+2 ; T1 HAD BETTER BE LESS THAN 12
MOVSI T4+1,(<POINT 8,>!1B12)
EXCTUX <XMOVEI T4+2,1(M)> ;GLOBAL ADDRESS OF DEST.
PXCT 11,[EXTEND T1,[MOVSLJ
0]] ;MOVE THE BYTES (I HOPE)
JFCL
POP P,T4+2
POP P,T4+1
RETSKP
> ;END IFN FTOPS10
SUBTTL LATOP% JSYS
.LATOP::
IFN FTOPS20,<
MCENT
UMOVE Q1,1 ;Get the argument block address from user
>;END IFN FTOPS20
IFN FTOPS10,<
CALL SAVE4 ;Save P1-P4
PUSH P,W ;Save the AC we use ( = Q1)
MOVE Q1,T1 ;Get the argument block address from user
;
; For TOPS10, we must address-check the arg list or else we might get
; an IME
;
PUSH P,M ;Save UUO for later store ( = Q2)
MOVE J,.CPJOB## ;Also need job number
MOVE M,T1 ;Point to user's arg list
CALL GETWRD## ;Get first word (arg count)
ITERR (LATXAC) ;Arg list is not addressable!
MOVE P1,T1 ;Save count of arg block
MOVE T1,Q1 ;Restore arg list address
MOVE T2,P1 ;Arg list is this long
CALL ARNGE## ; so check it.
NOP ;Not addressable!
ITERR (LATXAC) ;Not writeable (error because must update .LABCT)
;
; From here on, we know the arg list is addressable
; so UMOVEs are no problem.
;
>;END IFN FTOPS10
IFN FTOPS20,< UMOVE P1,.LAACT(Q1) ;Count of the argument block>
UMOVE P2,.LAFCN(Q1) ;LAT function code.
SKIPN Q2,LAHNDB ;If there is no data base,
ITERR (LATX03) ; then LAT is not operational
CAIL P2,MNLFCN ;Range check the function code
CAILE P2,MXLFCN ; ...
ITERR (ARGX02) ;Function code out of range
IFN FTOPS10,<
CAIE P2,.LASET ;Is the function .LASET?
CAIN P2,.LACLR ; or .LACLR?
JRST DOFUN ;Yes, these do their own address checking
CAIN P2,.LAZCO ;or is it .LAZCO?
JRST DOFUN ;Yes, no show buffer for that either
;
; Here, we can do some common address checking for all functions which
; have a SHOW buffer
UMOVE T1,.LABFA(Q1) ;Get SHOW buffer address
XCTU [HRRZ T2,.LABCT(Q1)] ; and buffer word count
JUMPE T2,DOFUN ;0 length, don't bother to check.
CALL ARNGE## ;See if all of it's addressable
NOP ;Not addressable
ITERR (LATXAC) ; or not writeable, give error
DOFUN:
>;END IFN FTOPS10
CALL @LAFDSP-MNLFCN(P2) ;Dispatch to perform the function
ITERR () ;Error already reported
IFN FTOPS20,< MRETNG ;Return to user >
IFN FTOPS10,<
POP P,M ;RESTORE UUO
POP P,W ;AND SAVED AC
RETSKP ;AND GIVE GOOD RETURN
>; END IFN FTOPS10
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:
IFN FTOPS10,<
MOVSI T1,JP.POK ;Check to see if the guy has POKE priv
CALL PRVBIT## ; or [1,2] or JACCT
JRST LASET1 ;Yup! non-skip means he's macho.
RETBAD(LATXPV) ;He's a wimp, don't let him do anything.
LASET1:
>
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(T1) ; ...
RETBAD (LATX07) ;No, return error
HRRZ T2,TBFLGS(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(T1) ; be range checked.
IFSKP. ;Yes
MOVE T3,TBRNGE(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(T1) ;Change flags to set.
ANDI T3,377
IORM T3,HN.FLG(Q2) ;Set them
XCT TBEXEC(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
IFN FTOPS10,<
CALL CKSTR## ;Check to see if ASCIZ string is addressable
RETBAD (LATXAC) ;Nope, lose
>;end ifn FTOPS10
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
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 RETRANSMIT TIMER
;SETRTX - Set LAT retransmit timer
;
;Call: Q1/ User's parameter buffer
; Q2/ Host node data base address
;Return: +2 always
;
SETRTX: STOR T2,HNTIM,(Q2) ;Store millisecond value
IMULI T2,JIFSEC## ;Convert to ticks
MOVE T1,T2
IDIVI T1,^D1000
MOVEM T1,RTXTIM ;Save retransmit time in ticks
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 pointer from the user.
IFN FTOPS10,<
CALL CKSTR## ;Check to see if ASCIZ string is addressable
RETBAD (LATXAC) ;Nope, lose
>;end ifn FTOPS10
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 pointer from the user.
IFN FTOPS10,<
CALL CKSTR## ;Check to see if ASCIZ string is addressable
RETBAD (LATXAC) ;Nope, lose
>;end ifn FTOPS10
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
;RETSKP - always, no errors.
SETCOD:
IFN FTOPS10,<
UMOVE T1,.LABFA(Q1) ;Get address of string
MOVEI T2,^D8 ;and length
CALL ARNGE## ;address check it
ITERR (LATXAC) ;not there
NOP ;not writeable (that's ok)
>;END IFN FTOPS10
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
; See Processor Reference Manual page 2-116 for algorithm
;
;Call: T1/ Byte to swap
;Returns:
; T1/ Swapped byte
BITSWP: MUL T1,[100200401002]
AND T2,[20420420020]
ANDI T1,41
DIVI T1,1777
MOVE T1,T2 ;put answer in T1
RET
;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: MOVNS T3 ;Make an AOBJN word
HRLZS T3 ;to count up and down at the same time
IFN FTOPS20,<
TLC T1,-1 ;Make an ASCII pointer to the
TLCN T1,-1 ; user's buffer
HRLI T1,(<POINT 7,0>) ; if necessary.
>; END IFN FTOPS20
UMVAZ0: XCTBU [ILDB T4,T1] ;Get a character from his string.
JUMPE T4,UMVAZ1 ;Null found so string ended
IDPB T4,T2 ;Deposit character in temp storage.
AOBJN T3,UMVAZ0 ;If we are not at the maximum, read more.
RET ;Hit max length without seeing a null.
UMVAZ1: HRRZS T3 ;Return only the positive byte count
RETSKP
;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
IDPB T4,T2 ;Deposit character at destination
CAML T3,MAXCNT ;If we are at the maximum, read no more.
RET
AOJA T3,MMVAZ0 ;Increment string count
ENDSV.
SWAPCD
SUBTTL LATOP% - 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: UMOVE T3,.LABFA(Q1) ;User buffer address
;Check to see if the user provided buffer is large enough for all the data
MOVEI T1,<HN.MXC-HN.MTI+<ML.LOC+3>/4+<ML.DSC+3>/4+<ML.NNM+3>/4+^D16>
LOAD T4,HNNSV,(Q2)
IMULI T4,GB.LEN ;Number words for service blocks
ADD T1,T4 ;Total buffer size in words
CALL LABFCK ;Is his buffer big enough?
RETBAD() ;No
;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
XBLTXU 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>]
XBLTXU 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
MOVEI T1,<<ML.NNM+4>/5+<ML.DSC+4>/5+1>
XMOVEI T2,HN.NMC(Q2) ;Address of node name and description
XBLTXU 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
MOVE T1,T4 ;Get back the total service block length
XMOVEI T2,HN.SRV(Q2) ;Address of service blocks
XBLTXU 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: LOAD T1,HNCON,(Q2) ;How many do we think are connected?
MOVE T4,T1 ;Save count
IMULI T1,<2+MW.SYS> ; * size of returned block
CALL LABFCK ;Is his buffer big enough?
RETBAD() ;No
MOVEI T1,NTTLAH ;Get size of SBvect
PUSH P,T1 ;Save as a counter
UMOVE P1,.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 T4,LASTCD ;Have we given the user as many as we said?
LASTCN: SOSLE (P) ;Or have we exhausted SBVECT?
AOJA P2,LASTCL ;No, just go for next data block
LASTCD: ADJSP P,-1 ;Done, adjust stack
UMOVE T1,.LABFA(Q1) ;Get address of show buffer again
SUB P1,T1 ;Calculate number of words moved
XCTU [HRLM P1,.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
; P1/ Next address in user's buffer
;
;Return: +1 no attached terminal or circuit block now
; +2 Success. Data moved to user's buffer
;
; P1/ Updated next address to fill in user's buffer (in either case)
;
LAS1C: SKIPE U,SB.TDB(T1) ;Any LDB associated with slot?
SKIPN T2,SB.CBA(T1) ;Any circuit block?
RET ;No, skip this one
LOAD T1,CBRSC,(T2) ;Yes, get server name string count
LDB U,LDPLNO## ;Yes, get TTY line number
UMOVEM U,(P1) ;Give to user
UMOVEM T1,1(P1) ;Store count
XMOVEI T2,CB.SNM(T2) ;and point to name string
MOVEI T1,<MW.SYS> ;length of string
MOVE T3,P1 ;Get current user buffer address
ADDI T3,2 ; +2 for the words we have filled
XBLTXU T1 ;Fill in the user's buffer
MOVE P1,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
REPEAT 0,<
UMOVE T1,.LAQUA(Q1) ;User's pointer to server name
CAIN T1,-1 ;Specific server wanted?
>
XCTU [SKIPN T1,.LAQUA(Q1)] ;Specific server wanted?
IFSKP. ;Yes.
IFN FTOPS10,<
CALL CKSTR## ;Check to see if ASCIZ string is addressable
RETBAD (LATXAC) ;Nope, lose
>;end ifn FTOPS10
IFN FTOPS20,<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
CALL LABFCK ;Is his buffer big enough?
RETBAD() ;No
UMOVE T3,.LABFA(Q1) ;Get his buffer address
XMOVEI T2,SASBEG(T4) ;Address of source
XBLTXU T1 ;Move to user space
RETSKP ;Return successfully.
ENDIF.
LOAD T1,HNNCC,(Q2) ;Get the number of allocated circuit blocks
IMULI T1,<1+MW.SYS+2> ;Compute space needed in user's buffer
CALL LABFCK ;Is his buffer big enough
RETBAD() ;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,MW.SYS ;There are 4 words of name
XMOVEI T2,CB.SNM(T4) ;Address of first to copy
XBLTXU 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
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
XBLTXU T1
RETSKP
;LAZCO - Zero LAT Counters
;CALL: Q1/ User argument block address
; Q2/ LAT host node data base address
LAZCO: CAIGE P1,5 ;Is the user's arg block the right size?
RETBAD (ARGX04) ;No
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 ;Account for the word we just zeroed
XBLT. T1 ;Zero the block if more than 1 word
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:
REPEAT 0,<
CAIN T1,-1 ;Zero local host counters?
>
XCTU [SKIPN T1,.LAQUA(Q1)] ;Get server name requested
IFSKP. ;Zero a particular server's counters
IFN FTOPS10,<
CALL CKSTR## ;Check to see if ASCIZ string is addressable
RETBAD (LATXAC) ;Nope, lose
>;end ifn FTOPS10
IFN FTOPS20,<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
IFN FTOPS10,<
CALL CKSTR## ;Check to see if ASCIZ string is addressable
RETBAD (LATXAC) ;Nope, lose
>;end ifn FTOPS10
IFN FTOPS20,<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
;LABFCK - Check if the user's show buffer is big enough
;Call: T1/ Needed size
;Return:
; +1 Not big enough
; T1/ LATX01
;
; +2 OK
LABFCK: 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
RETSKP ;Yes
IFN FTOPS20,<
;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
>;END IFN FTOPS20
IFN FTOPS10,<
;ERRRTN- Error return from LATOP. UUO
; Must only be called at top level within UUO
; i.e. the only thing on the stack is old value of M
; Otherwise, use RETBAD and propagate error return
;
ERRRTN: POP P,M ;Restore original UUO
POP P,W ;Restore saved AC
JRST STOTAC## ;Store error code and return to user
;
; Last thing in LATOP. UUO code
;
PURGE Q1,Q2 ;undefine TOPS-20 ACs
>;end ifn FTOPS10
SUBTTL LATINI - LAT Initialization
;LATINI - LAT Initialization
;
;Call: CALL LATINI
; Error return - resource failure
; Normal Return
;
ENTRY LATINI ;Keep LINK happy in library search mode
LATINI: SE1ENT
ACVAR <HN,XB,W1,W2> ;Reserve AC for Host Node data base address
MOVEI T1,HC.LST+1 ;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
SETONE HNRUN,(HN) ;Set flag that says NI not running (yet).
MOVEM T1,LAHNDB ;Store the data base address
MOVEI T1,CBMAXI ;Get storage for CBVECT
CALL MMGTZW ;
JRST INIRES ;Resource failure.
SOS T1 ;Save the address minus one:
MOVEM T1,CBVECT ; circuit block indices start with one
MOVEI T1,NTTLAH ;Get storage for SBVECT
CALL MMGTZW
JRST INIRES ;Resource failure.
SOS T1 ;Save the address minus one:
MOVEM T1,SBVECT ; slot block indices start with one
IFN FTOPS10,<
;
; Now carve up the core we got at ONCE time
; The free core pool will be turned into a
; linked list of fixed length buffers from which we can allocate
; transmit and receive buffers.
;
SETZRO QLFWD,+LATFRE ;Clear out the queue
SETZRO QLBWD,+LATFRE
CALL LAGTCR ;find out how much memory we got at ONCE time
IDIVI T1,BUFSIZ ;See how many buffers will fit
MOVE T4,T1 ;Put it where we need it later.
MOVEM T4,LATNFB ;Remember how many we have at start
MOVE T2,LATLOC ;Start of first one
CALL ALCBUF ;Link them together
MOVE T1,STANAM ;Get our node name
CALL CVTNOD ;Convert to ASCII
>;end IFN FTOPS10
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
IFN FTOPS20,< MOVX T1,<POINT 7,SVN##> ;Pointer to MONNAM.TXT>
IFN FTOPS10,< MOVX T1,<POINT 7,CONFIG> ;Pointer to Monitor name>
MOVX T2,<POINT 7,HN.ID(HN)>;Where to put it.
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
MOVEI T1,^D60 ;Default Host message retransmit limit
STOR T1,HNRLI,(HN)
MOVEI T1,^D1000 ;Default host virtual circuit timer(msec)
STOR T1,HNTIM,(HN) ;One second between retransmits
MOVEI T1,JIFSEC## ;Same thing as above
MOVEM T1,RTXTIM ; but in ticks instead of milliseconds
MOVEI T2,^D30 ;Default host multicast timer (secs)
STOR T2,HNMTI,(HN)
MOVE T1,LASDEF ;Get the default LAT Access State
STOR T1,HNLAS,(HN) ; and store it
CAIN T1,LS.OFF ;If default state is on, start transmitting
IFSKP. ; multicast messages immediately.
IFN FTOPS20,<
IMULI T2,^D1000 ;Multicast in millisecs
>;end IFN FTOPS20
MOVEM T2,LATMCT ;Initialize scheduler long timer word
ENDIF.
MOVEI T1,MAXCIR ;Default maximum allocatable circuit blocks
STOR T1,HNMXC,(HN)
;...
;...
STOR T1,HNMAC,(HN) ;And max active circuits
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)
MOVSI T1,(1B7) ;Set group 0 on
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 T4,HN.SRV(HN) ;Address of the host service blocks
STOR T1,GBRAT,(T4)
MOVE T1,OURCNT ;Default service name is node name so
STOR T1,GBNC,(T4) ; store the count
DMOVE T1,OURNAM ; and the name
DMOVEM T1,GB.NAM(T4)
IFN FTOPS10,<
LOAD T1,HNIDC,(HN) ;get Host node id count
CAILE T1,ML.SID
MOVEI T1,ML.SID ;Don't exceed what's allowed in this field
STOR T1,GBLC,(T4) ;SAVE it
IDIVI T1,5 ;Convert to word count
SKIPE T2 ;if not an even multiple of 5
ADDI T1,1 ;Round up
XMOVEI T2,HN.ID(HN) ;Get address of host descriptor
XMOVEI T3,GB.HID(T4) ;And address of service descriptor
XBLT. T1 ;make service descriptor the same as host
>;END IFN FTOPS10
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
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 ; ...
MOVEI T1,NTTLAH ;Get the number of LAT lines
IDIVI T1,^D36 ;Get the number of bits at the end
SKIPN T2 ;Are there any in the last word?
IFSKP. ;Yes, then build a bit mask for it
MOVEI T1,1 ;Calculate the negative number
SUB T1,T2 ; of bits to shift plus one
MOVX T2,1B0 ;Get a bit to be shifted
ASH T2,(T1) ;Build the bit mask for the last word
MOVEM T2,NFRSBQ+NSBWDS-1 ;Use it instead of minus one
ENDIF. ;
;...
;...
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.
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.
SKIPL UN.STA+UNB.OF(XB) ;Is returned state "running" ?
IFSKP.
SETZRO HNRUN,(HN) ;Yes, clear our flag (mark NI running)
CALL GETRBF ;Get a buffer for receives
TRN ;Will have to get buffers later
ENDIF.
ENDIF.
RET
INIRES:
BUG. (CHK,LATINE,LATSER,SOFT,<LATINI failed to initialize>,,<>)
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 Subroutine -- CWHLAT - Type a server spec for WHERE command
;CWHLAT - Types a server spec given a LATSER-owned LDB.
;
; Call:
; U/ Target LDB address
; 0(P)/ Command LDB address
;
; Return:
; RET ;If success
; via CWHNCN ;If line is not owned by LATSER
;
; Used: T1-T4
IFN FTOPS10,<
CWHLAT::POP P,T1 ;Can't call SE1ENT with junk on stack!
SE1ENT ;So we can access NZS data
PUSH P,T1 ;RE-save LDB of command term
SKIPN T1,LDBLAT##(U) ;Get slot block associated with target TTY
PJRST CWHNCN## ;Not connected now.
POP P,U ;Point to command terminal for typeout
PUSH P,DEVNAM(F) ;Save TTY name
PUSH P,SB.CBA(T1) ; and Circuit Block address.
CALL TTYKLQ## ;ZAP useless TTY DDB
CALL INLMES## ;Type out an intro
ASCIZ /LAT / ; guess what!
MOVE T2,(P) ;Get LAT circuit block address
LOAD T1,CBRSC,(T2) ;Get server name count from it
ADD T2,[OWGP. 7,CB.SNM] ;Make byte pointer to name
CALL TCSTR ;Type a counted string
MOVEI T3,"(" ;Separate it
CALL COMTYO## ; from location
POP P,T2 ;Get CB address for last time
LOAD T1,CBRLC,(T2) ;Get server location count from it
ADD T2,[OWGP. 7,CB.LOC] ;Make byte pointer to location
CALL TCSTR ;Type a counted string
CALL INLMES## ;Close it off
ASCIZ /) / ;and separate it.
POP P,T2 ;Get TTY name
CALL PRNAME## ; and type it.
PJRST PCRLF## ;and return
;
; Routine TCSTR - type a counted ASCII string
;
;Call: T1/ byte count
; T2/ byte pointer
;
TCSTR: SOJL T1,CPOPJ## ;No chars to type
PUSH P,T1 ;save the count in a safe place
TCST1: ILDB T3,T2 ;Get a character
CALL COMTYO## ; and type it
SOSL (P) ;Count them down
JRST TCST1 ; and do them all
POP P,T1 ;trim the stack
RET ; and return
> ;end IFN FTOPS10
SUBTTL Once a second code (TOPS10)
IFN FTOPS10,<
LATSEC::
IFN FTMP,<
XCT .CPSK0 ;SKIP IF WE'RE ON POLICY CPU
RET ;NO, ONLY RUN ONCE A SECOND!
>
SKIPLE LATMCT ;IS the timer non-zero?
SOSLE LATMCT ;Count down the timer
RET ;Not time to do anything yet
; CALLRET LATXMC ;Fall into multicast transmit code
>;end IFN FTOPS10
SUBTTL Multicast Transmitter
;
;LATXMC - Transmit Multicast Message
;
;CALL: CALL LATXMC
; Normal Return
LATXMC::
SE1ENT
ACVAR <HN,XB,W1,W2>
SKIPN HN,LAHNDB ;Get the address of the HN data base
RET ;LAT initialize not done yet.
LOAD T1,HNMTI,(HN) ;Reset the
IFN FTOPS20,<
IMULI T1,^D1000 ; multicast
>;end IFN FTOPS20
MOVEM T1,LATMCT ; timer
SKIPGE HN.RUN(HN) ;is the NI running?
RET ;No, go no further.
CALL GETRBF ;Get another buffer if we need one
NOP ;Oh well!
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
TLO 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 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>
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
LOAD W1,HNNSV,(HN) ;Get the total number of offered services
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
CALL DYNRAT ;If negative, get the 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 - Build the Available Host Services Data - BMCRAT
;
;Call: HN/ Host Node Data Base Address
; XB/ Address of the multicast message block in HN
; CALL BMCRAT
; 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.
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
CALL DYNRAT ;If negative, get the 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
;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,^D20 ;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,LATSER,SOFT,<LATCBR called from NISRV with illegal callback function code>,<<T1,CODE>>,<
NISRV has called the LATSER callback routine with an invalid 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: REPEAT <MAXCBF-MINCBF+1>,<
IFIW ILLCBR> ;Catch undefined call-back codes
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
JUMPE T4,LANTR1 ;If error code is 0 proceed
;
; Here, there was an error on receive. Don't wait for tick time
; to turn the buffer around.
;
MOVE T1,RB ;Get address of receive buffer
CALL RBPOSS ;Repost it now (preserving T1-T4)
JRST LANTR1 ;Couldn't re-post. Process it later.
RET ;Buffer re-posted. That's all we need to do.
LANTR1: 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,UNSID,+UNB.OF(T2);Save NISRV's error return
ETHLOK ;Interlock queue access
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
ETHULK ;Release interlock
RET
ENDAV.
SUBTTL LARSCH - Scheduler Level Virtual Circuit Message Receiver
;LARSCH - Complete circuit message processing at scheduler level
LARSCH::
IFN FTOPS10,<
LATSTO::
IFN FTMP,<
XCT .CPSK0 ;SKIP IF WE'RE ON POLICY CPU
RET ;NO, ONLY RUN ONCE A SECOND!
>
>; END FTOPS10
SE1ENT
ACVAR <HN,XB,RB,CB,SB,W1,W2>;Get a set of dedicated ACs
IFN FTOPS20,<
TRVAR <MSGDID,MSGSID,MSGACK,STPCOD> ;Message header variable storage
>; END FTOPS20
;**; [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
IFN FTOPS10,<
SETZM LATWFB ;Say we don't want more buffers yet
>;end IFN FTOPS10
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.SID+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. (INF,LATIMT,LATSER,SOFT,<LAT Illegal Message Type>,<<T1,MSGID>>,<
Cause: LAT virtual circuit message received with message type out of range.
Action:
>)
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 ;else 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:
ETHLOK ;No interruptions, please!
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
ETHULK ;Ok, interrupt me now
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
OPSTR <SKIPL>,UNSTA,(T2) ;Is NI running?
IFSKP.
ANDCAM T3,HN.FLG(HN) ;NI is up
MOVEI T1,1 ;So if multicast timer
SKIPLE LATMCT ; is running...
MOVEM T1,LATMCT ; set it to ring soon.
ELSE.
IORM T3,HN.FLG(HN) ;NI went down.
ENDIF.
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,HNLAS,(HN) ;If LAT access state is not ON, ignore the
CAIE T1,LS.ON ; START message.
RET ; ...
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,CBSTA,(CB) ;Get the current circuit state
CAIE T1,CS.RUN ;If the state is running,
IFSKP.
CALL RELSBS ; release all current slots before continuing
CALL RFXBFS ;Release all buffers on free queue.
CALL MDLXBF ;Mark all buffers on ACK queue for later release
ENDIF.
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
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.
IFN FTOPS20,< RET ; and return. >
IFN FTOPS10,<
MOVEI T1,MAXXBF+1 ;New circuit, add some buffers
CAMLE T1,LATNFB ;Are there already enough free buffers?
ADDM T1,LATWFB ;No, ask for some more
RET
>;END IFN FTOPS10
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) ;Count 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,ILLMESS ;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) ;Which is in seconds.
IMULI T1,JIFSEC## ;CONVERT TO Jiffies
HRLZM T1,CB.KAF(CB)
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)
CALL IGCSTR ;Skip the circuit name
JSP T2,ILLMES ;Illegally formatted message
MOVEI T1,ML.SYS ;Maximum length of system's name string
MOVX T2,<POINT 7,<CB.SNM>(CB)> ;Copy the name string
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
MOVX T2,<POINT 7,<CB.LOC>(CB)>;Copy the location string
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
XMOVEI T1,1(W1) ;and vector+1
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.
HLLZS CB.KAF(CB) ;Clear the host keep-alive timer.
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) ;Get first buffer on ack queue
CALL RELXBF ;Release buffer, since we got an ack
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
CALL MTTSTP ;Illegal slot, kill the circuit
RET ;All slots processed, return.
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 local 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?
RET ;Yes, just ignore it.
CALLRET CBKILL ;Else, release all CB resources and
; put on inactive Q
; 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.
IFN FTOPS20,<
NIHALT: MOVEI T1,CE.NIH ;NI halted
SETZ T2,0 ;PC meaningless
JRST INFSTP
>; END IFN FTOPS20
INVRUN: MOVEI T1,CE.INV ;Invalid message
JRST INFSTP
INFSTP: PUSH P,T2 ;Save PC of error
OPSTR <DMOVE T2,>,UNSAD,+UNB.OF(RB) ;Source NI address from recvd msg
DMOVEM T2,LASBAD ;Save ethernet address of sender of bad msg
POP P,T2 ;Restore PC of caller for typeout
BUG. (INF,LATNSC,LATSER,SOFT,<LAT Host node stopped circuit>,<<T1,CODE>,<T2,PC>,<LASBAD,EADDR1>,<LASBAD+1,EADDR2>>,<
Cause: LAT Host node stopped the circuit.
Action: Look at the Reason Code in T1 and the PC in T2, and the
Ethernet address in EADDR1 and EADDR2. This error, if
relatively infrequent is nothing to be concerned about. If it occurs
frequently, use the CODE and PC to determine further action.
>)
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,(<OWGP. 8,0>) ; 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
;CBKILL - Kill a circuit by releasing all slots and buffers
;
;Call: CB/ address of the circuit block
; CALL CBKILL
; Normal return
CBKILL: 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
; CALLRET CBDACT ;and move CB to the inactive queue
;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
SKIPE T1 ;If it is 0, assume MSGSID already contains correct ID
;This may prevent sending an illegal STOP with 0
;in the destination ID field.
MOVEM T1,MSGSID ; and put where we want it below
CALL CBKILL ;Free all circuit resources.
;
; 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: SKIPGE HN.RUN(HN) ;If the NI 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
SETZ T1, ;No flag set yet
SKIPN CB.XBQ(CB) ;Is this our last transmit buffer?
MOVX T1,CBRRF ;Yes, set flag
IORB T1,CB.RRF(CB) ;Stuff flag and get result
MOVE CX,RTXTIM ;Value for circuit timer
TXNE T1,CBRRF ;Is flag set for any reason?
STOR CX,CBTIM,(CB) ;Yes, start circuit timer ticking.
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
ANDI T1,377 ;Counter is only 8 bits
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
SKIPN MD.NXT+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
SOSE T1 ;Decrement the timer.
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 ; and retransmit the unack queue
MOVE T1,RTXTIM ; Reset the timer
STOR T1,CBTIM,(CB) ; in the circuit block
ELSE.
JSP T2,XMTLIM ; otherwise, terminate the circuit
ENDIF. ; cause communication broken with remote.
RET
ENDIF.
STOR T1,CBTIM,(CB) ;Restore decremented timer to CB
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
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
JRST XMTMS1 ;Function failed, clean up
OPSTRM <AOS>,HCXMT,(HN) ;Count total messages transmitted
JUMPE CB,RTN ;and if there is a CB,
OPSTRM <AOS>,CCXMT,(CB) ;count messages xmitted on that circuit
RET
XMTMS1: MOVSI T1,DLL.FL ;get "buffer-in-DLL" flag
ANDCAB T1,UN.UID+UNB.OF(XB) ;clear it in message
JUMPE T1,RELXBH ;If no CB for this one, just return buffer
OPSTRM <SOS>,CBDLL,(CB);Failed so put back count where is was
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.
MOVSI CB,DLL.FL ;Get "buffer-in-DLL" flag
ANDCAB CB,UN.UID+UNB.OF(T2) ;Clear it and load result
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
OPSTR <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
IGCSTR: SETZ T1, ;Here to ignore a counted string
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
;
;Call at RBPOSS if you need to preserve T1-T4
RBPOSS: CALL SAVT## ;Preserve T1-T4
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,(<OWGP. 8,0>) ; 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
CALLRET DLLUNI ;Do it and return +1 or +2 to caller
repeat 0,<
IFNSK.
BUG. (CHK,LAPRBF,LATSER,SOFT,<Specify Receive Buffer Failure>,<<T1,DLLERC>>,<
LATSER received an error from NISRV while attempting to post a receive buffer>)
RET
ENDIF.
RETSKP
> ;end repeat 0
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:
IFN FTOPS20,<
TRVAR <SLTDID,SLTSID,SLTCNT,SLTTYP,SLTEPT> ;Assign some variables on stack
>; END IFN FTOPS20
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 nearest even number
TRZ T1,1 ;Since there 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 ;and save it
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: REPEAT <MAXSTY-MINSTY+1>,<
IFIW ILLSLT> ;Catch undefined slot types
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 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
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)
MOVE T1,SLTCNT ;Get slot data count
SUBI T1,3 ;Account for the 3 bytes already read
EXCH T1,UN.BSZ+UNB.OF(RB) ;Put it where it's useful for GTCSTR
PUSH P,T1 ;and save real message byte count
MOVEI T1,ML.SNM ;Max length of service name
MOVX T2,<POINT 7,<SB.SNM>(SB)> ;Point to slot name string
CALL GTCSTR ;Get a counted string
SETZ T1, ;Ran out of message
STOR T1,SBSNC,(SB) ;Store actual length of name
CALL IGCSTR ;Ignore the subject service name
JRST HSSTR3 ;No more data in message
;
; Now we are into the variable parameter part of a START SLOT
; Loop and record whatever fields we think are important.
;
HSSTR1: SOSL T1,UN.BSZ+UNB.OF(RB) ;Count down bytes in slot
ILDB T1,UN.BFA+UNB.OF(RB) ;Get Parameter number
JUMPLE T1,HSSTR3 ;Either ran out of bytes, or hit end of parms
CAILE T1,SBPRMX ;Greater than max parm we know about?
SETZ T1, ;Yes, we will just ignore it.
PUSHJ P,@SBPRTB(T1) ;Call routine to deal with parameter
JRST HSSTR3 ;Ran out of message
JRST HSSTR1 ;Get next parameter from slot
HSSTR3: POP P,T1 ;Restore real message byte count
EXCH T1,UN.BSZ+UNB.OF(RB) ;to its rightful place
MOVEM T1,SLTCNT ;And put new remaining slot count where it goes
IFN FTOPS10,<
;First, check to see if remote TTYs can connect
MOVE T1,STATES## ;Get system states word
TRNE T1,ST.NRT ;Are remote TTYs allowed?
JRST SLSHUT ;Nope, reject the connection
> ;end IFN FTOPS10
CALL GETTDB ;Get a terminal data block and start a job
JRST SLRESF ;No terminal data blocks available
MOVX T1,SBSTR!SBFCC ;Flag this slot as waiting to send a START
CALL SSBDAV ; and a DATA_B slot when MUX runs.
RETSKP ;Return success
SLSHUT: SKIPA T2,[SE.SHU] ;Reject reason is "Shut down"
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 Process parameters from a received START slot
;
; Dispatch table of routines to deal with parameters from start slot
;
SBPRTB: IFIW IGCSTR ;Ignore ones we don't know about
IFIW IGCSTR ;Parameter 1
IFIW IGCSTR ;2
IFIW IGCSTR ;3
IFIW GETOPN ;OBJECT_PORT_NAME
IFIW GETSPN ;SUBJECT_PORT_NAME
SBPRMX=.-SBPRTB-1
GETOPN: MOVE T2,[POINT 7,SB.OPN(SB)] ;Get object_port_name
MOVEI T1,ML.PNM ;Set up max length of port name
CALL GTCSTR ;Go get the string
TDZA T1,T1 ;zero the count and give non-skip return
AOS (P) ;PROPAGATE SKIP
STOR T1,SBOPC,(SB) ;SAVE COUNT
RET
GETSPN: MOVE T2,[POINT 7,SB.SPN(SB)] ;Get Subject_port_name
MOVEI T1,ML.PNM ;Set up max length of port name
CALL GTCSTR ;Go get the string
TDZA T1,T1 ;zero the count and give non-skip return
AOS (P) ;PROPAGATE SKIP
STOR T1,SBSPC,(SB) ;SAVE COUNT
RET
SUBTTL Slot Processing Routines - STOP Slots
;HSSTOP - Process a STOP Slot.
;
;Call: SB/ Slot block address
; CALL HSSTOP
; Error return -
; Normal return
HSSTOP: SKIPN T2,SLTDID ;Destination ID cannot be zero
CALLRET IGNSLT ; but if it is zero, just ignore it.
SKIPN SLTSID ;Source ID must be zero
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
IFN FTOPS10,<
LOAD U,SBTDB,(SB) ;Get line data block address
>;end IFN FTOPS10
IFN FTOPS20,<
LOAD T2,SBTDB,(SB) ;Terminal dynamic data base
DYNST ;Get the line number
>;end IFN FTOPS20
NXSLCH:
IFN FTOPS20,<
OPSTR <ILDB T1,>,UNBFA,+UNB.OF(RB);Get a character.
CALL TTCHI ;Call TTYSRV to enter into input buffer
TRN ;Ignore error
>;end IFN FTOPS20
IFN FTOPS10,<
OPSTR <ILDB T3,>,UNBFA,+UNB.OF(RB);Get a character.
CALL RECPTY ;Call SCNSER to enter into input buffer
>;end IFN FTOPS10
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
OPSTRM <AOS>,HNCON,(HN) ;Count one more active connect
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:
IFN FTOPS20,< 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
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.
>;end IFN FTOPS20
IFN FTOPS10,<
OPSTR <SKIPN U,>,SBTDB,(SB) ;Is there a terminal data base
JRST RELSB0 ;No, go just release the SB resources
PUSH P,J ;NGVLDB blows J (our W2)
CALL NGVLDB ;Give the LDB back to SCNSER
POP P,J ;Get our AC back
>;end IFN FTOPS10
RELSB0: OPSTR <XMOVEI T1,>,CBSBQ,(CB) ;Point to the CB SBQ header
MOVE T2,SB ; and
CALL LAUNQ ; release the SB from this queue
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.
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
OPSTRM <SOS>,HNCON,(HN) ;One less connected session
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:
IFN FTOPS20,<
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
>;end IFN FTOPS20
IFN FTOPS10,<
; Get a remote LDB and make it mine
;
;
; Return:
; RET ;FAILED
; RETSKP ;SUCCESS
;
SAVEAC <W> ;SCNSER/NETSER WILL USE THIS
;Allocate an LDB
MOVSI T1,LTLLAT ;FLAG A NEW LAT TERMINAL
MOVEI T3,LATDSP ;LAT'S ISR DISPATCH TABLE
CALL GETLDB ;GET A REMOTE TTY LDB, POINTER IN U
RET ;GO ANNOUNCE THE FAILURE
;Finish making this a LAT LDB
MOVEI T1,LATRTQ ;GET POINTER TO OUTPUT Q HEADER
HRLZM T1,LDBQUH(U) ;STORE FOR SCNSER (TOPOKE/TOTAKE)
STOR U,SBTDB,(SB) ;REMEMBER OUR LDB
MOVEM SB,LDBLAT(U) ;STORE SB PTR IN LDB TOO
MOVEI T1,APCLAT ;GET LATSER CODE
DPB T1,LDPAPC ;SET ASYNC PORT CHARACTERISTIC
MOVEI T1,M.LIDL ;GET MAX. IDLE TIME FOR LAT LINES
CALL SCNADT ;SET THE TIMER GOING
LDB T1,LDPXNF ;Find out our XON/XOF setting
STOR T1,SBXNF,(SB) ;And make LAT's setting agree (we will also
;send a DATA_B slot to inform LATbox).
;Initialize the TTY with .HELLO force command
CALL TTFGRT ;GO FORCE .HELLO/.RESTART
RETSKP ;SUCCESS RETURN
SUBTTL Subroutines -- NGVLDB - Give an LDB back to SCNSER
;NGVLDB - Give an LDB back to SCNSER
;
; Call: U/ Pointer to LDB
;
; Return:
; RET ;ALWAYS
NGVLDB: SETZM LDBLAT(U) ;CLEAR OUT BY-NOW-STALE POINTER
MOVEI T1,IONND% ;"NODE DOWN" (PATH LOST/ETC) ERROR
CALL DETLDB ;DETACH THE LDB/DDB
CALLRET FRELDB ;RETURN LDB TO SCNSER FREE POOL
>;end IFN FTOPS10
SUBTTL Slot Multiplexor - Main Loop
;LAMUX Slot Multiplexor Main Loop
;
;Call: CALL LAMUX
; Normal return
;
LAMUX:
IFN FTOPS10,<
CALL LATSCN ;Scan active LDBs
>;end IFN FTOPS10
LOAD CB,QLFWD,+HN.QAC(HN) ;Get the first CB on the active queue
LANXCB:
IFN FTOPS20,< JUMPE CB,RTN ;All CBs processed, return>
IFN FTOPS10,< JUMPE CB,CHKWFB ;All CBs processed, check if we need buffers>
IFN FTOPS20,<
SKIPL HN.RUN(HN) ;Is the NI still running?
IFSKP.
CALL NIHALT ;No, so deallocate all resources
JRST MXNXCB
ENDIF.
> ;END IFN FTOPS20
SETZM NSLOTS ;Initialize number of slots in this msg
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
JRST MXNXCB] ;Kill it and look at next one.
LOAD T1,HNLAS,(HN) ;Get the current access state.
CAIN T1,LS.OFF ;If 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 not 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: AOS T1,CB.KAF(CB) ;Count keep-alive timer
HLRZ T2,T1 ;Get limit value of timer
HRRZS T1 ; and isolate timer part
CAML T1,T2 ;Have we hit limit yet?
MXNXC2: CALL LCLHLT ;Yes, we have not heard from the server
JRST MXNXCB ;in a long time, so kill the circuit.
SUBTTL
;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
SETZM MD.NXT+MDB.OF(XB) ;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.
MOVE T3,MD.NXT+MDB.OF(XB) ;Get MSD address
STOR T1,MDBYT,(T3) ;and store count 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 - 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 - 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 - 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>
IFN FTOPS10,<
LOAD U,SBTDB,(SB) ;Get address of LDB.
;We will carry this in U for a while
>;end IFN FTOPS10
OPSTR <SKIPN>,SBXCR,(SB);Do we still have any transmit credit?
IFSKP.
MOVEI W1,SZ.XBF-SZ.SHD;Get the residual buffer (minus slot header)
OPSTR <SUB W1,>,UNBSZ,+UNB.OF(XB); count.
IFN FTOPS20,<
LOAD T2,SBTDB,(SB) ;Get address of TDB
CAMLE W1,TTOCT(T2) ;Smaller of residual count - 4 and TTOCT
MOVE W1,TTOCT(T2) ;TTOCT was smaller
>;end IFN FTOPS20
OPSTR <CAMLE W1,>,SBMDS,(SB) ;Compare with maximum DATA_A slot size
LOAD W1,SBMDS,(SB) ;Maximum allowed by remote is less.
ELSE.
MOVEI W1,0 ;No transmit credits so can't send data.
ENDIF.
IFN FTOPS20,<
; 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
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.
OPSTRM <ADDM W1,>,UNBSZ,+UNB.OF(XB);Update the total message count.
JRST XSLDA1 ;Enter output data into slot if there is any.
XSLDA0: LOAD T2,SBTDB,(SB) ;Dynamic TT data base
CALL TTSND ;Get a character from the buffer
RET ;Probably PAGE STOP
OPSTR <IDPB T1,>,UNBFA,+UNB.OF(XB);Enter character into buffer
XSLDA1: SOJGE W1,XSLDA0 ;Continue if more characters
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: LOAD T2,SBTDB,(SB) ;Get the TDB address back
SKIPE TTOCT(T2) ;If there is more data to output, return
RETSKP ; successfully. 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
SKIPE TTOCT(T2) ;If there is no terminal output, clear
RET ; the TTOTP bit.
SETZRO TTOTP,(T2) ; ...
RET ; ...
>;end ifn ftops20
IFN FTOPS10,<
; 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, 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. We don't know
; at this point how many characters SCNSER has for us to send, so just
; keep trying to get chars until we fill the slot or run out of
; characters
;
; at this point:
; W1/ max characters we can send
;
MOVNS W1 ;Make into
HRLZS W1 ;AOBJN word
;
; now:
; W1/ -max,,0
;
JUMPE W1,NOCHR ;We don't have credit to send. Don't get a char
SKIPGE SB.TTO(SB) ;Is there saved character? (SBTTO is sign bit)
JRST GOTCHR ;Yes
CALL XMTCHR ;No, try to get a character from SCNSER
JRST NOCHR ;SCNSER doesn't have any for us.
SETONE SBTTO,(SB) ;SCNSER does have one, remember we got it
STOR T3,SBCHR,(SB) ;and save it away
GOTCHR: AOBJN W1,.+1 ;Got a char, count it
NOCHR: CALL CRECHK ;Check if we can grant more credit remote
TRNE W1,777777 ;Can't or don't need to. See if any data?
SKIPA ;Got data or can grant credit, send a slot
RET ;No slot to send, just return
MOVE W2,T1 ;Save CRECHK result
CALL XBFCHK ;Do we have a transmit buffer yet?
RET ;No, and can't get any.
LOAD T1,SBXCR,(SB) ;Get our transmit credit count
SOS T1 ;Decrement since we may consume one credit now
TRNE W1,777777 ;If data count is not zero, we will definitely
STOR T1,SBXCR,(SB) ;Store updated credit count
;
;Can't build slot header yet, since we don't know how many characters
; we will send. Save a pointer to the slot character count, and
; call BSLTHD anyway. We will stuff the char count later.
;
;At this point, the slot byte pointer points to the DST_SLOT_ID
;
MOVEI T1,3 ;the byte count is the 3rd byte of the slot
OPSTR <ADJBP T1,>,UNBFA,+UNB.OF(XB) ;so make a DPB pointer to it
PUSH P,T1 ;save first word of byte pointer
PUSH P,T2 ;and possible second word
;
;Now build the slot header
;
MOVE T1,W2 ;get CRECHK results
LSH T1,^D8 ;Shift to proper position
IORI T1,<ST.DTA_^D12> ;fill in the flag
CALL BSLTHD ;build the slot header (with bad char count)
TRNN W1,777777 ;Is there some data?
JRST XSLDA2 ;No, no characters to move
LOAD T3,SBCHR,(SB) ;Yes, get saved character.
SETZRO SBTTO,(SB) ;And remember we got it.
OPSTR <IDPB T3,>,UNBFA,+UNB.OF(XB) ;Stuff first character into buffer
JUMPGE W1,XSLDA2 ;if (heaven help us) there was only space for
;one character, stop now.
XSLDA0: CALL XMTCHR ;Get a character from SCNSER
JRST XSLDA2 ;None left
OPSTR <IDPB T3,>,UNBFA,+UNB.OF(XB) ;Stuff a character into buffer
AOBJN W1,XSLDA0 ;Count characters done
;
; Here we have either filled up the slot or run out of characters.
; Stuff the actual character count into the slot header.
;
XSLDA2: POP P,T2 ;retrieve pointer to slot header
POP P,T1 ; byte count
DPB W1,T1 ;and stuff actual character count
HRRZ T1,W1 ;get character count by itself
OPSTRM <ADDM T1,>,UNBSZ,+UNB.OF(XB) ;and update total message count.
OPSTRM <ADDB W2,>,SBRCR,(SB) ;Update his credit count. (remember total)
JUMPE W1,RSKP ;If we don't have any transmit credits, or
SKIPE W2 ;If the remote has zero credits for transmits
SKIPL LDBDCH(U) ;Or if SCNSER doesn't think line is idle
.CREF LDLIDL ;(really checking this bit)
RETSKP ;Return successfully without clearing request
MOVX T1,SBOUT ;Otherwise, mark this request as complete.
CALL CSBDAV
RETSKP
SUBTTL Slot Formatting Routines
;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 updated credit count
IFN FTOPS10,<
LOAD U,SBTDB,(SB) ;Point to LDB
>;end IFN FTOPS10
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
MOVEI T1,SL.DXF ;Assume disabling XOFF recognition
IFN FTOPS20,<
LOAD T2,SBTDB,(SB) ;Get the terminal data base address
TMNE TT%PGM,TTFLGS(T2)
>;end IFN FTOPS20
IFN FTOPS10,<
LOAD U,SBTDB,(SB) ;Get LDB address
LDB T2,LDPXNF ;Get XON/XOF flag
SKIPE T2 ;disabling XON/XOF recognition?
>;end IFN FTOPS10
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 them 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,
; 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.
IFN FTOPS20,<
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
>;end IFN FTOPS20
IFN FTOPS10,<
MOVEI T1,TTIWRN ;Get max characters for an input buffer
SUB T1,LDBTIC(U) ;Minus number that have been echoed
SUB T1,LDBECC(U) ;Also minus number that have not yet echoed
>;end IFN FTOPS10
IDIVI T1,MXSLSI ;Compute number of slot buffer that would fit
IFN FTOPS10,<
JUMPE T1,RTN ;None, so can't grant more.
>;end IFN FTOPS10
IFN FTOPS20,<
JUMPE T1,[SETONE TTFWK,(T4);None, so can't grant more so force
JRST RTN] ; the process to wake.
>;end IFN FTOPS20
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
; T3/ MSD address (also saved in MD.NXT+MDB.OF(XB))
GETXBF: MOVEI T1,XBFSIZ ;Buffer length to get
IFN FTOPS20,<CALL MMGTZW>
IFN FTOPS10,<CALL GETZBF> ;Go get the storage
RET ;No luck!
MOVE T3,T1
SETZ T1,
MOVX T2,<POINT 8,0>
STOR T2,MDAUX,(T3);Byte pointer to message
MOVEI T1,SZ.XBF
STOR T1,MDBYT,(T3);Zero length
MOVEI T1,VMC.XC ;Always use exec virtual paging
STOR T1,MDVMC,(T3); ...
XMOVEI T1,<MD.LEN>(T3) ;
MOVEM T3,MD.NXT+MDB.OF(XB) ;Link header MSD to this
STOR T1,MDALA,(T3) ;Message address
RETSKP
;XBFCHK - Check for Transmit buffer
;
;Call: XB/
; CALL XBFCHK
; Error Return
; Normal Return
XBFCHK: SKIPE T3,MD.NXT+MDB.OF(XB) ;If we already have one,
RETSKP ;return
CALL GETXBF ;Get an MSD transmit buffer
RET ;None available, return failure
XMOVEI T1,<MD.LEN>(T3) ;Make a one word 8 bit
TLO T1,(<OWGP. 8,0>) ;Global byte pointer and save as
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: MOVEI T1,0
EXCH T1,MD.NXT+MDB.OF(XB);If there is no MSD style buffer
JUMPE T1,RTN ; then just return
IFN FTOPS20,<CALLRET MMFREE>
IFN FTOPS10,<CALLRET GIVBF> ; and free the MSD's storage.
;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: 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,RBFSIZ ;Receive buffer size
IFN FTOPS20,<CALL MMGTZW>
IFN FTOPS10,<CALL GETZBF> ;Get the number of required buffers
RET ;Can't, return error.
CALL RBPOSS ;Post the receive buffer to NISRV
IFN FTOPS10,<CALLRET GIVBF>
IFN FTOPS20,<CALLRET MMFREE> ;Couldn't post it so free it.
OPSTRM <AOS>,HNNRB,(HN);Increment the number of receive buffers
RETSKP ;Return success
;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.
IFN FTOPS10,<CALL GIVBF>
IFN FTOPS20,<CALL MMFREE>
OPSTRM <SOS>,HNNRB,(HN) ;Reduce the number of receive buffers
RET
IFN FTOPS10,<
SUBTTL SCNSER DEVICE DEPENDANT ROUTINES
;
; TOPS10 does not call a device dependant routine for each output
; character. Nor does it call a routine to change TTY PAGE.
; At each scheduler interval, we scan all LDBs in LATSER's queue
; to see what must be done with each one.
;
LATSCN: MOVEI T1,LATRTQ ;Get SCNSER's queue header
CALL TOTAKE ;Get next active LDB on the chain
RET ;None. Just return.
SKIPN SB,LDBLAT(U) ;Get slot block address
RET ;None. This guy's a loser so punt.
MOVEI T1,0 ;T1 will get Slot flags
MOVEI T3,L1RCHP
TDNE T3,LDBBYT(U) ;Need to change parameters?
PUSHJ P,LATCHP ;Yes. See what needs change and set T1
TXO T1,SBOUT ;Always set flag for output. Check later anyway
;
; Here if we MIGHT have characters to output, or need to change TTY PAGE
;
MOVE CB,SB.CBA(SB) ;And associated circuit block address
CALL SSBDAV ;And set this slot's flags
JRST LATSCN ;Look at next LDB in the queue
;
;Routine to see if we need to change recognition of XON/XOFF
;
;Call: U/ LDB address
;
;Return: Flag SBFCC set in T1 if we need to change
;
LATCHP: ANDCAM T3,LDBBYT(U) ;Turn off CHP bit to show we checked
LDB T2,LDPXNF ;Get TTY PAGE bit
LOAD T3,SBXNF,(SB) ;Get bit that says what the LAT knows
CAMN T2,T3 ;Are they already the same?
RET ;Yes, no need to send a message, then
TXO T1,SBFCC ;Different. Need to send a message
STOR T2,SBXNF,(SB) ;AND remember what we are now.
RET
>;end IFN FTOPS10
ENDAV.
IFN FTOPS20,<
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
>;end IFN FTOPS20
IFN FTOPS10,<
SUBTTL More SCNSER device dependant routines
;Routine LTHNGU - LAT "disconnect" function
;Called by SCNSER thru ISRREM entry point in dispatch vector with
; a function code if IRRDSC in T3
;
; U/ LDB address
; T3/IRRDSC
; CALL LTHNGU
; Returns +2 always (note: there is no "error" return)
;
LTHNGU: MOVX T1,SBSTO ;Yes, we want to stop this slot
CALL LTSSDC ;SO do it
RETSKP ;And indicate success to SCNSER
;
;Routine LATREM
;Called from SCNSER through dispatch vector element ISRREM (7)
;
; U/ LDB address
; T3/ Function code
; Possible values are:
; (1) Input buffer low (ignored)
; (2) Input character not stored (ignored)
; (IRRSCG) Eat all output characters
; (IRRDSC) Disconnect LAT terminal
; (IRRTMO) Disconnect idle terminal
;
LATREM: CAIN T3,IRRDSC ;Is the the "disconnect" function?
CALLRET LTHNGU ;Yes, do it.
CAIN T3,IRRTMO ;Is it the "timeout" function?
CALLRET LTHNGU ;Yes, do it.
CAIE T3,IRRSCG ;Is it "send character gobbler"?
RET ;No, those are all we know about
LTCOBF: MOVX T1,SBFOU ;User hit ^O, tell remote to gobble output
LTSSDC: ACVAR <HN,XB,RB,CB,SB,W1,W2>
ETHLOK ;Interlock against races
SKIPN SB,LDBLAT(U) ;Get slot data base from LDB
JRST LTSSD0 ;Not there any more, ignore
MOVE CB,SB.CBA(SB) ;Get circuit data base from slot block.
CALL SSBDAV
LTSSD0: ETHULK ;Release interlock
RET
>;end IFN FTOPS10
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
IFN FTOPS20,<
CALL LGTAD ;Get internal time and date
>;end IFN FTOPS20
IFN FTOPS10,<
MOVE T1,DATE ;get TOPS-10 time and date
>;end IFN FTOPS10
ANDI T1,777777 ;Time only
IDIV T1,T4 ;MODULO max, (which is the remainder in T2)
SKIPN T1,T2 ;GET remainder
MOVEI T1,1 ;But if it's zero, make it 1
RET
ENDAV.
DYNRAT:
IFN FTOPS20,< FIXR T1,RJAV+2 ;15 minute load average>
IFN FTOPS10,< MOVEI T1,1 ;FOO>
RET
SUBTTL LAGTCR - LAT memory management routines
;
; Subroutine to tell COMMON how much core LAT needs at ONCE time
;
;Call: CALL LAGTCR
;
;Return: +1 always with T1/ size of memory LAT needs, which is:
;
; Space for 1 outstanding receive buffer +
; for each possible open circuit: MAXXBF transmit buffers
; + 1 receive buffer
; MAX.(BUFSIZ,<RBFSIZ,XBFSIZ>) ;BUFSIZ is size of largest buffer
; BUFSIZ==BUFSIZ+QL.LEN ;we need + 2 words for linking list
BUFSIZ==PAGSIZ## ;Oh, the hell with it! receive buffers have
;to be contiguous, so don't bother to
;split a buffer across a page boundary.
;Luckily, the actual size of a buffer
;is slightly smaller than a page.
;
LAGTCR::
MOVEI T1,<BUFSIZ*NBUFS>
RET
;
; Routine to ask for more memory from user free core
; We should get it a tick or two later.
;
CHKWFB: SKIPE T1,LATWFB ;Do we want any more buffers?
SKIPE LATRFB ;And have we not yet aksed for them?
RET ;No to either, don't get memory now.
;
; Here we have finally decided it's our turn to ask for some memory
;
IMULI T1,BUFSIZ ;Calculate how many words
TRZE T1,PG.BDY## ;Round up to page bound
ADDI T1,PAGSIZ##
MOVEM T1,LATRFB ;Remember how many words we asked for.
LSH T1,W2PLSH## ;Convert to pages
HRLI T1,(MS.SCA) ;Section to allocate from
JSP T2,GETCLP## ;Ask for the space and wait for it
;T2 contains callback routine address
;
; CHKRFB - Routine called when VMSER gave us memory
; If so, link it into free buffer list
;
CHKRFB: MOVE T4,T1 ;Get address of start
SETZ T1, ;Get a zero
EXCH T1,LATRFB ;Get number of pages we wanted (and clear it)
IDIVI T1,BUFSIZ ;Convert to number of buffers
ADDM T1,LATNFB ;Indicate that we've got more
MOVE T2,T4 ;Get address of first buffer
MOVE T4,T1 ;save buffer count
ALCBUF: MOVEI T1,LATFRE ;QUEUE header for LAT free core
CALL QUE1WB ;Link this buffer to the list.
ADDI T2,BUFSIZ ;Point to next one.
SOJG T4,ALCBUF ;Until all buffers have been allocated.
RET
;
; GETZBF - Routine to get a receive or transmit buffer and zero it.
;
;Call: T1/ required size
;Return: +1 failure
; +2 success T1/ address of buffer
;
GETZBF: PUSH P,T1 ;Save size wanted
MOVEI T1,LATFRE ;Address of queue header for free pool
CALL UNQ1WF ;Get a buffer
JRST TPOPJ ;Couldn't, restore T1 and return.
SOS LATNFB ;One less free buffer
PUSH P,T2 ;SAVE ADDRESS OF BUFFER
XMOVEI T3,1(T2) ;POINT TO SECOND WORD
SETZM (T2) ;ZERO FIRST WORD
MOVEI T1,BUFSIZ-1 ;SET UP TO ZERO THE WHOLE BLOCK
XBLT. T1 ;DO IT
POP P,T2 ;RESTORE ADDRESS
POP P,T1 ;Restore size
HRLI T1,'LAT' ;check word
MOVEM T1,(T2) ;save <'LAT',,size> as first word
HRRZ T3,T1 ;Isolate size
ADD T3,T2 ;Point to end of buffer
HRLI T1,'END'
MOVEM T1,1(T3)
XMOVEI T1,1(T2) ;POINT to address of data portion
RETSKP
;
; GIVBF - Routine to give back a buffer to free pool
;
;Call: T1/ address of data portion of buffer
;Return: +1 always
;
GIVBF: SUBI T1,1 ;Point to size word
HLRZ T2,(T1) ;Get check code
HRRZ T3,(T1) ;Get size
ADD T3,T1 ;Point to near end of buffer
HLRZ T3,1(T3) ;Get ending check code
CAIN T3,'END' ;See if either end has been wiped out
CAIE T2,'LAT' ;Is it still there?
BUG. (CHK,LATMEM,LATSER,SOFT,<LAT buffer overwritten>,<<T1,ADDRESS>>,<
While trying to return a buffer, LATSER discovered it had been overwritten.>)
MOVE T2,T1 ;Now return the buffer
MOVEI T1,LATFRE ;To the queue of free buffers
AOS LATNFB ;Count one more buffer available
CALLRET QUE1WB ;For re-use
MMGTZW:
IFN FTOPS20,<
CALL DNGWDZ
RET
RETSKP
>;END IFN FTOPS20
IFN FTOPS10,<
MOVEI T2,1(T1) ;Going to ask for 1 more word for check
PUSH P,T2 ;save requested size
CALL GETEWZ ;Get zeroed core from ethernet free pool
JRST TPOPJ ;No luck
POP P,T2 ;Get back requested size
ADDI T1,1 ;Point to data portion of block
MOVEM T2,-1(T1) ;save length as first word of block
RETSKP ;And return it to caller
MMFREE:
IFN FTOPS20,<
CALL DNFWDS
RET
>
IFN FTOPS10,<
XMOVEI T2,-1(T1) ;Point to block-1
HRRZ T1,(T2) ;Which contains length of block
CALLRET GIVEWS ;and give it back to ethernet free pool.
;
; Subroutine to convert SIXBIT nodename to ASCII
;
;Call: T1/ SIXBIT node name
;
;Return: +1 always with ASCII in OURNAM, OURNAM+1
; and character in OURCNT
;
CVTNOD: SETZM OURCNT ;No characters yet
SETZM OURNAM ;Or name
SETZM OURNAM+1 ; ...
JUMPE T1,RTN ;Done if no characters left
MOVE T3,[POINT 7,OURNAM] ;Where to put node name
CVTNO1: SETZ T2, ;Zap the temp AC
ROTC T1,6 ;Get next character into T2
ADDI T2,40 ;Convert to ASCII
AOS OURCNT ;Count characters done
IDPB T2,T3 ;Put the character into the name
JUMPN T1,CVTNO1 ;Continue
RET ;Done
XLIST
LIT
define .xcrf1(syms),<
irp syms,<purge syms>>
.xcmsy ;Purge some stupid MACSYM symbols
PRGALL ;Purge all ..nnnn symbols
>;end IFN FTOPS10
END