Trailing-Edge
-
PDP-10 Archives
-
decnet_mcb_cusps_703a
-
10,7/703mon/llinks.mac
There are 21 other files named llinks.mac in the archive. Click here to see a list.
;TITLE LLINKS - Network Services Layer of Phase III DECnet V046
;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
;OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1976, 1985, 1986.
;ALL RIGHTS RESERVED.
SUBTTL W. G. Nichols
SEARCH D36PAR,SCPAR,MACSYM
IFN FTOPS10,<
.CPYRT<1976,1986>
> ;END IFN FTOPS10
ENTRY NSP,NSPRTR,NSPINI,NSPJIF ;The entry points,
ENTRY NSPCG,NSPCR ; see procedures for calling sequence
SALL
; The NSP link block definitions are in SCPAR so that they will
; be available to DCNSPY and similar reporting programs.
FTORC==FTDEBUG ;Hang until all output dones return if FTORC=1
FTTROLL==0 ;GIVE A NAME TO TROLL CODE
FTGOL==0 ;Assemble goal code conditionally
FTBFR==0 ;Assemble buffer-rich code conditionally
IFN FTOPS10,<
TITLE LLINKS - Network Services Layer of Phase IV DECnet
SEARCH F,S
$RELOC ;Set up code and data segments according
.NTSHO==3 ;define SHOW 'cuz we don't search UUOSYM
>
IFN FTOPS20,<
SEARCH PROLOG
TTITLE (LLINKS,,< - Network Services Layer of Phase IV DECnet>)
>
D36SYM ;Fix up problems with MACSYM in DECnet-36
IFN FTTRACE,<
PRINTX FTTRACE On: Note that TRACE code has not been updated
PRINTX FTTRACE to deal with the 1-word global bytepointers
>
XRESCD ; 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. NSP Node Block Definition. . . . . . . . . . . . . . . 15
; 7. Resident Storage Allocation. . . . . . . . . . . . . . 16
; 8. NSP's Entry Vector Table . . . . . . . . . . . . . . . 18
; 9. NSPRCV - Receive a Message from Router . . . . . . . . 19
; 10. NSPRTR - The Entry Point from Router . . . . . . . . . 21
; 11. NSPINI - Initialization Entry Point. . . . . . . . . . 22
; 12. NSP Interlock Handlers
; 12.1. NSPLCQ - Queue on Failure . . . . . . . . . . 23
; 12.2. NSPLCW - Wait on Failure. . . . . . . . . . . 24
; 12.3. NSPLCF - Flags set for Failure. . . . . . . . 25
; 13. Session Control Calls
; 13.1. NSP - The Entry Point . . . . . . . . . . . . 27
; 13.2. NSPOPN - Open a Link. . . . . . . . . . . . . 28
; 13.3. NSPGOL - Set New Data Request Goals . . . . . 30
; 13.4. NSPACT - Enter Active . . . . . . . . . . . . 31
; 13.5. NSPACC - Accept Connect . . . . . . . . . . . 32
; 13.6. NSPSEG - Send Data. . . . . . . . . . . . . . 34
; 13.7. NSPDRQ - Data Request Call. . . . . . . . . . 36
; 13.8. NSPREJ - Reject a Connection. . . . . . . . . 37
; 13.9. NSPDSC - Synch Disconnect . . . . . . . . . . 39
; 13.10. NSPABO - Abort Link . . . . . . . . . . . . . 40
; 13.11. NSPCLS - Close Port . . . . . . . . . . . . . 41
; 14. CLSABO - Send a free "Abort by Object" . . . . . . . . 42
; 15. PROCXQ - Process the Transmit Queue. . . . . . . . . . 43
; 16. SCNIRS - Tell Session Control Port is Not in Run State 46
; 17. SNDRTR - Send a Message to Router. . . . . . . . . . . 47
; 18. ROUTER Calls
; 18.1. NSPRCV - Receive a Message from Router. . . . 49
; 18.2. NSPODN/NSPOND - Output Done/Not Done. . . . . 50
; 18.3. NSPRTS - Return a Message to Sender . . . . . 51
; 18.4. NSPRFR - Return a Message to Sender from Remote 51
; 19. Message Receivers
; 19.1. RCVPRC - Preliminary Processing . . . . . . . 52
; 19.2. PRCACK - Process ACKNUM/ACKOTH Field. . . . . 54
; 19.3. Receive a Data Segment. . . . . . . . . . . . 56
; 19.3.1. Check Length of Q. . . . . . . . . . 59
; 19.4. PROCRQ - Process the Receive Queue. . . . . . 60
; 19.5. RCVLKS - Receive a Link Service Message . . . 62
; 19.6. RCVACK and RCVNOP - Little Ones . . . . . . . 67
; 19.7. RCVCA - Receive a Connect ACK Message . . . . 68
; 19.8. RCVCI - Receive a Connect Initiate Request. . 69
; 19.9. RCVCC - Receive a Connect Confirm Message . . 71
; 19.10. RCVDI - Disconnect Initiate Message . . . . . 73
; 19.11. RCVDC - Disconnect Confirm Message. . . . . . 75
; 20. Clock-Driven Routines. . . . . . . . . . . . . . . . . 77
; 21. CLCXDQ - Calculate Transmit Data Requests. . . . . . . 82
; 22. NSPRJF - Request Jiffy Service . . . . . . . . . . . . 85
; 23. SECCHK - Once-a-Second Checks. . . . . . . . . . . . . 86
; 24. Memory Manager Calls
; 24.1. NSPCG - We're Congested . . . . . . . . . . . 92
; 24.2. NSPCR - Congestion is Relieved. . . . . . . . 93
; 25. NSIRLV - Congestion-Relieved Processor . . . . . . . . 94
; 26. PESFLO - Put Link in Pessimistic Flow Control. . . . . 95
; 27. SCLOSE - Start the Close Process . . . . . . . . . . . 97
; 28. SENDRQ - Send a Link Service Message . . . . . . . . . 104
; 29. SACKMG - Get a Message Block and send ACK message if can 106
; 30. SNDACK - Check a sublink to see if it needs an ACK sent 107
; 31. SENDDI - Send a DI Message . . . . . . . . . . . . . . 108
; 32. SNDCAK - Send a Connect ACK Message. . . . . . . . . . 109
; 33. SNDDSC - Send a Disconnect Complete Message. . . . . . 110
; 34. MGACKD - A Message has been ACKed. . . . . . . . . . . 111
; 35. SENDNL - Send a No Link Message. . . . . . . . . . . . 112
; 36. PTIRUN - Put a Port in Run State . . . . . . . . . . . 114
; 37. MAKPRT - Make a New Port Block . . . . . . . . . . . . 115
; 38. GTRESP - Get a Reserved Port . . . . . . . . . . . . . 116
; 39. MAKPRS - Make this Port Reserved . . . . . . . . . . . 117
; 40. SETPRT - Make a New Port Block . . . . . . . . . . . . 118
; 41. FNDPRT - Hash an LLA to Find a Port Block. . . . . . . 119
; 42. Hash Table Routines. . . . . . . . . . . . . . . . . . 120
; 43. NEWLLA - Make a New Local Link Address . . . . . . . . 123
; 44. NEWSGN - Make a New Message Segment Number . . . . . . 124
; 45. NSPpid Control - GETPID & FNDPID . . . . . . . . . . . 125
; 46. DSTPRT - Destroy a Port. . . . . . . . . . . . . . . . 127
; 47. QSRTMB - Queue a Message on a Sorted Queue . . . . . . 128
; 48. MAKHDR - Initialize the NSP MSD to build a Message Header 130
; 49. UPDELAY - Update a Logical Link's Roundtrip Delay. . . 131
; 50. Free a Message Block . . . . . . . . . . . . . . . . . 132
; 51. Reserved Port Management . . . . . . . . . . . . . . . 133
; 52. Reserved Port Process. . . . . . . . . . . . . . . . . 134
; 53. DCRORC - Decrement 'Out-in-router' count . . . . . . . 137
; 54. BLDRCI - Build a (R)CI message . . . . . . . . . . . . 138
; 55. RELCIM - Release saved (R)CI message . . . . . . . . . 139
; 56. Network management
; 56.1. Dispatch. . . . . . . . . . . . . . . . . . . 140
; 56.2. SET parameter . . . . . . . . . . . . . . . . 141
; 56.3. READ parameter. . . . . . . . . . . . . . . . 141
; 56.4. CLEAR parameter . . . . . . . . . . . . . . . 141
; 56.5. SHOW counters . . . . . . . . . . . . . . . . 142
; 56.6. SHOW and ZERO counters. . . . . . . . . . . . 142
; 57. Node data base
; 57.1. Find an NSP node block. . . . . . . . . . . . 143
; 57.2. Find an existing NSP node block . . . . . . . 144
; 57.3. Free a node block . . . . . . . . . . . . . . 145
; 57.4. Release a node block. . . . . . . . . . . . . 145
; 58. NSPJB0 - NSP periodic checks . . . . . . . . . . . . . 146
; 59. EVTINI - Initialize event logger interface . . . . . . 147
; 60. NSPEVT - Queue an Event to Network Management. . . . . 148
; 61. Trace-to-TTY Facility. . . . . . . . . . . . . . . . . 151
; 62. End of Program . . . . . . . . . . . . . . . . . . . . 154
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
EXTERN DNGWZP ;ROUTINE TO GET SOME ZEROED MEMORY
;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
;
;
;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
;Network management parameters
; T1/ parameter table
; T2/ parameter table length
; T3/ function code
EXTERN NTPARM
;Network management counters
; T1/ counter table
; T2/ counter table length
; T3/ function code
EXTERN NTCTRS
; T1/ Ptr to arg block with structure NE in it
EXTERN NMXEVT ;EVENT CATCHER
; Normal Return
;SCLINK node number/name mapping
EXTERN SCTA2N
;SCLINK event parameter routine
EXTERN LEVT.0 ;Event parameter 0
; Always +1 return
;NTMAN hook for counter NICE'zation
EXTERN PRSCOU
;DECnet initialization block
EXTERN IBBLK
;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,2 ;0=3.2, 1=3.1, 2=4.0, 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
;This structure is expected to be used to pull apart a value held
;in a register.
Repeat 0,< ;Architectural definition
BEGSTR SG ;The SEGNUM field in a NSP header
FILLER 20 ;Field is 16 bits wide
FIELD MBZ,3 ;Must be zero
FIELD DLY,1 ;ACK DELAY allowed
FIELD NUM,12 ;Segment number
ENDSTR
>
Repeat 1,< ;VMS definition
BEGSTR SG ;The SEGNUM field in a NSP header
FILLER 20 ;Field is 16 bits wide
FILLER 1
FIELD DLY,1 ;ACK DELAY allowed
FIELD MBZ,2 ;Must be zero
FIELD NUM,12 ;Segment number
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 NSP Node Block Definition
;The node block contains all the information LLINKS has to know about a
;node. There is a node block for all nodes that we have active links to.
;The node block is created when someone tries to connect to a node that
;does not yet have a node block associated with it.
;
;When the number of active links goes to zero, the node block is subject
;to possible deletion. If the number of node blocks is larger than NNDMAX,
;then the now unused node block is deleted after that its counters are
;logged with a 3.2 (database reused) event.
;
;The list of node blocks are pointed to by the queue header NMXNDQ.
;Note - many counters are full words, even though they only have to be
; 16 bits wide. This is so the OPSTR logic will generate a single
; read-modify-write instruction when updating them, and will spare
; us the worry of interlocking this data base.
;Defined in D36PAR
SUBTTL Resident Storage Allocation
;The following INTERNALs are for NMXSER, which reads and writes these
;parameters.
INTERNAL NSPVER ;NSP VERSION NUMBER (JNTMAN only)
INTERNAL NSPECO ;NSP VENDOR ECO NUMBER (JNTMAN only)
INTERNAL NSPUEC ;NSP USER ECO NUMBER (JNTMAN only)
INTERNAL EVPBYT ;Event parameter typeout
INTERNAL EVP2BT ; " " "
;See previous page for list of INTERNAL symbols.
RESDT
NSPBFR: DEC 3 ;REMOTE IS BUFFER-RICH IF GIVES .GE. NSPBFR DRQS
NSPVRN: EXP VER4.0 ;NSP PROTOCOL VERSION NUMBER (AS SEEN IN CI MSGS)
NSPVER: DEC 4 ;NSP PROTOCOL VERSION NUMBER (AS SEEN BY USERS)
NSPECO: DEC 0 ;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)
NSPADL: %NSADL## ;ACK DELAY INTERVAL IN SECONDS
NSGOAL: DEC 8 ;System-wide goal
NMXNDQ:: BLOCK QH.LEN ;QUEUE HEADER FOR NMX NODE BLOCKS
NODFLG: DEC 0 ;Set if the node queue needs pruning
NNDMAX: DEC 20 ;Maximum # of node blocks kept
BRKFLG: BLOCK 1 ;Set if 'link broken' messages should be sent
MORFLG: BLOCK 1 ;Set if 'link broken' table overflowed
BRKLEN==^D5 ;# of entries in link broken table
BRKTAB: BLOCK BRKLEN ;The 'link broken' table
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)
;** The pipe size should be approximately the same as the link quota **
NSPPSZ: DEC 8 ;Maximum pipe size is 8
NSPIFG: DEC 0 ;NON-ZERO WHEN NSP IS INITIALIZED
NSPNLA: BLOCK 1 ;NEXT LINK ADDRESS TO ASSIGN
IFN FTOPS10,<
INTERNAL NSPAPQ
>; END IFN FTOPS10
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
NSPECP: DEC 0 ;Event Communication block Pointer
IFN FTOPS20,<
NSPLKO: BLOCK 1 ;SERIAL NUMBER OF CPU WHICH OWNS NSP LOCK
NSPLOK: DEC -1 ;NSPLCx'S SEMAPHORE, -1 IS FREE
>
XRESCD
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&777777>> ;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 RFR ;Return to sender from remote 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
XRESCD
NSPRTR: SAVEAC <P1,P2,MB,MS,FREE1,FREE2>
SKIPN NSPIFG ;NSP INITIALIZED?
RET ; -no, just return
MOVE MB,T4 ;NSP EXPECTS MESSAGE BLOCK PTR IN MB
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 Initialization - Compute Memory Requirements
;NSPCCR computes LLINKS's core requirements.
;Call:
; PUSHJ P,NSPCCR
;Returns:
; T1/ Size of LLINKS core
IFN FTOPS10,<
NSPCCR::SETZ T1, ;NO ADDITIONAL MEMORY REQUIRED
RET ;AND RETURN
>; END IFN FTOPS10
SUBTTL NSPINI - Initialization Entry Point
;NSPINI - This is one of the few entry points to NSP
;
;Call: 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 BUGs and the
;calls will be ignored.
XSWAPCD
NSPINI: SAVEAC <P1,P2>
;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
CALL EVTINI ;Initialize event communication block
LOAD T1,IBADR,+IBBLK ;Get executor node address
CALL FNDNOD ;Create a node block for it
JFCL ; - at this point we do not care
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.
CALLRET RTRINI ;Pass on the torch to ROUTER
XRESCD ;Go resident again
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
;TOPS20 assumes NOINT, at least.
;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
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
;TOPS20 assumes NOINT, at least.
;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
;Must run in section 1 since the scheduler test data structure is currently
; only 18 bits wide.
RESCD
NSPLWB: SKIPL NSPLOK ;IS NSP LOCK FREE YET?
JRST (T4) ;NO, SLEEP ON
JRST 1(T4) ;YES, WAKE UP FORK
XRESCD
NSPLW2: BUG.(CHK,LLIBWK,LLINKS,SOFT,<SCTNSF call from sched w/o lock>,<<T6,CALLER>>,<
Cause: The DECnet entry point NSP has been called from scheduler
level when the NSP interlock was locked. This should never
happen.
Action: Inspect the stack to find out who the offender was.
Data: CALLER - The address of the routine that requested the interlock
>)
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&777777> ;1B0 MEANS JIFFY REQUEST
IFIW!<NSICGT&777777> ;1B1 MEANS CONGESTION-DETECTED
IFIW!<NSIRLV&777777> ;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
XRESCD
NSP: SAVEAC <P1,P2,MS,MB,FREE1,FREE2>
SKIPN NSPIFG ;NSP INITIALIZED?
RET ; -no, just return
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?
CALLRET DNFWDS ; -no, should never happen, deallocate
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 [SETZRO MBAR1,(MB) ;RETURN 0 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
IFN FTGOL <
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:
IFN FTGOL <
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
MOVX T1,MGFCI ;Build a CI message NSP header
CALL BLDRCI ; by using common routine
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
;Ask for the message back so we can remake it to a RCI and, if
; necessary, retransmit it
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?
JRST NSIAC1 ; -no, should never happen
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
IFN FTGOL <
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
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>,<<EL,ELPTR>,<ES,ESPTR>,<MB,MBPTR>>,<
Cause: LLINKS was asked to transmit two interrupt messages simultaneously.
A maximum of one is allowed. This is a software problem. Please
submit a SPR if it happens more than once, and include a dump of the
system and the additional data.
Data: ELPTR - Address of EL block
ESPTR - Address of ES block
MBPTR - Address of message block
>)
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 user bytes sent to node
OPSTRM <AOS>,NNXMC,(P1) ;Update count of user messages
;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
SETZRO ESDLY,(ES) ;No more ACK DELAY until remote requests it
WRTAK1:
;Write optional ACKOTH field
LOAD T1,ELVER,(EL) ;GET REMOTE NSP VERSION
CAIGE 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
SETZRO ESDLY,(ES) ;No more ACK DELAYs until remote requests it
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 ACKOTH FIELD
;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
JRST FREMSG ; Duplicate message queued for xmit, should
; never happen, free up message
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:
;Instead of dequeueing the first MB (they are sorted in ascending segment
; number), peek and check if we are allowed to send it. If not, we save some
; cpu cycles not having to dequeue and then queue it again.
LOAD MB,QHBEG,+ES.XMQ(ES) ;Get first MB on queue for this ES
JUMPE MB,RTN ; -return if there is no MB
LOAD T1,ESLAR,(ES) ;Get last ACK'ed
OPSTR <ADD T1,>,ESCWS,(ES) ; and add current window size
ANDI T1,<MASK.(WID(ESLAR),35)> ; and make highest allowed seg #
CMODGE T1,NMSGN,(MB) ;Allowed to send it?
RET ; -no, just return
;Yes, we are allowed to send this, dequeue it and proceed
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&777777> ;NO FLOW CONTROL
IFIW <PROCXS&777777> ;SEGMENT FLOW CONTROL
IFIW <PROCXM&777777> ;MESSAGE FLOW CONTROL
IFIW <PROCXI&777777> ;ILLEGAL FLOW CONTROL
;The No Flow Control case
;
;Allow ACK to be delayed if the message number we are sending is less or
; equal to the number of the last ACK'ed message + half the windowsize.
PROCXN: LOAD T1,ESCWS,(ES) ;Get current window size
AOJ T1, ;Up by 1
ASH T1,-1 ;Divide by 2
OPSTR <ADD T1,>,ESLAR,(ES) ;Add number of last ACK'ed
ANDI T1,<MASK.(WID(ESLAR),35)> ; and make highest allowed seg #
MOVX T2,NMDLY ;Get DELAY flag
CMODL T1,NMSGN,(MB) ;Ok to delay?
IORM T2,NM.DLY(MB) ; -yes
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
JUMPLE T1,PRCXS1 ;If this is the last DRQ do not delay ACK
SETONE NMDLY,(MB) ; - it wasnt, allow ACK to be delayed
PRCXS1: OPSTRM <SOS>,ESXLD,(ES) ;DECREMENT XMIT-FROM-LOCAL FDATA REQUESTS
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>,<<EL,ELPTR>,<ES,ESPTR>,<MB,MBPTR>>,<
Cause: An illegal flow control type was requested on transmit. This
should have been checked by a higher layer. Inspect the stack
to find the path that caused the bad value. Please submit a SPR
and include a dump and the additional data.
Data: ELPTR - Pointer to EL block
ESPTR - Pointer to ES block
MBPTR - Pointer to message block
>,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
JRST FREMSG ; Duplicate message queued for transmit,
; should never happen, free message
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
Repeat 0,<
;We need to build the NSP header if this is the first transmission of
; a message, or if this is a retransmission of a message that had the
; DLY bit set in the first transmission. Retransmitted messages should
; not have the DLY bit set.
SNDATA: TMNN NMCNT,(MB) ;Is this a retransmission?
IFSKP. ; -yes,
JE NMDLY,(MB),SNDAT2 ; If DLY was clear at first transmit, then
; we need not rebuild the NSP header.
SETZRO NMDLY,(MB) ; Clear DLY flag for retransmit and rebuild
ENDIF.
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
LOAD T2,ELVER,(EL) ;Get remote NSP version number
TMNE NMDLY,(MB) ;Should DLY be set?
CAIGE T2,VER4.0 ; and is remote at NSP version 4 or higher?
TRNA ; -no
TXO T1,SGDLY ; -yes to both, OR in the DLY bit
CALL DNP2BY
JN NMCNT,(MB),SNDAT2 ;If retransmitted, go to SNDAT2 for other flags
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
>
Repeat 1,<
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
LOAD T2,ELVER,(EL) ;Get remote NSP version number
TMNE NMDLY,(MB) ;Should DLY be set?
CAIGE T2,VER4.0 ; and is remote at NSP version 4 or higher?
TRNA ; -no
TXO T1,SGDLY ; -yes to both, OR in the DLY bit
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)
MOVE T2,NSPRTH ;Get retransmission threshold
LSH T2,-1 ;Divide by 2
OPSTR <CAMGE T2,>,NMCNT,(MB) ;Larger than current retry count?
TXO T1,ST%TRY ; -no, "try hard"
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
ST%TRY== 1B31 ;Ask ROUTER to "try hard"
ST%NTY== 0B31 ;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
LOAD T1,IBADR,+IBBLK ;Get source address
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 ptr stored
LOAD T1,ELLLA,(EL) ; and Local Link address
STOR T1,NMLLA,(MB) ; for NSIODN's consistency check
;Session Control has already stored MBBOM and MBEOM
; if this is a user's message segment
XMOVEI T1,NM.MSD(MB) ;Get # of bytes in
CALL DNSLNG ; NSP message
MOVE T4,T1 ;"Save" T1 in T4. *Note* We 'know' that T4
; is not used by DNSLNG. This is to have as
; few instructions as possible in this common
; path.
XMOVEI T1,UD.MSD(MB) ;Get # of bytes in
CALL DNSLNG ; user message
ADD T4,T1 ;Add them together gives total # of bytes
LOAD T1,ELNDB,(EL) ;GET PTR TO NODE BLOCK FOR THIS LINK
INCR NNTMX,(T1) ;UPDATE COUNT OF MESSAGES SENT TO NODE
OPSTRM <ADDM T4,>,NNTBX,(T1) ; and update count of bytes
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
IFN FTPARANOID < ;Catch "ORQ" problem
ENDQUE MB,EL.ORQ(EL),NM.ORQ,T1 ;Queue MB on "ORQ" queue
MOVE T1,[ORCQUO: EXP ^D60] ;Give it 60 seconds to time out
STOR T1,ELCLC,(EL) ;Store it in EL
XMOVEI T1,SNDRTR ;Get initial value of trace word
STOR T1,NMMAG,(MB) ;Store it in MB
>
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
TMNE NMDLY,(MB) ;DELAY?
ADD T1,NSPADL ; Add ACK DELAY so we dont timeout too early
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
TMNE NMDLY,(MB) ;Allowing delayed ACK's for this message?
JRST SNDRT1 ; -yes, dont time it
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
TXNE P1,ST%TRY ;Try hard?
TXO T1,RT%TRY ;Yes, ask ROUTER to do so
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(NSP,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(NSP,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
NSPODN: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSIODN: LOAD EL,NMPRT,(MB) ;Pointer to ELB in use when MB was sent
LOAD T1,NMLLA,(MB) ;Get local link address from returning MB
LOAD T2,ELLLA,(EL) ;Get LLA from ELB it used to be attached to
CAME T1,T2 ;That ELB still in use by same logical link?
JRST FREMSG ;No, toss the message
LOAD T1,ELSTA,(EL) ;Yes, get its state
IFSTATE T1,<DP>,FREMSG ;Ignore if in Destroy Port state
CALL DCRORC ;Decrement 'out-in-router' count
;Check if a CI or RCI message came back.
;
; If the link state is not CI, then release the message. It is unlikely
; that we will have recived a CA before ROUTER returns the CI message, but
; it is possible that a CA/CC/DI/DC came in while the RCI message was out-
; standing.
;
; Finally save a pointer to the message so it can be retransmitted.
LOAD T1,NMMGF,(MB) ;Get message type from message block
CAIE T1,MGFCI ;Is it a CI
CAIN T1,MGFRCI ; or a RCI message?
IFNSK. ; -yes,
LOAD T2,ELSTA,(EL) ; Get link state
IFNSTATE T2,<CI>,FREMSG ; return message if not in CI state
SETZRO NMACK,(MB) ; Clear 'needs ACK' just in case...
STOR MB,ELCIM,(EL) ; and allow retransmission again
RET
ENDIF.
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?
JRST FREMSG ; -no, we should never get on back with
; NMACK=0, but if, just release it
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
JRST FREMSG ; -duplicate msg, just release it
RET
SUBTTL ROUTER Calls -- NSPRTS - Return a Message to Sender
SUBTTL ROUTER Calls -- NSPRFR - Return a Message to Sender from Remote
;NSPRTS - Called by Router to return a message to sender
;NSPRFR - Called by Router to return a message to sender from remote Router
;
;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 and NSPRFR 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
;(entry NSPRTS) or by a route-through remote Router (entry NSPRFR) 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.
;If NSPRTS is called, then the message came back from local ROUTER
; and NSPODN has not been called, i.e. decrement ORC count
;
;If NSPRFR is called, then the message came back from a remote ROUTER
; and NSPODN was called before, i.e. it has already been decremented.
;
;P2 is used as a flag to distinguish between the two cases, since the
; code is common.
NSPRTS: JSP T6,NSPLCQ ;QUEUE UP THE REQUEST
NSIRTS: SETO P2, ;Decrement ORC count
JRST NSIRTR ;Go join common code
NSPRFR: JSP T6,NSPLCQ ;Queue up the request
NSIRFR: SETZ P2, ;Do not decrement ORC count
; JRST NSIRTR ;Go join common code
NSIRTR:
CALL INIHDR ;WE STILL HAVE THE PORT, SET UP TO READ MSG
;MSGFLG
CALL DNGEBY ;GET AN EXTENSIBLE BYTE
EVENT(NSP,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(NSP,MSG,No DLA in RTS,FREMSG)
MOVE P1,T1
;SLA
CALL DNG2BY ;GET REMOTE LINK ADDRESS IN T1
EVENT(NSP,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: SKIPE P2 ;Should we decrement ORC count?
CALL DCRORC ; -yes, do it
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
CALL INSBRK ;Insert into 'link broken' table
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(NSP,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(NSP,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
XMOVEI T1,IN.MSD(MB) ;Get length
CALL DNSLNG ; of message
LOAD T3,ELNDB,(EL) ;GET PTR TO NODE BLOCK FOR THIS LINK
INCR NNTMR,(T3) ;UPDATE COUNT OF MESSAGES FROM NODE
OPSTRM <ADDM T1,>,NNTBR,(T3) ;Update count of bytes received
LOAD T2,MBVST,(MB) ;Get visit count
IMULI T2,3 ; times 3
STOR T2,NNPSZ,(T3) ; gives pipe size
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(NSP,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
;ACKOTH
LOAD T1,ELVER,(EL) ;GET REMOTE NSP VERSION
CAIGE 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
RCVPR0: TXNE P2,RT%ACK ;OK TO SKIP THE ACKNUM FIELD?
EVENT(NSP,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(NSP,MSG,No predicted SEGNUM,FREMSG) ;BAD MESSAGE EVENT TYPE
RCVPR3: TXZN T1,SGDLY ;DLY bit set?
IFSKP. <SETONE NMDLY,(MB)> ; -yes, set DLY bit in message block
STOR T1,NMSGN,(MB) ;STORE SEGMENT NUMBER IN MESSAGE BLOCK
RETSKP ;SUCCESS RETURN
SUBTTL Message Receivers -- PRCACK - Process ACKNUM/ACKOTH Field
;PRCACK - Process the ACKNUM field of an incoming message
;
;Call: T1/ The ACKNUM/ACKOTH field
; EL/ The Port Block
; ES/ The Sublink Block
; CALL PRCACK, only if ACKNUM/ACKOTH 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
;Cross subchannel ACK in phase IV?
LOAD T4,ELVER,(EL) ;GET VERSION # OF REMOTE NSP
CAIL T4,VER4.0 ;IS IT VERSION 4.0?
CAILE P2,AK$CNK ;YES, IS IT A CROSS SUB-CHN ACK OR NAK?
EVENT(NSP,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
;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
JRST PRCNAK ; Ignore stale ACK, see if its a NAK
CMODLE P1,ESLMA,(ES) ;IF .GT. LAST MSG ASSIGNED (SENT)
RET ; Ignore early ACK, even if its a NAK
MOVE T1,P1 ;Get last ACK received
OPSTR <CAMGE T1,>,ESLAR,(ES) ;Will we wrap around?
MOVEI T1,<1B<35-WID(ESLAR)>>(P1) ; -yes, T1 := P1 + MAX(ESLAR)+1
OPSTR <SUB T1,>,ESLAR,(ES) ;# of messages ACK'ed by the ACK
OPSTR <ADD T1,>,ESCDA,(ES) ;Add to # of ACK's since last window change
ANDI T1,<MASK.(WID(ESCDA),35)> ;Protect againt overflow
STOR T1,ESCDA,(ES) ; and save new value
LOAD T1,ESCWS,(ES) ;Get current window size
OPSTR <CAMLE T1,>,ESCDA,(ES) ;Compare with # of ACK's since last change
IFSKP. ; Window size smaller
LOAD T2,ELNDB,(EL) ; Get node block pointer
LOAD T3,NNPSZ,(T2) ; Get pipe size
CAML T1,T3 ; Is window size LT than current pipe size
CAMGE T1,NSPPSZ ; or LT than 'maximum pipe size' ?
ANNSK. ; Its OK
INCR ESCWS,(ES) ; -so increment window size
SETZRO ESCDA,(ES) ; and clear # of ACK's since last window chg
ENDIF.
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 we zero the NMTIM field in all the remaining unACKed
;msgs for this sublink and then zero the remaining time in the long interval
;so CHKRSN will be called soon to resend the NAKed messages. Note that CHKRSN
;will not retransmit a message to a phase II node unless its NMTIM field has
;been so zeroed.
PRCNAK: 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
SETZM NSPJLC ;MAKE LONG-INTERVAL PROCESSING HAPPEN NEXT TICK
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>,<<EL,ELPTR>,<ES,ESPTR>,<MB,MBPTR>>,<
Cause: There is a duplicate interrupt message on the unacked interrupt
receive queue. This should not happen because the NSP interlock
should not release with anything on the receive queue.
Action: Either the interrupt flow control is wrong and more than one data
request was sent or the remote node sent an interrupt message without
a data request.
Data: ELPTR - Pointer to EL block
ESPTR - Pointer to ES block
MBPTR - Pointer to message block
>,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
IFN FTGOL <
LOAD T1,ESGOL,(ES) ;GET CURRENT GOAL FOR LINK
>
IFE FTGOL <
MOVE T1,NSGOAL ;Get system-wide goal
>
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*FTGOL,<
TMNE ESGOL,(ES) ;IF NOT ALREADY PESSIMIZED, TELL WATCHER
ETRACE NSP,<Busting overly optimistic link>
>
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
SETONE <ESACK,ESNAK>,(ES) ;Yes, NAK the other end
CALLRET PESFLO ; and 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:
IFN FTGOL <
LOAD T1,ESGOL,(ES) ;GET DATA REQUEST GOAL
>
IFE FTGOL <
MOVE T1,NSGOAL ;Get system-wide goal
SKIPE DCNCON ;System-wide congestion
SETZ T1, ;Yes, then goal of zero
>
LOAD T4,ESRFL,(ES) ;GET RECEIVE FLOW CONTROL TYPE
JRST @.+1(T4) ;DISPATCH
IFIW <PRCRQN&777777> ;NO FLOW CONTROL
IFIW <PRCRQG&777777> ;SEGMENT FLOW CONTROL
IFIW <PRCRQM&777777> ;MESSAGE FLOW CONTROL
IFIW <PRCRQN&777777> ;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
IFN FTBFR <
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.
;Buffer-richness has been superseded by ACK delay.
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, then do no more
SETONE <ESNAK,ESACK>,(ES) ;Missing a message, 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) ;Need an ACK
TMNE NMDLY,(MB) ;ACK DELAY set in message?
IFSKP. ; -no, queue jiffy service
CALL NSPRJF ; Do ACK at next jiffy
ELSE. ;-yes, ACK DELAY was set
TMNE ESDLY,(ES) ; Was DELAY already set?
IFSKP. ; -no, no jiffy service needed
SETONE ESDLY,(ES) ; Set the indicator
MOVE T1,NSPADL ; Get # of seconds before timeout of DELAY
STOR T1,ESDLT,(ES) ; and save for second-level check
ENDIF.
ENDIF.
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
OPSTRM <AOS>,NNRMC,(T4) ; and increment # of user messages received
;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&777777> ;NO FLOW CONTROL
IFIW <PRCRSS&777777> ;SEGMENT FLOW CONTROL
IFIW <PRCRSM&777777> ;MESSAGE FLOW CONTROL
IFIW <PRCRS6&777777> ;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>,<<EL,ELPTR>,<ES,ESPTR>,<MB,MBPTR>>,<
Cause: An illegal flow control type was found at PRCRQS when the receive
queue was processed. If a remote node had sent us a bad flow control
type, it should have been found by the message parsing routines.
Therefore this should never happen.
Action: If this happens more than once, please submit a SPR and include the
additional data and a dump of the system.
Data: ELPTR - Address of EL block
ESPTR - Address of ES block
MBPTR - Address of message block
>,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
;We set the ACK flag here, and then call the rest of the routine as a
; coroutine. The remaining part of the routine may cause another message
; to be sent, and if so, then we can piggy-back the ACK to the remote.
; When the coroutine returns we check whether that happened or not - if
; the latter, then we'll just call SACKMG to send the ACK.
SETONE ESACK,(ES) ;Flag that we need an ACK on this sublink
CALL RCVLK0 ;Call coroutine to process the message
TMNN ESACK,(ES) ;Still need to send an ACK?
RET ; -no, the ACK has been piggy-backed
CALLRET SACKMG ;Yes, go send ACK if can, NSPRJF if cant
;RCVLK0 processes the link service message
RCVLK0: SAVEAC <ES> ;Preserve sublink pointer
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(NSP,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(NSP,MSG,No FCVAL field,FREMSG)
LOAD T2,ESXFL,(ES) ;GET TRANSMIT FLOW CONTROL TYPE
JRST @.+1(T2) ;CASE OF FLOW CONTROL TYPE
IFIW <LKFCVN&777777> ;NO FLOW CONTROL, IGNORE THE VAL FIELD
IFIW <LKFCVS&777777> ;SEGMENT FLOW CONTROL
IFIW <LKFCVM&777777> ;MESSAGE FLOW CONTROL
IFIW <LKSILG&777777> ;RESERVED FLOW CONTROL TYPE
;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.
SGNMAX== ^D 127 ;MAX TOTAL DRQS ARE ALLOWED TO GET
SGNMIN==-^D 128 ;MIN TOTAL DRQS ARE ALLOWED TO GET
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:
IFN FTBFR <
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
>
;Here for no flow control
LKFCVN:
;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(NSP,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"
CAIN T1,LS.MOF ;Are we?
IFSKP. ; -no, we're turning it on again
MOVEI T1,1 ; Lower window size to 1 (suspected
STOR T1,ESCWS,(ES) ; congestion)
MOVEI T3,0 ; and set it on
ENDIF.
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
CALL RELCIM ;Release saved CI 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(NSP,MSG,No DLA in CI,FREMSG)
JUMPN T1,[EVENT(NSP,MSG,Non-zero DLA in CI,FREMSG)]
STOR T1,NMLLA,(MB) ;SHOULD ALREADY BE ZERO, BUT...
;SLA
CALL DNG2BY
EVENT(NSP,MSG,No SLA in CI,FREMSG)
STOR T1,NMRLA,(MB) ;STORE THE REMOTE LINK ADDRESS
CALL CHKDCI ;(T1)DUPLICATE RECEIVED CI MSG? (smashes EL)
RET ;YES, IT HAS BEEN DEALT WITH
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
XMOVEI T1,IN.MSD(MB) ;Get length
CALL DNSLNG ; of CI message
LOAD T2,ELNDB,(EL) ;GET POINTER TO NSP NODE BLOCK
SETZRO NNMSG,(T2) ;We need a new message of this node goes off
INCR NNRCC,(T2) ;INCR NUMBER OF RECEIVED CONNECT INITS
INCR NNTMR,(T2) ;UPDATE COUNT OF MESSAGES FROM NODE
OPSTRM <ADDM T1,>,NNTBR,(T2) ;Update count of bytes received
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
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
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> ;Not needed
;DLA
CALL DNG2BY
EVENT(NSP,MSG,No DLA in CC,FREMSG)
STOR T1,NMLLA,(MB) ;LOCAL LINK ADDRESS
MOVE P1,T1
;SLA
CALL DNG2BY
EVENT(NSP,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
CALL RELCIM ;Release CI message (in case no CA message)
XMOVEI T1,IN.MSD(MB) ;Get length
CALL DNSLNG ; of message
LOAD T2,ELNDB,(EL) ;GET PTR TO NODE BLOCK FOR THIS LINK
SETZRO NNMSG,(T2) ;We need a new message if this node goes off
INCR NNTMR,(T2) ;UPDATE COUNT OF MESSAGES FROM NODE
OPSTRM <ADDM T1,>,NNTBR,(T2) ;Update count of bytes
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(NSP,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(NSP,FLO,Reserved value in PROCCI,RTN) ;ILLEGAL FLOW CONTROL
TXNE T1,SVFL1 ;ASSURE THAT HIGH-ORDER ALL ZERO
EVENT(NSP,MSG,PROCCI found foul,RTN) ;ILLEGAL MESSAGE
ANDI T1,SVFL2 ;ASSURE THAT LOW-ORDER IS MAGIC NUMBER
CAIE T1,SV$FL2 ;IS IT?
EVENT(NSP,MSG,PROCCI found foul,RTN) ;ILLEGAL MESSAGE
;VERSION
CALL DNGEBY ;GET EXTENSIBLE BYTE
EVENT(NSP,MSG,No VERSION field,FREMSG)
CAIN T1,VER3.1 ;Is it a phase II NSP?
IFSKP. ; -no, is it
CAIE T1,VER3.2 ; phase III?
MOVX T1,VER4.0 ; -no, treat as 4.0 (phase IV)
ENDIF.
;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
IFN FTBFR <
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(NSP,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
CALL RELCIM ;Release any saved CI message
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(NSP,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 RELCIM ;Release any saved CI message
CALL CLRRCQ ;CLEAR OUT THE RECEIVE QUEUES
CALL DNG2BY ;GET REASON CODE FROM MESSAGE
EVENT(NSP,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,CC,RN> ;ARE WE IN AN EXPECTED STATE?
EVENT(NSP,MSG,DC recvd in bad state,FREMSG)
;Extract the reason code, and pass it up to session control
LOAD T1,ELSCB,(EL) ;FIRST ARGUMENT FOR SESSION CONTROL
MOVE T2,P1 ;REASON CODE
MOVX T3,SV.DCR ;SCTL FUNCTION CODE DISCONNECT CONFIRM RCVD
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
LOAD T1,ELSTA,(EL) ;GET THE STATE AGAIN
IFNSTATE T1,<CI> ;CONNECT INITIATE?
IFSKP.
NEWSTATE RJ ;YES, ENTER REJECT CONNECTION STATE
ELSE.
NEWSTATE CN ;NO, ENTER CLOSE NOTIFICATION STATE
ENDIF.
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(NSP,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
XRESCD
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 ;Go check congestion if no jiffy demand
TMNE QHBEG,+NSPJFQ ;TIME FOR SHORT INT SERVICE, ANY DEMAND?
JRST NSPJF1 ;YES
MOVE T1,NSPJSI ;NO, GET NSP JIFFY SHORT INTERVAL VALUE
MOVEM T1,NSPJSC ;RESET THE SHORT INTERVAL COUNTER
RET ;LEAVE NOW
NSPJF0: TMNN <LKCGT,LKRLV>,+NSPLKF ;Congestion processing queued?
RET ; -no leave now
JRST NSPJF2 ;Go do deferred congestion processing
NSPJF1: SETONE LKJIF,+NSPLKF ;REQUEST A NSIJIF RUN
;NSIJIF WILL UPDATE NSPJSC WHEN ITS DONE
NSPJF2:
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.
;Note: we run the 'second check' before the 'jiffy check'. This is so that,
; if the second code generates any resends, then we will be able to piggy-back
; ACK's on the resends.
NSIJIF:
;Did a long interval expire?
SKIPLE NSPJLC ;Has a long interval elapsed?
IFSKP. ; -yes,
CALL SECCHK ; Do "once-a-second" processing
MOVE T1,NSPJLI ; Get length of a long interval
MOVEM T1,NSPJLC ; Reset the long interval counter
ENDIF.
;Now do jiffy processing
TMNE QHBEG,+NSPJFQ ;ANY DEMAND FOR SHORT INT?
CALL JIFCHK ;YES, DO "ONCE-A-JIFFY" PROCESSING
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&777777> ;NO FLOW CONTROL
IFIW <CLCXD1&777777> ;SEGMENT FLOW CONTROL
IFIW <CLCXD1&777777> ;MESSAGE FLOW CONTROL
IFIW <CLCXDN&777777>](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
JRST 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,DI> ;IF RUN OR DI BEFORE DI MSG HAS BEEN SENT,
CALL CHKINA ; CHECK FOR INACTIVITY
CALL CHKDLY ;Check for timeout of ACK DELAY
;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
CALL CHKRCI ;Check if we need to resend any RCIs
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 CHKRTM ;Check if time to resend
JRST CHKRSX ; NOT YET, DON'T COMPLAIN
;Here if a message has timed out
SETZRO ESCDA,(ES) ;# of successful ACK's := 0
MOVEI T1,1 ;Lower current window
STOR T1,ESCWS,(ES) ; to 1
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
;CHKRCI - check if a RCI message needs retransmission
CHKRCI: LOAD T1,ELSTA,(EL) ;Get link state
IFNSTATE T1,<CI>,RTN ;Retransmit only if in CI state
ASSUME ELCIM,EQ,-1 ;Make sure fullword
OPSTR <SKIPN MB,>,ELCIM,(EL) ;Get RCI message
RET ; -there was none to worry about
CALL CHKRTM ;Is it time to resend
RET ; -no
LOAD T1,NMCNT,(MB) ;Get # of times this message was retransmitted
CAMLE T1,NSPRTH ;Reached threshold?
CALLRET RELCIM ; -yes, release message and wait for session
; control to time out
SETZRO ELCIM,(EL) ;Clear pointer to disallow deallocation and
; retransmission until the message has been
; returned from ROUTER
MOVX T1,MGFRCI ;Make it into a RCI message
CALL BLDRCI ;Build the NSP header
LOAD T1,ELNDB,(EL) ;Get pointer to node block
INCR NNXCC,(T1) ;Increment # of CI msgs sent
MOVX T1, ST%NRS ! ST%ACK ! ST%RQR ! ST%NTR
CALLRET SNDRTR ;Send to ROUTER, and ask for it back
;Subroutine to calculate if time to resend a message
; Returns RET if not time to resend, RETSKP if time to resend
CHKRTM:
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?
RET ; -no, not yet
RETSKP ;-yes, resend now
;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>,<<MB,MBPTR>>,<
Cause: A message that was being resent had a bad message type. This means
that the message was overwritten while it was waiting on the resend
queue. The message type was good when the message was sent the first
time.
Action: If this happens more than once, please submit a SPR with the
additional data and a dump of the system.
Data: MBPTR - Pointer to the message block describing the bad message
>,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
JFCL ; Duplicate msg requeued - should never happen
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 <CALL @>,ELSCV,(EL) ;TELL SESSION CONTROL ABOUT IT
CALLRET INSBRK ;Go add this node to 'link broken' table
;CHKDLY - Check for timeout of ACK DELAY
;
;Call: EL setup
; ES available
; CALL CHKDLY
; Normal return
;We need only check the normal sublink for ACK DELAY timeouts, since
; the DLY bit may only be sent on the data sublink.
CHKDLY: XMOVEI ES,EL.NSL(EL) ;Get pointer to ES
TMNN ESDLY,(ES) ;ACK DELAY running?
RET ; -no, just return
OPSTRM <SOS T1,>,ESDLT,(ES) ;Decrement timer
JUMPG T1,RTN ;No need to do any more if not yet timed out
SETZRO ESDLY,(ES) ;Clear DELAY flag
TMNN ESACK,(ES) ;Need to send an ACK?
RET ; -no, just return
CALLRET NSPRJF ;-yes, request jiffy ACK service
SUBTTL Memory Manager Calls -- NSPCG - We're Congested
;NSPCG Memory Manager calls this when available memory falls
; below a threshold.
;
;Call: CALL NSPCG
; 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.
XRESCD
NSPCG:
;We need congestion service, but we may come here on interrupt level.
; Since we do not want to run DECnet-36 on interrupt level on the 20,
; we defer the congestion processing by setting LKCGT. The processing
; routine will happen the next time someone gives up the interlock, or
; at the next jiffy service, whatever comes first.
SAVEAC <P1,P2,MS,MB,FREE1,FREE2>
SETONE LKCGT,+NSPLKF ;We need congestion-detected processing
RET ; but defer
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
JE QHBEG,+ES.RCQ(ES),NSICG2 ;Jump if there is no receive queue
SETONE <ESACK,ESNAK>,(ES) ;NAK and
CALL PESFLO ; TRY TO RELIEVE CONGESTION
NSICG2: 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 -- NSPCR - Congestion is Relieved
;NSPCR Memory Manager calls this when available memory recovers
; to above a threshold after having called NSPCG.
;
;Call: CALL NSPCR
; 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
; NSPCG first. This is fine now, since all we do is clear the
; the congestion flag. This should continue to be fine.
XRESCD
NSPCR:
;See comment at NSPCG about deferring processing
SAVEAC MB ;SAVE MB FROM NSPLCF
SETONE LKRLV,+NSPLKF ;We need congestion-relieved processing
RET ; and defer
SUBTTL NSIRLV - Congestion-Relieved Processor
;NSIRLV - Congestion-Relieved Processor
;
;Call: CALL NSIRLV
; Only return
;This routine is the interlocked version of NSPCR, 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>
CALL SCTUCG ;SESSION CONTROL IS INTERESTED TOO
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&777777> ;NO FLOW CONTROL (SEND OFF MSG)
IFIW <PESSEG&777777> ;SEGMENT FLOW CONTROL (SEND NEG DRQS)
IFIW <PESOFF&777777> ;MESSGAGE F.C. (SEND OFF MSG)
IFIW <RTN&777777>](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
JRST PESFL1 ; Should never happen when the # of
; bytes requested is zero.
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
IFN FTGOL <
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:
IFN FTORC,<
IFE FTPARANOID <
JN ELORC,(EL),RTN ;TRY AGAIN NEXT JIFFY IF ANYTHING IN RTR
>
IFN FTPARANOID <
JE ELORC,(EL),CHKCL0 ;Proceed if nothing in RTR
OPSTRM <SOS T1,>,ELCLC,(EL) ;Count down retry count
JUMPN T1,RTN ;Just return and try next jiffy unless time for
;BUGCHK
OPSTR <XMOVEI T1,>,ELORQ,(EL) ;Get queue header of "lost" MBs
LOAD T1,QHBEG,(T1) ;Get first pointer
LOAD T2,ELORC,(EL) ;Get ORC count
SETZ T3, ;Default T3 in case MB pointer is zero
SKIPE T1 ;Is it?
LOAD T3,NMMAG,(T1) ;Get magical debug word
BUG.(CHK,LLIORQ,LLINKS,SOFT,<ORQ is non-empty at port close>,<<T1,MBADR>,<T2,ORCNT>,<T3,MAGIC>>,
<
This BUG. only appears in DEBUG monitors.
>)
RET ;Just return after BUGCHK. Will loop each jiffy
;and decrement ELCLC to greater negative #s
CHKCL0:
>;end of IFN FTPARANOID
>;end of IFN FTORC
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
RETSKP ;Should never get here
;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
CALL RELCIM ;Return any CI message still around
;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 for debugging purposes only and will not be present in
a production monitor.
>)
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
RET ; This should never fail when zero user bytes
; are requested, but if, just RET
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
CALL NSPRJF ; and request jiffy service to send any LK's
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
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
MOVE T1,[XCDSEC,,SCTL] ;Default session control 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
IFN FTGOL <
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
LOAD T1,ELNDB,(EL) ;Get pointer to node block
LOAD T2,NNPSZ,(T1) ;Get current pipe size
STOR T2,ESCWS,+EL.NSL(EL) ; and use that for initial window size
STOR T2,ESCWS,+EL.OSL(EL) ; for both normal and other sublink
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
XSWAPCD
INIHSH: SKIPG T1,NSPHTS ;GET THE HASH TABLE SIZE
BUG.(HLT,LLIHTS,LLINKS,SOFT,<NSPHTS not set up>,,<
Cause: The monitor has a bad value for the hash table size.
Action: Rebuild or patch the monitor with a positive value in NSPHTS.
>,RTN)
CALL DNGWDZ ;GET A HASH TABLE
BUG.(HLT,LLIHTG,LLINKS,SOFT,<INIHSH cant get a hash table>,,<
Cause: The routine that initializes the LLINKS link hash table failed to get
memory for the hash table. If the value for the hash table size is
reasonable, this should never fail.
Action: Check that the contents of NSPHTS is a reasonable value.
>,RTN)
MOVEM T1,NSPHTB ;STORE ADDRESS OF TABLE
RET ;DNGWDZ ZEROS WORDS DELIVERED
XRESCD
;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: RET ;Should never happen
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>,<<EL,ELPTR>>,<
Cause: Session control gave LLINKS a bad ID. This is a coding error
in SCLINK, or a memory manager problem.
Action: Inspect the stack to find out how the monitor got here.
Inspect the ELB to see if it otherwise looks like an ELB.
Data: ELPTR - Pointer to the bad ELB
>,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?
RET ; Tried to desroy non-DP port, should never
; happen.
TRACE NSP,Port Destroyed
LOAD T1,ELNDB,(EL) ;GET POINTER TO NSP NODE BLOCK
DECR NNLKC,(T1) ;DECREMENT LINK COUNT FOR THIS NODE
CALL CHKNOD ;Check if we need to remove node blocks
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: FREMSG was requested to free a message. However, the pointer
to the message block was zero. This is a coding error in LLINKS.
Action: Inspect the stack to find out which routine called FREMSG.
>,RTN)
TRASH MB, ;JUST BE CAREFUL
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.
XSWAPCD
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 DNGWZP ;GET ENOUGH WORDS FOR A PORT
RET ; -no memory, should seldom happen
;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 DNGWZP ;GET A NODE BLOCK
RET ; -should seldom heppen
MOVEM T1,NSPRND ;SAVE FOR GTRESP
RET
XRESCD
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
MOVE MB,T4 ;SET UP THE MESSAGE BLOCK POINTER
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&777777>>
;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 DCRORC - Decrement 'Out-in-router' count
;DCRORC - called when a message is returned from ROUTER
;
;Call: MB/ pointer to message block
; EL/ pointer to port block
; CALL DCRORC
; +1 return always
;Changes temporary ACs
DCRORC:
IFN FTPARANOID < ;Catch "ORQ" problem
RMVQUE MB,EL.ORQ(EL),NM.ORQ,T1 ;Take entry off "ORQ" queue
>
OPSTRM <SOS T1,>,ELORC,(EL) ;ONE LESS OUT-IN-ROUTER
JUMPGE T1,RTN ;JUMP IF NO BUG
BUG.(CHK,LLIORC,LLINKS,SOFT,<ORC should never be negative>,,<
Cause: LLINKS has requested that a message be returned from ROUTER
after transmission. ROUTER just returned such a message to
LLINKS, but the count of outstanding messages was zero.
Action: This is a difficult problem. If it persists, send a SPR
and include a dump of the system.
>)
SETZRO ELORC,(EL) ;WEAK ATTEMPT TO RECOVER
RET ; and return
SUBTTL BLDRCI - Build a (R)CI message
;BLDRCI - build a (R)CI message NSP header
;
;Call: T1/ MGFCI or MGFRCI
; MB/ Pointer to message block containing CI message
; EL/ Port pointer
; CALL BLDRCI
; Return
; Changes T1-T4
BLDRCI: PUSH P,T1 ;Save T1 over call to MAKHDR
CALL MAKHDR ;MAKE NSPHDR MSD, BYTE PTR IN P1, COUNT IN P2
POP P,T1 ;Retrieve message type
;MSGFLG
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.
RET ;CI/RCI message is now built
SUBTTL RELCIM - Release saved (R)CI message
;RELCIM - Release a saved (R)CI message
;
; Call: EL/ port block
; CALL RELCIM
; Return
;Changes T1-T4
ASSUME ELCIM,EQ,-1 ;Make sure fullword
RELCIM: OPSTR <SKIPN T1,>,ELCIM,(EL) ;Saved (R)CI message block?
RET ; -no
CALL DNFMSG ;-yes, return it
SETZRO ELCIM,(EL) ;Clear pointer to it
RET ; and return
SUBTTL Network management -- Dispatch
;ECLNMX is the entry point for all network management calls to LLINKS
;
; Call:
; T1/ function code
; T2/ NF block address
;
; Return:
; RET Network management error code in T1
; RETSKP Operation succeded
XSWAPCD
;Define dispatch table
DEFINE NF(nam),<
ASSUME <.-ECLFNT>,EQ,NF.'nam
IFIW <NMX'nam&777777>
>
ECLFNT: NF SET ;SET parameter
NF CLR ;CLEAR parameter
NF RED ;READ parameter
NF COU ;SHOW counters
NF SZC ;SHOW and ZERO counters
NF RET ;Return list of entity ids
NF A2N ;Map node address to node name
NF N2A ;Map node name to node address
NF CET ;Check entity id
NF CKL ;Check loopback name
ASSUME <.-ECLFNT-1>,EQ,NF.MAX ;Verify highest recognized entry
;Network management parameter table
NODPAR:
PARAMETER(^D600,PANST!PANCL,,,,JFCL,<LOAD T2,NNLKC,(P2)>,JFCL,<Active links>)
PARAMETER(^D601,PANST!PANCL,,,,JFCL,<LOAD T2,NNDLY,(P2)>,JFCL,<Delay>)
PARAMETER(^D700,PANST!PANCL,,,,JFCL,<CALL RED700>,JFCL,<NSP version>)
PARAMETER(^D710,PANST!PANCL,,,,JFCL,<MOVEI T2,^D65535>,JFCL,<Maximum links allowed>)
PARAMETER(^D720,,,1,%NSDLY,<MOVEM T2,NSPDLY>,<MOVE T2,NSPDLY>,<MOVEM T2,NSPDLY>,<Delay>)
PARAMETER(^D721,,,1,%NSWGT,<MOVEM T2,NSPWGT>,<MOVE T2,NSPWGT>,<MOVEM T2,NSPWGT>,<Delay weight>)
PARAMETER(^D722,,,1,%NSINA,<MOVEM T2,NSPINA>,<MOVE T2,NSPINA>,<MOVEM T2,NSPINA>,<Inactivity timer>)
PARAMETER(^D723,,,1,%NSRTH,<MOVEM T2,NSPRTH>,<MOVE T2,NSPRTH>,<MOVEM T2,NSPRTH>,<Retransmit factor>)
NRNPAR==.-NODPAR
;Network management counter table
;Remote and executor node counters
NODCTR:
COUNTER(^D0,^D16,<CALL [SAVEAC <T2> ;;Save T2 so we can divide into it
CALL DNGTIM ;;Get current time in T1
OPSTR <SUB T1,>,NNTLZ,(P2) ;;Subtract time when zeroed
IDIVI T1,TIMBAS ;;Make into seconds
RET ]>,<CALL [CALL DNGTIM ;;Get current time
STOR T1,NNTLZ,(P2) ;;Store as when zeroed
RET ]>,,<Seconds since last zeroed>)
COUNTER(^D600,^D32,<LOAD T1,NNRBC,(P2)>,<SETZRO NNRBC,(P2)>,,<User byt rcvd>)
COUNTER(^D601,^D32,<LOAD T1,NNXBC,(P2)>,<SETZRO NNXBC,(P2)>,,<User byt xmit>)
COUNTER(^D602,^D32,<LOAD T1,NNRMC,(P2)>,<SETZRO NNRMC,(P2)>,,<User msgs rcvd>)
COUNTER(^D603,^D32,<LOAD T1,NNXMC,(P2)>,<SETZRO NNXMC,(P2)>,,<User msgs xmit>)
COUNTER(^D608,^D32,<LOAD T1,NNTBR,(P2)>,<SETZRO NNTBR,(P2)>,,<Total byt rcvd>)
COUNTER(^D609,^D32,<LOAD T1,NNTBX,(P2)>,<SETZRO NNTBX,(P2)>,,<Total byt xmit>)
COUNTER(^D610,^D32,<LOAD T1,NNTMR,(P2)>,<SETZRO NNTMR,(P2)>,,<Total msgs rcvd>)
COUNTER(^D611,^D32,<LOAD T1,NNTMX,(P2)>,<SETZRO NNTMX,(P2)>,,<Total msgs xmit>)
COUNTER(^D620,^D16,<LOAD T1,NNRCC,(P2)>,<SETZRO NNRCC,(P2)>,,<Connects rcvd>)
COUNTER(^D621,^D16,<LOAD T1,NNXCC,(P2)>,<SETZRO NNXCC,(P2)>,,<Connects xmit>)
COUNTER(^D630,^D16,<LOAD T1,NNTMC,(P2)>,<SETZRO NNTMC,(P2)>,,<Resp timeout>)
COUNTER(^D640,^D16,<LOAD T1,NNCRC,(P2)>,<SETZRO NNCRC,(P2)>,,<Rcv conn res er>)
NRCCTR==.-NODCTR
;Executor node only counters
COUNTER(^D700,^D16,<LOAD T1,NNLKM,(P2)>,<SETZRO NNLKM,(P2)>,,<Max activ links>)
EXCCTR==.-NODCTR
INTERN ECLNMX ;Declare network management entry point
XSWAPCD
ECLNMX: CAIL T1,NF.SET ;Range check
CAILE T1,NF.MAX ; the function code
RNMXER (NF.UFO) ;"Unrecognized function or option"
SAVEAC <P1,P2> ;Save preserved ACs
MOVE P1,T2 ;Put NF block address in P1
JRST @ECLFNT(T1) ; and branch to processing routine
;Unsupported functions come here
NMXRET: ;Return list of items
NMXA2N: ;Map node address to node name
NMXN2A: ;Map node name to address
NMXCET: ;Check entity ID
NMXCKL: ;Check loopback node name
RNMXER (NF.MPE) ; Return "management program error"
SUBTTL Network management -- SET parameter
SUBTTL Network management -- READ parameter
SUBTTL Network management -- CLEAR parameter
;NMXSET - SET parameter
;NMXRED - READ parameter
;NMXCLR - CLEAR parameter
;
; Call:
; T1/ function code
; P1/ NF block
; NFEID/ node address
; NFETY/ .NTNOD
; NFBFF/ clear
; NFBUF/ parameter value
NMXSET:
NMXRED:
NMXCLR:
MOVE P2,T1 ;Save function code for a while
LOAD T1,NFETY,(P1) ;Verify entity type
CAIE T1,.NTNOD ; is NODE
RNMXER (NF.URC) ; -no, unrecognized component
LOAD T1,NFEID,(P1) ;Get entity ID
CALL SRHNOD ;Locate node
RNMXND ; -not there, return OK but no data
MOVE T3,P2 ;Function code to T3
MOVE P2,T1 ;Node block pointer to P2
XMOVEI T1,NODPAR ;Load node table
MOVEI T2,NRNPAR ; and its length
CALLRET NTPARM ; and let D36COM do most of the job
;RED700 - NODE parameter 700 (NSP version)
RED700: SETZ T2, ;Clear return value
MOVEI T3,.NSVER ;Get NSP version
CAIE T3,VER3.2 ;Is it 3.2?
IFSKP. ; -yes
MOVEI T3,3 ; 3.
MOVEI T4,2 ; 2
ELSE. ; -no,
CAIE T3,VER4.0 ; Is it 4.0?
IFSKP. ; -yes,
MOVEI T3,4 ; 4.
SETZ T4, ; 0
ELSE. ; -no,
SETZB T3,T4 ; then unknown
ENDIF.
ENDIF.
STOR T3,VNVER,+T2 ;Store version #
STOR T4,VNECO,+T2 ; and ECO #
RNMXOK ; and return success
SUBTTL Network management -- SHOW counters
SUBTTL Network management -- SHOW and ZERO counters
;NMXCOU - SHOW counters
;NMXSZC - SHOW and ZERO counters
;
; Call:
; T1/ function code
; P1/ NF block
; NFEID/ entity ID (16-bit node address)
; NFETY/ .NTNOD
; NFBFF/ set
; NFBUF/ pointer to buffer
NMXCOU:
NMXSZC: MOVE P2,T1 ;Save function code for a while
LOAD T1,NFETY,(P1) ;Get entity type
CAIE T1,.NTNOD ;NODE?
RNMXER (NF.URC) ; -no, unrecognized component
LOAD T1,NFEID,(P1) ;Get entity ID
CALL SRHNOD ;Locate node
RNMXND ; -not there, return OK but no data
MOVE T3,P2 ;Function code to T3
MOVE P2,T1 ;Node block pointer to P2
XMOVEI T1,NODCTR ;Load node counter table
LOAD T2,NFEID,(P1) ;Get node ID again
OPSTR <CAME T2,>,IBADR,+IBBLK ;Is it executor?
IFNSK. ; -no, its a remote node
MOVEI T2,NRCCTR ; then get length without executor part
ELSE. ; -yes, its executor
MOVEI T2,EXCCTR ; so use full length
ENDIF.
CALLRET NTCTRS ; and let D36COM do the job
SUBTTL Node data base -- Find an NSP node block
;FNDNOD - Find an NMX Node Block, if fail, make a new one
;
;Call: T1/ Network Node Address of node
; CALL FNDNOD
; Error Return if no resources
; Normal Return with pointer to node block in T1
;Changes T1,T2,T3,T4
;Note that the new node block goes on the end of the queue of node
;blocks. This causes the blocks which we hear about soonest to be at
;the beginning of the queue. These are the nodes we are most likely
;to converse with, so having them at the beginning of the queue is a
;convenience for DDT if nothing else.
;This routine is called from LLINKS and is therefore resident
XRESCD
FNDNOD: SAVEAC P1
MOVE P1,T1 ;SAVE THE ARG
CALL SRHNOD ;FIRST, SEARCH FOR AN EXISTING ONE
JRST MAKND1 ;CAN'T FIND IT, TRY TO MAKE ONE
RETSKP ;FOUND IT, SUCCESS RETURN
MAKND1: MOVEI T1,NN.LEN ;LENGTH OF A NMX NODE BLOCK
CALL DNGWDZ ;ZERO SO MANY ZEROED WORDS
RET ;CAN'T, ERROR RETURN
STOR P1,NNNOD,(T1) ;STORE NODE ID IN NEW NODE BLOCK
MOVE P1,T1 ;SAVE ADDRESS OF NODE BLOCK
CALL DNGTIM ;GET A TIMESTAMP
STOR T1,NNSLZ,(P1) ;STORE AS LAST TIME BLOCK WAS ZEROED
ENDQUE P1,NMXNDQ,NN.NXT,T2
;Some initialization for NSP
MOVEI T2,TIMBAS ;MACRO/LINK CAN'T MULTIPLY EXTERNAL RIGHT
IMULI T2,5 ;ESTIMATE 5 SECOND FOR INITIAL DELAY
STOR T2,NNDLY,(P1) ;ZERO IS NOT GOOD FOR CONNECT CONFIRM!
;SEE UPDELAY ABOUT THIS
MOVE T1,NSPPSZ ;Initial pipesize = minimum 'maximum' pipe
; size (* be optimistic *)
STOR T1,NNPSZ,(P1) ;Store it
CALL CHKNOD ;Check if we need to prune the node block queue
MOVE T1,P1 ;RETURN PTR TO BLOCK IN T1
RETSKP
SUBTTL Node data base -- Find an existing NSP node block
;SRHNOD - Search for an existing NMX Node Block
;
;Call: T1/ Network Node Id
; CALL SRHNOD
; Error Return if not found
; Normal Return with pointer to it in T1
;Changes T1,T2,T3
;This must save T5 and T6 if it evers changes them, since SCMUUO calls it.
;Still resident...
IFN FTOPS10,<
INTERNAL SRHNOD
>; END IFN FTOPS10
SRHNOD: MOVE T2,T1 ;COPY NODE ID TO T2
LOAD T1,QHBEG,+NMXNDQ ;GET HEAD OF NMX NODES LIST
FNDND1: JUMPE T1,RTN ;LIST IS EMPTY, ERROR RETURN
OPSTR <CAMN T2,>,NNNOD,(T1) ;IS THIS THE NODE WE'RE LOOKING FOR?
RETSKP ;YES, SUCCESS RET WITH PTR IN T1
LOAD T1,QPNXT,+NN.NXT(T1) ;NO, GET THE NEXT ENTRY IN LIST
JRST FNDND1 ;AND TRY IT
SUBTTL Node data base -- Check if we need to delete node blocks
;CHKNOD - check if we need to prune the node block queue
;
; Call: CALL CHKNOD
;
; Return:
; +1 always
CHKNOD: LOAD T1,QHCNT,+NMXNDQ ;Get current queue length
CAMGE T1,NNDMAX ;On or over quota?
RET ; -no, no need to do anything
SETOM NODFLG ;-yes, flag that we need to prune queue
RET
;End of resident code
XRESCD ;End of network management
SUBTTL Node data base -- Release node blocks
;RELNOD - release node blocks if necessary
;
; RELNOD runs as part of the CHKR cycle, i.e. in process context approximately
; every 10 seconds. It will check if the node block queue needs to be pruned.
;
; Call:
; CALL RELNOD
;
; Returns:
; +1 always
XSWAPCD
RELNOD: SKIPN NODFLG ;Do we need to do anything?
RET ; -no, return immediately
SETZM NODFLG ;No longer needed
;Here to do processing
SAVEAC <T6,P1,MB>
IFN FTOPS20,< CSKED ;Critical section and NOINT>
XMOVEI T6,RE1NOD ;Address of processing routine
CALL NSPLCW ;Get the NSP interlock and process request
IFN FTOPS20,< ECSKED ;Exit critical section and OKINT>
RET
;RE1NOD does the actual processing.
; Come here with NSP interlock
RE1NOD: LOAD P1,QHBEG,+NMXNDQ ;Point to first block on queue
DO. ;LOOP over all node blocks on queue
LOAD T1,QHCNT,+NMXNDQ ; Get current count of nodes queued
CAMGE T1,NNDMAX ; On or over quota?
RET ; -no, just return
SKIPE P1 ; Null pointer?
IFSKP. ; -yes, we have scanned the entire queue
SETOM NODFLG ; without pruning enough so set NODFLG
RET ; so we run another pass next time
ENDIF.
OPSTR <SKIPE>,NNLKC,(P1) ;Any active links to this node?
IFSKP. ; -no, interesting!
LOAD T1,NNNOD,(P1) ; Get node ID
OPSTR <CAMN T1,>,IBADR,+IBBLK ;Is it executor?
ANSKP. ; -no, go ahead and delete it
CALL GENE32 ; Generate event 3.2
RMVQUE P1,NMXNDQ,NN.NXT,T2 ;Remove it from the queue
MOVE T1,P1 ; Get pointer for DNFWDS
LOAD P1,QPNXT,+NN.NXT(P1) ;Pick up pointer to next node
CALL DNFWDS ; Now OK to delete
LOOP. ; Go to next node
ENDIF.
LOAD P1,QPNXT,+NN.NXT(P1) ; Go to next node block
LOOP. ; Loop back to do next node block
ENDDO.
;Can never get here
;GENE32 - generate an event 3.2 "Database reused"
;
; Call: P1/ address of node block
; CALL GENE32
;
; Returns:
; +1 always
GENE32: MOVE MB,P1 ;Event logger wants node block ptr in MB
EVENT (NSP,DAT,<Database reused>,)
RET
;NEVT.2 - Database reused. The parameters for this event are all
; the NSP node counters that are flushed from the monitor.
;
; Call:
; MB/ ptr to node block
; P2/ byte pointer to data area
; CALL NEVT.2
;
; Return:
; RET always, with T5/ # of data bytes written
NEVT.2:
;** Note **
; This TRVAR must match the TRVAR in NTMAN at the .NTMAN jsys entry point
TRVAR <<NMXVAR,NX.LST>,<NFWBLK,NF.LST>> ;Set up trvar
;** End note **
;Clear the NMXVAR block
SETZM NMXVAR
HRRI T2,1+NMXVAR
HRLI T2,NMXVAR
BLT T2,NX.LST-1+NMXVAR
;Clear the NFWBLK block
SETZM NFWBLK
HRRI T2,1+NFWBLK
HRLI T2,NFWBLK
BLT T2,NF.LST-1+NFWBLK
;Set up the NMXVAR block to read and format the counters for the
; node that we have decided to purge
ASSUME NX.MCX,EQ,NX.WUS ;Make sure the two flags are in the same word
SETONE <NXMCX,NXWUS>,+NMXVAR ;Set 'monitor context' and 'write user'
ASSUME .NTNOD,EQ,0
SETZRO NXENT,+NMXVAR ;NODE entity
LOAD T1,NNNOD,(MB) ;Get the node id (node address)
STOR T1,NXNUM,+NMXVAR
MOVEI T1,.NTSHO ;Function is 'show'
STOR T1,NXFNC,+NMXVAR
XMOVEI T2,NX.DAT+NMXVAR ;Get pointer to byte pointer/count pair
MOVEI T1,EVTDLN ;Get max event data length
STOR T1,BPBYT,(T2) ;Store that
STOR P2,BPBPT,(T2) ; and byte pointer
CALL PRSCOU ;Call into NTMAN
IFNSK. ; -fail return
SETZ T5, ; Indicate no bytes written
RET ; and return
ENDIF.
XMOVEI T2,NX.DAT+NMXVAR ;Get pointer to byte pointer/count pair again
MOVEI T5,EVTDLN ;Get max # of bytes
OPSTR <SUB T5,>,BPBYT,(T2) ; and subtract # of bytes left
RET ; gives # of bytes written.
XRESCD
SUBTTL NSPJB0 - NSP periodic checks
;NSPJB0 - called from DCNJB0 (that is called from CHKR) every 10 seconds.
;
;Call: CALL NSPJB0
; Always +1 return
INTERNAL NSPJB0
XSWAPCD
NSPJB0:
IFN FTOPS20,<
CALL LNKBRK ;Generate 'link broken' messages if necessary
>; END IFN FTOPS20
CALLRET RELNOD ;Release node blocks if needed
IFN FTOPS20,<
SUBTTL Link broke message - Job 0 code
;LNKBRK is called as part of the CHKR cycle. It generates link broken
; message to the operator if needed
BRKMLN==<^D240+4>/5 ;# of words in message
LNKBRK: SKIPN BRKFLG ;Any broken links?
RET ; -no, all done
SETZM BRKFLG ;Clear the flag
SAVEAC <P1,P2,P3>
;The operator message is built into OPRMSG. MSGSND is a flag that is set
; non-zero if we find any nodes to report. This is to exclude the possibility
; of sending an empty report
TRVAR <<OPRMSG,BRKMLN>,MSGSND,<QUEARG,^D10>>
SETZM MSGSND ;Initialize
;P1 will contain a byte pointer to the message string
XMOVEI P1,OPRMSG ;Get address of it
TXO P1,.P0736 ; and make a byte pointer of it
MOVE T1,[POINT 7,[ASCIZ /
Communication failure to the following nodes:
/]]
CALL MOVSTR ;Copy the string to the message buffer
;Scan the link broken table to find nodes to report. By scanning backwards
; we dont have any interlocking problems with INSBRK.
XMOVEI P2,<BRKTAB+BRKLEN-1> ;Point to last entry in table
MOVEI P3,BRKLEN ; and load count
DO. ;LOOP over link broken table
SETZ T1, ; Prepare for EXCH
EXCH T1,(P2) ; Zero table entry, and get contents
SKIPN T1 ; Is there an entry?
IFSKP. ; - yes,
CALL SCTA2N ; Find node and convert it to sixbit
ANSKP. ; - yes, node was there
SETOM MSGSND ; Flag that we should send a message
MOVE T2,P1 ; Byte pointer to T2
XCALL (MSEC1,GETSIX) ; and call GETSIX to write sixbit string
MOVE P1,T2 ; Get byte pointer back
MOVEI T1,.CHTAB ; Get a TAB
IDPB T1,P1 ; and output it
ENDIF.
SOS P2 ; Back up pointer one step
SOJG P3,TOP. ; Loop back if more entries to do
ENDDO.
SKIPN MSGSND ;Did we find any nodes?
RET ; -no, just return
SKIPN MORFLG ;Did we lose any nodes before?
IFSKP. ; -yes,
MOVE T1,[POINT 7,[ASCIZ /and more../]]
CALL MOVSTR ; Output that
SETZM MORFLG ; Clear flag
ENDIF.
MOVE T1,[POINT 7,[ASCIZ /
/]] ;And a CR
CALL MOVSTR ; is always appreciated
SETZ T1, ;End with a NULL byte
IDPB T1,P1 ; so put it
;The message is now built in OPRMSG. Set up the argument for the QUEUE
; jsys and go..
;The QUEUE argument block has the following layout
;
; Word 0 QU%NRS+.QUWTO No response, function is WTOPR
; 1 0 No response pointer
; 2 Length+.QBTYP Privileged WTOPR
; 3 Address of ASCIZ string (header)
; 4 Length+.QBMSG Unprivileged WTOPR
; 5 Address of ASCIZ string (message)
; 6 QA%IMM+.QBDTY Display type
; 7 .QBDLK DECnet 'link broken' display type
; 8 QA%IMM+.QBDFG Formatting flags
; 9 QU%SJI!QU%NFO Suppress job info and no formatting
MOVX T1,QU%NRS!.QUWTO
MOVEM T1,.QUFNC+QUEARG ;Store in argument block
SETZM .QURSP+QUEARG
MOVX T1,FLD(5,QA%LEN)!FLD(.QBTYP,QA%TYP)
MOVEM T1,2+QUEARG ;*Numeric offsets*
XMOVEI T1,[ASCIZ /DECnet link message/]
MOVEM T1,3+QUEARG
MOVX T1,FLD(BRKMLN,QA%LEN)!FLD(.QBMSG,QA%TYP)
MOVEM T1,4+QUEARG ;*Numeric offsets*
XMOVEI T1,OPRMSG ;Get address of ASCIZ string
MOVEM T1,5+QUEARG
MOVX T1,QA%IMM!FLD(1,QA%LEN)!FLD(.QBDTY,QA%TYP)
MOVEM T1,6+QUEARG
MOVX T1,.QBDLK ;DECnet 'link broken' display type
MOVEM T1,7+QUEARG
MOVX T1,QA%IMM!FLD(1,QA%LEN)!FLD(.QBDFG,QA%TYP)
MOVEM T1,^D8+QUEARG
MOVX T1,QU%SJI!QU%NFO ;Formatting flags
MOVEM T1,^D9+QUEARG
MOVEI T1,^D10 ;10 words in argument block
XMOVEI T2,QUEARG ; and get address
QUEUE% ;Shoot!
ERJMP .+1 ;Ignore errors
RET
;MOVSTR - move a string to the message buffer
MOVSTR: DO. ;LOOP
ILDB T2,T1 ; Get a byte
JUMPE T2,RTN ; Return if null byte
IDPB T2,P1 ; Put a byte
LOOP. ; And go to next
ENDDO.
;Can never get here
> ;END IFN FTOPS20
SUBTTL Link broke message - Add a node to table
;INSBRK is called when a link broken event is detected. It adds
; the node to the 'link broken table'.
;
; Call:
; EL set up
; CALL INSBRK
;
; Returns:
; RET always
XRESCD ;Can be called from any context
INSBRK: LOAD T1,ELNDB,(EL) ;Get pointer to node block
TMNE NNMSG,(T1) ;Did we already send a message for this node?
RET ; -yes, nothing to do
SETONE NNMSG,(T1) ;No, but we are, so set the flag
LOAD T1,ELNNM,(EL) ;Get the node number
MOVE T2,[-BRKLEN,,BRKTAB] ;Get AOBJN ptr to the table
DO. ;LOOP over all entries
SKIPE (T2) ; Is this entry used?
IFSKP. ; -no,
MOVEM T1,(T2) ; save the node number
SETOM BRKFLG ; and set the flag so its processed
RET ; and all is done
ENDIF. ;
;This test will prevent race conditions - normally this test is
; superfluous since we have already tested the MSG flag but if the
; remote came up between the test and here, then we need the test.
CAMN T1,(T2) ; This node present in table already?
RET ; -yes, no need to do any more
AOBJN T2,TOP. ; Loop back to do next node
ENDDO.
SETOM MORFLG ;Table is full, remember overflow
SETOM BRKFLG ; and make code take a pass
RET
SUBTTL EVTINI - Initialize event logger interface
;EVTINI - Initialize event logger interface
;
;Call: CALL EVTINI
; Normal return
;Changes T1 and T2
XSWAPCD
EVTINI: MOVX T1,EV.GEC ;Get EC (Event Communication) block
CALL NMXEVT
RET ; -no memory, should seldom happen
MOVEM T1,NSPECP ;Save pointer to EC block
MOVX T2,2 ;No more than 2 events on queue for NSP
STOR T2,ECMAX,(T1)
RET
XRESCD
SUBTTL NSPEVT - Queue an Event to Network Management
;NSPEVT - Queue an Event to Network Management
;
;Call:
;IFN FTUSERMODE, T1/ event class, T2/ event 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.
;SCLINK and LLINKS in TOPS-20 6.1 will generate the following two events:
; 3.0 Invalid message
; 3.1 Invalid flow control
; 3.2 Database reused
EVTDLN==^D120 ;MAX LENGTH OF NSP HEADER DATA
INTERN NSPEVT
XRESCD
NSPEVT:
;EVENT MACRO SAVES NECESSARY ACS
DMOVE T5,T1 ;Save event class and type
STOR T5,FACCL,+T2 ;Event class and type goes in T2
STOR T6,FACTY,+T2
MOVX T1,EV.FIL ;Function code 'filter event'
CALL NMXEVT ;Apply global filters in NTMAN
RET ; -throw away, no more to do
;Check that the event communication pointer is non-zero
SKIPN NSPECP ;Is it?
RET ; -no, we probably got a BUGCHK at init time
; (or someone trashed the word...)
;Now allocate the event block
MOVX T1,NE.LEN+<<EVTDLN+3>/4> ;EVENT ARG BLK + SPACE FOR DATA
CALL DNGWDZ ;GET AN ARG BLK FOR NETWORK MGMT
RET ; -couldnt get memory, just go away
MOVE P1,T1 ;POINTER TO ARG BLOCK
;Fill in the arg block we just got for Network Management
MOVE T1,NSPECP ;The EC pointer
STOR T1,NEECP,(P1)
STOR T5,NECCL,(P1) ;Store the event class
STOR T6,NECTY,(P1) ; and type
SETONE NEETP,(P1) ;Assume 'no entity'
SETZRO NEEID,(P1) ; and therefore 'no entity id'
CAIN T5,.NCNSP ;Is it 'NSP layer'
CAIE T6,EVTDAT ; and 'Database reused'
IFSKP. ; -yes,
ASSUME .NTNOD,EQ,0 ; Verify 'node entity' is zero
SETZRO NEETP,(P1) ; so we can use SETZRO!
LOAD T1,NNNOD,(MB) ; Get node ID
STOR T1,NEEID,(P1) ; and store as entity ID
ENDIF.
;T5 will be count of bytes written, P2 byte ptr
; Move event class and type to T1/T2
DMOVE T1,T5 ; ...
SETZ T5, ;No bytes written yet
XMOVEI P2,NE.LEN(P1) ;Make byte pointer
TXO P2,.P0836 ;Global, 8-bit bytes, point to 1st byte
;Select processing routine depending on event class and type
CAIE T1,.NCNSP ;NSP event?
IFSKP.
SKIPL T2 ; Verify event is between 0
CAILE T2,EVTDAT ; and EVTDAT
JRST BADEVT ; -no, bad event
CALL @[ IFIW <NEVT.0&777777> ; Type 0 - Invalid message header
IFIW <NEVT.1&777777> ; 1 - Invalid flow control
IFIW <NEVT.2&777777>](T2) ; 2 - Database reused
ELSE. ;Not NSP event
CAIN T1,.NCLCG ; then is it LCG event?
SKIPE T2 ; and event type 0
JRST BADEVT ; -no to one or both, go complain
CALL LEVT.0 ; -ok, go process event
ENDIF.
STOR T5,NEDLN,(P1) ;Store count of data bytes
XMOVEI T1,NE.LEN(P1) ;Get pointer to data area
STOR T1,NEDAT,(P1) ; and store it in NE block
MOVX T1,EV.LOG ;Function code "log an event"
MOVE T2,P1 ;NE block pointer
CALL NMXEVT ;TELL NMX ABOUT THE EVENT
JFCL ;Always skip returns
RET ;All done, NTMAN will deallocate block
;BADEVT - bad event class and/or type - bugcheck
BADEVT: BUG.(CHK,LLITNE,LLINKS,SOFT,<Unknown event at NSPEVT>,<<T1,EVC>,<T2,EVT>>,<
Cause: The caller of the NSPEVT routine supplied a bad event class and type.
NSPEVT may be called by SCLINK as well as by LLINKS. The caller's
address is on the stack.
Data: EVC - Event class
EVT - Event type
>)
MOVE T1,P1 ;Get address of NE block that was allocated
CALLRET DNFWDS ;Deallocate and return
;NEVT.0 - Invalid message event
NEVT.0: CALL NEP.0 ;Do parameter 0 (MESSAGE)
CALLRET NEP.2 ;Do parameter 2 (SOURCE NODE)
;NEVT.1 - Invalid flow control event
NEVT.1: CALL NEVT.0 ;Do MESSAGE and SOURCE NODE
CALLRET NEP.1 ;Do parameter 1 (CURRENT FLOW REQUEST COUNT)
;NEP.0 - do parameter 0 (MESSAGE)
;
; I assume that the incoming message is at least 6 bytes long, i.e. that it
; contains MSGFLG DSTADDR SRCADDR and at least one more byte. I also assume
; implicitly that the MSGFLG field is exactly one byte long. This is to
; simplify the generation of the event. If any added functionality is required
; in future, you have my blessing....
NEP.0: STKVAR <COUNT>
LOAD T1,NMMK1,(MB) ;Get NSP's mark 1 for this input MSD
CALL DNGPOS ;Go there
XMOVEI T1,IN.MSD(MB) ;Pointer to MSD we are interested in
CALL DNSLNG ;How many bytes are there?
SUBI T1,5 ;Subtract 5 [MSGFLG DSTADDR SRCADDR]
JUMPLE T1,RTN ;If nothing more than that, then return
CAILE T1,10 ;Max 10 bytes more
MOVEI T1,10 ; ..
MOVEM T1,COUNT ;Count of 'data bytes'
SETZ T1, ;Parameter 0
CALL EVP2BT ;Output..
MOVEI T1,304 ;CM-4
CALL EVPBYT
MOVEI T1,041 ;H-1
CALL EVPBYT
CALL DNGEBY ;Get extensible byte (really only one byte)
JFCL ; Shouldnt happen
CALL EVPBYT
MOVEI T1,002 ;DU-2
CALL EVPBYT
CALL DNG2BY ;Get DSTADDR
JFCL
CALL EVP2BT
MOVEI T1,002 ;DU-2
CALL EVPBYT
CALL DNG2BY ;Get SRCADDR
JFCL
CALL EVP2BT
MOVEI T1,040 ;HI-10
CALL EVPBYT
MOVE T1,COUNT ;Get count of bytes to do
CALL EVPBYT
DO.
CALL DNG1BY ; Get a byte
JFCL
CALL EVPBYT ; and output it
SOSLE COUNT ; Any more bytes to do?
LOOP. ; -yes, go do them
ENDDO.
RET ;All done, return
;NEP.1 - do parameter 1 (CURRENT FLOW REQUEST COUNT)
NEP.1: MOVEI T1,1 ;Parameter 1
CALL EVP2BT ;Output it
MOVEI T1,021 ;DS-1
CALL EVPBYT ;Output it
LOAD T1,ESRFL,+EL.NSL(EL) ;Get flow request count
CALLRET EVPBYT ;Output it and return
;NEP.2 - do parameter 2 (SOURCE NODE)
NEP.2: MOVEI T1,2 ;Parameter 2
CALL EVP2BT ;Output it
MOVEI T1,301 ;CM-1
CALL EVPBYT
MOVEI T1,002 ;DU-2
CALL EVPBYT
LOAD T1,MBSRC,(MB) ;Get source node
CALLRET EVP2BT ;Put 2 bytes
;EVP2BT - write two bytes in pdp-11 order
EVP2BT: CALL EVPBYT
LSH T1,-^D8
CALLRET EVPBYT
;EVPBYT - write a byte, and update count
EVPBYT: IDPB T1,P2 ;Write the byte
AOJA T5,RTN ; and up the count and return
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
IFN FTOPS10,<
RESDT
NSPLOW::!
XRESCD
>; END IFN FTOPS10
END