Trailing-Edge
-
PDP-10 Archives
-
BB-X116A-BB_1984
-
llinks.mac
There are 21 other files named llinks.mac in the archive. Click here to see a list.
;DNET:LLINKS.MAC[10,36,MON,NEW], 02-Nov-1983 12:39:56, Edit by TARL
;MCO 11035 - Don't send link service until we have received an ACK for CC.
;DNET:LLINKS.MAC[10,36,MON,NEW], 22-Sep-1983 21:02:46, Edit by TARL
;MCO 10971 - Grand Re-merge of the -10/-20 sources
;TITLE LLINKS - Network Services Layer of Phase III DECnet V042
SUBTTL W. G. Nichols
SEARCH D36PAR,SCPAR,MACSYM
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE ONLY USED
; OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
IFN FTOPS10,<
.CPYRT<
COPYRIGHT (C) 1984 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.
>
> ;END IFN FTOPS10
ENTRY NSP,NSPRTR,NSPINI,NSPJIF ;The entry points,
ENTRY NSPCGT,NSPCGR ; see procedures for calling sequence
SALL
; The NSP link block definitions are in SCPAR so that they will
; be available to D36SPY and similar reporting programs.
FTTROLL==0 ;GIVE A NAME TO TROLL CODE
IFN FTOPS10,<
TITLE LLINKS - Network Services Layer of Phase III DECnet
SEARCH F,S
>
IFN FTOPS20,<
SEARCH PROLOG
TTITLE (LLINKS,,< - Network Services Layer of Phase III DECnet>)
>
D36SYM ;Fix up problems with MACSYM in DECnet-36
$RELOC ;Set up code and data segments according
$HIGH ; to FTOPSn0, macros defined in D36PAR
;This module implements the NSP layer of Phase III DECnet. It is the
;system independent logical link service. Below NSP is the Router
;layer, which handles node-to-node communication. Above NSP is the
;Session Control layer, which provides the interface between the
;logical links of NSP and the user program.
;
;The interfaces between NSP and its neighbors are via call vectors, so
;that by changing the base of the call vector, a logical link may use
;a different module at the NSP level of the DECnet hierarchy. Eg, a
;link may want to use APRAnet datagram service instead of DECnet
;logical link service.
SUBTTL Table of Contents
; Table of Contents for LLINKS
;
;
; Section Page
; 1. Table of Contents. . . . . . . . . . . . . . . . . . . 2
; 2. Register Definitions . . . . . . . . . . . . . . . . . 3
; 3. EXTERN Declarations. . . . . . . . . . . . . . . . . . 4
; 4. NSP Network Protocol Definitions . . . . . . . . . . . 11
; 5. Code-Generating Macros . . . . . . . . . . . . . . . . 13
; 6. Resident Storage Allocation. . . . . . . . . . . . . . 15
; 7. NSP's Entry Vector Table . . . . . . . . . . . . . . . 17
; 8. NSPRCV - Receive a Message from Router . . . . . . . . 18
; 9. NSPRTR - The Entry Point from Router . . . . . . . . . 20
; 10. NSPINI - Initialization Entry Point. . . . . . . . . . 21
; 11. NSP Interlock Handlers
; 11.1. NSPLCQ - Queue on Failure . . . . . . . . . . 22
; 11.2. NSPLCW - Wait on Failure. . . . . . . . . . . 23
; 11.3. NSPLCF - Flags set for Failure. . . . . . . . 24
; 12. Session Control Calls
; 12.1. NSP - The Entry Point . . . . . . . . . . . . 26
; 12.2. NSPOPN - Open a Link. . . . . . . . . . . . . 27
; 12.3. NSPGOL - Set New Data Request Goals . . . . . 29
; 12.4. NSPACT - Enter Active . . . . . . . . . . . . 30
; 12.5. NSPACC - Accept Connect . . . . . . . . . . . 31
; 12.6. NSPSEG - Send Data. . . . . . . . . . . . . . 33
; 12.7. NSPDRQ - Data Request Call. . . . . . . . . . 35
; 12.8. NSPREJ - Reject a Connection. . . . . . . . . 36
; 12.9. NSPDSC - Synch Disconnect . . . . . . . . . . 38
; 12.10. NSPABO - Abort Link . . . . . . . . . . . . . 39
; 12.11. NSPCLS - Close Port . . . . . . . . . . . . . 40
; 13. CLSABO - Send a free "Abort by Object" . . . . . . . . 41
; 14. PROCXQ - Process the Transmit Queue. . . . . . . . . . 42
; 15. SCNIRS - Tell Session Control Port is Not in Run State 45
; 16. SNDRTR - Send a Message to Router. . . . . . . . . . . 46
; 17. ROUTER Calls
; 17.1. NSPRCV - Receive a Message from Router. . . . 48
; 17.2. NSPODN/NSPOND - Output Done/Not Done. . . . . 49
; 17.3. NSPRTS - Return a Message to Sender . . . . . 50
; 18. Message Receivers
; 18.1. RCVPRC - Preliminary Processing . . . . . . . 51
; 18.2. PRCACK - Process Received ACKNUM Field. . . . 53
; 18.3. Receive a Data Segment. . . . . . . . . . . . 55
; 18.3.1. Check Length of Q. . . . . . . . . . 58
; 18.4. PROCRQ - Process the Receive Queue. . . . . . 59
; 18.5. RCVLKS - Receive a Link Service Message . . . 61
; 18.6. RCVACK and RCVNOP - Little Ones . . . . . . . 66
; 18.7. RCVCA - Receive a Connect ACK Message . . . . 67
; 18.8. RCVCI - Receive a Connect Initiate Request. . 68
; 18.9. RCVCC - Receive a Connect Confirm Message . . 70
; 18.10. RCVDI - Disconnect Initiate Message . . . . . 72
; 18.11. RCVDC - Disconnect Confirm Message. . . . . . 74
; 19. Clock-Driven Routines. . . . . . . . . . . . . . . . . 76
; 20. CLCXDQ - Calculate Transmit Data Requests. . . . . . . 81
; 21. NSPRJF - Request Jiffy Service . . . . . . . . . . . . 84
; 22. SECCHK - Once-a-Second Checks. . . . . . . . . . . . . 85
; 23. Memory Manager Calls
; 23.1. NSPCGT - We're Congested. . . . . . . . . . . 91
; 23.2. NSPCGR - Congestion is Relieved . . . . . . . 92
; 24. NSIRLV - Congestion-Relieved Processor . . . . . . . . 93
; 25. PESFLO - Put Link in Pessimistic Flow Control. . . . . 94
; 26. SCLOSE - Start the Close Process . . . . . . . . . . . 96
; 27. SENDRQ - Send a Link Service Message . . . . . . . . . 103
; 28. SACKMG - Get a Message Block and send ACK message if can 105
; 29. SNDACK - Check a sublink to see if it needs an ACK sent 106
; 30. SENDDI - Send a DI Message . . . . . . . . . . . . . . 107
; 31. SNDCAK - Send a Connect ACK Message. . . . . . . . . . 108
; 32. SNDDSC - Send a Disconnect Complete Message. . . . . . 109
; 33. MGACKD - A Message has been ACKed. . . . . . . . . . . 110
; 34. SENDNL - Send a No Link Message. . . . . . . . . . . . 111
; 35. PTIRUN - Put a Port in Run State . . . . . . . . . . . 113
; 36. MAKPRT - Make a New Port Block . . . . . . . . . . . . 114
; 37. GTRESP - Get a Reserved Port . . . . . . . . . . . . . 115
; 38. MAKPRS - Make this Port Reserved . . . . . . . . . . . 116
; 39. SETPRT - Make a New Port Block . . . . . . . . . . . . 117
; 40. FNDPRT - Hash an LLA to Find a Port Block. . . . . . . 118
; 41. Hash Table Routines. . . . . . . . . . . . . . . . . . 119
; 42. NEWLLA - Make a New Local Link Address . . . . . . . . 122
; 43. NEWSGN - Make a New Message Segment Number . . . . . . 123
; 44. NSPpid Control - GETPID & FNDPID . . . . . . . . . . . 124
; 45. DSTPRT - Destroy a Port. . . . . . . . . . . . . . . . 126
; 46. QSRTMB - Queue a Message on a Sorted Queue . . . . . . 127
; 47. MAKHDR - Initialize the NSP MSD to build a Message Header 129
; 48. UPDELAY - Update a Logical Link's Roundtrip Delay. . . 130
; 49. Free a Message Block . . . . . . . . . . . . . . . . . 131
; 50. Reserved Port Management . . . . . . . . . . . . . . . 132
; 51. Reserved Port Process. . . . . . . . . . . . . . . . . 133
; 52. NSPEVT - Queue an Event to Network Management. . . . . 136
; 53. Trace-to-TTY Facility. . . . . . . . . . . . . . . . . 139
; 54. End of Program . . . . . . . . . . . . . . . . . . . . 142
SUBTTL Register Definitions
;Registers T1 through T6 and P1 through P2 are defined in D36PAR for
;all of DECnet-36. Register P4 is redefined as MS for
;the DNxyBY routines in D36COM. Register MB is defined in D36PAR for
;NSP and Router only. The registers defined below are additions
;used in NSP only. The registers FREEn are defined in D36PAR to be
;registers not otherwise used in DECnet-36.
MB=MB ;THESE ARE DEFINED AS .NODDT GLOBALS
CX=CX ; IN THE UNIVERSAL, CHANGE THAT HERE
EL=FREE1 ;POINTER TO THE CURRENT PORT BLOCK
ES=FREE2 ;POINTER TO THE CURRENT SUBLINK BLOCK
SUBTTL EXTERN Declarations
EXTERN RTN ;NON-SKIP RETURN LABEL
EXTERN RSKP ;SKIP RETURN LABEL
;The Interlock interface
IFN FTOPS10,<
EXTERN NSPLOK ;INTERLOCK WORD
EXTERN NSPLKO ;INTERLOCK OWNER
>
;The Router interface
; T1/ Flags, see RT%RQR and RT%ODN in D36PAR.MAC
; MB/ Pointer to the message block
EXTERN RTRXMT ;SEND TO ROUTER
; Normal Return
;
;
; T1/ My Node Number
; T2/ NSP's entry address (call vector base)
; T3/ Flags, see RT%PH2 in D36PAR.MAC
EXTERN RTRINI ;INITIALIZE ROUTER
; Normal Return
;
;
EXTERN RTRADR ;MY NODE NUMBER
;The Session Control interface
; T1/ NSPpid for port just created
; T2/ See BEGSTR IA in D36PAR
; T3/ ignored
; T4/ Message Block
EXTERN SCTL ;NORMAL INTERFACE, CALLED VIA @EL.SCV(EL)
EXTERN SCTLCI ;THIS ENTRY USED ONLY FOR CI MESSAGES
; Normal Return ;ALL OTHER ENTRIES CALL @EL.SCV(EL)
; SEE PROCEDURES NSPOPN AND NSPACC
;
; T1/ SCTL Port id (ELSCB)
EXTERN SCTRIB ;RESERVE INPUT BLOCK
; Error Return ; NO BLOCK AVAILABLE
; Normal Return ;ONE BLOCK RESERVED
;
; No arguments
EXTERN SCTUCG ;CALL THIS ON MEMORY UNCONGESTION
; Normal Return
;The Network Management Interface
; T1/ Network Node Address of node
EXTERN FNDNOD
; Error Return if no resources
; Normal Return with pointer to node block in T1
;FNDNOD will try to find an existing node block, if it can't it
;will make one.
; T1/ Ptr to arg block with structure NE in it
EXTERN NMXEVT ;EVENT CATCHER
; Normal Return
;The following procedures manipulate message bytes. They all have the
;following calling convention:
;
; MS/ Pointer to Message Segment Descriptor (MSD)
; T1/ Data to be written/Data returned
;
;The put-byte routines do not skip return.
;
;The get-byte routines non-skip return if byte count is zero when
;they are called, else they skip return.
EXTERN DNP1BY ;Put a single byte
EXTERN DNP2BY ;Put a double byte
EXTERN DNPEBY ;Put an extensible byte
EXTERN DNG1BY ;Get a single byte
EXTERN DNG2BY ;Get a double byte
EXTERN DNGEBY ;Get an extensible byte
EXTERN DNGSBY ;Get a byte, skip if not extensible
; T1/ Number of bytes to back up
EXTERN DNBKBY ;Back up (T1) bytes in MS's MSD
; Normal Return
; T1/ Pointer to an MSD
EXTERN DNPINI ;Init MS for DNPxBY
; Normal Return
; T1/ Pointer to an MSD
EXTERN DNGINI ;Init MS for DNGxBY
; Normal Return
; T1/ Pointer to an Message Block
EXTERN DNLENGTH ;Return sum of lengths (bytes) of
; Normal Return ; text in all MSD(s) in T1
; T1/ Pointer to an MSD
EXTERN DNSLNG ;Return length (bytes) of
; Normal Return ; text in MSD(s) in T1
; MS/ Pointer to an MSD
EXTERN DNRPOS ;Read current MSD position
; Normal Return with position value in T1
; MS/ Pointer to an MSD
; T1/ Value returned from DNRPOS
EXTERN DNGPOS ;GOTO a position in this MSD's text
; Normal Return
;Continued on Next Page
;Continued from Previous Page
EXTERN DCNCON ;NON-ZERO IF SYSTEM IS CONGESTED
;
;
;Message and message segment allocation routines
;
; T1/ Length in bytes of message segment to go on UDMSD
EXTERN DNGMSG ;GET A DECnet-36 MESSAGE BLOCK
; Error Return if no resources
; Normal Return with pointer in T1
;
;
;Clear a message block, this is like deallocating and reallocating
;without fear of losing the block.
; T1/ Pointer to the block
; T2/ Number of bytes of User Data to get
EXTERN DNMINI
; Error Return if couldn't get user data
; Normal Return
;
;
;Message and message segment deallocation routines
;
; T1/ Pointer to block being returned
EXTERN DNFMSG ;FREE A DECnet-36 MESSAGE BLOCK
; Normal Return
;
;
;Free memory allocation and deallocation routines
;
; T1/ Number of contiguous words to allocate
EXTERN DNGWDS ;GET WORDS
EXTERN DNGWDZ ;GET ZEROED WORDS
; Error Return
; Normal Return with pointer in T1
;
; T1/ Address of start of block
; T2/ Address of end of block
; T3/ Value to put in block (typically zero)
EXTERN DNSWDS ;Smear value into block
; Normal Return ; using either BLT or XBLT
;
; T1/ Pointer to block to free
; T2/ Number of words in block
EXTERN DNFWDS ;FREE WORDS
; Normal Return
;
EXTERN TIMBAS ;CONVERSION FROM DECNET TIME BASE INTO SECONDS
EXTERN DNGTIM ;GET CURRENT TIME
; Normal Return ;WITH T1 CONTAINING MS TIME
;Trace and associated macros
DEFINE CALLTRACE(proc,code<TRCNSP>),<
IFN FTTRACE,<
CALL [
IFIDN <code>,<ETRNSP>,MOVE CX,S.ETRACE##
IFDIF <code>,<ETRNSP>,MOVE CX,S.TRACE##
TXNN CX,TRCNSP
RET
SAVEAC <T1,T2,T3,T4>
CALLSCAN proc
RET]
>
>
DEFINE TRACE(prefix,message),<XTRACE(prefix,<message>,TRC)>
DEFINE ETRACE(prefix,message),<XTRACE(prefix,<message>,ETR)>
DEFINE XTRACE(prefix,message,code),<
IFN FTTRACE,<
CALL [ PUSH P,T1
PPTRACE prefix,code
XMOVEI T1,[ASCIZ \message]
\]
CALLTRACE .TSTRG##,code''prefix
POP P,T1
RET]
>
>
DEFINE PTRACE(prefix),<
IFN FTTRACE,<
CALL [ PUSH P,T1
PPTRACE(prefix,TRC)
POP P,T1
RET]
>
>
DEFINE PETRACE(prefix),<
IFN FTTRACE,<
CALL [ PUSH P,T1
PPTRACE(prefix,ETR)
POP P,T1
RET]
>
>
DEFINE PPTRACE(prefix,code<TRC>),<
IFN FTTRACE,<
XMOVEI T1,[ASCIZ \[prefix: \]
CALLTRACE .TSTRG##,code''prefix
>
>
DEFINE TRCRET(prefix,message),<
IFE FTTRACE,RET
IFN FTTRACE,<
JRST [ TRACE(prefix,message)
RET]
>
>
DEFINE ETRCRET(prefix,message),<
IFE FTTRACE,RET
IFN FTTRACE,<
JRST [ ETRACE(prefix,message)
RET]
>
>
DEFINE NEWSTATE(nstate),<
MOVX CX,NPS.'nstate
STOR CX,ELSTA,(EL)
TRACE NSP,<New State: nstate>
>
SUBTTL NSP Network Protocol Definitions
;The architecture version number of this implementation
XP .NSVER,0 ;0=3.2, 1=3.1, others reserved
VER3.2==0 ;VERSION 3.1 (PHASE III) = 0
VER3.1==1 ;VERSION 3.0 (PHASE II) = 1
VER4.0==2 ;VERSION 4.0 (PHASE IV) = 2
;The contents of the MSGFLG field:
;The low-order two bits of the MSGFLG field are always zero, for this
;is how Router knows that this is an NSP MSGFLG field and not a
;Phase II routing header. The high-order bit is always zero also.
DEFINE MGF(nam,type,subtype),<
IFNB <nam>,<nam'== subtype'B31 ! type'B33>>
MGF MGFMSG, 0, 0 ;NORMAL SEGMENT WITH NO BOM OR EOM
MGF MGFLKS, 0, 1 ;LINK SERVICE MESSAGE
MGF MGFBOM, 0, 2 ;NORMAL SEGMENT WITH BOM
MGF MGFINT, 0, 3 ;INTERRUPT MESSAGE
MGF MGFEOM, 0, 4 ;NORMAL SEGMENT WITH EOM
MGF , 0, 5 ;ILLEGAL
MGF MGFONL, 0, 6 ;NORMAL ONLY SEGMENT (BOTH BOM AND EOM)
MGF , 0, 7 ;ILLEGAL
MGF MGFACK, 1, 0 ;NORMAL SUBLINK ACK
MGF MGFOAK, 1, 1 ;OTHER SUBLINK ACK
MGF MGFCAK, 1, 2 ;CONNECT ACK
MGF , 1, 3 ;ILLEGAL
MGF , 1, 4 ;ILLEGAL
MGF , 1, 5 ;ILLEGAL
MGF , 1, 6 ;ILLEGAL
MGF , 1, 7 ;ILLEGAL
MGF MGFNOP, 2, 0 ;NO OP
MGF MGFCI , 2, 1 ;CONNECT INITIATE
MGF MGFCC , 2, 2 ;CONNECT CONFIRM
MGF MGFDI , 2, 3 ;DISCONNECT INITIATE
MGF MGFDC , 2, 4 ;DISCONNECT CONFIRM
MGF , 2, 5 ;ILLEGAL, PHASE II NODE INITIATE
MGF MGFRCI, 2, 6 ;RETRANSMITTED CONNECT INITIATE
MGF , 2, 7 ;ILLEGAL
MGF , 3, anything ;ILLEGAL
PURGE MGF
;The format of an ACKNUM field
;This structure is expected to be used to pull apart a value held
;in a register.
BEGSTR AK
FILLER 20 ;ONLY THE RIGHTMOST 16 BITS COUNT
FIELD PNT, 1 ;FLAG SET IF FIELD IS PRESENT
FIELD QAL, 3 ;QUALIFIER:
AK$QAK==0 ; 0 IS ACK
AK$QNK==1 ; 1 IS NAK
AK$CAK==2 ; 2 IS CROSS-SUB CHANNEL ACK
AK$CNK==3 ; 3 IS CROSS-SUB CHANNEL NAK
FIELD NUM, 12 ;THE ACK NUMBER, WE KNOW THIS IS RT-JUSTIFIED
ENDSTR ; NEGATIVE IF HIGH BIT OF BYTE IS SET
; SEE LOADE MACRO (E IS AS IN HRRE)
;This structure is expected to be used to pull apart a value held
;in a register.
BEGSTR LS ;THE LSFLAGS FIELD OF A LINK SERVICE MESSAGE
FILLER 28 ;ONLY THE RIGHTMOST 8 BITS COUNT
FIELD ZRO,4 ;MUST BE ZERO
FIELD INT,2 ;INTERPRETATION
LS.INR==0 ;NORMAL DATA REQUEST
LS.IOT==1 ;OTHER DATA REQUEST (2 & 3 RESERVED)
FIELD MOD,2 ;THE ON/OFF INDICATOR
LS.MNC==0 ;NO CHANGE, CODE USES JUMPE
LS.MOF==1 ;TURN SUBLINK OFF (IGNORED ON "OTHER")
LS.MON==2 ;TURN SUBLINK ON (IGNORED ON "OTHER")
LS.MRS==3 ;RESERVED
ENDSTR
;This structure is expected to be used to pull apart a value held
;in a register.
BEGSTR SV ;THE SERVICES FIELD OF A CI OR CC MSG
FIELD FL1,32 ;FILLER 1, CHECK FOR ALL ZEROES
FIELD OPT,2 ;THE FLOW CONTROL OPTION, SEE FCM.xx
FIELD FL2,2 ;FILLER 2, CHECK FOR BEING "01"
SV$FL2==1 ;MAGIC NUMBER THAT SVFL2 MUST BE
ENDSTR
SUBTTL Code-Generating Macros
;Use: AC/ One of the NPS.xx state codes
;
;a) IFSTATE AC,<OP,CC,RJ>
; executed if match found
;b) IFSTATE AC,<OP,CC,RJ>,LABEL (Go there if match found)
DEFINE IFSTATE(ac,states,glabel),<IFST1(ac,<states>,<glabel>,N,GE,L)>
;Use: AC/ One of the NPS.xx state codes
;
;a) IFNSTATE AC,<OP,CC,RJ>
; executed if match NOT found
;b) IFNSTATE AC,<OP,CC,RJ>,LABEL (Go there if match NOT found)
DEFINE IFNSTATE(ac,states,glabel),<IFST1(ac,<states>,<glabel>,E,L,GE)>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DEFINE IFST1(ac,states,glabel,mod1,mod2,mod3),<
ZZ==0
ZZCNT==0
IRP states,<ZZ==ZZ ! 1B<NPS.'states>
ZZCNT==ZZCNT+1>
IFE ZZCNT-1,<
CAI'mod1 ac,NPS.'states
IFNB <glabel>,<
IFIDN <glabel>,<RTN>,<RET>
IFDIF <glabel>,<RTN>,<JRST glabel>
>>
IFG ZZCNT-1,< ;;IF MORE THAN ONE STATE TESTED
MOVX CX,ZZ
ROT CX,(ac)
IFB <glabel>,< SKIP'mod2 CX >
IFNB <glabel>,< JUMP'mod3 CX,glabel >
>
PURGE ZZCNT,ZZ
>
;Macros to do mod-mask-size compares on FIELDs
;These macros preserve ac passed as argument, use ac CX
;THESE MACROS ARE NOT SKIPPABLE
;Consider the set of numbers which can fit in the field to be a circle
;with values increasing in a clockwise direction until they return to
;zero after a full circle. X is less than Y if X falls within the
;semicircle before (counterclockwise) of Y. X is greater than Y if it
;falls within the semicircle after (clockwise) of Y. The number
;exactly opposite Y is considered greater than Y.
;CMODL - Skip if AC is less than target (mod mask-size)
; NB, This macro is more expensive than CMODLE
DEFINE CMODL(ac,mask,offset),<CMODX(ac,<mask>,<offset>,E,0)>
;CMODLE - Skip if AC is less than or equal to target (mod mask-size)
DEFINE CMODLE(ac,mask,offset),<CMODX(ac,<mask>,<offset>,E)>
;CMODGE - Skip if AC is less than target (mod mask-size)
; NB, This macro is more expensive than CMODG
DEFINE CMODGE(ac,mask,offset),<CMODX(ac,<mask>,<offset>,N,1)>
;CMODG - Skip if AC is greater than target (mod mask-size)
DEFINE CMODG(ac,mask,offset),<CMODX(ac,<mask>,<offset>,N)>
;The following are just for compatibility, same as OPSTR <CAMxx...>
;CMODE - Skip if AC is equal to target
DEFINE CMODE(ac,mask,offset),<OPSTR <CAME ac,>,<mask>,<offset>>
;CMODN - Skip if AC is not equal to target
DEFINE CMODN(ac,mask,offset),<OPSTR <CAMN ac,>,<mask>,<offset>>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;CMODX - Common macro for CMODLE and CMODG to call
DEFINE CMODX(ac,mask,offset,EorN,ecase,%fin),<
LOAD CX,<mask>,<offset>
SUB CX,ac
IFNB <ecase>,<JUMPE CX,%fin+ecase>
TXN'EorN CX,1B<^D36-WID(mask)> ;;TEST HIGH-ORDER BIT OF BYTE
%fin:!
>
SUBTTL Resident Storage Allocation
;The following INTERNALs are for NMXSER, which reads and writes these
;parameters.
INTERNAL NSPVER ;NSP VERSION NUMBER
INTERNAL NSPECO ;NSP VENDOR ECO NUMBER
INTERNAL NSPUEC ;NSP USER ECO NUMBER
INTERNAL NSPDLY ;DELAY FACTOR
INTERNAL NSPWGT ;DELAY WEIGHT
INTERNAL NSPINA ;INACTIVITY TIMER (SECONDS)
INTERNAL NSPRTH ;RE-TRANSMIT THRESHOLD
INTERNAL NSPAPQ ;ALL PORTS QUEUE, FOR NMX TO SEE QHMAX
;See previous page for list of INTERNAL symbols.
$LOW ;IFN FTOPS20, THIS IS DEFINED IN D36PAR
NSPBFR: DEC 3 ;REMOTE IS BUFFER-RICH IF GIVES .GE. NSPBFR DRQS
NSPVRN: EXP VER3.2 ;NSP PROTOCOL VERSION NUMBER (AS SEEN IN CI MSGS)
NSPVER: DEC 3 ;NSP PROTOCOL VERSION NUMBER (AS SEEN BY USERS)
NSPECO: DEC 2 ;DEC ECO NUMBER
NSPUEC: DEC 0 ;USER ECO NUMBER
NSPRPN: DEC 2 ;NUMBER OF RESERVED PORTS TO KEEP
NSPRND: BLOCK 1 ;POINTER TO THE RESERVED PORT'S NDB
IFN FTTROLL,<
NSPTRI: DEC 0 ;INITIALIZER FOR THE TROLL COUNTER
NSPTRL: BLOCK 1 ;THE TROLL COUNTER
>;END OF IFN FTTROLL
NSPJSI: DEC 3 ;JIFFIES IN A "SHORT INTERVAL"
NSPJSC: BLOCK 1 ;SHORT INTERVAL COUNTER
NSPJLI: DEC 60 ;JIFFIES IN A "LONG INTERVAL"
NSPJLC: BLOCK 1 ;LONG INTERVAL COUNTER
NSPWGT: %NSWGT## ;ROUND-TRIP WEIGHTING FACTOR (SEE UPDELAY)
NSPFLR: %NSFLR## ;FLOOR UNDER ROUND-TRIP DELAY (SEE UPDELAY)
NSPRUF: %NSRUF## ;ROOF OVER ROUND-TRIP DELAY
NSPDLY: %NSDLY## ;MULT BY NNDLY FOR RESEND TIMER (16THS) (CHKRSN)
NSPRTH: %NSRTH## ;RE-SEND THRESHOLD, HOW OFTEN TO RESEND (CHKRSN)
NSPINA: %NSINA## ;SECONDS BEFORE LINK IS CALLED INACTIVE (CHKINA)
NSPHMX: BLOCK 1 ;MAX LENGTH OF A HASH BUCKET LIST
NSPHTB: BLOCK 1 ;HASH TABLE ADDRESS
NSPHTS: DEC 73 ;HASH TABLE SIZE IN WORDS (PRIME NUMBER)
NSPIFG: DEC 0 ;NON-ZERO WHEN NSP IS INITIALIZED
NSPNLA: BLOCK 1 ;NEXT LINK ADDRESS TO ASSIGN
NSPAPQ: BLOCK QH.LEN ;ALL-PORTS QUEUE
NSPJFQ: BLOCK QH.LEN ;JIFFY-SERVICE PORTS QUEUE
NSPRPQ: BLOCK QH.LEN ;QUEUE OF RESERVED PORTS
NSPITQ: BLOCK QH.LEN ;INPUT TRANSACTION QUEUE FOR NSPLCx
;SEE BEGSTR QH FOR FORMAT OF A Q HEADER
NSPLKF: DEC 0 ;BIT MAP OF NSPLCF REQUESTS PENDING
BEGSTR LK ;BITS IN NSPLKF
FIELD FLG,6 ;A JFFO WORD, SAME ORDER AS NSPULT
BIT JIF ;JIFFY SERVICE, MUST BE SIGN BIT FOR NSPJIF
BIT CGT ;CONGESTION-DETECTED SERVICE
BIT RLV ;CONGESTION-RELIEVED SERVICE
ENDSTR
IFN FTOPS20,<
NSPLKO: BLOCK 1 ;SERIAL NUMBER OF CPU WHICH OWNS NSP LOCK
NSPLOK: DEC -1 ;NSPLCx'S SEMAPHORE, -1 IS FREE
>
$HIGH ;IFN FTOPS20, THIS IS DEFINED IN D36PAR
SUBTTL NSP's Entry Vector Table
;See procedure NSP for a description of the calling sequence used to
;call NSP.
;The offsets NV.xxx into this table are defined in D36PAR.
DEFINE NV(nam),<
IFN .-NSPFNT-NV.'nam,< PRINTX ?NSPFNT table not in same order
PRINTX ?as NV.'nam in D36PAR.UNV
PASS2
END>
IFIW NSP'nam> ;ALLOW EXTENDED ADDRESSING INDIRECTION
NSPFNT: NV OPN ;OPEN FROM SESSION CONTROL
NV ACT ;ENTER ACTIVE FROM SESSION CONTROL
NV ACC ;ACCEPT CONNECT FROM SESSION CONTROL
NV REJ ;REJECT CONNECT FROM SESSION CONTROL
NV SEG ;SEND DATA FROM SESSION CONTROL
NV DRQ ;REQUEST DATA FOR SESSION CONTROL
NV GOL ;SET QUOTA/GOAL FROM SESSION CONTROL
NV DSC ;SEND DISCONNECT FROM SESSION CONTROL
NV ABO ;SEND ABORT FROM SESSION CONTROL
NV CLS ;CLOSE PORT FROM SESSION CONTROL
NV RCV ;RECEIVE MESSAGE FROM ROUTER
NV ODN ;OUTPUT DONE FROM ROUTER
NV RTS ;RETURN TO SENDER FROM ROUTER
NV.MAX==.-NSPFNT-1 ;THE HIGHEST RECOGNIZED ENTRY OFFSET
PURGE NV
SUBTTL NSPRCV - Receive a Message from Router
;The MSGFLG Call Tables for NSIRCV, see also the next page
;The UPTO numbers must be in order, for they
; are used by SOJLs in procedure RCVPRC
UPTOMG== 0 ;READ UPTO THE MSGFLG FIELD
UPTODL== 1 ;READ UPTO THE DLA FIELD
UPTOSL== 2 ;READ UPTO THE SLA FIELD
UPTOAK== 3 ;** use this for MSGFLG== ACK message ONLY **
UPTOSG== 4 ;READ UPTO THE SEGNUM FIELD
NORMAL== 0 ;USE THE "NORMAL" SUBLINK
OTHER== 1 ;USE THE "OTHER" SUBLINK
NRQACK== 0 ;ACKNUM FIELD NOT REQUIRED
REQACK== 1 ;ACKNUM FIELD REQUIRED (MSGFLG == ACK)
NO.RESP==0 ;SENDER DOES NOT EXPECT A RESPONSE
RESPOND==1 ;SENDER EXPECTS A RESPONSE
FLOW==1 ;THIS MESSAGE TYPE IS FLOW CONTROLLED
NOFLOW==0 ;THIS MESSAGE TYPE IS NOT FLOW CNTRLD
DEFINE MGTYPS,< ;{subtype,type}
MSGTYP(MIDSEG,RCVMDS,UPTOSG,NORMAL,NRQACK,RESPOND,FLOW ) ; 0: {0,0}
MSGTYP(ACK, RCVACK,UPTOAK,NORMAL,REQACK,NO.RESP,NOFLOW) ; 1: {0,1}
MSGTYP(NOP, RCVNOP,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ; 2: {0,2}
MSGTYP(<0,3>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ; 3: {0,3}
MSGTYP(LNKSRV,RCVLKS,UPTOSG,OTHER ,NRQACK,RESPOND,NOFLOW) ; 4: {1,0}
MSGTYP(OTHACK,RCVACK,UPTOAK,OTHER ,REQACK,NO.RESP,NOFLOW) ; 5: {1,1}
MSGTYP(CI, RCVCI, UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ; 6: {1,2}
MSGTYP(<1,3>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ; 7: {1,3}
MSGTYP(BEGSEG,RCVBGS,UPTOSG,NORMAL,NRQACK,RESPOND,FLOW ) ;10: {2,0}
MSGTYP(CA, RCVCA, UPTODL,NORMAL,NRQACK,NO.RESP,NOFLOW) ;11: {2,1}
MSGTYP(CC, RCVCC, UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;12: {2,2}
MSGTYP(<2,3>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;13: {2,3}
MSGTYP(INTSEG,RCVONS,UPTOSG,OTHER ,NRQACK,RESPOND,FLOW ) ;14: {3,0}
MSGTYP(<3,1>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;15: {3,1}
MSGTYP(DI, RCVDI, UPTOSL,NORMAL,NRQACK,RESPOND,NOFLOW) ;16: {3,2}
MSGTYP(<3,3>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;17: {3,3}
MSGTYP(ENDSEG,RCVENS,UPTOSG,NORMAL,NRQACK,RESPOND,FLOW ) ;20: {4,0}
MSGTYP(<4,1>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;21: {4,1}
MSGTYP(DC, RCVDC, UPTOSL,NORMAL,NRQACK,NO.RESP,NOFLOW) ;22: {4,2}
MSGTYP(<4,3>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;23: {4,3}
MSGTYP(<5,0>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;24: {5,0}
MSGTYP(<5,1>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;25: {5,1}
MSGTYP(<5,2>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;26: {5,2}
MSGTYP(<5,3>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;27: {5,3}
MSGTYP(ONYSEG,RCVONS,UPTOSG,NORMAL,NRQACK,RESPOND,FLOW ) ;30: {6,0}
MSGTYP(<6,1>, RCVILM,UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;31: {6,1}
MSGTYP(RCI, RCVCI, UPTOMG,NORMAL,NRQACK,RESPOND,NOFLOW) ;32: {6,2}
>;END OF MGTYPS MACRO
;Continued on Next Page
;Continued from Previous Page
;Receive Table Definitions
BEGSTR RT
FIELD FLG,6
BIT FLO ;MSG TYPE FLOW CONTROLLED, FOR CHKRSN
BIT OTH ;SET IF THIS IS "OTHER" SUBLINK
BIT ACK ;SET IF MSG MUST INCLUDE ACKNUM FIELD
BIT RSP ;SET IF SENDER EXPECTS A RESPONSE
FIELD UPT,3 ;THE "UPTO" FIELD, MOD 3 BITS FOR DDT
HWORD RTN ;LOCAL ADDR OF ROUTINE TO PROCESS MSG
ENDSTR
DEFINE MSGTYP(type,routine,upto,sublink,ack,respond,flow),<
;;must agree with BEGSTR RT, above;;
BYTE (1)flow, sublink, ack, respond, 0, 0 (3)upto (9)0 (18)routine>
MSGTBL: MGTYPS
RCVMAX==.-MSGTBL-1
IFN FTTRACE,<
DEFINE MSGTYP(type,routine,upto,sublink,ack,respond,flow),<
[ASCIZ \type\]
> ;;USED BY MSGTRC, MESSAGE TRACE ROUTINE
MGTYNM: MGTYPS
MGTMAX==.-MGTYNM-1
[ASCIZ /Illegally large/]
ILLMGT==.-MGTYNM-1
>
PURGE NORMAL,OTHER,NRQACK,REQACK,NO.RESP,RESPOND,MSGTYP,MGTYPS
PURGE FLOW,NOFLOW
SUBTTL NSPRTR - The Entry Point from Router
;NSPRTR - The place where Router calls any NSP routine.
;
;Call: T2/ Caller's Args, or pointer thereto
; T2/ Caller's Args
; T3/ Offset into NSP's entry vector
; T4/ Full-word, direct pointer to a message block
; CALL NSPRTR
; Normal Return
;Preserves all but T1,T2,T3,T4
NSPRTR: SAVEAC <P1,P2,MB,MS,FREE1> ;FREE2 NOT USED IN RTR,
SKIPN NSPIFG ;NSP INITIALIZED?
BUG.(CHK,LLIXNN,LLINKS,SOFT,<NSP not yet initialized>,,<
Cause: This BUG is not documented yet.
>,RTN)
CAIL T3,NV.RVF ;CHECK AGAINST 1ST RTR VECTOR OFFSET
CAILE T3,NV.RVL ; AND LAST ROUTER VECTOR OFFSET
BUG.(CHK,LLIXVO,LLINKS,SOFT,<Illegal Router call>,,<
Cause: This BUG is not documented yet.
>,RTN)
SKIPN MB,T4 ;NSP EXPECTS MESSAGE BLOCK PTR IN MB
BUG.(CHK,LLIXZM,LLINKS,SOFT,<NSP called with no message block>,,<
Cause: This BUG is not documented yet.
>,RTN)
STOR T1,MBAR1,(MB) ;STORE THE TWO WORDS OF ARG DATA
STOR T2,MBAR2,(MB)
CALLRET @NSPFNT(T3) ;GO PROCESS THE CALL
;NO RETSKPS ALLOWED HERE
SUBTTL NSPINI - Initialization Entry Point
;NSPINI - This is one of the few entry points to NSP
;
;Call: T1/ Node address of this NSP
; T2/ ignored
; T3/ RT%PH2, see story below
; CALL NSPINI
; Error Return if Router says node number was illegal
; Normal Return
;Changes T1,T2,T3,T4
;This entry is called at system startup. Before this initialization
;is done, all calls to NSP will result in continuable BUG.s and the
;calls will be ignored.
;
;RT%PH2 is a flag passed to exactly one NSP in the system. NSP
;ignores the flag except to pass it on to Router. Router uses
;to flag to know which NSP (if there is more than one) gets the phase II
;messages, which have no destination node number attached to them.
NSPINI: SAVEAC <P1,P2>
MOVE P1,T1 ;SAVE NODE ADDRESS FOR ROUTER LAYER
MOVE P2,T3 ;SEND T3 THROUGH UNCHANGED
;Here we initialize the next logical link number to a pseudo-random
;number. This reduces the risk of a reloaded system continuing a
;logical link from before the crash. We use the high-order 16 bits of the
;time to avoid any similarities in the number due to a similar number of
;seconds since startup. System time will always start on an even second.
IFN FTOPS10,MOVE T1,TIME## ;JIFFIES SINCE MIDNIGHT
IFN FTOPS20,CALL LGTAD ;GET TIME & DATE
ANDI T1,177777 ;PARE IT DOWN TO 16 BITS FOR LINK ADDRESS
MOVEM T1,NSPNLA ;STORE AS NEXT LINK NUMBER TO ASSIGN
CALL INIHSH ;INITIALIZE THE PORT HASH TABLE
CALL RPNINI ;INITIALIZE THE RESERVED PORTS
SETOM NSPIFG ;NSP IS NOW INITIALIZED
;Assure that we are initialized before we let RTR do a node init
;and thus open the door to incoming messages.
XMOVEI T2,NSPRTR ;ADDRESS RTR IS TO CALL NSP AT
MOVE T1,P1 ;RESTORE NODE ADDRESS FOR ROUTER
MOVE T3,P2 ;RECOVER THE PHASE II FLAG PASSED TO US
CALL RTRINI ;LET ROUTER VALIDATE THE NODE NUMBER
RET ;PROPOGATE THE ERROR RETURN
RETSKP ;SUCCESS RETURN
SUBTTL NSP Interlock Handlers -- NSPLCQ - Queue on Failure
;NSPLCQ - Get NSP interlock, Queue on Failure
;
;Call: T1/ Arg to callee
; T2/ Arg to callee
; T6/ Address of NSP routine to process this message
; MB/ Ptr to message block
; CALL NSPLCQ
; Normal Return
;Callee changes T1 - T6
;NSPLCQ - Get interlock, Queue message block & return if can't lock.
NSPLCQ: STOR T1,MBAR1,(MB) ;STORE THE TWO WORDS OF ARG DATA
STOR T2,MBAR2,(MB)
STOR T6,MBPRC,(MB) ;STORE ADDR OF PROCESSING PROCEDURE
D36OFF ;INTERLOCK FOR ENDQUE BELOW
IFN FTOPS20,CONSO PI,176B27 ;AT INTERRUPT LEVEL?
AOSE NSPLOK ;NO, TEST AND SET THE SEMAPHORE
JRST NSPLQ1 ;LOCKED OR INT LEVEL, QUEUE MSG BLK AND LEAVE
APRID NSPLKO ;WAS FREE, SET NEW OWNER CPU
D36ON ;UNDO THE D36OFF
OPSTR <CALL @>,MBPRC,(MB) ;GO PROCESS, NO ERROR RETURNS ALLOWED
CALLRET NSPULK ;GO PROCESS.
NSPLQ1: ENDQUE MB,NSPITQ,MB.NXT,T1
D36ON ;UNDO THE D36OFF
RET ;ONLY RETURN
SUBTTL NSP Interlock Handlers -- NSPLCW - Wait on Failure
;NSPLCW - Get NSP interlock, Wait on Failure
;
;Call: T1/ Passed through to callee
; T2/ " " " "
; T6/ Address of NSP routine to process this message
; CALL NSPLCW ;MB NOT REQUIRED
; Normal Return
;Callee changes T1 - T6
;NSPLCW - Get interlock, Wait if can't get lock.
; TOPS10 - Lock should always be free on entry except on SMP.
; TOPS20 - Another process may be blocked for page I/O.
NSPLCW: ;T1 & T2 SAVED FOR CALLEE
SAVEAC MB ;CALLER NEEDS POINTER TO THIS MSG BLK
IFN FTOPS20,<
NSPLW1:!AOSN NSPLOK ;TEST AND SET THE SEMAPHORE
JRST NSPLW3 ;WAS FREE, GO USE IT
SKIPG NSKED ;IN USE, ARE WE NOSKED
SKIPE INSKED ; OR ARE WE AT SCHEDULAR LEVEL?
JRST NSPLW2 ;YES. BUG.
PUSH P,T1 ;MUST SAVE T1 & T2 TO PASS TO CALLEE
PUSH P,T2 ; SINCE THEY ARE THE ARGS PASSED
XMOVEI T1,NSPLWB ;NO, SET UP SCHEDULAR TEST
MDISMS ;WAIT FOR COMPETING PROCESS TO FINISH
POP P,T2 ;T6 WILL BE SAVED ACROSS MDISMS - IT IS Q2
POP P,T1
JRST NSPLW1 ;OK, LETS TRY AGAIN
NSPLWB: SKIPL NSPLOK ;IS NSP LOCK FREE YET?
JRST (T4) ;NO, SLEEP ON
JRST 1(T4) ;YES, WAKE UP FORK
NSPLW2: BUG.(CHK,LLIBWK,LLINKS,SOFT,<SCTNSF call from sched w/o lock>,
<<-2(P),CALLER>,<INSKED,INSKED>,<NSKED,NSKED>>
,<
Cause: The DECnet entry point SCTNSF has been called from schedular
level when the Session Control interlock was locked.
Action: All schedular level routines which call SCTNSF should first
check SCTLOK. If SCTLOK is not -1, then the caller should
wait for the next schedular cycle before calling SCTNSF.
>)
RET ;LET CALLER WORRY ABOUT NO DECNET ACTION
NSPLW3:
>;END IFN FTOPS20
IFN FTOPS10,<
NSPLW1:!AOSE NSPLOK ;TEST AND SET THE SEMAPHORE
JRST NSPLW1 ;LOCKED, SPIN-WAIT UNTIL LOCK IS FREE
>;END IFN FTOPS10
APRID NSPLKO ;SET THE OWNER OF THE INTERLOCK
CALL 0(T6) ;(T1,T2)GO PROCESS, NO ERROR RETURNS ALLOWED
CALLRET NSPULK ;CHECK FOR QUEUED TRANSACTIONS
SUBTTL NSP Interlock Handlers -- NSPLCF - Flags set for Failure
;NSPLCF - Get NSP interlock, Flags set for Failure
;
;Call: No Arguments
; CALL NSPLCF
; Normal Return
;Callee changes T1 - T6
;NSPLCF - Get interlock, return if can't, caller has set Flag for NSPULK.
NSPLCF: AOSE NSPLOK ;TEST AND SET THE SEMAPHORE
RET ;LOCKED, CALLER HAS SET FLAG
APRID NSPLKO ;SET OWNER OF THE INTERLOCK
CALLRET NSPULK ;WAS FREE, NOW LOCKED, GO PROCESS MSG
;NSPULK - Called from NSPLCx to process message(s)
;
;Call:
; CALL NSPULK
; Normal Return
;Vicariously changes T1,T2,T3,T4,T5,T6,P1,P2,MS
;We don't need CSKED here, since we are always called either by
;Session Control which has CSKED or at some interrupt level.
NSPULK:
NSPUL1: D36OFF ;TURN OFF NETPI, INTERLOCK SMP
SKIPE T1,NSPLKF ;GET LOCK FLAGS FOR NSPLCF REQUESTS
JFFO T1,NSPUL3 ;SEE WHAT REQUESTS ARE PENDING
LKJIF==LKJIF ;PUT SYMBOL IN CREF FOR JFFO'S ACCESS
LKCGT==LKCGT ; DITTO
LKRLV==LKRLV ; DITTO
;NO FLAGGED REQUESTS NOW
DEQUE MB,NSPITQ,MB.NXT,NSPUL4 ;GO TO NSPUL4 IF EMPTY
D36ON ;GOT ONE, UNDO THE D36OFF
OPSTR <CALL @>,MBPRC,(MB) ;GO PROCESS, NO ERROR RETURNS ALLOWED
JRST NSPUL1 ;PROCESS THIS MESSAGE NOW
NSPUL3: MOVE T1,NSPULB(T2) ;GET BIT TO CLEAR
ANDCAM T1,NSPLKF ;CLEAR FLAG JFFO JUST FOUND
D36ON ;END OF D36OFF AT NSPUL1
CALL @NSPULT(T2) ;GO PROCESS ROUTINE INDICATED BY JFFO
JRST NSPUL1 ;SEE WHAT MORE THERE IS TO DO UNDER LOCK
NSPULB: EXP 1B0 ;BIT TO CLEAR FOR T2=0
EXP 1B1 ;BIT TO CLEAR FOR T2=1
EXP 1B2 ;BIT TO CLEAR FOR T2=2
NSPULT: IFIW NSIJIF ;1B0 MEANS JIFFY REQUEST
IFIW NSICGT ;1B1 MEANS CONGESTION-DETECTED
IFIW NSIRLV ;1B2 MEANS CONGESTION-RELIEVED
;We have found both NSPLKF and NSPITQ empty in one D36OFF
NSPUL4: SETOM NSPLKO ;NO CPU OWNS THE INTERLOCK
SETOM NSPLOK ;FREE THE NSP INTERLOCK
D36ON ;UNDO THE D36OFF
RET ; RETURN TO NSPLCx
SUBTTL Session Control Calls -- NSP - The Entry Point
;NSP - The main place where Session Control calls any NSP routine.
;
;Call: T1/ Caller's Args, or pointer thereto
; T2/ Caller's Args
; T3/ Offset into NSP's entry vector
; T4/ Full-word, direct pointer to a message block
; CALL NSP
; Normal Return
;Preserves all but T1,T2,T3,T4
NSP:: SAVEAC <P1,P2,MS,MB,FREE1,FREE2>
SKIPN NSPIFG ;NSP INITIALIZED?
BUG.(CHK,LLINNI,LLINKS,SOFT,<NSP not yet initialized>,,<
Cause: NSP (LLINKS) will reject all messages received from either
Session Control or Router until DECnet initialization is
complete.
>,RTN)
CAIL T3,0 ;NEGATIVE OFFSET ILLEGAL
CAILE T3,NV.MAX ;LEGAL OFFSET?
BUG.(CHK,LLIIVO,LLINKS,SOFT,<Illegal call vector offset>,,<
Cause: This BUG is not documented yet.
>,RTN)
MOVE MB,T4 ;NSP EXPECTS MESSAGE BLOCK PTR IN MB
CALLRET @NSPFNT(T3) ;GO PROCESS THE CALL
;NO RETSKPS ALLOWED HERE
SUBTTL Session Control Calls -- NSPOPN - Open a Link
;NSPOPN - Called by Session Control via NSP with NV.OPN offset.
;
;Call: T1/ Pointer to argument block, see BEGSTR OA, in D36PAR.MAC
; T2/ Length (words) of argument block T1 points to.
; MB/ Message Block
; CALL NSPOPN
; Normal Return, on success: T1/ new NSPpid
; on failure: T1/ -1
;Changes T1,T2,T3,T4
;The OPEN call requires an immediate response, so it calls NSPLCW
;rather than NSPLCQ to check the interlock. Unlike most interface
;routines, NSPOPN will return the message block it was called with.
NSPOPN: XMOVEI T6,NSIOPN ;ADDRESS OF INTERLOCKED ROUTINE
CALL NSPLCW ;WILL RETURN WHEN MSG HAS BEEN PROCESSED
LOAD T1,MBAR1,(MB) ;RETRIEVE THE "T1" ARG FOR SESSION CONTROL
RET
NSIOPN: CAIE T2,OA.LEN ;IS IT THE RIGHT LENGTH?
BUG.(CHK,LLIOAL,LLINKS,SOFT,<OPEN arg blk wrong length>,,<
Cause: This BUG is not documented yet.
>,DNFWDS)
PUSH P,T1 ;SAVE POINTER TO ARGUMENT BLOCK
CALL NS1OPN ;CALL MEAT OF OPEN ROUTINE
POP P,T1
CALLRET DNFWDS ;DEALLOCATE THE ARGUMENT BLOCK
;Here to process the OPEN function
NS1OPN: MOVE P1,T1 ;PUT ARG BLOCK POINTER IN P1 (P1 ALREADY SAVED)
CALL NEWLLA ;RETURN NEW LLA IN T1
HRRZS T1 ;LLA IN RH, NO RLA IN LH
LOAD T2,OANOD,(P1) ;GET NODE ID PASSED BY SESSION CONTROL
CALL MAKPRT ;MAKE A NEW PORT BLOCK, PTR IN EL
JRST [SETONE MBAR1,(MB) ;RETURN -1 FOR NO-RESOURCES
RET]
LOAD T1,OASCB,(P1) ;SCTL'S NAME FOR PORT
STOR T1,ELSCB,(EL)
LOAD T1,OAFLO,(P1) ;COPY THE RECEIVE FLOW ORDERS
STOR T1,ESRFL,+EL.NSL(EL) ; INTO THE "NORMAL" SUBLINK BLOCK
LOAD T1,OAGOL,(P1) ;COPY THE GOALS FROM ARG BLOCK
STOR T1,ESGOL,+EL.NSL(EL) ; TO THE NEW PORT BLOCK
STOR T1,ESCGL,+EL.NSL(EL) ; CONGESTION GOAL TOO
LOAD T1,OASIZ,(P1) ;COPY MAX SEGMENT SIZE (BYTES)
STOR T1,ELSIZ,(EL)
LOAD T1,OASCV,(P1) ;SCTL'S ENTRY ADDRESS
STOR T1,ELSCV,(EL)
LOAD T1,OACIR,(P1) ;GET LOOPBACK CIRCUIT IF ANY
STOR T1,ELCIR,(EL)
NEWSTATE OP ;NEW PORT IS IN "OPEN" STATE
CALL GETPID ;RETURN THE NSPPID IN T1
STOR T1,MBAR1,(MB) ;TELL SESSION CONTROL THE NEW NSPPID
RET
SUBTTL Session Control Calls -- NSPGOL - Set New Data Request Goals
;NSPGOL - Session Control call to set new goals for a link
;
;Call: T1/ NSPpid
; T2/ Goal
; MB/ Message Block
; CALL NSPGOL
; Normal Return
;Changes T1,T2
NSPGOL: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSIGOL: LOAD T1,MBAR1,(MB) ;RESTORE CALLER'S ARGS TO ACS T1 & T2
LOAD P1,MBAR2,(MB) ;SAVE GOAL
CALL FNDPID ;GET PORT POINTER IN EL, BUG IF FAIL
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
STOR P1,ESGOL,+EL.NSL(EL)
STOR P1,ESCGL,+EL.NSL(EL) ;AFTER-CONGESTION GOAL TOO
CALLRET FREMSG
;This goal will take effect next time it is used. We have nothing
;to do now to expedite the process.
SUBTTL Session Control Calls -- NSPACT - Enter Active
;NSPACT - Enter Active Call from Session Control
;
;Call: T1/ NSPpid
; T2/ ignored
; MB/ Message Block, whose User Data MSD points to DATA-CTL
; fields of the outgoing message. The User Data
; field starts with a one-byte binary count of the
; bytes in the field. Session Control must provide
; this count (even if it is zero).
; CALL NSPACT
; Normal Return
;Changes T1,T2,T3,T4
NSPACT: JSP T6,NSPLCQ
NSIACT: LOAD T1,MBAR1,(MB) ;RESTORE CALLER'S ARGS TO ACS T1 & T2
LOAD T2,MBAR2,(MB) ;...
CALL FNDPID ;GET PORT POINTER IN EL, BUG IF FAIL
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
CALL MAKHDR ;MAKE NSPHDR MSD, BYTE PTR IN P1, COUNT IN P2
;MSGFLG
MOVX T1,MGFCI ;GET "CI" MSGFLG CODE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
MOVEI T1,0 ;DON'T YET KNOW THE DLA
CALL DNP2BY ;WRITE TWO-BYTE FIELD
;SLA
LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS
CALL DNP2BY ;WRITE TWO-BYTE FIELD
;SERVICES
CALL WRTSRV ;WRITE SERVICES FIELD
;INFO
MOVE T1,NSPVRN ;GET MY VERSION CODE (0=3.2, 1=3.1)
CALL DNPEBY ;WRITE EXTENSIBLE BYTE
;SEGSIZE
LOAD T1,ELSIZ,(EL) ;MAX SEGMENT SIZE WE WILL ALLOW IN BYTES
CALL DNP2BY ;WE GOT ELSIZ FROM SCTL IN OPEN CALL
;The DATA-CTL field has already been provided by Session Control.
LOAD T1,ELNDB,(EL) ;GET PTR TO NSP NODE BLOCK
INCR NNXCC,(T1) ;INCREMENT NUMBER OF CI MSGS SENT
NEWSTATE CI ;GO INTO CONNECT INIT STATE
SETZRO MBOTH,(MB) ;SEND CI ON 'NORMAL' SUBLINK
IFE FTPH4,<
;A CI message is timed with ELDTM, but is not acked, so NSIACT
;has to fill in ELDTM for CI messages itself.
CALL DNGTIM ;GET A TIMESTAMP IN T1
STOR T1,ELDTM,(EL) ;REMEMBER TIME FOR UPDELAY
SETZRO ELDSG,(EL) ;CI MESSAGE IS SEGMENT ZERO
SETZRO ELDTO,(EL) ; ON THE 'NORMAL' SUBLINK
MOVX T1, ST%NRS ! ST%NAK ! ST%RQR ! ST%NTR
>;END IFE FTPH4
IFN FTPH4,MOVX T1, ST%NRS ! ST%ACK ! ST%RQR ! ST%NTR
CALLRET SNDRTR ;SEND TO ROUTER
SUBTTL Session Control Calls -- NSPACC - Accept Connect
;NSPACC - Accept Connect Call from Session Control
;
;Call: T1/ Pointer to argument block, see BEGSTR AA,
; T2/ Length (words) of block T1 points to
; MB/ Message Block, whose User Data MSD points to DATA-CTL
; fields of the outgoing message. The User Data
; field starts with a one-byte binary count of the
; bytes in the field. Session Control must provide
; this count (even if it is zero).
; CALL NSPACC
; Normal Return
;Changes T1,T2,T3,T4
NSPACC: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSIACC: LOAD T1,MBAR1,(MB) ;RESTORE CALLER'S ARGS TO ACS T1 & T2
LOAD T2,MBAR2,(MB) ;...
CAIE T2,AA.LEN ;IS IT THE RIGHT LENGTH?
BUG.(CHK,LLIAAL,LLINKS,SOFT,<Arg blk to NSPACC wrong length>,,<
Cause: This BUG is not documented yet.
>,NSIAC1)
PUSH P,T1 ;SAVE POINTER TO THE ARGUMENT BLOCK
CALL NS1ACC ;CALL MEAT OF THE ACCEPT ROUTINE
POP P,T1 ;RESTORE PTR TO ARG BLK FOR FREE WORDS
CALLRET DNFWDS ;DEALLOCATE THE ARGUMENT BLOCK
;Here after BUG
NSIAC1: CALL DNFWDS ;FREE THE ARG BLK
CALLRET FREMSG ;AND THE MSG BLK WHICH CARRIED IT
;Here to process the accept function
NS1ACC: MOVE P1,T1 ;POINTER TO ARG BLOCK (P1 ALREADY SAVED)
LOAD T1,AAPID,(P1) ;GET PID SESSION CONTROL IS AIMING FOR
CALL FNDPID ;GET PORT POINTER IN EL, BUG IF FAIL
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
LOAD T1,AASCB,(P1) ;GET SESSION CONTROL'S NAME FOR PORT
STOR T1,ELSCB,(EL) ;STORE IN THE PORT BLOCK
LOAD T1,AAFLO,(P1) ;GET FLOW CONTROL VARIABLE
STOR T1,ESRFL,+EL.NSL(EL) ;STORE RECEIVE FLOW OF NORMAL SUBLINK
LOAD T1,AAGOL,(P1) ;COPY THE GOAL FROM ARG BLOCK
STOR T1,ESGOL,+EL.NSL(EL) ; TO THE NEW PORT BLOCK
STOR T1,ESCGL,+EL.NSL(EL) ; AFTER-CONGESTION GOAL TOO
LOAD T1,AASIZ,(P1) ;GET SCTL'S MAX SEGMENT SIZE (BYTES)
OPSTR <CAMGE T1,>,ELSIZ,(EL) ;COMPARE WITH REMOTE'S MAX SIZE
STOR T1,ELSIZ,(EL) ;STORE MINIMUM OF THE TWO MAXIMA
LOAD T1,AASCV,(P1) ;SCTL'S ENTRY ADDRESS
STOR T1,ELSCV,(EL)
NEWSTATE CC ;PORT IS NOW IN "CONNECT CONFIRMED" STATE
SETZRO ELSCM,(EL) ;SEE PROCEDURE JCHSCM FOR EXPLANATION
;Continued on Next Page
;Continued from Previous Page
;Procedure SENDCC sends a Connect Confirm message to the remote.
;If the remote is a Phase II node, there is no more to do. If the
;remote is a Phase III node, the Connect Confirm message is error
;controlled. We are expected to resend it after timeout just as a data
;message until we receive a "normal" data ACK from the remote.
;
;In order to fit the Connect Confirm message into the normal
;ACK-handling scheme of NSP-36, we pretend that the Connect Confirm
;message is message number zero (the first data message is defined to
;be message one). Of course, the message number does not go into the
;message, there is no number field in a Connect Confirm message. The
;message number in the message block is only used by the resend and ACK
;routines to figure out which message to resend or ACK.
CALL MAKHDR ;INITIALIZE THE NSP HEADER MSD AND DNxyaBY
;MSGFLG
MOVX T1,MGFCC ;GET "CONNECT CONFIRM" MSGFLG CODE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;GET REMOTE LINK ADDRESS (DESTINATION)
CALL DNP2BY ;WRITE TWO-BYTE FIELD
;SLA
LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS (SOURCE)
CALL DNP2BY ;WRITE TWO-BYTE FIELD
;SERVICES
CALL WRTSRV ;WRITE THE SERVICES FIELD
;INFO
MOVE T1,NSPVRN ;GET MY VERSION CODE (0=3.2, 1=3.1)
CALL DNPEBY ;WRITE EXTENSIBLE BYTE
;SEGSIZE
LOAD T1,ELSIZ,(EL) ;GET CALCULATED SEG SEIZE
CALL DNP2BY ;WRITE TWO-BYTE FIELD
;The DATA-CTL field has already been provided by Session Control.
SETZRO NMSGN,(MB) ;SEGMENT NUMBER, ...
SETZRO MBOTH,(MB) ;GO ON THE "NORMAL" SUBLINK
SETZRO ESLMA,+EL.NSL(EL) ;LAST MSG # ASSIGNED, SEE STORY ABOVE
MOVX T1, ST%NRSC ! ST%NAK ! ST%NRQR ! ST%TRL
LOAD T2,ELVER,(EL) ;GET REMOTE'S VERSION NUMBER
CAIN T2,VER3.1 ;IS IT PHASE II (NSP VERSION 3.1)?
CALLRET SNDRTR ;YES, DON'T SET UP FOR ACK
;Here to set up for the ACK that a Phase III link expects for a CC msg.
;We could just let the CC msg sit on the AKQ until we get an ACK for
;the first real msg we send on the normal sublink, but if the traffic
;on this link will all be one-way receiving, we could tie up a msg blk
;for a long time.
TXO T1,ST%ACK ;WE DO EXPECT AN ACK FOR OUR CC MSG
SETONE ESLAR,+EL.NSL(EL) ;LAST ACK REC'D WAS -1 SO NSIODN
; WON'T THINK THIS (0) HAS ALREADY
; BEEN ACKED.
CALLRET SNDRTR ;SEND TO ROUTER
SUBTTL Session Control Calls -- NSPSEG - Send Data
;NSPSEG - Session Control wants to send data on either sublink
;
;Call: T1/ NSPpid
; T2/ Flags, see definitions in BEGSTR DA
; CALL NSPSEG
; Normal Return
;Changes T1,T2,T3,T4
NSPSEG: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSISEG: LOAD T1,MBAR1,(MB) ;RESTORE CALLER'S ARGS TO ACS T1 & T2
LOAD P2,MBAR2,(MB) ;SAVE FLAGS IN A SAFE PLACE
CALL FNDPID ;GET PORT POINTER IN EL, BUG IF FAIL
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
STOR EL,NMPRT,(MB) ;SAVE PTR TO PORT IN MESSAGE BLOCK
LOAD T1,ELSTA,(EL) ;GET CURRENT PORT STATE
IFNSTATE T1,<CC,RN,DN>,SCNIRS ; BY CALLING SC'S NOT IN RUN STATE ENTRY
MOVE T1,MB.FLG(MB) ;GET THE PUBLIC FLAGS FROM MESSAGE
XMOVEI ES,EL.NSL(EL) ;ASSUME THIS IS ON "NORMAL" SUBLINK
TXNN T1,MBOTH ;ON THE "OTHER" SUBLINK?
JRST NSISG1 ;NO, SKIP "OTHER" PROCESSING
XMOVEI ES,EL.OSL(EL) ;YES, POINT ES AT "OTHER" SUBLINK BLOCK
TXC T1,MBBOM ! MBEOM ;IF EITHER
TXCE T1,MBBOM! MBEOM ; EOM OR BOM IS OFF
BUG.(CHK,LLIILI,LLINKS,SOFT,<Interrupt message must not be segmented>,,<
Cause: This BUG is not documented yet.
>)
SETONE <MBBOM,MBEOM>,(MB) ;SET THESE IN CASE WE CONTINUED A BUG
TMNE QHBEG,+ES.XMQ(ES) ;IF NO XMIT QUEUE, ALL IS OK
BUG.(CHK,LLIQIN,LLINKS,SOFT,<Queued interrupt message illegal>,,<
Cause: This BUG is not documented yet.
>)
NSISG1:
CALL NEWSGN ;PUT NEW SEG NUMBER IN MSG BLK
IFN FTTRACE,<
LOAD T1,ESXFL,(ES) ;GET TRANSMIT FLOW CONTROL MODE
CAIN T1,FCM.NO ;IS IT SEGMENT OR MESSAGE?
JRST NSISG2 ;NO, NO FLOW CONTROL, DON'T COMPLAIN
LOADE T1,ESXLD,(ES) ;ESXLD MAY BE NEGATIVE
JUMPG T1,NSISG2 ;ANY LOCAL DRQS AVAILABLE?
TRACE NSP,Session Control sent without DRQs
NSISG2:
>
;It is OK for Session Control to appear to send without DRQs here
;because it may have sent a message to NSP which was in NSPITQ
;while NSP was sending a negative data request to Session Control.
;Continued on Next Page
;Continued from Previous Page
;Update the NSP node block's counters for Network Management
LOAD P1,ELNDB,(EL) ;GET PTR TO NSP NODE BLOCK
XMOVEI T1,UD.MSD(MB) ;POINTER TO USER-DATA MSD
CALL DNSLNG ;RETURN LENGTH (BYTES) OF USER DATA IN T1
OPSTRM <ADDM T1,>,NNXBC,(P1) ;UPDATE COUNT OF BYTES SENT TO NODE
;The number of messags sent to the node is updated in SNXBKK, since
;all types of NSP messages are counted.
CALLRET PROCXB ;SEND THIS MSG BLK NOW (SMASHES MB & MS)
SUBTTL Session Control Calls -- NSPDRQ - Data Request Call
;NSPDRQ - Session Control's Data Request Call
;
;Call: T1/ NSPpid
; T2/ Flags (OFF) and count, see BEGSTR QA
; CALL NSPDRQ
; Normal Return
;Changes T1,T2,T3,T4
NSPDRQ: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSIDRQ: LOAD T1,MBAR1,(MB) ;RESTORE CALLER'S ARGS TO ACS T1 & T2
LOAD P2,MBAR2,(MB) ;SAVE ARG FLAGS IN PRESERVED AC
CALL FNDPID ;GET PORT POINTER IN EL, BUG IF FAIL
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
XMOVEI ES,EL.NSL(EL) ;ASSUME "NORMAL" SUBLINK
TMNE MBOTH,(MB) ;SCTL ASKED FOR "OTHER" SUBLINK?
XMOVEI ES,EL.OSL(EL) ;YES, LOAD IT INTO SUBLINK POINTER
LOAD T1,QACNT,+P2 ;GET THE COUNT FIELD PASSED, ALWAYS POSITIVE
OPSTRM <ADDM T1,>,ESRLD,(ES) ;ADD TO THE RECEIVE, LOCAL COUNT
CALL FREMSG ;FREE MSG WE GOT FROM SCTL
CALLRET PROCRQ ;PROCESS RECEIVE Q AND SEND ANY
; UNUSED DATA REQUESTS
;PROCRQ will check for any data requests that need to be sent to the
;remote and will send them or request jiffy service as appropriate.
SUBTTL Session Control Calls -- NSPREJ - Reject a Connection
;NSPREJ - Reject Call from Session Control
;
;Call: T1/ NSPpid
; T2/ ignored
; MB/ Message Block, whose User Data MSD points to reason and DATA-CTL
; fields of the outgoing message. The User Data
; field starts with a one-byte binary count of the
; bytes in the field. Session Control must provide
; this count (even if it is zero).
; CALL NSPREJ
; Normal Return
;Changes T1,T2,T3,T4
NSPREJ: JSP T6,NSPLCQ
NSIREJ: LOAD T1,MBAR1,(MB) ;RESTORE CALLER'S ARGS TO ACS T1 & T2
LOAD T2,MBAR2,(MB) ;...
CALL FNDPID ;GET PORT POINTER IN EL, BUG IF FAIL
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
CALL MAKPRS ;MAKE THIS PORT A RESERVED PORT
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
XMOVEI ES,EL.NSL(EL) ;SEND THIS ON THE "NORMAL" SUBLINK
NEWSTATE DR ;GET "DISCONNECT REJECT" STATE CODE
SETZRO ELSCM,(EL) ;SEE PROCEDURE JCHSCM FOR EXPLANATION
LOAD T1,ELNDB,(EL) ;GET PTR TO NSP NODE BLOCK
INCR NNXRC,(T1) ;INCREMENT NUMBER OF REJECTS XMITTED
CALL MAKHDR ;INITIALIZE THE NSP HEADER MSD AND DNxyaBY
;MSGFLG
MOVX T1,MGFDI ;GET "DISCONNECT INIT" MSGFLG CODE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;GET REMOTE LINK ADDRESS (DESTINATION)
CALL DNP2BY ;WRITE TWO-BYTE FIELD
;SLA
LOAD T1,ELLLA,(EL) ;ADMIT AN LLA SO WE CAN RECEIVE THE DC
CALL DNP2BY ;WRITE TWO-BYTE FIELD
;The REASON and DATA-CTL fields have been passed from Session Control
;in the "user-data" MSD
CALL NEWSGN ;MAKE NSIODN THINK THIS MESSAGE IS NEW
MOVX T1, ST%NRSC ! ST%ACK ! ST%NRQR ! ST%TRL
CALLRET SNDRTR ;SEND TO ROUTER
;WRTSRV - Write the SERVICES field of a CI or CC message
;
;Call: EL/ Port Block
; CALL WRTSRV
; Normal Return
;Changes T1
WRTSRV:
;The SERVICES field is complicated, so here it is once with literals,
;this seemed simpler than trying to define the thing in structures and
;then fill it in with strange STORs that are just as "literal" as the
;1 and 2 below.
LOAD T1,ESRFL,+EL.NSL(EL) ;THE FCOPT (RECEIVE FLOW CONTROL OPTION)
LSH T1,2 ;SHIFT IT INTO THE EXPECTED POSITION
TRO T1,1 ;LITERAL 1 EXPECTED IN LOW BIT
CALLRET DNPEBY ;WRITE EXTENSIBLE BYTE
;WRTACK - Write out the ACKNUM field of the NSP message header
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message Block
; CALL WRTACK
; Normal Return
;Changes T1
WRTACK: JE ESACK,(ES),WRTAK1 ;TRY OTHER SUBLINK IF THIS DOESN'T NEED ACK
LOAD T1,ESLMR,(ES) ;GET LAST MESSAGE RECEIVED NUMBER
TMNE ESNAK,(ES) ;IS THIS ACK REALLY A NAK?
TXOA T1,<AKPNT ! <FLD(AK$QNK,AKQAL)>> ;YES, QUALIFY IT AS A NAK
TXO T1,<AKPNT ! <FLD(AK$QAK,AKQAL)>> ;NO, QUALIFY IT AS AN ACK
SETZRO <ESACK,ESNAK>,(ES) ;NO LONGER NEED TO SEND AN ACK
CALL DNP2BY ;ADD IN THE OPTIONAL ACKNUM FIELD
WRTAK1:
IFE FTPH4,RET
IFN FTPH4,< ;THIS IS PHASE IV STUFF
LOAD T1,ELVER,(EL) ;GET REMOTE NSP VERSION
CAIE T1,VER4.0 ;IS IT VERSION 4.0?
RET ;NO, MUSTN'T SEND CROSS-SUBCHN ACKS THEN
XMOVEI T2,EL.OSL(EL) ;ASSUME WE WERE CALLED ON NSL, NOW ON OSL
TMNE ESOTH,(ES) ;TRUE?
XMOVEI T2,EL.NSL(EL) ;NO, WE WERE CALLED ON OSL, NOW ON NSL
JE ESACK,(T2),RTN ;IF THIS DOESN'T NEED ACK, WE'RE DONE
LOAD T1,ESLMR,(T2) ;GET LAST MESSAGE RECEIVED NUMBER
TMNE ESNAK,(T2) ;IS THIS ACK REALLY A NAK?
TXOA T1,<AKPNT ! <FLD(AK$CNK,AKQAL)>> ;YES, QUALIFY IT CROSS NAK
TXO T1,<AKPNT ! <FLD(AK$CAK,AKQAL)>> ;NO, CROSS-SUBCHANNEL ACK
SETZRO <ESACK,ESNAK>,(T2) ;NO LONGER NEED TO SEND AN ACK
CALLRET DNP2BY ;ADD IN THE OPTIONAL ACKNUM FIELD
>;END IFN FTPH4
;WRTMGF - Write the MSGFLG field of a message
;
;Call: T1/ The message flag code (MGFxxx)
; MB/ The Message Block
; CALL WRTMGF
; Normal Return
;Changes T1,T2,T3,T4
WRTMGF: STOR T1,NMMGF,(MB) ;STORE THE MESSAGE TYPE IN MESSAGE BLOCK
CALLRET DNPEBY ;WRITE AN EXTENSIBLE BYTE
SUBTTL Session Control Calls -- NSPDSC - Synch Disconnect
;NSPDSC - Disconnect Call from Session Control
;
;Call: T1/ NSPpid
; MB/ Message Block, whose User Data MSD points to reason and DATA-CTL
; fields of the outgoing message. The User Data
; field starts with a one-byte binary count of the
; bytes in the field. Session Control must provide
; this count (even if it is zero).
; CALL NSPDSC
; Normal Return
;Changes T1,T2,T3,T4
NSPDSC: JSP T6,NSPLCQ
NSIDSC: LOAD T1,MBAR1,(MB) ;RESTORE CALLER'S ARGS TO ACS T1 & T2
LOAD T2,MBAR2,(MB) ;...
CALL FNDPID ;GET PORT POINTER IN EL, BUG IF FAIL
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
LOAD T1,ELSTA,(EL) ;GET CURRENT PORT STATE
IFNSTATE T1,<CR,CC,RN>,SCNIRS
;SESSION CONTROL: NOT IN RUN STATE
;AS LONG AS NOT CI STATE, NO CI MSG IN ELDIM
STOR MB,ELDIM,(EL) ;STORE MSG POINTER TO SEND LATER
NEWSTATE DI ;GO INTO "DI" STATE
CALLRET SCLOSE ;START UP THE CLOSE FUNCTION
SUBTTL Session Control Calls -- NSPABO - Abort Link
;NSPABO - Session Control Call to Abort Link
;
;Call: T1/ NSPpid
; MB/ Message Block, whose User Data MSD points to reason and DATA-CTL
; fields of the outgoing message. The User Data
; field starts with a one-byte binary count of the
; bytes in the field.
; CALL NSPABO
; Normal Return
;Changes T1,T2,T3,T4
;We can't call RETBUF just now and be sure that it will clear out
;all the messages on the XMQ,RCQ and AKQ queues, because there may
;still be some messages Out In Router. CHKCLS checks for this,
;and then checks the ELABO flag when ELORC (out in Router).
;When ELORC is zero, CHKCLS calls RETBUF for us.
NSPABO: JSP T6,NSPLCQ
NSIABO: LOAD T1,MBAR1,(MB) ;RESTORE CALLER'S ARGS TO ACS T1 & T2
CALL FNDPID ;GET PORT POINTER IN EL, BUG IF FAIL
CALLRET FREMSG ;IGNORE CALL IF BUG GIVEN
LOAD P1,ELSTA,(EL) ;GET CURRENT PORT STATE
IFNSTATE P1,<CC,RN>,SCNIRS
;SESSION CONTROL: NOT IN RUN STATE
SETONE ELABO,(EL) ;SET THE ABORT FLAG
;ELABO TELLS CHKCLS TO SEND VERY SOON
;AS LONG AS NOT CI STATE, NO CI MSG IN ELDIM
STOR MB,ELDIM,(EL) ;STORE MSG POINTER TO SEND LATER
NEWSTATE DI ;GO INTO "DI" STATE
CALLRET SCLOSE ;START UP THE CLOSE FUNCTION
SUBTTL Session Control Calls -- NSPCLS - Close Port
;NSPCLS - Session Control Close Port Call
;
;Call: T1/ NSPpid
; T2/ SLBid in case this is an early release or RESET (or zero)
; CALL NSPCLS
; Normal Return
;Changes T1,T2,T3,T4
;NSPCLS can be called with or without a message block. It is called
;with a message block if there is any chance that the call will have to
;be queued on the NSP interlock, eg, when NSP calls NSPCLS itself or
;when Session Control calls NSPCLS in a subroutine called by NSP.
;
;NSPCLS is called without a message block from RESET and RELEASE, which
;must not fail, and thus cannot affort the risk of requiring a message
;block. Both these funtions are always called from UUO level and never
;from Session Control nested under NSP.
NSPCLS: XMOVEI T6,NSICLS ;GET ADDRESS OF INTERLOCKED CODE
JUMPN MB,NSPLCQ ;USE Q'D INTERLOCK IF WE HAVE MSG BLK
CALLRET NSPLCW ;ELSE WAIT FOR INTERLOCK
NSICLS: JUMPE MB,NSICL0 ;NO SAVED ARGS IF NO MSG BLK
LOAD T1,MBAR1,(MB) ;IF WE Q'D THE CALL,
LOAD T2,MBAR2,(MB) ; WE SAVED THE ARGS
NSICL0: DMOVE P1,T1 ;KEEP NSPpid AND SLBid FOR LATER
SKIPE T1,MB ;IF WE HAVE A MSG BLK,
CALL DNFMSG ; FREE IT
MOVE T1,P1 ;RESTORE NSPpid
CALL FNDPID ;(T1)GET PORT POINTER IN EL, BUG IF FAIL
RET ;IGNORE CALL IF BUG GIVEN
STOR P2,ELSCB,(EL) ;IN CASE WE DON'T HAVE SLBid YET (RESET)
SETZRO ELSCM,(EL) ;DON'T SEND CONNECT ACK IF WE WERE TEMPTED
LOAD T1,ELSTA,(EL) ;GET OLD PORT STATE
IFNSTATE T1,<CI>,NSICL1 ;LINK IN CI STATE?
OPSTR <SKIPE T1,>,ELDIM,(EL) ;YES, THERE A CONNECT INIT MESSAGE THERE?
CALL DNFMSG ;YES, FREE IT
SETZRO ELDIM,(EL) ; AND FORGET IT
JRST NSICL2 ;SKIP RUN-STATE CHECKING
NSICL1: IFSTATE T1,<CR,CC,RN> ;IF RUNNING,
CALL CLSABO ; SEND AN ABORT IF WE CAN
NSICL2: NEWSTATE CL ;NOW GO INTO "CL" STATE
CALLRET SCLOSE ;START THE CLOSE SEQUENCE
SUBTTL CLSABO - Send a free "Abort by Object"
;CLSABO - Send a free "Abort by Object" if closed from RUN state
;
;Call:
; CALL CLSABO
; Normal Return
;Changes T1,T2,T3,T4
;
;If a link is closed (by .NSFRL (release) function or by RESET), we
;send a DI msg with Abort by Object message. Since the link will be
;closed immediately thereafter, we do not ask Router to return the
;message on output done; it is not error controlled. If the message
;is lost, the remote node will have to wait for its inactivity timer
;to expire as DNA would have required anyway. Note that since we tell
;Router this msg is not ACKable, we return the msg blk to the free
;pool quickly as required in comment above.
CLSABO: SAVEAC ES
MOVEI T1,0 ;NO USER DATA
CALL DNGMSG ;GET A MESSAGE BLK
RET ;CAN'T, WELL DON'T SEND THE ABORT THEN
MOVE MB,T1 ;PICK UP PTR TO NEW MSG BLK
XMOVEI ES,EL.NSL(EL) ;WE'LL SEND THIS ON THE NSL
CALL MAKHDR ;INITIALIZE DNPxBY FOR THE NSP HEADER
;MSGFLG
MOVX T1,MGFDI ;ITS A DISCONNECT INITIATE MESSAGE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;GET THE REMOTE LINK ADDRESS
CALL DNP2BY
;SLA
LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS
CALL DNP2BY
;Here we give the DI message the next message number for the
;"normal" sublink. We do not update ESLMA so that CLSRMT won't
;think we're waiting for this message.
LOAD T1,ESLMA,(ES) ;GET LAST MESSAGE NUMBER ASSIGNED
AOS T1
ANDI T1,MASK.(WID(ESLMA),35) ;RESULT IN T1 MUST BE CYCLED TOO
STOR T1,NMSGN,(MB)
;REASON
MOVX T1,RSNABO ;'ABORT BY OBJECT' REASON CODE
CALL DNP2BY ;2 BYTE FIELD
;USER DATA
MOVX T1,0 ;ZERO BYTE USER DATA
CALL DNP1BY ;1 BYTE COUNT FIELD
MOVX T1, ST%NRS ! ST%NAK ! ST%NRQR ! ST%TRL
CALLRET SNDRTR ;NO RETURN TO SESSION CONTROL
;NO ACK REQUIRED
;NO RETURN REQUESTED FROM ROUTER
;TROLL ALLOWED
SUBTTL PROCXQ - Process the Transmit Queue
;PROCXQ - Process the Transmit Queue, sending any messages we can
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; CALL PROCXQ
; Normal Return
;Changes T1,T2,T3,T4
;Alternate entry point for normal path, so it doesn't have to queue
;message only to dequeue it right away.
PROCXB: LOAD T1,QHBEG,+ES.XMQ(ES) ;GET 1ST MSG ON TRANSMIT QUEUE
TMNN ESXOF,(ES) ;TRANSMISSION TURNED OFF?
JUMPE T1,PROCX2 ;NO, DON'T QUEUE NEW MSG IF QUEUE WAS EMPTY
XMOVEI T1,ES.XMQ(ES) ;GET A POINTER TO THE XMT-Q HEADER
CALL QSRTMB ;QUEUE MESSAGE IN (MB) TO SEND
BUG.(CHK,LLIXM2,LLINKS,SOFT,<Duplicate msg queued for xmit>,,<
Cause: This BUG is not documented yet.
>,FREMSG)
JRST PROCX0 ;DON'T NEED THE SAVEAC FROM NSISEG
;Entry point from all routines other than NSISEG
PROCXQ: SAVEAC <MB,MS>
PROCX0: TMNE ESXOF,(ES) ;TRANSMISSION TURNED OFF?
RET ;YES, IMMEDIATE SUCCESS RETURN
PROCX1: DEQUE MB,ES.XMQ(ES),MB.NXT,RTN ;RTN IF QUEUE IS EMPTY
PROCX2: LOAD T1,ESXFL,(ES) ;CASE OF THE FLOW
JRST @.+1(T1) ; CONTROL TYPE
IFIW PROCXN ;NO FLOW CONTROL
IFIW PROCXS ;SEGMENT FLOW CONTROL
IFIW PROCXM ;MESSAGE FLOW CONTROL
IFIW PROCXI ;ILLEGAL FLOW CONTROL
;The No Flow Control case
PROCXN: CALL SNDATA ;NO FLOW CONTROL, JUST SEND THE DATA
JRST PROCX1 ;TRY TO DEQUEUE ANOTHER MESSAGE
;The Segment Flow Control case
PROCXS: LOADE T1,ESXRD,(ES) ;GET XMIT-TO-REMOTE DRQS, EXTENDED LIKE HRRE
SOJL T1,PROCXR ;IF NO DATA REQUESTS, REQUEUE MESSAGE
STOR T1,ESXRD,(ES) ;DECREMENT XMIT-TO-REMOTE DATA REQUESTS
STOR T1,ESXLD,(ES) ;XLD AND XRD ALWAYS IN SYNCH IN SEG MODE
CALL SNDATA ;GO AHEAD AND SEND
JRST PROCX1 ;TRY TO DEQUEUE ANOTHER MESSAGE
;The Message Flow Control case; "other" sublink always comes here
PROCXM: JN NMCNT,(MB),PRCXM2 ;SEND NOW IF NOT FIRST SEND FOR THIS MSG
TMNN ESOTH,(ES) ;IS THIS THE "OTHER" SUBLINK?
JRST PRCXM1 ;NO
LOAD T1,ESLAR,(ES) ;YES, ANY MESSAGES TO BE ACKED?
AOS T1 ;WE KNOW THIS INT MSG IS NOT YET ACKED
ANDX T1,MASK.(WID(ESLMA),35) ;WRAP AROUND
CMODE T1,ESLMA,(ES) ;LAST MSG # ASSIGNED = LAST ACK REC'D?
JRST PROCXR ;NO, CAN'T SEND ON "OTHER" SUBLINK.
PRCXM1: LOAD T1,ESXRD,(ES) ;NO NEG DATA REQUESTS IN MESSAGE MODE
SOJL T1,PROCXR ;LEAVE IF NO DATA REQUESTS AVAILABLE
TMNN MBEOM,(MB) ;DO WE HAVE EOM?
JRST PRCXM2 ;NO, DON'T DIDDLE THE DRQS
STOR T1,ESXRD,(ES) ;YES, DECR XMIT-TO-REMOTE DATA REQUESTS
OPSTRM <SOS>,ESXLD,(ES) ; AND DECR WHAT SCTL THINKS IS LEFT
PRCXM2: CALL SNDATA ;SEND A DATA MESSAGE
JRST PROCX1 ;TRY TO DEQUEUE ANOTHER MESSAGE
;The Illegal Flow Control case
PROCXI: BUG.(CHK,LLIIFC,LLINKS,SOFT,<Illegal flow control type>,,<
Cause: This BUG is not documented yet.
>,PROCX1)
;Here when PROCXx couldn't send the message in MB for some reason. MB
;still points at the message, and we have to put it back on the
;transmit queue that we took it off.
PROCXR: XMOVEI T1,ES.XMQ(ES) ;GET POINTER TO TRANSMIT QUEUE
CALL QSRTMB ;PUT MB'S MESSAGE BACK ON QUEUE
BUG.(CHK,LLIXR2,LLINKS,SOFT,<Duplicate msg requeued for xmit>,,<
Cause: This BUG is not documented yet.
>,FREMSG)
RET
;SNDATA - Routine called by PROCXx when it has decided to send a message
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message
; CALL SNDATA
; Normal Return
;Changes T1,T2,MS
SNDATA: JN NMCNT,(MB),SNDAT2 ;JUMP IF THIS IS NOT FIRST SEND FOR MESSAGE
CALL MAKHDR ;INITIALIZE THE NSP HEADER MSD AND DNxyBY
;MSGFLG
MOVX T1,MGFINT ;ASSUME ITS AN INTERRUPT MESSAGE
JN ESOTH,(ES),SNDAT1 ;JUMP IF "OTHER" SUBLINK
MOVX T1,MGFMSG ;ASSUME ITS "NORMAL", NO BOM OR EOM
MOVE T2,MB.FLG(MB) ;GET THE MESSAGE BLOCK FLAGS
TXNE T2,MBBOM ;BEG-OF-MESSAGE SET?
IORI T1,MGFBOM ;YES
TXNE T2,MBEOM ;END-OF-MESSAGE SET?
IORI T1,MGFEOM ;YES
SNDAT1: CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;REMOTE LINK ADDRESS IS DESTINATION
CALL DNP2BY
;SLA
LOAD T1,ELLLA,(EL) ;LOCAL LINK ADDRESS IS SOURCE
CALL DNP2BY
;ACKNUM
CALL WRTACK ;WRITE THE ACKNUM FIELD(S) & ZERO ESACK
;SEGNUM
LOAD T1,NMSGN,(MB) ;GET SEGMENT NUMBER ALREADY ASSIGNED
CALL DNP2BY
LOAD T1,ESOTH,(ES) ;GET THE "OTHER" SUBLINK FLAG
STOR T1,MBOTH,(MB) ;COPY IT TO THE MESSAGE BLOCK
MOVX T1,ST%RSC ! ST%ACK ! ST%NRQR ! ST%TRL
;RETURN TO SESSION CONTROL
;MESSAGE NEEDS AN ACK
;DON'T RETURN FROM ROUTER ON FAILURE
;ALLOW THE TROLL
CALLRET SNDRTR ;SEND TO ROUTER
;Here if we are resending the message
SNDAT2: MOVX T1,ST%NRQR ! ST%TRL ;WE KNOW WE'LL ALLOW THE TROLL
; AND WE DON'T REQUEST RETURN (ONLY CI)
LOAD T2,NMFLG,(MB) ;GET THE MSG BLK FLAGS
TXNE T2,NM%ACK ;DID IT NEED AN ACK?
TXO T1,ST%ACK ;YES, PASS ON THE IDEA
TXNE T2,NM%RET ;DID IT WANT TO RETURN TO SCTL?
TXO T1,ST%RSC ;YES, PASS ON THE IDEA
CALLRET SNDRTR ;SEND TO ROUTER
SUBTTL SCNIRS - Tell Session Control Port is Not in Run State
;SCNIRS - Send a Not in Run State transaction to Session Control
;
;Call: MB/ The Message Block to return to Session Control
; CALL SCNIRS
; Normal Return
;Changes T1,T2,T3,T4
SCNIRS: LOAD T1,ELSCB,(EL) ;GET SESSION CONTROL'S PORT ID
;T2 IS IGNORED
MOVEI T3,SV.NRN ;NOT-IN-RUN-STATE FUNCTION CODE
MOVE T4,MB ;MESSAGE BLOCK POINTER IN T4
OPSTR <CALLRET @>,ELSCV,(EL) ;CALL SESSION CONTROL
SUBTTL SNDRTR - Send a Message to Router
;SNDRTR - Last chance to fondle messages bound for Router.
;
;Call: T1/ Flags, see below
; MB/ Message Block
; EL/ Port Block
; CALL SNDRTR
; Normal Return
;Changes T1,T2,T3,T4
;This routine is called with a message to send and some flags that
;govern the sending. Each flag has a zero counterpart so that all
;callers can specify all the flags. That way its harder to miss
;what's going on.
ST%RSC== 1B35 ;RETURN THIS MESSAGE TO SC
ST%NRS== 0B35 ;DON'T
ST%RQR== 1B34 ;RETURN REQUESTED FROM ROUTER ON FAILURE
ST%NRQ== 0B34 ;DON'T
ST%ACK== 1B33 ;THIS MESSAGE NEEDS AN ACK
ST%NAK== 0B33 ;DOESN'T
ST%TRL== 1B32 ;ALLOW THE TROLL TO STEAL THIS MESSAGE
ST%NTR== 0B32 ;DON'T
SNDRTR: SAVEAC P1
MOVE P1,T1 ;SAVE FLAGS PASSED TO THIS PROCEDURE
MOVX T1,NMRET ;GET THE RETURN TO SCTL FLAG
TXNE P1,ST%RSC ;RETURN THIS MESSAGE TO SESSION CONTROL?
IORM T1,NM.RET(MB) ;YES, TELL THE MESSAGE BLOCK
MOVE T1,RTRADR ;GET MY NODE NUMBER
STOR T1,MBSRC,(MB) ;STORE AS SOURCE NODE
LOAD T1,ELNNM,(EL) ;GET REMOTE'S NODE NUMBER
STOR T1,MBDST,(MB) ;STORE AS DESTINATION NODE
STOR EL,NMPRT,(MB) ;ACKABLES AND CIs NEED EL STORED
;Session Control has already stored MBBOM and MBEOM
; if this is a user's message segment
LOAD T1,ELNDB,(EL) ;GET PTR TO NODE BLOCK FOR THIS LINK
INCR ELPKS,(EL) ;INCREMENT THE COUNT OF MESSAGES SENT
INCR NNXMC,(T1) ;UPDATE COUNT OF MESSAGES SENT TO NODE
TXNN P1,ST%ACK ;WILL WE GET THIS MESSAGE BACK?
JRST SNDRT1 ;NO, DON'T COUNT OR TIME THIS MSG
SETONE NMACK,(MB) ;TELL NSPODN WE NEED AN ACK
INCR ELORC,(EL) ;INCR THE OUT-IN-ROUTER COUNT
INCR NMCNT,(MB) ;INCR # OF TIMES WE'VE SENT THIS MSG
;Note that we timestamp all ST%ACK messages, whether first send
;or not. This is the resend timer, round-trip delay timer is
;separate, see end of this routine.
;
;A CI message is timed with ELDTM, but is not acked, so NSIACT
;has to fill in ELDTM for CI messages itself.
CALL DNGTIM ;GET A TIMESTAMP IN T1
STOR T1,NMTIM,(MB) ;STAMP IT NOW, SAVE T1 FOR STOR BELOW
TMNE ELDTM,(EL) ;A DELAY-CALC TIMER GOING NOW?
JRST SNDRT1 ;YES, DON'T RESTART IT AGAIN
STOR T1,ELDTM,(EL) ;NO, START ONE NOW
LOAD T1,NMSGN,(MB) ;GET THIS MSG'S SEGMENT NUMBER
STOR T1,ELDSG,(EL) ;THIS IS THE SEGMENT BEING TIMED
LOAD T1,MBOTH,(MB) ;GET "OTHER" SUBLINK FLAG
STOR T1,ELDTO,(EL) ;REMEMBER WHICH SUBLINK WE'RE TIMING
SNDRT1:
IFN FTTROLL,<
CALL SNXTRL ;CHECK THE TROLL
RET ;TROLL ATE THE MESSAGE
>;END OF IFN FTTROLL
IFN FTTRACE,<
LOAD T1,NMMGF,(MB) ;GET THE MESSAGE TYPE (MSGFLG FIELD)
XMOVEI T2,[ASCIZ \Sent\]
CALL TRCMSG
>;END OF IFN FTTRACE
LOAD T1,ELCIR,(EL) ;GET LOOPBACK CIRCUIT IF ANY
STOR T1,MBCHN,(MB) ;AND STORE FOR ROUTER
MOVEI T1,0 ;PREPARE THE FLAGS WORD FOR ROUTER CALL
TXNE P1,ST%RQR ;TELL ROUTER TO RETURN IF SEND FAILS?
TXO T1,RT%RQR ;YES, SET "RETURN REQUESTED"
TXNE P1,ST%ACK ;DOES THIS MESSAGE NEED AN ACK?
TXO T1,RT%ODN ;YES, TELL ROUTER TO GIVE US ODN CALL
CALLRET RTRXMT ;SEND THE MESSAGE AND RETURN
;SNXTRL - See if the troll wants to eat this message
;
;Call: P1/ Flags passed to SNDRTR
; CALL SNXTRL
; Error Return if the troll ate the message
; Normal Return
;
;Changes T1
IFN FTTROLL,<
SNXTRL: TXNE P1,ST%TRL ;ALLOWED TO USE THE TROLL ON THIS CALL?
SKIPN NSPTRI ;YES, TROLL INITIALIZED?
RETSKP ;NO, GO SEND THE MESSAGE NOW
SOSLE NSPTRL ;YES, DECREMENT THE TROLL, AT ZERO YET?
RETSKP ;NO, GO SEND THE MESSAGE
MOVE T1,NSPTRI ;YES, RE-INITIALIZE THE TROLL
MOVEM T1,NSPTRL ; FOR NEXT TIME
IFN FTTRACE,<
LOAD T1,NMMGF,(MB) ;GET THE MESSAGE TYPE (MSGFLG FIELD)
XMOVEI T2,[ASCIZ \Troll ate\]
CALL TRCMSG
>;END OF IFN FTTRACE
TXNN P1,ST%ACK ! ST%RQR ;IS ODN INTERESTED IN MESSAGE?
JRST FREMSG ;NO, JUST DEALLOCATE AND RETURN
TXNE P1,ST%RQR ;YES, ROUTER RETURN REQUESTED?
JRST NSPRTS ;YES, PRETEND RETURN TO SENDER
JRST NSPODN ;NO, MUST BE WAITING FOR ACK
>;END OF IFN FTTROLL
SUBTTL ROUTER Calls -- NSPRCV - Receive a Message from Router
;NSPRCV - Receive a Message from Router
;
;Call: The arguments are in the public area of the message block
; MB/ The Message Block
; CALL NSPRCV
; Normal Return
;Changes T1,T2,T3,T4
; C A U T I O N
;This page has some careful coding for non-zero sections. See comment
;above the BEGSTR RT definitions.
;
;In this routine, MS is for DNGxBY calls
; P1 holds the UPTO count, full-word count
; P2 holds the MSGTBL flags, right justified
NSPRCV: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSIRCV: CALL INIHDR ;SET UP MS (ALREADY SAVED) FOR DNGxBY
CALL DNRPOS ;READ CURRENT POSITION INTO T1
STOR T1,NMMK1,(MB) ;STORE AS NSP'S MARK 1
;MSGFLG
CALL DNGSBY ;SKIP IF NOT EXTENSIBLE, BYTE IN T1
EVENT(MSG,Extensible MSGFLG field,FREMSG)
STOR T1,NMMGF,(MB) ;TELL MESSAGE BLOCK THE MESSAGE TYPE
LSH T1,-2 ;THE LOW-ORDER 2 BITS ARE ALWAYS ZERO
CAILE T1,RCVMAX ;IS IT A LEGAL MESSAGE TYPE?
JRST RCVILM ;NO, EVENT AND TOSS IT
MOVE P1,T1 ;SAVE FOR CALLRET BELOW
CALL RCVPRC ;DO PRELIMINARY RECEIVE PROCESSING
RET ;ERROR ALREADY GIVEN, AND MSG BLK FREED
;SUCCESSFUL PRELIMINARY PROCESSING
HRRZ T1,MSGTBL(P1) ;ADDRESS MUST BE IN THIS LOCAL SECTION!
CALLRET (T1) ;GO DO THE MAIN RECEIVE ROUTINE
;Here if we get an illegal message type
RCVILM: EVENT(MSG,Illegal message type recvd,FREMSG)
SUBTTL ROUTER Calls -- NSPODN/NSPOND - Output Done/Not Done
;NSPODN - Get a message back from Router after message has been sent
;
;Call: CALL NSPODN
; Normal Return
;Changes T1,T2,T3,T4
;It is OK to use the port block pointer in the message block, because
;even if Session Control did try to close the port while the message
;block was languishing in Router, NSP will not actually close the
;port until all expected messages are returned from Router.
NSPODN: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSIODN: LOAD EL,NMPRT,(MB) ;SEE BLOCK COMMENT ABOVE
XMOVEI ES,EL.OSL(EL) ;ASSUME "OTHER" SUBLINK
TMNN MBOTH,(MB) ;WAS IT THE "OTHER" SUBLINK?
XMOVEI ES,EL.NSL(EL) ;NO, IT WAS "NORMAL"
TMNN NMACK,(MB) ;DOES THE MESSAGE NEED AN ACK?
BUG.(CHK,LLIODN,LLINKS,SOFT,<NSIODN got message with NMACK=0>,,<
Cause: This BUG is not documented yet.
>,FREMSG)
OPSTRM <SOS T1,>,ELORC,(EL) ;ONE LESS OUT-IN-ROUTER
JUMPGE T1,NSIOD2 ;JUMP IF NO BUG
BUG.(CHK,LLIORC,LLINKS,SOFT,<ORC should never be negative>,,<
Cause: This BUG is not documented yet.
>)
SETZRO ELORC,(EL) ;WEAK ATTEMPT TO RECOVER
NSIOD2:
LOAD T1,NMSGN,(MB) ;GET THE MESSAGE NUMBER
CMODLE T1,ESLAR,(ES) ;HAVE WE ALREADY RECEIVED ACK FOR IT?
JRST NSIOD3 ;NO, GO QUEUE IT FOR ACK
;YES, A SPEEDY ACK, PERHAPS LOCAL TSK
TRACE NSP,<Msg already ACKed at NSIDON>
MOVX T1,MA%DONE ;SAY THE SEND WAS DONE
CALLRET MGACKD ; AND TELL SESSION CONTROL THE MESSAGE
; HAS BEEN ACKED
NSIOD3: XMOVEI T1,ES.AKQ(ES) ;MESSAGE NOT ACKED, LOAD UP QUEUE HEADER
CALL QSRTMB ;GO QUEUE IT UP IN SORTED ORDER
BUG.(CHK,LLIAK2,LLINKS,SOFT,<Duplicate msg put on ACK queue>,,<
Cause: This BUG is not documented yet.
>,FREMSG)
RET
SUBTTL ROUTER Calls -- NSPRTS - Return a Message to Sender
;NSPRTS - Called by Router to return a message to sender
;
;Call: The arguments are in the public area of the message block
; MB/ The Message Block
; CALL NSPRTS
; Normal Return
;Changes T1,T2,T3,T4
;NSPRTS is only called when a message sent with the RT%RQR flag set is
;not sent. This can be returned either by the local Router or by a
;route-through Router when the target node is not available.
;NSP only lights the RT%RQR flag for CI messages.
;We can't use anything stored in the message block, because this may
;not be the same message block that we sent out. The message may have
;been sent out on the network and then returned by a route-through node
;which could not forward the message due to a change in the topology.
;Router guarantees that however we got this message, IN.MSD will be
;pointing at the beginning of the NSP header when we get it.
;We don't have to decrement the number of messages outstanding in
;Router, because we didn't count this one when we sent it.
NSPRTS: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSIRTS: CALL INIHDR ;WE STILL HAVE THE PORT, SET UP TO READ MSG
;MSGFLG
CALL DNGEBY ;GET AN EXTENSIBLE BYTE
EVENT(MSG,No MSGFLG in RTS,FREMSG)
STOR T1,NMMGF,(MB) ;SAVE MSGFLG TYPE IN THE MESSAGE BLOCK
CAXE T1,MGFCI ;IS IT A CI MESSAGE?
CAXN T1,MGFRCI ; OR A RETRANSMITTED CI MESSAGE?
JRST NSIRT1 ;YES, NO PROBLEM
ETRACE NSP,<Non-CI message returned>
CALLRET FREMSG ;NO, IGNORE IT (SHOULD NOT HAPPEN)
NSIRT1:
;DLA
CALL DNG2BY ;GET THE LOCAL LINK ADDRESS
EVENT(MSG,No DLA in RTS,FREMSG)
MOVE P1,T1
;SLA
CALL DNG2BY ;GET REMOTE LINK ADDRESS IN T1
EVENT(MSG,No SLA in RTS,FREMSG)
MOVE T2,P1 ;GET BACK LLA FOR FNDPRT
CALL FNDPRT ;SEE IF THE PORT STILL EXISTS
TRNA ;NO
JRST NSIRT2 ;YES
ETRACE NSP,<Can't find port for returned msg>
CALLRET FREMSG
NSIRT2: LOAD T1,ELSCB,(EL) ;GET SESSION CONTROL'S PORT NAME
MOVX T3,SV.NCM ;NO COMMUNICATION FUNCTION CODE
MOVE T4,MB ;SESSION CONTROL WANTS IT THAT WAY
OPSTR <CALL @>,ELSCV,(EL) ;CALL SESSION CONTROL
NEWSTATE NC ;GO INTO NO COMMUNICATION STATE
CALLRET SCLOSE ;START THE CLOSE PROCESS
SUBTTL Message Receivers -- RCVPRC - Preliminary Processing
;RCVPRC - Preliminary Processing
;
;Call: T1/ Full-word pointer to the MSGTBL entry
; MB/ The Message Block
; EL/ The Port Block
; CALL RCVPRC
; Error Return
; Normal Return, if UPTO .GE. the SLA, sets up registers EL & ES
;Changes T1,T2,T3,T4
;MSGTBL defines an UPTO field for each message type. RCVPRC will
;process fields in each incoming message up to the field specified in
;MSGTBL. NSP headers for messages with segment numbers (data, interrupt
;& link service) are fully processed here. Headers for other messages
;are processed here to the extent that they correspond to numbered
;messages.
RCVPRC: SAVEAC <P1,P2>
LOAD P1,RTUPT,+MSGTBL(T1) ;GET "UPTO" COUNT FOR THIS MSG TYPE
LOAD P2,RTFLG,+MSGTBL(T1) ;GET RECEIVE TABLE FLAGS FOR MSG TYPE
MOVX T1,MBOTH ;GET MSG BLK'S "OTHER" SUBLINK FLAG
ANDCAM T1,MB.OTH(MB) ;ASSUME WE'RE ON THE "NORMAL" SUBLINK
TXNE P2,RT%OTH ;IS MESSAGE FOR THE "OTHER" SUBLINK?
IORM T1,MB.OTH(MB) ;YES, SET FLAG IN THE MESSAGE BLOCK
IFN FTTRACE,<
LOAD T1,NMMGF,(MB) ;GET MESSAGE TYPE
CALL TRCRCV ;TRACE RECEIPT OF MESSAGE
>
SOJL P1,RSKP ;UPTO INCLUDE THE DLA?
;DLA
CALL DNG2BY
EVENT(MSG,No DLA,FREMSG) ;BAD MESSAGE EVENT TYPE
STOR T1,NMLLA,(MB) ;STORE THE DLA AS THE LOCAL LINK ADDRESS
SOJL P1,RSKP ;UPTO INCLUDE THE SLA?
;SLA
CALL DNG2BY ;GET THE SLA
EVENT(MSG,No SLA,FREMSG) ;BAD MESSAGE EVENT TYPE
STOR T1,NMRLA,(MB) ;STORE THE SLA AS THE REMOTE LINK ADDRESS
MOVE T2,T1 ;REMOTE LINK ADDRESS INTO T2 FOR FNDPRT
LOAD T1,NMLLA,(MB) ;LOCAL LINK ADDRESS INTO T1 FOR FNDPRT
CALL FNDPRT ;FIND THE PORT BLOCK, POINTER TO EL
JRST [TXNE P2,RT%RSP ;NO SUCH LINK, SUPPOSED TO RESPOND?
CALLRET SENDNL ;YES, SEND A NO-LINK MESSAGE
CALLRET FREMSG] ;NO, JUST IGNORE THE MESSAGE
; AND THEN GIVE A NON-SKIP ERROR RETURN
LOAD T1,ELNDB,(EL) ;GET PTR TO NODE BLOCK FOR THIS LINK
INCR ELPKR,(EL) ;INCREMENT THE NUMBER OF MESSAGES RECIEVED
INCR NNRMC,(T1) ;UPDATE COUNT OF MESSAGES FROM NODE
XMOVEI ES,EL.NSL(EL) ;ASSUME "NORMAL" SUBLINK
TXNE P2,RT%OTH ;"OTHER" SUBLINK?
XMOVEI ES,EL.OSL(EL) ;YES.
SOJL P1,RSKP ;UPTO INCLUDE THE DLA?
;Continued on Next Page with ACKNUM field
;Continued from Previous Page, ready for ACKNUM field
;Note that there is a peculiarity in the ACKNUM field. It is not
;there if the two bytes bytes in this position in the message do not
;have their high-order bit turned on. If that bit is off, then the
;two byte field is the SEGNUM field.
;ACKNUM
CALL DNG2BY ;GET 2 BYTES WHICH MIGHT BE THE ACKNUM
EVENT(MSG,No predicted ACKNUM,FREMSG) ;BAD MESSAGE EVENT TYPE
TXNN T1,AKPNT ;IS ACK FIELD "PRESENT"?
JRST RCVPR0 ;NO, GO SEE WHO'S FIELD IT IS
CALL PRCACK ;YES, PROCESS THE ACKNUM FIELD
IFE FTPH4,JRST RCVPR2 ;GO CHECK SEGNUM FIELD
IFN FTPH4,< ;PHASE IV CODE
LOAD T1,ELVER,(EL) ;GET REMOTE NSP VERSION
CAIE T1,VER4.0 ;IS IT VERSION 4.0?
JRST RCVPR2 ;NO, NO CROSS-SUBCHANNEL ACKING
CALL DNG2BY ;YES, GET NEXT 2-BYTE FIELD
JRST RCVPR2 ;NONE, SEE IF WE WANTED ONE
TXNN T1,AKPNT ;IS CROSS ACK FIELD "PRESENT"?
JRST RCVPR1 ;NO, GO SEE WHO'S FIELD IT IS
CALL PRCACK ;YES, PROCESS CROSS-SUBCHN ACKNUM FIELD
JRST RCVPR2 ;THEN LOOK FOR THE SEGNUM FIELD
>;END IFN FTPH4
RCVPR0: TXNE P2,RT%ACK ;OK TO SKIP THE ACKNUM FIELD?
EVENT(MSG,Missing required ACKNUM,FREMSG) ;NO, TELL NTMAN & TOSS
RCVPR1: SOJGE P1,RCVPR3 ;IF WE WANTED SEGNUM, STORE IT
MOVEI T1,2 ;NO SEGNUM? BACK UP 2 BYTES
CALL DNBKBY ; TO RE-INTERPRET FIELD WHICH IS NOT ACKNUM
RCVPR2: SOJL P1,RSKP ;UPTO INCLUDE THE SEGNUM FIELD?
;IF THIS SOJL JUMPS, ITS AN ACK MESSAGE
;SEGNUM
CALL DNG2BY ;GET SEGNUM IF LAST ONE WAS INDEED ACKNUM
EVENT(MSG,No predicted SEGNUM,FREMSG) ;BAD MESSAGE EVENT TYPE
RCVPR3: STOR T1,NMSGN,(MB) ;STORE SEGMENT NUMBER IN MESSAGE BLOCK
RETSKP ;SUCCESS RETURN
SUBTTL Message Receivers -- PRCACK - Process Received ACKNUM Field
;PRCACK - Process the ACKNUM field of an incoming message
;
;Call: T1/ The ACKNUM field
; EL/ The Port Block
; ES/ The Sublink Block
; CALL PRCACK, only if ACKNUM field is "present"
; Normal Return
;Changes T1,T2,T3,T4
PRCACK: SAVEAC <MB,P1,P2>
LOAD P2,AKQAL,+T1 ;GET THE QUALIFIER FIELD
CAIG P2,AK$QNK ;IS IT ACK OR NAK?
JRST PRCAK1 ;YES, OK
IFE FTPH4,<EVENT(MSG,Bad ACKNUM byte,RTN)> ;NO, BAD ACKNUM FIELD
IFN FTPH4,< ;PHASE IV CODE
LOAD T4,ELVER,(EL) ;NO, GET VERSION # OF REMOTE NSP
CAIN T4,VER4.0 ;IS IT VERSION 4.0?
CAILE P2,AK$CNK ;YES, IS IT A CROSS SUB-CHN ACK OR NAK?
EVENT(MSG,Bad ACKNUM byte,RTN) ;NO, BAD ACKNUM FIELD
;YES, POINT ES ACROSS SUB-CHANNEL
SAVEAC ES ;SAVE CALLER'S SUBLINK POINTER
XMOVEI T4,EL.OSL(EL) ;ASSUME WE'RE MOVING FROM 'NORMAL' TO 'OTHER'
TMNE ESOTH,(ES) ;CURRENTLY IN 'NORMAL' SUBLINK?
XMOVEI T4,EL.NSL(EL) ;NO, MOVING FROM 'OTHER' TO 'NORMAL'
MOVE ES,T4 ;SET UP ES TO POINT TO 'CROSS' SUBCHANNEL
>;END IFN FTPH4
;T1 must be preserved from PRCACK to PRCAK1
PRCAK1: LOAD P1,AKNUM,+T1 ;PRESERVE THE ACK NUMBER
CMODG P1,ESLAR,(ES) ;IF ACKNUM IS .LE. LAST ACK RECEIVED
TRCRET NSP,Ignoring stale ACK
CMODLE P1,ESLMA,(ES) ;IF .GT. LAST MSG ASSIGNED (SENT)
ETRCRET NSP,Ignoring early ACK
STOR P1,ESLAR,(ES) ;A NEW ACK, ITS NOW OUR LAST RECEIVED
MOVE T1,P1 ;PRESENT ACK NUMBER TO UPDELAY
CALL UPDELAY ;SEE IF IT WAS BEING TIMED
CALL DNGTIM ;GET A TIMESTAMP TO RESET ITS
STOR T1,ELTMA,(EL) ; INACTIVITY TIMER
;Continued on Next Page
;Continued from Previous Page
;Here we free up any messages ACKed by this ACKNUM field
PRCAK2: LOAD MB,QHBEG,+ES.AKQ(ES) ;GET PTR TO FIRST MESSAGE W/O DEQUING
JUMPE MB,PRCAK3
CMODGE P1,NMSGN,(MB) ;IF NEW ACK IS .LT. THIS MESSAGE NUMBER
JRST PRCAK3 ; NO MORE TO DO, SINCE QUEUE IS SORTED
DEQUE T2,ES.AKQ(ES),MB.NXT,PRCAK3 ;GO TO PRCAK3 IF Q EMPTY (?)
MOVX T1,MA%DONE ;SEND WAS DONE
CALL MGACKD ;RETURN OR DESTROY MESSAGE
JRST PRCAK2
PRCAK3:
;When we receive a NAK (probably, but not necessarily from a Phase II
;NSP) we zero the NMTIM field in all the remaining unACKed msgs for
;this sublink. Note that CHKRSN will not retransmit a msg to a phase
;II node unless its NMTIM field has been so zeroed.
CAIE P2,AK$QNK ;IS THIS A NAK
CAIN P2,AK$CNK ; OR A CROSS-SUBCHANNEL NAK?
CAIA ;YES
JRST PRCAK5 ;NO
LOAD MB,QHBEG,+ES.AKQ(ES) ;YES, GET FIRST MSG ON ACK Q
JUMPE MB,PRCAK5 ;JUMP WHEN WE'VE FINISHED LIST
PRCAK4: SETZRO NMTIM,(MB) ;ZERO TIMER SO MSG WILL BE RESENT SOON
LOAD MB,MBNXT,(MB) ;GET PTR TO NEXT MSG BLK ON ACK Q
JUMPN MB,PRCAK4 ;RESEND ALL MSGS IN ACK QUEUE
PRCAK5:
;Only one message is allowed out on the "other" sublink at any one
;time. So if this is an "other" ACK, we request jiffy service to send
;any "other" sublink messages which may have been blocked waiting for
;this ACK. We don't send the waiting message now, because if this ACK
;we are processing now is piggybacked on a link service message, we
;will be able to piggyback its ACK on the outgoing message when LKSACK
;gets a chance to request an ACK.
TMNN ESOTH,(ES) ;IS THIS THE "OTHER" SUBLINK?
RET ;NO, ONLY RETURN FROM PRCACK
CALLRET NSPRJF ;YES, SEND INTERRUPT AND/OR DRQs SOON
SUBTTL Message Receivers -- Receive a Data Segment
;RCVxxS - Envelope routines to receive a data segment
; The xx in the names varies with the BOM and EOM flags
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message Block
; CALL RCVxxS
; Normal Return
;Changes T1,T2,T3,T4
;When we get here, RCVPRC has read the NSP header, IN.MSD is now:
; MSGFLG DLA SLA [ACKNUM] SEGNUM <-> DATA
RCVONS: SETONE <MBBOM,MBEOM>,(MB) ;ONLY SEGMENT, BOTH BOM AND EOM SET
CALLRET RCVSEG
RCVBGS: SETONE MBBOM,(MB) ;BEGIN SEGMENT, BOM SET, EOM CLEAR
SETZRO MBEOM,(MB)
CALLRET RCVSEG
RCVMDS: SETZRO <MBBOM,MBEOM>,(MB) ;MIDDLE SEGMENT, BOTH BOM AND EOM CLEAR
CALLRET RCVSEG
RCVENS: SETONE MBEOM,(MB) ;END SEGMENT, BOM CLEAR, EOM SET
SETZRO MBBOM,(MB)
CALLRET RCVSEG
;RCVSEG - Process in incoming data segment, for either sublink
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message Block, with MBBOM and MBEOM filled in
; CALL RCVSEG
; Normal Return
;Changes T1,T2,T3,T4
RCVSEG: LOAD T1,ELSTA,(EL) ;CHECK THAT WE'RE IN THE RIGHT STATE
IFNSTATE T1,<CC,RN,DI>,FREMSG ;FREE THE MESSAGE BLOCK
IFSTATE T1,CC ;IF IN CC STATE, WAITING FOR FIRST MESSAGE,
CALL PTIRUN ; PUT IN RUN STATE
XMOVEI T1,ES.RCQ(ES) ;GET PTR TO APPROPRIATE RECEIVE QUEUE
CALL QSRTMB ;ADD MESSAGE SORTED BY SEGNUM
JRST RCVSG1 ;DUPLICATE UNACKED MESSAGE RECEIVED
CALL PROCRQ ;PROCESS THE RECEIVE QUEUE, SEND UNUSED DRQS
TMNE QHBEG,+ES.RCQ(ES) ;IF ANYTHING ON THE QUEUE,
CALLRET CHKRCQ ; TRIM IT IF STILL TOO LONG AFTER PROCRQ
RET
;We do not decrement ESRRD,(ES) until we take the message off the
;receive queue for three reasons: (1) the receive queue may be
;stripped if the memory manager needs free message blocks, (2) some
;received messages may be stripped by CHKRCQ (q.v.), and (3) some of
;the messages on the receive queue may be duplicates. We do not find
;out that they are duplicates until we remove them from the receive
;queue.
;Here when we receive a message with the same number as a message we
;already have queued for Session Control. If we receive such a
;message, it means that the remote NSP is getting tired of waiting for
;an ACK which we have not yet sent because the user program is slow.
;If we receive a duplicate msg any yet we still have data requests
;outstanding from SCTL, we are blocked because of lost messages, not
;our slowness, so clear the queue (to free up memory for cleaner links)
;but don't pessimize the link (its already got too much traffic on it
;another link service message will just add to the rush. In this case
;the transmitter might consider holding back voluntarily.
;
;In this case, we discard all queued (unACKed) messages and send out
;some negative data requests to make the remote stop resending. We
;don't want the remote to time out the line just because the
;application is slow (or swapped out) our system is still here and
;listening!
RCVSG1: TMNE ESOTH,(ES) ;ASSURE WE'RE ON THE "NORMAL" SUBLINK
BUG.(CHK,LLIDIR,LLINKS,SOFT,<Duplicate Interrupt Message Received>,,<
Cause: We have just found a duplicate interrupt message on the
unacked interrupt receive queue. We should never be
able to get this, for we should never get out of the
NSP interlock with anything on this receive queue.
Cure: Either the interrupt flow control screwed up and we sent
more than one data request or the remote node sent an
interrupt message without a data request. Find out which.
>,CLRSRQ)
LOAD T1,NMSGN,(MB) ;GET THIS MSG'S SEGMENT NUMBER
SOSGE T1 ;CALC PREVIOUS NUMBER
ANDI T1,MASK.(WID(ESLMR),35) ;MOD MASK-SIZE
CMODE T1,ESLMR,(ES) ;IS THIS EXPECTED NEXT MESSAGE?
CALLRET FREMSG ;NO, Q ONLY OUT-OF-ORDER, DON'T PESSIMIZE
CALL FREMSG ;YES, FREE THE DUPLICATE MESSAGE
ETRACE NSP,<Duplicate unACKed msg rcvd, link 'pessimized'>
CALLRET PESFLO ;PUT LINK IN PESSIMISTIC FLOW CONTROL
SUBTTL Message Receivers -- Receive a Data Segment -- Check Length of Q
;CHKRCQ - Check the length of the receive queue.
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message Block
; CALL CHKRCQ
; Normal Return
;Changes T1,T2,T3,T4
;This routine is only called when this sublink's receive queue is not
;empty after calling PROCRQ.
;If the congestion flag is non-zero, we must not cache any incoming
;messages on the "normal" sublink. The "other" sublink will never
;cache anyway.
;The receive queue will exceed the goal only with message or no flow
;control modes (barring bugs). In these modes, the link will be
;turned on again when Session Control again sends a data request to NSP.
;We cannot queue out-of-order interrupt messages here for fear that
;they will interfere with link service messages.
CHKRCQ: TMNE ESOTH,(ES) ;IS THIS THE "OTHER" SUBLINK?
CALLRET CLRSRQ ;YES, CLEAR OUT "OTHER" QUEUE
;Here for the normal sublink
SKIPE DCNCON ;SYSTEM-CONGESTION FLAG SET?
JRST CHKRC1 ;YES, CLEAR UNACKED INPUT QUEUE
LOAD T1,ESGOL,(ES) ;GET CURRENT GOAL FOR LINK
LOADE T2,ESRLD,(ES) ;GET CURRENT QUOTA FOR LINK
CAMGE T1,T2 ;IF QUOTA IS LARGER THAN GOAL
MOVE T1,T2 ; USE QUOTA FOR COMPARISON
OPSTR <CAML T1,>,QHCNT,+ES.RCQ(ES) ;IS RECEIVE Q WITHIN RIGHTS?
RET ;YES, WE'RE OK
IFN FTTRACE,<
TMNE ESGOL,(ES) ;IF NOT ALREADY PESSIMIZED, TELL WATCHER
ETRACE NSP,<Busting overly optimistic link>
>;END OF IFN FTTRACE
CHKRC1: LOAD T2,QHBEG,+ES.RCQ(ES) ;GET PTR TO FIRST MSG ON RECEIVE Q
JUMPE T2,RTN ;SHOULD NOT JUMP
LOAD T1,NMSGN,(T2) ;GET MSG'S SEGMENT NUMBER
SOSGE T1 ;CALC PREVIOUS NUMBER
ANDI T1,MASK.(WID(ESLMR),35) ;MOD MASK-SIZE
CMODE T1,ESLMR,(ES) ;IS THIS EXPECTED NEXT MESSAGE?
CALLRET CLRSRQ ;NO, JUST CLEAR THE INPUT Q
CALLRET PESFLO ;YES, PESSIMIZE OVER-OPTIMISTIC LINK
SUBTTL Message Receivers -- PROCRQ - Process the Receive Queue
;PROCRQ - Process the receive queue, sending messages to Session Control
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; CALL PROCRQ
; Normal Return
;Changes T1,T2,T3,T4
;PROCRQ will check for data requests to be sent whether or not
;any messages have actually been passed to Session Control. This
;is so that caller does not have to make a special check for the
;first call after a link is opened. No data requests will be sent
;to the remote until Session Control makes its first request of NSP.
PROCRQ: SAVEAC <MB,MS> ;FOLLOWING ROUTINES NEED THIS
TMNE QHBEG,+ES.RCQ(ES) ;UNLESS THERE IS NOTHING ON IT,
CALL PRCRQS ; PROCESS THE RECEIVE QUEUE
JE ESROF,(ES),PRCRQ1 ;DON'T BOTHER THE OFF FLAG IF ITS CLEAR
JE ESRLD,(ES),PRCRQ1 ;ITS OFF, JUMP IF NO DRQs FROM SCTL
SETZRO ESROF,(ES) ;SHOULD BE ON NOW, TURN IT ON
SETONE ESROC,(ES) ; AND TELL CHKDRQ THAT 'OFF' HAS CHANGED
CALL NSPRJF ;GET JIFFY SERVICE TO SEND LINK SRV MSG
PRCRQ1: LOAD T1,ESGOL,(ES) ;GET DATA REQUEST GOAL
LOAD T4,ESRFL,(ES) ;GET RECEIVE FLOW CONTROL TYPE
JRST @.+1(T4) ;DISPATCH
IFIW PRCRQN ;NO FLOW CONTROL
IFIW PRCRQG ;SEGMENT FLOW CONTROL
IFIW PRCRQM ;MESSAGE FLOW CONTROL
IFIW PRCRQN ;ILLEGAL FLOW CONTROL
PRCRQG: LOADE T2,ESRLD,(ES) ;YES, GET RECEIVE LOCAL DRQ
CAMGE T1,T2 ;USE LARGER OF GOAL AND RLD
MOVE T1,T2 ; SO IF GOAL IS ZERO, WE USE RLD
PRCRQ2: LOADE T2,ESRRD,(ES) ;GET RECEIVE REMOTE REQS OUTSTANDING
SUB T1,T2 ;FIND HOW MANY WE CAN SEND NOW
JUMPL T1,PRCRQN ;DON'T SEND NEGATIVE REQUESTS UNTIL
; OTHER SYSTEMS KNOW HOW TO RECEIVE THEM
ADD T2,T1 ;REMEMBER THE NEW TOTAL OUTSTANDING
STOR T2,ESRRD,(ES)
LOADE T2,ESRSD,(ES) ;GET CURRENT COUNT TO SEND
ADD T1,T2 ; MAKE NEW TOTAL (POSSIBLY NEGATIVE)
STOR T1,ESRSD,(ES) ; AND TELL SENDRQ TO SEND THEM
JRST PRCRQ3 ;TOTAL DRQS TO SEND IN T1
;Please Note:
;The correct behavior of the "other" sublink depends on PRCRQM not
;sending any data requests when the data requests from SCTL fall to zero.
;The "other" sublink does not really use message flow control, so we fake
;it here. We must not send a data request, even though the "other" goal
;is one, when we have no permission from session control, for we will never
;cache an interrupt message received.
PRCRQM: JN ESRLD,(ES),PRCRQ2 ;ONLY SEND MSG DRQS IF SCTL HAS AT
; LEAST 1 OUTSTANDING RECV DRQ
PRCRQN: MOVEI T1,0 ;SEND NO DRQS
PRCRQ3: ;T1 CONTAINS DRQs TO SEND
TMNN ESACK,(ES) ;ANY MESSAGES NEED ACKING?
JRST PRCRQ4 ;NO, PASS DRQs TO SEND IN T1 TO PRCRQ4
TMNE ESBFR,(ES) ;YES, IS THIS A BUFFER-RICH SUBLINK?
JRST PRCRQ4 ;YES, DON'T SEND THE ACK YET
;If SNDACK is called on the "other" sublink, it will try to piggy
;back the ACKs on a link service message. We know that we are also
;buffer-poor on the "other" sublink (by definition of the "other"
;protocol). SNDACK on OSL will also send any DRQs outstanding.
CALL SNDACK ;POOR, SEND ACKS NOW (SEE COMMENT ABOVE)
CALLRET NSPRJF ;CAN'T SEND, TRY LATER
;We'll send DRQs right now on any sublink if we think the remote
;node has no more DRQs from us on this link.
LOADE T1,ESRSD,(ES) ;HOW MANY DRQS DO WE HAVE TO SEND?
PRCRQ4: JUMPE T1,RTN ;NONE, LEAVE NOW
LOADE T2,ESRRD,(ES) ;TOTAL WE THINK REMOTE WILL HAVE FROM US
CAMG T2,T1 ;IS REMOTE STARVED?
CAIG T1,0 ;YES, ARE THERE ANY DRQ'S TO SEND?
CALLRET NSPRJF ;NO, SEND WHEN WE'VE COLLECTED MORE
CALL CHKSDQ ;REMOTE IS STARVED, SEND DRQS NOW
CALLRET NSPRJF ;CAN'T, SEND LATER
TMNN ESACK,(ES) ;STILL NEED TO SEND AN ACK?
RET ;NO, ALL DONE
CALLRET NSPRJF ;YES, SEND AN ACK NEXT JIFFY
;PRCRQS - Subroutine called by PROCRQ to process entries on the queue
;
;Call: MB/ saved by caller, available
; EL/ The Port Block
; ES/ The Sublink Block
; CALL PRCRQS
; Normal Return
;Changes T1,T2,T3,T4
PRCRQS:
PRCRS1: LOADE T1,ESRLD,(ES) ;GET SIGN-EXTENDED RECEIVE LOCAL DRQS
JUMPLE T1,RTN ;LEAVE IF NO MORE AVAILABLE
LOAD MB,QHBEG,+ES.RCQ(ES) ;GET PTR TO FIRST MESSAGE W/O DEQUING
JUMPE MB,RTN ;ALL DONE IF QUEUE IS EMPTY
LOAD T1,NMSGN,(MB) ;GET THIS MESSAGE'S SEGMENT NUMBER
SOSGE T1 ;IS IT THE ONE WE'RE EXPECTING NEXT?
ANDI T1,MASK.(WID(ESLMR),35) ;MOD MASK-SIZE
CMODG T1,ESLMR,(ES) ;IS IT EXPECTED MSG OR DUPLICATE?
JRST PRCRS2 ;YES
;Here when we receive a message whose segment number is larger than
;expected, indicating that we have missed a message segment.
LOAD T1,ELVER,(EL) ;GET REMOTE NSP VERSION
CAIE T1,VER3.1 ;IS IT PHASE II?
RET ;NO, NO NAK NEEDED
SETONE <ESNAK,ESACK>,(ES) ;YES, MISSING A MSG, SEND
CALLRET NSPRJF ; A NAK NEXT JIFFY
;Here when the next message on the queue is expected or duplicate
PRCRS2: DEQUE MB,ES.RCQ(ES),MB.NXT,RTN ;RETURN IF Q IS EMPTY (?)
LOAD T1,NMSGN,(MB) ;GET THIS MSG'S SEGMENT NUMBER
CMODLE T1,ESLMR,(ES) ;DUPLICATE?
JRST PRCRS3 ;NO
CALL FREMSG ;YES, FREE THE MSG BLOCK
SETONE ESACK,(ES) ;SEND AN ACK EVEN IF ITS A DUPLICATE
CALL NSPRJF ; AT THE NEXT JIFFY
JRST PRCRS1 ;GO TRY NEXT ENTRY ON QUEUE
PRCRS3: JN ESOTH,(ES),PRCRS4 ;DON'T CHECK QUOTA FOR INTERRUPT MESSAGES
LOAD T1,ELSCB,(EL) ;TELL SCTL WHICH LINK THIS IS
CALL SCTRIB ;RESERVE AN INPUT BUFFER IN SESSION CONTROL
TRNA ;FAILED
JRST PRCRS4 ;SUCCEEDED
LOAD T1,ELVER,(EL) ;GET REMOTE NSP VERSION
CAIE T1,VER3.1 ;IS IT PHASE II?
JRST PRCRS9 ;NO, NO NAK NEEDED
CALL FREMSG ;YES, FORGET MESSAGE
SETONE <ESNAK,ESACK>,(ES) ;MISSING A MSG, SEND A
CALLRET NSPRJF ; NAK NEXT JIFFY
PRCRS9: XMOVEI T1,ES.RCQ(ES) ;GET APPROPRIATE RECEIVE QUEUE
CALL QSRTMB ;RE-QUEUE MESSAGE
CALLRET FREMSG ;SHOULD NOT BE DUPLICATE
RET ;ONLY RETURN
;Here when SCTRIB has blessed this receive
PRCRS4: SETONE ESACK,(ES) ;SEND AN ACK
CALL NSPRJF ; AT THE NEXT JIFFY
LOAD T1,NMSGN,(MB) ;GET THIS MSG'S SEGMENT NUMBER AGAIN
STOR T1,ESLMR,(ES) ;UPDATE LAST MSG FULLY RECEIVED NUMBER
XMOVEI T1,IN.MSD(MB) ;GET LENGTH OF SESSION
CALL DNSLNG ; CONTROL PART OF MESSAGE
LOAD T4,ELNDB,(EL) ;GET POINTER TO NSP NODE BLOCK
OPSTRM <ADDM T1,>,NNRBC,(T4) ;ADD TO TOTAL BYTES RECEIVED FROM NODE
;Number of messages received from node is updated at RCVMSG, since
;all messages are counted, not just user data messages.
LOAD T1,ESRFL,(ES) ;UPDATE FLOW CONTROL VARIABLES PROPERLY
JRST @.+1(T1) ;CASE OF FLOW CONTROL MODE
IFIW PRCRSN ;NO FLOW CONTROL
IFIW PRCRSS ;SEGMENT FLOW CONTROL
IFIW PRCRSM ;MESSAGE FLOW CONTROL
IFIW PRCRS6 ;ILLEGAL FLOW CONTROL
PRCRSM: TMNN MBEOM,(MB) ;IS THIS SEGMENT AN END OF MESSAGE?
JRST PRCRS5 ;NO
PRCRSS: OPSTRM <SOS>,ESRRD,(ES) ;DECREMENT REMOTE RECEIVE DRQS
PRCRS5:
PRCRSN: OPSTRM <SOS>,ESRLD,(ES) ;ONE LESS RECEIVE LOCAL DRQ ANYWAY
LOAD T1,ESOTH,(ES) ;GET THE "OTHER" SUBLINK FLAG
STOR T1,MBOTH,(MB) ;COPY TO THE MESSAGE BLOCK
LOAD T1,ELSCB,(EL) ;GET THE SCBID FOR SESSION CONTROL
SETZ T2, ;IGNORED
MOVEI T3,SV.SEG ;GET THE FUNCTION CODE FOR RECEIVE DATA
MOVE T4,MB
OPSTR <CALL @>,ELSCV,(EL) ;INDIRECT THROUGH SCTL'S VECTOR
JRST PRCRS1 ;ANY MORE?
;Here if the flow control variable is in illegal state
PRCRS6: BUG.(CHK,LLIS2S,LLINKS,SOFT,<Illegal flow control at PRCRQS>,,<
Cause: This BUG is not documented yet.
>,PRCRS1)
SUBTTL Message Receivers -- RCVLKS - Receive a Link Service Message
;RCVLKS - Receive a Link Service (data request) message
;
;Call: EL/ The Port Block
; ES/ The Sublink Block, for the link service message itself
; MB/ The Message Block
; CALL RCVLKS
; Normal Return
;Changes T1,T2,T3,T4
;
; MSGFLG DLA SLA [ACKNUM] SEGNUM <-> LSFLAGS FCVAL
;
;If this link service message is not the next message expected on the
;"other" sublink, throw it away. We do not cache messages on the
;"other" sublink because of the complexity of uncaching data and link
;service messages separately. Interrupt data messages are also thrown
;away if they are not the next expected message on the "other" sublink.
RCVLKS: LOAD T1,NMSGN,(MB) ;GET THE SEGMENT NUMBER
LOAD T2,ESLMR,(ES) ;GET NUMBER OF LAST MESSAGE RECEIVED
AOS T2 ;INCREMENT BEFORE "ANDI" FOLLOWING
ANDI T2,MASK.(WID(ESLMR),35) ;MAKE IT MOD MASK-SIZE (12 BITS)
CAME T1,T2 ;IS NEW MESSAGE THE NEXT EXPECTED?
JRST LKSAK1 ;NO, SEE IF WE NEED TO ACK IT
STOR T1,ESLMR,(ES) ;YES, LAST "OTHER" MESSAGE RECEIVED
CALL SACKMG ;SEND ACK IF CAN, NSPRJF IF CAN'T
LOAD T1,ELSTA,(EL) ;CHECK THAT WE'RE IN THE RIGHT STATE
IFNSTATE T1,<CC,RN,DI>,FREMSG
IFSTATE T1,CC ;IF IN CC STATE, WAITING FOR FIRST MESSAGE,
CALL PTIRUN ; PUT IN RUN STATE
;LSFLAGS
CALL DNGEBY ;GET EXTENSIBLE BYTE INTO T1
EVENT(MSG,No LSFLAGS field,FREMSG)
XMOVEI ES,EL.NSL(EL) ;ASSUME "NORMAL" SUBLINK
JUMPE T1,RCVLK1 ;JUMP IF NOTHING UNUSUAL
CALL LKSLSF ;GO PROCESS THE LSFLAGS FIELD
CALLRET FREMSG ;ERROR, IGNORE LSFLAGS AS WELL
;FCVAL
RCVLK1: CALL DNG1BY ;GET A SINGLE BYTE (FC VAL FIELD) INTO T1
EVENT(MSG,No FCVAL field,FREMSG)
LOAD T2,ESXFL,(ES) ;GET TRANSMIT FLOW CONTROL TYPE
JRST @.+1(T2) ;CASE OF FLOW CONTROL TYPE
IFIW LKFCVN ;NO FLOW CONTROL, IGNORE THE VAL FIELD
IFIW LKFCVS ;SEGMENT FLOW CONTROL
IFIW LKFCVM ;MESSAGE FLOW CONTROL
IFIW LKSILG ;RESERVED FLOW CONTROL TYPE
;Here for no flow control
LKFCVN: JUMPE T1,LKFCV2 ;ASSURE FCVAL FIELD IS ZERO FOR NO FLOW CTL
CALLRET LKSILG ;ELSE ILLEGAL FLOW CONTROL EVENT
SGNMAX== ^D 127 ;MAX TOTAL DRQS ARE ALLOWED TO GET
SGNMIN==-^D 128 ;MIN TOTAL DRQS ARE ALLOWED TO GET
;The FCVAL field has not had its sign bit extended for this call.
;Only links in segment flow control mode can have negative data
;requests, so here we will see a negative data request (should one be
;sent by mistake in message mode) as an oversized postive request.
LKFCVM: OPSTR <ADD T1,>,ESXRD,(ES) ;ADD CURRENT REMOTE XMIT DRQS
CAILE T1,SGNMAX ;IS THE RESULT A LEGAL TOTAL?
JRST LKSILG ;NO, EVENT AND ERROR RETURN
STOR T1,ESXRD,(ES) ;YES, USE THE NEW TOTAL
JRST LKFCV1 ;PASS T1 ACROSS SEGMENT FLOW CONTROL CODE
;Only a link in segment flow control can have negative data requests,
;so it is only in this routine that we bother to extend the sign of
;the FCVAL field.
LKFCVS: TRNE T1,200 ;TEST HIGH-ORDER BIT OF 8-BIT BYTE
ORCMI T1,377 ;ITS NEGATIVE, EXTEND THE SIGN BIT
LOADE T2,ESXRD,(ES) ;ADD IN SIGNED VERSION OF
ADD T1,T2 ; CURRENT XMIT DATA REQUESTS
CAXL T1,SGNMIN ;IS THE RESULT A
CAXLE T1,SGNMAX ; LEGAL TOTAL?
JRST LKSILG ;NO, EVENT AND ERROR RETURN
STOR T1,ESXRD,(ES) ;YES, USE THE NEW TOTAL
;ESXLD WILL BE BROUGHT INTO SYNCH
; BY CLCXDQ ASAP
;We also mark the 'normal' sublink as buffer rich in PROCCI if we
;find the remote node electing NO flow control.
LKFCV1: MOVX T2,ESBFR ;IS TOTAL OF DRQS UP TO THE
CAML T1,NSPBFR ; BUFFER-RICH THRESHOLD?
IORM T2,ES.BFR(ES) ;YES, MARK LINK BUFFER RICH
;PROCXQ will use any of these new data requests that it can, then
;send the rest to Session Control.
LKFCV2: TMNE QHBEG,+ES.XMQ(ES) ;NORMALLY WON'T BE ANYTHING ON XMIT Q
CALL PROCXQ ;SOMETHING THERE, TRY TO SEND IT
CALL CLCXDQ ;FIGURE HOW MANY XMIT DRQS SCTL GETS
;# DRQS TO SEND RETURNED IN T1
JUMPE T1,FREMSG ;DONE IF NO NEWS FOR SESSION CONTROL
;... T1 still contains # of DRQs to send
;... T1 still contains # of DRQs to send
;Now we have decided to send a message to Session Control
LOAD T3,ESOTH,(ES) ;GET THE "OTHER" SUBLINK FLAG
STOR T3,MBOTH,(MB) ;COPY TO THE MESSAGE BLOCK
SETZ T2, ;CLEAR OUT FLAGS FIELDS
STOR T1,QACNT,+T2 ;LOADE/STOR IN CASE FIELDS NOT SAME SIZE
SETZRO ESXSD,(ES) ;DON'T SEND THIS ONE ANY MORE
LOAD T1,ELSCB,(EL) ;GET SESSION CONTROL'S SCBID FOR PORT
MOVX T3,SV.DRQ ;FUNCTION CODE FOR A DATA REQUEST
MOVE T4,MB ;SESSION CONTROL WANTS MB IN T4
OPSTR <CALLRET @>,ELSCV,(EL) ;CALL SESSION CTL ON THE CALL VECTOR
;Here if we are to ignore the message as a duplicate.
LKSAK1: CMODG T1,ESLMR,(ES) ;IS THIS A DUPLICATE MESSAGE?
CALL SACKMG ;YES, SEND ACK IF CAN, NSPRJF IF CAN'T
CALLRET FREMSG ;NOT EXPECTED MESSAGE, IGNORE IT
;Here if we receive an illegally formatted message.
LKSILG: EVENT(FLO,Illegal Flow Control Value)
CALLRET FREMSG ;ILLEGAL MESSAGE FORMAT
;LKSLSF - Process non-zero Link Service LSFLAGS field for RCVLKS
;
;Call: T1/ The LSFLAGS byte (only if non-zero)
; EL/ The Port Block
; ES/ Initialized to "normal" sublink
; MB/ The Message Block
; CALL LKSLSF
; Error Return if we have given an EVENT
; Normal Return with ES set to appropriate sublink block
;Changes T2,T3,T4
;
;This routine is only called if the Link Service Flags field of the
;message is non-zero.
LKSLSF: LOAD T2,LSINT,+T1 ;GET THE INTERPRETATION FIELD OF LSFLAGS
JUMPE T2,LKSLS2 ;JUMP IF DATA REQUEST IS FOR "NORMAL" SUBLINK
CAIE T2,LS.IOT ;NOT NORMAL IS IT "OTHER"
CALLRET LKSILG ;NO, EVENT AND ERROR RETURN
;Here if the link service message is for the "other" sublink
XMOVEI ES,EL.OSL(EL) ;ITS FOR "OTHER", SET UP ES APPROPRIATELY
RETSKP ;IGNORE FC MOD FIELD FOR "OTHER" SUBLINK
;Here if the link service message is for the "normal" sublink
LKSLS2: ;CALLER HAS SET ES UP FOR NSL
JUMPE T1,RSKP ;JUMP IF NO CHANGE TO "OFF" FLAG
CAIN T1,LS.MRS ;IS IT THE RESERVED VALUE?
CALLRET LKSILG ;EVENT AND ERROR RETURN
MOVEI T3,1 ;ASSUME TURNING FLAG "OFF"
CAIE T1,LS.MOF ;ARE WE?
MOVEI T3,0 ;NO WE'RE TURNING IT ON AGAIN
STOR T3,ESXOF,(ES) ;YES, STORE NEW VALUE
RETSKP ;WE'RE NOW FINISHED WITH LSFLAGS FIELD
SUBTTL Message Receivers -- RCVACK and RCVNOP - Little Ones
;RCVACK - Deal with the remains of the ACK message
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message Block
; CALL RCVACK
; Normal Return
;Changes T1,T2,T3,T4
;We've already done ACKNUM field at PRCACK
RCVACK: LOAD T1,ELSTA,(EL)
IFSTATE T1,CC ;IF WE'RE IN CC STATE,
CALL PTIRUN ; PUT PORT IN RUN STATE (uses ES)
RCVNOP: CALLRET FREMSG ;OTHERWISE, IGNORE THE MSG
;RCVNOP - A phase II no-operation, ignore it completely
;
;Call: MB/ The Message Block
; CALL RCVNOP
; Normal Return
;Changes T1,T2,T3,T4
;See null routine above
SUBTTL Message Receivers -- RCVCA - Receive a Connect ACK Message
;RCVCA - Process an incoming Connect ACK Message
;
;Call: MB/ The Message Block, EL and ES not filled in yet
; CALL RCVCA
; Normal Return
;Changes T1,T2,T3,T4
RCVCA: LOAD T1,NMLLA,(MB) ;GET THE DLA SENT IN THE CA MESSAGE
MOVEI T2,0 ;WE DON'T KNOW THE RLA YET
CALL FNDPRT ;DO WE KNOW OF SUCH A PORT?
JRST FREMSG ;NO, IGNORE THE MESSAGE
XMOVEI ES,EL.NSL(EL) ;POINT ES TO THE 'NORMAL' SUBLINK
LOAD T1,ELSTA,(EL) ;GET PORT STATE
IFNSTATE T1,<CI>,FREMSG ;UNLESS WE'RE IN CI STATE, IGNORE CONN ACK
MOVEI T1,0 ;THE CI MSG ALWAYS GOES OUT AS MSG 0
CALL UPDELAY ;INITIALIZE THE ROUND-TRIP GUESSER
OPSTR <SKIPE T1,>,ELDIM,(EL) ;IS THERE A CONNECT INIT MESSAGE THERE?
CALL DNFMSG ;YES, FREE IT
SETZRO ELDIM,(EL) ; AND FORGET IT
SETZRO ELTMA,(EL) ;CLEAR INACTIVITY TIMER, SEE ABOVE
NEWSTATE CD ;GO INTO CD (CONNECT DELIVERED) STATE
;Send the Connect Ack info to Session Control which needs it to
;stop the Connect Initiate timer.
LOAD T1,ELSCB,(EL) ;GET SCTL'S SCBid FOR THIS PORT
MOVX T3,SV.CAK ;CONNECT ACK FUNCTION CODE
MOVE T4,MB
OPSTR <CALLRET @>,ELSCV,(EL)
SUBTTL Message Receivers -- RCVCI - Receive a Connect Initiate Request
;RCVCI - Process an incoming Connect Initiate request
;
;Call: MB/ The Message Block
; EL/ not yet set up
; ES/ not yet set up
; CALL RCVCI
; Normal Return
;Changes T1,T2,T3,T4
;MSGFLG <-> DLA SLA SERVICES INFO SEGSIZE DATA-CTL
RCVCI: ;CI OR RETRANSMITTED CI MESSAGE
;DLA
CALL DNG2BY ;GET THE DESTINATION LINK ADDRESS
EVENT(MSG,No DLA in CI,FREMSG)
JUMPN T1,[EVENT(MSG,Non-zero DLA in CI,FREMSG)]
STOR T1,NMLLA,(MB) ;SHOULD ALREADY BE ZERO, BUT...
;SLA
CALL DNG2BY
EVENT(MSG,No SLA in CI,FREMSG)
STOR T1,NMRLA,(MB) ;STORE THE REMOTE LINK ADDRESS
IFN FTPH4,<
CALL CHKDCI ;(T1)DUPLICATE RECEIVED CI MSG? (smashes EL)
RET ;YES, IT HAS BEEN DEALT WITH
>;END IFN FTPH4
CALL NEWLLA ;NO, CREATE A NEW LLA FOR THE PORT
STOR T1,NMLLA,(MB) ;MAKPRT WILL STORE IN THE NEW PORT BLOCK
LOAD T2,NMRLA,(MB) ;GET BACK THE SLA (REMOTE LINK ADDR)
HRL T1,T2 ;SET UP HALF-WORDS FOR MAKPRT
LOAD T2,MBSRC,(MB) ;GET THE SOURCE NODE ADDRESS FOR MAKPRT
CALL MAKPRT ;MAKE A NEW PORT BLOCK
JRST SENDNR ;CAN'T, SEND "NO RESOURCES" MESSAGE
SETZRO ELSIZ,(EL) ;TELL PROCCI TO STORE NEW ELSIZ ALWAYS
CALL PROCCI ;PROCESS CI INFORMATION
JRST [NEWSTATE DP ;TELL NSISEC TO DELETE THE PORT BLOCK
CALLRET FREMSG] ;IGNORE THE CI MESSAGE
;ROUTER HAS FILLED IN MBCHN FOR US
LOAD T1,ELNDB,(EL) ;GET POINTER TO NSP NODE BLOCK
INCR NNRCC,(T1) ;INCR NUMBER OF RECEIVED CONNECT INITS
INCR NNRMC,(T1) ;UPDATE COUNT OF MESSAGES FROM NODE
NEWSTATE CR ;GO TO CONNECT RECEIVED STATE
SETONE ELSCM,(EL) ;SEND A CONNECT ACK
CALL NSPRJF ; NEXT JIFFY
CALL GETPID ;GET THE NSPPID FOR EL INTO T1
LOAD T3,ESXFL,+EL.NSL(EL) ;THE FLOW CONTROL TYPE CHOSEN BY REMOTE
STOR T3,IAFLO,+T2
LOAD T3,ELSIZ,(EL) ;GET SIZE OF SEGMENT ON THIS LOGICAL LINK
STOR T3,IASIZ,+T2
;T3 IS IGNORED HERE
MOVE T4,MB ;T1,T2,T3,T4 NOW SET UP
CALLRET SCTLCI ;THE SPECIAL ENTRY FOR CI MESSAGES
;CHKDCI - Check for Duplicate CI Message Received
;
;Call: MB/ The Message Block
; EL/ not yet set up, need not be saved
; ES/ not yet set up, need not be saved
; T1/ Remote Link Address in received CI or RCI message
; CALL CHKDCI ;CALLED ONLY FROM RCVCI
; Error Return if duplicate, message freed as appropriate
; Normal Return
;Changes T1,T2,T3,T4
IFN FTPH4,<
CHKDCI:
LOAD EL,QHBEG,+NSPAPQ ;PTR TO FIRST LINK ON NSP ALL LINKS QUEUE
CKDCI1: JUMPE EL,RSKP ;IF NO LINKS, THIS CAN'T BE DUPLICATE
LOAD T2,ELRLA,(EL) ;GET THIS LINK'S REMOTE LINK ADDRESS
CAMN T1,T2 ;DOES RECEIVED CI DUPLICATE THIS LINK'S RLA?
JRST CKDCI2 ;YES
LOAD EL,ELAPQ,(EL) ;NO, TRY NEXT LINK
JRST CKDCI1 ;LOOP THROUGH ALL LINKS
;Here if received CI or RCI message is a duplicate
CKDCI2: LOAD T2,ELSTA,(EL) ;GET STATE OF EXISTING LINK
;IF LINK IS OR HAS BEEN RUNNING
IFNSTATE T2,<CR,CC,DR>,FREMSG ; THEN DISCARD THIS MESSAGE
;Here if we are to send a Connect ACK again, using this message block.
MOVE T1,MB ;ADDRESS OF MESSAGE BLOCK
MOVEI T2,0 ;NO USER DATA REQUIRED
CALL DNMINI ;RE-INITIALIZE THE MESSAGE BLOCK
RET ;(?? NO USER DATA REQUESTED ??)
CALLRET SNDCAK ;SEND A CONNECT ACK MESSAGE
>;END IFN FTPH4
SUBTTL Message Receivers -- RCVCC - Receive a Connect Confirm Message
;RCVCC - Process an incoming Connect Confirm message
;
;Call: MB/ The Message Block
; EL/ not yet set up
; ES/ not yet set up
; CALL RCVCC
; Normal Return
;Changes T1,T2,T3,T4
;MSGFLG <-> DLA SLA SERVICES INFO SEGSIZE DATA-CTL
RCVCC: SAVEAC <P1,P2>
;DLA
CALL DNG2BY
EVENT(MSG,No DLA in CC,FREMSG)
STOR T1,NMLLA,(MB) ;LOCAL LINK ADDRESS
MOVE P1,T1
;SLA
CALL DNG2BY
EVENT(MSG,No SLA in CC,FREMSG)
STOR T1,NMRLA,(MB) ;REMOTE LINK ADDRESS
MOVE P2,T1 ;SAVE RLA FOR LATER
MOVE T1,P1 ;LOCAL LINK ADDRESS AGAIN
SETZ T2, ;RLA NOT YET MARKED IN PORT BLOCK
CALL FNDPRT ;LOOK UP PORT BLOCK FOR LLA
JRST SENDNL ;NOT THERE, SEND NO LINK AND IGNORE
MOVE P1,T1 ;FNDPRT LEAVES STATE IN T1
LOAD T1,ELNDB,(EL) ;GET PTR TO NODE BLOCK FOR THIS LINK
INCR NNRMC,(T1) ;UPDATE COUNT OF MESSAGES FROM NODE
XMOVEI ES,EL.NSL(EL) ;CONNECT IS ON 'NORMAL' SUBLINK
IFNSTATE P1,<CI,CD,RN>,FREMSG ;RN STATE TOO, SEE RCVCC1, BELOW
LOAD T1,ELVER,(EL) ;GET REMOTE'S VERSION NUMBER
CAIN T1,VER3.1 ;IS IT PHASE II?
JRST RCVCC1 ;YES, NO ACK REQUIRED
SETONE ESACK,(ES) ;NO, SEND A "NORMAL" SUBLINK ACK
CALL NSPRJF ; NEXT JIFFY
RCVCC1: IFSTATE P1,<RN>,FREMSG ;IF ALREADY IN RUN STATE, SKIP STARTUP
CALL PROCCI ;PROCESS CONNECT INFORMATION
JRST FREMSG ;IF ERROR, IGNORE THE MESSAGE
STOR P2,ELRLA,(EL) ;OK, WE CAN SIGN UP THE RLA NOW
CALL PTIRUN ;PUT PORT IN RUN STATE
LOAD T1,ELSCB,(EL) ;GET SESSION CONTROL'S SCBID
LOAD T3,ESXFL,+EL.NSL(EL) ;GET REMOTE'S FLOW SPECIFICATION
STOR T3,IAFLO,+T2
LOAD T3,ELSIZ,(EL) ;GET SIZE IN BYTES OF A SEGMENT
STOR T3,IASIZ,+T2
MOVX T3,SV.CCR ;CONNECT CONFIRM RECEIVED
MOVE T4,MB
OPSTR <CALLRET @>,ELSCV,(EL) ;TELL SESSION CONTROL ABOUT THE CC
;PROCCI - Process common part of Connect Init and Connect Confirm messages
;
;Call: EL/ The Port Block
; MB/ The Message Block
; CALL PROCCI
; Error Return if bad format
; Normal Return with message data copied to port block
;Changes T1,T2,T3,T4
;If ELSIZ,(EL) is zero, always store the value of the SIZE field in
;the incoming message. If ELSIZ,(EL) is non-zero, store the minimum
;of it and the SIZE field of the incoming message. RCVCI calls with
;NPSIZE,(EL) zero, RCVCC calls with it non-zero.
PROCCI: SAVEAC P1
;SERVICES
CALL DNGEBY ;GET EXTENSIBLE BYTE
EVENT(MSG,No SERVICES field,FREMSG)
LOAD P1,SVOPT,+T1 ;GET THE FLOW CONTROL OPTION FIELD
CAIN P1,FCM.XX ;IS FLOW CONTROL MODE THE RESERVED VALUE?
EVENT(FLO,Reserved value in PROCCI,RTN) ;ILLEGAL FLOW CONTROL
TXNE T1,SVFL1 ;ASSURE THAT HIGH-ORDER ALL ZERO
EVENT(MSG,PROCCI found foul,RTN) ;ILLEGAL MESSAGE
ANDI T1,SVFL2 ;ASSURE THAT LOW-ORDER IS MAGIC NUMBER
CAIE T1,SV$FL2 ;IS IT?
EVENT(MSG,PROCCI found foul,RTN) ;ILLEGAL MESSAGE
;VERSION
CALL DNGEBY ;GET EXTENSIBLE BYTE
EVENT(MSG,No VERSION field,FREMSG)
CAIE T1,VER3.1 ;IS IT A PHASE II NSP?
MOVX T1,VER3.2 ;NO, PHASE III OR MORE, TREAT AS PHASE III
;We've passed all the errors, safe to start filling in the port block now
STOR T1,ELVER,(EL) ;STORE THE REMOTE'S VERSION NUMBER
STOR P1,ESXFL,+EL.NSL(EL) ;STORE OUR TRANSMIT FLOW CONTROL FOR NORMAL
;"OTHER" SUBLINK IS DONE IN MAKPRT
MOVX T1,ESBFR ;GET 'BUFFER-RICH' FLAG
CAIN P1,FCM.NO ;HAS REMOTE CHOSEN NO FLOW CONTROL?
IORM T1,ES.BFR+EL.NSL(EL) ;YES, CONSIDER IT 'BUFFER-RICH'
;SIZE
CALL DNG2BY
EVENT(MSG,No SIZE field,FREMSG)
LOAD T2,ELSIZ,(EL) ;GET SCTL'S MAX SEGMENT SIZE
JUMPE T2,PRCCI1 ;JUMP IF WE DON'T KNOW IT YET
CAMGE T1,T2 ;IS IT BIGGER THAN SCTL'S MAX?
PRCCI1: STOR T1,ELSIZ,(EL) ;NO, STORE NEGOTIATED SIZE
RETSKP ;SUCCESS RETURN
SUBTTL Message Receivers -- RCVDI - Disconnect Initiate Message
;RCVDI - Process an incoming Disconnect Initiate Message
;
;Call: EL/ The Port Block
; MB/ The Message Block
; CALL RCVDI
; Normal Return
;Changes T1,T2,T3,T4
;There should not be any messages in the receive queues, because the
;remote node should not have sent a DI message unless it had received
;ACKs from us for all outstanding messages. In the case that the
;remote is aborting and has not waited for the ACKs, we don't care
;about any messages that may be in the pipeline and we may delete them.
;We clear the receive queues in the CLRRCQ call on the next page.
RCVDI: SAVEAC P1 ;P1/ NEW STATE CODE, SEE NEXT PAGE
LOAD T1,ELSTA,(EL) ;THE PORT STATE BEFORE RECEIVED DI MSG
IFSTATE T1,<CI,CD>,RCVDI1 ;CONNECT INIT, CONNECT DELIVERED
IFSTATE T1,<RN,CC>,RCVDI2 ;RUN OR CC (PSEUDO-RUN)
IFSTATE T1,<DI> ,RCVDI3 ;DISCONNECT INIT
IFSTATE T1,<DR> ,RCVDI4 ;DISCONNECT REJECT
;Fall through to here if port is not in an appropriate state for a DI
CALLRET FREMSG ;IGNORE THE DI MESSAGE
;Here if port was in CI or CD state
RCVDI1: LOAD T1,NMRLA,(MB) ;WE DON'T KNOW THE RLA YET,
STOR T1,ELRLA,(EL) ; STORE SO WE CAN RETURN THE DC
LOAD T1,ELNDB,(EL) ;IF WE WERE TRYING TO CONNECT,
INCR NNRRC,(T1) ; THEN COUNT ONE MORE RECEIVED REJECT
NEWSTATE RJ ;GO INTO DISCONNECT RECEIVED STATE
CALL RCVDIS ;CALL COMMON CODE
CALLRET SCLOSE ;GO START THE CLOSE PROCESS
;Here if port was in RUN state
RCVDI2: NEWSTATE DN ;GO INTO DISCONNECT NOTIFICATION STATE
CALL RCVDIS ;CALL COMMON CODE
CALLRET SCLOSE ;GO START THE CLOSE PROCESS
;Here if port was in DI state
RCVDI3: NEWSTATE IC ;GO INTO DISCONNECT INIT COMPLETE STATE
CALL RCVDIS ;CALL COMMON CODE
CALLRET SCLOSE ;GO START THE CLOSE PROCESS
;Here if port was in DR state
RCVDI4: NEWSTATE RC ;GO INTO DISCONNECT REJECT COMPLETE
CALL RCVDIS ;CALL COMMON CODE
CALLRET SCLOSE ;GO START THE CLOSE PROCESS
;Common subroutine for RCVDIn
RCVDIS: SETONE ELSDM,(EL) ;SEND A DISCONNECT CONFIRM MESSAGE
CALL DNG2BY ;GET REASON CODE
EVENT(MSG,No REASON in DI,FREMSG)
MOVE T2,T1 ;SESSION CONTROL WANTS IT IN T2
LOAD T1,ELSCB,(EL) ;FIRST ARGUMENT FOR SESSION CONTROL
MOVX T3,SV.DIR ;SESSION CONTROL FUNCTION CODE
MOVE T4,MB ;POINTER TO MESSAGE BLOCK
OPSTR <CALLRET @>,ELSCV,(EL) ;CALL SESSION CONTROL
SUBTTL Message Receivers -- RCVDC - Disconnect Confirm Message
;RCVDC - Process an incoming Disconnect Confirm Message
;
;Call: EL/ The Port Block
; MB/ The Message Block
; CALL RCVDC
; Normal Return
;Changes T1,T2,T3,T4
;We CALL CLRRCQ in this routine for the same reason
;as we did in RCVDI (q.v.).
RCVDC: SAVEAC P1 ;P1/ REASON CODE
CALL CLRRCQ ;CLEAR OUT THE RECEIVE QUEUES
CALL DNG2BY ;GET REASON CODE FROM MESSAGE
EVENT(MSG,No REASON in DC,FREMSG)
MOVE P1,T1 ;SAVE FOR LATER
CAIN P1,RSNRES ;NO RESOURCES
JRST RCVDCR
CAIN P1,RSNNLK ;NO LINK
JRST RCVDCL
CAIN P1,RSNDSC ;DISCONNECT COMPLETE
JRST RCVDCC
;Here if the reason code was not one of those expected for Phase III
LOAD T1,ELSTA,(EL) ;GET PORT STATE
IFNSTATE T1,<CI,CD>
EVENT(MSG,DC recvd in bad state,FREMSG)
LOAD T1,ELSCB,(EL) ;FIRST ARGUMENT FOR SESSION CONTROL
MOVE T2,P1 ;REASON CODE
MOVX T3,SV.DCR ;SESSION CONTROL FUNCTION CODE
MOVE T4,MB ;POINTER TO MESSAGE BLOCK
OPSTR <SKIPN T5,>,ELSCV,(EL) ;HAS SCTL BLESSED THIS LINK YET?
XMOVEI T5,FREMSG ;NO, FREE MESSAGE BLOCK
; SCTL WILL FIND OUT SOON FROM NSP STATE
CALL 0(T5) ;TELL SCTL ABOUT THE DC OR FREE MESSAGE
SETZRO <ELSDM,ELSCM>,(EL) ;DON'T SEND A DISCONNECT CONFIRM MESSAGE
; & WE NO LONGER NEED A CAK
NEWSTATE CN ;GO INTO CLOSED NOTIFICATION STATE
CALLRET SCLOSE ;START THE CLOSE PROCESS AND RETURN
;Here if the DC message was "No Resources"
RCVDCR: LOAD T1,ELSTA,(EL)
IFNSTATE T1,<CI>
EVENT(MSG,<NR recvd in non-CI state>,FREMSG)
LOAD T1,ELSCB,(EL) ;ARGUMENT FOR SESSION CONTROL
MOVX T3,SV.NRS ;NO RESOURCES FUNCTION CODE
MOVE T4,MB ;THE MESSAGE BLOCK POINTER
OPSTR <SKIPN T5,>,ELSCV,(EL) ;HAS SCTL BLESSED THIS LINK YET?
XMOVEI T5,FREMSG ;NO, FREE MESSAGE BLOCK
; SCTL WILL FIND OUT SOON FROM NSP STATE
CALL 0(T5) ;TELL SCTL ABOUT THE DC OR FREE MESSAGE
SETZRO <ELSCM,ELSDM>,(EL) ;DON'T SEND A DISCONNECT CONFIRM MESSAGE
; & WE NO LONGER NEED THE CAK
NEWSTATE NR ;GO INTO NO RESOURCES STATE
CALLRET SCLOSE ;GO START THE CLOSE PROCESS
;Here if the DC message was "No Link"
RCVDCL: LOAD T1,ELSCB,(EL) ;ARGUMENT FOR SESSION CONTROL
MOVX T3,SV.NLK ;NO LINK FUNCTION CODE
MOVE T4,MB ;THE MESSAGE BLOCK
OPSTR <SKIPN T5,>,ELSCV,(EL) ;SESSION CONTROL INTERESTED IN THIS LINK?
; SCTL WILL FIND OUT SOON FROM NSP STATE
XMOVEI T5,FREMSG ;NO, JUST FREE THIS MESSAGE BLOCK
CALL 0(T5) ;TELL SCTL ABOUT THE DC OR FREE MESSAGE
SETZRO <ELSCM,ELSDM>,(EL) ;DON'T SEND A DISCONNECT CONFIRM MESSAGE
; & WE NO LONGER NEED THE CAK
NEWSTATE CN ;GO INTO CLOSE NOTIFICATION STATE
CALLRET SCLOSE ;GO START THE CLOSE PROCESS
;Here if the DC message was "Disconnect Complete"
RCVDCC: LOAD T1,ELSTA,(EL)
IFNSTATE T1,<DI,IC,DR,RC>
ETRCRET NSP,DCC recvd in unexpected state
LOAD T1,ELSCB,(EL) ;ARGUMENT FOR SESSION CONTROL
MOVX T3,SV.DCR ;DISCONNECT COMPLETE RECEIVED FUNCTION
MOVE T4,MB ;THE MESSAGE BLOCK
OPSTR <SKIPN T5,>,ELSCV,(EL) ;SESSION CONTROL INTERESTED IN THIS LINK?
; SCTL WILL FIND OUT SOON FROM NSP STATE
XMOVEI T5,FREMSG ;NO, JUST FREE THIS MESSAGE BLOCK
CALL 0(T5) ;TELL SCTL ABOUT THE DC OR FREE MESSAGE
;DON'T CLEAR ELSDM IN CASE WE WERE
; IN IC OR RC STATE AND NEED TO SEND DC
NEWSTATE CN ;GO INTO CLOSE NOTIFICATION STATE
CALLRET SCLOSE ;GO START THE CLOSE PROCESS
SUBTTL Clock-Driven Routines
;NSPJIF - Called by the system once a jiffy (approximately)
;
;Call: CALL NSPJIF
; Normal Return
;Changes T1,T2,T3,T4
;Note that we save no ACs until we are sure that we will call
;NSPLCF and thence the rest of NSP.
IF2,IFN QHBEG+1,PRINTX ?NSPJIF requires that QHBEG be a full word
NSPJIF::
;Notice that the SAVEAC is only called after we are pretty sure we need it.
;Don't use any sensitive ACs until then!
IFN FTOPS10,<
XCT .CPSK0## ;SKIP IF BOOT CPU
RET ;JIFFY PROCESSING ONLY ON BOOT CPU
>;END IFN FTOPS10
SOS T1,NSPJLC ;DECREMENT THE LONG INTERVAL TIMER
SOS T2,NSPJSC ; AND THE SHORT INTERVAL TIMER
TMNE LKJIF,+NSPLKF ;ALREADY A CLOCK REQUEST QUEUED?
RET ;YES, LET IT DO THE WORK
JUMPLE T1,NSPJF1 ;ALWAYS DO A LONG INT PROCESS IF TIME
JUMPG T2,NSPJF0 ;LEAVE IF NO SHORT INT REQUEST
TMNE QHBEG,+NSPJFQ ;TIME FOR SHORT INT SERVICE, ANY DEMAND?
JRST NSPJF1 ;YES
NSPJF0: TMNE QHBEG,+NSPITQ ;NO, ANY TRANSACTIONS TO PROCESS?
JRST NSPJF2 ;YES, GET NSPULK TO PROCESS THEM NOW
MOVE T1,NSPJSI ;NO, GET NSP JIFFY SHORT INTERVAL VALUE
MOVEM T1,NSPJSC ;RESET THE SHORT INTERVAL COUNTER
RET ;LEAVE NOW
NSPJF1: SETONE LKJIF,+NSPLKF ;REQUEST A NSIJIF RUN
NSPJF2: ;NSIJIF WILL UPDATE NSPJSC WHEN ITS DONE
IFN FTOPS10,SEC1 ;MAKE THIS RUN IN SECTION 1
SAVEAC <T5,T6,P1,P2,MS,MB,FREE1,FREE2>
CALLRET NSPLCF ;TRY FOR THE NSP INTERLOCK
;If we get to NSIJIF, we know that we want to run a short
;interval process, since we can only be here if there was a
;request for a short interval process or if a long interval has
;passed, in which case we run a short interval process anyway
;as an auditor.
NSIJIF:
;Here to process after a short interval (typically a jiffy)
TMNE QHBEG,+NSPJFQ ;ANY DEMAND FOR SHORT INT?
CALL JIFCHK ;YES, DO "ONCE-A-JIFFY" PROCESSING
SKIPLE NSPJLC ;HAS A LONG INTERVAL ELAPSED?
JRST NSIJFX ;NO
;Here to process after a long interval (typically a second)
CALL SECCHK ;DO "ONCE-A-SECOND" PROCESSING
MOVE T1,NSPJLI ;GET LENGTH OF A LONG INTERVAL
MOVEM T1,NSPJLC ;RESET THE LONG INTERVAL COUNTER
;Common NSIJIF exit
NSIJFX: MOVE T1,NSPJSI ;GET LENGTH OF A SHORT INTERVAL
MOVEM T1,NSPJSC ;RESET THE SHORT INTERVAL COUNTER
RET
;JIFCHK - Once a jiffy processing
;
;Call: CALL JIFCHK
; Normal Return
;Changes T1,T2,T3,T4
;We must not continue processing after getting a no-resources return
;because the procedure which gave that return put the current port
;back on the jiffy-request queue. If we were to continue along that
;queue, we would visit that port over and over forever.
JIFCHK: SAVEAC <EL,ES,MB,MS,P1> ;SAVE THE SAME ACS AT AUDCHK
JIFCH1: DEQUE EL,NSPJFQ,EL.JFQ,RTN ;RTN IF Q IS EMPTY
SETZRO ELOJQ,(EL) ;NOT ON-JIFFY-QUEUE ANY MORE
CALL JIFSER ;GIVE JIFFY SERVICE TO THIS PORT
JRST NSPRJF ;COULDN'T, ASK FOR SOME NEXT TIME
JRST JIFCH1 ;GO SERVICE NEXT PORT
;JIFSER - Once a jiffy processing
;
;Call: EL/ The Port to check
; ES/ available, caller has saved it
; MB/ available, caller has saved it
; MS/ available, caller has saved it
; P1/ available, caller has saved it
; CALL JIFSER
; Error Return if no resources
; Normal Return
;Changes T1,T2,T3,T4
JIFSER: LOAD P1,ELSTA,(EL) ;LOAD UP THE STATE FOR LATER CHECKS
IFSTATE P1,<CL,DP>,JIFSR2 ;IGNORE TESTS IF PORT IS CLOSED
CALL JCHSDQ ;BEFORE ACK, TRY TO PIGGYBACK "OTHER" ACKS
RET ;NO RESOURCES, MUST NOT CONTINUE
CALL JCHACK ;SEND ANY ACKS NOT ALREADY PIGGYBACKED
RET ;NO RESOURCES, MUST NOT CONTINUE
TMNN <ELSCM,ELSDM>,(EL) ;ANY DIS/CONNECT MSGS REQUIRED?
JRST JIFSR1 ;NO
CALL JCHSCM ;YES, SEND ANY CONNECT MESSAGES
RET ;NO RESOURCES, MUST NOT CONTINUE
JIFSR1:
;Put more jiffy processors here
IFNSTATE P1,<RC,CN,DI,IC,DN,RJ,NC,NR>,RSKP
JIFSR2: CALL CHKCLS ;DO ANY DELAYED CLOSE PROCESSING
RET ;NO RESOURCES, MUST NOT CONTINUE
RETSKP ;SUCCESS RETURN
;CHKSDQ - Send data requests
;
;Call: EL/ The Port Block
; ES/ available for JCHSDQ, caller has saved it
; MB/ available for JCHSDQ, caller has saved it
; MS/ available for JCHSDQ, caller has saved it
; CALL CHKSDQ/JCHSDQ
; Error Return if no resources
; Normal Return
;Changes T1,T2,T3,T4
CHKSDQ: SAVEAC <MS,MB,ES> ;ENTRY FOR CALLERS WHO NEED SAVEAC
JCHSDQ: LOAD T1,ELSTA,(EL) ;ENTRY FOR JIFFY SERVICE, ACS ALREADY SAVED
IFSTATE T1,<CC,DN,CN,RJ,NC,IC,RC,CL,DP>,RSKP
;LEAVE IF REMOTE NO LONGER INTERESTED
LOAD T1,ESLAR,+EL.OSL(EL) ;ANY "OTHER" MESSAGES TO BE ACKED?
CMODE T1,ESLMA,+EL.OSL(EL) ;LAST MSG # ASSIGNED = LAST ACK REC'D?
RETSKP ;NO, PRCACK WILL REQUEST JIFFY SERVICE AGAIN
XMOVEI ES,EL.OSL(EL) ;CHECK THE "OTHER" SUBLINK FIRST
; SINCE IF IT NEEDS IT, IT REALLY DOES!
TMNN QHBEG,+ES.XMQ(ES) ;INTERRUPT MSG WAITING FOR XMIT?
JRST CHKSD1 ;NO, TRY TO SEND LINK SERVICE
CALL PROCXQ ;YES, GO SEND INTERRUPT MESSAGE
RETSKP ; WHICH WILL PREVENT LINK SERVICE FOR NOW
CHKSD1: JN ESRSD,(ES),CHKSD2 ;IF ANY INTERRUPT REQUESTS TO GO, SEND NOW
;"OTHER" SUBLINK IS NEVER "OFF"
XMOVEI ES,EL.NSL(EL) ;CHECK THE "NORMAL" SUBLINK NOW
JN ESROC,(ES),CHKSD2 ;IF "OFF" FLAG CHANGED, SEND NOW
LOAD T1,ESRSD,(ES) ;GET NUMBER OF DRQS TO SEND
JUMPE T1,RSKP ;LEAVE IF NO DRQS TO SEND
LOAD T2,ESRRD,(ES) ;GET DRQs REMOTE WILL HAVE WHEN WE SEND
SUBI T2,3(T1) ;DOES REMOTE HAVE LESS THAN 3 NOW?
JUMPG T2,RSKP ;RETURN NOW IF REMOTE NOT DRQ-STARVED
CHKSD2: MOVEI T1,0 ;NO USER DATA NEEDED
CALL DNGMSG ;GET A MESSAGE BLOCK
RET ;CAN'T, ERROR RETURN
MOVE MB,T1 ;MESSAGE BLOCK POINTER RETURNED IN T1
CALL SENDRQ ;SEND LINK SERVICE MESSAGE
RET ;CAN'T SEND NOW, CALLER WILL CALL NSPRJF
RETSKP ;SUCCESS RETURN
SUBTTL CLCXDQ - Calculate Transmit Data Requests
;CLCXDQ - Calculate number of xmit data requests to send to Session Control.
;
;Call: ES/ The Sublink Block
; CALL CLCXDQ
; Normal Return with total data requests to send in T1
;Changes T1,T2,T3,T4
CLCXDQ: LOAD T1,ESXFL,(ES) ;GET THE TRANSMIT FLOW CONTROL MODE
JRST @[ IFIW CLCXDN ;NO FLOW CONTROL
IFIW CLCXD1 ;SEGMENT FLOW CONTROL
IFIW CLCXD1 ;MESSAGE FLOW CONTROL
IFIW CLCXDN](T1) ;ILLEGAL FLOW CONTROL
;Here to calculate the number of data requests to send to SCTL
CLCXD1: LOADE T1,ESXRD,(ES) ;DRQS OUTSTANDING FROM REMOTE
LOADE T2,ESXLD,(ES) ;DRQS OUTSTANDING TO SCTL
STOR T1,ESXLD,(ES) ;SCTL NOW AGREES WITH REMOTE
SUB T1,T2 ;GET THE DIFFERENCE
LOADE T2,ESXSD,(ES) ;GET ANY PREVIOUS SEND COUNT
ADD T1,T2 ;SIGNED ARITHMETIC
STOR T1,ESXSD,(ES) ;SEND THESE TO SESSION CONTROL
RET ;T1 HOLDS CURRENT VALUE OF ESXSD
;Here to send no data requests
CLCXDN: MOVEI T1,0 ;RETURN ZERO DATA REQUESTS
RET
;JCHACK - Send any ACKs that need sending for both sublinks
;
;Call: EL/ The Port Block
; ES/ available, caller has saved it
; MB/ available, caller has saved it
; CALL JCHACK
; Error Return if no resources
; Normal Return
;Changes T1,T2,T3,T4
JCHACK: TMNN ESACK,+EL.OSL(EL) ;NEED AN 'OTHER' ACK?
JRST JCHAK1 ;NO
XMOVEI ES,EL.OSL(EL) ;YES, SET UP FOR "OTHER" SUBLINK
CALL SNDACK ;SEND 'OTHER' ACK
RET ;PROPOGATE ERROR RETURN
JCHAK1: TMNN ESACK,+EL.NSL(EL) ;NEED AN ACK ON 'NORMAL' SUBLINK?
RETSKP ;NO, SUCCESS RETURN
XMOVEI ES,EL.NSL(EL) ;SET UP FOR "NORMAL" SUBLINK
CALL SNDACK ;CALL COMMON CODE
RET ;PROPOGATE ERROR RETURN
RETSKP ;SUCCESS RETURN
;JCHSCM - Send a Connect-type message if needed
;
;Call: EL/ The Port Block
; ES/ available, caller has saved it
; MB/ available, caller has saved it
; CALL JCHSCM
; Error Return if no resources
; Normal Return
;Changes T1,T2,T3,T4
;
;Only called if ELSCM or ELSDM is TRUE by JIFSER
JCHSCM: LOAD T1,ELSTA,(EL) ;GET PORT STATE
IFSTATE T1,<CR,CC,DR> ,CHKSC1 ;SEND A CONNECT ACK MESSAGE
IFSTATE T1,<RC,IC,DN,CN>,RSKP ;LET CHKCLS SENT DISCON CONFIRM
IFSTATE T1,<CL,DP> ,CHKSC2 ;NO NEED TO SEND ANY MORE
;If we fall through to here, ELSxM should not have been set
BUG.(CHK,LLISCM,LLINKS,SOFT,<ELSCM should not have been set>,,<
Cause: This BUG is not documented yet.
>,CHKSC2)
;IGNORE THE ELSCM FLAG
;Here if port is in a Connect state, send a Connect ACK Message
CHKSC1: MOVEI T1,0 ;NO USER DATA BUFFER NEEDED
CALL DNGMSG ;GET A MESSAGE BLOCK
RET ;ERROR RETURN, REQUESTING JIFFY SERVICE
MOVE MB,T1 ;MESSAGE BLOCK POINTER RETURNED IN T1
CALL SNDCAK ;SEND CONNECT ACK MESSAGE
;FALL THROUGH TO CHKSC3
;Here if port is in CLose state, ELSCM is past due, ignore it
CHKSC2: SETZRO <ELSCM,ELSDM>,(EL)
RETSKP ;SUCCESS, CALLER DOESN'T CARE ABOUT
; ERRORS OTHER THAN NO-RESOURCES
SUBTTL NSPRJF - Request Jiffy Service
;NSPRJF - Request Jiffy Service
;
;Call: EL/ The Port Block
; CALL NSPRJF
; Normal Return
;Changes T1,T2,T3,T4
NSPRJF: MOVX T1,ELOJQ ;THE ON-JIFFY-QUEUE BIT
TDNE T1,EL.OJQ(EL) ;ALREADY ON JIFFY-REQUEST QUEUE?
RET ;YES, DON'T REPEAT
IORM T1,EL.OJQ(EL) ;NO, BUT IT IS NOW
ENDQUE EL,NSPJFQ,EL.JFQ,T1
RET
SUBTTL SECCHK - Once-a-Second Checks
;SECCHK - Once-a-Second Checks
;
;Call: CALL SECCHK
; Normal Return
;Changes T1,T2,T3,T4
SECCHK: SAVEAC <EL,P1,ES,MB,MS> ;SAVED FOR CALLEES
LOAD P1,QHBEG,+NSPAPQ ;GET FIRST PORT ON ALL-PORT-QUEUE
SECCH1: SKIPN EL,P1 ;POINT TO NEXT PORT IN QUEUE
RET ;NO MORE PORTS, LEAVE
LOAD P1,ELSTA,(EL) ;GET PORT STATE
IFSTATE P1,<CR,CL,DP>,SECCH2 ;IGNORE CHECKS IF SCTL NOT YET
; INVOLVED OR IF PORT IS CLOSED
CALL CHKRSN ;CHECK FOR RESENDS * BEFORE CHKCNF *
CALL CHKCNF ;CHECK FOR CONFIDENCE IN PORT
IFSTATE P1,<RN> ;IF ITS IN RUN STATE
CALL CHKINA ; CHECK FOR INACTIVITY
;This check must be last, since it may destroy the port block!
SECCH2: MOVE T1,P1 ;WE'LL NEED P1 FOR NEXT PORT POINTER
LOAD P1,QPNXT,+EL.APQ(EL) ;GET NEXT POINTER WHILE WE HAVE PORT
IFSTATE T1,<DP> ;IF ITS IN DESTROY-PORT STATE,
CALL DSTPRT ; DESTROY THE PORT
JRST SECCH1 ;GO SEE IF THERE ARE MORE PORTS TO DO
;CHKINA - Check Port for Inactivity
;
;Call: EL/ The Port Block
; ES/ available, saved by caller
; MB/ available, saved by caller
; CALL CHKINA
; Normal Return
;Changes T1,T2,T3,T4
;This is only called when the port is in RUN state
CHKINA: CALL DNGTIM ;GET CURRENT TIME INTO T1
OPSTR <SUB T1,>,ELTMA,(EL) ;SUBTRACT TIME OF LAST ACTIVITY
IDIVI T1,TIMBAS ;MAKE IT SECONDS
CAMG T1,NSPINA ;IS IT TOO LONG?
RET ;NO
LOAD T2,ESXOF,+EL.NSL(EL) ;GET "NORMAL" TRANSMIT OFF FLAG
LOAD T1,ESLAR, +EL.NSL(EL) ;GET LAST "NORMAL" ACK RECEIVED
CMODE T1,ESLMA,+EL.NSL(EL) ;SAME AS LAST MESSAGE SENT?
JUMPE T2,RTN ;NO, LEAVE OF LINK STILL TURNED ON
LOAD T1,ESLAR, +EL.OSL(EL) ;GET LAST "OTHER" ACK RECEIVED
CMODE T1,ESLMA,+EL.OSL(EL) ;SAME AS LAST MESSAGE SENT?
RET ;NO, STILL SOME OUTSTANDING
;Here if we have decided that the port has been inactive too long. Try
;to send a null link service message and wait for the ACK. If the ACK
;times out then we will claim no confidence in the logical link.
MOVEI T1,0 ;NO USER DATA AREA NEEDED
CALL DNGMSG ;GET A MESSAGE BLOCK
RET ;CAN'T, HAVE TO TRY AGAIN LATER
MOVE MB,T1 ;POINTER TO MSG BLK RETURNED IN T1
XMOVEI ES,EL.NSL(EL) ;SEND THE DRQ ON THE "NORMAL" SUBLINK
CALL SENDRQ ;SEND A NULL LINK SERVICE MESSAGE.
RET ;(SHOULD NOT HAPPEN, WE'VE JUST CHECKED)
CALL DNGTIM ;WE'RE MAKING SOME ACTIVITY, SO WE
STOR T1,ELTMA,(EL) ; WON'T KEEP FINDING NONE EVERY SECOND.
RET
;CHKRSN - Check for Resends
;
;Call: EL/ The Port Block
; ES/ available, saved by caller
; MB/ available, saved by caller
; CALL CHKRSN
; Normal Return
;Changes T1,T2,T3,T4
;In order to avoid difficult mangling of the ACK queues, we only check
;the first message on the queue for timeout. It will almost always be
;the oldest on the queue, and when it isn't it won't be much different.
CHKRSN: TMNN ELCNF,(EL) ;DO WE HAVE CONFIDENCE IN LINK?
RET ;NO, JUST WAIT FOR CLOSE
XMOVEI ES,EL.NSL(EL) ;CHECK THE "NORMAL" SUBLINK
CALL CHKRSC ;CALL COMMON CODE
XMOVEI ES,EL.OSL(EL) ;CHECK THE "OTHER" SUBLINK
;Continued on Next Page
;Continued from Previous Page
;Common resend code for the "normal" and "other" sublinks
CHKRSC:
CHKRS1:!LOAD MB,QHBEG,+ES.AKQ(ES) ;GET POINTER TO FIRST MESSAGE ON QUEUE
JUMPE MB,CHKRSX ;FINISH UP IF LIST NOW EMPTY
CALL DNGTIM ;GET CURRENT TIME INTO T1
OPSTR <SUB T1,>,NMTIM,(MB) ;GET TIME SINCE WE SENT THIS MESSAGE
LOAD T3,ELNDB,(EL) ;GET ADDR OF NODE BLOCK FOR THIS LINK
LOAD T2,NNDLY,(T3) ;GET EXPECTED ROUND-TRIP DELAY FOR NODE
IMUL T2,NSPDLY ;MULTIPLY IT BY THE DELAY FACTOR
ASH T2,-4 ; WHICH IS IN 16ths
CAMG T1,T2 ;HAVE WE BEEN WAITING THAT LONG?
JRST CHKRSX ;NOT YET, DON'T COMPLAIN
;Here if a message has timed out
LOAD T2,ELVER,(EL) ;GET REMOTE NSP'S VERSION CODE
CAIE T2,VER3.1 ;IS IT PHASE II?
JRST CHKRS2 ;NO
LOAD T1,NMTIM,(MB) ;ZERO IF NAK FORCED RETRANSMISSION
JUMPN T1,CHKRSX ;LEAVE UNLESS NAK FORCED RETRANS
JRST CHKRS3 ;PHASE II NEVER GETS NO CONFIDENCE
CHKRS2: ;NMCNT INCR'D BY SNDRTR
LOAD T1,NMCNT,(MB) ;GET NUMBER OF TIMES WE'VE SENT MESSAGE
CAMG T1,NSPRTH ;GREATER THAN RESEND THRESHOLD?
JRST CHKRS3 ;NO
;Here if a message has timed out more than the allowed number of times
TMNN ELCNF,(EL) ;YES, DID WE HAVE CONFIDENCE?
RET ;NO?, DON'T TELL SCTL AGAIN
SETZRO ELCNF,(EL) ;YES, BUT NO MORE
SETONE ELSNC,(EL) ;TELL SESSION CONTROL ABOUT IT
; SECCHK CHECKS ELSNC NEXT
RET ;ALL DONE IF NO CONFIDENCE
;Here to resend a message
CHKRS3: LOAD T1,ELNDB,(EL) ;GET POINTER TO NSP NODE BLOCK
INCR NNTMC,(T1) ;INCREMENT NUMBER OF TIMEOUTS
DEQUE MB,ES.AKQ(ES),MB.NXT,CHKRS1 ;SHOULD NOT BE EMPTY
CALL RSNMSG ;RESEND THIS MESSAGE
JRST CHKRS1 ;GO TRY THE NEXT MESSAGE
;Here when we have finished looking at the ACK queue
;See if we have put anything on the xmit queue for transmission
CHKRSX: TMNE QHBEG,+ES.XMQ(ES) ;ANYTHING IN THE XMIT QUEUE NOW?
CALLRET PROCXQ ;YES, TRY TO SEND IT NOW
RET ;NORMAL RETURN
;RSNMSG - Resend a Message
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message to Resend
; CALL RSNMSG
; Normal Return
;Changes T1,T2,T3,T4
RSNMSG: LOAD T1,NMMGF,(MB) ;GET MESSAGE TYPE
LSH T1,-2 ;SET UP TO READ MSGTBL (MESSAGE TABLE)
CAILE T1,RCVMAX ;IS IT A LEGAL MESSAGE TYPE?
BUG.(CHK,LLIPIM,LLINKS,SOFT,<PROCXQ found illegal message type>,,<
Cause: This BUG is not documented yet.
>,SNDATA)
TMNN RTFLO,+MSGTBL(T1) ;THIS MESSAGE TYPE FLOW CONTROLLED?
CALLRET SNDATA ;NO, SEND IT IMMEDIATELY
;Here if we are resending a flow controlled message
LOAD T1,ESXFL,(ES) ;GET THE FLOW CONTROL MODE
CAIE T1,FCM.SG ;IS IT SEGMENT FLOW CONTROL MODE?
JRST RSNMS1 ;NO
OPSTRM <AOS T1,>,ESXRD,(ES) ;(GOOD AS LOADE ON AOS NEGATIVE)
STOR T1,ESXLD,(ES) ;GIVE PERMISSION TO RESEND IT
;XLD AND XRD ALWAYS IN SYNCH IN SEG MODE
RSNMS1: XMOVEI T1,ES.XMQ(ES) ;NO, GET PTR TO THE XMT-Q HEADER PAIR
CALL QSRTMB ;QUEUE MESSAGE IN (MB) TO SEND AGAIN
BUG.(CHK,LLIRQ2,LLINKS,SOFT,<Duplicate msg requeued>,,<
Cause: This BUG is not documented yet.
>,RTN)
RET
;CHKCNF - Check confidence, tell Session Control if its news
;
;Call: EL/ The Port Block
; ES/ available, saved by caller
; MB/ available, saved by caller
; CALL CHKCNF
; Normal Return
;Changes T1,T2,T3,T4
;We don't use the message block we just got, but we have to have one
;for the call to Session Control in case Session Control wants to
;queue the call.
CHKCNF: TMNN ELSNC,(EL) ;HAS CONFIDENCE FLAG CHANGED?
RET ;NO
;ONCE OFF, ELCNF NEVER LIGHTS AGAIN
MOVEI T1,0 ;NO USER DATA NEEDED
CALL DNGMSG ;GET A MESSAGE BLOCK
RET ;CAN'T, TRY AGAIN LATER
SETZRO ELSNC,(EL) ;CONFIDENCE CHANGE NO LONGER NEEDED
MOVE T4,T1 ;THE MESSAGE BLOCK RETURNED
MOVEI T3,SV.NCF ;NO CONFIDENCE FUNCTION CODE
LOAD T1,ELSCB,(EL) ;GET SESSION CONTROL'S PORT ID
OPSTR <CALLRET @>,ELSCV,(EL) ;TELL SESSION CONTROL ABOUT IT
SUBTTL Memory Manager Calls -- NSPCGT - We're Congested
;NSPCGT Memory Manager calls this when available memory falls
; below a threshold.
;
;Call: CALL NSPCGT
; Normal Return, NSP may have freed some buffers
;
;Uses: T1,T2,T3,T4,T5,T6 (in principle)
;If DCNCON is non-zero, RCVSEG will not cache incoming messages.
NSPCGT:: ;MAY BE AT INTERRUPT LEVEL HERE
SAVEAC <P1,P2,MS,MB,FREE1,FREE2>
SETONE LKCGT,+NSPLKF ;WE NEED CONGESTION-DETECTED PROCESSING
CALLRET NSPLCF ;TRY FOR THE INTERLOCK WITH LKCGT SET
NSICGT: SKIPN DCNCON ;ARE WE CONGESTED STILL?
RET ;NO, LEAVE NOW
;For each port in the all ports queue, call PESFLO to return cached
;buffers, put the link into pessimistic flow control and send out
;negative data requests.
;
;Note that we do not have to worry about throwing away a message from a
;Phase II node, since Session Control will not allow a non-zero goal
;for a link to a Phase II node; thus there will never be a receive queue
;unless we miss a message. If we miss a Phase II message, the link is
;sunk anyway.
LOAD EL,QHBEG,+NSPAPQ ;GET HEAD OF ALL PORTS LIST
NSICG1: JUMPE EL,RTN ;LEAVE IF LIST IS EMPTY
XMOVEI ES,EL.NSL(EL) ;ONLY CHECK "NORMAL" SUBLINK
TMNE QHBEG,+ES.RCQ(ES) ;IF THERE IS A RECEIVE Q,
CALL PESFLO ; TRY TO RELIEVE CONGESTION
LOAD EL,QPNXT,+EL.APQ(EL) ;NEXT PORT ON THE ALL-PORTS Q
JRST NSICG1 ;DO NEXT IF THERE IS ONE
SUBTTL Memory Manager Calls -- NSPCGR - Congestion is Relieved
;NSPCGR Memory Manager calls this when available memory recovers
; to above a threshold after having called NSPCGT.
;
;Call: CALL NSPCGR
; Normal Return, NSP may have freed some buffers
;
;Uses: T1,T2,T3,T4,T5,T6 (in principle)
;Note: The memory manager may call this routine without having called
; NSPCGT first. This is fine now, since all we do is clear the
; the congestion flag. This should continue to be fine.
NSPCGR::SAVEAC MB ;SAVE MB FROM NSPLCF
SETONE LKRLV,+NSPLKF ;WE NEED CONGESTION-RELIEVED PROCESSING
CALL NSPLCF ;TRY TO GET INTERLOCK NOW
CALLRET SCTUCG ;SESSION CONTROL IS INTERESTED TOO
SUBTTL NSIRLV - Congestion-Relieved Processor
;NSIRLV - Congestion-Relieved Processor
;
;Call: CALL NSIRLV
; Only return
;This routine is the interlocked version of NSPCGR, which is called by
;the memory manager when congestion is relieved.
;The idea is to visit each link which was turned off when congestion
;was detected and to turn it back on again.
NSIRLV: SAVEAC <EL,ES,P1,P2>
MOVE EL,NSPAPQ ;POINTER TO FIRST OF ALL NSP PORTS
NSRLV1: JUMPE EL,RTN ;LEAVE WHEN WE'VE PROCESSED ALL PORTS
XMOVEI ES,EL.NSL(EL) ;POINT PROCRQ TO NORMAL DATA SUBLINK
TMNE ESROF,(ES) ;TURNED OFF?
CALL PROCRQ ;YES, TRY TO TURN IT ON AGAIN
LOAD EL,ELAPQ,(EL) ;STEP TO NEXT
JRST NSRLV1 ; PORT ON ALL PORTS LIST
SUBTTL PESFLO - Put Link in Pessimistic Flow Control
;PESFLO - Put Link in Pessimistic Flow Control
;
;Call: EL/ A port block with a non-zero receive queue
; ES/ The Sublink Block (always the "normal" sublink)
; CALL PESFLO
; Normal Return
;
;Uses: T1,T2,T3,T4
PESFLO: SAVEAC MB
LOAD T1,ESRFL,(ES) ;GET THE RECEIVE FLOW CONTROL MODE
CALL @[ IFIW PESOFF ;NO FLOW CONTROL (SEND OFF MSG)
IFIW PESSEG ;SEGMENT FLOW CONTROL (SEND NEG DRQS)
IFIW PESOFF ;MESSGAGE F.C. (SEND OFF MSG)
IFIW RTN](T1) ;ILLEGAL F.C. (IGNORE IT)
JRST PESFL1 ;ALREADY PESSIMIZED
;Use the first message block on the queue to send the link
;service message immediately.
DEQUE MB,ES.RCQ(ES),MB.NXT,RTN ;RETURN IF QUEUE IS EMPTY
MOVE T1,MB ;SET UP FOR CALL TO DNMINI
MOVEI T2,0 ;NO USER DATA REQUIRED
CALL DNMINI ;CLEAN OUT THE MESSAGE BLOCK
BUG.(CHK,LLICGT,LLINKS,SOFT,<Can't DNMINI a msg blk>,,<
Cause: DNMINI has refused to initialize a message block. This should
never happen when the number of bytes requested is zero.
Cure: Probably something wrong with the message block, its pointer or DNMINI
>,PESFL1)
CALL SENDRQ ;SEND OUT THE LINK SERVICE MSG NOW
CALL NSPRJF ;CAN'T, OSL IS IN USE, HAVE TO WAIT
;Deallocate the rest of the messages on the receive queue
PESFL1: CALLRET CLRSRQ ;CLEAR OUT SUBLINK'S RECEIVE Q
;Here to turn off a link.
PESSEG:
PESOFF: TMNE ESROF,(ES) ;ALREADY OFF?
RET ;YES, DON'T SEND OFF MSG AGAIN
SETONE <ESROF,ESROC>,(ES) ;LINK IS OFF, OFF FLAG HAS CHANGED
SETZRO ESGOL,(ES) ;ZERO THE GOAL FOR CHKRCQ
RETSKP ;SENDRQ WILL SEND THE OFF MSG
SUBTTL SCLOSE - Start the Close Process
;SCLOSE - Start the Close Process when Ready
;
;Call: T1/ The new port state
; EL/ The Port Block
; CALL SCLOSE
; Normal Return
;Changes T1,T2,T3,T4
SCLOSE: CALL CHKCLS ;CALL THE VERSION THAT COMPLAINS
JRST NSPRJF ; IF CAN'T START THE CLOSE YET
RET ;SMOOTH OUT THE RETURN
;CHKCLS - Inner routine for SCLOSE (q.v)
;
;Call: EL/ The Port Block in its new state
; CALL CHKCLS
; Error Return if need jiffy service
; Normal Return
;Changes T1,T2,T3,T4
CHKCLS: JN ELORC,(EL),RTN ;TRY AGAIN NEXT JIFFY IF ANYTHING IN RTR
LOAD T1,ELSTA,(EL)
IFSTATE T1,<RJ,DI> ,CHKCL1 ;IF ABORT CLEAR QS; CLOSE REMOTE
IFSTATE T1,<DN,RC,IC>,CLSRMT ;CLOSE REMOTE
IFSTATE T1,<CN> ,CHKCL2 ;CLEAR QUEUES, CLOSE REMOTE
IFSTATE T1,<CL> ,CLSLOC ;CLOSE LOCAL PORT
IFSTATE T1,<NC,NR,DP>,RSKP ;NOTHING TO DO IN THESE STATES
;Here if we are in an unexpected state
BUG.(CHK,LLICLS,LLINKS,SOFT,<Tried to close in non-pre-close state>,,<
Cause: This BUG is not documented yet.
>,RSKP)
;Here for RJ or DI state
;If this is an abort, DI will go out now, since RETBUF
;took away any reason not to.
CHKCL1: TMNE ELABO,(EL) ;ABORT?
CALL RETBUF ;YES, RETURN ALL BUFFERS (XMQ,RCQ,AKQ)
SETZRO ELABO,(EL) ;DON'T CLEAN OUT DI MSG NEXT TIME
CALLRET CLSRMT ;THEN GO CLOSE REMOTE
;Here for CN or IC or RC state
CHKCL2: CALL RETBUF ;RETURN ALL BUFFERS (XMQ,RCQ,AKQ)
CALLRET CLSRMT ;THEN GO CLOSE REMOTE
;CLSRMT - The Remote Node has Closed this Link
;
;Call: EL/ The Port Block
; CALL CLSRMT
; Error Return if we need jiffy service
; Normal Return
;Changes T1,T2,T3,T4
CLSRMT: SAVEAC <MB,MS,ES>
CALL JCHACK ;SEND AN ACK IF NEED BE
RET ;COULDN'T, TRY AGAIN NEXT JIFFY
LOAD T1,ESLMA,+EL.NSL(EL) ;"NORMAL" SUBLINK FINISHED ACKING?
OPSTR <CAME T1,>,ESLAR,+EL.NSL(EL)
RET ;NO, TRY NEXT JIFFY
LOAD T1,ESLMA,+EL.OSL(EL) ;"OTHER" SUBLINK FINISHED ACKING?
OPSTR <CAME T1,>,ESLAR,+EL.OSL(EL)
RET ;NO, TRY NEXT JIFFY
;Though ELDIM is timeshared to hold a CI message in CI state and a DI
;message in DI state, we are guaranteed that we only have a DI message
;here because you can't get to DI state from CI state without going
;through CC or RJ states, both of which clear out any CI message.
LOAD MB,ELDIM,(EL) ;GET POINTER TO SAVED DI MSG
JUMPE MB,CLSRM1 ;JUMP IF NONE THERE
SETZRO ELDIM,(EL) ;NOT THERE ANY MORE
LOAD T1,ELSTA,(EL) ;GET LINK'S STATE
XMOVEI T2,FREMSG ;ASSUME NOT DI STATE (MIGHT BE CI MSG)
IFSTATE T1,<DI> ;IS IT IN DISCONNECT INITIATED STATE?
XMOVEI T2,SENDDI ;YES, WE'LL SEND THIS DI MESSAGE
CALL 0(T2) ;SEND OR FREE MESSAGE IN MB
CLSRM1: TMNN ELSDM,(EL) ;NEED TO SEND A DISCONNECT CONFIRM MSG?
RETSKP ;NO, SUCCESS RETURN NOW
MOVEI T1,0 ;YES, NO USER DATA ON MESSAGE BLOCK
CALL DNGMSG ;GET A MESSAGE BLOCK
RET ;TRY AGAIN NEXT JIFFY
MOVE MB,T1
CALL SNDDSC ;SEND DISCONNECT COMPLETE MESSAGE
SETZRO ELSDM,(EL) ;DON'T NEED TO SEND IT ANY MORE
RETSKP ;SUCCESS RETURN
;CLSLOC - Close local side of port
;
;Call: EL/ The Port Block
; CALL CLSLOC
; Error Return if need jiffy service
; Normal Return
;Changes T1,T2,T3,T4
CLSLOC: CALL RETBUF ;RETURN ALL BUFFERS ON PORT
;We deallocate any DI message hanging off ELDIM before calling CLSRMT
;because we don't want CLSRMT to send the message. If the message
;were sent, ROUTER would return it at some later time. NSIODN
;(output done) would then try to queue it on the link block we are now
;destroying.
OPSTR <SKIPE T1,>,ELDIM,(EL);ANY SAVED DI MESSAGE?
CALL DNFMSG ;YES, TOSS IT AWAY
SETZRO ELDIM,(EL) ;NONE THERE NOW
CALL CLSRMT ;TRY TO CLOSE REMOTE SOMEWHAT POLITELY
JFCL ;FAILED, HE'LL HAVE TO GET A NO LINK
;We know all the queues are empty now, cause we just RETBUFed them
MOVEI T1,0 ;NO USER DATA NEEDED
CALL DNGMSG ;GET A MESSAGE BLOCK
RET ;TRY AGAIN NEXT JIFFY
MOVE T4,T1 ;SESSION CONTROL WANTS MSG PTR IN T4
LOAD T1,ELSCB,(EL) ;GET SESSION CONTROL'S NAME FOR PORT
MOVEI T3,SV.CLS ;PORT CLOSED FUNCTION CODE
OPSTR <CALL @>,ELSCV,(EL) ;CALL SESSION CONTROL
NEWSTATE DP ;DESTROY PORT NEXT SECCHK
RETSKP ;SUCCESS RETURN
;RETBUF - Return all buffers (messages) on all queues of port
;
;Call: EL/ The Port Block
; CALL RETBUF
; Normal Return
;Changes T1,T2,T3,T4
RETBUF: CALL CLRRCQ ;CLEAR THE RECEIVE QUEUES
CALL CLRXMQ ;CLEAR THE TRANSMIT QUEUES
CALL CLRAKQ ;CLEAR THE ACK QUEUES
IFN FTPARANOID,<
SAVEAC ES
XMOVEI ES,EL.NSL(EL) ;DO THE "NORMAL" SUBLINK
CALL RETBF1 ;CALL COMMON CODE
XMOVEI ES,EL.OSL(EL) ;DO THE "OTHER" SUBLINK
RET
RETBF1: LOAD T1,ESLAR,(ES) ;GET LAST ACK RECEIVED
CMODN T1,ESLMA,(ES) ;SAME AS LAST MESSAGE SENT?
RET ;YES, RETBUF WORKED PROPERLY
BUG.(CHK,LLILMA,LLINKS,SOFT,<RETBUF left LAR # LMA>,,<
Cause: This BUG is not documented yet.
>)
STOR T1,ESLMA,(ES) ;TRY TO RECOVER
>;END OF IFN FTPARANOID
RET
;CLRRCQ - Clear all messages on both receive queues
;
;Call: EL/ The Port Block
; CALL CLRRCQ
; Normal Return
;Changes T1,T2,T3,T4
CLRRCQ: SAVEAC ES
XMOVEI ES,EL.NSL(EL) ;DO THE "NORMAL" SUBLINK
CALL CLRSRQ ;CALL SINGLE-Q CLEARER
XMOVEI ES,EL.OSL(EL) ;DO THE "OTHER" SUBLINK
;FALL THROUGH TO SINGLE-Q CLEARER
;CLRSRQ - Clear all messages on a receive queue
;
;Call: ES/ The Sublink Block
; CALL CLRSRQ
; Normal Return
;Changes T1,T2,T3,T4
CLRSRQ: DEQUE T1,ES.RCQ(ES),MB.NXT,RTN ;RTN IF Q IS EMPTY
CALL DNFMSG ;JUST TOSS THE MESSAGE FROM T1
JRST CLRSRQ ;LOOP ALONG THE WHOLE QUEUE
;CLRXMQ - Clear all messages on both transmit queues
;
;Call: EL/ The Port Block
; CALL CLRXMQ
; Normal Return
;Changes T1,T2,T3,T4
CLRXMQ: SAVEAC <ES,MB,P1,P2>
XMOVEI ES,EL.NSL(EL) ;DO THE "NORMAL" SUBLINK
XMOVEI P2,ES.XMQ(ES) ;CLEAN OUT THE XMIT QUEUE
CALL CLRP2Q ;CALL COMMON CODE
XMOVEI ES,EL.OSL(EL) ;DO THE "OTHER" SUBLINK
XMOVEI P2,ES.XMQ(ES) ;CLEAN OUT THE XMIT QUEUE
CALLRET CLRP2Q ;CALL COMMON CODE
;CLRAKQ - Clear all messages on both ACK queues
;
;Call: EL/ The Port Block
; CALL CLRAKQ
; Normal Return
;Changes T1,T2,T3,T4
CLRAKQ: SAVEAC <ES,MB,P1,P2>
XMOVEI ES,EL.NSL(EL) ;DO THE "NORMAL" SUBLINK
XMOVEI P2,ES.AKQ(ES) ;CLEAN OUT THE ACK QUEUE
CALL CLRP2Q ;CALL COMMON CODE
XMOVEI ES,EL.OSL(EL) ;DO THE "OTHER" SUBLINK
XMOVEI P2,ES.AKQ(ES) ;CLEAN OUT THE ACK QUEUE
;FALL THROUGH TO COMMON CODE
;CLRP2Q - Clear the queue pointed to by P2 and ES
CLRP2Q: TMNN QHBEG,(P2) ;ANYTHING IN THE QUEUE?
RET ;NO, DON'T CHANGE ESLAR
CLRP21: DEQUE MB,QH.BEG(P2),MB.NXT,CLRP22 ;CLRP22 IF Q IS EMPTY
LOAD P1,NMSGN,(MB) ;GET THIS MSG'S SEGMENT NUMBER
MOVX T1,MA%NDONE ;OUTPUT NOT DONE FLAG
CALL MGACKD ;PRETEND THAT THE MESSAGE WAS ACKED
JRST CLRP21 ;LOOP OVER THE WHOLE QUEUE
;Here we store the number of the last message we MGACKD
;The queue is sorted in ascending order, so we know it was
;the highest segment number we have seen.
CLRP22: CMODLE P1,ESLAR,(ES) ;IS LATEST MSG RETURNED HIGHEST?
STOR P1,ESLAR,(ES) ;STORE AS LAST ACK RECEIVED,
SETZRO ELDTM,(EL) ;WE'RE FINISHED WITH DELAY TIMER
RET ;QUEUE LOOKS TRUELY EMPTY NOW
SUBTTL SENDRQ - Send a Link Service Message
;SENDRQ - Send a Link Service Message
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message Block to send the link service message in
; CALL SENDRQ
; Error Return if OSL is in use, message block freed
; Normal Return
;Changes T1,T2,T3,T4,MS
;SENDRQ zeros the ESROC and ESRSD fields after it has filled in the
;appropriate fields of the link service message, so that the requests
;will not be honored again next jiffy.
;Note that SENDRQ will send a Link Service message every time it is
;called and the "other" sublink is available, whether or not one really
;needs to be sent. It is the caller's responsibility to see if ESRSD
;and/or ESROC are non-zero if they want to avoid sending null link
;service messages. The reason is that SECCHK (or its children) may
;call SENDRQ to send a null link service message on purpose if the
;inactivity timer expires.
;When we find that we cannot send a DRQ message because the "other"
;sublink is in use (has an ACK outstanding), we do not build the
;Link Service message and queue it. Instead, we leave the number
;of DRQs to send in the sublink block (or inactivity timer) and wait
;for the next jiffy service to discover them. This prevents queuing
;up several messages when we could add the DRQs and send a single
;message when we have a message block and the OSL is free.
SENDRQ: LOAD T1,ESLAR,+EL.OSL(EL) ;ANY OSL MESSAGES TO BE ACKED?
CMODE T1,ESLMA,+EL.OSL(EL) ;LAST MSG # ASSIGNED = LAST ACK REC'D?
CALLRET FREMSG ;NO, SEND IT LATER FROM JIFSER
;Its OK to send the Link Service message now.
SAVEAC <ES,P1> ;P1 WILL HOLD CALLER'S ES PTR
MOVE P1,ES ; SO THAT ES CAN POINT TO "OTHER" SL
XMOVEI ES,EL.OSL(EL) ; SINCE THIS MSG IS GOING OUT ON OSL
CALL MAKHDR ;INITIALIZE DNPxBY FOR THE NSP HEADER
;Continued on Next Page
;Continued from Previous Page
;Note that P1 holds the pointer to caller's sublink block
;ES points to the "other" sublink block, since we are sending
; the DRQ on the "other" sublink
;MSGFLG
MOVX T1,MGFLKS ;ITS A LINK SERVICE MESSAGE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;GET THE REMOTE LINK ADDRESS
CALL DNP2BY
;SLA
LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS
CALL DNP2BY
;ACKNUM
CALL WRTACK ;WRITE THE ACKNUM FIELD & ZERO ESACK
;SEGNUM
CALL NEWSGN ;PUT NEW SGN IN MSG BLK & T1
CALL DNP2BY
;LSFLAGS
SETZ T1, ;SET TO BUILD LSFLAGS FIELD HERE
;FCVAL INT
MOVX T2,LS.INR ;ASSUME A "NORMAL" SUBLINK DATA REQUEST
TMNE ESOTH,(P1) ;IS IT THE "OTHER" SUBLINK?
MOVX T2,LS.IOT ;YES
STOR T2,LSINT,+T1 ;STORE INTERPRETATION SUBFIELD
;FC MOD
MOVX T2,ESROC ;LOAD UP THE RECEIVE OFF CHANGED FLAG
TDNN T2,ES.ROC(P1) ;OFF FLAG CHANGED?
JRST SENDR1 ;NO, LEAVE FC MOD FIELD ZERO
ANDCAM T2,ES.ROC(P1) ;YES, NOT ANY MORE
MOVX T2,LS.MOF ;ASSUME TURNED OFF
TMNN ESROF,(P1) ;IS IT OFF?
MOVX T2,LS.MON ;NO, IT MUST BE ON
STOR T2,LSMOD,+T1 ;STORE FC MOD FIELD
SENDR1: CALL DNPEBY ;WRITE EXTENSIBLE FIELD
;FCVAL
LOADE T1,ESRSD,(P1) ;GET # OF DRQS TO SEND TO REMOTE
CALL DNP1BY ;ONE BYTE FIELD (2S COMPLEMENT IF NEG)
SETZRO ESRSD,(P1) ;WE'VE SENT THEM, ZERO DRQ REQUEST FIELD
SETONE MBOTH,(MB) ;GO ON "OTHER" SUBLINK
MOVX T1, ST%NRS ! ST%ACK ! ST%NRQR ! ST%TRL
CALL SNDRTR ;NO RETURN TO SESSION CONTROL
;ACK REQUIRED
;NO RETURN REQUESTED FROM ROUTER
RETSKP ;TROLL ALLOWED
SUBTTL SACKMG - Get a Message Block and send ACK message if can
;SACKMG - Get a Message Block and send ACK message if can
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; CALL SACKMG
; Normal Return, message sent or NSPRJF called
;Changes T1,T2,T3,T4
SACKMG: SAVEAC <MB,MS> ;SNDACK WILL SMASH THESE
SETONE ESACK,(ES) ;WE NEED AN ACK ON THIS SUBLINK
CALL SNDACK ;SEND THE ACK
JRST NSPRJF ;CAN'T, GET JIFFY SERVICE
RET ;SUCCESS
SUBTTL SNDACK - Check a sublink to see if it needs an ACK sent
;SNDACK - Check a sublink to see if it needs an ACK sent
;
;Call: EL/ The Port Block
; ES/ The Sublink Block
; MB/ available, caller has saved it
; MS/ available, caller has saved it
; CALL SNDACK
; Error Return if can't get a message block
; Normal Return
;Changes T1,T2,T3,T4
;ACKs on the "other" sublink must go out right away, since the sublink
;cannot be used while an ACK is outstanding. While we are sending a
;message on the OSL, we might as well see if there are any DRQs waiting
;for a jiffy. If so, we can piggyback the ACK on the DRQ message.
SNDACK: TMNN ESOTH,(ES) ;WE ON THE "OTHER" SUBLINK?
JRST SNDAK1 ;NO
CALL CHKSDQ ;YES, TRY TO PIGGYBACK THIS ACK ON A DRQ
RET ;CAN'T GET A MSG BLK
TMNN ESACK,(ES) ;DID WE SEND THE ACK?
RETSKP ;YES, ALL DONE
;NO, SEND IT ALONE
SNDAK1: MOVEI T1,0 ;NO USER DATA NEEDED
CALL DNGMSG ;GET A MESSAGE BLOCK
RET ;CAN'T, ERROR RETURN TO TRY AGAIN
MOVE MB,T1 ;MESSAGE BLOCK POINTER RETURNED IN T1
CALL MAKHDR ;INITIALIZE DNPxBY FOR THE NSP HEADER
;MSGFLG
MOVX T1,MGFACK ;ASSUME ITS A NORMAL ACK
TMNE ESOTH,(ES) ;IS THIS THE "OTHER" SUBLINK?
MOVX T1,MGFOAK ;YES, LOAD UP AN "OTHER" ACK CODE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;GET THE REMOTE LINK ADDRESS
CALL DNP2BY
;SLA
LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS
CALL DNP2BY
;ACKNUM
CALL WRTACK ;WRITE THE ACKNUM FIELD & ZERO ESACK
LOAD T1,ESOTH,(ES) ;GET THE "OTHER" SUBLINK FLAG
STOR T1,MBOTH,(MB) ;COPY TO THE MESSAGE BLOCK
MOVX T1, ST%NRS ! ST%NAK ! ST%NRQR ! ST%TRL
CALL SNDRTR ;NO RETURN TO SESSION CONTROL
;NO ACK REQUIRED
;NO RETURN REQUESTED FROM ROUTER
;TROLL ALLOWED
RETSKP ;SUCCESS RETURN
SUBTTL SENDDI - Send a DI Message
;SENDDI - Send a Disconnect Initiate Message
;
;Call: EL/ The Port Block
; MB/ The Message Block with REASON and DATA-CTL field in User Data
; CALL SENDDI
; Normal Return
;Changes T1,T2,T3,T4
;Note that Session Control is expected to have put the REASON field (2
;bytes) and the DATA-CTL field, with leading byte of binary count, in
;the user data part of the message. NSP only fills in the MSGFLG,DLA
;and SLA fields before the Session Control contributions.
SENDDI: SAVEAC ES
XMOVEI ES,EL.NSL(EL) ;WE'LL SEND THIS ON THE NSL
CALL MAKHDR ;INITIALIZE DNPxBY FOR THE NSP HEADER
;MSGFLG
MOVX T1,MGFDI ;ITS A DISCONNECT INITIATE MESSAGE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;GET THE REMOTE LINK ADDRESS
CALL DNP2BY
;SLA
LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS
CALL DNP2BY
;Here we give the DI message the next message number for the "normal"
;sublink, since there will be no more real messages, and we don't want
;NSIODN to think that the DI message has already been ACKed.
CALL NEWSGN ;PUT NEW SGN IN MSG BLK
MOVX T1, ST%NRS ! ST%ACK ! ST%NRQR ! ST%TRL
CALLRET SNDRTR ;NO RETURN TO SESSION CONTROL
;ACK REQUIRED
;NO RETURN REQUESTED FROM ROUTER
;TROLL ALLOWED
SUBTTL SNDCAK - Send a Connect ACK Message
;SNDCAK - Send a Connect ACK Message
;
;Call: EL/ The Port Block
; MB/ The Message Block, initialized
; CALL SNDCAK
; Normal Return
;Changes T1,T2,T3,T4
SNDCAK: CALL MAKHDR ;INITIALIZE DNPxBY FOR THE NSP HEADER
;MSGFLG
MOVX T1,MGFCAK ;ITS A CONNECT ACK MESSAGE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;GET THE REMOTE LINK ADDRESS
CALL DNP2BY
MOVX T1, ST%NRS ! ST%NAK ! ST%NRQR ! ST%TRL
CALLRET SNDRTR ;NO RETURN TO SESSION CONTROL
;NO ACK REQUIRED
;NO RETURN REQUESTED FROM ROUTER
;TROLL ALLOWED
SUBTTL SNDDSC - Send a Disconnect Complete Message
;SNDDSC - Send a Disconnect Complete Message
;
;Call: EL/ The Port Block
; MB/ The Message Block
; CALL SNDDSC
; Normal Return
;Changes T1,T2,T3,T4
SNDDSC: CALL MAKHDR ;INITIALIZE DNPxBY FOR THE NSP HEADER
;MSGFLG
MOVX T1,MGFDC ;ITS A DISCONNECT INITIATE MESSAGE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;GET THE REMOTE LINK ADDRESS
CALL DNP2BY
;SLA
LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS
CALL DNP2BY
;REASON
MOVX T1,RSNDSC ;DISCONNECT COMPLETE REASON
CALL DNP2BY
MOVX T1, ST%NRS ! ST%NAK ! ST%NRQR ! ST%TRL
CALLRET SNDRTR ;NO RETURN TO SESSION CONTROL
;NO ACK REQUIRED
;NO RETURN REQUESTED FROM ROUTER
;TROLL ALLOWED
SUBTTL MGACKD - A Message has been ACKed
;MGACKD - Return or destroy an ACKed message
;
;Call: T1/ Flags, see below
; EL/ The Port Block
; ES/ The Sublink Block
; MB/ The Message Block
; CALL MGACKD
; Normal Return
;Changes T1,T2,T3,T4
MA%DONE== 1B35 ;SEND WAS DONE
MA%NDONE==0B35 ;SEND WAS NOT DONE
;The message has already been unlinked from any queue it may have been
;on. Here we decide whether to send the message back to Session
;Control or to free the message block.
MGACKD: TMNN NMRET,(MB) ;SESSION CONTROL WANT THE MESSAGE BACK?
JRST FREMSG ;NO, JUST DEALLOCATE IT AND RETURN
;T2 IS IGNORED ON THIS CALL
MOVEI T3,SV.ODN ;ASSUME OUTPUT WAS DONE
TXNN T1,MA%DONE ;WAS IT?
MOVEI T3,SV.OND ;NO, OUTPUT NOT DONE
MOVE T4,MB ;SESSION CONTROL WANTS MB IN T4
LOAD T1,ELSCB,(EL) ;SESSION CONTROL'S SCB ID FOR PORT
OPSTR <CALLRET @>,ELSCV,(EL) ;TELL SESSION CONTROL AND RETURN
SUBTTL SENDNL - Send a No Link Message
;SENDNL - Send a No Link Message
;
;Call: MB/ Message Block to use, may have IN.MSD pointing to garbage
; The DLA and SLA should be filled into the message block
; CALL SENDNL
; Normal Return
;Changes T1,T2,T3,T4
;This routine is called by input routines when they discover that the
;DLA to which the message was bound does not exist. In this case,
;there will probably be message text hanging off the IN.MSD message
;descriptor. Before we use the message block to send the No Link
;message, we deallocate the unwanted input text. We do not deallocate
;the entire message block and then allocate another, because there is
;a possibility that we might not get a message block again, and the
;code here is not prepared for that.
SENDNL: MOVEI T1,RSNNLK ;"NO LINK" REASON CODE
CALLRET SENDC0 ;GO SEND A DISCONNECT CONFIRM
; WITH A ZERO-LENGTH DATA-CTL FIELD
;SENDNR - Send a No Resources message, just like a No Link, really
;
;Call: MB/ The Message Block
; The DLA and SLA should be filled into the message block
; CALL SENDNR
; Normal Return
;Changes T1,T2,T3,T4
SENDNR: MOVEI T1,RSNRES ;"NO RESOURCES" REASON CODE
CALLRET SENDC0 ;GO SEND A DISCONNECT CONFIRM
; WITH A ZERO-LENGTH DATA-CTL FIELD
;SENDC0 - Send a No Link or No Resources Message
;
;Call: T1/ Reason
; MB/ The Message Block
; CALL SENDC0
; Normal Return
;Changes T1,T2,T3,T4
;This routine is called to send No Link and No Resources messages and
;the like. See them for bigger comment.
;We get a reserved port so that SNDRTR will have somewhere to get
;its destination info from. We put the port in DP state so that next
;time SECCHK is run it will destroy the port. A DC message is not
;ACKed, so there is no need for the port block after this one send.
SENDC0: SAVEAC <P1,EL,ES>
MOVE P1,T1 ;SAVE THE REASON CODE
LOAD T1,NMLLA,(MB) ;LOAD UP THE LOCAL LINK ADDRESS
LOAD T2,NMRLA,(MB) ; AND THE REMOTE LINK ADDRESS
HRL T1,T2 ;SET UP RLA,,LLA IN T1 FOR GTRESP
LOAD T2,MBSRC,(MB) ;GET THE SOURCE NODE ADDRESS
CALL GTRESP ;GET A RESERVED PORT TO SEND WITH
JRST FREMSG ;CAN'T, JUST IGNORE THE MESSAGE
XMOVEI ES,EL.NSL(EL) ; (IT WILL COME AGAIN LATER)
NEWSTATE DP ;TELL SECCHK TO DESTROY PORT NEXT SECOND
MOVE T1,MB ;BLOCK TO BE CLEARED
MOVEI T2,0 ;BYTES OF USER DATA TO GET
CALL DNMINI ;INITIALIZE THE MSG BLK
BUG.(CHK,LLISIF,LLINKS,SOFT,<SENDC0's DNMINI failed>,,<
Cause: This BUG is not documented yet.
>,RTN)
CALL MAKHDR ;PREPARE AN NSP HEADER IN MESSAGE BLK
;MSGFLG
MOVEI T1,MGFDC ;DISCONNECT CONFIRM MESSAGE TYPE/SUBTYPE
CALL WRTMGF ;WRITE THE MSGFLG FIELD
;DLA
LOAD T1,ELRLA,(EL) ;LOAD UP THE OLD LOCAL LINK ADDRESS
CALL DNP2BY
;SLA
LOAD T1,ELLLA,(EL) ;LOAD UP THE NEW LOCAL LINK ADDRESS
CALL DNP2BY
;REASON
MOVE T1,P1 ;THE REASON CODE PASSED BY CALLER
CALL DNP2BY ;TAKES 2 BYTES FOR THAT??
MOVX T1,ST%NRS ! ST%NRQR ! ST%NAK ! ST%TRL
CALLRET SNDRTR ;NO RETURN TO SESSION CONTROL
;NO RETURN REQUESTED ON XMISSION FAILURE
;NO ACK EXPECTED
;TROLL ALLOWED
SUBTTL PTIRUN - Put a Port in Run State
;PTIRUN - Put a port from CC, CI or CD state to RUN state
;
;Call: EL/ The Port Block
; CALL PTIRUN
; Normal Return
;Changes T1,T2,T3,T4
PTIRUN: LOAD T2,ELSTA,(EL)
IFNSTATE T2,<CC>,PTIRN0 ;IF WE WERE IN CC STATE
SAVEAC ES
XMOVEI ES,EL.NSL(EL) ;CONNECT CONFIRM MSG IN ON 'NORMAL' SUBLINK
MOVX T1,<FLD(AK$QAK,AKQAL)> ;BUILD ACK FOR NORMAL DATA MSG ZERO
CALL PRCACK ;ACK THE CONNECT MESSAGE WE SENT
LOAD T2,ELSTA,(EL) ;RELOAD T2 WITH PORT STATE
PTIRN0: IFNSTATE T2,<CI>,PTIRN1 ;IF WE WERE IN CI STATE
LOAD T1,ELDIM,(EL) ;IS THERE A CONNECT INIT MESSAGE THERE?
JUMPE T1,PTIRN1 ;JUMP IF NOT
CALL DNFMSG ;YES, FREE IT
SETZRO ELDIM,(EL) ; AND FORGET IT
LOAD T2,ELSTA,(EL) ;RELOAD T2 WITH PORT STATE
PTIRN1: IFSTATE T2,<CD>,PTIRN2 ;IF WE MISSED THE CONNECT ACK
MOVEI T1,0 ;CI MSG ALWAYS GOES OUT AS MSG 0
CALL UPDELAY ;INITIALIZE THE ROUND-TRIP DELAY
;Note that the call to UPDELAY must come before we change the
;state, since UPDELAY needs to know the old state to know how
;to handle the delay time.
PTIRN2: NEWSTATE RN ;PUT THE PORT IN "RUN" STATE
CALL DNGTIM ;GET A TIMESTAMP
STOR T1,ELTMA,(EL) ;START THE INACTIVITY TIMER
CALLRET NSPRJF ;AND REQUEST JIFFY SERVICE TO SEND DRQ'S
SUBTTL MAKPRT - Make a New Port Block
;MAKPRT - Make a New Port Block and Initialize It
;
;Call: T1/ RLA,,LLA
; T2/ Node Id
; CALL MAKPRT
; Error Return if no resources for block(s)
; Normal Return with pointer to new block in EL
;Changes T1,T2,T3,T4,EL,ES
MAKPRT: SAVEAC <P1,P2> ;P1/ ADDRESS, P2/ NODEID
DMOVEM T1,P1 ;SAVE ADDRESSES IN P1 & P2
MOVEI T1,EL.LEN ;LENGTH OF A PORT BLOCK
CALL DNGWDZ ;GET A ZEROED BLOCK AT LEAST THAT LONG
RET ;ERROR RETURN IF FAILED
MOVE EL,T1 ;POINT TO THE NEW PORT BLOCK
STOR P1,ELLLA,(EL) ;STORE THE LLA (RH OF P1)
HLRZ T1,P1 ;GET THE RLA
STOR T1,ELRLA,(EL)
STOR P2,ELNNM,(EL) ;STORE REMOTE'S NODE NUMBER FOR SNDRTR
MOVE T1,P2 ;NODEID FROM ARGS
CALL FNDNOD ;LOOK UP OR BUILD NSP NODE BLOCK
JRST MAKPTE ;DEALLOCATE PORT BLOCK, RETURN
STOR T1,ELNDB,(EL) ;SAVE PTR TO NSP NODE BLOCK
CALL SETPRT ;SETUP THE NEW PORT BLOCK
JRST MAKPTE ;FAILED, DEALLOCATE THE PORT BLOCK
XMOVEI T1,SCTL ;DEFAULT SESSION CONRTOL ADDRESS TO SCLINK
STOR T1,ELSCV,(EL) ;MAKPRS WILL CHANGE FOR 'RESERVED PORTS'
; NSPCLS CAN NEED THIS DEFAULT ADDRESS
RETSKP ;SUCCESS
MAKPTE: NEWSTATE DP ;TELL SECCHK TO DESTROY PORT BLOCK
RET
SUBTTL GTRESP - Get a Reserved Port
;GTRESP - Get a Reserved Port off the Reserved Port Queue
;
;Call: T1/ RLA,,LLA
; T2/ Node Id
; CALL GTRESP
; Error Return if no resources for block(s)
; Normal Return with pointer to new block in EL
;Changes T1,T2,T3,T4,EL,ES
GTRESP: SAVEAC <P1,P2> ;P1/ ADDRESS, P2/ NODEID
DMOVEM T1,P1 ;SAVE ADDRESSES IN P1 & P2
MOVEI T1,EL.LEN ;LENGTH OF A PORT BLOCK
CALL DNGWDZ ;GET A ZEROED BLOCK AT LEAST THAT LONG
JRST GTRES1 ;FAILED, TRY THE RESERVED PORT Q
MOVE EL,T1 ;POINT TO THE NEW PORT BLOCK
JRST GTRES2 ;BACK TO COMMON CODE
GTRES1:
DEQUE EL,NSPRPQ,EL.APQ,RTN ;RTN IF Q IS EMPTY
MOVE T1,EL ;ADDRESS OF BLOCK TO BE ZEROED
XMOVEI T2,EL.LEN-1(EL) ;END OF BLOCK
MOVEI T3,0 ;VALUE TO SMEAR INTO BLOCK
CALL DNSWDS ;USE BLT OR XBLT AS APPROPRIATE
GTRES2: STOR P1,ELLLA,(EL) ;STORE THE LLA (RH OF P1)
HLRZ T1,P1 ;GET THE RLA
STOR T1,ELRLA,(EL)
STOR P2,ELNNM,(EL) ;STORE REMOTE'S NODE NUMBER FOR SNDRTR
MOVE T1,NSPRND ;POINT TO THE "RESERVED" NODE
STOR T1,ELNDB,(EL) ;SAVE PTR TO NSP NODE BLOCK
CALL SETPRT ;SETUP THE NEW PORT BLOCK
JRST GTRESE ;FAILED, DEALLOCATE THE PORT BLOCK
CALL MAKPRS ;MAKE THIS PORT A RESERVED PORT
JRST GTRESE ;FAILED, DEALLOCATE THE PORT BLOCK
RETSKP ;SUCCESS
GTRESE: NEWSTATE DP ;TELL SECCHK TO DESTROY PORT BLOCK
RET
SUBTTL MAKPRS - Make this Port Reserved
;MAKPRS - Change this port so that it is a reserved port
;
;Call: EL/ The Port Block
; CALL MAKPRS
; Error Return, if BUG given
; Normal Return
;Changes T1,T2,T3,T4,EL,ES
MAKPRS: XMOVEI T1,RESPRC ;POINTER TO THE RESERVED PROCESS CODE
STOR T1,ELSCV,(EL) ;MAKE THIS THE SESSION CONTROL ENTRY
CALL GETPID ;GET THE NSPpid FROM EL
STOR T1,ELSCB,(EL) ;MAKE "SESSION CONTROL" PTR SAME AS NSP
; SO RESPRC KNOWS WHO TO TALK TO
RETSKP ;SUCCESS RETURN
SUBTTL SETPRT - Make a New Port Block
;SETPRT - Setup a new port block or a newly assigned reserved port
;
;Call: EL/ The Port Block, with LLA, RLA (or zero) and ELNNM filled in
; CALL SETPRT
; Error Return if no resources for block(s)
; Normal Return with pointer to new block in EL
;Changes T1,T2,T3,T4,EL,ES
SETPRT: CALL ADDHSH ;PUT NEW PORT BLOCK IN HASH TABLE
RET ;CAN'T, NO RESOURCES
;No more errors, now its safe to deal with new Port Block
SETONE ELCNF,(EL) ;WE HAVE CONFIDENCE, SO FAR
LOAD T1,ELNDB,(EL) ;GET POINTER TO THE NSP NODE BLOCK
OPSTRM <AOS T2,>,NNLKC,(T1) ;ONE MORE ACTIVE LINK FOR THIS NODE
OPSTR <CAMLE T2,>,NNLKM,(T1) ;THIS A NEW MAX?
STOR T2,NNLKM,(T1) ;YES, STORE NEW MAX
ENDQUE EL,NSPAPQ,EL.APQ,T1
;Fill in some static fields in the "other" sublink
SETONE ESOTH,+EL.OSL(EL) ;SET "OTHER" FLAG IN THE "OTHER" ES
;ALREADY CLEAR IN "NORMAL" SUBLINK BLK
MOVX T1,FCM.MG ;ALWAYS MESSAGE FLOW CONTROL MODE
STOR T1,ESRFL,+EL.OSL(EL) ; IN BOTH DIRECTIONS
STOR T1,ESXFL,+EL.OSL(EL)
MOVEI T1,1 ;ONE FREE XMIT DRQ IN BOTH DIRECTIONS
STOR T1,ESXLD,+EL.OSL(EL) ;DON'T TELL SESSION CONTROL ABOUT IT,
STOR T1,ESRLD,+EL.OSL(EL) ; SINCE S/HE ALREADY ASSUMES IT
STOR T1,ESXRD,+EL.OSL(EL) ;ARCHITECTURE GRANTS A FREE DRQ
STOR T1,ESRRD,+EL.OSL(EL) ; TO OSL IN BOTH DIRECTIONS
STOR T1,ESGOL,+EL.OSL(EL) ;SET THE DATA REQUEST GOAL
STOR T1,ESCGL,+EL.OSL(EL) ;NEVER USED FOR "OTHER" BUT...
;Initialize some dynamic fields which should not start out zero
MOVX T1,FCM.XX ;GET THE ILLEGAL FLOW CONTROL MODE
STOR T1,ESRFL,+EL.NSL(EL) ;ASSURE WE INIT BEFORE WE USE
STOR T1,ESXFL,+EL.NSL(EL) ; THESE VARIABLES
STOR EL,ELCHK,(EL) ;STORE THE CHECK FOR FNDPID
RETSKP ;SUCCESS RETURN
SUBTTL FNDPRT - Hash an LLA to Find a Port Block
;FNDPRT - Given an LLA and RLA, return a port block pointer in EL
;
;Call: T1/ The LLA to look for
; T2/ The RLA to check for consistency, zero if no RLA yet
; CALL FNDPRT
; Error Return if not found or in CL or DP states
; Normal Return, EL holds the address of the associated port block.
; T1 holds the state of the port, callers count on it
;Changes T1,T2,T3,T4
FNDPRT: DMOVE T3,T1 ;SAVE LLA AND RLA IN T3 & T4
IDIV T1,NSPHTS ;DIVIDE BY HASH TABLE SIZE
ADD T2,NSPHTB ;ADD REMAINDER TO HASH TABLE ADDRESS
SKIPN EL,(T2) ;GET POINTER TO FIRST PORT IN BUCKET
RET ;NO MATCH IF BUCKET WAS EMPTY
FNDPT1: OPSTR <CAMN T3,>,ELLLA,(EL) ;IS THIS OUR LOCAL LINK ADDR?
JRST FNDPT3 ;YES, GO CHECK REMOTE LINK ADDR
FNDPT2: LOAD EL,ELHBQ,(EL) ;POINT TO NEXT PORT IN BUCKET
JUMPN EL,FNDPT1 ;AND CHECK IT IF IT EXISTS
RET ;END OF LIST, NO MATCH RETURN
;Here when we have matched the Local Link Address
FNDPT3: JUMPE T4,FNDPT4 ;SUCCESS IF WE DON'T KNOW RLA
LOAD T1,ELRLA,(EL) ;CHECK RLA TOO
JUMPE T1,FNDPT4 ;SUCCESS IF WE DON'T KNOW RLA
CAMN T4,T1 ;RLA MATCH?
JRST FNDPT4 ;WE FOUND IT!
ETRACE NSP,LLAs match but RLAs do not
JRST FNDPT2 ;NOT IT, TRY NEXT
;Here when we have found a port which matches LLA and RLA
FNDPT4: LOAD T1,ELSTA,(EL) ;GET ITS PORT STATE
IFNSTATE T1,<CL,DP>,RSKP ;JUMP IF SUCCESS, POINTER IN EL, STATE IN T1
RET ;CAN'T CALL <CL,DP> STATE A REAL PORT
SUBTTL Hash Table Routines
;INIHSH - Initialize the hash table, called from NSPINI
;
;Call: CALL INIHSH
; Normal Return
;
;Changes T1,T2,T3,T4
INIHSH: SKIPG T1,NSPHTS ;GET THE HASH TABLE SIZE
BUG.(HLT,LLIHTS,LLINKS,SOFT,<NSPHTS not set up>,,<
Cause: This BUG is not documented yet.
>,RTN)
CALL DNGWDZ ;GET A HASH TABLE
BUG.(HLT,LLIHTG,LLINKS,SOFT,<INIHSH cant get a hash table>,,<
Cause: This BUG is not documented yet.
>,RTN)
MOVEM T1,NSPHTB ;STORE ADDRESS OF TABLE
RET ;DNGWDZ ZEROS WORDS DELIVERED
;ADDHSH - Add a port block to the hash table
;
;Call: EL/ Pointer to a port block with LLA and RLA filled in
; CALL ADDHSH
; Error Return if can't get core for new hash table
; Normal Return
;
;Changes T1,T2
ADDHSH: LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS
IDIV T1,NSPHTS ;DIVIDE BY HASH TABLE SIZE
ADD T2,NSPHTB ;ADD REMAINDER TO TABLE ADDRESS
MOVE T1,(T2) ;GET PTR TO CURRENT OCCUPANT
STOR T1,ELHBQ,(EL) ;WE ARE TO BE FIRST IN THIS QUEUE
MOVEM EL,(T2) ;POINT BUCKET TO THIS PORT BLOCK
;Now calc the length of this hash bucket's list and keep the
;max length up to date.
MOVE T1,EL ;START WITH PORT AFTER NEW ADDITION
MOVEI T2,1 ;WE KNOW WE HAVE AT LEAST ONE
ADDHS1: OPSTR <SKIPE T1,>,ELHBQ,(T1) ;GET PTR TO NEXT PORT IN BUCKET
AOJA T2,ADDHS1 ;TRY FOR MORE
CAMLE T2,NSPHMX ;BIGGER THAN CURRENT MAX?
MOVEM T2,NSPHMX ;YES, A NEW MAXIMUM
RETSKP ;ALWAYS SUCCEED UNTIL WE LEARN
; TO GROW A HASH TABLE (IF EVER)
;RMVHSH - Remove a port block from the hash table
;
;Call: EL/ Pointer to a port block with LLA filled in
; CALL RMVHSH
; Normal Return, even if the port was not found, so what?
;
;Changes T1,T2
IF2,<IFN ELHBQ+1,<PRINTX ?ELHBQ must be a full word structure>>
RMVHSH: LOAD T1,ELLLA,(EL) ;GET THE LOCAL LINK ADDRESS
IDIV T1,NSPHTS ;DIVIDE LLA BY HASH TABLE SIZE
ADD T2,NSPHTB ;SET UP FIRST PREVIOUS POINTER
SUBI T2,EL.HBQ ;PREPARE FOR EL.HBQ OFFSET LATER
RMVHS1: MOVE T1,T2 ;REMEMBER THE PREVIOUS PTR
LOAD T2,ELHBQ,(T2) ;LOAD PTR TO NEXT PORT IN BUCKET
JUMPE T2,RMVHS2 ;CHECK OUT THAT PORT IF ITS THERE
CAME T2,EL ;IS THIS THE PORT?
JRST RMVHS1 ;NO, TRY NEXT ON LIST
LOAD T2,ELHBQ,(EL) ;GET OUR PORT'S NEXT PTR
STOR T2,ELHBQ,(T1) ;MAKE PREVIOUS BLOCK PT TO NEXT
RET ;OUR PORT IS NOW OUT OF LIST
;Here if we did not find the port in its hash bucket
RMVHS2: BUG.(CHK,LLIRMH,LLINKS,SOFT,<RMVHSH didnt find port>,,<
Cause: This BUG is not documented yet.
>,RTN)
SUBTTL NEWLLA - Make a New Local Link Address
;NEWLLA - Make a new local link address
;
;Call: CALL NEWLLA
; Normal Return with LLA in T1
;Changes T1,T2
;The main reason this is a subroutine rather than just in-line code is
;so that we can change the method of allocating local link addresses
;if we need to.
NEWLLA: SAVEAC EL
NEWLL1: AOS T1,NSPNLA ;INCREMENT THE "NEXT LLA"
ANDI T1,177777 ;MOD 2**16
CAIN T1,0 ;IF NEW LLA WOULD BE ZERO
MOVEI T1,1 ; MAKE IT ONE INSTEAD
MOVEM T1,NSPNLA ;RESTORE THE CYCLED NUMBER
CALL FNDPRT ;THIS ONE ALREADY IN USE?
TRNA ;NO, ALL IS OK
JRST NEWLL1 ;YES, BETTER TRY NEXT INSTEAD
MOVE T1,NSPNLA ;RETURN NEW LLA TO CALLER IN T1
RET
SUBTTL NEWSGN - Make a New Message Segment Number
;NEWSGN - Make a new message segment number
;
;Call: ES/ The Sublink Block on which to send the message
; MB/ The Message Block to receive the new Segment number
; CALL NEWSGN
; Normal Return with new segment number in T1
; and in sublink and message blocks
;Changes T1
;The main reason this is a subroutine rather than just in-line code is
;so that we can change the method of allocating message segment numbers
;if we need to.
NEWSGN: LOAD T1,ESLMA,(ES) ;GET LAST MESSAGE NUMBER ASSIGNED
AOS T1 ;NEXT HIGHER SEGMENT NUMBER
ANDI T1,MASK.(WID(ESLMA),35) ;RESULT IN T1 MUST BE CYCLED TOO
STOR T1,ESLMA,(ES) ;STORE NEW 'LAST MSG NUMBER ASSIGNED'
STOR T1,NMSGN,(MB) ;STORE SEG NUMBER IN MSG BLK
RET ;ONLY RETURN
SUBTTL NSPpid Control - GETPID & FNDPID
;The only reason these procedures exist is to provide a single place
;where nature of the NSPpid can be changed. For example, we may want
;to change the NSPpid from a direct memory pointer to an LLA such as
;we send across the network. This would be safer and more expensive.
;At present, I don't think we have to be all that safe with Session
;Control.
;GETPID - Returns the NSPpid that we send to Session Control.
;
;Call: EL/ Pointer to the port block in question
; CALL GETPID
; Normal Return with NSPpid in T1
;Changes T1
GETPID: MOVE T1,EL
RET
;FNDPID - Returns a pointer to the port block corresponding to an NSPpid
;
;Call: T1/ The NSPpid
; CALL FNDPID
; Error Return if NSPpid not found, BUG given
; Normal Return with port block pointer in EL
;Changes T1
FNDPID: SKIPG EL,T1 ;PTR MUST BE POSITIVE NON-ZERO
JRST FNSPD1 ;NOPE
LOAD T1,ELSTA,(EL) ;GET PORT'S STATE
IFSTATE T1,<CL,DP>,FNSPD1 ;WE DON'T ADMIT TO CLOSED PORTS
OPSTR <CAME EL,>,ELCHK,(EL) ;IS THE CHECK ADDRESS THERE?
FNSPD1: BUG.(CHK,LLIFNS,LLINKS,SOFT,<SCTL passed bad NSPpid>,,<
Cause: This BUG is not documented yet.
>,RTN)
RETSKP ;OK RETURN
SUBTTL DSTPRT - Destroy a Port
;DSTPRT - Called only from SECCHK or its children
;
;Call: EL/ The Port Block
; CALL DSTPRT
; Normal Return
;Changes T1,T2,T3,T4
;Removing a port from NSPAPQ (the all ports queue) decrements the
;count of all active ports, which is held in the NSPAPQ queue header.
;We don't zero the block here so that, for debugging, it will still be
;around after it has been freed until the memory allocation routine
;actually gives it away again.
DSTPRT: LOAD T1,ELSTA,(EL) ;GET PORT'S STATE
IFNSTATE T1,<DP> ;IS IT IN <DP> STATE?
BUG.(CHK,LLIDDP,LLINKS,SOFT,<Tried to destroy non-DP port>,,<
Cause: This BUG is not documented yet.
>,RTN)
TRACE NSP,Port Destroyed
LOAD T1,ELNDB,(EL) ;GET POINTER TO NSP NODE BLOCK
DECR NNLKC,(T1) ;DECREMENT LINK COUNT FOR THIS NODE
RMVQUE EL,NSPAPQ,EL.APQ,T1 ;REMOVE EL FROM NSP ALL PORTS Q
RMVQUE EL,NSPJFQ,EL.JFQ,T1 ;REMOVE EL FROM JIFFY-SERVICE Q
CALL RMVHSH ;REMOVE PORT IN (EL) FROM HASH TABLE
SETZRO ELCHK,(EL) ;DESTROY THE CHECK WORD FOR FNDPID
MOVE T1,EL ;ADDRESS OF BLOCK TO FREE
MOVEI T2,EL.LEN ;LENGTH OF A PORT BLOCK IN WORDS
LOAD T4,QHCNT,+NSPRPQ ;GET CURRENT LENGTH OF RESERVED PORT Q
CAML T4,NSPRPN ;LESS THAN TARGET RESERVED PORT NUMBER?
JRST DNFWDS ;YES, FREE THE WORDS OF THIS BLOCK
ENDQUE EL,NSPRPQ,EL.APQ,T1 ;NO, PUT BACK ON THE RES PORT QUEUE
RET
SUBTTL QSRTMB - Queue a Message on a Sorted Queue
;QSRTMB - Queue a message on a sorted queue
;
;Call: T1/ Points to the queue header, see BEGSTR QH
; MB/ The message block pointer, as usual
; CALL QSRTMB
; Error Return if message is duplicate, message is queued
; Normal Return
;Changes T2,T3,T4
;Note that this routine only queues message blocks, no other block
;types need apply.
;
;Make the empty queue case fast, it will be the most common.
;
;The CMODxx macros compare an AC with a structure instance, but they
;compare MOD the size of the byte. See macro definitions for an
;example.
QSRTMB: SETZRO MBNXT,(MB) ;ASSUME THIS MSG WILL BE END OF QUEUE
LOAD T4,QHBEG,(T1) ;GET POINTER TO BEGINNING OF QUEUE
JUMPN T4,QSRTM1 ;JUMP IF QUEUE IS NOT EMPTY
;The simple case: the queue was empty
STOR MB,QHBEG,(T1) ;MAKE THIS MESSAGE FIRST ON QUEUE
STOR MB,QHEND,(T1) ; AND ALSO THE LAST ON THE QUEUE
;Here on success to increment queue counter
QSRTMS: INCRQH <(T1)>,T2 ;Q HEADER TO INCREMENT, TEMPAC FOR MACRO
RETSKP ;FINISH UP AND LEAVE
;The sorting cases: the queue was not empty
QSRTM1: LOAD T2,QHEND,(T1) ;MOST LIKELY IT WILL FIT AT END
LOAD T3,NMSGN,(MB) ;GET OUR NEW MSG'S SEGMENT NUMBER
CMODG T3,NMSGN,(T2) ;GET # OF MSG AT END OF QUEUE
JRST QSRTM2 ;NO, NEW MSG FITS BACK IN QUEUE
;Here if the new message goes on the end of the queue.
STOR MB,MBNXT,(T2) ;POINT OLD LAST TO NEW LAST MSG
STOR MB,QHEND,(T1) ;POINT EO-QUEUE TO NEW LAST MSG
JRST QSRTMS ;FINISH UP AND LEAVE
QSRTM2: CMODLE T3,NMSGN,(T4) ;IS NEW MSG LOWER THAN FIRST?
JRST QSRTM3 ;NOPE, NOT AT THE BEGINNING
;Here if message is to be first on queue
CMODN T3,NMSGN,(T4) ;YES, IS THIS A DUPLICATE?
RET ;YES, ERROR RETURN
STOR T4,MBNXT,(MB) ;NO, PT NEW FIRST TO OLD FIRST
STOR MB,QHBEG,(T1) ;POINT QUEUE HEADER AT NEW FIRST
JRST QSRTMS ;FINISH UP AND LEAVE
QSRTM3: ;Here if the new message goes somewhere in the middle.
;This should be simple rather than fast, since it won't be
;done very often.
QSRTM4: LOAD T2,MBNXT,(T4) ;POINT TO NEXT MSG ON QUEUE
CMODG T3,NMSGN,(T2) ;COMPARE WITH NEXT MSG ON QUEUE
JRST QSRTM5 ;GOT THE SLOT, GO INSERT IT
MOVE T4,T2 ;SET UP NEW PREVIOUS POINTER
JRST QSRTM4 ;GO TRY NEXT MSG
QSRTM5: CMODN T3,NMSGN,(T2) ;IS THIS A DUPLICATE MSG NUMBER?
RET ;YES, ERROR RETURN
STOR T2,MBNXT,(MB) ;NO, STORE FORWARD PTR IN NEW MSG
STOR MB,MBNXT,(T4) ;STORE FORWARD PTR IN PREV MSG
JRST QSRTMS ;FINISH UP AND LEAVE
SUBTTL MAKHDR - Initialize the NSP MSD to build a Message Header
;MAKHDR - Initialize the NSP MSD to build a Message Header
;
;Call: CALL MAKHDR
; Normal Return with MS set up for DNPxBY routines
;Changes T1,T2
;This routine initializes the NSP Message Segment Descriptor to accept
;an NSP message header. It sets up MS for the DNPxBY routines that
;store bytes into DECnet-36 messages.
MAKHDR: XMOVEI T1,NM.MSD(MB) ;GET POINTER TO THE MSD TO INITIALIZE
CALLRET DNPINI ;INITIALIZE MS FOR DNPxBY
;INIHDR - Initialize the input MSD for DNGxBY
;
;Call: CALL INIHDR
; Normal Return with MS set up
;Changes T1,T2,T3,T4
INIHDR: MOVE T1,MB ;POINT TO THE RELEVANT MESSAGE BLOCK
CALLRET DNGINI ;INITIALIZE MS FOR DNGXBY
SUBTTL UPDELAY - Update a Logical Link's Roundtrip Delay
;UPDELAY - Update a Logical Link's Roundtrip Delay
;
;Call: T1/ "NORMAL" Message segment number just ACKed
; EL/ The Port Block
; ES/ The Sublink Block
; CALL UPDELAY
; Normal Return
;Changes T1,T2
;Procedure UPDELAY is called whenever we get an ACK for a "normal"
;message.
;SNDRTR fills in ELDTM and ELDSG when it sends a message and the timer
;is not running.
;Note that the message timer is not reset when we resend a message, so
;resends are included in the average round-trip time. If this were
;not the case, we could receive an ACK immediately after resending a
;message and our resend time would then be latched into an
;unrealistically small value: once its too small the likelyhood of
;this same phenomenon increases greatly.
;
;We put a floor under the expected delay, because if the delay gets too
;small the delay factor becomes too small to cover normal delays
;in the network, thus leading to spurious retransmissions and
;no confidence conditions.
;
;we put a roof over the expected delay, because the delay can increase
;drastically if we are re-transmitting a lot. The specific case of a
;fast line leading to a slow line will cause the middle node to drop
;many messages, and will cause the delay timer to increase beyond all
;reason.
;
; delta_t := {system uptime} - start_time;
; n.NN_DELAY := n.NN_DELAY + ((delta_t - n.NN_DELAY) / NSPWGT);
UPDELAY:TMNN ELDTM,(EL) ;IS THERE A TIMED MESSAGE?
RET ;NO TIMER GOING, LEAVE NOW
LOAD T2,ESOTH,(ES) ;WHICH SUBLINK IS THIS ACK ON?
LOAD T3,ELDTO,(EL) ;WHICH SUBLINK IS TIMED MSG ON?
CAME T2,T3 ;IS THIS ACK ON THE TIMED SUBLINK?
RET ;NO
CMODGE T1,ELDSG,(EL) ;DOES THIS ACK THE TIMED MESSAGE?
RET ;NOT YET
CALL DNGTIM ;GET THE CURRENT TIME IN T1
OPSTR <SUB T1,>,ELDTM,(EL) ;GET THIS MESSAGE'S ROUND-TRIP TIME
SETZRO ELDTM,(EL) ;WE'RE FINISHED WITH THIS TIMER
LOAD T4,ELNDB,(EL) ;GET POINTER TO RELEVANT NODE BLOCK
MOVX T2,NNGDL ;HAVE WE
TDNN T2,NN.GDL(T4) ; "GOT DELAY" YET?
JRST UPDLY2 ;NO, USE THIS TIME UNDIMINISHED
OPSTR <SUB T1,>,NNDLY,(T4) ;GET DIFFERENCE FROM OLD ESTIMATE
IDIV T1,NSPWGT ;DIMINISH BY WEIGHTING FACTOR
OPSTR <ADD T1,>,NNDLY,(T4) ;GET UPDATED DELAY
UPDLY1: CAMGE T1,NSPFLR ;RESULT LOWER THAN THE FLOOR?
MOVE T1,NSPFLR ;YES, LOAD UP THE FLOOR INSTEAD
CAMLE T1,NSPRUF ;RESULT GREATER THAN THE ROOF?
MOVE T1,NSPRUF ;YES, LOAD UP THE ROOF INSTEAD
STOR T1,NNDLY,(T4) ;THAT'S THE NEW DELAY ESTIMATE
RET
;Here if this is the first time for this remote node
UPDLY2: IORM T2,NN.GDL(T4) ;WE'VE "GOT DELAY" NOW, THOUGH
JRST UPDLY1 ;GO STORE UNDIMINISHED VALUE
SUBTTL Free a Message Block
;FREMSG - Free a message block, pointer in MB
;
;Call: MB/ Pointer to message block
; CALL FREMSG
; Normal Return, message block deallocated
;Changes T1,T2,T3,T4
FREMSG: SKIPG T1,MB ;PREPARE FOR DNFMSG
BUG.(CHK,LLIFZM,LLINKS,SOFT,<Tried to free zero msg>,,<
Cause: This BUG is not documented yet.
>,RTN)
TRASH MB, ;JUST BE CAREFULL
CALLRET DNFMSG ;FREE THE MESSAGE BLOCK
SUBTTL Reserved Port Management
;RPNINI - Collect some ports for the reserved port list
;
;Call: CALL RPNINI
; Normal Return
;
;Changes T1,T2,T3
;Called only from NSPINI at system startup time.
RPNINI: SAVEAC P1
SKIPG P1,NSPRPN ;NUMBER OF RESERVED PORTS TO KEEP
RETSKP ;ALL DONE IF NO RESERVED PORTS
RPNIN1: MOVEI T1,EL.LEN ;LENGTH OF A PORT
CALL DNGWDZ ;GET ENOUGH WORDS FOR A PORT
BUG.(CHK,LLINRP,LLINKS,SOFT,<No memory for reserved ports>,,<
Cause: This BUG is not documented yet.
>,RTN)
;Set up the reserved port block here
ENDQUE T1,NSPRPQ,EL.APQ,T2 ;PUT IT ON THE RES PORT QUEUE
SOJG P1,RPNIN1 ;DO ALL THE PORTS REQUESTED
;Set up the reserved node block here. The reserved node
;block is used by the reserved port blocks in case anyone tries to
;access the node block attached to a reserved port. We cannot
;presume to use a real node block, because such a block may not
;exist, and the reason we are using a reserved port is most likely
;that there is no memory left to allocate to new blocks.
MOVEI T1,NN.LEN ;LENGTH OF A NODE BLOCK
CALL DNGWDZ ;GET A NODE BLOCK
BUG.(CHK,LLINNP,LLINKS,SOFT,<No memory for reserved NDB>,,<
Cause: This BUG is not documented yet.
>,RTN)
MOVEM T1,NSPRND ;SAVE FOR GTRESP
RET
SUBTTL Reserved Port Process
;RESPRC - The Reserved Port Process
; Called with the standard SCTL calling sequence
;
;Call: T1/ SCBid = EL (see proc MAKPRS)
; T2/ Function-specific arguments
; T3/ Function Code (offset into SV.xxx table)
; T4/ Full-word pointer to the message block
; CALL RESPRC
; Normal Return
;
;Changes T1,T2,T3
;A pointer to this process is put into a reserved port so that
;calls to Session Control via that port will come here. NSIREJ
;also puts such a pointer into the port whose connect initiate
;is being rejected so that Session Control will not have to hear
;about the port after it has rejected it.
RESPRC: SAVEAC <P1,MB,MS,ES>
MOVE P1,T1 ;ELSCB HAS (EL), SEE MAKPRS
CAIL T3,0 ;IS THE FUNCTION CODE
CAILE T3,RP.MAX ; LEGAL?
BUG.(CHK,LLIRFN,LLINKS,SOFT,<NSP called RESPRC with bad fcn code>,,<
Cause: LLINKS's reserved port handler has been called with an unknown
Session Control function code in T3.
Cure: If there is a new Session Control function code this routine doesn't
know about, add it, else look on the stack to find which LLINKS
routine called Session Control, which the reserved port handler
is impersonating, with a bogus function code.
>,RTN)
SKIPN MB,T4 ;SET UP THE MESSAGE BLOCK POINTER
BUG.(CHK,LLIRMG,LLINKS,SOFT,<NSP called RESPRC without msg blk>,,<
Cause: LLINKS's reserved port handler has been called with no message
block pointer, that is, T4 is zero.
Cure: Look on the stack to find which LLINKS routine called Session Control,
which the reserved port handler is impersonating, with T4/ 0.
>,RTN)
CALLRET @RSPFNT(T3) ;CALL FUNCTION AND RETURN TO NSP
;The jump table by which the reserved port process interprets
;NSP calls.
;The offsets SV.xxx into this table are defined in D36PAR.
DEFINE RP(nam),<
IFN .-RSPFNT-SV.'nam,< PRINTX ?RSPFNT table (nam) not in same order
PRINTX ? as SV.'nam in D36PAR.UNV
PASS2
END>
IFIW RSP'nam>
;These are the function codes for the calls to Session Control from
;NSP.
RSPFNT: RP CCR ;CONNECT CONFIRMED CALL
RP DIR ;DISCONNECT INITIATE RECEIVED CALL
RP DCR ;DISCONNECT CONFIRM RECEIVED CALL
RP OND ;OUTPUT NOT DONE CALL
RP ODN ;OUTPUT DONE CALL
RP SEG ;SEGMENT RECEIVED CALL
RP DRQ ;DATA REQUEST RECEIVED CALL
RP NCF ;NO CONFIDENCE IN PORT CALL
RP NRS ;NO RESOURCES CALL
RP CLS ;CLOSE COMPLETED CALL
RP NLK ;NO LINK CALL
RP NCM ;NO COMMUNICATION CALL
RP NRN ;NOT IN RUN STATE
RP CAK ;CONNECT ACK CALL
RP.MAX==.-RSPFNT-1 ;THE HIGHEST RECOGNIZED ENTRY OFFSET
;Connect Initiate calls ;The Connect Initiate call does not go
;never go to a reserved ;through the vectored interface, since
;port ;it must go to a single routine
;capable of sorting out which Session
;Control will handle the incoming
;message. the connect initiate call
;goes to the global label SCTLCI.
PURGE RP
RSPCAK: ;CONNECT ACK CALL
RSPNRN: ;NOT IN RUN STATE
RSPCCR: ;CONNECT CONFIRMED CALL
RSPSEG: ;SEGMENT RECEIVED CALL
RSPDRQ: ;DATA REQUEST RECEIVED CALL
RSPCLS: ;CLOSE COMPLETED CALL
RSPOND: ;OUTPUT NOT DONE CALL
RSPODN: ;OUTPUT DONE CALL
RSPDIR: ;DISCONNECT INITIATE RECEIVED CALL
CALLRET FREMSG ;IGNORE THIS CALL
RSPDCR: ;DISCONNECT CONFIRM RECEIVED CALL
RSPNLK: ;NO LINK CALL
RSPNCF: ;NO CONFIDENCE IN PORT CALL
RSPNRS: ;NO RESOURCES CALL
RSPNCM: ;NO COMMUNICATION CALL
TRACE NSP,Reserved Port Process closing port
MOVE T1,P1 ;THE NSPpid SAVED ABOVE, SEE MAKPRS
MOVE T2,P1 ;SCBid IS SAME AS NSPpid FOR RESERVED PORT
MOVX T3,NV.CLS ;CLOSE FUNCTION
MOVE T4,MB ;PASS MSG BLK POINTER IN T4
CALLRET NSP ;TELL NSP TO CLOSE THE PORT
SUBTTL NSPEVT - Queue an Event to Network Management
;NSPEVT - Queue an Event to Network Management
;
;Call:
;IFN FTUSERMODE, T1/ [ASCIZ /text/],,type
;IFE FTUSERMODE, T1/ type
; MB/ The Message Block
; MS/ Pointer to IN.MSD, the input MSD
; CALL NSPEVT ;SKIPPABLE
; Normal Return
;EVENT Macro Preserves T1,T2,T3,T4,T5,T6,P1,P2 ***
;Called only with the EVENT macro, (q.v., above)
;This routine is called by SCLINK as well as LLINKS, so we can't
;count on the NSP interlock in this routine.
EVTDLN==^D11 ;MAX LENGTH OF NSP HEADER DATA
NSPEVT:: ;EVENT MACRO SAVES NECESSARY ACS
SKIPL P2,T1 ;SAVE EVENT CODE
CAILE P2,1 ;WE ONLY KNOW ABOUT 2 TYPES HERE
BUG.(CHK,LLITNE,LLINKS,SOFT,<Unknown Event Type at NSPEVT>,,<
Cause: T1 contains an illegal NSP event type.
Cure: Note that NSPEVT is called by SCLINK as well as LLINKS.
Caller address is on the stack.
>,RTN)
MOVX T1,NE.LEN+<<EVTDLN+3>/4> ;EVENT ARG BLK + SPACE FOR DATA
CALL DNGWDZ ;GET AN ARG BLK FOR NETWORK MGMT
BUG.(CHK,LLIWNE,LLINKS,SOFT,<Can't get event arg blk>,,<
Cause: There isn't any free memory for an event argument block.
Presumably it really ran out, but some may have been lost.
Cure: Either allocate more free memory or accept that some events
will be lost.
>,RTN)
MOVE P1,T1 ;POINTER TO ARG BLOCK
;Fill in the arg block we just got for Network Management
STOR P2,NECTY,(P1) ;THE EVENT CODE TYPE
MOVX T1,.NCNSP
STOR T1,NECCL,(P1) ;THE EVENT CODE CLASS IS "NSP"
MOVX T1,.NTNOD
STOR T1,NEETP,(P1) ;THE ENTITY TYPE IS "NODE"
MOVE T1,RTRADR ;NMX WILL FIGURE OUT NODE NAME
STOR T1,NEEID,(P1) ;ENTITY ID IS MY NODE NUMBER
CALL @[ IFIW NEVT.0 ;TYPE 0 - INVALID MESSAGE HEADER
IFIW NEVT.1](P2) ;TYPE 1 - INVALID FLOW CONTROL
MOVE T1,P1 ;GET PTR TO ARG BLK
CALL NMXEVT ;TELL NMX ABOUT THE EVENT
JFCL ;SO WHAT IF WE LOST THE EVENT
MOVE T1,P1 ;GET BACK POINTER TO BLOCK
CALLRET DNFWDS ;RETURN THE BLOCK OF MEMORY.
;Here for Invalid Message event
NEVT.0: LOAD T1,NMMK1,(MB) ;GET NSP'S MARK 1 FOR THIS INPUT MSG
CALL DNGPOS ;GO TO SAVED POSITION IN INPUT DATA
XMOVEI T1,IN.MSD(MB) ;POINTER TO MSD WHOSE LENGTH TO COUNT
CALL DNSLNG ;GET COUNT OF BYTES IN THIS MSD
CAILE T1,EVTDLN ;LIMIT TO STATUTORY MAXIMUM
MOVX T1,EVTDLN
MOVE P2,T1 ;SAVE BYTE COUNT FOR SOJG BELOW
STOR P2,NEDLN,(P1) ;STORE AS DATA LENGTH (BYTE COUNT)
XMOVEI T1,NE.LEN(P1) ;FULL-WORD ADDR OF STRING DATA
STOR T1,NEDAT,(P1) ;POINTER TO STRING DATA
PUSH P,[POINT 8,NE.LEN(P1)] ;INDEXED BYTE PTR FOR EXTENDED ADDR
NEVT01: CALL DNG1BY ;GET A BYTE FROM RECEIVED MESSAGE
JRST NEVT02 ;ALL DONE (SHOULD NOT HAPPEN)
IDPB T1,(P) ;STORE IN ARG BLK FOR NMX
SOJG P2,NEVT01 ;LOOP FOR ALL BYTES IN COUNT
NEVT02: ADJSP P,-1 ;FORGET BYTE PTR ON STACK
RET
;Here for Invalid Flow Control Event
NEVT.1: MOVEI T1,1 ;ASSOC DATA IS ONLY 1 BYTE LONG HERE
STOR T1,NEDLN,(P1) ;TELL NMX THE DATA LENGTH
XMOVEI T1,NE.LEN(P1) ;FULL-WORD ADDR OF STRING DATA
STOR T1,NEDAT,(P1) ;POINTER TO STRING DATA
LOAD T1,ESRFL,+EL.NSL(EL) ;GET CURRENT RECEIVE FLOW CONTROL
DPB T1,[POINT 8,NE.LEN(P1),7] ;INDEXED PTR FOR EXTENDED ADDR
RET
IFN FTTRACE,<
;EVTTRC - Event Tracer, called only by EVENT macro
; Called from LLINKS and from SCLINK
;
;Call: T1/ Extended Address of caller
; T2/ Pointer to ASCIZ string
; T3/ Event Type
; CALL EVTTRC
; Normal Return
;
;Uses T1,T2,T3,T4
EVTTRC::SAVEAC <P1,P2>
DMOVE P1,T1 ;SAVE T1 AND T2
PUSH P,T3 ;SAVE EVENT TYPE
XMOVEI T1,[ASCIZ /NSP event: /]
CALLTRACE .TSTRG##,ETRNSP
POP P,T1 ;EVENT TYPE
SKIPL T1
CAILE T1,3 ;LEGAL EVENT TYPE?
MOVEI T1,3 ;NO, GET EVENT BUG TYPE
MOVE T1,[[ASCIZ /Illegal message format/]
[ASCIZ /Illegal flow control/]
[ASCIZ /NSP data base changed/]
[ASCIZ /Unknown event type/]](T1)
CALLTRACE .TSTRG##,ETRNSP
XMOVEI T1,[ASCIZ / at PC /]
CALLTRACE .TSTRG##,ETRNSP
MOVE T1,P1 ;CALLER'S TOP LITERAL LEVEL PC
CALLTRACE .TOCTW##,ETRNSP
CALLTRACE .TCRLF##,ETRNSP
SKIPN P2 ;POINTER TO TEXT
RET ;LEAVE NOW IF NO TEXT TO TYPE
CALLTRACE .TTABC##,ETRNSP ;TYPE A TAB
MOVE T1,P2 ;GET PTR TO ASCIZ STRING
CALLTRACE .TSTRG##,ETRNSP
CALLTRACE .TCRLF##,ETRNSP
RET
>;END OF IFE FTTRACE
SUBTTL Trace-to-TTY Facility
IFN FTTRACE,< ;AVAILABLE ONLY IN USER MODE, OF COURSE
;TRCSND/TRCRCV - Trace a message sent or received
;
;Call: T1/ The MSGFLG field of the message
; MB/ Pointer to the Message Block
; CALL TRCSND/TRCRCV
; Normal Return
;Preserves all ACs, except CX
DEFINE TRCSAV,<SAVEAC <T1,T2,T3,T4,T5,T6,P1,P2,MS>>
TRCRCV: MOVE CX,S.TRACE## ;GET THE TRACE FLAGS WORD
TXNN CX,TRCNSP ;ARE WE TRACING NSP?
RET ;NO, SAVE A LITTLE OVERHEAD
TRCSAV ;SAVE THEM ALL JUST TO BE SURE FOR LATER
XMOVEI T2,[ASCIZ \Received\]
CALL TRCMHD ;PUT OUT COMMON TRACE HEADER
CALLTRACE .TRBRK## ;RIGHT SQUARE BRACKET
CALLTRACE .TCRLF##
LOAD T1,NMCNT,(MB) ;NUMBER OF TIMES WE'VE SENT MSG
SOJG T1,RTN ;ONLY REPORT ON FIRST SEND
;HERE WE USE THE 'MARK' COUNT
LOAD T5,MDBYT,(MS) ;NUMBER OF BYTES NOW LEFT TO READ
LOAD T4,NMMK1,(MB) ;BYTES LEFT WHEN MARK WAS TAKEN
SUB T5,T4 ;NEGATIVE THE DIFFERENCE
LOAD T1,MDPTR,(MS) ;BYTE POINTER TO CURRENT POSITION
OPSTR <ADJBP T5,>,MDPTR,(MS) ;BACK UP THE BYTE PTR TO BEG OF HDR
LOAD T6,MDALA,(MS) ;GET ALLOC ADDR FOR INDEX
CALL TRCMGT ;T4,T5 AND T6 ARE ARGS TO TRCMGT
CALLRET TRCMGX
TRCMSG: MOVE CX,S.TRACE## ;GET THE TRACE FLAGS WORD
TXNN CX,TRCNSP ;ARE WE TRACING NSP?
RET ;NO, SAVE A LITTLE OVERHEAD
TRCSAV ;SAVE THEM ALL JUST TO BE SURE FOR LATER
CALL TRCMHD ;PUT OUT COMMON TRACE HEADER
MOVEI T1,[ASCIZ \, # \]
CALLTRACE .TSTRG##
LOAD T1,NMSGN,(MB) ;GET THE MESSAGE NUMBER
CALLTRACE .TDECW## ;TYPE IT IN DECIMAL
CALLTRACE .TRBRK## ;RIGHT SQUARE BRACKET
CALLTRACE .TCRLF##
LOAD T1,NMCNT,(MB) ;NUMBER OF TIMES WE'VE SENT MSG
SOJG T1,RTN ;ONLY REPORT ON FIRST SEND
LOAD T4,MDBYT,+NM.MSD(MB) ;NUMBER OF BYTES INVOLVED
LOAD T5,MDAUX,+NM.MSD(MB) ;BYTE POINTER TO NSP HEADER
LOAD T6,MDALA,+NM.MSD(MB) ;GET ALLOC ADDR FOR INDEX
CALL TRCMGT
CALL TRCMGU
CALLRET TRCMGX
;Here to put out the common trace header
TRCMHD: MOVE P1,T1
PUSH P,T2
PTRACE NSP ;PUT OUT A TRACE PREFIX WITH LLA
POP P,T1 ;RECEIVED, SENT, TROLL ATE, ETC.
CALLTRACE .TSTRG##
XMOVEI T1,[ASCIZ / "Normal"/]
TMNE MBOTH,(MB)
XMOVEI T1,[ASCIZ / "Other"/]
CALLTRACE .TSTRG##
XMOVEI T1,[ASCIZ \ NSP message of type \]
CALLTRACE .TSTRG##
LSH P1,-2 ;THE LOW-ORDER 2 BITS ARE ALWAYS ZERO
CAILE P1,RCVMAX ;IS IT A LEGAL MESSAGE TYPE?
XMOVEI P1,ILLMGT ;NO, FAKE UP AN ERROR MESSAGE
XMOVEI T1,@MGTYNM(P1) ;ASCIZ NAME OF THE MESSAGE TYPE
CALLTRACE .TSTRG##
RET
;Here to trace appropriate MSD for both send and receive
;The following has knowlege of MSD secrets to which
; only D36COM should be privy.
TRCMGT: PTRACE NSP ;PUT OUT A TRACE PREFIX
XMOVEI T1,[ASCIZ /NSP Hdr:/]
CALLTRACE .TSTRG##
CALLRET TRCMGB ;OUTPUT BYTE STRING
;Here to trace UD.MSD for output messages only
TRCMGU: XMOVEI T1,[ASCIZ /]
/]
CALLTRACE .TSTRG##
PTRACE NSP ;PUT OUT A TRACE PREFIX
XMOVEI T1,[ASCIZ /User Data:/]
CALLTRACE .TSTRG##
LOAD T4,MDBYT,+UD.MSD(MB) ;NUMBER OF BYTES INVOLVED
LOAD T5,MDAUX,+UD.MSD(MB) ;BYTE POINTER TO BEG OF DATA
LOAD T6,MDALA,+UD.MSD(MB) ;PICK UP ALLOCATED ADDR FOR INDEX
CALLRET TRCMGB ;OUTPUT BYTE STRING
;Here to finish off the trace
TRCMGX: CALLTRACE .TRBRK## ;RIGHT ANGLE BRACKET
CALLTRACE .TCRLF##
RET
;Subr to output byte strings for trace routines
;T6 is loaded with the index for the byte pointer in T5
TRCMXT: DEC 500 ;ONLY TYPE THIS NUMBER OF BYTES
TRCMGB: SAVEAC <P1,P2>
SKIPG P1,T4 ;SAVE FOR LATER
RET ;RETURN NOW IF NO USER DATA
CAMLE T4,TRCMXT ;LET'S LIMIT THE TYPOUT
MOVE T4,TRCMXT
MOVE P2,T4 ;NUMBER OF BYTES WE'LL TYPE OUT
TRCMU1: CALLTRACE .TSPACE##
ILDB T1,T5
CALLTRACE .TOCTW## ;OUTPUT BYTE IN OCTAL
SOJG P2,TRCMU1
MOVEI T1,[ASCIZ / .../]
CAMLE P1,TRCMXT ;DID WE TRUNCATE?
CALLTRACE .TSTRG## ;YES, TELL WATCHER
RET
>;END OF IFN FTTRACE
SUBTTL End of Program
IFN FTOPS20,TNXEND
END