mirror of
https://github.com/PDP-10/stacken.git
synced 2026-01-31 13:52:00 +00:00
6571 lines
225 KiB
Plaintext
6571 lines
225 KiB
Plaintext
;TITLE LLINKS - Network Services Layer of Phase III DECnet V047
|
||
|
||
|
||
;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,1988.
|
||
;ALL RIGHTS RESERVED.
|
||
|
||
|
||
SUBTTL W. G. Nichols
|
||
|
||
SEARCH D36PAR,SCPAR,MACSYM
|
||
|
||
IFN FTOPS10,<
|
||
.CPYRT<1976,1988>
|
||
> ;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,ESNAK,ESACK>,(ES) ;TELL CHKDRQ THAT 'OFF' HAS CHANGED
|
||
; AND SEND AN ACK TO RESTART REMOTE NODE
|
||
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
|