;TITLE LLINKS - Network Services Layer of Phase III DECnet V046 ;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED ;OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE. ; ;COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1976, 1985, 1986. ;ALL RIGHTS RESERVED. SUBTTL W. G. Nichols SEARCH D36PAR,SCPAR,MACSYM IFN FTOPS10,< .CPYRT<1976,1986> > ;END IFN FTOPS10 ENTRY NSP,NSPRTR,NSPINI,NSPJIF ;The entry points, ENTRY NSPCG,NSPCR ; see procedures for calling sequence SALL ; The NSP link block definitions are in SCPAR so that they will ; be available to DCNSPY and similar reporting programs. FTORC==FTDEBUG ;Hang until all output dones return if FTORC=1 FTTROLL==0 ;GIVE A NAME TO TROLL CODE FTGOL==0 ;Assemble goal code conditionally FTBFR==0 ;Assemble buffer-rich code conditionally IFN FTOPS10,< TITLE LLINKS - Network Services Layer of Phase IV DECnet SEARCH F,S $RELOC ;Set up code and data segments according .NTSHO==3 ;define SHOW 'cuz we don't search UUOSYM > IFN FTOPS20,< SEARCH PROLOG TTITLE (LLINKS,,< - Network Services Layer of Phase IV DECnet>) > D36SYM ;Fix up problems with MACSYM in DECnet-36 IFN FTTRACE,< PRINTX FTTRACE On: Note that TRACE code has not been updated PRINTX FTTRACE to deal with the 1-word global bytepointers > XRESCD ; to FTOPSn0, macros defined in D36PAR ;This module implements the NSP layer of Phase III DECnet. It is the ;system independent logical link service. Below NSP is the Router ;layer, which handles node-to-node communication. Above NSP is the ;Session Control layer, which provides the interface between the ;logical links of NSP and the user program. ; ;The interfaces between NSP and its neighbors are via call vectors, so ;that by changing the base of the call vector, a logical link may use ;a different module at the NSP level of the DECnet hierarchy. Eg, a ;link may want to use APRAnet datagram service instead of DECnet ;logical link service. SUBTTL Table of Contents ; Table of Contents for LLINKS ; ; ; Section Page ; 1. Table of Contents. . . . . . . . . . . . . . . . . . . 2 ; 2. Register Definitions . . . . . . . . . . . . . . . . . 3 ; 3. EXTERN Declarations. . . . . . . . . . . . . . . . . . 4 ; 4. NSP Network Protocol Definitions . . . . . . . . . . . 11 ; 5. Code-Generating Macros . . . . . . . . . . . . . . . . 13 ; 6. NSP Node Block Definition. . . . . . . . . . . . . . . 15 ; 7. Resident Storage Allocation. . . . . . . . . . . . . . 16 ; 8. NSP's Entry Vector Table . . . . . . . . . . . . . . . 18 ; 9. NSPRCV - Receive a Message from Router . . . . . . . . 19 ; 10. NSPRTR - The Entry Point from Router . . . . . . . . . 21 ; 11. NSPINI - Initialization Entry Point. . . . . . . . . . 22 ; 12. NSP Interlock Handlers ; 12.1. NSPLCQ - Queue on Failure . . . . . . . . . . 23 ; 12.2. NSPLCW - Wait on Failure. . . . . . . . . . . 24 ; 12.3. NSPLCF - Flags set for Failure. . . . . . . . 25 ; 13. Session Control Calls ; 13.1. NSP - The Entry Point . . . . . . . . . . . . 27 ; 13.2. NSPOPN - Open a Link. . . . . . . . . . . . . 28 ; 13.3. NSPGOL - Set New Data Request Goals . . . . . 30 ; 13.4. NSPACT - Enter Active . . . . . . . . . . . . 31 ; 13.5. NSPACC - Accept Connect . . . . . . . . . . . 32 ; 13.6. NSPSEG - Send Data. . . . . . . . . . . . . . 34 ; 13.7. NSPDRQ - Data Request Call. . . . . . . . . . 36 ; 13.8. NSPREJ - Reject a Connection. . . . . . . . . 37 ; 13.9. NSPDSC - Synch Disconnect . . . . . . . . . . 39 ; 13.10. NSPABO - Abort Link . . . . . . . . . . . . . 40 ; 13.11. NSPCLS - Close Port . . . . . . . . . . . . . 41 ; 14. CLSABO - Send a free "Abort by Object" . . . . . . . . 42 ; 15. PROCXQ - Process the Transmit Queue. . . . . . . . . . 43 ; 16. SCNIRS - Tell Session Control Port is Not in Run State 46 ; 17. SNDRTR - Send a Message to Router. . . . . . . . . . . 47 ; 18. ROUTER Calls ; 18.1. NSPRCV - Receive a Message from Router. . . . 49 ; 18.2. NSPODN/NSPOND - Output Done/Not Done. . . . . 50 ; 18.3. NSPRTS - Return a Message to Sender . . . . . 51 ; 18.4. NSPRFR - Return a Message to Sender from Remote 51 ; 19. Message Receivers ; 19.1. RCVPRC - Preliminary Processing . . . . . . . 52 ; 19.2. PRCACK - Process ACKNUM/ACKOTH Field. . . . . 54 ; 19.3. Receive a Data Segment. . . . . . . . . . . . 56 ; 19.3.1. Check Length of Q. . . . . . . . . . 59 ; 19.4. PROCRQ - Process the Receive Queue. . . . . . 60 ; 19.5. RCVLKS - Receive a Link Service Message . . . 62 ; 19.6. RCVACK and RCVNOP - Little Ones . . . . . . . 67 ; 19.7. RCVCA - Receive a Connect ACK Message . . . . 68 ; 19.8. RCVCI - Receive a Connect Initiate Request. . 69 ; 19.9. RCVCC - Receive a Connect Confirm Message . . 71 ; 19.10. RCVDI - Disconnect Initiate Message . . . . . 73 ; 19.11. RCVDC - Disconnect Confirm Message. . . . . . 75 ; 20. Clock-Driven Routines. . . . . . . . . . . . . . . . . 77 ; 21. CLCXDQ - Calculate Transmit Data Requests. . . . . . . 82 ; 22. NSPRJF - Request Jiffy Service . . . . . . . . . . . . 85 ; 23. SECCHK - Once-a-Second Checks. . . . . . . . . . . . . 86 ; 24. Memory Manager Calls ; 24.1. NSPCG - We're Congested . . . . . . . . . . . 92 ; 24.2. NSPCR - Congestion is Relieved. . . . . . . . 93 ; 25. NSIRLV - Congestion-Relieved Processor . . . . . . . . 94 ; 26. PESFLO - Put Link in Pessimistic Flow Control. . . . . 95 ; 27. SCLOSE - Start the Close Process . . . . . . . . . . . 97 ; 28. SENDRQ - Send a Link Service Message . . . . . . . . . 104 ; 29. SACKMG - Get a Message Block and send ACK message if can 106 ; 30. SNDACK - Check a sublink to see if it needs an ACK sent 107 ; 31. SENDDI - Send a DI Message . . . . . . . . . . . . . . 108 ; 32. SNDCAK - Send a Connect ACK Message. . . . . . . . . . 109 ; 33. SNDDSC - Send a Disconnect Complete Message. . . . . . 110 ; 34. MGACKD - A Message has been ACKed. . . . . . . . . . . 111 ; 35. SENDNL - Send a No Link Message. . . . . . . . . . . . 112 ; 36. PTIRUN - Put a Port in Run State . . . . . . . . . . . 114 ; 37. MAKPRT - Make a New Port Block . . . . . . . . . . . . 115 ; 38. GTRESP - Get a Reserved Port . . . . . . . . . . . . . 116 ; 39. MAKPRS - Make this Port Reserved . . . . . . . . . . . 117 ; 40. SETPRT - Make a New Port Block . . . . . . . . . . . . 118 ; 41. FNDPRT - Hash an LLA to Find a Port Block. . . . . . . 119 ; 42. Hash Table Routines. . . . . . . . . . . . . . . . . . 120 ; 43. NEWLLA - Make a New Local Link Address . . . . . . . . 123 ; 44. NEWSGN - Make a New Message Segment Number . . . . . . 124 ; 45. NSPpid Control - GETPID & FNDPID . . . . . . . . . . . 125 ; 46. DSTPRT - Destroy a Port. . . . . . . . . . . . . . . . 127 ; 47. QSRTMB - Queue a Message on a Sorted Queue . . . . . . 128 ; 48. MAKHDR - Initialize the NSP MSD to build a Message Header 130 ; 49. UPDELAY - Update a Logical Link's Roundtrip Delay. . . 131 ; 50. Free a Message Block . . . . . . . . . . . . . . . . . 132 ; 51. Reserved Port Management . . . . . . . . . . . . . . . 133 ; 52. Reserved Port Process. . . . . . . . . . . . . . . . . 134 ; 53. DCRORC - Decrement 'Out-in-router' count . . . . . . . 137 ; 54. BLDRCI - Build a (R)CI message . . . . . . . . . . . . 138 ; 55. RELCIM - Release saved (R)CI message . . . . . . . . . 139 ; 56. Network management ; 56.1. Dispatch. . . . . . . . . . . . . . . . . . . 140 ; 56.2. SET parameter . . . . . . . . . . . . . . . . 141 ; 56.3. READ parameter. . . . . . . . . . . . . . . . 141 ; 56.4. CLEAR parameter . . . . . . . . . . . . . . . 141 ; 56.5. SHOW counters . . . . . . . . . . . . . . . . 142 ; 56.6. SHOW and ZERO counters. . . . . . . . . . . . 142 ; 57. Node data base ; 57.1. Find an NSP node block. . . . . . . . . . . . 143 ; 57.2. Find an existing NSP node block . . . . . . . 144 ; 57.3. Free a node block . . . . . . . . . . . . . . 145 ; 57.4. Release a node block. . . . . . . . . . . . . 145 ; 58. NSPJB0 - NSP periodic checks . . . . . . . . . . . . . 146 ; 59. EVTINI - Initialize event logger interface . . . . . . 147 ; 60. NSPEVT - Queue an Event to Network Management. . . . . 148 ; 61. Trace-to-TTY Facility. . . . . . . . . . . . . . . . . 151 ; 62. End of Program . . . . . . . . . . . . . . . . . . . . 154 SUBTTL Register Definitions ;Registers T1 through T6 and P1 through P2 are defined in D36PAR for ;all of DECnet-36. Register P4 is redefined as MS for ;the DNxyBY routines in D36COM. Register MB is defined in D36PAR for ;NSP and Router only. The registers defined below are additions ;used in NSP only. The registers FREEn are defined in D36PAR to be ;registers not otherwise used in DECnet-36. MB=MB ;THESE ARE DEFINED AS .NODDT GLOBALS CX=CX ; IN THE UNIVERSAL, CHANGE THAT HERE EL=FREE1 ;POINTER TO THE CURRENT PORT BLOCK ES=FREE2 ;POINTER TO THE CURRENT SUBLINK BLOCK SUBTTL EXTERN Declarations EXTERN RTN ;NON-SKIP RETURN LABEL EXTERN RSKP ;SKIP RETURN LABEL EXTERN DNGWZP ;ROUTINE TO GET SOME ZEROED MEMORY ;The Interlock interface IFN FTOPS10,< EXTERN NSPLOK ;INTERLOCK WORD EXTERN NSPLKO ;INTERLOCK OWNER > ;The Router interface ; T1/ Flags, see RT%RQR and RT%ODN in D36PAR.MAC ; MB/ Pointer to the message block EXTERN RTRXMT ;SEND TO ROUTER ; Normal Return ; ; ; T1/ My Node Number ; T2/ NSP's entry address (call vector base) ; T3/ Flags, see RT%PH2 in D36PAR.MAC EXTERN RTRINI ;INITIALIZE ROUTER ; Normal Return ; ; ;The Session Control interface ; T1/ NSPpid for port just created ; T2/ See BEGSTR IA in D36PAR ; T3/ ignored ; T4/ Message Block EXTERN SCTL ;NORMAL INTERFACE, CALLED VIA @EL.SCV(EL) EXTERN SCTLCI ;THIS ENTRY USED ONLY FOR CI MESSAGES ; Normal Return ;ALL OTHER ENTRIES CALL @EL.SCV(EL) ; SEE PROCEDURES NSPOPN AND NSPACC ; ; T1/ SCTL Port id (ELSCB) EXTERN SCTRIB ;RESERVE INPUT BLOCK ; Error Return ; NO BLOCK AVAILABLE ; Normal Return ;ONE BLOCK RESERVED ; ; No arguments EXTERN SCTUCG ;CALL THIS ON MEMORY UNCONGESTION ; Normal Return ;The Network Management Interface ;Network management parameters ; T1/ parameter table ; T2/ parameter table length ; T3/ function code EXTERN NTPARM ;Network management counters ; T1/ counter table ; T2/ counter table length ; T3/ function code EXTERN NTCTRS ; T1/ Ptr to arg block with structure NE in it EXTERN NMXEVT ;EVENT CATCHER ; Normal Return ;SCLINK node number/name mapping EXTERN SCTA2N ;SCLINK event parameter routine EXTERN LEVT.0 ;Event parameter 0 ; Always +1 return ;NTMAN hook for counter NICE'zation EXTERN PRSCOU ;DECnet initialization block EXTERN IBBLK ;The following procedures manipulate message bytes. They all have the ;following calling convention: ; ; MS/ Pointer to Message Segment Descriptor (MSD) ; T1/ Data to be written/Data returned ; ;The put-byte routines do not skip return. ; ;The get-byte routines non-skip return if byte count is zero when ;they are called, else they skip return. EXTERN DNP1BY ;Put a single byte EXTERN DNP2BY ;Put a double byte EXTERN DNPEBY ;Put an extensible byte EXTERN DNG1BY ;Get a single byte EXTERN DNG2BY ;Get a double byte EXTERN DNGEBY ;Get an extensible byte EXTERN DNGSBY ;Get a byte, skip if not extensible ; T1/ Number of bytes to back up EXTERN DNBKBY ;Back up (T1) bytes in MS's MSD ; Normal Return ; T1/ Pointer to an MSD EXTERN DNPINI ;Init MS for DNPxBY ; Normal Return ; T1/ Pointer to an MSD EXTERN DNGINI ;Init MS for DNGxBY ; Normal Return ; T1/ Pointer to an Message Block EXTERN DNLENGTH ;Return sum of lengths (bytes) of ; Normal Return ; text in all MSD(s) in T1 ; T1/ Pointer to an MSD EXTERN DNSLNG ;Return length (bytes) of ; Normal Return ; text in MSD(s) in T1 ; MS/ Pointer to an MSD EXTERN DNRPOS ;Read current MSD position ; Normal Return with position value in T1 ; MS/ Pointer to an MSD ; T1/ Value returned from DNRPOS EXTERN DNGPOS ;GOTO a position in this MSD's text ; Normal Return ;Continued on Next Page ;Continued from Previous Page EXTERN DCNCON ;NON-ZERO IF SYSTEM IS CONGESTED ; ; ;Message and message segment allocation routines ; ; T1/ Length in bytes of message segment to go on UDMSD EXTERN DNGMSG ;GET A DECnet-36 MESSAGE BLOCK ; Error Return if no resources ; Normal Return with pointer in T1 ; ; ;Clear a message block, this is like deallocating and reallocating ;without fear of losing the block. ; T1/ Pointer to the block ; T2/ Number of bytes of User Data to get EXTERN DNMINI ; Error Return if couldn't get user data ; Normal Return ; ; ;Message and message segment deallocation routines ; ; T1/ Pointer to block being returned EXTERN DNFMSG ;FREE A DECnet-36 MESSAGE BLOCK ; Normal Return ; ; ;Free memory allocation and deallocation routines ; ; T1/ Number of contiguous words to allocate EXTERN DNGWDS ;GET WORDS EXTERN DNGWDZ ;GET ZEROED WORDS ; Error Return ; Normal Return with pointer in T1 ; ; T1/ Address of start of block ; T2/ Address of end of block ; T3/ Value to put in block (typically zero) EXTERN DNSWDS ;Smear value into block ; Normal Return ; using either BLT or XBLT ; ; T1/ Pointer to block to free ; T2/ Number of words in block EXTERN DNFWDS ;FREE WORDS ; Normal Return ; EXTERN TIMBAS ;CONVERSION FROM DECNET TIME BASE INTO SECONDS EXTERN DNGTIM ;GET CURRENT TIME ; Normal Return ;WITH T1 CONTAINING MS TIME ;Trace and associated macros DEFINE CALLTRACE(proc,code),< IFN FTTRACE,< CALL [ IFIDN ,,MOVE CX,S.ETRACE## IFDIF ,,MOVE CX,S.TRACE## TXNN CX,TRCNSP RET SAVEAC CALLSCAN proc RET] > > DEFINE TRACE(prefix,message),,TRC)> DEFINE ETRACE(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),< 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, > 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 ,> 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, ; executed if match found ;b) IFSTATE AC,,LABEL (Go there if match found) DEFINE IFSTATE(ac,states,glabel),,,N,GE,L)> ;Use: AC/ One of the NPS.xx state codes ; ;a) IFNSTATE AC, ; executed if match NOT found ;b) IFNSTATE AC,,LABEL (Go there if match NOT found) DEFINE IFNSTATE(ac,states,glabel),,,E,L,GE)> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; DEFINE IFST1(ac,states,glabel,mod1,mod2,mod3),< ZZ==0 ZZCNT==0 IRP states, ZZCNT==ZZCNT+1> IFE ZZCNT-1,< CAI'mod1 ac,NPS.'states IFNB ,< IFIDN ,, IFDIF ,, >> IFG ZZCNT-1,< ;;IF MORE THAN ONE STATE TESTED MOVX CX,ZZ ROT CX,(ac) IFB ,< SKIP'mod2 CX > IFNB ,< 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),,,E,0)> ;CMODLE - Skip if AC is less than or equal to target (mod mask-size) DEFINE CMODLE(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),,,N,1)> ;CMODG - Skip if AC is greater than target (mod mask-size) DEFINE CMODG(ac,mask,offset),,,N)> ;The following are just for compatibility, same as OPSTR ;CMODE - Skip if AC is equal to target DEFINE CMODE(ac,mask,offset),,,> ;CMODN - Skip if AC is not equal to target DEFINE CMODN(ac,mask,offset),,,> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;CMODX - Common macro for CMODLE and CMODG to call DEFINE CMODX(ac,mask,offset,EorN,ecase,%fin),< LOAD CX,, SUB CX,ac IFNB , 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 > ;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 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 ;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 ,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,,<>,< 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 ,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! ;1B0 MEANS JIFFY REQUEST IFIW! ;1B1 MEANS CONGESTION-DETECTED IFIW! ;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 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 ,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,,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 ,(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,,<,,>,< 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 ,NNXBC,(P1) ;Update count of user bytes sent to node OPSTRM ,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 ,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,> ;YES, QUALIFY IT AS A NAK TXO T1,> ;NO, QUALIFY IT AS AN ACK SETZRO ,(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,> ;YES, QUALIFY IT CROSS NAK TXO T1,> ;NO, CROSS-SUBCHANNEL ACK SETZRO ,(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,,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,,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,,NSICL1 ;LINK IN CI STATE? OPSTR ,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, ;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 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 ,ESCWS,(ES) ; and add current window size ANDI T1, ; 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 ;NO FLOW CONTROL IFIW ;SEGMENT FLOW CONTROL IFIW ;MESSAGE FLOW CONTROL IFIW ;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 ,ESLAR,(ES) ;Add number of last ACK'ed ANDI T1, ; 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 ,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 ,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,,<,,>,< 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 ,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 ,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 ,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,,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,,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, 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, 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, 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 ,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 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 ,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. ; -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 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 ,ESLAR,(ES) ;Will we wrap around? MOVEI T1,<1B<35-WID(ESLAR)>>(P1) ; -yes, T1 := P1 + MAX(ESLAR)+1 OPSTR ,ESLAR,(ES) ;# of messages ACK'ed by the ACK OPSTR ,ESCDA,(ES) ;Add to # of ACK's since last window change ANDI T1, ;Protect againt overflow STOR T1,ESCDA,(ES) ; and save new value LOAD T1,ESCWS,(ES) ;Get current window size OPSTR ,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 ,(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 ,(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,,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,,<,,>,< 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, 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 ,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, > 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 ,(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 ;FOLLOWING ROUTINES NEED THIS TMNE QHBEG,+ES.RCQ(ES) ;UNLESS THERE IS NOTHING ON IT, CALL PRCRQS ; PROCESS THE RECEIVE QUEUE JE ESROF,(ES),PRCRQ1 ;DON'T BOTHER THE OFF FLAG IF ITS CLEAR JE ESRLD,(ES),PRCRQ1 ;ITS OFF, JUMP IF NO DRQs FROM SCTL SETZRO ESROF,(ES) ;SHOULD BE ON NOW, TURN IT ON SETONE ESROC,(ES) ; AND TELL CHKDRQ THAT 'OFF' HAS CHANGED CALL NSPRJF ;GET JIFFY SERVICE TO SEND LINK SRV MSG PRCRQ1: IFN FTGOL < LOAD T1,ESGOL,(ES) ;GET DATA REQUEST GOAL > IFE FTGOL < MOVE T1,NSGOAL ;Get system-wide goal SKIPE DCNCON ;System-wide congestion SETZ T1, ;Yes, then goal of zero > LOAD T4,ESRFL,(ES) ;GET RECEIVE FLOW CONTROL TYPE JRST @.+1(T4) ;DISPATCH IFIW ;NO FLOW CONTROL IFIW ;SEGMENT FLOW CONTROL IFIW ;MESSAGE FLOW CONTROL IFIW ;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 ,(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 ,(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 ,NNRBC,(T4) ;ADD TO TOTAL BYTES RECEIVED FROM NODE OPSTRM ,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 ;NO FLOW CONTROL IFIW ;SEGMENT FLOW CONTROL IFIW ;MESSAGE FLOW CONTROL IFIW ;ILLEGAL FLOW CONTROL PRCRSM: TMNN MBEOM,(MB) ;IS THIS SEGMENT AN END OF MESSAGE? JRST PRCRS5 ;NO PRCRSS: OPSTRM ,ESRRD,(ES) ;DECREMENT REMOTE RECEIVE DRQS PRCRS5: PRCRSN: OPSTRM ,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 ,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,,<,,>,< 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 ;Preserve sublink pointer LOAD T1,ELSTA,(EL) ;CHECK THAT WE'RE IN THE RIGHT STATE IFNSTATE T1,,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 ;NO FLOW CONTROL, IGNORE THE VAL FIELD IFIW ;SEGMENT FLOW CONTROL IFIW ;MESSAGE FLOW CONTROL IFIW ;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 ,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 ,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,,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 ,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 ,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 ,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,,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 ;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 ,NNTBR,(T2) ;Update count of bytes XMOVEI ES,EL.NSL(EL) ;CONNECT IS ON 'NORMAL' SUBLINK IFNSTATE P1,,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,,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 ,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,,RCVDI1 ;CONNECT INIT, CONNECT DELIVERED IFSTATE T1,,RCVDI2 ;RUN OR CC (PSEUDO-RUN) IFSTATE T1, ,RCVDI3 ;DISCONNECT INIT IFSTATE T1, ,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 ,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, ;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 ,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 ,(EL) ;DON'T SEND A DISCONNECT CONFIRM MESSAGE ; & WE NO LONGER NEED A CAK LOAD T1,ELSTA,(EL) ;GET THE STATE AGAIN IFNSTATE T1, ;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, EVENT(NSP,MSG,,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 ,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 ,(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 ,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 ,(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, 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 ,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 ,+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 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 ;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,,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 ,(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,,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 ;ENTRY FOR CALLERS WHO NEED SAVEAC JCHSDQ: LOAD T1,ELSTA,(EL) ;ENTRY FOR JIFFY SERVICE, ACS ALREADY SAVED IFSTATE T1,,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 ;NO FLOW CONTROL IFIW ;SEGMENT FLOW CONTROL IFIW ;MESSAGE FLOW CONTROL IFIW ](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, ,CHKSC1 ;SEND A CONNECT ACK MESSAGE IFSTATE T1,,RSKP ;LET CHKCLS SENT DISCON CONFIRM IFSTATE T1, ,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 ,(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 ;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,,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, ;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, ;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 ,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,,RTN ;Retransmit only if in CI state ASSUME ELCIM,EQ,-1 ;Make sure fullword OPSTR ,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 ,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,,<>,< 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 ,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 ,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 ,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 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 ,(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 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 ;NO FLOW CONTROL (SEND OFF MSG) IFIW ;SEGMENT FLOW CONTROL (SEND NEG DRQS) IFIW ;MESSGAGE F.C. (SEND OFF MSG) IFIW ](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 ,(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 ,ELCLC,(EL) ;Count down retry count JUMPN T1,RTN ;Just return and try next jiffy unless time for ;BUGCHK OPSTR ,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,,<,,>, < 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, ,CHKCL1 ;IF ABORT CLEAR QS; CLOSE REMOTE IFSTATE T1,,CLSRMT ;CLOSE REMOTE IFSTATE T1, ,CHKCL2 ;CLEAR QUEUES, CLOSE REMOTE IFSTATE T1, ,CLSLOC ;CLOSE LOCAL PORT IFSTATE T1,,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 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 ,ESLAR,+EL.NSL(EL) RET ;NO, TRY NEXT JIFFY LOAD T1,ESLMA,+EL.OSL(EL) ;"OTHER" SUBLINK FINISHED ACKING? OPSTR ,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, ;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 ,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 ,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,,,< 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 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 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 ;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 ;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 ,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 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,,PTIRN0 ;IF WE WERE IN CC STATE SAVEAC ES XMOVEI ES,EL.NSL(EL) ;CONNECT CONFIRM MSG IN ON 'NORMAL' SUBLINK MOVX T1, ;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,,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,,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/ 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/ 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 ,NNLKC,(T1) ;ONE MORE ACTIVE LINK FOR THIS NODE OPSTR ,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 ,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,,RSKP ;JUMP IF SUCCESS, POINTER IN EL, STATE IN T1 RET ;CAN'T CALL 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,,,< 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,,,< 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 ,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,> 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,,FNSPD1 ;WE DON'T ADMIT TO CLOSED PORTS OPSTR ,ELCHK,(EL) ;IS THE CHECK ADDRESS THERE? FNSPD1: BUG.(CHK,LLIFNS,LLINKS,SOFT,,<>,< 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, ;IS IT IN 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 ,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 ,NNDLY,(T4) ;GET DIFFERENCE FROM OLD ESTIMATE IDIV T1,NSPWGT ;DIMINISH BY WEIGHTING FACTOR OPSTR ,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,,,< 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 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 > ;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 ,ELORC,(EL) ;ONE LESS OUT-IN-ROUTER JUMPGE T1,RTN ;JUMP IF NO BUG BUG.(CHK,LLIORC,LLINKS,SOFT,,,< 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 ,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 > 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,,JFCL,) PARAMETER(^D601,PANST!PANCL,,,,JFCL,,JFCL,) PARAMETER(^D700,PANST!PANCL,,,,JFCL,,JFCL,) PARAMETER(^D710,PANST!PANCL,,,,JFCL,,JFCL,) PARAMETER(^D720,,,1,%NSDLY,,,,) PARAMETER(^D721,,,1,%NSWGT,,,,) PARAMETER(^D722,,,1,%NSINA,,,,) PARAMETER(^D723,,,1,%NSRTH,,,,) NRNPAR==.-NODPAR ;Network management counter table ;Remote and executor node counters NODCTR: COUNTER(^D0,^D16, ;;Save T2 so we can divide into it CALL DNGTIM ;;Get current time in T1 OPSTR ,NNTLZ,(P2) ;;Subtract time when zeroed IDIVI T1,TIMBAS ;;Make into seconds RET ]>,,,) COUNTER(^D600,^D32,,,,) COUNTER(^D601,^D32,,,,) COUNTER(^D602,^D32,,,,) COUNTER(^D603,^D32,,,,) COUNTER(^D608,^D32,,,,) COUNTER(^D609,^D32,,,,) COUNTER(^D610,^D32,,,,) COUNTER(^D611,^D32,,,,) COUNTER(^D620,^D16,,,,) COUNTER(^D621,^D16,,,,) COUNTER(^D630,^D16,,,,) COUNTER(^D640,^D16,,,,) NRCCTR==.-NODCTR ;Executor node only counters COUNTER(^D700,^D16,,,,) 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 ;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 ,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 ,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 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 ,NNLKC,(P1) ;Any active links to this node? IFSKP. ; -no, interesting! LOAD T1,NNNOD,(P1) ; Get node ID OPSTR ,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,,) 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 <,> ;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 ,+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 ,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 ;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 <,MSGSND,> 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, ;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+</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 ife ftki10,< TXO P2,.P0836 ;Global, 8-bit bytes, point to 1st byte >;julgran... ifn ftki10,< hrli p2,(point 8,0) >;ki10 ;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 ; Type 0 - Invalid message header IFIW ; 1 - Invalid flow control IFIW ](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,,<,>,< 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 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 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,> 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 ,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 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