TITLE NETSER - DEVICE INDEPENDENT NETWORK SERVICE ROUTINES - V1200 SUBTTL D. TODD/DRT/EJW/JBS/DRL 27-JUN-88 SEARCH F,S,NETPRM $RELOC $HIGH ;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 ; 1974,1975,1976,1977,1978,1979,1980,1982,1984,1986,1988. ;ALL RIGHTS RESERVED. .CPYRT<1974,1988> XP VNETSER,1200 ;PUT VERSION NUMBER IN GLOB AND LOADER MAP NETSER::ENTRY NETSER ;LOADING IF IN LIBRARY SEARCH MODE Comment @ Loose ends. NETSER problems. o The interface to PSISER with respect to PSINTC (Topology change) is poor. Probably should have a bit in the NDB that indicates whether or not the interrupt has been given. Network device (rdx, cdr, lpt, tsk etc) problems Monitor problems (in routines other than NETSER) o Make COMMON generate the right amount of free core. DN87 problems (-11 code) o LB.IBF gets filled up too easily. Make it use a chunk? Loose ends in general (Programs to write etc...) @ SUBTTL SYSINI INITIALIZATION $INIT NETINI::MOVE T1,ANFNUM## ;ANF-10 STATION NUMBER HRLM T1,NETNDB##+NDBNNM ;STUFF INTO OUR NDB MOVEI T2,NETNDB## ;GET NDB ADDRESS FOR CENTRAL STATION HRRM T2,.GTNDA##(T1) ;SAVE FOR NETSER MOVEM T1,JBTLOC## ;AND FOR THE REST OF THE MONITOR MOVE T1,ANFNAM## ;STATION NAME MOVEM T1,NETNDB##+NDBSN2 ;SAVE IN NDB POPJ P, ;RETURN $HIGH SUBTTL INTERFACE TO AUTCON ;ROUTINE TO SET THE COUNT OF VARIOUS DEVICES IN THE HOST'S NDB. ;CALL: PUSHJ P,NETDEV ; ; AC USAGE: T1 - T4 NETDEV::PUSH P,W ;SAVE W XMOVEI W,NETNDB## ;POINT TO OUR NDB MOVEI T1,M.RMCR## ;SEE IF WE HAVE AN MCR MOVEM T1,DDBCNT##+.TYMCR ;FAKE OUT OUR LOOP BELOW HRRZ T1,NETRTY## ;GET NUMBER OF LOCAL TERMINAL LINES MOVEM T1,DDBCNT##+.TYTTY ;WE DON'T CARE ABOUT SCNN MOVSI T1,-TYPMAX-1 ;AOBJN POINTER TO DDB COUNT TABLE NETDE1: SKIPN DDBTAB##(T1) ;IS THERE A PROTOTYPE DDB? JRST NETDE3 ;TRY THE NEXT ENTRY HRRZ T2,T1 ;GET .TY??? MOVSI T3,- ;AOBJN POINTER NETDE2: HLRZ T4,OBJTAB##(T3) ;GET A DEVICE TYPE CAME T2,T4 ;MATCH? AOBJN T3,NETDE2 ;TRY ANOTHER JUMPGE T3,NETDE3 ;FOUND ONE? MOVE T2,DDBCNT##(T1) ;GET DEVICE COUNT DPB T2,NETCNF##(T3) ;UPDATE DEVICE COUNT IN THE NDB NETDE3: AOBJN T1,NETDE1 ;LOOP FOR ALL DEVICES JRST WPOPJ## ;RESTORE W AND RETURN SUBTTL INTERFACE TO COMCON ;ENTRY FROM COMCON WITH A NETWORK ASSIGN COMMAND ; ; ASSIGN NODENAME_DEV:LOGICAL ; ;COMCON HAS ALREADY SCANNED OF THE NODE NAME ; T2=NODE NAME ; T3=DELIMETER ;RETURN ; CPOPJ ;NOT A NETWORK DEVICE. ERROR MESSAGE PRINTED ; CPOPJ1 ;IT IS A NETWORK DEVICE. T2 := THE "TRANSLATED" ; ; NAME OF THE FORM GGGNNU NETASG:: ;ENTRY MOVE P1,T2 ;SAVE THE STATION NAME CAIE T3,"_" ;TERMINATED BY A _ JRST CPOPJ1## ;IF NOT, DON'T CONVERT THE NAME PUSHJ P,COMTYS## ;YES, SKIP THE "_" NETDBJ ;INTERLOCK THIS CODE PUSHJ P,CTXDEV## ;GET THE DEVICE NAME JUMPE T2,NOTENF## ;ILLEGAL ARG LIST MOVE P2,T2 ;SAVE THE DEVICE NAME TRNE T2,505050 ;MUST BE NUMERIC GGGU JRST NOTDEV## ;ERROR RETURN TLNE P2,505050 ;IS LH A NUMBER? JRST NETAS1 ;NO, CONTINUE NORMALLY MOVSS P2,P2 ;FLIP HALVES OF ARGUMENT NETAS0: TRNE P2,77 ;IS RIGHTMOST DIGIT ZERO? JRST NETAS2 ;NO, DONE LSH P2,-6 ;SHIFT RIGHT ONE DIGIT JRST NETAS0 ; AND TRY AGAIN NETAS2: TRO P2,202020 ;MAKE IT ALL DIGITS HRLI P2,(SIXBIT/TTY/); AND MAKE LH SAY TTY ;HERE TO VALIDATE THE STATION NAME NETAS1: MOVE T1,P1 ;COPY THE STATION NAME PUSHJ P,SRCNDB ;DOES THE STATION EXIST JRST [HRRZ T1,NRTUNN ;TYPE AN ERROR PJRST ERRMES##] ;ERROR MESSAGE MOVE T2,P2 ;RESTORE DEVICE NAME HLRZ P1,NDBNNM##(W) ;GET NODE # CAMN P1,JBTLOC## ;LOCAL NODE? JRST CPOPJ1## ;YES, JUST RETURN MOVEI P1,(W) ;SAVE THE NDB POINTER ;HERE TO VALIDATE THE REMOTE DEVICE NAME HLRZ T1,P2 ;GENERIC NAME ONLY FROM THE LEFT HALF CAIN T1,(SIXBIT /TTY/) ;IS IT TERMINAL? JRST NETAST ;YES, ASSIGN IT TRNN P2,505050 ;MUST BE LEFT HALF NUMERIC OR ZEROS PUSHJ P,SRCNDT ;SEARCH THE NDT PJRST NOTDEV## ;NOT A LEGAL DEVICE HLL P2,NDTNAM(W) ;COPY THE GENERIC -10 DEVICE NAME MOVEI W,(P1) ;GET THE NODE POINER BACK HLRZ P1,NDBNNM(W) ;PUT THE NODE NUMBER IN P1 ;HERE TO CONVERT THE DEVICE NAME INTO A NETWORK NAME ;DEVICE MUST BE OF THE FORM(S) GGGU,OR GGG MOVEI T1,(P1) ;CONVERT THE NODE NUMBER TO SIXBIT TRO T1,77700 ;FORCE CONVERSION TO LINE UP S0PSHJ CVTSBT## ;IN UUOCON MOVEI T2,(P2) ;COPY THE UNIT NUMBER IF ANY LSH T2,-14 ;SHIFT THE UNIT NUMBER IF ANY TO LOW ORDER IORI T2,(T1) ;COMBINE THE RIGHT HALF HLL T2,P2 ;AND INSERT THE GENERIC NAME JRST CPOPJ1## ;RETURN WITH T2 := GGGNNU NETAST: ;HERE IF NETWORK TERMINAL BEING ASSIGNED HRLZ T1,P2 ;GET THE REMOTE LINE NUMBER PUSHJ P,CVTOCT## ;CONVERT IT TO OCTAL JRST NOTDEV## ;NON NUMERIC LINE NUMBER HLL T1,NDBNNM(W) ;GET THE NODE NUMBER PUSHJ P,ASGTTY## ;AND TRY TO CONVERT THE PAIR TO "TTYNNN" JRST NOTDEV## ;COULDN'T CONNECT. DEVICE NOT AVAILABLE MOVE T2,T1 ;COPY THE DEVICE NAME JRST CPOPJ1## ;WE SUCCEDED. T2 := DEVICE NAME SUBTTL INTERFACE TO COMCON FOR THE "NODE" COMMAND NODE.A::NETDBJ ;INTERLOCK THIS CODE SKIPN T1,T2 ;CHECK FOR AN ARG JRST [PUSHJ P,FNDSTA## ;IF NO ARG, GET THE STATION OUR TTY JRST NODEC1] ; IS AT, AND USE THAT ONE PUSHJ P,CVTOCT## ;TRY TO CONVERT THE NUMBER MOVE T1,T2 ; IF WE CAN'T, USE THE NAME ;TYPE OUT A PARTICULAR NODE NAME NODEC1: PUSHJ P,SRCNDB ;FIND THE NODE BLOCK POPJ P, ;NO SUCH NODE, GIVE FAIL RETURN (IF MORE ; NETWORKS, LET THEM TRY). ; PJRST NODEC2 ;TYPE THE INFO NODEC2: PUSHJ P,TYPNDB ;TYPE IT PUSHJ P,INLMES## ;TYPE CR-LF-TAB ASCIZ / / MOVSI P1,- ;GET THE DEVICE TABLE SIZE NODEC4: LDB T1,NETCNF##(P1) ;GET THE NUMBER OF DEVICES JUMPE T1,NODEC5 ;NONE DON'T PRINT HRLZ T2,OBJTAB##(P1) ;GET THE DEVICE NAME PUSHJ P,PRNAME## ;PRINT IT OUT MOVEI T3,"[" ;BRACKET PUSHJ P,COMTYO## ;TYPE LDB T1,NETCNF##(P1) ;GET THE NUMBER OF ENTRIES PUSHJ P,RADX10## ;PRINT PUSHJ P,INLMES## ;BRACKET SPACE ASCIZ /] / NODEC5: AOBJN P1,NODEC4 ;CONTINUE TO THE END PUSHJ P,PCRLF## ;END OF LINE JRST CPOPJ1## ;AND RETURN SUCCESS SUBTTL INTERFACE TO COMCON ;WHERE COMMAND CWHANF::MOVE U,0(P) ;COMMAND TTY LDB PUSHJ P,SRCNDB ;SEARCH FOR THE NDB MOVEI W,NETNDB## ;USE THE LOCAL MODE PUSHJ P,TYPNDB ;TYPE THE NODE INFO PUSHJ P,PRSPC## MOVE T2,DEVNAM(F) PUSHJ P,PRNAME## MOVSI T1,DVTTY ;CHECK FOR A TTY TDNN T1,DEVMOD(F) ;IS DEVICE A TTY? JRST [PUSHJ P,ZAPNET ;NO, FREE DDB IF APPROPRIATE POP P,U ;RESTORE U PJRST CRLF##] ;TYPE CRLF AND EXIT MOVE U,DDBLDB##(F) ;YES, GET THE LDB LDB T1,LDPLNO## ;GET THE LOCAL LINE NUMBER MOVSI T2,LTLANF## ;ANF NETWORK VIRTUAL TERMINAL FLAG TDNE T2,LDBTTW##(U) ;CHECK A BIT LDB T1,LDPRLN## ;GET REMOTE LINE NUMBER POP P,U ;RESTORE THE LDB PUSH P,T1 ;SAVE LINE NUMBER PUSHJ P,INLMES## ;TYPE OUT ASCIZ / line # / POP P,T1 ;RESTORE LINE NUMBER PUSHJ P,PRTDI8## ;TYPE IT PJRST PCRLF## ;AND CRLF SUBTTL INTERFACE TO CPNSER COMMENT @ PCBMRK and PCBCHK These routines are used to manage the transfer of PCB's to and from Front End Kontrollers in a Multi-Cpu, or Cached environment. The protocol for using these two routines is as follows. PCBMRK This routine should be called after the PCB is altered by the -10. In a cached environment, it stores the Cache Sweep Serial number so that PCBCHK can tell if cache needs to be swept at a later date. On a non-cached, or multi-cpu machine (free core is not cached on a SMP system) this routine is a no-op. PCBCHK This routine should be called with J := FEK address, and U := PCB address. It should be immediatly before either "FEKRDD" or "FEKWRT" is executed. If it skips, then that implies that the FEK may access the data. If it does not skip, then either the FEK is on the wrong CPU, or the data is still in cache, and may not be accessed by the FEK. @ IFN FTKL10,< ;THINGS ARE MESSY FOR KL'S PCBMRK::CONSO APR,LP.CSB+LP.CSD ;IF THE CACHE IS ALREADY SWEEPING (BUSY) TDZA T1,T1 ; THEN IT MAY HAVE MISSED SOME OF THE PCB MOVEI T1,1 ; IF SO, ADD ONE TO THE SWEEP SERIAL NUMBER ADD T1,.CPCSN## ;GET SWEEP SERIAL NUMBER (MAYBE PLUS ONE) MOVEM T1,PCBCSN(U) ;SAVE IT FOR PCBCHK TO LOOK AT POPJ P, ;EXIT WITH THE PCB MARKED PCBCHK:: IFN FTMP,< PUSHJ P,MLSCSH## ;SEE IF FRECOR IS CACHED POPJ P, ; RETURN NOW. (NO SWEEP PROBLEMS) >;END FTMP MOVE T1,.CPCSN## ;GET THE CURRENT CSSN CAMG T1,PCBCSN(U) ; SEE IF WE'VE SWEPT SINCE LAST ACCESS TO PCB PUSHJ P,CSDMP## ;IF NOT, SWEEP NOW. (TOO SLOW TO WAIT) POPJ P, ;WE'VE SWEPT, GIVE GOOD RETURN >;END FTKL10 ;ROUTINE CALLED FROM CPNSER WHEN A CPU HAS CRASHED. THIS ROUTINE ;TELLS NETSER WHICH FRONT ENDS HAVE BECOME INACCESSABLE DUE TO THE ;CPU GOING DOWN, AND CRASHES THE APPROPRIATE FEKS. ;CALL: MOVE T1, ADDRESS OF THE CRASHED CPU'S CDB ; PUSHJ P,BRKFEK ; ;PRESERVES ALL ACS IFE FTMP, IFN FTMP,< BRKFEK::SKIPN [M.CPU##-1] ;MULTI-CPU? POPJ P, ;NO PUSHJ P,SAVT## ;PRESERVE THE T'S MOVSI T2,FK.CPD ;FLAG THAT SAYS "THIS FEK'S CPU DIED" SKIPA T3,[FEKFST##] ;GET ADDRESS OF THE FIRST FEK. BRKFE1: HRRZ T3,FEKBLK(T3) ;GET THE ADDRESS OF THE NEXT FEK. JUMPE T3,CPOPJ## ;ZERO ADDRESS MEANS WEVE DONE THEM ALL HLRE T4,FEKUNI(T3) ;GET THE CPU NUMBER OF THIS FEKS CPU CAME T4,.CPCPN##-.CPCDB##(T1) ;SEE IF THIS IS THE CPU THAT DIED JRST BRKFE1 ;NO AOS .CPNBI##-.CPCDB##(T1) ;COUNT THE BROKEN INTERLOCK IORM T2,FEKBLK(T3) ;IF ON JUST-DEAD CPU, SET BIT FOR 1/SECOND JRST BRKFE1 ;GO CHECK THE NEXT FEK. > ;END IFN FTMP SUBTTL INTERFACE TO UUOCON FOR DDB DISPATCH SUBTTL NETDDB DISPATCH TABLE JSP T4,DSPOBJ ;(-5) ON LINE CHECK JSP T4,DSPOBJ ;(-4) DEVOP. UUO JSP T4,DSPOBJ ;(-3) RETURN BUFFER SIZE POPJ P, ;(-2) DEVICE INITIALIZATION JSP T4,DSPOBJ ;(-1) HUNG DEVICE NETDSP::JSP T4,DSPOBJ ;(0) RELEASE DEVICE JSP T4,DSPOBJ ;(1) CLOSE JSP T4,DSPOBJ ;(2) OUTPUT JSP T4,DSPOBJ ;(3) INPUT JSP T4,DSPOBJ ;(4) ENTER JSP T4,DSPOBJ ;(5) LOOKUP JSP T4,DSPOBJ ;(6) DUMP MODE OUTPUT JSP T4,DSPOBJ ;(7) DUMP MODE INPUT JSP T4,DSPOBJ ;(10) USETO JSP T4,DSPOBJ ;(11) USETI JSP T4,DSPOBJ ;(12) UGETF UUO JSP T4,DSPOBJ ;(13) RENAME UUO JSP T4,DSPOBJ ;(14) CLOSE INPUT JSP T4,DSPOBJ ;(15) UTPCLR UUO JSP T4,DSPOBJ ;(16) MTAPE UUO ;DISPATCH ON THE OBJECT TYPE DSPOBJ: SUBI T4,NETDSP+1 ;RELOCATE THE ENTRY HRRES T4 ;MAKE IT A FULL WORD NUMBER MOVSI W,DVCNET ;GET THE NETWORK DEVICE BIT TDNN W,DEVCHR(F) ;IS THIS A NETWORK DEVICE? STOPCD CPOPJ##,DEBUG,DFU,;++DEVICE UNRECOGNIZED IFN PARANOID&P$DDB,< ;SOME CONSISTENCY CHECKING MOVSI W,DVLNG ;THE LONG-DISPATCH FLAG TDNN W,DEVMOD(F) ;CAN DEVICE HANDLE WHIZZY FUNCTIONS? CAIG T4,DIN ;NO, WAS WHIZZY ACTION REQUESTED? CAIA ;REASONABLE FUNCTION PUSHJ P,NTDSTP ;++ BAD DEVSER DISPATCH IFN FTXMON,< XMOVEI W,. ;GET CURRENT PC TLNE W,-1 ;NETSER/NETDEV/ET AL REALLY WANT SECTION 0 PUSHJ P,NTDSTP ;CALLED FROM NON-ZERO SECTION! > ;IFN FTXMON > ;IFN PARANOID&P$DDB CAME T4,[DDVO] ;EXCEPT FOR DEVOP. UUO JUMPL T4,DSPOB1 ;DON'T INTERLOCK HUNG DEVICE CHECK ETC. ; (THEY COME IN AT LEVEL #7) NETDBJ ;NETSER INTERLOCK FROM HERE ON.. DSPOB1: HLRZ W,DEVNET(F) ;GET THE NDT POINTER PJRST @NDTDSP(W) ;GO TO DEVICE DEPENDENT SERVICE ROUTINE ; (NDTDSP IS @N---DP(T4)) SUBTTL INTERFACE TO UUOCON FOR THE NODE UUO ;NODE. UUO SUB-FUNCTIONS ; .NDALN==1 ;ASSIGN LOGICAL NAME ; .NDRNN==2 ;RETURN NODE NUMBER ; .NDSSM==3 ;SEND STATION CONTROL MESSAGE ; .NDRBM==4 ;RECEIVE BOOT REQUEST MESSAGE ; .NDRCI==5 ;RETURN CONFIGURATION INFORMATION ; .NDOUT==6 ;DO DOUTPU WITH E-O-R (NOT IMPLEMENTED) ; .NDIN==7 ;DO INPUT WITH E-O-R (NOT IMPLEMENTED) ; .NDTCN==10 ;TERMINAL CONNECT ; .NDTDS==11 ;TERMINAL DIS-CONNECT ; .NDLND==12 ;LIST KNOWN NODES ; .NDNDB==13 ;RETURN VARIOUS NDB FIELDS ; .NDGNF==14 ;GET NEXT UN-GREETED NODE, CLEAR UN-GREETED FLG ;NODE. UUO ERROR CODES ND%IAL==ECOD1## ;ILLEGAL ARGUMENT LIST ND%INN==ECOD2## ;ILLEGAL NODE NAME/NUMBER ND%PRV==ECOD3## ;CALLER NOT PRIVILEGED ND%NNA==ECOD4## ;NODE NOT AVAILABLE ND%NLC==ECOD5## ;NOT LOCKED IN CORE ND%TOE==ECOD6## ;TIME OUT ERROR ND%RNZ==ECOD7## ;RESERVED WORD NOT ZERO ND%NND==ECOD10## ;NOT NETWORK DEVICE ND%IOE==ECOD11## ;IO ERROR ND%NFC==ECOD12## ;NO FREE CORE ND%IAJ==ECOD13## ;IN USE BY ANOTHER JOB (TERMINAL) ND%NMA==ECOD14## ;NO MESSAGE AVAILABLE ND%TNA==ECOD15## ;TERMINAL NOT AVAILABLE ND%NLT==ECOD16## ;NOT A LEGAL TERMINAL ND%ISF==ECOD17## ;ILLEGAL SUB-FUNCTION ND%RBS==ECOD20## ;RECEIVE BUFFER TOO SMALL ND%NUG==ECOD21## ;NO UNGREETED NODES ND%ILN==ECOD22## ;ILLEGAL LINE NUMBER (IN STC MESSAGE) ND%ADC==ECOD23## ;ADDRESS CHECK NODE.U:: ;ENTRY POINT HLRZ T4,T1 ;GET THE FUNCTION IN T4 SKIPE T4 ;NOT ZERO CAILE T4,NUULEN ;CHECK THE LENGTH JRST ND%IAL ;ILLEGAL RETURN 1 HRRI M,(T1) ;GET THE ARG LIST IN M NETDBJ ;INTERLOCK THIS CODE JRST UUOTAB-1(T4) ;JUMP ON THE FUNCTION TYPE UUOTAB: ;NODE UUO FUNCTIONS JRST NODE.1 ;ASSIGN A DEVICE JRST NODE.2 ;RETURN A NODE NUMBER JRST NODE.3 ;STATION CONTOL MESSAGE JRST NODE.4 ;AUTO RELOAD OF DAS 80 SERIES JRST NODE.5 ;RETURN CONFIG INFO FOR NODE JRST CPOPJ## ;OUTPUT DATA TO NETWORK JRST CPOPJ## ;INPUT DATA FROM NETWORK JRST NODE10 ;CONNECT A TERMINAL JRST NODE11 ;DISCONNECT A TERMINAL JRST NODE12 ;GET COUNT AND NUMBERS OF ALL NODES JRST NODE13 ;GET SELECTED INFORMATION ABOUT A NODE JRST NODE14 ;RETURN/CLEAR UN-GREETED NODE FLAG NUULEN==.-UUOTAB ;LENGTH OF THE TABLE SUBTTL NODE.1 - ASSIGN A REMOTE DEVICE NODE.1: POPJ P, ;RESERVED (UNTIL IT WORKS) PUSHJ P,SAVE4## ;SAVE THE P'S PUSHJ P,GETWRD## ;GET THE ARGUMENT LIST SIZE JRST ND%ADC ;ADDRESS CHECK CAIE T1,4 ;MUST HAVE FOUR ARGS JRST ECOD1## ;ILLEGAL ARG LIST PUSHJ P,GETWR1## ;GET THE NODE NAME JRST ND%ADC ;ADDRESS CHECK PUSHJ P,SRCNDB ;CHECK IF DEFINED JRST ECOD2## ;NO, ILLEGAL NODE NAME HLRZ T1,NDBNNM(W) ;GET NODE NUMBER CAMN T1,JBTLOC## ;IS IT LOCAL SITE? JRST ECOD2## ;YES, CAN'T ASSIGN LOCAL DEVICE MOVEI P1,(W) ;SAVE THE NODE DATA BLOCK PUSHJ P,GETWR1## ;GET THE PHYSICAL DEVICE NAME JRST ND%ADC ;ADDRESS CHECK SKIPN P2,T1 ;COPY THE NAME JRST ECOD3## ;ZERO IS ILLEGAL HLRZS T1 ;GENERIC NAME ONLY FOR SEARCH PUSHJ P,SRCNDT ;SEARCH THE DEFINED NAME TABLE JRST ECOD3## ;ILLEGAL DEVICE NAME PUSHJ P,GETWR1## ;GET THE LOGICAL NAME JRST ND%ADC ;ADDRESS CHECK MOVE P3,T1 ;COPY THE NAME PUSHJ P,MAKDDB ;MAKE A REMOTE NETWORK DDB JRST ECOD4## ;NO CORE AVAILABLE PUSHJ P,NCSCNT ;CONNECT THE DEVICE JRST [MOVEI T1,10(T1);STEP UP THE ERROR NUMBER PUSHJ P,STOTAC## ;TELL CALLER WHY IT FAILED PJRST RMVNET] ;REMOVE DDB FROM SYSTEM PUSHJ P,LNKDDB ;LINK NEWLY CONNECTED DDB MOVEI T1,ASSCON ;NOW THAT WE'VE ASSIGNED THE DEVICE, WE IORM T1,DEVMOD(F) ; BETTER MARK IT. (OR ELSE UUOCON WILL ZAP IT) MOVE T1,DEVNAM(F) ;GET NAME OF DEVICE WE CREATED PJRST STOTC1## ;TELL USER AND SUCCEED SUBTTL NODE.2 RETURN A NODE NUMBER IN THE AC NODE.2: ;ENTRY PUSHJ P,GETWRD## ;GET THE ARG COUNT JRST ND%ADC ;ADDRESS CHECK CAIE T1,2 ;MUST BE A 2 JRST ND%IAL ;NO ILLEGAL FUNCTION PUSHJ P,GETWR1## ;GET THE NODE NAME JRST ND%ADC ;ADDRESS CHECK PUSHJ P,NODE.S ;FIND CORRECT NDB PJRST ND%INN ;ILLEGAL NODE MOVE T2,T1 ;COPY ARGUMENT HLRZ T1,NDBSNM(W) ;GET THE NAME POINTER MOVE T1,(T1) ;GET THE NAME TLNE T2,-1 ;WANTS A NAME? HLRZ T1,NDBNNM(W) ;NO, GET THE NODE NUMBER PJRST STOTC1## ;RETURN NODE NUMBER IN AC ;SUBROUTINE TO FIND NDB FOR NODE.UUO FUNCTIONS ;CALL T1 = ARGUMENT TO NODE.UUO ;RETURNS CPOPJ IF NOT FOUND ; CPOPJ1 WITH W SET TO NDB, RESPECTS T1 NODE.S::PUSH P,T1 ;SAVE ARGUMENT PUSHJ P,SRCNDB ;SCAN NDB'S PJRST TPOPJ## ;NOPE, GIVE ERROR MOVSI T1,NDB.UP ;DON'T DO THE TRANSLATION TDNN T1,NDBFLG(W) ; UNLESS THE NODE IS UP. JRST TPOPJ## ; IF WE DO, WE GET GARBAGE NAMES PJRST TPOPJ1## ;RETURN WITH W SET UP SUBTTL NODE.3 STATION CONTROL MESSAGES ;FORMAT OF THE UUO ARGS ; XWD TIME,ARG-LIST-LENGTH ; SIXBIT /NODE NAME/ OR NUMBER ; XWD COUNT,ADR OF OUTPUT BUFFER ; XWD COUNT,ADR OF RESPONSE BUFFER NODE.3: PUSHJ P,NTDPRV ; SURE THAT THIS GUY IS PRIVILEDGED PJRST ND%PRV ;USER HAS INSUFFICIENT PRIVILEDGES PUSHJ P,SAVJW## ;WE WILL CLOBBER THESE PUSHJ P,SAVE4## ;WE COPY THE UUO ARGS IN P1 - P4 PUSHJ P,N34SET ;GO READ THE UUO ARGS POPJ P, ; IF BAD ARGLIST, PROPAGATE THE ERROR MOVE T1,P2 ;GET THE SECOND ARG (NODE ID) PUSHJ P,SRCNDB ;GO SET W := NDB POINTER JRST ND%INN ;IF NO NDB, GIVE ILLEGAL NODE ERROR CAIN W,NETNDB## ;IF HE SPECIFIED THE LOCAL NODE, JRST NODE3L ; GO TRY TO SEND MSG TO A FEK HRRZ J,NDBSTC(W) ;SEE IF ANYONE IS USING STATION CONTROL CAMN J,.CPJOB## ;IF WE ARE USING IT, THEN ASSUME A PAGE JRST N34NRD ; FAULT WHILE READING STC AND TRY AGAIN JUMPN J,ND%NNA ;IF SOMEONE ELSE HAS IT, NOT AVAILABLE HRRZ J,NDBICT(W) ;GET MESSAGE ADDR SKIPE J ;IS THERE ONE? PUSHJ P,GIVSTC ; FREE IT, SINCE LOOKING FOR A RESPONSE SETZM NDBICT(W) ;CLEAR THE INCOMING MESSAGE POINTER. ;NOW SEND THE STATION CONTROL MESSAGE MOVEI T1,(P3) ;GET THE ADDRESS OF THE OUTPUT MESSAGE HLRE T2,P3 ;GET ITS LENGTH IN BYTES JUMPLE T2,ND%IAL ;IF GARBAGE LENGTH, GIVE ILLEGAL ARG ERROR ADDI T2,3 ;ROUND UP AND GET THE LENGTH OF THE LSH T2,-2 ;MESSAGE IN WORDS PUSH P,T2 ;SAVE LENGTH IN WORDS FOR LATER HRLI T1,(IFIW) ;SECTION LOCAL ADDRESSING PUSHJ P,ARNGE## ;MAKE SURE ALL THE DATA IS IN CORE. JRST ND%ADC ;ADDRESS CHECK JFCL ;ADDR OK BUT ILLEGAL FOR I/O (IGNORED HERE) HRRZ T1,(P) ;GET THE WORD COUNT BACK ADDI T1,5 ;ACCOUNT FOR A HEADER AND WORD ALIGNMENT PUSHJ P,MKNPCB ;MAKE A "NUMBERED" PCB JRST [POP P,T1 ;IF NO CORE, CLEAN UP THE STACK JRST ND%NFC] ; AND GIVE THE "NO FREE CORE" ERROR PUSH P,P2 ;SAVE THE NODE'S NAME/NUMBER PUSH P,P3 ;SAVE THE OUTPUT BUFFER POINTER MOVE P2,PCBPTR(U) ;GET BYTE POINTER TO THE PCB SETZ T1, ;WE WANT AN NCT OF 0 (NUMBERED MSG) PUSHJ P,NCSWHD ;WRITE THE NCL HEADER ;DLA XMTI 0 ;DLA = 0 MEANS NOT DEVICE CONTROL ;CNT HLRZ T1,(P) ;GET MESSAGE'S BYTE COUNT ADDI T1,1 ; +1 FOR "NC.CTL" TYPE FIELD XMT T1 ;WRITE THE COUNT ;TYP XMTI NC.CTL ;THIS IS A STATION CONTROL MESSAGE ADDM P3,PCBCTR(U) ;THAT'S ALL OF THE "HEADER" UPDATE ITS LENGTH POP P,P3 ;GET THE OUTPUT MESSAGE POINTER BACK ;NOW BLT THE DATA INTO THE PCB MOVEI T1,1(P2) ;GET THE ADDRESS OF FIRST "FULL" FREE WORD HRLI T1,(POINT 8,0) ;MAKE ADDRESS A BYTE POINTER MOVEM T1,PCBPT2(U) ; STORE IT AS "SECONDARY" BUFFER POINTER HLRZM P3,PCBCT2(U) ;SET NUMBER OF BYTES TO WRITE AS SECONDARY SIZE HRLI T1,(P3) ;GET THE USER ADDRESS OF THE DATA IN SRC(BLT) POP P,P2 ;GET THE NODE NUMBER BACK IFE FTXMON,< POP P,T2 ;GET THE LENGTH OF THE DATA IN WORDS ADDI T2,-1(T1) ;GET ADDRESS OF LAST WORD TO FILL WITH DATA EXCTUX ;COPY THE DATA TO THE PCB > IFN FTXMON,< XSFM T2 ;GET PCS HRLZS T2 ;IN LH WHERE IT BELONGS HLR T2,T1 ;FORM USER SOURCE ADDRESS HRRZ T3,T1 ;FORM MONITOR DESTINATION ADDRESS POP P,T1 ;GET THE LENGTH OF THE DATA IN WORDS XBLTUX T1 ;COPY THE DATA TO THE PCB > MOVEI T1,PCV.NC ;SAY THAT DATA IS 8 BIT BYTES (NO CONVERSION) PUSHJ P,NTDWRT ;GIVE THE MESSAGE TO THE NETWORK ;NOW CLAIM OWNERSHIP OF STATION CONTROL AND WAIT FOR A RESPONSE JUMPE P4,CPOPJ1## ;IF NO RESPONSE BUFFER, UUO IS DONE. HLLZ T1,P1 ;LH OF NDBSTC := SECONDS TO WAIT FOR ANSWER SKIPN T1 ;IF USER SPECIFIED A TIME, THEN USE HIS MOVSI T1,10 ; OTHERWISE DEFAULT TO 8 SECONDS MOVE J,.CPJOB## ;GET OUR JOB NUMBER HRRI T1,(J) ;RH OF NDBSTC := JOB TO WAKE UPON ANSWER MOVEM T1,NDBSTC(W) ;STORE NDBSTC SO ICMCTL CAN WAKE US NODE31: MOVEI T1,EV.STC ;GET THE "STATION CONTROL" EVENT WAIT CODE PUSHJ P,[NTSAVE ; RETURN THE "NT" RESOURCE JRST ESLEEP##] ; AND SLEEP UNTIL MSG COMES OR WE TIME OUT MOVE T1,P2 ;WE'VE BEEN AWAKENED. TRACK THE NDB DOWN AGAIN PUSHJ P,SRCNDB ; AS IT MAY HAVE CRASHED. PJRST ND%INN ;IF NDB GONE, GIVE ILLEGAL NODE ERROR SKIPE T1,NDBICT(W) ;SEE IF THERE IS AN INCOMING MESSAGE PJRST N34NRD ;IF RESPONSE, GO READ IT SKIPN T1,NDBSTC(W) ;SEE IF STATION CONTROL STILL ASSIGNED PJRST ND%TOE ;IF NOT, WE MUST HAVE TIMED OUT. MOVEI T1,(T1) ;GET JUST THE JOB NUMBER CAME T1,.CPJOB## ;MAKE SURE WE STILL OWN STATION CONTROL PJRST ND%TOE ;IF NOT, THEN TIME OUT ERROR JRST NODE31 ;IF SO, THEN SPURIOUS WAKE, SLEEP SOME MORE IFE FTKS10, ;THE KS-10 IS CURRENTLY THE ONLY SYSTEM ; THAT CAN DOWN-LINE LOAD ITS FEKS. IFN FTKS10,< ;IF ON A KS-10, ASSEMBLE STC-FEK STUFF ;HERE WHEN A STC MESSAGE FOR THE "LOCAL" NODE IS SEEN. SEE IF IT'S FOR ; ONE OF OUR LINES. IF IT IS, SEND THE STC MESSAGE TO THE FEK. NODE3L: MOVEI T1,(P3) ;GET THE ADDRESS OF THE STC MESSAGE MOVE J,.CPJOB## ;SET UP OUR JOB (SO WE DON'T GET AN LN1) PUSHJ P,UADRCK## ;MAKE SURE IT'S LEGAL AND IN CORE EXCTUX ;GET THE "LINE NUMBER FIELD" JUMPE T1,ND%INN ;DON'T LET STC TO THIS NODE DO ANYTHING PUSHJ P,LN2FEK ;CONVERT LINE NUMBER TO FEK ADDRESS (IN "J") JRST ND%ILN ;GIVE "ILLEGAL LINE NUMBER" IF NO SUCH FEK HRRZ T1,FEKBJN(J) ;GET THE NUMBER OF JOB OWNING STC ON FEK CAMN T1,.CPJOB## ;IF WE OWN THE STC OF THIS FEK, JRST NODFRD ; ASSUME PAGE FAULT WHILE STORING RESPONSE JUMPN T1,ND%NNA ;IF SOMEONE ELSE HAS STC, SAY "NOT AVAIL" ;HERE STC FOR THIS FEK IS FREE. COPY THE MESSAGE AND SEND IT MOVE W,J ;USE "W" AS A FEK POINTER FOR A BIT SKIPE J,FEKICT(W) ;SEE IF THERE ARE ANY STALE INPUT MSGS PUSHJ P,GIVSTC ;IF STALE MSGS, FREE THEM SETZM FEKICT(W) ;CLEAR INCOMING MESSAGE POINTER MOVEI T1,(P3) ;GET USER ADDRESS OF MESSAGE TO SEND HLRE T2,P3 ;GET LENGTH OF THE MESSAGE (BYTES) JUMPLE T2,ND%IAL ;SAY ILLEGAL ARG LIST IF GARBAGE LENGTH ADDI T2,3 ;WE WANT LENGTH IN WORDS. ROUND BYTES UP LSH T2,-2 ; AND DIVIDE BY 4 BYTES PER WORD PUSH P,T2 ;SAVE THE WORD COUNT FOR BLT COMING UP HRLI T1,(IFIW) ;SECTION LOCAL ADDRESSING PUSHJ P,ARNGE## ;MAKE SURE WE DON'T IME. JRST ND%ADC ;ADDRESS CHECK JFCL ;ADDR OK BUT ILLEGAL FOR I/O (IGNORED HERE) HLRE T1,P3 ;GET THE BYTE COUNT AGAIN PUSHJ P,GETSTC ;GET A STC BLOCK TO HOLD THE DATA JRST [POP P,T1 ;IF NO CORE, FIXUP THE STACK JRST ND%NFC] ; AND TELL THE USER IFE FTXMON,< MOVSI T1,(P3) ;SOURCE(BLT) := USER ADDRESS HRRI T1,STCDAT(J) ;DEST(BLT) := STC BLOCK POP P,T2 ;GET THE LENGTH (IN WORDS) BACK ADDI T2,-1(T1) ;GET LAST MONITOR ADDRESS TO FILL EXCTUX ;COPY THE DATA INTO THE STC BLOCK > IFN FTXMON,< XSFM T2 ;GET PCS HRLZS T2 ;IN LH WHERE IT BELONGS HRRI T2,(P3) ;FORM USER SOURCE ADDRESS HRRZI T3,STCDAT(J) ;FORM MONITOR DESTINATION ADDRESS POP P,T1 ;GET THE LENGTH (IN WORDS) BACK XBLTUX T1 ;COPY DATA TO STC BLOCK > ;NOW PUT OUR NODE NUMBER IN THE FIRST BYTE, AND GIVE MSG TO THE FEK HLRZ T1,NETNDB##+NDBNNM ;GET OUR NODE NAME DPB T1,[POINT 8,STCDAT(J),7] ; AND STORE IT AS THE "SNA" MOVE U,J ;SET UP U := STC MESSAGE MOVE J,W ; AND J := FEK -- FOR CALL TO FEK HLRE T1,P1 ;GET THE USERS TIMER VALUE SKIPG T1 ;IF HE DIDN'T SPECIFY A VALUE MOVEI T1,10 ; THEN USE 8 SECONDS MOVSI T1,(T1) ;PUT TIMER IN THE LH HRR T1,.CPJOB## ;GET OUR JOB NUMBER MOVEM T1,FEKBJN(J) ;CLAIM OWNERSHIP OF THE FEK'S STC MOVEI T1,FF.STC ;GET THE "STC" FUNCTION CODE XCT FEKDSP(J) ;CALL THE FEK JRST [SETZM FEKBJN(J);IF THE FEK DOESN'T ACCEPT THE STC, MOVEI J,(U) ; CLEAR STC OWNERSHIP, GET STC IN "J" PUSHJ P,GIVSTC ; FREE THE STC BLOCK JRST ND%ILN] ; AND GIVE ILLEGAL LINE ERROR JUMPE P4,[SETZM FEKBJN(J) ;IF NO RESPONSE WANTED, CLEAR OWNER RETSKP] ; AND GIVE GOOD RETURN ;NOW WE'VE SENT THE MESSAGE. CHECK FOR A RESPONSE. IF NONE, WAIT. NOD3L1: HRRZ T1,FEKBJN(J) ;GET THE OWNER OF THE FEK'S STC CAME T1,.CPJOB## ;MAKE SURE IT'S US. JRST ND%TOE ;IF NOT US, THEN GIVE "TIME OUT ERROR" SKIPE FEKICT(J) ;SEE IF ANY INCOMING MSGS. JRST NODFRD ;IF INCOMING MESSAGE. RETURN IT TO THE USER MOVEI T1,EV.STC ;GET THE "STATION CONTROL" EVENT WAIT CODE PUSH P,J ;SAVE OUR FEK FOR A BIT MOVE J,.CPJOB## ;SET J := JOB NUMBER (FOR ESLEEP) PUSHJ P,[NTSAVE ;RETURN THE "NT" INTERLOCK JRST ESLEEP##] ; AND SLEEP WAITING FOR A RESPONSE POP P,J ;GET OUR FEK ADDRESS BACK JRST NOD3L1 ;UPON AWAKING, SEE IF WE HAVE A MSG. >;END IFN FTKS10 SUBTTL NODE.4 RECEIVE STATION CONTROL BOOT MESSAGES ;FORMAT OF THE UUO ARGS ; EXP 0 ; EXP 0 ; EXP 0 ; XWD COUNT,ADR OR RESPONSE BUFFER NODE.4: PUSHJ P,NTDPRV ; SURE THAT THIS GUY IS PRIVILEDGED PJRST ND%PRV ;USER HAS INSUFFICIENT PRIVILEDGES PUSHJ P,SAVJW## ;WE WILL CLOBBER THESE PUSHJ P,SAVE4## ;WE COPY THE UUO ARGS IN P1 - P4 PUSHJ P,N34SET ;GO READ THE UUO ARGS POPJ P, ; IF BAD ARGLIST, PROPAGATE THE ERROR ;NOW SEARCH FOR AN NDB WITH AN UNCLAIMED INCOMING CONTROL MESSAGE SKIPA W,[EXP NETNDB##];START WITH OUR NDB NODE41: HRRZ W,NDBNNM(W) ;STEP TO THE NEXT NDB JUMPE W,NODE42 ;IF NO MSGS ON NDB'S, CHECK FEKS SKIPN NDBSTC(W) ;IF SOME JOB IS WAITING, OR SKIPN NDBICT(W) ; IF NO STC MESSAGE HAS COME, JRST NODE41 ; THEN GO TRY NEXT NDB N34NRD: HRRZ J,NDBICT(W) ;GET THE ADDRESS OF THE STC MESSAGE JUMPE J,ND%TOE ;IF NONE, THEN PROBABLY A TIME OUT HLL T1,STCBLK(J) ;THE NUMBER OF BYTES IS THE LH(STCBLK) HLLZM T1,STCLNN(J) ; SAVE THE COUNT FOR N34RED PUSHJ P,N34RED ;STORE THE MESSAGE IN THE USERS BUFFER SOS (P) ;PROPAGATE THE ERROR (IF ANY) SETZM NDBSTC(W) ;CLEAR STC OWNERSHIP SETZM NDBICT(W) ;CLEAR (NOW FREED) INCOMING STC MSG RETSKP ;GIVE (POSSIBLY BAD) RETURN IFE FTKS10, ;IF NO MESSAGES FROM FEKS, SAY NO MSG AVAIL IFN FTKS10,< ;THE KS-10 GETS STC MSGS FROM ITS D8K FEKS NODE42: MOVEI J,FEKFST## ;GET THE ADDRESS OF THE FIRST FEK. CAIA ;SKIP INTO THE LOOP NODE43: HRRZ J,FEKBLK(J) ;ADVANCE TO THE NEXT FEK JUMPE J,ND%NMA ;IF NO MORE, SAY "NO MESSAGE AVAILABLE" SKIPN FEKBJN(J) ;IF SOMEONE IS WAITING, OR SKIPN FEKICT(J) ; THERE ISN'T A MESSAGE JRST NODE43 ;GO TRY THE NEXT FEK. ;NODFRD ROUTINE TO RETURN THE STC MESSAGE QUEUED ON FEK ;CALL J := FEK ;RETURN CPOPJ ;ERROR (BUT STC FREED) ; CPOPJ1 ;OK (STC FREED) NODFRD: SKIPN U,FEKICT(J) ;GET U := INCOMING MESSAGE JRST ND%TOE ;IF NONE, ASSUME A TIME OUT ERROR (?) EXCH J,U ;REMEMBER THE FEK, SET J := STC MSG ;NOW REMOVE ANY NODE NUMBER ON THE MESSAGE AND INSERT THE LINE NUMBER ; THIS MEANS COPYING THE MESSAGE DOWN IF THE NODE NUMBER IS EXTENSIBLE HLRZ T1,STCBLK(J) ;GET THE INITIAL LENGTH OF THE MESSAGE MOVE T2,[POINT 8,STCDAT(J)] ;A POINTER TO THE MESSAGE MOVEI T3,1 ;FINAL VALUE OF MESSAGE (START WITH LINE #) MOVE T4,T2 ;"PUTTER" BYTE POINTER FOR COPYING SOJGE T1,[ILDB W,T2 ;GET NEXT BYTE TRNE W,200 ;AND SEE IF IT'S STILL THE NODE NUMBER JRST . ;STILL EXTENSIBLE, KEEP LOOKING JRST .+1] ; END OF NODE NUMBER HRRZ W,STCLNN(J) ;GET THE LINE NUMBER IDPB W,T4 ;STORE AS THE FIRST BYTE ; AOS T3 ;COUNT THE BYTE (DONE ABOVE...) SOJGE T1,[ILDB W,T2 ;FOR ALL THE REST OF THE BYTES, LOAD IDPB W,T4 ; AND STORE IN THE NEW MESSAGE AOJA T3,.] ; COUNT THE BYTE AND LOOP HRLM T3,STCLNN(J) ;STORE THE LENGTH FOR N34RED MOVEI W,NETNDB## ;SET UP "W" := NETNDB TO RETURN OUR NODE # PUSHJ P,N34RED ;STUFF THE MESSAGE IN THE USERS BUFFER SOS (P) ;IF FAIL RETURN, MAKE SURE WE DON'T SKIP SETZM FEKBJN(U) ;CLEAR THE STC OWNER SETZM FEKICT(U) ;CLEAR ANY INCOMING MSG (N34RED FREED IT) RETSKP >; END OF IFN FTKS10 ;N34RED ROUTINE TO COPY STC MESSAGES BACK INTO THE USERS BUFFER ;CALL P1 - P4 := AS SET UP BY N34SET ; J := ADDR OF STC MESSAGE TO COPY ; W := NDB OF NODE WHOS NUMBER WE RETURN AS THE BOOT NODE ;RETURN NOT AT ALL IF ADDRESS CHECK OR PAGE FAULT ; CPOPJ ;IF ERROR, WITH STC BLOCK FREED ; CPOPJ1 ;IF OK, WITH STC BLOCK FREED N34RED: MOVEI T1,(P4) ;GET THE ADDRESS OF THE USER'S INPUT BUFFER HLRZ T2,STCLNN(J) ;GET THE LENGTH OF THE STC MESSAGE (BYTES) HLRZ T3,P4 ;GET THE LENGTH OF THE USER'S BUFFER (BYTES) CAIGE T3,(T2) ;IF THE MESSAGE IS LONGER THAN THE USER'S JRST [PUSHJ P,GIVSTC ;IF STC WON'T FIT, FREE THE MESSAGE PJRST ND%RBS] ; AND GIVE A "RECEIVE BUFFER 2 SMALL" ERROR ADDI T2,3 ;ROUND BYTES UP TO NEXT EVEN WORD LSH T2,-2 ;CONVERT BYTES TO WORDS PUSH P,T2 ;SAVE LENGTH IN WORDS HRLI T1,(IFIW) ;SECTION LOCAL ADDRESSING PUSHJ P,ARNGE## ;RANGE CHECK THE USER'S INPUT BUFFER AREA JRST ND%ADC ;ADDRESS CHECK JRST ND%ADC ;ILLEGAL FOR I/O IFE FTXMON,< POP P,T2 ;GET LENGTH BACK ADDI T2,-1(T1) ;COMPUTE LAST ADDRESS MOVEI T1,(P4) ;GET THE ADDRESS OF THE USER'S BUFFER BACK HRLI T1,STCDAT(J) ;GET THE ADDRESS OF THE STC MESSAGE EXCTXU ;COPY THE STC MESSAGE > IFN FTXMON,< XSFM T3 ;GET PCS HRLZS T3 ;IN LH WHERE IT BELONGS HRRI T3,(P4) ;FORM USER DESTINATION ADDRESS HRRZI T2,STCDAT(J) ;FORM MONITOR SOURCE ADDRESS POP P,T1 ;GET LENGTH IN WORDS BACK XBLTXU T1 ;COPY THE STC MESSAGE > PUSH P,J ;PROTECT STC POINTER FROM DEATH BY PUTWRD HRRI M,1(M) ;SET RH(M) TO THE ADDRESS TO RETURN NODE NUMBER HLRZ T1,NDBNNM(W) ;GET THE NODE NUMBER PUSHJ P,PUTWRD## ;RETURN THE NODE NUMBER JRST ND%ADC ;ADDRESS CHECK MOVEI T1,(P4) ;GET THE ADDRESS OF THE USERS INPUT BUFFER HRRZ J,(P) ;RECOVER STC POINTER AGAIN HLL T1,STCLNN(J) ;GET THE NUMBER OF VALID BYTES IN IT HRRI M,2(M) ;SET RH(M) TO ADDRESS OF USERS INPUT BUFFER PUSHJ P,PUTWRD## ;STORE UPDATED "XWD LENGTH,ADDR" FOR THE USER JRST ND%ADC ;ADDRESS CHECK POP P,J ;GET STC POINTER ONCE AGAIN PUSHJ P,GIVSTC ;RETURN THE STC BLOCK RETSKP ;GIVE GOOD RETURN ;N34SET ROUTINE TO SET P1 - P4 TO THE NODE. UUO ARGUMENTS ;CALL M := UUO N34SET: PUSHJ P,GETWRD## ;P1 GETS THE "XWD TIME,ARG-LIST-LENGTH" JRST ND%ADC ;ADDRESS CHECK MOVE P1,T1 HRRZ T1,T1 ;GET JUST THE ARG LIST LENGTH CAIGE T1,4 ;MAKE SURE IT'S LONG ENOUGH PJRST ND%IAL ; IF TOO SHORT, GIVE ILLEGAL ARG LIST PUSHJ P,GETWR1## ;P2 GETS THE NODE NAME JRST ND%ADC ;ADDRESS CHECK MOVE P2,T1 PUSHJ P,GETWR1## ;P3 GETS THE OUTPUT BUFFER POINTER JRST ND%ADC ;ADDRESS CHECK MOVE P3,T1 PUSHJ P,GETWR1## ;P4 GETS THE INPUT BUFFER POINTER JRST ND%ADC ;ADDRESS CHECK MOVE P4,T1 SUBI M,3 ;SET THE UUO POINTER BACK RETSKP ;SKIP RETURN WITH P1 - P4 SET UP SUBTTL NODE.5 RETURN CONFIG INFO FOR NODE ;FORMAT OF THE UUO ARGS ; XWD 0,,COUNT ; SIXBIT /NODE NAME/ OR NUMBER ; 0 RESERVED FOR DATE AND NAME ; BLOCK INFO RETURNED HERE NODE.5: PUSHJ P,SAVE1## ;SAVE P1 FIRST PUSHJ P,GETWRD## ;GET ARGUMENT LIST SIZE JRST ND%ADC ;ADDRESS CHECK SUBI T1,3 ;DEDUCT OVERHEAD CAIG T1,^D1000 ;RIDICULOUS SIZE SKIPG P1,T1 ;ANY ROOM LEFT FOR ANSWER PJRST ND%IAL ;NO, ILLEGAL ARG LIST PUSHJ P,GETWR1## ;GET NODE NAME JRST ND%ADC ;ADDRESS CHECK PUSHJ P,NODE.S ;FIND CORRECT NDB PJRST ND%INN ;ILLEGAL NODE PUSHJ P,GETWR1## ;GET RESERVED WORD JRST ND%ADC ;ADDRESS CHECK JUMPN T1,ND%RNZ ;MUST BE ZERO NODE51: PUSHJ P,GETWR1## ;GET DEVICE TYPE REQUEST JRST ND%ADC ;ADDRESS CHECK MOVSI T2,- ;NUMBER OF KNOWN DEVICES NODE52: LDB T3,[EXP OB%TYP##+OBJTAB##+(T2)] ;GET A TYPE CAIE T3,(T1) ;FOUND REQUEST AOBJN T2,NODE52 ;NO, TRY ANOTHER TYPE SKIPL T2 ;FIND ANY TDZA T2,T2 ;NONE OF REQUESTED TYPE LDB T2,NETCNF##(T2) ;GET NUMBER OF THAT TYPE HRL T1,T2 ;INSERT COUNT FOR USER PUSHJ P,PUTWRD## ;STORE BACK JRST ND%ADC ;ADDRESS CHECK SOJG P1,NODE51 ;DO FOR ALL ARGS PJRST CPOPJ1## ;GIVE GOOD RETURN SUBTTL NODE.6 NETWORK OUTPUT WITH USER CONTROL OF EOR REPEAT 0,< ;SUPERSEDED BY TSK. UUO ;FORMAT OF THE UUO ARGS ; XWD 0,NUMBER OF ARGS ; XWD FLAGS,IO CHANNEL NODE.6: PUSHJ P,SAVE1## ;HAVE TO SAVE FLAGS SOMEWHERE PUSHJ P,N67SET ;SETUP F, T1 MOVE P1,T1 ;SAVE PARAMETER WORD FOR EOR CHECK BELOW HLR M,DEVBUF(F) ;GET ADDRESS OF OUTPUT BUFFER HEADER PUSHJ P,GETWRD## ;GET ADDRESS OF CURRENT BUFFER JRST ND%ADC ;ADDRESS CHECK JUMPL T1,NODE61 ;IF BUFFER NEVER SET UP, JUST DO THE OUTPUT HRR M,T1 ;TO READ LINKAGE WORD OF BUFFER PUSHJ P,GETWRD## ;GET IT JRST ND%ADC ;ADDRESS CHECK JUMPL P1,NODE60 ;SKIP IF USER WANTS EOR TLOA T1,IONER ;DOESN'T, FLAG BUFFER NODE60: TLZ T1,IONER ;DOES, MAKE SURE THIS IS OFF PUSHJ P,PUTWRD## ;PUT BACK IN BUFFER JRST ND%ADC ;ADDRESS CHECK NODE61: PUSHJ P,TOUT## ;DO THE OUTPUT PJRST CPOPJ1## ;SUCCESS, GIVE USER A SKIP RETURN JRST N67IOE ;FAILED, RETURN ERROR STATUS >;REPEAT 0 SUBTTL NODE.7 NETWORK INPUT WITH NOTIFICATION OF END OF RECORD REPEAT 0,< ;SUPERSEDED BY TSK. UUO ;FORMAT OF UUO ARGS ; XWD 0,NUMBER OF ARGS ; XWD 0,IO CHANNEL NODE.7: PUSHJ P,N67SET ;VERIFY IO CHANNEL, SETUP F, T1 PUSHJ P,TIN## ;DO THE INPUT JRST NODE70 ;SUCCESS, CHECK EOR N67IOE: MOVEI T1,11 ;ERROR CODE 11 IS IO ERROR HRL T1,DEVIOS(F) ;AND WE RETURN THE GETSTS INFO TO THE PJRST STOTAC## ; SO THE USER DOESN'T NEED ANOTHER UUO NODE70: HRR M,DEVBUF(F) ;GET ADDRESS OF INPUT BUFFER HEADER PUSHJ P,GETWRD## ;GET IT JRST ND%ADC ;ADDRESS CHECK HRR M,T1 ;GET ADDRESS OF BUFFER LINKAGE WORD PUSHJ P,GETWRD## ;GET THAT JRST ND%ADC ;ADDRESS CHECK TLNE T1,IONER ;NOW, DOES THIS BUFFER NOT MARK THE END OF RECORD? TDZA T1,T1 ;RIGHT, MAKE BIT 0 0 MOVSI T1,400000 ;END OF RECORD, SET BIT 0 PJRST STOTC1## ;RETURN THAT TO USER ;ROUTINE FOR NODE. UUO FUNCTIONS 6 AND 7 TO DO COMMON SETUP. ; M/RH: ADDR OF ARG LIST (SETUP BY UUOCON) ; PUSHJ P,N67SET ; NORMAL RETURN, ; F/ DDB ADDRESS, T1/ FLAGS,,IO CHANNEL ;THIS ROUTINE ABORTS THE UUO IF THE IO CHANNEL IS NOT OPEN OR THE DEVICE ;IS NOT A NETWORK DEVICE. N67SET: PUSHJ P,GETWRD## ;GET ARG LIST LENGTH JRST ND%ADC ;ADDRESS CHECK CAIGE T1,2 ;NEED AT LEAST 2 WORDS PJRST ND%IAL ;TOO SHORT, RETURN LIST LENGTH ERROR PUSHJ P,GETWR1## ;GET FLAGS,,IOCHAN WORD JRST ND%ADC ;ADDRESS CHECK PUSH P,T1 PUSHJ P,SETUF## JRST [POP P,(P) PJRST ND%IAL] POP P,T1 MOVSI T2,DVCNET ;MAKE SURE DEVICE IS A NETWORK DEVICE TDNN T2,DEVCHR(F) ; FOR PARANOIA'S SAKE PJRST ND%NND ;IT ISN'T, ABORT UUO POPJ P, ;RETURN TO DO IO >;REPEAT 0 ;NODE10 (.NDTCN) UUO TO ATTEMPT TO CONNECT REMOTE TERMINAL'S TO THE SYSTEM. ;ARGUMENT BLOCK FORMAT. ; ; 1) XWD 0,LENGTH ;LENGTH ALWAYS = 2 ; 2) XWD NODE,LINE ;NODE AND LINE TO TRY TO CONNECT ; ;RETURNS ; SIXBIT /TTYNNN/ ;THE NAME THE TERMINAL WAS ASSIGNED. ; ;ERRORS ; NDIAL%==1 ;ILLEGAL ARGUMENT LIST ; NDTNA%==2 ;TERMINAL NOT AVAILABLE. ; NODE10: PUSHJ P,GETWRD## ;GET THE ARG-LIST LENGTH JRST ND%ADC ;ADDRESS CHECK CAIE T1,2 ;WE ONLY WANT 1 DATA ARGUMNET JRST ND%IAL ;ILLEGAL ARG LIST (NDIAL%) PUSHJ P,GETWR1## ;GET THE "XWD NODE,LINE" TO CONNECT TO JRST ND%ADC ;ADDRESS CHECK PUSHJ P,ASGTTY## ;ATTEMPT THE CONNECT. PJRST ND%TNA ;TERMINAL NOT AVAILABLE (NDTNA%) PJRST STOTC1## ;RETURN TERMINAL NUMBER IN AC ;NODE11 (.NDTDS) DISCONNECT A REMOTE TERMINAL FROM THE SYSTEM (WITH OPTIONAL ; RECONNECT TO ANOTHER SYSTEM). ; ;NODE. UUO ARGUMENT BLOCK. ; 1) XWD 0,LENGTH ;EITHER 2 OR 3 (3 IMPLIES AUTO RECONNECT) ; 2) SIXBIT /TTYNNN/ ;NAME OF TERMINAL DO DIS/RE-CONNECT ; 3) EXP NODE-NUMBER ;OPTIONAL NODE NUMBER TO RECONNECT TO. ; ;RETURNS ; NOTHING. ON A SUCCESSFUL RETURN, THE AC IS UNCHANGED ; ;ERRORS ; NDIAL%==1 ;ILLEGAL ARGUMENT LIST (LENGTH WRONG) ; NDINN%==2 ;ILLEGAL NODE NUMBER/NAME (CAN'T HAPPEN...) ; NDIAJ%==3 ;THE TERMINAL IS IN USE BY ANOTHER JOB. ; NDNLT%==4 ;USER DID NOT GIVE A LEGAL TERMINAL NAME AS ; ; THE SECOND ARGUMENT. ; NODE11: PUSHJ P,SAVE4## ;WE USE THE P'S FOR TEMPS PUSHJ P,GETWRD## ;GET THE LENGTH OF THE ARG-LIST JRST ND%ADC ;ADDRESS CHECK CAIL T1,2 ;RANGE CHECK THE LENGTH. MUST CAILE T1,3 ; BE BETWEEN 2 AND 3 JRST ND%IAL ;ILLEGAL ARG LIST (NDIAL%) MOVE P2,T1 ;SAVE THE LENGTH FOR A BIT PUSHJ P,GETWR1## ;GET THE TERMINAL NAME JRST ND%ADC ;ADDRESS CHECK MOVE P3,T1 ;SAVE THE NAME MOVEI P4,RSN.OK ;ASSUME THAT THIS IS SIMPLE DISCONNECT. CAIN P2,3 ;BUT IF THIS IS A RE-CONNECT, JRST [PUSHJ P,GETWR1## ;THEN GET NODE NUMBER TO RECONNECT TO. JRST ND%ADC ;ADDRESS CHECK HRLZ P4,T1 ;AND PUT NODE NUMBER IN LH HRRI P4,RSN.RC ;REASON IS "RE-CONNECT" JRST .+1] ;REJOIN MAIN FLOW WITH ALL ARGS READ NOD11A: MOVE T1,P3 ;GET THE TERMINAL NAME BACK PUSHJ P,TTYPHY## ;SET U := LDB, F := DDB(IF ANY) PJRST ND%NLT ;GIVE "NDNLT%" ERROR IF TERMINAL NAME IS BAD JUMPN F,[LDB T1,PJOBN## ;IF WE FOUND A LDB, SEE IF WE GOT A DDB. SKIPE T1 ;IF NO-ONE OWNS IT, WE'RE OK CAMN T1,.CPJOB## ; IF WE OWN IT WE'RE OK CAIA ; WE'RE OK JRST ND%IAJ ; IF WE DON'T OWN IT, GIVE "NDIAJ%" ERROR PUSHJ P,TTYDTC## ; DETACH THE TERMINAL JRST .+1] ; IF WE DO OWN IT, THEN ALL'S OK PUSHJ P,MCRCHK## ;SEE IF IT'S A LEGAL REMOTE TERMINAL. JRST ND%NLT ;IF NOT, THEN RETURN "NDNLT%" LDB T1,LDPRNN## ;GET THE NODE NUMBER THAT IT'S CONNECTED TO PUSHJ P,SRCNDB ;FIND THE NDB STOPCD .,STOP,ANFNNT, ;++ NO NBD FOR TERMINAL MOVE T1,P4 ;GET "XWD NODE,REASON" PUSHJ P,TRMXDC## ;SEND THE DISCONNECT JRST [PUSHJ P,NETSLP ;IF NO CORE, SLEEP FOR A BIT JRST NOD11A] ; AND TRY AGAIN JRST CPOPJ1## ;ALL DONE. GIVE GOOD RETURN ;NODE12 (.NDLND) RETURNS THE NUMBER OF NODES IN THE NETWORK, AND A LIST ; OF THE NODE NUMBERS. ; ;NODE. UUO ARGUMENT BLOCK ; 1) XWD 0,LENGTH ;LENGTH OF ARG BLOCK (INCLUDES THIS WORD) ; 2) EXP RTN-VAL-1 ;FILLED IN WITH NODE NUMBER OF FIRST NODE ; 3) EXP RTN-VAL-2 ;FILLED WITH SECOND NODE NUMBER ; . ; . ; . ; N+1) EXP RTN-VAL-N ;FILLED WITH NUMBER OF N'TH NODE ; ;RETURNS ; IN THE AC, THE NUMBER OF NODES IN THE NETWORK ; IN ARG-LIST(2 THRU N+1) THE NUMBERS OF THE NODES IN THE NETWORK ; ;ERRORS ; NDIAL%==1 ;ARG-LIST LENGTH LESS THAN 1 ; ;NOTE! NO ERROR IS RETURNED IF THE ARGUMENT LIST IS TOO SHORT TO HOLD ; ALL THE NODES. THE EXTRA ARGUMENTS ARE SIMPLY NOT RETURNED. NODE12: PUSHJ P,GETWRD## ;GET THE LENGTH OF THE ARG LIST JRST ND%ADC ;ADDRESS CHECK CAIGE T1,1 ;MAKE SURE IT'S LEGAL JRST ND%IAL ;NDIAL% ILLEGAL ARG LIST MOVE T3,T1 ;SAVE THE ARG-LIST LENGTH FOR A WHILE MOVEI T2,NETNDB## ;GET THE ADDRESS OF THE FIRST DDB SETZ T4, ;WE'VE COUNTED NO NODES YET. NOD12A: JUMPE T2,[MOVE T1,T4 ;IF WE'VE COUNTED ALL THE NODES, JRST STOTC1##] ;COPY THE COUNT AND RETURN IT HLRZ T1,NDBNNM(T2) ;GET THE NUMBER OF THE NODE SOJLE T3,NOD12B ;COUNT DOWN ANOTHER RETURN-VALUE PUSHJ P,PUTWR1## ;RETURN THE NODE NUMBER JRST ND%ADC ;ADDRESS CHECK NOD12B: HRRZ T2,NDBNNM(T2) ;STEP TO THE NEXT NODE AOJA T4,NOD12A ;COUNT THIS ONE AND LOOP OVER ALL NODES ;NODE13 (.NDNDB) RETURN INFORMATION ABOUT AN NDB. ; ;NODE. UUO ARGUMENT LIST ; 1) XWD 0,LENGTH ;LENGTH OF ARG LIST. (INCLUDES THIS WORD) ; 2) EXP NODE NUMBER/NAME;NUMBER OR NAME OF THE NODE IN QUESTION ; 3) EXP SUB-FCN NUMBER ;DESCRIBES WHICH FIELD TO RETURN ; 4) RTN-VALUE-1 ;FIRST RETURN VALUE ; 5) RTN-VALUE-2 ;SECOND RETURN VALUE ; . ; . ; . ; 3+N) N'TH-RETURN-VALUE ;LAST RETURN VALUE ; ;ERRORS ; NDIAL%==1 ;ARG LIST TOO SHORT ; NDINN%==2 ;ILLEGAL NODE NUMBER/NAME ; NDISF%==3 ;ILLEGAL SUB-FUNCTION ; ;VALID FIELDS (ARG #3) ; ND.NNM==1 ;THE NUMBER OF THE SPECIFIED NODE ; ND.SNM==2 ;THE NAME IN 6 OR LESS SIXBIT CHARACTERS ; ND.SID==3 ;THE SOFTWARE ID IN "ASCIZ" ; ND.DAT==4 ;THE SOFTWARE DATE IN "ASCIZ" ; ND.LMA==5 ;THE NCL LAST MESSAGE ASSIGNED ; ND.LMS==6 ;THE NCL LAST MESSAGE SENT ; ND.LAR==7 ;THE NCL LAST ACK RECEIVED ; ND.LAP==10 ;THE NCL LAST ACK PROCESSED ; ND.LMR==11 ;THE NCL LAST MESSAGE RECEIVED ; ND.LMP==12 ;THE NCL LAST MESSAGE PROCESSED ; ND.LAS==13 ;THE NCL LAST ACK SENT ; ND.MOM==14 ;THE MAXIMUM OUTSTANDING MESSAGE COUNTER ; ND.TOP==15 ;THE TOPOLOGY TABLE. THIS IS "NGHMAX" WORDS ; ; OF THE FORM "XWD LEVEL,NODE" ; ND.CNF==16 ;THE CONFIGURATION TABLE. THIS IS VARIABLE ; ; LENGTH OF THE FORM "XWD OBJ,NUMBER" ; ND.CTL==17 ;RETURN STATION CONTROL JOB ; ND.OPR==20 ;RETURN OPR TTY NUMBER ; ND.NVR==21 ;RETURN NCL VERSION NUMBER ; NODE13: PUSHJ P,SAVE4## ;WE USE THE P'S AS TEMPS PUSHJ P,GETWRD## ;GET THE LENGTH OF THE ARG LIST JRST ND%ADC ;ADDRESS CHECK CAIGE T1,4 ;MAKE SURE THAT IT'S AT LEAST 4 WORDS LONG JRST ND%IAL ;RETURN NDIAL% -- ILLEGAL ARG LIST MOVE P4,T1 ;SAVE THE LENGTH FOR A WHILE SUBI P4,3 ;ADJUST SO P4 = RETURN LOCATIONS FREE PUSHJ P,GETWR1## ;GET THE NODE NUMBER/NAME JRST ND%ADC ;ADDRESS CHECK PUSHJ P,SRCNDB ;SET UP W FROM THE NUMBER/NAME JRST ND%INN ;RETURN NDINN% -- ILLEGAL NODE NUMBER PUSHJ P,GETWR1## ;GET THE SUB-SUB-FUNCTION CODE. JRST ND%ADC ;ADDRESS CHECK CAIL T1,1 ;RANGE CHECK IT CAILE T1,N13LEN ; TO MAKE SURE IT'S VALID JRST ND%ISF ;RETURN NDISF% -- INVALID SUB-FUNCTION XCT N13TBL-1(T1) ;CALL ROUTINE TO GET FIELD SKIPA ;MUST STORE JRST CPOPJ1## ;DON'T N13WD1: PUSHJ P,PUTWR1## ;NON-SKIP MEANS WE SHOULD STORE ARG HERE JRST ND%ADC ;ADDRESS CHECK JRST CPOPJ1## ;ALL DONE. ;N13TBL TABLE OF INSTRUCTIONS TO EXECUTE TO RETURN VARIOUS FIELDS OF AN NDB N13TBL: HLRZ T1,NDBNNM(W) ;(01) GET THE NODE NUMBER FIELD MOVE T1,NDBSN2(W) ;(02) GET NODE NAME JRST N13SID ;(03) CALL SUBROUTINE TO GET SOFTWARE-ID JRST N13DAT ;(04) CALL SUBROUTINE TO GET SOFTWARE-DATE LDB T1,NDBLMA ;(05) GET LAST MESSAGE ASSIGNED LDB T1,NDBLMS ;(06) GET LAST MESSAGE SENT LDB T1,NDBLAR ;(07) GET LAST ACK RECEIVED LDB T1,NDBLAP ;(10) GET LAST ACK PROCESSED LDB T1,NDBLMR ;(11) GET LAST MESSAGE RECEIVED LDB T1,NDBLMP ;(12) GET LAST MESSAGE PROCESSED LDB T1,NDBLAS ;(13) GET LAST ACK SENT MOVE T1,NDBMOM(W) ;(14) GET MAXIMUM OUTSTANDING MESSAGE COUNTER JRST N13TOP ;(15) RETURN TOPOLOGY TABLE JRST N13CNF ;(16) RETURN CONFIGURATION TABLE HRRZ T1,NDBSTC(W) ;(17) GET STATION CONTROL JOB NUMBER PUSHJ P,N13OPR ;(20) GET OPR TTY NUMBER HRRZ T1,NDBNVR(W) ;(21) GET NCL VERSION NUMBER N13LEN==.-N13TBL ;DEFINE LENGTH FOR RANGE CHECK ;N13SID (03) RETURN THE SOFTWARE ID (VERSION INFO) FOR THE NODE. N13SID: HRRZ P1,NDBSID(W) ;GET A POINTER TO THE SID PJRST N13ASZ ;AND GO USE ASCIZ COPY ROUTINE ;N13DAT (04) RETURN THE SOFTWARE DATE FOR THE NODE N13DAT: HLRZ P1,NDBSID(W) ;GET A POINTER TO THE DATE ; PJRST N13ASZ ;AND GO USE THE ASCIZ COPY ROUTINE N13ASZ: SOSGE P4 ;COUNT DOWN NEXT WORD TO RETURN JRST ND%IAL ;IF WE COUNT OUT, THEN LIST WAS TO SHORT SKIPE T1,P1 ;UNLESS POINTER NOT PRESENT, MOVE T1,(P1) ;GET THE NEXT WORD TO RETURN PUSHJ P,PUTWR1## ;STORE THE WORD IN THE ARG LIST JRST ND%ADC ;ADDRESS CHECK MOVEI T2,5 ;COUNT TO SEE ANY CHAR WAS A NULL N13AS1: TLNN T1,774000 ;WAS THIS CHAR A NUL JRST CPOPJ1## ;IF SO, GIVE SKIP RETURN LSH T1,7 ;SHIFT TO NEXT CHAR POSITION SOJG T2,N13AS1 ;LOOP OVER ALL 5 CHARACTERS AOJA P1,N13ASZ ;LOOP OVER ALL WORDS IN NAME ;N13TOP (15) RETURN THE TOPOLOGY TABLE FOR THE PARTICULAR NODE. ; ;THE TABLE IS RETURNED IN THE FORM OF UP TO 16 WORDS OF ; XWD LEVEL,NODE ; WHERE NODE IS THE NEIGHBOR OF THE NODE IN QUESTION, AND LEVEL IS THE ; "COST" OF THE LINK TO GET THERE N13TOP: MOVE P3,[POINT 9,NDBTOP(W)] ;POINTER TO THE TOPOLOGY TABLE MOVEI P2,NGHMAX ;MAX LENGTH OF THE TOPOLOGY TABLE N13TP1: SOSGE P2 ;COUNT DOWN ANOTHER ENTRY JRST N13RZR ;ALL DONE. WRITE TERMINATING ZERO AND RETURN ILDB T1,P3 ;GET THE NEXT ENTRY ILDB T2,P3 ;GET ITS COST JUMPE T1,N13TP1 ;IF NULL, KEEP TRYING SOSGE P4 ;COUNT DOWN THE LENGTH OF THE ARGLIST JRST ND%IAL ;ILLEGAL ARGLIST (TOO SHORT) HRLI T1,(T2) ;PUT COST IN LH OF RETURN VALUE PUSHJ P,PUTWR1## ;STORE THE "XWD LEVEL,NODE" JRST ND%ADC ;ADDRESS CHECK JRST N13TP1 ;LOOP OVER ALL TABLE ENTRIES ;N13CNF (16) RETURN THE CONFIGURATION INFORMATION FOR A NODE. ; ; CONFIGURATION INFORMATION RETURNED AS UP TO "OBJ.MX" WORDS OF THE FORM ; XWD OBJECT-TYPE,NUMBER ; WHERE OBJECT-TYPE IS THE NCL TYPE OF THE DEVICE, AND NUMBER IS THE ; NUMBER OF SAID DEVICES THAT THE NODE POSSESSES. N13CNF: MOVEI P1,OBJ.MX+1 ;MAXIMUM OBJECT TYPE +1 N13CN1: SOJL P1,N13RZR ;COUNT DOWN THROUGH ALL OBJECT TYPES LDB T1,NETCNF##(P1) ;GET THE NUMBER OF THIS TYPE JUMPE T1,N13CN1 ;IF NONE, KEEP TRYING HRLI T1,(P1) ;PUT OBJECT TYPE IN THE LH SOSGE P4 ;COUNT DOWN ANOTHER RETURN VALUE JRST ND%IAL ;ARG LIST TOO SHORT PUSHJ P,PUTWR1## ;RETURN THE VALUE JRST ND%ADC ;ADDRESS CHECK JRST N13CN1 ;LOOP OVER ALL OBJECT TYPES ;N13RZR ROUTINE TO WRITE THE TERMINATING ZERO FOR N13TOP AND N13CNF N13RZR: SOSGE P4 ;COUNT DOWN THE ARGLIST JRST ND%IAL ;ERROR IF LIST TOO SHORT SETZ T1, ;ZERO TO STORE JRST N13WD1 ;AND LET NODE13 STORE IT FOR US. ;N13OPR (20) RETURNS OPR TTY NUMBER OR -1 IF NOT CONNECTED N13OPR: HRRZ T1,NDBOPR(W) ;GET LDB OF OPR LINE FOR THIS NODE SKIPN T1 ;HAVE ONE? SOJA T1,CPOPJ## ;NO--RETURN -1 EXCH U,T1 ;SAVE U, GET LDB ADDRESS SSX U,MS.SCN ;USE SCNSER SECTION LDB U,LDPLNO## ;GET THE LOCAL LINE NUMBER EXCH T1,U ;RESTORE U POPJ P, ;AND RETURN ;NODE14 (.NDGNF) READ/SET THE GREETED NODE FLAG ;NODE. UUO ARGUMENT BLOCK ; 1) XWD 0,LENGTH ;LENGTH MUST BE "2" ; 2) EXP 0!NODE # ;EITHER 0 (RETURN FIRST UN-GREETED NODE) ; ; OR NODE # (SET "GREETED BIT") ;RETURNS ; IN ARG 2 THE NODE # OF THE FIRST "UN-GREETED" NODE) ; ;ERRORS: ; ND%IAL ;ILLEGAL ARG LIST ; ND%NUG ;NO UNGREETED NODES ; ND%INN ;ILLEGAL NODE NUMBER (SET GREETED BIT FCN) ; ND%PRV ;USER NOT PRIVLEGDED ; NODE14: PUSHJ P,NTDPRV ;CHECK PRVS RIGHT AWAY PJRST ND%PRV ;USER DOESN'T HAVE PRVS TO DO THIS PUSHJ P,GETWRD## ;GET ARG LIST LENGTH JRST ND%ADC ;ADDRESS CHECK CAIGE T1,2 ;MAKE SURE ITS LONG ENOUGH PJRST ND%IAL ;IF TO SHORT, GIVE "ILLEGAL ARG LIST" ERROR PUSHJ P,GETWR1## ;GET 0 (RETURN) OR NODE # (SET) JRST ND%ADC ;ADDRESS CHECK JUMPE T1,NOD14A ;IF 0 (RETURN) GO SEARCH NDB'S PUSHJ P,SRCNDB ;GO SEE IF WE CAN FIND AN NDB FOR HIM PJRST ND%INN ;IF NO NDB, GIVE ILLEGAL NODE NUMBER ERROR MOVSI T1,NDB.GF ;GET THE "GREETED FLAG" IORM T1,NDBFLG(W) ; AND SET IT RETSKP ;GIVE GOOD RETURN TO THE UUO. NOD14A: HRRZI W,NETNDB ;GET THE ROOT OF ALL NODES NOD14B: HRRZ W,NDBNNM(W) ;GO TO NEXT NODE (SKIP NETNDB) JUMPE W,ND%NUG ;IF AT END OF LIST, GIVE NO UN-GREETED NODE ERR MOVE T1,NDBFLG(W) ;GET THE FLAGS TLNE T1,NDB.UP ;IF THE NODE IS NOT UP, TLNE T1,NDB.GF ; OR IT'S ALREADY BEEN GREETED JRST NOD14B ;THEN GO TRY THE NEXT NODE HLRZ T1,NDBNNM(W) ;THIS IS OUR NODE, GET THE NUMBER PUSHJ P,PUTWRD## ;STORE IT FOR THE USER JRST ND%ADC ;ADDRESS CHECK RETSKP ;GIVE GOOD RETURN TO UUO SUBTTL INTERFACE TO UUOCON (RECON. UUO) ;SYSTEM SLEEP ROUTINES ;ROUTINE TO CALL ALL FEKS ON THIS CPU WHEN SYSTEM GOES TO SLEEP ;CALL PUSHJ P,FEKCPS ;RETURN CPOPJ ;CLOBBERS NO REGISTERS FEKCPS::PUSHJ P,SAVE1## ;SAVE P1 MOVEI P1,FF.CPS ;GET THE "CPU SLEEP" BIT JRST FEKCPC ; AND GO TO COMMON CODE TO CALL THE FEKS ;ROUTINE TO CALL ALL FEKS ON THIS CPU WHEN SYSTEM WAKES UP ;CALL PUSHJ P,FEKCPW ;RETURN CPOPJ ;CLOBBERS NO REGISTERS FEKCPW::PUSHJ P,SAVE1## ;SAVE P1 MOVEI P1,FF.CPW ;GET THE "CPU WAKING UP" FUNCTION CODE ; PJRST FEKCPC ;GO TO COMMON CODE ;CODE TO CALL ALL FEKS ON THIS CPU WITH THE FUNCTION CODE IN "P1" FEKCPC: PUSHJ P,SAVT## ;SAVE ALL THE TEMPS (FOR SPRINI) PUSH P,U ; AS WELL AS PUSH P,F ; SUNDRY OTHER PUSH P,W ; REGISTERS PUSH P,J ; .. MOVEI J,FEKFST## ;START WITH THE FIRST FEK JUMPE J,FEKCP2 ; IF NONE AT ALL, WE'RE DONE FEKCP1: MOVE T1,P1 ;GET THE FUNCTION CODE IFN FTMP,< HLRZ T2,FEKUNI(J) ;GET THE CPU THIS FEK IS ON CAMN T2,.CPCPN## ; AND IF WE ARE ON THE RIGHT ONE. > XCT FEKDSP(J) ;CALL THE FEK HRRZ J,FEKBLK(J) ;GET THE NEXT FEK JUMPN J,FEKCP1 ; AND CALL HIM TOO. FEKCP2: POP P,J ;RESTORE ALL POP P,W ; THESE REGISTERS JRST FUPOPJ## ;AND WE'RE DONE SUBTTL NDB INTERFACE ROUTINES ;SUBROUTINE NETOPR -- KEEP NETNDB(NDBOPR) UP TO DATE WITH OPRLDB ;CALL MOVE U,NEW OPR LDB ;RETURN CPOPJ1 NETOPR::HRRM U,NETNDB##+NDBOPR ; STORE THE LDB ADDRESS JRST CPOPJ1## ; SKIP RETURN FOR SCNSER ;SUBROUTINE TO LOAD INTO T1 THE TERMINAL NUMBER OF THE OPR'S ; TERMINAL FOR THE STATION WHOSE NUMBER IS IN T1. STBOPR:: SE1ENT PUSH P,U ;SAVE U HRRZS T1 ;STATION NUMBER IN T1 NETOFF ;PROTECT OURSELF WHEN SCANNING THE NDB'S ; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB") PUSHJ P,SRCND0 ;FIND THE NODE BLOCK (BYPASS 'NT' CHECK) JRST LCLOPR ;NO, USE LOCAL OPR HRRZ U,NDBOPR(W) ;GET THE OPR LDB SKIPN U ;ANY ASSIGNED LCLOPR: MOVE U,OPRLDB## ;NO, USE LOCAL OPR SSX U,MS.SCN ;USE SCNSER SECTION LDB T1,LDPLNO## ;GET NUMBER OF TERMINAL IN U NETON ;INT'S ON AGAIN JRST UPOPJ## ;RESTORE U AND EXIT. ;SUBROUTINE STBSCA - RETURN THE STATION NUMBER ;CALL MOVEI T1,NNM ; PUSHJ P,STBSCA ;RETURN CPOPJ ;NO SUCH STATION ; CPOPJ1 ;STATION VALID T1=NNM STBSCA:: ;ENTRY PUSHJ P,SAVJW## ;SAVE J AND W NETOFF ;PROTECT WHILE LOOKING AT THE NDB'S ; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB") PUSHJ P,SRCND0 ;SEARCH NDB'S (BYPASS THE 'NT' CHECK) JRST NTONPJ ;RETURN INVALID STATION HLRZ T1,NDBNNM(W) ;GET THE STATION NUMBER NETON ;ALL'S CLEAR PJRST CPOPJ1## ;EXIT SUBTTL ERROR MESSAGES. NODERT: XWD [ASCIZ / Assigned/] NRTDNA: XWD [ASCIZ /Device Not Available/] NRTNCE::XWD [ASCIZ /Network Capacity Exceeded/] NRTTNA: XWD [ASCIZ /Task Not Available/] NRTUNT: XWD [ASCIZ /Undefined Network Task/] NRTUNN::XWD [ASCIZ /Undefined Network Node/] NRTUND: XWD [ASCIZ /Undefined Network Device/] SUBTTL TSTNET - FIND OUT IF A DEVICE EXISTS ON THE NETWORK ;SUBROUTINE TSTNET - ASSIGN A DDB FOR THE DEVICE IF IT EXISTS ;CALL MOVE T1,DEVICE NAME ; PUSHJ P,TSTNET ;RETURN CPOPJ ;DEVICE DOES NOT EXIST/NOT AVAILABLE ; CPOPJ1 ;DEVICE EXISTS AND IS AVAILABLE TSTNET:: ;ENTRY FROM UUOCON HLRZ T2,M ;GET THE UUO ANDI T2,777000 ;ONLY THE UUO OP CODE CAIE T2,(OPEN) ;IS IT POPJ P, ;NO, GET THE ASSIGN LATER HLRZ T2,T1 ;GET GENERIC PORTION OF NAME CAIN T2,'TTY' ;IS IT A TTY? POPJ P, ;YES, TTY'S DON'T HAVE STATION NUMBERS IN DDBS TRNE T1,7700 ;IS THERE A STATION NUMBER PUSHJ P,DVSCVT## ;GET THE STATION NUMBER POPJ P, ;NOT A STATION NUMBER PUSH P,T1 ;SAVE THE NAME DPB T1,[POINT 6,T1,23] ;MOVE THE UNIT NUMBER OVER TRZ T1,7777 ;CLEAR THE JUNK PUSHJ P,GENNET ;GO ASSIGN THE DEVICE PJRST TPOPJ## ;NO SUCH DEVICE PJRST TPOPJ1## ;DEVICE ASSIGNED F=DDB ;SUBROUTINE GENNET - TEST FOR A GENERIC NETWORK DEVICE ;CALL MOVE T1,[SIXBIT /DEVNAME/] ; MOVE T2,STATION NUMBER ; PUSHJ P,GENNET ;RETURN POPJ ;NO ON THE NETWORK OR AVAILABLE ; CPOPJ1 ;F=DDB ADDRESS GENNET:: ;ENTRY FROM UUOCON NETDBJ ;INTERLOCK THIS CODE CAMN T2,JBTLOC## ;LOCAL STATION POPJ P, ;YES, RETURN PUSHJ P,SAVT## ;NO, SAVE THE T'S MOVEI T4,(P) ;GET THE STACK ADDRESS CAMG T4,LOCORE## ;RUNNING AT CLOCK LEVEL POPJ P, ;YES, DON'T TRY THE NETWORK PUSHJ P,SAVJW## ;SAVE J AND W PUSHJ P,SAVE3## ; AND THE P'S MOVE P2,T1 ;COPY THE DEVICE NAME MOVEI T1,(T2) ;PUT THE STATION NUMBER IN T1 PUSHJ P,SRCNDB ;DOES THE STATION EXIST POPJ P, ;NO EXIT MOVEI P1,(W) ;SAVE THE NDB POINTER SETZ P3, ;CLEAR THE LOCAL NAME HLRZ T1,P2 ;GET THE GENERIC DEVICE NAME PUSHJ P,SRCNDT ;SEE IF IT EXISTS AT THE STATION POPJ P, ;NO, EXIT PUSHJ P,MAKDDB ;YES, MAKE A DDB POPJ P, ;CAN'T PUSHJ P,NTDCNT ;USE THE "NORMAL" DEVICE CONNECT ROUTINE (WAITS) PJRST RMVNET ;FAILED DISCARD DDB PUSHJ P,LNKDDB ;LINK CONNECTED DDB PJRST CPOPJ1## ;WE WON SUBTTL DDB -- ROUTINES TO MANAGE NETWORK DEVICE DATA BLOCKS ;MAKDDB ROUTINE TO CREATE A NETWORK DDB ;CALL MOVE P1,NDB POINTER ; MOVE P2,DEVICE NAME ; MOVE P3,LOGICAL NAME ; MOVE W,NDT POINTER ; MOVE T2,LENGTH ;MAKDDC ONLY ; PUSHJ P,MAKDDB/MAKDDC ;RETURN CPOPJ ;NO SPACE AVAILABLE ; CPOPJ1 ;F=DDB POINTER,U=PCB ; ;NOTE! THIS ROUTINE DOES NOT ASSIGN AN SLA MAKDDB::MOVEI T2,NETLEN ;GET THE LENGTH OF THE PROTOTYPE DDB MAKDDC::NTDBUG ;VERIFY THE INTERLOCK IFN PARANOID&P$DDB,< ;SOME CONSISTENCY CHECKING CAIL T2,NETLEN ;CAN'T BE SMALLER THAN MINIMUM NETWORK LENGTH CAILE T2,NETLEN+10 ;AND NONE ARE MUCH BIGGER STOPCD .,STOP,ANFUND, ;++ UNREASONABLE NETWORK DDB LENGTH > ;END IFN PARANOID&P$DDB PUSH P,T2 ;SAVE ALLOCATED LENGTH PUSHJ P,GETZWD ;ALLOCATE THE SPACE JRST T2POPJ## ;SPACE NOT AVAILABLE MOVEI F,(T1) ;COPY THE DDB POINTER HRLI T1,NETDDB## ;BLT POINTER TO COPY THE PROTOTYPE DDB BLT T1,NETLEN-1(F) ;COPY IT POP P,T2 ;RETRIEVE LENGTH OF NEW DDB DPB T2,NETZWD ;SET LENGTH OF DDB FOR RMVNET CAIG T2,NETLEN ;IS THIS AN EXTENDED DDB? JRST MAKDD2 ;NO SETZM NETLEN(F) ;YES, CLEAR OUT REMAINDER MOVSI T1,NETLEN(F) ;CONCOCT A HRRI T1,NETLEN+1(F) ; BLT POINTER ADDI T2,(F) ;AND A BLT TERMINATOR BLT T1,-1(T2) ;TO CLEAR DDB EXTENSIONS ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE MAKDD2: MOVEM P2,DEVNAM(F) ;STORE THE DEVICE NAME MOVEM P3,DEVLOG(F) ;STORE THE LOGICAL NAME HLRZ T1,NDBNNM(P1) ;GET THE NODE NUMBER HRRZM T1,DEVNET(F) ;STORE THE NODE NUMBER DPB T1,PDVSTA## ;STORE AS THE STATION NUMBER TRO T1,77700 ;FORCE A STATION NUMBER ALIGNMENT S0PSHJ CVTSBT## ;CONVERT TO SIXBIT MOVE T2,P2 ;COPY THE DEVICE NAME TRNN T2,7777 ;CHECK FOR GGGU LSH T2,-14 ;YES, SHIFT FOR THE UNIT NUMBER ANDI T2,77 ;ONLY ONE DIGIT IORI T1,(T2) ;INSERT THE DIGIT HRRM T1,DEVNAM(F) ;STORE IN THE DEVNAME ANDI T1,77 ;SAVE LAST DIGIT SKIPN T1 ;IS IT A BLANK IE GENERIC TROA T1,177 ;YES, USE 177 FOR GENERIC SEARCH ANDI T1,7 ;ONLY THREE BITS WORTH (FOR GGGNNU) DPB T1,PUNIT## ;STORE THE UNIT NUMBER MOVSI T1,DVCNET ;GET THE NETWORK OWNERSHIP BIT IORM T1,DEVCHR(F) ;STORE WE OWN IT DPB J,PJOBN## ;STORE THE JOB NUMBER LDB T1,NDTBFZ ;GET THE DEFAULT BUFFER SIZE DPB T1,PBUFSZ## ;STORE THE BUFFER SIZE LDB T1,NDTTYP ;GET THE DEVICE TYPE DPB T1,DEYTYP## ;STORE THE DEVICE TYPE LDB T1,NDTSPL ;GET THE DEFAULT SPOOL BITS DPB T1,DEYSPL## ; AND STORE THEM IN THE NEW DDB MOVE T1,NDTMOD(W) ;GET THE DEVMOD BITS MOVEM T1,DEVMOD(F) ;STORE DEVMOD HRLM W,DEVNET(F) ;LINK THE NDT TO THE DDB JRST CPOPJ1## ;EXIT WITH F=NETDDB, U=PCB, W=NDT ;SUBROUTINE LNKDDB - ADD THE DDB(F) INTO THE DDB CHAIN ;CALL MOVEI F,DDB ; PUSHJ P,LNKDDB ;RETURN CPOPJ LNKDDB::PUSHJ P,SAVE1## ;SAVE P1 PUSH P,F ;SAVE THE DDB POINTER MOVE T1,DEVLOG(F) ;GET THE LOGICAL NAME PUSHJ P,DEVLG## ;CHECK FOR IN USE JRST LNKDD1 ;NO, SETZM DEVLOG(F) ;YES, CLEAR THE LOGICAL NAME MOVE T2,DEVMOD(F) ;GET THE DEV MODE BITS TLNE T2,DVDSK ;IS IT A DSK TRNE T2,ASSPRG ; AND INIT'ED JRST LNKDD1 ;NO IGNORE PUSHJ P,CLRDVL## ;CLEAR JOB NUMBER IN DDB PUSHJ P,CLRDDB## ;YES, CLEAR THE DDB LNKDD1: POP P,F ;RESTORE THE DDB POINTER DPB J,PJCHN## ;SET JCH IN DDB SKIPN DEVLOG(F) ;WAS IT BEING CLEARED PUSHJ P,ASSCK1## ;YES, RECLAIM SPACE PJRST AUTLNK## ;PUT IT IN THE DEVICE CHAIN AND RETURN ;SUBROUTINE UNLDDB -- ROUTINE TO UNLOAD A NETWORK DDB. THIS ROUTINE ; UNLINKS AND DELETES THE DDB. ;CALL MOVEI F,DDB ; PUSHJ P,UNLDDB ;RETURN CPOPJ UNLDDB:: ;GLOBAL ENTRY POINT NTDBUG ;VERIFY THE INTERLOCK PUSHJ P,UNLKDB ;UNLINK DDB (ALSO WORKS OK IF NOT LINKED!!) PJRST RMVNET ;NO GIVE THE STORAGE BACK TO THE MONITOR ;SUBROUTINE UNLKDB - UNLINK A DDB FROM THE SYSTEM'S LIST ;CALL MOVE F,DDB ;RETURN CPOPJ UNLKDB: MOVEI T2,(F) ;COPY DDB ADDRESS SETZ T1, ;ZERO LENGTH ARG MEANS DON'T DELETE PJRST AUTKIL## ;HALF-KILL THIS DDB ;RMVNET ROUTINE TO FREE THE STORAGE ASSOCIATED WITH A DDB. ;CALL MOVEI F,DDB ;NOT LINKED OR CONNECTED ; PUSHJ P,RMVNET ;RETURN CPOPJ ;DDB FREED ; RMVNET:: ;HERE TO DELETE A DDB PUSHJ P,CLNNET ;CLEAN OUT THE DDB (FREE RANDOM STUFF) LDB T1,NETZWD ;GET LENGTH OF THIS PARTICULAR DDB MOVEI T2,(F) ;GET ADDR OF THE DDB PUSHJ P,GIVZWD ;FREE THE DDB SETZ F, ;MAKE SURE F DOESN'T POINT ANYWHERE POPJ P, ;ALL DONE ;CLNNET ROUTINE TO INITIALIZE A DDB. ;CALL MOVEI F,DDB ;MUST NOT BE CONNECTED ; PUSHJ P,CLNNET ;RETURN CPOPJ ;DDB INITIALIZED CLNNET::NTDBUG ;JUST CHECKING... IFN PARANOID&P$DDB,< ;MAKE SURE THAT THIS DDB IS TRULY FREE. SKIPN F ;IS THERE A DDB THERE STOPCD .,STOP,ANFCND ;++ CLNNDB HAS NO DDB LDB T1,NETSLA ;SHOULDN'T HAVE A LAT ASSIGNED SKIPE T1 ; ALL THE DISCONNECT ROUTINES CLEAR THIS STOPCD .,STOP,ANFCLA ;++ LAT STILL ASSIGNED IN CLNNDB > PUSHJ P,GV2NPD ;WE MUST FREE THE NPD'S PUSH P,U ;SAVE "U" FOR A BIT HRRZ U,DEVPCB(F) ;GET THE POINTER TO QUEUED PCB'S (IF ANY) PUSHJ P,RMVALP ;FREE THEM ALL POP P,U ;RESTORE "U" SETZM DEVDRQ(F) ;CLEAR ANY STALE DATA-REQUESTS SETZB T1,DEVPCB(F) ;CLEAR POINTER TO JUST FREED PCB'S SETZB T2,DEVPBP(F) ; AND CLEAR AUX POINTER TO SUB MSG DMOVEM T1,DEVAXI(F) ;CLEAR INPUT BUFFER POINTERS DMOVEM T1,DEVAXO(F) ; AND OUTPUT TOO. HLLZS DEVNET(F) ;CLEAR NODE NUMBER DPB T1,NETSLA ;CLEAR SOURCE LINK ADDR DPB T1,NETDLA ; DESTINATION LINK DPB T1,NETRLN ; RECORD LENGTH DPB T1,NETDVT ; AND ATTRIBUTES POPJ P, ;ALL DONE. RESTORE A CLEAN DDB ;ZAPNET ROUTINE TO TAKE CARE OF USELESS NETWORK DDB'S ;CALL MOVEI F,DDB ; PUSHJ P,ZAPNET ;RETURN CPOPJ, F/0 IF DDB ZAPPED, OTHERWISE UNCHANGED ; ;ZAPNET IS CALLABLE FOR ANY NON-TTY DDB IN F, WILL RETURN HARMLESSLY IF ;THE DDB IS NOT NETWORK-ZAPPABLE (I.E., IS NOT A ANF DEVICE, IS UNDER MDA ;CONTROL, IS RESTRICTED, OR IS ASSIGNED/INITED BY A JOB/PROGRAM) ZAPNET::MOVE T1,DEVMOD(F) ;A COLLECTION OF DEVICE FLAGS TRNE T1,ASSCON!ASSPRG;NEVER DISCONNECT A DDB WHICH IS IN USE POPJ P, ;DON'T DISCONNECT THIS DEVICE ZAPNE0: MOVE T1,DEVCHR(F) ;SOME DIFFERENT DEVICE FLAGS IFN FTMDA, ;NEVER DISCONNECT ONE OF QUASAR'S PLAYTHINGS TLNN T1,DVCNET ;NEVER DISCONNECT A NON-ANF-NETWORK DEVICE POPJ P, ;DON'T DISCONNECT THIS DEVICE MOVE T1,DEVSTA(F) ;OTHER ASSORTED DEVICE FLAGS TLNE T1,DEPRAS ;NEVER DISCONNECT A RESTRICTED DEVICE POPJ P, ;DON'T DISCONNECT THIS DEVICE LDB T1,PDVTYP## ;GET THE DEVICE TYPE CAIE T1,.TYDDP ;IS THIS A DDP DEVICE? JRST ZAPNE1 ;NO SKIPE [M.RDDP##] ;DDCMP DEVICE SERVICE LOADED? PJRST ZAPDDP## ;YES, FIRST CHECK WITH DDPSER ON ZAPABILITY ZAPNE1::NETDBJ ;GET THE NETSER INTERLOCK SETCM T1,NETZAP## ;GET CATASTROPHIC ERROR FLAG JUMPN T1,ZAPNE2 ;NORMAL RELEASE DPB T1,NETSLA ;PITCH SLA DPB T1,NETDLA ;AND DLA ZAPNE2: MOVE S,DEVIOS(F) ;GET THE IO STATUS TLNE S,IOSZAP ;HAVE WE FREED THIS DDB ONCE ALREADY?? STOPCD .,STOP,ANFRDT, ;++ RELEASING DDB TWICE TLNN S,IOSCON ;IS THIS DEVICE CONNECTED? PJRST UNLDDB ;IF NOT CONNECTED, JUST DELETE THE DDB. MOVEI T1,RSN.OK ;NORMAL DISCONNECT REASON PUSHJ P,NTDXDS ;SEND THE DISCONNECT. JRST [PUSHJ P,NETSLP ;IF WE CAN'T, WAIT FOR CORE TO SHOW UP JRST ZAPNET] ;AND TRY AGAIN. MOVSI S,IOSZAP ;SINCE NO-ONE WANTS THIS DEVICE ANY MORE, IORB S,DEVIOS(F) ;INDICATE THAT IT IS FREE TO BE DELETED. PUSHJ P,UNLKDB ;UNLINK THE DDB SO NO-ONE ELSE GETS IT SETZ F, ;INDICATE THAT THE DDB IS GONE, AND POPJ P, ;RETURN. (NTDDSC WILL EVENTUALLY FREE THE DDB) SUBTTL PCB -- ROUTINES TO HANDLE PROTOCOL CONTROL BLOCKS ;SUBROUTINE MK?PCB - BUILD A PROTOCOL DATA BLOCK ;CALL MOVEI F,DDB OR 0 IF AN NCL MESSAGE ; MOVEI W,NDB ; MOVEI T1,BUFFERSIZE ;SIZE OF ASSOCIATED MESSAGE BUFFER ; PUSHJ P,MK?PCB ;RETURN CPOPJ ;NO SPACE AVAILABLE, OR TOO MANY PENDING MSGS ; CPOPJ1 ;U=PCB POINTER ; ;MKUPCB THIS SUBROUTINE IS USED TO GET A PCB FOR AN UNNUMBERED MESSAGE. ; IT DOES NOT DECREMENT, OR CHECK NDBMOM(W) ;MKNPCB THIS SUBROUTINE IS USED TO ALLOCATE A PCB FOR A NUMBERED MESSAGE. ; IT CHECKS TO MAKE SURE THAT NDBMOM(W) IS GREATER THAN ZERO. THIS ; CHECK IS NECESSARY TO ENSURE THAT WE DO NOT SEND MORE THAN 128 MSGS ; TO ANY GIVEN NODE. (IF WE DID THE MSG NUMBERS WOULD WRAP AROUND!) MKNPCB:: ;MAKE NUMBERED PCB SKIPE NTUEFC ;IS THIS AN EMERGENCY? JRST MKNPC1 ;YES - SKIP THIS CHECK PUSH P,T1 ;NO MOVE T1,NDBMOM(W) ;GET NUMBER OF MESSAGES AVAILABLE CAIG T1,5 ;ARE THERE A FEW FREE? JRST TPOPJ## ;NO - SAY WE CAN'T DO IT POP P,T1 ;DO IT MKNPC1: SOSLE NDBMOM(W) ;CHECK ON THE NUMBER OF OUTSTANDING MSGS PUSHJ P,MKUPCB ; IF WE CAN, TRY TO MAKE ANOTHER PCB AOSA NDBMOM(W) ;HERE IF WE CAN'T SEND ANOTHER PCB NOW. JRST [SETZM NTUEFC ;IF WE GOT ONE, CLEAR "EMERGENCY FLAG" RETSKP] ; AND GIVE GOOD RETURN SKIPN NTUEFC ;IF WE CAN'T USE EMERGENCY FREE CORE, POPJ P, ; THEN GIVE BAD RETURN SETZM NTUEFC ;OTHERWIZE CLEAR THE "EMERGENCY" FLAG PJRST PCBEGT ; AND GO GET "EMERGENCY" MEMORY MKUPCB:: ;MAKE UN-NUMBERED PCB IFN PARANOID&P$COR,< ;IF WE'RE BEING CAUTIOUS, SKIPLE T1 ; THEN MAKE SURE THAT THE CALLER CAILE T1,MSGMAW## ; ASKED FOR A POSITIVE, NOT-TOO-LARGE BUFFER STOPCD .,STOP,ANFMRL, ;++ MESSAGE REQUEST TOO LARGE > ;END IFN PARANOID&P$COR ADDI T1,MSGAGW##-1 ;ROUND UP AND ANDCMI T1,MSGAGW##-1 ; TRUNCATE MODULO ALLOCATION GRANULARITY MOVE T2,T1 ;COPY OF REQUESTED SIZE (WORDS) LSH T2,-MSGAGN## ;REDUCE TO ALLOCATION GRANULARITY NETOFF ;NO INTERRUPTS WHILE LOOKING AT FREE-LISTS SKIPG NTFREC##(T2) ;ARE THERE ANY PCB'S ON THIS FREE LIST? JRST MAKPC1 ;NO FREE PCB'S, ALLOCATE A NEW ONE HRRZ U,NTFREF##(T2) ;GET THE FIRST FREE PCB HRRZ T1,PCBBLK(U) ;GET THE NEXT FREE PCB HRRZM T1,NTFREF##(T2) ; AND MAKE THE "NEXT" ONE THE NEW "FIRST" ONE SOSG NTFREC##(T2) ;DECREMENT THE FREE COUNT, IF ZERO, THEN SKIPN NTFREF##(T2) ;MAKE SURE THAT PCB FREE LIST IS NULL SKIPG U ;MAKE SURE THAT WE GOT A VALID PCB STOPCD .,STOP,ANFPCL, ;++ PCB LISTS SCREWED UP NETON ;ALL'S CONSISTENT. INTS BACK ON HLLZS PCBBLK(U) ;MAKE SURE WE DON'T KEEP ANY POINTERS IFN PARANOID&P$COR,< ;IF WE'RE BEING CAUTIOUS, MOVE T1,PCBTAG(U) ;A PCB UNIQUENESS CAME T1,['PCBTAG'] ;DOES ALLEGED PCB LOOK LIKE A PCB? STOPCD .,STOP,ANFPCT ;++ PCB TRASHED MOVE T1,PCBALN(U) ;LENGTH OF DATA BUFFER (WORDS) LSH T2,MSGAGN## ;LENGTH IT SHOULD BE CAME T1,T2 ;IF LENGTHS DIFFERENT STOPCD .,STOP,ANFBLW, ;++ BUFFER LENGTH WRONG MOVE T1,PCBADR(U) ;ADDRESS OF DATA BUFFER PUSH P,T2 ;SAVE LENGTH HLRZ T2,-1(T1) ;GET TOP-END CHECK WORD ADD T1,0(P) ;ADJUST ADDRESS AND MOVE T1,0(T1) ;GET BOTTOM-END CHECK WORD CAMN T1,['NETMEM'] ;IS BOTTOM-END CHECK WORD OK? CAIE T2,'NET' ;IS TOP-END CHECK WORD OK? STOPCD .,STOP,ANFDMU, ;++ DATA BUFFER MESSED UP > ;END IFN PARANOID&P$COR MOVE T1,PCBADR(U) ;DATA BUFFER ADDRESS HRLI T1,(POINT 8,) ;PROTOTYPE BYTE POINTER MOVEM T1,PCBPTR(U) ;RESET PCB BYTE POINTER ; (CAN BE TRASHED BY ETHSER/D8EINT) JRST TPOPJ1## ;GIVE SUCCESS RETURN ;HERE TO ALLOCATE THE PCB FROM MONITOR FREE CORE MAKPC1: NETON ;NOT DIDDLING THE FREE LIST ANYMORE PUSH P,T1 ;SAVE THE BUFFER SIZE MOVEI T2,PCBLEN ;GET THE LENGTH OF A PCB PUSHJ P,GETZWD ;GET A ZERO BLOCK OF FREE CORE JRST TPOPJ ;NO SPACE MOVEI U,(T1) ;COPY THE PCB POINTER MOVE T2,(P) ;GET BUFFER SIZE BACK JUMPE T2,TPOPJ1 ;IF NO BUFFER WANTED, LEAVE NOW PUSHJ P,GETZWD ;GET THE BUFFER PJRST [POP P,T1 ;IF NONE AVAILABLE, CLEAN STACK AND PJRST RMVPCB] ;AND RETURN THE PCB MOVEM T1,PCBADR(U) ;SAVE BUFFER ALLOCATED ADDRESS HRLI T1,(POINT 8) ;MAKE BYTE POINTER TO THE BUFFER AREA MOVEM T1,PCBPTR(U) ;AND SAVE IT AWAY POP P,PCBALN(U) ;REMEMBER BUFFER ALLOCATED LENGTH AOS (P) ;GIVE A GOOD RETURN PJRST CLNPCB ; AND SET UP CACHE INFORMATION ;SUBROUTINE RMVPCB - REMOVE THE PCB FROM FREE CORE ;CALL MOVEI U,PCB ; S0PSHJ RMVPCB ;RETURN CPOPJ RMVPCB: JUMPE U,CPOPJ## ;DON'T FREE A NON-EXISTANT PCB IFN PARANOID&P$COR,< ;IF WE ARE BEING CAUTIOUS, SKIPE PCBAL2(U) ;MAKE SURE THAT THE "SECONDARY" STOPCD .,STOP,ANFSBA, ;++ SECONDARY BUFFER ALLOCATED ("OLD FEATURE") > MOVE T2,PCBALN(U) ;GET THE LENGTH OF THE PRIMARY BUFFER JUMPE T2,ZAPPCB ;IF NO PRIMARY BUFFER, RETURN PCB TO FREE-CORE PUSH P,T2 ;HANG ON TO DATA BUFFER LENGTH IFN PARANOID&P$COR,< ;NOW WE CAN CHECK TO SEE IF SIZE IS REASONABLE MOVE T1,PCBTAG(U) ;A PCB UNIQUENESS CAMN T1,['PCBTAG'] ;DOES PCB STILL LOOK LIKE A PCB? TRNE T2,MSGAGW##-1 ;IS PCB'S DATA BUFFER LENGTH NICELY GRANULAR? STOPCD .,STOP,ANFPCR, ;++ PCB TAG WORD TRASHED WHEN REMOVING LSH T2,-MSGAGN## ;CONVERT LENGTH TO GRANULARITY INDEX CAILE T2,MSGALN## ;IS PCB'S DATA BUFFER A REASONABLE LENGTH? STOPCD .,STOP,ANFMBL, ;++ BUFFER LENGTH WRONG MOVE T1,PCBADR(U) ;ADDRESS OF DATA BUFFER HLRZ T2,-1(T1) ;GET THE TOP-END CHECK WORD ADD T1,0(P) ;ADVANCE BUFFER ADDRESS AND MOVE T1,0(T1) ;GET THE BOTTOM-END CHECK WORD CAMN T1,['NETMEM'] ;IS BOTTOM-END CHECK WORD OK? CAIE T2,'NET' ;IS TOP-END CHECK WORD OK? STOPCD .,STOP,ANFPCM, ;++ PCB MESSAGE CHECK WORDS TRASHED > PUSHJ P,CLNPCB ;REFRESH THE PCB POP P,T2 ;RETRIEVE DATA BUFFER LENGTH LSH T2,-MSGAGN## ;REDUCE TO ALLOCATION GRANULARITY NETOFF ;DISABLE INTERRUPTS WHILE HACKING FREE-LISTS SKIPG NTFREC##(T2) ;SEE IF FREE-LIST IS EMPTY JRST [HRRZM U,NTFREF##(T2) ;IF EMPTY, THEN THIS PCB GOES ON FIRST HRRZM U,NTFREL##(T2) ; AS WELL AS LAST JRST RMVPC1] ;GO TO COMMON CODE TO CLEAN UP HRRZ T1,NTFREL##(T2) ;GET THE ADDRESS OF THE "LAST" PCB IN THE LIST HRRM U,PCBBLK(T1) ; PUT THIS PCB JUST AFTER IT IN THE FREE LIST HRRM U,NTFREL##(T2) ; AND ALSO MAKE THIS THE NEW "LAST" PCB RMVPC1: AOS NTFREC##(T2) ;COUNT THE NEW FREE PCB SETZ U, ;CLEAR "U" TO INDICATE PCB HAS BEEN FREED RMVPC7: NETON ;RE-ENABLE INTERRUPTS POPJ P, ;RETURN WITH PCB NOW ON PROPER FREE LIST ;THIS ROUTINE DOES NOT 'RECYCLE' THE PCB. PCB IS RETURNED TO FREE-CORE ZAPPCB: HLLZS PCBTAG(U) ;CLEAR THE 'UNIQUE' TAG SKIPE PCBCT2(U) ;GET THE DATA LENGTH STOPCD .,STOP,ANFOBS, ;++ OBSOLETE FEATURE ZAPPC2: SKIPG T1,PCBALN(U) ;GET THE DATA BUFFER LENGTH JRST ZAPPC3 ;NONE MOVE T2,PCBADR(U) ;GET THE INPUT POINTER PUSHJ P,GIVZWD ;REMOVE THE SPACE ZAPPC3: MOVEI T2,(U) ;COPY THE ADDRESS MOVEI T1,PCBLEN ;GET THE LENGTH PUSHJ P,GIVZWD ;RETURN THE SPACE SETZ U, ;CLEAR U TO FLAG PCB AS REMOVED POPJ P, ;CLNPCB REFRESH A PCB. ;CALL MOVE U,PCB ; PUSHJ P,CLNPCB ;RETURN CPOPJ ;ALWAYS ;CLOBBERS T1,T2 CLNPCB: SETZM PCBBLK(U) ;CLEAR FLAGS AND LINK FIELDS SETZM PCBFEK(U) ;CLEAR FEK AND NODE NUMBER SETZM PCBCTR(U) ;CLEAR VALID DATA BYTE COUNT SETZM PCBCT2(U) ;CLEAR SECONDARY DATA BYTE COUNT SETZM PCBAL2(U) ;AND THE "ANFSBA" STOPCD GENERATOR MOVE T1,[SIXBIT /PCBTAG/] ;GET AND SET THE "TAG" MOVEM T1,PCBTAG(U) ; WORD SO WE KNOW IT'S A PCB IFN FTKL10, ;SET UP THE CACHE INFORMATION IN THE PCB POPJ P, ;ALL DONE ;"EMERGENCY" PCB MANAGEMENT. Comment @ One of the most persistant problems with NETSER has been that it uses a great deal of free core, and when free core runs low, NETSER tends to crash. What follows it the latest in a continuing series of HACKS to ameloiate the effects of finite free-core. The routines involved are: PCBECK This routine checks to make sure that there is sufficient "emergency" free core available, and that there are message numbers available. If it skips, one may call PCBEGT with out fear. PCBEGT This routine gets an "emergency" numbered pcb. If one is not available, it STOPCD's. Unfortunatly, NETSER is not organized in a fashion that makes using these routines easy. The problem is that very low-level subroutines are the ones that allocate messages (pcbs). Figuring out if it is an "emergency" or not is beyond their limited abilities. What is needed is a way for "middle" level routines to inform lower level ones that there is an emergency. This is currently accomplished by the gross hack of using a global flag. The mechanisims for this are: NTUEFC If this location is non-zero, then the next call to MKNPCB will not fail. It will attempt to use "normal" free-core, but if none is available, it will use emergency free core. EMRGCY This is a macro that currently SETOM's ntuefc. It should be used just before calls to routine such as NCSHDR when it is imperative that they NOT fail. End Comment @ ;ROUTINES TO MANAGE EMERGENCY STORAGE ;PCBECK ROUTINE TO SEE IF EMERGENCY MESSAGES ARE AVAILABLE. ;CALL W := NDB POINTER ;RETURN CPOPJ ;EITHER NO FREE MESSAGE NUMBERS, OR NO CORE ; CPOPJ1 ;THERE ARE FREE MESSAGE NUMBERS, AND ; ; EMERGENCY STORAGE. IN PARTICULAR, A ; ; CALL TO PCBEGT WILL SUCCEED. PCBECK: MOVE T1,NDBMOM(W) ;GET THE COUNT OF FREE MESSAGE NUMBERS, CAIG T1,3 ; AND MAKE SURE IT IS REASONABLY LARGE POPJ P, ;IF NOT ENOUGH FREE NUMBERS GIVE ERROR RETURN MOVE T1,PCBECT ;GET THE COUNT OF "EMERGENCY" PCB'S CAIL T1,2 ; AND IF THERE ARE 2 OR MORE, THEN RETSKP ;GIVE "SUCCESS" RETURN PUSH P,U ;"U" IS IN USE BY NETSCN AT THIS POINT MOVEI T1,MSGMAW## ;IF NOT ENOUGH "EMERGENCY" PCB'S, TRY PUSHJ P,MKUPCB ; TO ALLOCATE THE LARGEST POSSIBLE. PJRST UPOPJ## ;IF NO MORE FREE-CORE, GIVE ERROR RETURN NETOFF ;IF WE GOT ONE, TURN OFF INTERRUPTS HRRZ T1,PCBELS ; AND INSERT THE NEW ONE HRRM T1,PCBBLK(U) ; ON THE FRONT OF THE "EMERGENCY" HRRM U,PCBELS ; PCB FREE LIST (PCBELS). AOS PCBECT ;COUNT THE NEW "EMEGENCY" PCB NETON ;RE-ENABLE INTERRUPT, AND POP P,U ;GET NETSCN'S "U" BACK JRST PCBECK ;GO SEE IF WE HAVE ENOUGH NOW. ;PCBEGT ROUTINE TO GET AN "EMERGENCY" NUMBERED PCB ;CALL W := NDB POINTER ;(WE NEED TO FIX NDBMOM) ;RETURN CPOPJ1 ;WITH U := "EMERGENCY" PCB ; ; (IF NONE AVAILABLE, WE STOPCD) PCBEGT: NETOFF ;DISABLE INTERRUPTS WHILE HACKING LISTS SOSL NDBMOM(W) ;DECREMENT THE FREE MESSAGE NUMBER COUNTER SOSGE PCBECT ;DECREMENT THE COUNT OF "EMERGENCY" PCB'S STOPCD .,STOP,ANFNFP, ;++ NO FREE PCBS OR NO FREE MESSAGES HRRZ U,PCBELS ;GET THE FIRST FREE PCB, HRRZ T1,PCBBLK(U) ;GET THE ADDRESS OF THE SECOND (IF ANY) HRRM T1,PCBELS ;MAKE THE "SECOND" THE NEW "FIRST" PJRST NTONP1 ;RE-ENABLE INTERRUPTS AND SKIP RETURN. ;ONCE/SECOND CODE FOR PCB MANAGEMENT. ;THIS CODE FREES EXCESS PCBS FROM THE FREE LISTS ONCE/SECOND. ;AT LEAST ONE FREE PCB IS ALWAYS KEPT IN ORDER TO MINIMIZE THE ;CONTENTION ON GENERAL MONITOR FREE CORE. IN PARTICULAR, FOR ;THE MSGMAX-SIZED PCB LIST TWO FREE PCBS ARE KEPT (SINCE ALL ;INPUT OPERATIONS MUST ALMOST-BY-DEFINITION USE THIS SIZE). PCBSEC: MOVE T1,TIME## ;GET SYSTEM TIME (IN JIFFIES) IDIV T1,TICSEC## ;GET SYSTEM TIME (IN SECONDS) IDIVI T1,MSGALN##+1 ;TAKE MOD ALLOCATION TABLE MAXIMA NETOFF ;MAKE SURE NOT INTERRUPTED SKIPG T1,NTFREC##(T2) ;GOT A FREE PCB HERE? PJRST RMVPC7 ;NO, JUST RE-ENABLE AND GO AWAY CAIL T2,MSGALN## ;IS THIS THE MSGMAX-SIZED PCB LIST? SUBI T1,1 ;YES, ALLOW FOR ONE MORE SOJLE T1,RMVPC7 ;ALWAYS KEEP THE "LAST" ONE HRRZ U,NTFREF##(T2) ;GET THE ADDRESS OF THE "FIRST" FREE ONE HRRZ T1,PCBBLK(U) ;GET THE ADDRESS OF THE "NEXT" FREE ONE HRRM T1,NTFREF##(T2) ;MAKE THE "NEXT" THE "FIRST" SOSLE NTFREC##(T2) ;COUNT DOWN THE PCB COUNTER JRST PCBSE3 ;ALL DONE SKIPE NTFREF##(T2) ;THAT WAS THE LAST, BETTER NOT BE ANY MORE STOPCD .,STOP,ANFFCW, ;++ FREE PCB COUNT WRONG SETZM NTFREL##(T2) ;CLEAR POINTER TO LAST PCBSE3: NETON ;NO LONGER PLAYING WITH FREE LISTS HLLZS PCBBLK(U) ;NO STRAY POINTERS PJRST ZAPPCB ;RETURN THE PCB TO FREE CORE ;RMVALP THIS ROUTINE FREES AN ENTIRE CHAIN OF PCB'S ;CALL U := FIRST ONE TO GO ;RETURN CPOPJ RMVALP: PUSHJ P,SAVE1 ;SAVE P1 FOR THE DURATION RMVAL1: JUMPE U,CPOPJ ;ALL DONE IF NO PCB HRRZ P1,PCBBLK(U) ;GET AND KEEP POINTER TO NEXT PCB S0PSHJ RMVPCB ;FREE THIS PCB MOVEI U,(P1) ;CURRENT PCB := NEXT PCB PJRST RMVAL1 ;TAIL-RECURSE SUBTTL NDB -- ROUTINES TO MANAGE NODE DATA BLOCKS ;SUBROUTINE MAKNDB - BUILD A NODE DATA BLOCK (NDB) ;CALL MOVEI J,FEK ; MOVEI T1,NNM ; PUSHJ P,MAKNDB ;RETURN CPOPJ ;NO CORE ; CPOPJ1 ;W=NDB MAKNDB: ;ENTRY NTDBUG ;VERIFY THE INTERLOCK PUSH P,T1 ;SAVE THE NODE NUMBER MOVEI T2,NDBLEN ;LENGTH PUSHJ P,GETZWD ;ALLOCATE PJRST TPOPJ## ;NO SPACE MOVEI W,(T1) ;COPY THE POINTER HRRM J,NDBFEK(W) ;STORE THE FEK POINTER HRRZ T1,NETNDB## ;GET THE START OF THE NDB CHAIN NETOFF ;NEED PROTECTION SINCE 'STBOPR' AND ; OTHER ROUTINES SCAN THE NDB LIST WITH ; OUT FIRST GETTING THE 'NT' INTERLOCK ; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB") HRRM T1,NDBNNM(W) ;LINK THIS NDB TO THE START HRRM W,NETNDB## ;LINK POP P,T1 ;GET THE NODE NUMBER CAIG T1,NODMAX ;RANGE CHECK HRRM W,.GTNDA##(T1) ;STORE ADDRESS IF VALID HRLM T1,NDBNNM(W) ;STORE NETON ;ALL'S CLEAR PJRST CPOPJ1## ;EXIT SUBTTL SUBROUTINE TO RETURN STORAGE DATA BLOCK TO THE MONITOR ;SUBROUTINE RMVNDB - REMOVE A NODE DATA BLOCK NDB ;CALL MOVEI W,NDB ; PUSHJ P,RMVNDB ;RETURN CPOPJ RMVNDB: NTDBUG ;VERIFY THE INTERLOCK PUSHJ P,SAVJW## ;SAVE J AND W PUSHJ P,SAVE1## ;SAVE P1 PUSHJ P,NODEDN ;CALL "NODE DOWN" PUSHJ P,CLNNDB ;CLEAN OUT STUFF HANGING ON THE NDB MOVEI T1,NETNDB## ;GET THE START OF THE NDB CHAIN NETOFF ;TURN OFF THE PI'S SINCE STBOPR ; DOESN'T ALWAYS GET THE 'NT' INTERLOCK ; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB") RMVNT2: HRRZ T2,NDBNNM(T1) ;GET THE NEXT POINTER JUMPE T2,CPOPJ## ;EXIT IF END CAIN T2,(W) ;IS THIS THE BLOCK JRST RMVNT3 ;YES, MOVEI T1,(T2) ;NO, STEP ALONG JRST RMVNT2 ;TRY AGAIN RMVNT3: HRRZ T3,NDBNNM(W) ;GET THE FORWARD LINK HRRM T3,NDBNNM(T1) ;STORE BACKWARD HLRZ T1,NDBNNM(W) ;GET NODE REMOVED CAIG T1,NODMAX ;RANGE CHECK HLLZS .GTNDA##(T1) ;AND CLEAR POINTER NETON ;ALL'S CLEAR MOVEI T1,NDBLEN ;GET THE LENGTH PJRST GIVZWD ;RELEASE THE BLOCK ;CLNNDB CLEAN OUT AN NDB. THIS ROUTINE ZEROS ALL MESSAGE COUNTS ; AND OTHER APPROPRIATE FIELDS AND FREES ALL DATA HANGING OFF OF ; AN NDB. (CALL WHEN SENDING OR RECEIVING STARTS) ;CALL W := NDB ;RETURN CPOPJ CLNNDB: NTDBUG ;VERIFY THE INTERLOCK PUSHJ P,SAVE4## ;THIS ROUTINE CLOBBERS EVERYTHING PUSHJ P,SAVJW## ;THESE TOO PUSH P,U ;AND EVEN THIS IFN PARANOID&P$NDB, ;MAKE SURE WE DON'T DELETE OUR NDB ;HERE TO CHECK FOR STATION CONTROL RESOURCE PUSH P,W ;SAVE NDB POINTER HRRZ J,NDBICT(W) ;GET MESSAGE ADDR SKIPE J ;IS THERE ONE ? PUSHJ P,GIVSTC ;IF SO, FREE THEM SKIPE T1,NDBSTC(W) ;ANYONE USING STATION CONTROL PUSHJ P,EWAKE## ;IF SO, WAKE HIM UP. POP P,W ;GET NDB POINTER BACK ;HERE TO CALL ALL THE NETWORK DEVICES AND INFORM THEM THAT THIS NODE HAS ; GONE AWAY. ALL THE SERVICE ROUTINES WILL BE CALLED ON THEIR "NDPNWD" ; ENTRY POINT. THE INTERFACE FOR A NDPNWD ENTRY SHOULD BEHAVE AS FOLLOWS. ;AT ENTRY ; P1 := NODE NUMBER OF CRASHED NODE. ; P2 := LAT INDEX ('SLA') ; F := LAT ENTRY FOR THIS DEVICE (XWD FLAGS,DDB/LDB) ;RETURN CPOPJ ;ALWAYS. CLNND0: MOVSI P2,-LATLEN## ;MAKE AN AOBJN POINTER FOR ACCESSING THE LAT. HLRZ P1,NDBNNM(W) ;GET THE NUMBER OF THE CRASHED NODE. PUSH P,W ;WE MAY CLOBBER "W". PRESERVE IT FOR NOW. JRST CLNND2 ;SKIP OVER THE ZERO'TH ENTRY (NETDDB) CLNND1: SKIPN T2,NETLAT##(P2) ;GET THE NEXT LAT ENTRY JRST CLNND2 ;THIS ENTRY NOT IN USE LDB F,LATPP2 ;EXTRACT DDB/LDB ADDRESS (INDEX P2) TLNN T2,LAT.TY ;IS THIS A DDB OR LDB SKIPA T1,DEVNET(F) ;DDB, GET NODE NUMBER LDB T1,LDPRNF## ;LDB, GET NODE NUMBER ANDI T1,777 ;MASK OFF LH JUNK CAIE T1,(P1) ;IS THIS DEVICE/TTY DOOMED? JRST CLNND2 ;NO, LEAVE IT ALONE THEN MOVEI T1,NDPNWD ;GET THE "NODE WENT DOWN" FCN CODE PUSHJ P,ICMNDP ;AND CALL THE DISPATCH ROUTINE CLNND2: AOBJN P2,CLNND1 ;LOOP OVER ALL LAT ENTRYS POP P,W ;RECOVER OUR NDB POINTER ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;HERE TO PURGE THE NDB NAME POINTERS MOVEI T1,^D8 ;FOUR WORDS TO REMOVE HLRZ T2,NDBSID(W) ;GE THE SOFTWARE ID POINTER SKIPE T2 ;ASSIGNED PUSHJ P,GIVZWD ;RETURN THE SPACE MOVEI T1,^D8 ;FOUR WORDS TO RETURN HRRZ T2,NDBSID(W) ;GET THE ADDRESS SKIPE T2 ;NOT ASSIGNED PUSHJ P,GIVZWD ;REMOVE THE SPACE ;HERE TO REMOVE THE QUEUES FOR THE NODES HRRZ U,NDBQUE(W) ;GET AND FREE THE PUSHJ P,RMVALP ; THE RIGHT HAND HLRZ U,NDBQUE(W) ;GET AND FREE THE PUSHJ P,RMVALP ; THE LEFT HAND HRLZI T1,(W) ;BUILD A BLT POINTER HRRI T1,1(W) ; TO ZERO THE NDB NETOFF ;COVER OURSELVES WHILE NDB IS SCREWED UP ; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB") MOVE T2,NDBNNM(W) ;SAVE "XWD NODE#,LINK" FOR A WHILE HRRZ T3,NDBFEK(W) ;SAVE THE ADDR OF THE FEK SETZM (W) ;ZAP THE FIRST, AND THEN BLT T1,NDBLEN-1(W) ; THE REST OF THE NDB. MOVEM T3,NDBFEK(W) ;RESTORE THE FEK ADDRESS AND THE MOVEM T2,NDBNNM(W) ; NODE#,,LINK WORD NETON ;ALL IS CONSISTENT NOW MOVEI T1,^D30 ;ONLY ALLOW A MAX OF 30 OUTSTANDING (IE. ; UN-ACKED) MESSAGES MOVEM T1,NDBMOM(W) ;INITIALIZE MAX OUTSTANDING MESSAGES PJRST UPOPJ ;RESTORE U AND RETURN ;ROUTINE TO HANDLE NODE ONLINE EVENTS ;CALL MOVX W,NDB-POINTER ; PUSHJ P,NODEUP ;RETURN CPOPJ ;DOES "ONLINE" PROCESSING ONLY IF NODE WAS NOT ; ; ONLINE BEFORE NODEUP: MOVSI T1,NDB.UP ;GET THE NODE "ONLINE" FLAG TDNE T1,NDBFLG(W) ;SEE IF WE ARE ALREADY ONLINE POPJ P, ; IF ONLINE ALREADY, RETURN IORM T1,NDBFLG(W) ;MARK THE NODE AS ONLINE S0PSHJ ONLNDB ;PRINT THE ONLINE MESSAGE PUSHJ P,PSINTC## ;SIGNAL THE ONLINE EVENT MOVE T1,[XWD .CSCNO,.ERCSC] ;GET CODE TO TELL DAEMON NODE ONLINE PJRST NODEAM ; AND GO TELL DAEMON ;ROUTINE TO HANDLE NODE OFFLINE EVENTS ;CALL MOVX W,NDBPOINTER ; PUSHJ P,NODEDN ;RETURN CPOPJ ;DOES "OFFLINE" PROCESSING ONLY IF THE ; ;NODE WAS ONLINE. NODEDN: MOVSI T1,NDB.UP ;GET THE "ONLINE" BIT TDNN T1,NDBFLG(W) ;IF WE'RE NOT ONLINE NOW, POPJ P, ; THEN DON'T DO ANY OFFLINE STUFF ANDCAM T1,NDBFLG(W) ;CLEAR THE ONLINE BIT S0PSHJ OFLNDB ;TELL THE OPERATOR IT WENT AWAY PUSHJ P,PSINTC## ;SIGNAL THE OFFLINE EVENT MOVE T1,[XWD .CSCNF,.ERCSC] ;GET NODE OFF-LINE DAEMON CODE ; PJRST NODEAM ; AND GO TELL DAEMON ;ROUTINE TO CALL DAEMON FOR NODE ON/OFF LINE EVENTS ;CALL MOVX T1,"DAEMON CODE" ; PUSHJ P,NODEAM ;RETURN CPOPJ ;ALWAYS NODEAM: PUSHJ P,SAVJW## ;"J" GETS MASHED BY DAEEIM PUSH P,U ;DON'T WANT TO CLOBBER INCTSK'S PCB SETZ F, ;CLEAR "F" SO AS NOT TO CONFUSE DAEMON HLRZ U,NDBNNM(W) ;GET THE NODE NUMBER IN U PUSHJ P,DAEEIM## ;CALL THE NON-BLOCKING FORM OF DAEMON PJRST UPOPJ## ;CLEAN UP AND RETURN ;SRCNDB ROUTINE TO FIND A NDB GIVEN A NODE NAME OR NUMBER ;CALL MOVE T1,NNM ;NODE NUMBER ;OR MOVE T1,[SIXBIT /NODE NAME/] ;SIXBIT NODE NAME ; PUSHJ P,SRCNDB ;RETURN CPOPJ ;NOT FOUND ; CPOPJ1 ;FOUND W=NDB POINTER SRCNDB::NTDBUG ;VERIFY THE INTERLOCK SRCND0::TLNE T1,-1 ;IF IT'S A NAME, JRST SRCND3 ; GO USE A DIFFERENT LOOP SRCND1: SKIPN T1 ;IF NODE NUMBER IS ZERO, MOVE T1,JBTLOC##+0 ;USE THE LOCAL NODE CAILE T1,NODMAX ;RANGE CHECK POPJ P, ;NOT IN RANGE HRRZ W,.GTNDA##(T1) ;GET NDB ADDRESS CAIE W,0 ;ONLINE AOS (P) ;YES, SKIP POPJ P, ;AND RETURN SRCND3: PUSHJ P,SRCND4 ;FIRST TRY IT AS A "NAME" CAIA ; AND IF THAT SUCCEEDS, JRST CPOPJ1## ; THEN WE'RE DONE. PUSH P,T1 ;OTHERWISE, TRY TO CONVERT PUSHJ P,CVTOCT## ; A SIXBIT NUMBER JRST TPOPJ## ; IF THAT FAILS, GIVE UP PUSHJ P,SRCND1 ;TRY THE NUMBER JRST TPOPJ## ; FAILED JRST TPOPJ1## ; SUCCEEDED. SRCND4: MOVEI W,NETNDB ;GET THE HEAD OF THE CHAIN SRCND5: HLRZ T3,NDBSNM(W) ;GET THE ADDRESS OF THE NAME JUMPE T3,SRCND6 ;IF NO NAME, NO ADDRESS OF NAME CAMN T1,(T3) ; SEE IF THEY MATCH JRST CPOPJ1## ;RETURN SUCCESS IF THEY DO SRCND6: HRRZ W,NDBNNM(W) ;OTHERWISE GO TO THE NEXT JUMPN W,SRCND5 ; AND CHECK THAT POPJ P, ;ERROR RETURN IF NO MORE ;*** FOOTNOTE *** COMMENT \ The entry SRCND0 should only be called under cover of a "NETOFF" macro. One of the fundamental goals in the 7.01 NETSER was to eliminate crashes caused by "stale" pointers to NDB's that had been destroyed. In light of this, the only time that a pointer to an NDB is valid is under the NETSER interlock. Unfortunatly, ocasionally the need arises to look at some field in some NDB at a time when it is not convenient to request the NETSER interlock. Examples of this are translating a node name to a node number, and finding the OPR ldb of a particular remote station. Both of these routines (STBSCA and STBOPR) are called from clock and/or interrupt levels, and hence cannot do a NDTDBJ. Routines like these MUST NETOFF for the period that they possess a valid NDB address. \ SUBTTL NDT -- ROUTINES TO SEARCH THE NDT ;SUBROUTINE SRCNDT - SEARCH THE NETWORK DEVICE TABLE ;CALL HRRZ T1,(SIXBIT /GENERIC NETWORK DEVICE NAME/) ; MOVEI P1,NDB ; PUSHJ P,SRCNDT ;RETURN CPOPJ ;DEVICE NOT FOUND ; CPOPJ1 ;DEVICE FOUND W(RT)=THE NDT POINTER SRCNDT::CAIE T1,'MCR' ;THE "MONITOR COMMAND ROUTINE" ISN'T CAIN T1,'MC ' ; A REAL DEVICE (TOO BAD, MIGHT BE NEAT) POPJ P, ;REJECT MCR: NTDBUG ;VERIFY THE INTERLOCK PUSH P,T4 ;SAVE T4 MOVE T4,NDTXWD## ;GET THE SEARCH POINTER SRCNDC: SKIPN W,(T4) ;GET AN NDT AOBJN T4,.-1 ;KEEP SEARCHING JUMPGE T4,SRCNDX ;DONE? HLRZ T2,NDTNAM(W) ;GET THE -10 DEVICE NAME HRRZ T3,NDTNAM(W) ;GET THE -11 DEVICE NAME CAIE T1,(T2) ;IS IT A -10 DEVICE CAIN T1,(T3) ;OR A -11 DEVICE JRST SRCNDD ;CHECK THE CONFIGURATION AOBJN T4,SRCNDC ;AND TRY AGAIN SRCNDX: POP P,T4 ;RESTORE T4 POPJ P, ;DEVICE NOT FOUND SRCNDD: MOVE T4,W ;COPY NDT POINTER EXCH T4,(P) ;SAVE NDT AND RESTORE T4 LDB T1,NDTOBJ ;GET THE DEVICE TYPE MOVEI W,(P1) ;COPY THE NDB POINTER LDB T1,NETCNF##(T1) ;GET THE CONFIGURATION COUNT JUMPE T1,WPOPJ## ;THAT NODE DOES NOT HAVE ANY PJRST WPOPJ1## ;DEVICE IS OK SUBTTL LAT -- ROUTINES FOR MANIPULATING THE LINK ADDRESS TABLE ;GETSLA ROUTINE TO GET A SOURCE LINK ADDRESS FROM THE LAT TABLE ;CALL: MOVE T1, ; MOVEI T2,LAT.XX ;LAT STATE ; PUSHJ P,GETSLA ;RETURN CPOPJ ;NONE AVAILABLE ; CPOPJ1 ;T1=SLA GETSLA::NTDBUG ;VERIFY THE INTERLOCK PUSH P,T1 ;SAVE LAT ADDRESS AND FLAGS IFN PARANOID&P$LAT,< TLZ T1,777700 ;MASK OUT ALL BUT ADDRESS PUSH P,T2 ;SAVE DESIRED INITIAL STATE CAIL T2,0 ;RANGE CAILE T2,LAT.MX ; CHECK PUSHJ P,NTDSTP ;++ INITIAL LAT STATE ILLEGAL MOVSI T2,-LATLEN## ;PROTOTYPE LAT INDEXER GETSL1: LDB T3,LATPT2 ;GET THIS SLA'S DDB/LDB ADDRESS CAMN T3,T1 ;DOES THIS DDB/LDB ALREADY HAVE A LAT SLOT? STOPCD .,STOP,ANFWLA, ;++ WRONG LAT ASSIGNED AOBJN T2,GETSL1 ;LOOP OVER ALL LAT ENTRIES POP P,T2 ;RETRIEVE INITIAL LAT STATE > ;IFN PARANOID&P$LAT MOVSI T1,-LATLEN## ;GET THE LENGTH FOR THE AOBJN SKIPE NETLAT##(T1) ;LOOK FOR AN AVAILABLE SLOT AOBJN T1,.-1 ;CONTINUE STEPPING JUMPGE T1,TPOPJ## ;NONE AVAILABLE POP P,NETLAT(T1) ;MARK THE SLOT AS IN USE DPB T2,LATSTA ;SET INITIAL STATE IN LAT ENTRY FOR THIS SLA JRST CPOPJ1 ;T1=SLA ;GIVSLA ROUTINE TO RETURN A LAT ADDRESS ;CALL MOVEI F,DDB ;THIS DOESN'T WORK FOR TERMINAL ; PUSHJ P,GIVSLA ;RETURN THE LAT ENTRY, ZERO NETSLA ;RETURN CPOPJ ;UNLESS WE STOPCD. ; ;NOTE! THIS ROUTINE PRESERVES ALL REGISTERS (ESPECIALLY T1) GIVSLA::NTDBUG ;VERIFY THE NETSER INTERLOCK PUSHJ P,SAVE2## ;SAVE P1, P2 SO WE DON'T CLOBBER THE T'S LDB P2,NETSLA ;GET THE CURRENT SLA FOR THIS DEVICE HRRZ P1,NETLAT##(P2) ;GET [MOST OF] THE LAT ENTRY. ;IT'S OK TO ONLY LOOK AT 18 BITS HERE . . . CAIE P1,(F) ;MAKE SURE EVERYTHING IS CONSISTENT. STOPCD .,STOP,ANFLAT, ;++ DDB AND LAT DON'T AGREE SETZB P1,NETLAT##(P2) ;FREE THE LAT DPB P1,NETSLA ;CLEAR THE SLA IN THE DDB DPB P1,NETDLA ; GET THE DLA SINCE NOT CONNECTED. POPJ P, ;ALL DONE SUBTTL NPD -- ROUTINES TO MANIPULATE NETWORK CONNECT DISCRIPTORS ;GETNPD ROUTINE TO ALLOCATE A NETWORK CONNECTION DESCRIPTOR BLOCK ;CALL MOVEI T1,LENGTH ;LENGTH OF DESIRED NPD (INCLUDES NPDBLK) ; PUSHJ P,GETNPD ;RETURN CPOPJ ;ERROR RETURN. NO CORE ; CPOPJ1 ;WITH J := A POINTER TO THE NPD ; GETNPD:: ;HERE TO ALLOCATE AN NPD MOVEI T2,(T1) ;COPY THE LENGTH FOR GETZWD PUSH P,T1 ;SAVE IT SO WE CAN PUT IT IN NPDBLK PUSHJ P,GETZWD ;ALLOCATE THE STORAGE JRST TPOPJ## ;NO STORAGE. ERROR RETURN POP P,T2 ;GET THE LENGTH BACK MOVEI J,(T1) ;COPY THE NPD POINTER HRLZM T2,NPDBLK(J) ;SAVE THE LENGTH JRST CPOPJ1## ;GOOD RETURN ;GIVNPD ROUTINE TO RETURN A NETWORK CONNECTION DESCRIPTOR BLOCK ;CALL MOVEI J,NPD ADDRESS ; PUSHJ P,GIVNPD ;RETURN CPOPJ ;ALWAYS ; GIVNPD::HLRZ T1,NPDBLK(J) ;GET THE ALLOCATED LENGTH OF THE NPD MOVEI T2,(J) ;GET THE ADDRESS OF THE STORAGE PUSHJ P,GIVZWD ;FREE THE NPD SETZ J, ;INDICATE THAT IT IS GONE POPJ P, ;GIVE A GOOD RETURN ;GV2NPD RETURN BOTH NPD'S OF A TASK ;CALL F := DDB ;RETURN CPOPJ ;ALWAYS GV2NPD::PUSH P,J ;PRESERVE "J" HRRZ J,DEVNPD(F) ;GET THE ADDRESS OF THE FIRST NPD SKIPE J ;IF IT'S NOT NULL, PUSHJ P,GIVNPD ;RETURN IT HLRZ J,DEVNPD(F) ;GET THE ADDRESS OF THE REMOTE NPD SKIPE J ;IF IT'S NOT NULL, PUSHJ P,GIVNPD ;RETURN IT TOO SETZM DEVNPD(F) ;CLEAR THE NPD POINTER JRST JPOPJ## ;RESTORE "J" AND RETURN SUBTTL STC -- ROUTINES TO MANIPULATE STATION CONTROL MESSAGE BLOCKS ;GETSTC ROUTINE TO ALLOCATE A STC (STATION CONTROL MESSAGE) BLOCK ;CALL T1 := NUMBER OF BYTES IN THE STC MESSAGE ; W := POINTER TO THE NDB WE'RE SENDING TO/RECEIVING FROM ;RETURN CPOPJ ;NO CORE ; CPOPJ1 ;STC ALLOCATED. ; ; J := POINTER TO STC BLOCK GETSTC::PUSH P,T1 ;SAVE THE LENGTH (IN BYTES) MOVEI T2,3+<4*STCDAT>(T1) ;SET T2 := LENGTH(BYTES) OF ENTIRE STC LSH T2,-2 ;CHANGE WORDS TO BYTES (ROUNDED UP) PUSHJ P,GETZWD ;GET A BLOCK OF FREE-CORE TO HOLD STC MSG JRST TPOPJ## ; IF NO MORE MEMORY, GIVE ERROR RETURN MOVEI J,(T1) ;MAKE "J" THE STC POINTER POP P,T1 ;GET BACK THE LENGTH OF THE MESSAGE (BYTES) HRLZM T1,STCBLK(J) ;STORE LENGTH IN STC BLOCK RETSKP ;GIVE GOOD RETURN ;GIVSTC ROUTINE TO FREE A STC (STATION CONTROL MESSAGE) BLOCK ;CALL J := POINTER TO THE STC MESSAGE BLOCK ;RETURN CPOPJ ;ALWAYS GIVSTC::MOVEI T2,(J) ;SET T2 := ADDRESS OF STC BLOCK HLRZ T1,STCBLK(J) ;GET THE LENGTH (BYTES) OF THE STC DATA ADDI T1,3+ ;GET THE LENGTH (BYTES) OF STC DATA + STC BLOCK LSH T1,-2 ;SET T1 := LENGTH OF BLOCK (WORDS ROUNDED UP) PJRST GIVZWD ;RETURN THE STORAGE AND RETURN ;FEK2LN ROUTINE TO CONVERT FROM FEK ADDRESS TO STATION CONTROL LINE NUMBER ;CALL J := FEK ;RETURN CPOPJ ;T1 := LINE NUMBER ; ;STOPS IF J /= A VALID FEK ADDRESS FEK2LN: MOVEI T1,1 ;START WITH LINE NUMBER 1 MOVEI T2,FEKFST ;GET THE ADDRESS OF THE FIRST FEK FEK2L1: CAIN T2,(J) ;IF THIS IS THE FEK WE'RE LOOKING FOR POPJ P, ; RETURN WITH T1 := ITS LINE NUMBER HRRZ T2,FEKBLK(T2) ;GET THE ADDRESS OF THE NEXT FEK SKIPN T2 ;IF THERE AREN'T ANY MORE FEKS, THEN STOPCD .,STOP,ANFGFK, ;++ GARBAGE FEK POINTER AOJA T1,FEK2L1 ;INCREMENT THE LINE NUMBER AND CHECK NEXT FEK ;LN2FEK ROUTINE TO CONVERT FROM STATION CONTROL LINE NUMBER TO FEK ADDRESS ;CALL T1 := LINE NUMBER ;RETURN CPOPJ ;NO FEK CORRISPONDS TO THAT LINE ; CPOPJ1 ;J := ADDRESS OF FEK. LN2FEK: MOVEI J,FEKFST ;GET THE ADDRESS OF FEK FOR LINE #1 SOJN T1,[HRRZ J,FEKBLK(J) ;DECREMENT THE LINE NUMBER, GO TO NEXT FEK JUMPE J,CPOPJ ;IF DONE ALL FEKS, GIVE FAIL RETURN JRST .] ;KEEP LOOKING RETSKP ;RETURN WITH J := ADDRESS OF FEK REPEAT 0,< ;USELESS CODE (I THINK) ;STCREJ ROUTINE TO SEND A STATION CONTROL REJECT <13> MESSAGE ;CALL J := POINTER TO STC MESSAGE WITH ; 1ST BYTE := LINE # ; LH(STCNNL) := NODE NUMBER TO SEND REJECT TO ;RETURN CPOPJ WITH STC FREED STCREJ: PUSH P,W ;SAVE W (WE PUT DESTINATION NDB IN IT) PUSH P,J ;SAVE THE STC POINTER HLRZ T1,STCNNL(J) ;GET THE NODE NUMBER PUSHJ P,SRCNDB ; AND LOOK UP THE NDB JRST STCRE1 ;IF NODE NOT THERE, JUST FREE THE STC MSG PUSHJ P,NCMHDR ;WRITE THE MESSAGE HEADER JRST STCRE1 ;IF NO CORE, JUST TOSS THE STC BLOCK ;TYP XMTI NC.CTL ;THIS IS A STATION CONTROL MESSAGE ;LINE LDB T1,[POINT 8,STCDAT(J),7] ;GET THE LINE NUMBER XMT T1 ; AND SEND THAT ;CODE XMTI STC.RJ ;SEND THE REJECT CODE JSP T1,NETWRC ;SEND THE MESSAGE (AND CLOBBER "J") STCRE1: POP P,J ;GET THE STC POINTER BACK PUSHJ P,GIVSTC ;RETURN THE STC BLOCK JRST WPOPJ## ;RESTORE "W" AND RETURN >;END REPEAT 0 ;STCSEC ROUTINE CALLED ONCE/SEC TO DO STATION CONTROL TIMING FUNCTIONS ;CALL W := NDB POINTER ;RETURN CPOPJ ;ALWAYS ;ACTION IF THE "NDBSTC" TIMER GOES OFF, THE JOB OWNING NDBSTC ; IS WOKEN, AND NDBSTC(W) IS CLEARED ; IF THE "NDBICT" TIMER GOES OFF, THE INCOMING MESSAGE IS FREED STCSEC: SKIPN T1,NDBSTC(W) ;IS THE STATION CONTROL DEVICE IN USE? JRST STCSE1 ; IF NOT IN USE, GO TIME OUT BOOT REQUESTS HLRZ T1,T1 ;GET THE TIMER FOR STATION CONTROL USERS SOSLE T1 ;DECREMENT IT, SKIP IF IT COUNT'S OUT JRST [HRLM T1,NDBSTC(W) ;IF NOT COUNTED OUT YET, STORE IT -1 JRST STCSE1] ; BACK AND GO TIME INCOMING MESSAGES HRRZ T1,NDBSTC(W) ;GET THE JOB NUMBER OF THE USER THAT TIMED OUT SKIPE T1 ; IF NO ONE WAS USING IT, DON'T WAKE ANYONE PUSHJ P,EWAKE## ;WAKE THE USER NOW THAT HE'S TIMED OUT SETZM NDBSTC(W) ;CLEAR STC TO SAY THAT NO-ONE IS USING IT. STCSE1: SKIPN T1,NDBICT(W) ;SEE IF ANY BOOT MSGS ARE WAITING JRST STCSE2 ;IF NONE, DON'T LOOK ANY FARTHER HLRZ T1,T1 ;GET THE COUNTER FOR INCOMING STC MSGS SOSLE T1 ;COUNT OFF ONE MORE SECOND. IF NOT TIMED OUT, JRST [HRLM T1,NDBICT(W) ;THEN JUST STORE THE UPDATED COUNT JRST STCSE2] ;BACK AND EXIT PUSH P,J ;IF MESSAGE BEEN HERE TO LONG, FREE IT. SAVE J HRRZ J,NDBICT(W) ;GET A POINTER TO THE STC BLOCK PUSHJ P,GIVSTC ;RETURN THE STC BLOCK POP P,J ;RESTORE "J" SETZM NDBICT(W) ;INDICATE THAT NO STC MESSAGES ARE WAITING STCSE2: POPJ P, ;RETURN. SUBTTL FEK -- ROUTINES TO MANIPULATE FEKS ;CLNFEK THIS ROUTINE CLEANS OUT A FEK. IT RETURNS ALL ; BUFFER SPACE ASSOCIATED WITH A FEK, AND GIVES THE ; OUTPUT MESSAGES TO THE OUTPUT DONE ROUTINE IN NETSCN. ; IN THIS WAY MESSAGES BEING ROUTED THROUGH THE NODE WILL ; GET RE-ROUTED, AND MESSAGES TO THE NODE WILL BE DISCARDED. ;CALL MOVE J,FEK ;GET THE ADDRESS OF THE FEK ; PUSHJ P,CLNFEK ;CLEAN IT OUT ;RETURN CPOPJ ;ALWAYS ;CLOBBERS S,U,T1,T2 NTFONC==:CLNFK2 ;FOR NOW. CLNFEK: ;HERE TO RECYCLE A FEK IFN PARANOID&P$FEK,< ;DON'T REFURBISH NULL FEK HLLZ S,FEKBLK(J) ;GET THE FEK'S FLAGS TLNE S,FK.NUL ;SEE IF THIS IS A NULL FEK STOPCD .,STOP,ANFNUL, ;++ NULL FEK BEING CLEANED > SKIPE U,FEKIAD(J) ;GET THE INPUT PCB, IF THERE WAS ONE S0PSHJ RMVPCB ; THEN FREE THE INPUT BUFFER SETZM FEKIAD(J) ;CLEAR THE INPUT PCB POINTER SKIPG T2,FEKOCT(J) ;ARE THERE ANY OUTPUT PCBS? JRST CLNFK2 ;IF NOT, THEN DON'T FREE ANY. HRRZ U,FEKOAD(J) ;GET THE FIRST OUTPUT PCB SKIPA T1,U ;INITIALIZE WHAT WILL BECOME POINTER TO LAST CLNFK1: HRRZ T1,PCBBLK(T1) ;ADVANCE THE POINTER TOWARDS THE LAST SKIPN T1 ;MAKE SURE WE'VE GOT A PCB STOPCD .,STOP,ANFNPL, ;++ NO PCB'S ON LIST SOJG T2,CLNFK1 ;ADVANCE N-1 TIMES WHERE N = # OF PCB'S ; (EXIT WITH T1 POINTING TO LAST PCB) HRRZ T3,PCBBLK(T1) ;GET THE POINTER TO THE NTH+1 PCB SKIPE T3 ; IF IT ISN'T ZERO, THEN STOPCD .,STOP,ANFPCC, ;++ COUNT OF PCB'S ON LIST IS WRONG NETOFF ;PROTECTION WHEN DIDDLING QUEUES EXCH U,NTQOUT ;PUT THIS LIST ON THE HEAD OF NTQOUT HRRM U,PCBBLK(T1) ;AND SPLICE THE OLD ONE ON THE END NETON ;ALL CLEAR (WASN'T THAT EASY!) CLNFK2: MOVSI S,FK.ONL!FK.NID!FK.STO!FK.STI!FK.OAC!FK.IAC!FK.CPD ANDCAB S,FEKBLK(J) ;IN THE FEK STATUS WORD. SETZM FEKOAD(J) ;CLEAR POINTER TO THE PCB'S SETZM FEKODN(J) ;CLEAR OUTPUT DONE POINTER SETZM FEKOCT(J) ;SET FEK IDLE SETZM FEKHTM(J) ;CLEAR THE HUNG TIMER HLLZS FEKNNM(J) ;CLEAR THE NODE NUMBER SETOM FEKBSO(J) ;CLEAR OUTPUT BUSY SETOM FEKBSI(J) ;CLEAR INPUT BUSY POPJ P, ;AND WE ARE DONE SUBTTL ROUTINE TO GENERATE THE NCL PROTOCOL HEADERS ;SUBROUTINE NCSHDR - CREATE ;CALL MOVEI W,NDB ; MOVEI T1,NCT MESSAGE TYPE ; PUSHJ P,NCSHDR ;RETURN CPOPJ ;NO CORE ; CPOPJ1 ;P3=COUNT, P2=CURRENT BYTE POINTER NCSHDR: ;ENTRY NTDBUG ;VERIFY THE INTERLOCK PUSHJ P,SAVE1## ;DON'T CLOBBER P1. MOVE P1,T1 ;COPY THE FLAGS MOVEI T1,^D16 ;MAKE A PCB WITH 16 WDS OF BUFFER TRNE P1,7 ;IS THIS A NUMBERED MESSAGE? JRST [PUSHJ P,MKUPCB ; IF NOT, THEN GET AN 'UN-NUMBERED' PCB POPJ P, ; (EXIT NCSHDR IF NOT AVAILABLE) JRST NCSHD1] ;RETURN TO MAIN FLOW WITH U := PCB PUSHJ P,MKNPCB ;IF IT'S A DATA MESSAGE, GET A NUMBERED PCB POPJ P, ; BUT GIVE ERROR RETURN IF NOT AVAILABLE. NCSHD1: MOVE P2,PCBPTR(U) ;GET BYTE POINTER AOS (P) ;WE WILL SUCCEED, SO GIVE GOOD RETURN MOVE T1,P1 ;GET THE NCT FLAGS BACK FOR NCSWHD ; PJRST NCSWHD ;FILL IN THE HEADER ;NCSWHD ROUTINE TO WRITE THE HEADER OF AN NCS MESSAGE. ;CALL MOVE T1,NCT FLAGS ; MOVE U,POINTER TO VIRGIN PCB ; PUSHJ P,NCSWHD ;RETURN CPOPJ ;ALWAYS ; ; PCB HAS NCT, DNA, SNA, NCA, NCN FILLED IN, ; ; PCBCTR IS UPDATED AND P3 IS ZERO, ; ; P2 HAS THE POINTER TO THE "DLA" BYTE. NCSWHD:: SETZ P3, ;CLEAR THE COUNT FIELD TRO T1,NCT.RH!NCT.SQ ;INSERT THE ADDITIONAL FLAGS PUSH P,T1 ;SAVE THE FLAGS FOR LATER ;NCT PUSHJ P,BI2EBI ;OUTPUT THE FLAGS ;DNA HLRZ T1,NDBNNM(W) ;GET THE DESTINATION NODE ADDRESS HRRM T1,PCBNNM(U) ;STORE IN PCB FOR NETWRT TO USE WHEN SENDING PUSHJ P,BI2EBI ;OUTPUT ;SNA MOVEI T1,NETNDB## ;ADDRESS OF THE NODE DATA BLOCK HLRZ T1,NDBNNM(T1) ;GET THE SOURCE NODE ADDRESS (US) PUSHJ P,BI2EBI ;OUTPUT ;NCA ;REAL MESSAGE NUMBER IS ASSIGNED BY NETWRT MOVEI T1,0 ;DUMMY MESSAGE NUMBER NOW PUSHJ P,DPBBIN ;STORE ;NCN MOVEI T1,0 ;AGAIN PUSHJ P,DPBBIN ;EXIT MOVSI T1,PCB.NM ;GET NUMBERED MESSAGE FLAG POP P,T2 ;GET THE NCT FLAGS BACK TRNN T2,NCT.TP ;NUMBERED MESSGE?? IORM T1,PCBBLK(U) ;YES, SET THE FLAG ADDM P3,PCBCTR(U) ;UPDATE THE CURRENT COUNT SETZ P3, ;CLEAR THE COUNT FIELD POPJ P, SUBTTL UNNUMBERED NCS CONTOL MESSAGES ;SUBROUTINE NCSSTR/NCSSAK - SEND A START/STACK MESSAGE ;CALL MOVEI W,NDB ;WHERE TO SEND THE MESSAGE ; PUSHJ P,NCSSSM ;SEND A START OR A STACK MESSAGE ;RETURN CPOPJ ;CAN'T NO CORE ; CPOPJ1 ;OK NCSSSM: MOVE T1,NDBFLG(W) ;GET THE FLAGS, AND TLNE T1,NDB.SK ; IF WE ARE SUPPOSED TO SEND A STACK JRST NCSSS1 ; THEN SKIP THE SEND-START CODE PUSHJ P,CLNNDB ;CLEAN OUT THE NDB FOR STARTERS MOVEI T1,NCT.ST ;START FLAGS SKIPA NCSSS1: MOVEI T1,NCT.SK ;START ACK FLAGS PUSHJ P,SAVE3## ;SAVE THE P'S IFN PARANOID&P$NDB, ;MAKE SURE WE DON'T SEND OURSELF A START PUSH P,T1 ;SAVE THE FLAGS SETZ F, ;NCS MESSAGE PUSHJ P,NCSHDR ;MAKE A HEADER (U = NEW PCB) PJRST TPOPJ## ;EXIT NO CORE PUSHJ P,NCSOPD ;GET THE HRRZ T1,NETNDB##+NDBNVR ;GET OUR NCL VERSTION PUSHJ P,BI2EBI ;SEND IT IN MESSAGE ADDM P3,PCBCTR(U) ;UPDATE THE COUNT POP P,T2 ;RESTORE THE FLAGS PUSHJ P,NETWRT ;SEND THE STACK MESSAGE JRST CPOPJ1 ;ALL DONE ;SUBROUTINE NCSNID - SEND A NODE ID MESSAGE ;CALL MOVEI J,FEK ; PUSHJ P,NCSNID ;RETURN CPOPJ ;NO CORE ETC ; CPOPJ1 ;SENT NCSNID: PUSHJ P,SAVE3## ;SAVE THE P'S IFN PARANOID&P$FEK,< ;MAKE SURE WE DON'T SEND THE NULL FEK A NODEID HLLZ T1,FEKBLK(J) ;GET THE FEK'S FLAGS TLNE T1,FK.NUL ;SEE IF IT'S A NULL FEK STOPCD .,STOP,ANFNFI, ;++ SENDING NODE ID TO THE NULL FEK > SETZB W,F ;NO DDB OR NDB MOVEI T1,^D16 ;MESSAGE SIZE PUSHJ P,MKUPCB ;ALLOCATE THE MESSAGE SPACE POPJ P, ;NO SPACE AVAILABLE MOVE P2,PCBPTR(U) ;GET THE BYTE POINTER SETZ P3, ;CURRENT COUNT IS ZERO ;NCT MOVEI T1,NCT.ID!NCT.SQ ;GET ID FLAGS PUSHJ P,BI2EBI ;WRITE THE FLAGS ;NCA MOVEI T1,0 ;NO, ACKS FOR MESSAGE NUMBERS PUSHJ P,BI2EBI ;WRITE ;NCN MOVEI T1,0 ;SAME AS ABOVE PUSHJ P,BI2EBI ;WRITE ;OPD PUSHJ P,NCSOPD ;SEND THE IFN FTENET,< MOVE T1,FEKBLK(J) ;GET FEK FLAGS TLNN T1,FK.ETM ;ETHERNET (MASTER) FEK? JRST NCSNI4 ;NO, SEND A P-P NODEID MOVEI T1,NIT.BC ;YES, MARK THIS AS A "BROADCAST" NODEID PUSHJ P,BI2EBI ;AND NCL'IZE IT AOS T1,FEKNIS(J) ;INCREMENT NODEID SERIAL NUMBER PUSHJ P,BI2EBI ;NCL'IZE THE SERIAL NUMBER HLRZ T2,FEKNIT(J) ;GET NEW BROADCAST TIMER INTERVAL HRRM T2,FEKNIT(J) ;AND SET IT FOR ONCE/SECOND CHECKING JRST NCSNI5 ;FINISH OFF NODE-ID > ;END IFN FTENET NCSNI4: XMTI NIT.PP ;THIS IS A POINT-TO-POINT NODE-ID NCSNI5: HRRZ T1,NETNDB##+NDBNVR ;GET OUR NCL VERSION NUMBER PUSHJ P,BI2EBI ;NCL-IZE IT MOVSI T1,FK.NID ;GET NODE ID FLAG IORM T1,FEKBLK(J) ;SET FLAG NODE ID SENT ADDM P3,PCBCTR(U) ;UPDATE THE COUNT AOS NCLXTP+NCT.ID ;COUNT THE MESSAGE TYPE AOS (P) ;SKIP RETURN IFN FTKL10, ;SET UP CACHE INFO FOR CALL TO FRCWRT NETOFF ;NO RACE, "NETWRT" WILL RESTORE PI'S PJRST FRCWRT ;FORCE WRITE THE MESSSAGE ;SUBROUTINE NCSOPD - GENERATE THE OPTIONAL DATA ;CALL MOVEI U,PCB ; PUSHJ P,NCSOPD ;RETURN CPOPJ NCSOPD: ;ENTRY PUSH P,W ;SAVE THE NDB POINTER MOVEI W,NETNDB## ;GET THE NODE DATA BLOCK ;NNM HLRZ T1,NDBNNM(W) ;GET THE NODE NUMBER PUSHJ P,BI2EBI ;WRITE ;SNM HLRZ T1,NDBSNM(W) ;GET THE POINTER TO THE SYSTEM NAME MOVE T1,(T1) ;GET THE NAME PUSHJ P,SX2EAS ;WRITE ;SID HRRZ P1,NDBSID(W) ;SOFTWARE NAME PUSHJ P,AS2EAZ ;WRITE HLRZ P1,NDBSID(W) ;CREATION DATE POP P,W ;RESTORE THE NDB PJRST AS2EAZ ;WRITE ;NCSNAK THIS ROUTINE FIRST SCRAPS ALL UNPROCESSED MESSAGES ON ; LH(NDBQUE(W)). IT THEN SENDS A NAK. ; NOTE NAK'S ARE ONLY SENT AS A RESPONSE TO A REP. ; NOTE REPS ALWAYS ELICIT A NAK RESPONSE NCSNAK: NETOFF ;PROTECT FROM FEK'S HLRZ U,NDBQUE(W) ;GET THE LIST OF PCB'S HRRZS NDBQUE(W) ;SPLICE OUT THE LIST NETON ;ALL CLEAR NOW PUSHJ P,RMVALP ;FREE THE LIST MOVEI T1,NCT.NK ;GET THE NAK NCT PJRST NCSANR ;GO TO COMMON CODE ;NCSREP THIS ROUTINE SENDS A REP MESSAGE (NO OTHER PROCESSING) ;CALL W := NDB ;RETURN CPOPJ FAILED ; CPOPJ1 SENT IT OK NCSREP: MOVEI T1,NCT.RP ;GET REP NCT PJRST NCSANR ;GO TO COMMON CODE ;SUBROUTINE NCSACK - NCSNAK SEND AN ACK MESSAGE ;CALL MOVEI W,NDB ;PUSHJ P,NCSACK/NCSNAK ;RETURN CPOPJ ;ERROR ; CPOPJ1 ;OK NCSACK: MOVEI T1,NCT.AK ;GET THE ACK TYPE ; PJRST NCSANR ;COMMON CODE TO FINISH ACK/NAK/REP NCSANR: ;COMMON CODE FOR ACK-NAK-REP ;CALL WITH T1 := NCT IFN PARANOID&P$NDB, ;MAKE SURE WE AREN'T SENDING IT TO US PUSHJ P,SAVE3## ;SAVE THE P'S PUSH P,F ;SAVE F SETZ F, ;CLEAR F NCS MESSAGE PUSHJ P,NCSHDR ;WRITE THE HEADER JRST FPOPJ ;RESTORE F AND GIVE ERROR EXIT POP P,F ;RESTORE F PJRST NETWSR ;SEND THE MESSAGE SUBTTL NCS NUMBERED CONTROL MESSAGE HEADER ;NCMHDR ROUTINE TO BUILD THE HEADER FOR NUMBERED CONTROL MESSAGES. ;CALL MOVEI W,NDB ; PUSHJ P,NCMHDR ;RETURN CPOPJ ;NO CORE ; CPOPJ1 ;U := PCB, -1(P) := BYTE POINTER TO "CNT" ; ;NOTE!!! ; THIS ROUTINE DOES THE GROSS AND UGLY THING OF MEDDLING WITH THE ; STACK ON A SUCCESSFUL RETURN. SINCE EVERY PLACE THIS ROUTINE WAS ; CALLED IMMEDIATLY DID A "PUSH P,P2" THIS IS FAKED BEFORE NCMHDR ; GIVES A SUCCESSFUL RETURN. (THIS LEAVES THE STACK IN THE CORRECT ; STATE FOR A "JSP T1,NETWRC" TO SEND THE MESSAGE.) ; NCMHDR:: ;HERE TO BUILD A NUMBERED MESSAGE HEADER MOVEI T1,0 ;CONTROL MESSAGE PUSHJ P,NCSHDR ;OUTPUT THE HEADER POPJ P, ;NO CORE, GIVE ERROR RETURN MOVEI T1,0 ;NUMBERED CONTROL MESSGE PUSHJ P,BI2EBI ;DEPOSIT MOVEI P3,2 ;ACCOUNT FOR THE FIRST ADDM P3,PCBCTR(U) ; TWO BYTES SETZ P3, ;CLEAR THE COUNT FIELD IBP P2 ;STEP OVER THE FIELD POP P,T1 ;GET THE RETURN ADDRESS PUSH P,P2 ;SAVE A POINTER TO THE "CNT" FIELD FOR NETWRC JRST 1(T1) ;GIVE SKIP (CPOPJ1) RETURN ;NCSCNT ROUTINE TO SEND A CONNECT CONFIRM MESSAGE. ;CALL MOVE T1,[XWD "ROUTINE TO WRITE SPN","ROUTINE TO WRITE DPN"] ; MOVEI F,DDB ; PUSHJ P,NCSCNC ;RETURN CPOPJ ;SOMETHING WAS WRONG. ERROR CODE IN T1. ; CPOPJ1 ;CONNECT CONFIRM WAS SENT - DEVICE IS CONNECTED NCSCNC::PUSHJ P,NCSCNT ;SEND A "CONNECT" MESSAGE POPJ P, ;HO HUM MOVSI S,IOSCON ;THE "DEVICE IS CONNECTED" FLAG IORB S,DEVIOS(F) ;TELL THE WORLD THE DEVICE IS GOOD NOW JRST CPOPJ1## ;SUCCESSFUL RETURN WITH HAPPY DEVICE ;NCSCNT ROUTINE TO SEND A CONNECT INITIATE MESSAGE. ;CALL MOVE T1,[XWD "ROUTINE TO WRITE SPN","ROUTINE TO WRITE DPN"] ; MOVEI F,DDB ; PUSHJ P,NCSCNT ;RETURN CPOPJ ;SOMETHING WAS WRONG. ERROR CODE IN T1. ; CPOPJ1 ;CONNECT WAS SENT. (BUT NOT CONFIRMED) NCSCNT:: ;HERE TO SEND A CONNECT MESSAGE NTDBUG ;JUST CHECKING... PUSHJ P,SAVE3## ;SAVE THE P'S PUSHJ P,SAVJW## ;WE WILL CLOBBER J(FEK) AND W(NDB) PUSH P,U ;SAVE ANY MESSAGE WE MAY BE READING MOVE P1,T1 ;SAVE ADDRESS TO WRITE "DPN" & "SPN" HRRZ T1,DEVNET(F) ;GET THE NUMBER OF THE NODE THAT OWNS THIS PUSHJ P,SRCNDB ;SET UP W TO POINT TO ITS NDB. JRST [MOVEI T1,NRTUNN-NODERT ;GIVE A NODE-WENT-AWAY ERROR JRST UPOPJ##] ;AND AN ERROR ROUTINE SO CALLER WILL NOTICE. MOVEI T1,MSGMAW## ;BECAUSE CONNECT MSGS MAY BE VERY LONG PUSHJ P,MKNPCB ; WE MUST GET A LARGE PCB JRST [MOVEI T1,NRTNCE-NODERT ;BUT IF WE ARE OUT OF CORE, THEN JRST UPOPJ##] ;GIVE A NODE-CAPACITY-EXCEEDED ERROR MOVE P2,PCBPTR(U) ;GET THE ADDRESS OF THE MESSAGE BUFFER SETZ T1, ;THIS IS A NUMBERED MSG (TYPE 0) PUSHJ P,NCSWHD ;WRITE THE 5 HEADER BYTES XMTI 0 ;NO DLA SIGNIFIES NUMBERED CONTROL PUSH P,P2 ;REMEMBER WHERE THE "CNT" FIELD IS XMT1 T1 ;WRITE TWO GARBAGE XMT1 T1 ; BYTES. (WILL HOLD AN EXTENSIBLE COUNT) ADDM P3,PCBCTR(U) ;UPDATE THE LENGTH OF THE MESSAGE SO FAR SETZ P3, ;RESET P3 SO WE CAN MEASURE THE CONNECT MSG. ;FALL THROUGH TO WRITE THE BODY OF THE CONNECT MESSAGE ;TYP MOVEI T1,NC.CNT ;THIS IS A CONNECT MESSAGE PUSHJ P,BI2EBI ;WRITE THE TYPE ;DLA LDB T1,NETDLA ;GET THE DLA FROM THE DDB PUSHJ P,BI2EBI ;SLA LDB T1,NETSLA ;GET THE SLA FROM THE DDB IFN PARANOID&P$LAT,< SKIPN T1 ;JUST A BIT OF PARANOIA AGAIN STOPCD .,STOP,ANFSLA, ;++ NO SLA ON A CONNECT > PUSHJ P,BI2EBI ;DPN(OBJ,PID) HRRZ T1,P1 ;GET SUBR PUSHJ P,(T1) ;LET CALLER WRITE THE DEST PROCESS DESCRIPTOR ;SPN(OBJ,PID) MOVS T1,P1 ;SWAP THE HALVES HRRZS T1 ;WATCH THAT XADDR STUFF! PUSHJ P,(T1) ;LET THE CALLER WRITE THE SOURCE PROCESS DESC. ;MML LDB T1,NETMML ;GET SERVICE-ROUTINE-SPECIFIED MAX MESSAGE SIZE CAIG T1,0 ;GO WITH IT IF ANYTHING SPECIFIED MOVEI T1,MSGMAD## ;GET OUR LARGEST PERMISSABLE MESSAGE ; (*** SEE FOOTNOTE) PUSHJ P,BI2EBI ;OUTPUT ;FEA(DCM) PUSH P,W ;SAVE THE NDB POINTER FOR A BIT HLRZ W,DEVNET(F) LDB T1,NDTDCM ;GET THE DEVICE MODES POSSIBLE PUSHJ P,BI2EBI ;WRITE ;FEA(,RLN) MOVEI T1,0 ;RECORD LENGTH IS VARIABLE PUSHJ P,BI2EBI ;WRITE ;FEA(,,DVT) LDB T1,NDTDVT ;GET THE DEVICE ATTRIBUTES PUSHJ P,BI2EBI ;WRITE ;FEA(,,,DVU) LDB T1,NETDVU ;GET DEVICE "UNIT" TYPE PUSHJ P,BI2EBI ;WRITE ;FEA(,,,,DVV) LDB T1,NETDVV ;GET DEVICE "CONTROLLER" TYPE PUSHJ P,BI2EBI ;WRITE ;FEA(,,,,,DFT) HRRZ T1,NDBNVR(W) ;GET REMOTE'S NCL VERSION NUMBER JUMPE T1,NCSCN1 ;SKIP DFT FIELD IF OLD NODE MOVE T1,DEVDFT(F) ;GET FORMS TYPE WORD PUSHJ P,SX2EAS ;WRITE IT OUT ;FALL THROUGH TO FIXUP THE LENGTH AND SEND THE MESSAGE NCSCN1: ;CNT (GO BACK AND FILL IT IN) POP P,W ;RESTORE THE NDB POINTER CAILE P3,^D256 ;IS THE MSG LONGER THAN THIS ARBITRARY VALUE STOPCD .,STOP,ANFCIL, ;++ CONNECT INITIATE MESSAGE TOO LONG, ; SOMEONE (TSKSER) SCREWED UP ADDM P3,PCBCTR(U) ;UPDATE THE TOTAL MESSAGE LENGTH POP P,T2 ;GET THE POINTER TO THE TWO BYTE "CNT" BACK LDB T1,[POINT 7,P3,35] ;GET THE LOW 7 BITS TRO T1,200 ; AND MAKE THEM EXTENSIBLE IDPB T1,T2 ;STORE THE FIRST BYTE OF THE COUNT LDB T1,[POINT 7,P3,28] ;GET THE REST OF THE LENGTH IDPB T1,T2 ; AND STORE THAT PUSHJ P,NETWRT ;SEND THE MESSAGE PJRST UPOPJ1## ;GIVE GOOD RETURN ;*** FOOTNOTE *** COMMENT \ Historically, this value was calculated by taking the minimum of all message length's for all nodes in the path between the node we were sending the connect to and ourself. This seems wrong to me. Given a multi-path environment, we may find ourselves using an un-expected path. The result of this is that the sender must calculate the maximum message length based on the current minimum of all nodes in the network. In light of this, my opinion is that the MML field should represent our "local" maximum. It doesn't really matter though since no one uses the field anyway... Matson And I take offense at the above footnote - I tried to believe the silly value in the DDP code in the -87, and look where it got me - into trap service 'cuz the -10 sent some silly number that had no relation with reality. Boo Hiss! Such are the trials and tribulations of life in the small country pits . . . -RDH \ ;NCSDSC ROUTINE TO SEND A DISCONNECT MESSAGE. ;CALL MOVEI F,DDB ; MOVEI T1,"REASON" ; PUSHJ P,NCSDSC ;RETURN CPOPJ ;NO CORE, OR NODE WENT AWAY. ; CPOPJ1 ;DISCONNECT SENT (BUT NOT CONFIRMED!) ; NCSDSC:: ;HERE TO SEND A DISCONNECT MESSAGE NTDBUG ;JUST CHECKING PUSHJ P,SAVE3## ;SAVE THE P'S PUSHJ P,SAVJW## ;WE CLOBBER J(FEK) AND W(NDB) PUSH P,U ;SAVE ANY INPUT MESSAGE WE MAY BE PROCESSING MOVEI P1,(T1) ;SAVE THE REASON IN A SAFE REGISTER IFN PARANOID&P$LAT,< LDB T1,NETSLA ;JUST A QUICK CHECK TO MAKE SURE THAT THE LAT HRRZ T1,NETLAT##(T1) ; AND THE DDB AGREE WITH RESPECT TO THE SLA ;IT'S OK TO ONLY LOOK AT 18 BITS . . . CAIE T1,(F) ;DOES THE SLA POINT BACK TO THE DDB? STOPCD .,STOP,ANFLDD, ;++ LAT AND DDB DISAGREE > HRRZ T1,DEVNET(F) ;GET THE NODE NUMBER PUSHJ P,SRCNDB ;SET UP "W" WITH THE NDB POINTER STOPCD .,STOP,ANFNWA, ;++ NODE WENT AWAY, DRIVER SHOULD HAVE NOTICED PUSHJ P,NCMHDR ;MAKE A CONTROL MESSAGE HEADER. JRST UPOPJ ; IF NO CORE, GIVE AN ERROR RETURN ;TYP MOVEI T1,NC.DSC ;WE ARE A DISCONNECT MESSAGE PUSHJ P,BI2EBI ;SEND THE DISCONNECT TYPE ;DLA LDB T1,NETDLA ;GET THE REMOTE'S ADDRESS FOR THIS CONNECTION IFN PARANOID&P$LAT,< SKIPN T1 ;JUST A LITTLE PARANOIA STOPCD .,STOP,ANFDLA, ;++ NO DLA ON CONNECT > PUSHJ P,BI2EBI ;SEND THE DLA ;SLA LDB T1,NETSLA ;GET OUR ADDRESS FOR THIS CONNECTION PUSHJ P,BI2EBI ;SEND THE SLA ;RSN MOVEI T1,(P1) ;GET THE CALLER SPECIFIED REASON PUSHJ P,BI2EBI ;SEND THE REASON ;CNT JSP T1,NETWRC ;SEND THE MESSAGE PJRST UPOPJ1## ;GIVE A GOOD RETURN ;*** FOOTNOTE *** COMMENT \ Since this routine must be called under the protection of the NETSER interlock, there is no reason that the device driver cannot check to make sure that the node he is attempting to send a disconnect to is still up. The reason for replacing the "UPOPJ" with a STOPCD is to enforce such practices in device drivers \ ;SUBROUTINE NCSNBN - SEND NEIGHBOR NAME MESSAGE ;CALL PUSHJ P,NCSNBN ;RETURN CPOPJ ; CPOPJ1 NCSNBN: ;ENTRY PUSHJ P,SAVE3## ;SAVE THE P'S PUSHJ P,SAVJW## ;SAVE J AND W SETZ F, ;CONTROL MESSAGE PUSHJ P,NCMHDR ;WRITE NUMBERED HEADER (PUSH BYTE PTR ON STK) POPJ P, ;NO CORE MOVEI T1,NC.NBN ;GET HEIGHBOR NAMES PUSHJ P,BI2EBI ;WRITE PUSH P,W ;SAVE THE NDB POINTER MOVEI W,NETNDB## ;GET OUR NDB MOVE T4,[POINT 9,NDBTOP(W)] ;SEARCH THE TOPOLOGY TABLE MOVEI T3,NGHMAX ;GET THE SIZE OF THE NEIGHBORS TABLE NCSNB1: ILDB T1,T4 ;GET THE NODE NUMBER JUMPE T1,[IBP T4 ;IF NO NODE IN THIS SLOT. SKIP USLESS COST JRST NCSNB2] ;AND GO DO NEXT SLOT ;NNM XMT T1 ;SEND THE NODE NUMBER ;LVL ILDB T1,T4 ;GET THE COST XMT T1 ;SEND THE COST NCSNB2: SOJG T3,NCSNB1 ;LOOP OVER ALL NEIGHBORS POP P,W ;RESTORE THE TARGET NODE MOVSI T1,NDB.NB ;GET NEIGHBORS SENT IORM T1,NDBFLG(W) ;SET IT JSP T1,NETWRC ;SEND THE MESSAGE JRST CPOPJ1 ;GIVE GOOD (SKIP) RETURN ;SUBROUTINE SETNBN - SET THE BITS TO SEND NEIGHBORS MESSAGES TO ALL ;CALL PUSHJ P,SETNBN ;RETURN CPOPJ SETNBN: ;ENTRY ON CHANGE OF NEIGHBORS PUSHJ P,SAVJW## ;SAVE J AND W MOVSI T1,NDB.NB ;GET NEIGHBORS BIT MOVEI W,NETNDB ;START OF NDB'S SETNB1: HRRZ W,NDBNNM(W) ;GET THE FIRST NDB LESS OURS JUMPE W,CPOPJ## ;END OF LIST ANDCAM T1,NDBFLG(W) ;CLEAR THE BIT JRST SETNB1 ;CONTINUE ;SUBROUTINE NCSRCF - REQUEST CONFIGURATION MESSAGE ;CALL MOVEI W,NDB ; PUSHJ P,NCSRCF ;RETURN CPOPJ ;NO CORE ; CPOPJ1 ;RETURN NCSRCF: ;ENTRY PUSHJ P,SAVE3## ;SAVE THE P'S IFN PARANOID&P$NDB, ;MAKE SURE WE AREN'T SENDING IT TO US PUSHJ P,SAVJW## ;SAVE J AND W SETZ F, ;CONTROL MESSAGE PUSHJ P,NCMHDR ;WRITE A HEADER (AND PUSH "CNT" POSITION ON STK) POPJ P, ;CAN'T MOVEI T1,NC.RCF ;TYPE REQUEST CONFIGURATION PUSHJ P,BI2EBI ;SEND JSP T1,NETWRC ;SEND THE MESSAGE PJRST CPOPJ1## ;SKIP RETURN ;SUBROUTINE NCSCNF - SEND A CONFIGURATION MESSAGE UPON REQUEST ;CALL MOVEI W,NDB ; PUSHJ P,NCSCNF ;RETURN CPOPJ ; CPOPJ1 NCSCNF: ;ENTERY PUSHJ P,SAVE3## ;SAVE THE P'S IFN PARANOID&P$NDB, ;MAKE SURE WE AREN'T SENDING IT TO THIS NODE PUSHJ P,SAVJW## ;SAVE J W SETZ F, ;CLEAR THE DDB PUSHJ P,NCMHDR ;GET A HEADER (AND PUSH "CNT" POSITION) POPJ P, ;NO CORE AVAILABLE MOVEI W,NETNDB## ;GET OUR NODE DATA BLOCK MOVEI T1,NC.CNF ;CONFIGURATION MESSAGE PUSHJ P,BI2EBI ;SEND ;OBJ MOVSI P1,- ;NUMBER OF DEVICE TYPES NCSCF1: LDB T1,NETCNF##(P1) ;GET THE COUNT JUMPE T1,NCSCF2 ;DON'T HAVE ANY MOVEI T1,(P1) ;GET THE TYPE BACK PUSHJ P,BI2EBI ;SEND THE DEVICE TYPE ;NDV LDB T1,NETCNF##(P1) ;DEVICE COUNT PUSHJ P,BI2EBI ;SEND ;PID MOVEI T1,0 ;ZERO PID PUSHJ P,BI2EBI ;SEND NCSCF2: AOBJN P1,NCSCF1 ;CONTINUE THROUGH THE LIST JSP T1,NETWRC ;SEND THE MESSAGE JRST CPOPJ1## ;GIVE SKIP RETURN ;SUBROUTINE NCSDRQ - SEND A DATA REQUEST ;CALL MOVEI T1,DATA REQUEST COUNT ; MOVEI F,DDB ; PUSHJ P,NCSDRQ ;RETURN CPOPJ ;NO CORE ; CPOPJ1 ;REQUEST SENT NCSDRQ:: ;ENTRY NTDBUG ;VERIFY THE INTERLOCK MOVE S,DEVIOS(F) ;GET THE DEVICE STATUS TLNN S,IOSCON ;ARE WE STILL CONNECTED? JRST CPOPJ1## ;NO, GOOD RETURN, OUTPUT BETTER TRAP ERROR PUSHJ P,SAVE3## ;SAVE THE P'S PUSHJ P,SAVJW## ;SAVE J AND W PUSH P,U ;SAVE U (SELF CONTAINED ROUTINE) MOVEI P1,(T1) ;SAVE THE DRQ COUNT HRRZ T1,DEVNET(F) ;GET THE NODE NUMBER PUSHJ P,SRCNDB ;SET UP W FROM THE NODE NUMBER SKIPA ;NODE WENT AWAY. GIVE ERROR RETURN PUSHJ P,NCMHDR ;WRITE THE HEADER (AND PUSH "CNT" POINTER) JRST UPOPJ## ;RESTORE U AND GIVE ERROR RETURN MOVEI T1,NC.DQR ;DATA REQUEST PUSHJ P,BI2EBI ;WRITE ;DLA LDB T1,NETDLA ;GET THE DESTINATION LINK ADDRESS IFN PARANOID&P$LAT,< SKIPN T1 ;JUST A BIT OF PARANOIA AGAIN STOPCD .,STOP,ANFDRZ, ;++ SENDING DRQ'S TO DEVICE ZERO > PUSHJ P,BI2EBI ;WRITE ;DQR MOVEI T1,(P1) ;GET THE DQR PUSHJ P,BI2EBI ;WRITE ;CNT JSP T1,NETWRC ;SEND THE MESSAGE (POP "CNT" POINTER OFF STK) PJRST UPOPJ1## ;SKIP EXIT SUBTTL MEMORY CONTROL ROUTINES ;SUBROUTINE GETZWD - GET A BLOCK OF MONITOR FREE CORE AND ZERO ;CALL MOVEI T2,#WORDS ; PUSHJ P,GETZWD ;RETURN CPOPJ ;NO CORE AVAILABLE ; CPOPJ1 ;T1=ADDRESS GETZWD:: IFN PARANOID&P$COR,< ;KEEP A LINKED LIST OF ALL NETWORK CORE ADDI T2,3 ;WE USE 3 EXTRA WORDS IF WE ARE PARANOID > PUSH P,T2 ;SAVE THE NUMBER OF WORDS HLRE T1,FREPTR## ;GET LENGTH OF FREE CORE MAP MOVNS T1 IMULI T1,^D36 ;MAKE #4 WORD BLOCKS (=1/4 FREE CORE TOTAL) JFCL ; CAML T1,%NTCOR ;DON'T USE MORE THAN 1/4 OF FREE CORE PUSHJ P,GETWDS## ;ALOCATE THE SPACE PJRST T2POPJ## ;NO, SPACE AVAILABLE SETZM (T1) ;CLEAR THE FIRST WORD MOVE T2,(P) ;GET THE NUMBER OF WORDS ADDM T2,%NTCOR ;ACCOUNT FOR CORE USED MOVEM T1,(P) ;SAVE THE STARTING ADDRESS ADDI T2,-1(T1) ;POINT TO THE END OF THE LIST HRLI T1,1(T1) ;MAKE A BLT POINTER MOVSS T1 ;FROM,,TO BLT T1,(T2) ;CLEAR THE BLOCK MOVE T1,%NTCOR ;IF WE NOW ARE USING MORE CAMLE T1,%NTMAX ; CORE THAN EVER BEFORE MOVEM T1,%NTMAX ; RECORD FOR PRYING EYES IFN PARANOID&P$COR,< ;NOW LINK IN THE BLOCK AND SET LAST WORD MOVE T1,[SIXBIT /NETMEM/] ;"UNIQUE" PATTERN FOR END WORD MOVEM T1,(T2) ;SO WE CAN SEE IF USER WROTE TOO FAR. MOVE T1,(P) ;GET ADDRESS OF START OF THE BLOCK NETOFF ;WE NEED PROTECTION WHILE HACKING LISTS MOVE T2,%NTMEM ;GET POINTER TO FIRST BLOCK IN THE CHAIN HRLI T2,%NTMEM ;MAKE OUR BACK LINK POINT TO "%NTMEM" MOVEM T2,(T1) ;STORE DOUBLE LINK IN FIRST WORD ANDI T2,-1 ;STRIP OFF IME-INDUCING LH GARBAGE MOVEM T1,%NTMEM ;MAKE THE LIST START WITH US HRLM T1,(T2) ;FIX UP BACKWARD LINK IN NEXT BLOCK NETON ;ALL'S CLEAR MOVE T2,-1(P) ;GET THE CALLER'S PC HRLI T2,'NET' ;GET A "UNIQUE" 18 BIT STRING MOVEM T2,1(T1) ;AND PUT "XWD STRING,PC" IN SECOND WORD ADDI T1,2 ;MAKE POINTER POINT TO THE THIRD WORD MOVEM T1,(P) ;AND GIVE THAT TO THE USER > PJRST TPOPJ1## ;RESTORE THE ADDRESS AND EXIT ;SUBROUTINE GIVZWD - RETURN MONITOR FREE CORE ;CALL MOVEI T1,#WORDS ; MOVEI T2,ADDRESS ; PUSHJ P,GIVZWD ;RETURN CPOPJ GIVZWD:: IFN PARANOID&P$COR,< ;CHECK FOR ILLEGAL MEMORY USAGE ADDI T1,3 ;ACCOUNT FOR CHECK WORDS SUBI T2,2 ;ADJUST POINTER TO BEGINNING OF BLOCK > ;END IFN PARANOID&P$COR PUSH P,T1 ;SAVE THE LENGTH IFN PARANOID&P$COR,< ;CHECK FOR ILLEGAL MEMORY USAGE PUSH P,T2 ;SAVE THE ADDRESS NETOFF ;PROTECTION WHILE HACKING LISTS MOVE T1,(T2) ;GET THE "XWD BACKWARD,FORWARD" LINK WORD HRRZ T3,T1 ;JUST THE ADDRESS FOR IME-LESS INDEXING HLLM T1,(T3) ;SPLICE OUT ONE POINTER MOVS T1,T1 ;SWAP HALVES HRRZ T3,T1 ;JUST THE ADDRESS FOR IME-LESS INDEXING HLRM T1,(T3) ;SPLICE OUT THE OTHER POINTER NETON ;ALL IS CLEAR. WE ARE SPLICED OUT HLRZ T1,1(T2) ;GET THE LEFT OF THE SECOND CHECK WORD CAIE T1,'NET' ;MAKE SURE IT WASN'T CLOBBERED STOPCD .,STOP,ANFWMB, ;++ USER WROTE IN MEMORY BEFORE BLOCK ADD T2,-1(P) ;GET A POINTER TO ONE PAST THE END OF THE BLOCK EXCH T2,-1(T2) ;GET THE LAST CHECK WORD (AND MAKE IT GARBAGE) CAME T2,[SIXBIT /NETMEM/] ;VERIFY THAT HE DIDN'T CLOBBER IT STOPCD .,STOP,ANFWPE, ;++ USER WROTE PAST THE END OF THE BLOCK POP P,T2 ;GET THE ADDRESS BACK > ;END IFN PARANOID&P$COR MOVN T1,0(P) ;"SUB'ABLE" LENGTH ADDB T1,%NTCOR ;REDUCE CORE USED SKIPGE T1 ;CHECK RESULT STOPCD TPOPJ##,DEBUG,CWN,;++CORE ALLOCATION WENT NEGATIVE POP P,T1 ;RESTORE WORD COUNT PJRST GIVWDS## ;RETURN THE CORE ;SUBROUTINE SVEVM - SAVE THE JOB EVM AND RESTORE ON POPJ ;CALL PUSHJ P,SVEVM ;RETURN CPOPJ SVEVM:: JUMPE F,CPOPJ## ;NO EVM IF NO DDB SKIPN DEVEVM(F) ;ANY EVM POPJ P, ;NO RETURN PUSHJ P,RTEVM## ;YES, RETURN THE EVM POP P,(P) ;PUT THE RETURN ON THE END OF THE STACK PUSHJ P,@1(P) ;RETURN TO THE CALLER PJRST RSTEVM## ;NON-SKIP RESTORE EVM AOS (P) ;SKIP RETURN PJRST RSTEVM## ;RESTORE EVM AND EXIT SUBTTL COMMON SUBROUTINES TO CONVERT EXTENSIBLE ASCII/BINARY ;SUBROUTINE EAS2SX CONVERT EXTENSIBLE ASCII TO SIXBIT ;CALL MOVEI P1,[INPUT POINTER] ; PUSHJ P,EAS2SX ;RETURN CPOPJ ;T1=SIXBIT EAS2SX::MOVE T2,[POINT 6,T1] ;GET BYTE POINTER SETZB T1,T4 ;CLEAR OUTPUT EAS2S1: SOJL P4,CPOPJ## ;EXIT IF NO MORE DATA ILDB T3,P1 ;GET THE BYTE TRZN T3,200 ;CONTINUE BIT SETO T4, ;NO, SET FLAG TRNE T3,140 ;MAKE SURE ASCII BEFOR SUBI SUBI T3,40 ;CONVERT TO SIXBIT TRNN T3,177 ;CHECK FOR A BLANK CHARACTER JUMPE T1,EAS2S2 ;AND A LEADING BLANK CAIE T3,'[' ;CHECK TO [XXX,XXX] CAIN T3,']' ;AND EXIT POPJ P, ;IF FOUND CAIN T3,',' ;ALSO A COMMA POPJ P, ;WILL EXIT TLNE T2,(77B5) ;END OF WORD IDPB T3,T2 ;STORE WORD EAS2S2: JUMPGE T4,EAS2S1 ;NO CONTINUE POPJ P, ;RETURN ;SUBROUTINE SX2EAS CONVERT SIXBIT TO EXTENSIBLE ASCII ;CALL MOVE T1,[SIXBIT /.../] ; MOVE P2,[OUTPUT BYTE POINTER] ; PUSHJ P,SX2EAS ;RETURN CPOPJ ;P3 COUNTED UP SX2EAS::SKIPE T2,T1 ;COPY THE SIXBIT NAME SX2EA1: SETZ T1, ;CLEAR THE OUTPUT AC LSHC T1,6 ;GET A SIXBIT CHARACTER ADDI T1,240 ;CONVERT TO ASCII WITH CONTINUE BIT IDPB T1,P2 ;STORE CHARACTER ADDI P3,1 ;COUNT THE CHARACTER JUMPE T2,CLRBT8 ;EXIT AND CLEAR CONTINUE BIT JRST SX2EA1 ;COUNT AND CONTINUE ;SUBROUTINE AS2EAS CONVERT ASCIZ TO EXTENSIBLE ASCII ;CALL MOVE P1,[INPUT BYTE POINTER] ASCII 7 ; MOVE P2,[OUTPUT BYTE POINTER] EXTENSIBLE ASCII 8 ; PUSHJ P,AS2EAS ;RETURN CPOPJ ;P3 UPDATED WITH CHARACTER COUNT AS2EAZ: JUMPE P1,CPOPJ## ;DO NOTHING IF NO POINTER AS2EAS::TLNN P1,-1 ;IS THERE A BYTE POINTER HRLI P1,(POINT 7) ;NO, SUPPLY ONE AS2EA1: ILDB T1,P1 ;GET AN ASCII CHARACTER JUMPE T1,CLRBT8 ;JUMPE IF END TRO T1,200 ;SET HIGH ORDER BIT IDPB T1,P2 ;STORE 8 BIT BYTE AOJA P3,AS2EA1 ;COUNT CHARACTER TRY AGAIN CLRBT8::LDB T1,P2 ;GET LAST STORED CHARACTER BACK TRZ T1,200 ;CLEAR THE CONTINUE BIT DPB T1,P2 ;STORE CHARACTER POPJ P, ;RETURN ;SUBROUTINE EAS2AZ SEE IF ANYTHING FOR EAS2AS TO CONVERT ;CALL MOVE P1,[INTPUT BYTE POINTER] ; PUSHJ P,EAS2AZ ;RETURN 1 NOTHING TO COPY ;RETURN 2 SOMETHING FOR EAS2AS TO DO ;USES T1 & T2 EAS2AZ: JUMPE P4,CPOPJ## ;RETURN IF NOTHING TO COPY MOVE T1,P1 ;COPY BYTE POINTER ILDB T2,T1 ;EXAMINE FIRST CHARACTER JUMPN T2,CPOPJ1## ;SKIP IF SOMETHING TO COPY MOVE P1,T1 ;NO, COPY INCREMENTED BYTE POINTER SOJA P4,CPOPJ## ;ACCOUNT FOR CHARACTER AND GIVE 'NONE' RETURN ;SUBROUTINE EAS2AS CONVERT AN EXTENSIBLE ASCII STRING TO ASCIZ ;CALL MOVE P1,[INPUT BYTE POINTER] ; MOVE P2,[OUTPUT BYTE POINTER] ; PUSHJ P,EAS2AS ;EXIT EAS2AS::HRLI P2,(POINT 7) ;MAKE A BYTE POINTER MOVEI T2,^D37 ;ALLOW A MAX CHARACTER COUNT EAS2A1: SOJL P4,CPOPJ## ;EXIT IF NO MORE ILDB T1,P1 ;GET AN 8 BIT CHARACTER IDPB T1,P2 ;STORE A SEVEN BIT CHARACTER TRNE T1,200 ;IS CONTINUE BIT ON SOJG T2,EAS2A1 ;YES, CONTINUE SETZ T1, ;SET UP A NULL IDPB T1,P2 ;STORE THE NULL POPJ P, ;RETURN ;SUBROUTINE BI2EBI CONVERT A BINARY NUMBER TO EXTENSIBLE BINARY ;CALL MOVE T1,[A BINARY NUMBER] ; MOVE P2,[OUTPUT BYTE POINTER] 8 BIT ; PUSHJ P,BI2EBI,OCT2EBI,DE2EBI ;RETURN CPOPJ ;P3 UPDATED BI2EBI::CAIG T1,177 ;GREATER THAN 177 JRST DPBBIN ;NO OUTPUT LSHC T1,-7 ;SHIFT OFF THE BITS ROT T2,7 ;SAVE IN T2 TRO T2,200 ;SET CONTINUE BIT IDPB T2,P2 ;STORE IN MESSAGE AOJA P3,BI2EBI ;CONTINUE ;SUBROUTINE EBI2BI TO CONVERT EXTENSIBLE BINARY TO BINARY ;CALL MOVE P1,[INPUT BYTE POINTER ; PUSHJ P,EBI2BI ;RETURN CPOPJ ;T1=BINARY NUMBER EBI2BI::SETZB T1,T2 ;INITIALIZE FOR NUMBER-BUILDING LOOP EBI2B1: SOJL P4,CPOPJ## ;EXIT IF THE END OF DATA ILDB T3,P1 ;GET THE NEXT CHARACTER TRZN T3,200 ;IS THE NUMBER EXTENDED JRST EBI2B5 ;NO, ACCUMLATE END OF NUMBER AND EXIT LSH T3,(T2) ;POSITION NEXT "DIGIT" IOR T1,T3 ;AND ACCUMULATE NUMBER ADDI T2,7 ;"ADVANCE" ACCUMULATOR-POSITIONER JRST EBI2B1 ;CONTINUE EBI2B5: LSH T3,(T2) ;POSITION NEXT "DIGIT" IOR T1,T3 ;AND ACCUMLATE NUMBER POPJ P, ;RETURN WITH BINARY IN T1 ;SUBROUTINE BYT2BI READ ONE BYTE FROM THE STREAM ;CALL JUST LIKE THE REST ;RETURN CPOPJ ;T1 = THE 8 BIT BYTE BYT2BI::SOJL P4,CPOPJ## ;EXIT IF END OF MESSAGE ILDB T1,P1 ;GET THE BYTE POPJ P, ; AND RETURN ;SUBROUTINE XX2EAS CONVERT NUMBER TO EXTENSIBLE ASCII ;CALL P2/ OUTPUT BYTE POINTER P3/ COUNTER T1/ NUMBER ; PUSHJ P,OC2EAS/DC2EAS/RX2EAS ;RETURN CPOPJ WITH P2/P3 UPDATED DC2EAS: SKIPA T3,[^D10] ;DECIMAL CONVERSION OC2EAS::MOVEI T3,^D8 ;OCTAL CONVERSION RX2EAS: IDIVI T1,(T3) ;SEPERATE AGAIN PUSH P,T2 ;STORE THE BYTE SKIPE T1 ;ANY LEFT NOW PUSHJ P,RX2EAS ;YES, TRY AGAIN POP P,T1 ;NO, GET THE LAST DIGIT BACK ADDI T1,"0" ;CONVERT TO ASCII DPBEAS: TRO T1,200 ;SET THE CONTINUE BIT DPBBIN::ADDI P3,1 ;COUNT IDPB T1,P2 ;NO STORE THE DIGIT POPJ P, ;RETURN ;SUBROUTINE EAS2 - CONVERT ASCII TO OCTAL/DECIMAL/RADIX ;CALL P1, P4:= POINTER TO INPUT ; PUSHJ P,EAS20C/EAS2DC/2ASRX ;RETURN CPOPJ; T1:= NUMBER, T2:= TERMINATING CHARACTER EAS2DC::SKIPA T3,[^D10] ;DECIMAL RADIX EAS2OC::MOVEI T3,^D8 ;OCTAL RADIX EAS2RX::SETZB T1,T4 ;ARBITRARY RADIX IN T3 EAS2NN::SOJL P4,CPOPJ## ;COUNT DOWN INPUT ILDB T2,P1 ;NEXT INPUT CHARACTER TRZN T2,200 ;END OF FIELD SETO T4, ;YES CAIL T2,"0" ;VALID ? CAILE T2,"0"(T3) ; DIGIT ? POPJ P, ;NO, END OF NUMBER IMULI T1,(T3) ;ROOM FOR NEXT ADDI T1,-"0"(T2) ;ADD UP NUMBER JUMPE T4,EAS2NN ;LOOP FOR WHOLE NUMBER POPJ P, ;OR UNITL END OF EXTENSIBLE ASCII ;XSKIP ROUTINE TO SKIP OVER AN EXTENSIBLE FIELD ;CALL P1, P4 := POINT TO FIELD ; PUSHJ P,XSKIP ;RETURN CPOPJ ;ALWAYS ; XSKIP:: SOJL P4,CPOPJ## ;COUNT OFF THIS BYTE ILDB T1,P1 ;GET THE BYTE TRNE T1,200 ;EXTENSIBLE JRST XSKIP ;YES. KEEP LOOKING FOR THE END POPJ P, ; NO. ALL DONE ;SKIP1 ROUTINE TO SKIP OVER A 1 BYTE FIELD ;CALL P1, P4 := POINT TO BYTE TO SKIP ; PUSHJ P,SKIP1 ;RETURN CPOPJ ; SKIP1:: SOJL P4,CPOPJ## ;COUNT DOWN THE BYTE IBP P1 ;INCREMENT THE BYTE POINTER POPJ P, ;ALL DONE ;SUBROUTINE PP2EAS - OUTPUT A PPN IN EXTENSIVE ASCII ;CALL MOVE T1,[PPN] ; PUSHJ P,PP2EAS ;RETURN CPOPJ PP2EAS::PUSH P,T1 ;SAVE THE PPN MOVEI T1,"[" ;OPEN BRACKET PUSHJ P,DPBEAS ;OUTPUT HLRZ T1,(P) ;GET THE PROGRAMMER NUMBER PUSHJ P,OC2EAS ;OUTPUT MOVEI T1,"," ;SEPERATOR PUSHJ P,DPBEAS ;OUTPUT POP P,T1 ;RESTORE THE STACK GET PROGRAMMER # HRRZS T1 ;RT HALF PUSHJ P,OC2EAS ;OUTPUT MOVEI T1,"]" ;CLOSING BRACKET PUSHJ P,DPBEAS ;OUTPUT PJRST CLRBT8 ;CLEAR THE LAST BIT ;SUBROUTINE EAS2PP - INPUT A PROCESS ANEM AND UIC ;CALL PUSHJ P,EAS2PP ;RETURN CPOPJ EAS2PP:: ;ENTRY PUSHJ P,EAS2SX ;GET THE SIXBIT NAME PUSH P,T1 ;SAVE SETZ T2, ;CLEAR THE PPN WORD CAIE T3,'[' ;DOES A PPN FOLLOW PJRST TPOPJ## ;NO EXIT T1=NAME T2=PPN PUSHJ P,EAS2OC ;GET THE PROJECT NUMBER TLNE T1,-1 ;LESS THAN 7 DIGITS ? SETZ T1, ;ILLEGAL SET TO ZERO PUSH P,T1 ;SAVE THE PROJECT NUMBER PUSHJ P,EAS2OC ;GET THE PROGRAMMER NUMBER TLNE T1,-1 ;LESS THAN 7 DIGITS ? SETZ T1, ;ILLEGAL HRL T1,(P) ;GET THE PROGRAMMER NUMBER BACK MOVE T2,T1 ;COPY TO T2 POP P,(P) ;REMOVE SCRATCH FROM THE STACK PJRST TPOPJ## ;EXIT T1=NAME T2=PPN SUBTTL NTFSEC - ONCE A SECOND CODE ;CALLED ONCE A SECOND WITH J SET FOR EACH FEK ; NTFSEC::PUSHJ P,SAVJW## ;SAVE J AND W ETC SKIPN T1,FEKBJN(J) ;SEE IF SOME NODE OWNS THE BOOT JRST NTFSE1 ; IF NOT BOOT-STRAPING, GO CHECK REST OF FEK HLRZ T1,T1 ;GET THE TIMER VALUE SOJLE T1,[HRRZ T1,FEKBJN(J) ;IF TIMER RAN OUT, GET JOB NUMBER PUSHJ P,EWAKE## ;WAKE THE USER SETZM FEKBJN(J) ;RELINQUISH OWNERSHIP OF STC JRST NTFSE1] ;AND GO CHECK FOR STALE MESSAGES HRLM T1,FEKBJN(J) ;STORE THE NEW TIMER VALUE NTFSE1: SKIPN T1,FEKICT(J) ;SEE IF THERE ARE ANY INPUT MSGS QUEUED JRST NTFSE2 ; IF NO INPUT STC MSGS, CHECK REST OF FEK HLRZ T1,T1 ;GET JUST THE TIMER VALUE SOJLE T1,[PUSH P,J ;IF THE MESSAGE TIMES OUT, SAVE FEK HRRZ J,FEKICT(J) ;GET J := STC MESSAGE PUSHJ P,GIVSTC ;FREE THE STC MESSAGE POP P,J ;GET THE FEK ADDRESS BACK SETZM FEKICT(J) ;CLEAR THE (NOW FREED) STC MSG POINTER JRST NTFSE2] ;AND CONTINUE WITH OTHER CHECKING HRLM T1,FEKICT(J) ;STORE THE UPDATED COUNTER NTFSE2: HLLZ S,FEKBLK(J) ;SET UP THE FEK'S FLAGS JUMPGE S,CPOPJ ;IF IT'S NOT ONLINE, DON'T CHECK ANY FARTHER TLNN S,FK.NID ;NODE ID SENT PUSHJ P,NCSNID ;NO, SEND IT JFCL ;ERROR NO CORE (SEND NEXT TIME) SKIPG FEKIAD(J) ;IS THERE AN INPUT PCB? PUSHJ P,NETRDD ;IF NO INPUT PCB, TRY TO SET UP READ REQUEST SKIPG FEKOCT(J) ;IS THERE AN OUTPUT PCB BEING SERVICED? POPJ P, ; IF NOT, THEN DON'T DO HUNG CHECK. AOS T1,FEKHTM(J) ;INCREMENT THE TIME THAT THIS PCB HAS TAKEN. CAIG T1,^D5 ;HAS THIS PCB TAKEN MORE THAN 5 SECONDS? POPJ P, ; IF NOT, THEN IT'S POSSIBLY ALL RIGHT ;HERE IF FEK HAS TIMED OUT. (IE. MORE THAN 5 SECONDS TO SEND LAST MESSAGE) MOVEI T1,FF.CRS ;ASK THIS FEK TO "CRASH" ITSELF XCT FEKDSP(J) ;CALL THE FRONT-END SERVICE ROUTINE POPJ P, ;AND RETURN ;NETSEC THIS ROUTINE IS CALLED ONCE A SECOND FROM CLOCK1 NETSEC:: IFN FTMP, ;ONLY DO SECOND STUFF ON THE BOOT CPU SETOM NTQSEC ;SIGNAL THE SECOND POPJ P, ;LET JIFFY CODE CALL NETSCN SUBTTL NETCTC - CALL ON RESET OR ^C^C AND NOT CCON/CON NETCTC:: ;CALLED BY UUOCON NETDBJ ;GET THE INTERLOCK TO DO THIS. PUSHJ P,SAVJW## ;SAVE J AND W ;CHECK FOR REMOTE DIALER IN USE CAMN J,DIALJB ;DOES THIS JOB HAVE THE [NETWORK] DIALER? SETZM DIALDB ;YES, CLEAR THE DIALER ;CLEAR THE STATION CONTROL DEVICE IF THERE MOVEI W,NETNDB## ;GET THE START ON THE NDB CHAIN NETCT1: SKIPN T1,NDBSTC(W) ;IS STATION CONTROL BUSY JRST NETCT2 ;NO, CONTINUE HRRZS T1 ;GET THE JOB NUMBER FOR COMPARE CAIN T1,(J) ;DOES THIS JOB HAVE THE DEVICE SETZM NDBSTC(W) ;YES, CLEAR THE DEVICE NETCT2: HRRZ W,NDBNNM(W) ;GET THE NEXT STATION POINTER JUMPN W,NETCT1 ;CONTINUE UNLESS THE END OF NDB ;HERE TO CHECK FOR STATION CONTROL IN USE ON ANY FEKS IFN FTKS10,< ;ONLY THE KS-10 HAS SMART FEKS... MOVEI W,FEKFST## ;GET THE ADDRESS OF THE FIRST FEK CAIA ;SKIP INTO THE LOOP NETCT3: HRRZ W,FEKBLK(W) ;ADVANCE TO THE NEXT FEK JUMPE W,NETCT4 ;IF NO MORE FEKS, GO CHECK FOR OTHER THINGS SKIPN T1,FEKBJN(W) ;SEE IF THIS FEK'S STC IS IN USE JRST NETCT3 ;IF NO ONE IS USING IT, GO CHECK NEXT FEK HRRZ T1,T1 ;CLEAR OUT THE TIMER VALUE CAIN T1,(J) ;ARE WE THE ONE USING THE STC? SETZM FEKBJN(W) ; IF WE'RE USING IT, RELEASE IT JRST NETCT3 ;GO CHECK THE REST... >;END IFN FTKS10 ;HERE TO CHECK FOR PENDING CONNECT/DISCONNECT MESSAGES NETCT4: PUSHJ P,FNDPDS## ;FIND THE PDB FOR THE JOB PUSH P,F ;SAVE THE DDB POINTER HLRZ F,.PDNET##(W) ;GET THE POSSIBLE DDB POINTER JUMPE F,NETCT5 ;NONE HRRZS .PDNET##(W) ;CLEAR THE POINTER MOVE T1,DEVMOD(F) ;GET DEVICE'S DV??? FLAGS TRNE T1,ASSPRG ;IS DEVICE OPEN/INITED? JRST NETCT5 ;YES, RELEASE SHOULD GET IT LATER TLNE T1,DVTTY ;IS DEVICE A TTY? JRST [PUSHJ P,TTYKIL## ;YES, THEN USE A DIFFERENT JRST NETCT5] ; ROUTINE TO FREE THE DDB MOVE S,DEVIOS(F) ;IF THE DEVICE NEVER GOT CONNECTED TLNN S,IOSCON ;OR IS BEING DISCONNECTED, JRST [MOVSI S,IOSZAP ;THEN SET IOSZAP. THIS CAUSES ALL IORB S,DEVIOS(F) ;RIGHT THINGS TO HAPPEN AS MSGS COME IN JRST NETCT5] ;RETURN TO MAIN LINE PUSHJ P,ZAPNE0 ;IF CONNECTED, SEND A DISCONNECT NETCT5: JRST FPOPJ## ;RESTORE F AND EXIT ;NDBCHK ROUTINE TO VERIFY THAT W DOES NOT POINT AT NETNDB. (THIS ; ROUTINE CHECKS TO MAKE SURE THAT WE AREN'T TALKING TO OURSELF.) ;CALL PUSHJ P,NDBCHK ;RETURN CPOPJ ;OR STOPCD ; NDBCHK: PUSH P,T1 ;SAVE THIS FOR A BIT MOVEI T1,NETNDB## ;GET THE ADDRESS OF NETNDB CAIN T1,(W) ;MAKE SURE RH(W) DOESN'T POINT AT IT STOPCD .,STOP,ANFTLK, ;++ TALKING TO OURSELVES PJRST TPOPJ ;RESTORE T1 AND RETURN ;FRCNLD ROUTINE TO START UP NETLDR ;CALL PUSHJ P,FRCNLD ;RETURN CPOPJ ;STARTS NETLDR ONLY IF NOT DEBUGGING FRCNLD: MOVE T1,STATES## ;BEFORE WE START UP NETLDR, MAKE SURE TRNN T1,ST.DDL ; OPR SAYS IT'S OK. IF NO NETLDR, SKIPGE DEBUGF ; OR WE'RE DEBUGGING THIS MESS POPJ P, ;DON'T RUN NETLDR PUSH P,U ;FIRST WE NEED AN LDB, SO SAVE POSSIBLE PCB PUSH P,W ;WE ALSO MUST SET UP "W" FOR GETLDB SETZ T1, ;SIGNIFY THAT THERE'S NO REMOTE INVOLVED MOVEI T3,ERRDSP## ;SET UP NULL ISR DISPATCH PUSHJ P,GETLDB## ;GET A FREE LDB JRST FRCNL1 ;IF NO FREE LDB'S JUST IGNORE THE REQUEST MOVEI T1,M.AIDL## ;ANF-10 IDLE MAX PUSHJ P,SCNADT## ;START TIMER LOGIC ON LINE MOVEI T1,TTFCXL## ;GET THE ".NETLD" FORCE COMMAND INDEX PUSHJ P,TTFORC## ;FORCE IT ON THE TERMINAL PUSHJ P,FRELDB## ;*** KROCK *** MUST GET THIS ONTO FRCLIN *** FRCNL1: POP P,W ;RESTORE ANY NDB POINTER PJRST UPOPJ## ;RETURN. NETLDR WILL START UP SOON. ;NETWRC ROUTINE TO FINISH OFF A MESSAGE STARTED BY NCMHDR & FRIENDS ;CALL P3 := "CNT" OF THE CONTROL MESSAGE ; (P) := A BYTE POINTER TO THE "CNT" FIELD IN THE MESSAGE ; T1 := RETURN ADDRESS ;RETURN JRST (T1) ;VIA A CPOPJ FROM NETWRT ; NETWRC:: ;HERE TO SEND A CONTROL MESSAGE EXCH T1,(P) ;GET THE POINTER TO "CNT" (SAVE RETURN ADDR) DPB P3,T1 ;STORE THE COUNT ADDM P3,PCBCTR(U) ;UPDATE THE PCB'S LENGTH PJRST NETWRT ;SEND THE MESSAGE SUBTTL NETWRT - SEND A MESSAGE TO THE FRONT END KONTROLLER FEK ;SUBROUTINE NETWRT - SEND A MESSAGE ;CALL MOVEI U,PCB ; PUSHJ P,NETWRT ;CALLED AT INTERRUPT OR UUO LEVEL ;RETURN CPOPJ ;ALWAYS NETWSR: AOS (P) ;SKIP RETURN NETWRT:: ;ENTRY NTDBUG ;VERIFY THE INTERLOCK PUSHJ P,SAVJW## ;SAVE J/W HRRZ T1,PCBNNM(U) ;GET THE NODE NUMBER PUSHJ P,SRCNDB ;SET UP W FROM THE NODE NUMBER S0JRST RMVPCB ;NODE WENT AWAY, DELETE PCB HRRZ J,NDBFEK(W) ;GET THE FEK POINTER MOVE T4,PCBPTR(U) ;GET ADDRESS OF MESSAGE HRLI T4,(POINT 8,0,7);POINT TO NCT LDB T1,T4 ; AND GET IT ANDI T1,NCT.TP ;ISOLATE MESSAGE TYPE AOS NCLXTP(T1) ;COUNT IT JUMPN T1,NETWR2 ;WE'RE DONE IF THIS IS UNNUMBERED CONTROL ADD T4,[-] ;POINT TO DLA LDB T1,T4 ; AND GET IT JUMPN T1,NETWR1 ;NON-ZERO MEANS DATA MESSAGE ILDB T1,T4 ;GET THE COUNT BYTE TRNE T1,200 ; AND IF IT'S EXTENSIBLE JRST .-2 ; KEEP READING ILDB T1,T4 ;GET NUMBERED MESSAGE TYPE CAIG T1,NC.MAX ;IN RANGE? AOS NCLXMT(T1) ;YES, COUNT IT JRST NETWR2 ;ALL DONE, OKAY TO SEND NETWR1: MOVE T1,PCBCTR(U) ;GET LENGTH OF ENTIRE MESSAGE ADD T1,PCBCT2(U) ;INCLUDING SECONDARY (USER) DATA, IF ANY SUBI T1,6 ;DISCARD PROTOCOL OVERHEAD CAIGE T1,1_ ; IN RANGE OF TABLE? JFFO T1,.+2 ;YES, GET APPROPRIATE RANGE TDZA T1,T1 ;OUT OF RANGE, INCREMENT ENTRY 0 MOVNI T1,-^D36(T2) ;MAKE ASCENDING ENTRIES MEAN LONGER LENGTHS AOS NCLXDL(T1) ; AND RECORD IT NETWR2: NETOFF ;TURN OF THE PI SYS ;ASSIGN MESAGE NUMBERS HERE ;NCA MOVE T4,PCBPTR(U) ;GET THE OUTPUT POINTER HRLI T4,(POINT 8,0,31) ;POINT TO THE NCA/NCN FIELDS LDB T1,NDBLMP ;GET THE LAST MESSAGE PROCESSED DPB T1,NDBLAS ;SAVE AS LAST ACK DPB T1,T4 ;STORE THE ACK NUMBER ; MOVSI T1,NDB.XA ;GET AND CLEAR THE "NEED TO ACK" FLAG ; ANDCAB T1,NDBFLG(W) ; TO SIGNIFY ACK WENT THIS JIFFY ;NCN LDB T1,NDBLMA ;GET THE LAST MESSAGE NUMBER ASSIGNED SKIPGE PCBBLK(U) ;NUMBERED MESSAGE ADDI T1,1 ;YES, UPATE THE COUNT DPB T1,NDBLMA ;STORE AS LAST ASSIGNED IDPB T1,T4 ;STORE THIS MESSAGE NUMBER DPB T1,PCBMSN ;SAVE THE MESSAGE NUMBER OF THIS PCB IFN FTKL10, ;UPDATE THE CACHE SWEEP SERIAL IN THE PCB ;DROP THRU INTO FRCWRT ;DROP THRU FROM NETWRT (WITH NETOFF) FRCWRT: ;PCBMRK MUST HAVE BEEN CALLED IFN PARANOID&P$PCB,< ;IF CHECKING PCB'S MOVE T1,[SIXBIT /PCBTAG/] ;GET THE PCB IDENTIFIER, CAME T1,PCBTAG(U) ; AND MAKE SURE WE HAVE A PCB. STOPCD .,STOP,ANFPCV, ;++ PCB NOT VALID > HLLZS PCBBLK(U) ;MAKE SURE WE ONLY GOT 1 PCB SETZ T3, ;CLEAR "CHECK" COUNTER MOVEI T1,FEKOAD-PCBBLK(J) ;GET ADDRESS OF OUTPUT LIST CAIA ;SKIP INTO LOOP FRCWR1: MOVEI T1,(T2) ;COPY THE PCB ADDRESS HRRZ T2,PCBBLK(T1) ;GET ADDRESS OF "NEXT" PCB SKIPE T2 ;IF NO NEXT PCB, SKIP OUT OF LOOP AOJA T3,FRCWR1 ;COUNT THE PCB AND KEEP LOOPING CAME T3,FEKOCT(J) ;MAKE SURE THAT THE COUNT IS RIGHT STOPCD .,STOP,ANFFEK, ;++ FEK BAD, FEKOAD AND FEKOCT IN CONFLICT HRRM U,PCBBLK(T1) ;QUEUE THIS PCB ON THE "END" OF THE LIST AOS T3,FEKOCT(J) ;COUNT THIS PCB. NETON ;QUEUES ARE CONSISTENT. INTS OK NOW MOVEI T1,FF.WRT ;GET "FEKWRT" FUNCTION CODE AND CALL KONTROLLER XCT FEKDSP(J) ; TO SET UP A WRITE REQUEST POPJ P, ;RETURN WITH MESSAGE ON ITS WAY ;ENQPCB THIS ROUTINE IS USED TO INSERT A PCB INTO A QUEUE SUCH ; AS NDBQUE. THE QUEUES ARE MAINTAINED WITH ALL UN-NUMBERED ; MESSAGES FIRST FOLLOWED BY ALL NUMBERED MESSAGES IN ASCENDING ; ORDER. ;CALL T1 := BYTE POINTER TO BEGINNING OF QUEUE ; U := ADDR OF PCB TO ENQUEUE ;RETURN CPOPJ ENQPCB: LDB T2,T1 ;GET ADDRESS OF FIRST PCB ON LIST JUMPE T2,ENQPC3 ;IF EMPTY, THEN PUT THIS FIRST MOVSI T3,PCB.NM ;GET THE "THIS IS A NUMBERED MESSAGE" FLAG TDNE T3,PCBBLK(U) ;IF THIS MSG IS NUMBERED, JRST ENQPC2 ; THEN USE DIFFERENT ROUTINE TDNE T3,PCBBLK(T2) ;IF FIRST MESSAGE IN QUEUE IS NUMBERED, JRST ENQPC3 ; THEN SPLICE THIS IN AT BEGINNING ENQPC1: MOVSI T1,(POINT 18,0,35) ;ELSE BUILD A BYTE POINTER, HRRI T1,(T2) ;AND RECURSE PJRST ENQPCB ;DOWN THE LIST ENQPC2: ;HERE FOR NUMBERED MESSAGES TDNN T3,PCBBLK(T2) ;IF FIRST MESSAGE IS UNNUMBERED, JRST ENQPC1 ;THEN RECURSE ONWARD PUSH P,U ;SAVE U, WE NEED IT FOR A TEMP LDB T3,PCBMSN ;GET OUR MESSAGE NUMBER MOVE U,T2 ;COPY OTHER POINTER TO MAKE - LDB U,PCBMSN ; THIS LDB WORK RIGHT SUB T3,U ;COMPARE THE TWO MESSAGE NUMBERS, POP P,U ;GET OUR PCB PTR BACK TRNN T3,200 ;SKIP IF OUR PCB .LT. FIRST. JRST ENQPC1 ;OUR PCB .GT. FIRST, RECURSE ON ENQPC3: HRRM T2,PCBBLK(U) ;HERE TO INSERT PCB. MAKE LINK ONWARD, DPB U,T1 ;AND LINK US IN. POPJ P, ;AND THAT'S ALL ;ENQNDB THIS ROUTINE QUEUES AN NDB FOR SERVICE ON OR ABOUT THE ; NEXT CLOCK TICK. THE QUEUE IS NOT ORDERED, AS ALL NDB'S ; IN THE QUEUE ARE PROCESSED. THE HEAD OF THE QUEUE IS ; CONTAINED IN THE LOCATION "NTQNDB". THE BIT ".NDBNQ" ; INDICATES THAT THE NDB HAS ALREADY BEEN QUEUED. ;CALL W := ADDR OF NDB ;RETURN CPOPJ ENQNDB: NETOFF ;PROTECT OURSELVES FROM FEK'S MOVSI T1,NDB.NQ ;GET THE "THIS NDB IS QUEUED" BIT TDOE T1,NDBFLG(W) ;SEE IF NDB HAS ALREADY BEEN QUEUED JRST NTONPJ ;IF SO, THEN EXIT NOW HRR T1,NTQNDB ;OTHERWISE ENQUEUE THIS ONE BY GETTING MOVEM T1,NDBFLG(W) ;THE CURRENT HEADER AND SAVEING IT HERE HRRZM W,NTQNDB ;AND MAKING THE START OF THE QUEUE US. JRST NTONPJ ;ALL DONE, TURN THE PI'S BACK ON SUBTTL NETHIB/NETWAK - HIBERNATE AND SLEEP ROUTINE FOR .CPJOB ON THE NET ;SUBROUTINE NETHIB - PUT THE JOB IN THE HIBER STATE ;CALL MOVEI F,DDB ; PUSHJ P,NETHIB ;RETURN CPOPJ ;WHEN AWAKEN BY NETWAK NETHIC: SKIPA T1,[EXP EV.NTC] ;CONNECT WAIT (NO ^C ALLOWED) NETHIB::MOVEI T1,EV.NET ;GET REASON FOR EVENT WAKE SKIPN F ;MUST HAVE A DDB STOPCD CPOPJ##,STOP,FFU,;++F FOULED UP NTSAVE ;RETURN 'NT' FOR REST OF THIS ROUTINE LDB J,PJOBN## ;GET THE JOB NUMBER PUSHJ P,FNDPDS## ;GET THE PDB HRLM F,.PDNET##(W) ;STORE THE DDB POINTER IN CASE OF RESET(^C^C) PUSHJ P,ESLEEP## ;WAIT HRRZ W,JBTPDB##(J) ;GET THE PDB BACK HRRZS .PDNET##(W) ;CLEAR THE POINTER POPJ P, ;YES, EXIT TO CALLER ;SUBROUTINE NETWAK - WAKE THE JOB UP PUT TO SLEEP BY NETHIB ;CALL MOVEI F,DDB ; PUSHJ P,NETWAK ;RETURN CPOPJ ;JOB(PC) WILL BE @ NETHIB NETWAK::PUSHJ P,SAVJW## ;SAVE J AND W LDB T1,PJOBN## ;GET THE JOB NUMBER PJRST EWAKE## ;WAKE THE JOB ;SUBROUTINE NETSLP - PUT THE JOB TO SLEEP FOR 2 SECONDS ;CALL MOVEI F,DDB ; PUSHJ P,NETSLP ;RETURN CPOPJ ;AFTER 2 SECOND NETSLP::PUSHJ P,SAVT## ;SAVE THE T'S DON'T KNOW WHO IS CALLING NTSAVE ;RETURN 'NT' FOR REST OF THIS ROUTINE LDB J,PJOBN## ;GET THE JOB NUMBER MOVEI T1,2 ;TWO SECONDS PUSHJ P,SLEEPF## ;SLEEP JFCL ;MAYBE POPJ P, ;RETURN ;*** FOOTNOTE *** COMMENT \ Note that both these routines clobber "J" and "W". In general these routines are called with contents(J) pointing to a FEK, and contents(W) pointing to an NDB. Since these pointers would no longer be valid (the FEK or node might have crashed) these registers are intentionally clobbered. \ ;SUBROUTINE INCTBD - RECORD AND DISCARD A BAD INPUT MESSAGE ;CALL MOVEI U,PCB ; MOVEI J,FEK ; MOVEI T1,. ; PUSHJ P,INCTBD ;USUALLY PJSP T1,INCTBD ;RETURN CPOPJ INCTBP: HRRZ T1,(P) ;GET PC FOR THOSE WHO PUSHJ INCTBD::AOS %NTBAD ;COUNT THIS MESSAGE HRLI U,(T1) ;KEEP PC OF FINDER IN LH OF %NTBLC EXCH U,%NTBLC ;SAVE, GET LAST ONE S0JRST RMVPCB ;FLUSH THE PCB AND RETURN ;SUBROUTINE NETHRU - SEND A ROUTE THROUGH MESSAGE ;CALL MOVEI U,PCB ; MOVE T1,NODE NUMBER OF DESTINATION ; JRST NETHRU ;RETURN CPOPJ NETHRU: ;ENTRY PUSH P,J ;SAVE THE INPUT FEK PUSHJ P,SRCNDB ;FIND THE NODE TO SEND THE MESSAGE JSP T1,[POP P,J ;RESTORE THE INPUT FEK JRST INCTBD] ;ILLEGAL MESSAGE HRRZ J,NDBFEK(W) ;GET THE OUTPUT FEK IFN FTKL10, ;MSG WAS LOOKED AT, CLEAR VALID BIT NETOFF PUSHJ P,FRCWRT ;SEND THE MESSAGE JRST JPOPJ## ;RESTORE THE INPUT FEK AND RETURN ;SUBROUTINE NETRDD - SET UP A READ REQUEST TO THE FRONT END ;CALL MOVE J,FEK ; PUSHJ P,NETRDD ;RETURN CPOPJ NETRDD: SETZ W, ;CLEAR THE NODE POINTER SKIPE FEKIAD(J) ;CURSORY CHECK TO SEE IF INPUT BUFFER POSTED POPJ P, ;IF SO, DON'T GO THROUGH THE HAIR IFN FTENET,< MOVE T1,FEKBLK(J) ;GET FEK FLAGS TLNN T1,FK.ETH ;ETHERNET FEK? JRST NETRD1 ;NO, NORMAL DEVICE FEK TLNN T1,FK.ETM ;ETHERNET MASTER FEK? POPJ P, ;NO, SLAVE FEKS DON'T DO INPUT MOVE T1,FEKNIQ(J) ;GET COUNT OF INPUT PCBS ALREADY QUEUED CAIL T1,4 ;GOT PLENTY? POPJ P, ;YES, DON'T QUEUE UP TOO MANY > ;END IFN FTENET ;ALLOCATE AN INPUT PCB NETRD1: MOVEI T1,MSGMAW## ;BUFFER SIZE THAT IS THE LARGEST PCB PUSHJ P,MKUPCB ;GET THE BUFFER SPACE POPJ P, ;IF NO CORE, JUST EXIT (WILL TRY AGAIN LATER) MOVEI T1,MSGMAX## ;GET, AND SET THE NUMBER OF BYTES MOVEM T1,PCBCTR(U) ; AVAILABLE IN THE PCB. ;NOW QUEUE THE INPUT REQUEST NETOFF ;WE MUST WORRY ABOUT RACES AGAIN. SKIPE FEKIAD(J) ;MAKE SURE WE DIDN'T GET BEAT. IF SOMEONE SET JRST [NETON ; A READ REQUEST JUST NOW, TURN INTERRUPTS ON PJRST RMVPCB] ; RETURN THE BUFFER AND EXIT HRRZM U,FEKIAD(J) ;POST THE INPUT BUFFER NETON ;RE-ENABLE INTERRUPTS WITH BUFFER SET UP ;KICK THE FEK (POST READ REQUEST) MOVEI T1,FF.RDD ;GET THE READ-REQUEST FUNCTION CODE XCT FEKDSP(J) ; AND CALL THE FRONT END KONTROLLER IFE FTENET, ;ALL DONE IFN FTENET, ;SEE IF NEED MORE BUFFERS SUBTTL FEKINT -- ENTRY POINT TO NETSER FROM FRONT END'S ;CALL J := FEK ADDRESS ; T1 := FUNCTION CODE ;RETURN CPOPJ ;CALLING ROUTINE MUST DISMISS THE INTERRUPT FEKINT::TRNN J,-1 ;MAKE SURE FEK IS SET UP STOPCD .,STOP,ANFZFK, ;++ ZERO FEK POINTER CAIL T1,FI.RDD ;RANGE CHECK THE FUNCTION CODE CAILE T1,FI.IDN ; (BETWEEN 0 & 4) STOPCD .,STOP,ANFIFC, ;++ ILLEGAL FEKINT FUNCTION CODE JRST @.+1(T1) ;DISPATCH TO APPROPRIATE ROUTINE IFIW INPINT ;INPUT DONE INTERRUPT (VIA FEKIAD) IFIW OUTINT ;OUTPUT DONE INTERRUPT IFIW STCINT ;WE JUST GOT A BOOT MESSAGE IN "U" IFIW KONDWN ;FRONT END JUST DIED IFIW INPIDN ;INPUT DONE INTERRUPT (VIA FEKIDN) ;HERE WHEN A KONTROLLER GOES DOWN KONDWN: PUSHJ P,CLNFEK ;CLEAN OUT THE FEK SETOM NTQRCT ;TELL NETSCN TO RECOMPUTE THE TOPOLOGY PJRST NETQUE ;AND GO DO IT. SUBTTL OUTINT - OUTPUT INTERRUPT PROCESSOR FOR ALL FEK'S ;OUTINT ROUTINE TO HANDLE THE OUTPUT DONE INTERRUPT FROM FEK'S ;CALL J := FEK ADDRESS ;RETURN CPOPJ OUTINT: PUSH P,U ;SAVE U (THE FRONT END MAY CARE...) SETZ U, ;GET A ZERO EXCH U,FEKODN(J) ;GET THE PCB, CLEAR OUTPUT-DONE SKIPN U ;MAKE SURE WE GOT A PCB STOPCD .,STOP,ANFOUT, ;++ OUTPUT DONE WITH NO PCB ;NOW QUEUE THE PCB FOR NETSCN TO PROCESS (QUEUE TO WAIT FOR ACK) NETOFF ;NO INTERRUPTS WHILE HACKING QUEUES HRRZ T1,NTQOUT ;GET THE "REST" OF THE LIST HRRM T1,PCBBLK(U) ;MAKE OUR PCB POINT TO THE "REST" HRRZM U,NTQOUT ;MAKE THE LIST HEAD BE OUR PCB NETON ;INTERRUPTS OK. QUEUE IS CONSISTENT SETZM FEKHTM(J) ;CLEAR THE HUNG TIMER. FEK IS RUNNING POP P,U ;GET FEK'S "U" BACK ; PUSHJ P,NETQUE ;QUEUE AN INTERRUPT TO NETSCN AND RETURN ; (SEE FOOTNOTE) POPJ P, ;RETURN TO INTERRUPT ROUTINE ;*** FOOTNOTE *** ;We don't really need to run netser at this point. Since we are ; just going to queue the output PCB and wait for the ACK, we ; can postpone queueing the PCB until we get an input messsage. SUBTTL INPINT - INPUT INTERRUPT PROCESSOR FOR ALL FEK'S ;INPINT ROUTINE TO HANDLE THE INPUT DONE INTERRUPT FROM FEK'S ;CALL J := FEK ADDRESS ;RETURN CPOPJ INPIDN: PUSH P,U ;PRESERVE FEK'S "U" SETZ U, ;GET A ZERO EXCH U,FEKIDN(J) ;GET INPUT PCB, CLEAR INPUT-DONE POINTER JRST INPIN0 ;CONTINUE WITH INPUT PROCESSING INPINT: PUSH P,U ;PRESERVE FEK'S "U" SETZ U, ;GET A ZERO EXCH U,FEKIAD(J) ;GET INPUT PCB, CLEAR INPUT BUFFER POINTER INPIN0: SKIPN U ;MAKE SURE THERE WAS ONE STOPCD .,STOP,ANFINP, ;++ INPUT DONE, NO INPUT BUFFER MOVE T2,PCBALN(U) ;ALLOCATED (I.E., MAX) SIZE OF PCB DATA BUFFER LSH T2,2 ;CONVERT WORD SIZE TO BYTES CAMGE T2,PCBCTR(U) ;IF DATA OVERFLOWED PCB'S BUFFER ALLOCATION STOPCD CPOPJ##,STOP,PBO, ;++ PCB BUFFER OVERFLOW ;NOW QUEUE THIS PCB AT THE END OF THE INPUT PCB CHAIN HRLM J,PCBFEK(U) ;REMEMBER THE FEK (FOR NODE ID) NETOFF ;PROTECTION SKIPE T1,NTQINP ;GET THE CURRENT INPUT PCB QUEUE JRST INPIN2 ;AND PUT THIS PCB AT ITS TAIL HRRZM U,NTQINP ;NO INPUT PCBS, THIS IS THE NEW FIRST PCB JRST INPIN4 ;INPUT PCB SAFELY QUEUED INPIN2: MOVE T2,T1 ;SAVE POTENTIAL NEW LAST PCB HRRZ T1,PCBBLK(T1) ;LINK TO NEXT PCB IN CHAIN JUMPN T1,INPIN2 ;KEEP GOING TILL FIND THE END HRRM U,PCBBLK(T2) ;QUEUE THIS PCB AT THE END OF THE INPUT CHAIN INPIN4: NETON ;ALL'S CLEAR NOW ;REPLENISH THE FEK AND START UP NETSER INPUT PROCESSING PUSHJ P,NETRDD ;GIVE THE FEK A NEW INPUT BUFFER POP P,U ;GET THE FEK'S "U" BACK PUSHJ P,NETQUE ;AND QUEUE A CALL TO NETSCN POPJ P, ;RETURN SUBTTL STCINT -- ROUTINE TO QUEUE INCOMING MAINTENANCE MESSAGES ;STCINT ROUTINE TO ACCEPT MAINTENANCE MESSAGES FROM THE FRONT ENDS ;CALL U := POINTER TO STC BLOCK. NOTHING IN STCNNL(U) IS SET UP STCINT: PUSHJ P,SAVJW## ;FOR CONVENIENCE... STC USUALLY GOES IN J PUSHJ P,FEK2LN ;GET T1 := THE LINE NUMBER HRRZM T1,STCLNN(U) ;STORE THE LINE NUMBER TO RETURN TO USER MOVE W,J ;GET W := FEK MOVE J,U ;AND J := STC SKIPE FEKICT(W) ;IF THERE IS ALREADY A MESSAGE QUEUED, PJRST GIVSTC ; FREE THIS ONE IMMEDIATLY AND RETURN. HRLI J,^D10 ;GIVE THE MESSAGE A 10 SEC TIME OUT MOVEM J,FEKICT(W) ;STORE THE NEW "XWD TIMER,MSG" IN THE FEK SKIPE T1,FEKBJN(W) ;IF SOMEONE IS WAITING FOR A STC MSG JRST EWAKE## ; THEN WAKE HIM AND RETURN PJRST FRCNLD ;OTHERWISE WAKE NETLDR TO LOOK AT IT. ;HERE ONCE A TICK TO START TERMINAL OUTPUT FOR ALL LINES IN THE QUEUE ; WE ALSO CHECK EACH OF THE FEKS ON THIS CPU TO SEE IF THEY ; NEED SUCH THINGS DONE AS THEIR INPUT, OR OUTPUT STARTED NETSTO::SKIPA J,[FEKFST##] ;GET THE ADDRESS OF THE FIRST FEK NETST1: HRRZ J,FEKBLK(J) ;GET THE ADDRESS OF THE NEXT FEK JUMPE J,NETST5 ;IF NO MORE, GO DO REST OF JIFFY CODE IFN FTMP,< ;IF A SMP SYSTEM, ONLY TIME ON OWNING CPU HLRE T1,FEKUNI(J) ;GET THE CPU NUMBER SKIPGE T1 ;IF IT CAN RUN ON ANY CPU, MOVE T1,BOOTCP## ; CALL IT ONLY ON THE BOOT CPU CAME T1,.CPCPN## ; AND SKIP IF IT'S US JRST NETST1 ;WRONG CPU. GO CHECK NEXT FEK > NETST2: SKIPL T1,FEKBLK(J) ;GET THE FLAGS AND CHECK FK.ONL JRST NETST1 ;EITHER FEK OFFLINE, OR NOT ON THIS CPU MOVSI T2,FK.STO ;GET THE "FEK WANTS OUTPUT STARTED" BIT TDNN T2,FEKBLK(J) ;DOES THE FEK WANT TO GET POKED JRST NETST3 ;DOESN'T WANT A KICK ANDCAB T2,FEKBLK(J) ;CLEAR THE BIT NOW (PREVENTS A RACE) MOVEI T1,FF.WRT ;GET THE FEK WRITE FUNCTION CODE XCT FEKDSP(J) ;DISPATCH TO GIVE THE FEK AN OUTPUT POKE NETST3: MOVSI T2,FK.STI ;GET THE "NEEDS INPUT STARTED" BIT TDNN T2,FEKBLK(J) ;DOES THE FEK WANT TO DO INPUT JRST NETST4 ;DOESN'T WANT A KICK ANDCAB T2,FEKBLK(J) ;CLEAR NOW BEFORE OTHER CPU MAY SET IT MOVEI T1,FF.RDD ;GET THE "SET UP READ REQUEST" FUNCTION XCT FEKDSP(J) ;TELL THE FEK IT HAS AN INPUT BUFFER TO FILL NETST4: JRST NETST1 ;LOOP OVER ALL FEK'S NETST5: IFN FTMP,< SKPCPU (0) ;IF NOT ON THE BOOT CPU POPJ P, ;DON'T CALL NETSCN (NO NEED TO) > SETOM NTQJIF ;SIGNAL A JIFFY. PJRST NETSCN ; AND GO PROCESS INTERLOCKED CODE ;NETINT THIS IS WHERE THE NETSER SOFTWARE GENERATED INTERRUPTS ARE ; HANDLED. AN INTERRUPT IS GENERATED BY THE SUBROUTINE ; "NETLKU" ONLY WHEN THE "NT" INTERLOCK IS RELEASED AND THE ; FLAG "NTRQST" IS SET. THE PURPOSE OF THIS IS TO LET ; NETSER CONTINUE WITH SOMETHING THAT HAPPENED AT INTERRUPT ; LEVEL WHILE SOME JOB HAD THE "NT" INTERLOCK. THE ONLY ; REASON THAT THIS INTERRUPT IS USED, RATHER THAN CALLING ; NETSCN DIRECTLY, IS THAT IT TURNS OFF THE ACCOUNTING ; METERS. NETINT:: SETZM .CPNTF## ;CLEAR THE INDICATOR CONO PI,CLRNET## ;CLEAR THE INTERRUPT PJRST NETSCN ;AND SEE WHAT NETSCN WANTED. ;NETQUE THIS IS THE ROUTINE TO CALL TO SET UP A INTERRUPT TO RUN NETSCN NETQUE::CONO PI,PI.OFF ;MAKE SURE THAT DIFFERENT PI LEVELS SETOM .CPNTF## ; DON'T SET THE FLAG AT ONCE CONO PI,PI.ON+REQNET## ;CAUSE THE INTERRUPT POPJ P, ; AND RETURN SUBTTL NETSCN -- QUEUE DRIVEN NETSER 'LOOP' LEVEL ;NETSCN -- THIS ROUTINE IS CALLED AT INTERRUPT LEVEL. IT PROCESSES ENTRIES ; IN THE FOLLOWING QUEUES ; A) NTQRCT THIS IS A QUEUE OF FEK REQUESTS. ; B) NTQJIF THIS IS A FLAG THAT IS SET ONCE A JIFFY. IT ; ENABLES 'JIFFY' PROCESSING. (IE STARTING TERMINAL OUTPUT) ; C) NTQOUT THIS IS A QUEUE OF UN-SORTED OUTPUT MESSAGES. (IE JUST AS ; THEY ARE GIVEN BACK BY THE FEK'S). NETSCN REMOVES MESSAGES ; FROM THIS QUEUE AND APPENDS THEM TO THE RH(NDBQUE) OF THE ; APPRIOATE NDB WHERE THEY THEN WAIT FOR AN ACK TO FREE THEM. ; D) NTQINP THIS IS A QUEUE OF UN-SORTED INPUT MESSAGES. ; NTSC.I ROUTES THEM TO THE APPROPRIATE NDB'S ; E) NTQNDB THIS IS A QUEUE OF NDB'S THAT NEED SERVICE. ; THE PRIMARY REASON THAT AN NDB WILL NEED SERVICE IS ; THAT IT HAS INPUT DATA TO PROCESS (FROM NTQINP) ; F) NTQSEC THIS IS A FLAG THAT ENABLES THE ONCE PER SECOND ; PROCESSING (IE REP TIMING ETC) ; ; NETSCN IS MEANT TO BE CALLED FROM INTERRUPT LEVEL. IT FIRST CHECKS ; THE INTERLOCK (THE NT INTERLOCK) TO SEE IF IT CAN PROCESS IMEDIATLY. ; IF IT CAN, IT SETS THE INTERLOCK BUSY AND GOES TO WORK. IF IT CAN'T ; GET THE INTERLOCK IT RETURNS IMMEDIATLY. ; NOTE!! ANY ONE WHO REQUESTS THE INTERLOCK SHOULD CALL NETSCN UPON ; RELEASING IT. (THIS WILL BE DONE AUTOMATICALLY BY NETLKU) NETSCN: SE1ENT ;ENTER SECTION 1 PUSHJ P,INTLVL## ;NETSCN MUST RUN AT A INTERRUPT LEVEL. STOPCD .,STOP,ANFNIL, ;++ NOT AT INTERRUPT LEVEL NTDBLI ;TRY TO GET THE INTERLOCK JRST [SETOM NTRQST ; BUT IF WE CAN'T, JUST SET THIS FLAG POPJ P,] ; AND RETURN PUSHJ P,SAVJW## ;SAVE THESE PUSHJ P,SAVE4 ; AND THESE SKIPE NTQRCT ;NEED TO RECOMPUTE TOPOLOGY?? PUSHJ P,NTSC.R ; IF SO, GO DO IT. SKIPE NTQJIF ;JIFFY? PUSHJ P,NTSC.J ; IF SO ... SKIPE NTQOUT ;OUTPUT DONE INTERRUPTS?? PUSHJ P,NTSC.O ; ... SKIPE NTQINP ;INPUT MESSAGES TO PROCESS?? PUSHJ P,NTSC.I ; ... SKIPE NTQNDB ;NDB'S THAT WANT SERVICING?? PUSHJ P,NTSC.N ; ... SKIPE NTQSEC ;ONCE/SECOND PROCESSING TO DO??? PUSHJ P,NTSC.S SETZM NTRQST ;CLEAR THE "REQUEST INTERRUPT" FLAG NTDBUI ;RETURN THE 'NT' INTERLOCK POPJ P, ;AND RETURN ;HERE WHEN NETSCN WANTS TO RECOMPUTE THE TOPOLOGY NTSC.R: ;HERE TO REBUILD OUR NEIGHBORS TABLE SETZM NTQRCT ;CLEAR THE REQUEST PUSHJ P,RCMPTP ;RECOMPUTE THE TOPOLOGY PUSHJ P,SETNBN ;NOW SEND NEIGHBORS MESSAGES TO EVERYONE POPJ P, ;ALL DONE ;HERE FOR ONCE/JIFFY STUFF. FUNCTIONS PERFORMED ONCE/JIFFY ARE ; 1) STARTING TERMINALS AND VTMS ; 2) WE CHECK TO SEE IF WE OWE ANYONE AN ACK AND IF SO, SEND IT NTSC.J: SETZM NTQJIF ;SAY WE PROCESSED THE JIFFY, PUSHJ P,MCRJIF## ;START UP NETWORK MCRS PUSHJ P,VTMJIF## ;START UP NETWORK VIRTUAL TERMINALS ;NOW SEE IF ANY NODES NEED ACK'S SENT SKIPN NTFACK ;DID WE RECEIVE ANY MSGS LAST JIFFY JRST NTSCJ2 ;IF NOT, THEN WE DON'T NEED TO SEND ACKS SETZM NTFACK ;CLEAR THE "MAY NEED TO ACK" FLAG MOVEI W,NETNDB ;GET THE ADDRESS OF THE FIRST NDB NTSCJ1: HRRZ W,NDBNNM(W) ;GET THE NEXT NDB (SKIP NETNDB) JUMPE W,NTSCJ2 ;EXIT IF WE'VE CHECKED ALL NODES LDB T1,NDBLMP ;GET THE LAST MESSAGE PROCESSED LDB T2,NDBLAS ; AND THE LAST ACK SEND CAMN T1,T2 ;IF WE OWE THE REMOTE AN ACK, JRST NTSCJ1 ;DON'T COUNT NEED ACK FLAG PUSHJ P,NCSACK ; THEN SEND HIM ONE AOS NTFACK ;IF WE FAIL, SET THE FLAG AGAIN JRST NTSCJ1 ;LOOP OVER ALL NDBS NTSCJ2: POPJ P, ;ALL DONE WITH JIFFY PROCESSING ;NTSC.O -- ROUTINE TO PROCESS MESSAGES THAT HAVE BEEN OUTPUT BY FEK'S ; AFTER A MESSAGE HAS BEEN SENT OUT BY A FEK, IT IS PUT ON THE QUEUE ; "NTQOUT". THIS ROUTINE (UNDER PROTECTION OF THE NETSER INTERLOCK) ; ATTEMPTS TO PROPERLY DISPOSE OF THESE MESSAGES. THE BASIC ALGORITHM IS: ; 1) IF IT IS A NUMBERED MESSAGE, THEN WE MUST QUEUE IT ON ; RH(NDBQUE) FOR THE APPROPRIATE NDB. THE MESSAGE WILL ; REMAIN HERE UNTIL DESTROYED BY AN ACK FROM THE OTHER ; NODE ; 2) IF IT IS AN UNNUMBERED MESSAGE THE MESSAGE IS DESTROYED ; IMMEDIATLY. (NOTE THAT ROUTE-THROUGH MESSAGES ARE ; UNNUMBERED, AND HENCE THEY WILL NOT BE PUT ON THE ; THE RH(NDBQUE). THIS IS A CONVENIENT ARTIFACT OF THE MANNER ; IN WHICH THE "NUMBERED MESSAGE FLAG" (PCB.NM) IS KEPT) NTSC.O: SKIPN NTQOUT ;ANYTHING IN THE QUEUE POPJ P, ;NOPE. LOOK ELSE WHERE FOR BUSY WORK NETOFF ;NO PI WHILE MODIFYING THE LIST HRRZ U,NTQOUT ;GET THE HEAD OF THE LIST HRRZ T1,PCBBLK(U) ; GET THE REST OF THE LIST HRRZM T1,NTQOUT ; AND MAKE THAT THE LIST NETON MOVE T1,PCBCTR(U) ;GET THE FIRST OUTPUT BYTE COUNT ADD T1,PCBCT2(U) ;PLUS THE SECOND BUFFER'S BYTE COUNT ADDM T1,%NTBYO ;COUNT TOTAL BYTES OUTPUT MOVE T1,PCBBLK(U) ;GET THE FLAGS AND POINTER HLLZS PCBBLK(U) ;CLEAR THE LINK POINTER JUMPGE T1,NTSCO1 ;UNNUMBERED. FLUSH IT NOW HRRZ T1,PCBNNM(U) ;GET THE NODE NUMBER PUSHJ P,SRCNDB ;SET UP W FROM THE NODE NUMBER JRST NTSCO1 ;THE NODE WENT AWAY. FLUSH THE PCB CAIN W,NETNDB## ;IF THIS MESSAGE IS FROM THE NULFEK, JRST [AOS NDBMOM(W) ; BUMP OUR MAX OUTSTANDING MSG COUNTER JRST NTSCO1] ; AND FREE THE PCB (WE DON'T ACK OURSELF...) LDB T1,PCBMSN ;GET THE NUMBER OF THE MESSAGE SENT DPB T1,NDBLMS ;SAVE AS LAST SENT MOVSI T1,(POINT 18,0,35) ;HERE GENERATE A POINTER TO THE HRRI T1,NDBQUE(W) ;RH OF NDBQUE(W) S0PSHJ ENQPCB ;AND USE IT TO QUEUE THE PCB. JRST NTSC.O ;AND GO BACK FOR MORE NTSCO1: ;HERE DELETE THE PCB AND GO BACK S0PSHJ RMVPCB ;DELETE THE PCB JRST NTSC.O ;AND DO NEXT MESSAGE ;NTSC.I -- ROUTINE TO PROCESS INPUT MESSAGES. ; WHEN MESSAGES COME IN FROM A FEK, THEY ARE ENQUEUED ON THE QUEUE NTQINP. ; THIS ROUTINE (UNDER PROTECTION OF THE NETSER INTERLOCK) ATTEMPTS TO ; ROUTE THE MESSAGES TO THE APPROPRIATE QUEUES FOR FURTHER PROCESSING. THE ; BASIC ALGORITHM IS: ; A) REMOVE THE MESSAGE AND LOOK AT ITS NCT. IF IT IS ; A NODE ID, THEN QUEUE IT ON THE LH(NDBQUE) OF NETNDB. ; B) READ THE DESTINATION ADDRESS, AND IF IT'S NOT FOR US, ; ATTEMPT TO ROUTE IT THROUGH TO THE DESTINATION NODE. ; C) IF IT IS FOR US, THEN QUEUE IT (ORDERED BY MESSAGE NUMBER) ; ON THE LH(NDBQUE) OF THE SOURCE NODE'S NDB. ALSO MARK ; THE NDB AS "NEEDING SERVICE". NTSC.N WILL TAKE OVER FROM ; THERE. ; POINTS TO NOTE: ; 1) ACK'S ARE NOT PROCESSED AT THIS TIME. (THERE MAY BE ; SOMETHING "WRONG" WITH THE MESSAGE) ; 2) THIS CODE SETS THE NUMBERED MESSAGE FLAG (PCB.NM) ON ; NUMBERED MESSAGES DESTINED FOR US. (BUT NOT ROUTE-THROUGH ; MESSAGES) NTSC.I: SKIPN NTQINP ;ANY MORE INPUT?? POPJ P, ;NOPE. GO BACK TO NETSCN. NETOFF ;PROTECTION HRRZ U,NTQINP ;GET FIRST PCB HRRZ T1,PCBBLK(U) ;GET REST OF LIST HRRZM T1,NTQINP ; AND SAVE IT FOR LATER. NETON ;ALL'S CLEAR MOVE T1,PCBCTR(U) ;GET THE INPUT BYTE COUNT, ADDM T1,%NTBYI ; AND COUNT THESE INPUT BYTES MOVSI T1,PCB.NM ;ASSUME THAT THIS IS AN UN-NUMBERED ANDCAM T1,PCBBLK(U) ; MESSAGE. (IN CASE ROUTE-THROUGH) HLLZS PCBBLK(U) ;CLEAR ANY STRAY POINTERS SETZB W,F ;CLEAR THE NDB POINTER,DDB POINTER MOVE P1,PCBPTR(U) ;GET THE INPUT MESSAGE POINTER MOVE P4,PCBCTR(U) ;GET THE INPUT BYTE COUNT JUMPLE P4,NTSCI3 ;ZERO LENGTH IS ILLEGAL ;NCT PUSHJ P,EBI2BI ;READ THE FLAGS HRLZM T1,NTFLGS ;SAVE THE FLAGS ANDI T1,NCT.TP ;JUST THE MESSAGE TYPE MOVE T2,NTFLGS ;GET FLAGS BACK TLNE T2,NCT.RH ;ROUTING HEADER PRESENT JRST NTSCI1 ;YES, CAIE T1,NCT.ID ;IS THIS AN ID MESSAGE JRST NTSCI3 ;NO, BAD MESSAGE MOVEI W,NETNDB ;QUEUE NODE ID AS INPUT FROM OUR NODE ; SINCE WE PROBABLY DON'T HAVE AN NDB FOR IT PJRST NTSCI2 ;AND NOW ENQUEUE THE MESSAGE FOR NEXT LEVEL NTSCI1: ;DNA PUSHJ P,EBI2BI ;GET HLRZ T2,NDBNNM+NETNDB;GET OUR NODE NUMBER CAIE T1,(T2) ;IS IT FOR US? JRST [S0PSHJ NETHRU ;IN ONE EAR AND OUT THE OTHER JRST NTSC.I] ; AND CHECK FOR ANY OTHER INPUT ;SNA PUSHJ P,EBI2BI ;YES, GET THE PUSHJ P,SRCNDB ;FIND THE NODE BLOCK JRST NTSCI3 ;SOURCE NODE WENT AWAY ;NCA ILDB T1,P1 ;GET THE ACK NUMBER PUSHJ P,CHKNCA ;ACK IT NOW TO PREVENT DEADLOCKS ;NCN ILDB T1,P1 ;GET THIS MESSAGE NUMBER MOVE T2,NTFLGS ;FLAGS TLNE T2,NCT.TP ;IS THIS A NUMBERED MESSAGE? JRST NTSCI2 ;IF NOT, DON'T PLAY WITH IT ANY MORE DPB T1,PCBMSN ;SAVE THE NUMBER IN THE PCB MOVSI T1,PCB.NM ;GET AND IORM T1,PCBBLK(U) ; SET THE NUMBERED MESSAGE FLAG NTSCI2: MOVSI T1,(POINT 18,0,17) ;BUILD A BYTE POINTER HRRI T1,NDBQUE(W) ; TO NDBQUE (LH) S0PSHJ ENQPCB ;AND QUEUE THE PCB FOR CLOCK LEVEL S0PSHJ ENQNDB ;MAKE SURE THE NDB GETS SCANNED JRST NTSC.I ;NOW GO BACK AND SEE IF THERE ARE ANY MORE NTSCI3: PUSHJ P,INCTBP ;HERE WE FLUSH A GARBAGE MESSAGE JRST NTSC.I ;SEE IF THERE ARE ANY MORE ;NTSC.N -- ROUTINE TO SERVICE AN NDB. ; THIS ROUTINE PROCESSES NDB'S THAT HAVE BEEN "QUEUED" ON THE QUEUE ; "NTQNDB". THIS ROUTINE PERFORMS THE FOLLOWING SERVICES FOR AN ; NDB: ; 1) IT PROCESSED QUEUED INPUT MESSAGES FROM THE RH(NDBQUE) ; 2) IT SENDS ANY OTHER "SPECIAL" MESSAGES THAT MAY BE ; REQUIRED BY THE NODE. (MOST NOTABLY NAK'S) ; THE PROCESSING OF INPUT MESSAGES (1) CAN BE BROKEN UP INTO THE ; FOLLOWING PHASES: ; A) TAKE THE FIRST MESSAGE OFF OF THE RH(NDBQUE) AND ; GET ITS NETWORK CONTROL TYPE (NCT). IF IT'S A NODE-ID ; THEN GO PROCESS IT IMMEDIATLY ; B) IF WE ARE NOT CURRENTLY IN CONTACT (START-STACK EXCHANGED) ; WITH THE NODE, THROW AWAY ALL MESSAGES EXCEPT START OR ; STACK. ; C) PERFORM A CURSORY VALIDITY CHECK OF THE MESSAGE AND ; PROCESS THE NETWORK CONTROL ACK (NCA) FIELD. ; D) IF IT IS AN UNNUMBERED MESSAGE, THEN PROCESS IT ; IMMEDIATLY. ; E) IF IT IS A NUMBERED MESSAGE, THEN CHECK TO SEE IF IT IS ; THE "NEXT" MESSAGE IN SEQUENCE. IF IT IS ; 1) A REPEATED MESSAGE, THEN DELETE IT ; 2) THE NEXT MESSAGE, THEN PROCESS IT ; 3) AN "OUT OF ORDER" MESSAGE, THEN RE-QUEUE IT ON THE ; RH(NDBQUE) AND EXIT PROCESSING INPUT MESSAGES. ; NOTE THAT THERE IS NO 100% SAFE WAY TO TELL WEATHER OR ; NOT A MESSAGE IS A REPEATED MESSAGE, OR ONE THAT ; ARRIVED EARLY. THE ALGORITHM IMPLEMENTED HERE ASSUMES ; THAT MESSAGES UP TO 127 AHEAD OF THE LAST MESSAGE PROCESSED ; ARE MESSAGES THAT ARRIVED EARLY, AND ALL OTHERS (EXCEPT THE ; "NEXT") ARE REPEATED MESSAGES. NTSC.N: SKIPN T2,NTQNDB ;ANY NDB'S TO PROCESS POPJ P, ;NO. GO CHECK SOMETHING ELSE NETOFF ;GUARD AGAINST UNRULY FEK'S HRRZ W,NTQNDB ;GET THE NDB, HRRZ T1,NDBFLG(W) ;GET THE LINK TO NEXT NDB IN QUEUE HRRZM T1,NTQNDB ;SPLICE THIS NDB OUT OF THE QUEUE HRLOI T1,NDB.NQ ;GET THE 'THIS NDB IS QUEUED' BIT ANDCAM T1,NDBFLG(W) ;AND CLEAR IT IN NDBFLG NETON ;NTQNDB QUEUE IS CONSISTENT AGAIN HRRZ J,NDBFEK(W) ;SET UP J FOR INCTNK AND NCSSTR (AND OTHERS) ;DROP THROUGH ;FROM ABOVE ;W := NEXT NDB TO PROCESS NTSCN1: ;HERE TO PROCESS THE NEXT INPUT MESSAGE ON THE LH OF NDBQUE(W) NETOFF ;WATCH OUT. A FEK MIGHT PUT ONE ON HLRZ U,NDBQUE(W) ;GET FIRST PCB JUMPE U,[ NETON ;IF NONE, THEN JRST NTSCN5]; SEE WHAT ELSE NEEDS TO BE DONE MOVE T1,PCBBLK(U) ;GET "XWD FLAGS,LINK" FROM PCB HRLM T1,NDBQUE(W) ; AND SPLICE OUT THE PCB NETON ;NDBQUE IS CONSISTENT AGAIN. HLLZS PCBBLK(U) ;IT'S NOT POLITE TO POINT. (TO BE SURE!) ; NOW PROCESS THE PCB IFN PARANOID&P$EAT,< ;HACK TO EAT EVERY N'TY MESSAGE MOVEI T1,NETNDB## ;SEE IF THIS IF FROM US CAIN T1,(W) ;IF SO, THEN JRST STARVE ;DON'T EAT THE MESSAGE (WE DON'T REP OURSELF) SOSG %TROLL ;HAVE WE COUNTED DOWN YET? PJSP T1,[PUSHJ P,INCTBD ;IF SO, TOSS THIS MESSAGE MOVEI T1,100 ;RESET THE MOVEM T1,%TROLL; COUNTER JRST NTSCN1] ;AND GO GET THE NEXT ONE STARVE: ;HERE IF WE CAN'T EAT THE MSG > MOVE P4,PCBCTR(U) ;GET THE LENGTH OF THE MESSAGE. MOVE P1,PCBPTR(U) ;GET THE BYTE POINTER ;NCT PUSHJ P,EBI2BI ;GET THE FLAGS HRLZM T1,NTFLGS ;WEVE ALREADY GOT THEM BUT.. ANDI T1,NCT.TP ;ISOLATE THE TYPE AOS NCLRTP(T1) ;TALLY MOVE T2,NTFLGS ;GET FLAGS BACK TLNE T2,NCT.RH ;ROUTING INFORMATION PRESENT? JRST NTSCN2 ;YES. CONTINUE CAIE T1,NCT.ID ;NO ROUTINE, IS IT A NODE-ID? STOPCD .,STOP,ANFMSQ, ;++ MESSAGE QUEUES ARE SCREWED UP S0PSHJ INCTID ;GO PROCESS THE NODE-ID MESSAGE JRST NTSCN8 ;FREE PCB AND GET NEXT MESSAGE ;HERE FROM ABOVE IF THE MESSAGE HAS A ROUTING HEADER (IE ISN'T A NODE-ID) NTSCN2: MOVE T2,NDBFLG(W) ;IS THIS NODE UP AND RUNNING? TLNE T2,NDB.UP ;SKIP IF NOT JRST NTSCN3 ;IT IS, CONTINUE PROCESSING CAIE T1,NCT.ST ;SINCE IT'S NOT RUNNING, SEE IF THIS CAIN T1,NCT.SK ;IS A START OR A STACK JRST NTSCN3 ;IF IT IS, OK TLNE T2,NDB.SK ;IF WE ARE SENDING STACKS, JRST [PUSHJ P,NODEUP ;THEN ANY MESSAGE GETS US INTO "RUN" STATE JRST NTSCN3] ;NOW GO PROCESS THE MESSAGE PUSHJ P,INCTBP ;IF WE AREN'T RUNNING, SIGNAL BAD MESSAGE, JRST NTSCN1 ;AND GO BACK FOR ANOTHER NTSCN3: ;HERE TO DO A LITTLE MORE CHECKING ;DNA PUSHJ P,EBI2BI ;GET DNA HLRZ T2,NETNDB+NDBNNM;GET OUR NODE NUMBER CAIE T1,(T2) ;BETTER BE THE SAME STOPCD .,STOP,ANFRDN, ;++ ROUTING HEADER HAS BAD DESTINATION NODE ;SNA PUSHJ P,EBI2BI ;GET SOURCE NODE NUMBER HLRZ T2,NDBNNM(W) ;GET THE NODE NUMBER OF THIS NDB CAIE T1,(T2) ;BETTER BE THE SAME STOPCD .,STOP,ANFRSN, ;++ ROUTING HEADER HAS BAD SOURCE NODE ;NCA PUSHJ P,BYT2BI ;GET THE IMPLICIT ACK (ALREADY PROCESSED) ;NCN PUSHJ P,BYT2BI ;GET THE MESSAGE NUMBER AND MOVE T2,NTFLGS ;GET FLAGS BACK TLNE T2,NCT.TP ;SKIP IF IT IS A NUMBERED MESSAGE JRST INCTUN ;IT'S NOT, GO PROCESS UNNUMBERED MESSAGE DPB T1,NDBLMR ;SET LAST MESSAGE RECEIVED LDB T2,NDBLMP ;GET LAST MESSAGE PROCESSED ADDI T2,1 ;GET NUMBER OF NEXT MESSAGE TO PROCESS ANDI T2,377 ;MAKE SURE IT'S MOD(1000) SUBI T1,(T2) ;IF THIS IS 'NEGATIVE' WE'VE SEEN IT BEFORE TRNE T1,200 ;IF THIS MESSAGE HAS BEEN SEEN JRST NTSCN8 ; BEFORE, THEN TOSS IT JUMPN T1,NTSCN4 ;AN OUT OF ORDER (EARLY) MESSAGE ;DROP THROUGH IF THIS IS THE NEXT MESSAGE TO PROCESS (IE IS IN ; SEQUENCE) ;HERE FROM ABOVE WITH U POINTING TO THE NEXT MESSAGE TO PROCESS ; SINCE SOME MESSAGES REQUIRE IMMEDIATE RESPONSE, MAKE SURE THAT WE CAN ; SEND AT LEAST ONE RESPONSE BEFORE WE PROCESS THE MESSAGE. I THINK ; THAT THIS IS TERRIBLE CODE, BUT I'M AT A LOSS AS TO HOW TO ; HANDLE THIS PROBLEM. PUSHJ P,PCBECK ;MAKE SURE THERE ARE "EMERGENCY" ; RESERVES AVAILABLE JRST NTSCN4 ;NO FREE MESSAGES JRST INCTNM ;ALL'S OK. GO PROCESS THE 'NEXT' MESSAGE. ; (INCTNM WILL RETURN TO NTSCN8) ;HERE IF A MESSAGE ARRIVES OUT OF ORDER OR FOR SOME REASON (NO CORE ; OR NO FREE MESSAGE NUMBERS) WE CAN'T PROCESS IT NTSCN4: MOVSI T1,(POINT 18,0,17);CONJURE UP A POINTER TO THE HRRI T1,NDBQUE(W) ;THE LH OF NDBQUE S0PSHJ ENQPCB ;RE-QUEUE THE PCB (BUT NOT THE NDB) ; PJRST NTSCN5 ;"FINISH" UP WITH ANY SPECIAL MESSAGES ;HERE WHEN WE ARE DONE PROCESSING MESSAGES FROM NDBQUE. (EITHER ; BECAUSE THERE ARE NO MORE, WE ARE OUT OF CORE, NO FREE MESSAGE ; NUMBERS, OR THERE ARE OUT OF ORDER MESSAGES). THIS CODE CHECKS ; TO SEE IF ANY SPECIAL (ACK, NAK) MESSAGES ARE REQUIRED. NTSCN5: MOVEI T1,NETNDB## ;FIRST CHECK TO MAKE SURE THAT WE AREN'T CAIN T1,(W) ; TRYING TO SEND ANY ACK'S, NAK'S OR REP'S JRST NTSCN7 ; TO OURSELF. MOVSI T1,NDB.XN ;GET THE 'NEED TO NAK' FLAG TDNN T1,NDBFLG(W) ;SKIP IF WE DO. JRST NTSCN6 ;NOPE. DON'T NEED TO PUSHJ P,NCSNAK ;TRY TO SEND THE NAK JRST NTSCN6 ;WE CAN'T. TRY LATER MOVSI T1,NDB.XN ;GET THE FLAG BACK ANDCAM T1,NDBFLG(W) ;AND CLEAR IT NTSCN6: AOS NTFACK ;SET FLAG SAYING WE MAY NEED TO ACK IN JIFFY NTSCN7: JRST NTSC.N ;NOW GO DO THE NEXT NODE ;HERE ON RETURNS FROM INCTUN AND INCTNM AS WELL AS FROM VARIOUS ; PLACES IN NTSC.N. FLUSH THE PCB (IF ANY) AND ATTEMPT TO PROCESS ; THE NEXT MESSAGE IN ORDER. NTSCN8: S0PSHJ RMVPCB ;RETURN THE PCB AOS NTFACK ;SIGNAL THAT AN ACK IS PENDING JRST NTSCN1 ;GET NEXT ITEM OFF OF LH(NDBQUE) ;HERE FOR ONCE PER SECOND PROCESSING NTSC.S: SETZM NTQSEC ;CLEAR THE SECOND FLAG ;HERE TO SCAN THE NDB'S MOVEI W,NETNDB## ;FIRST NODE TO CHECK JRST NTSCS6 ;JUMP IN TO MIDDLE (DON'T DO NETNDB!) NTSCS2: HRRZ T1,NDBFEK(W) ;GET FEK FOR THIS NODE HLL J,NDBFLG(W) ;CARRY THE BITS IN LH(J) TLNE J,NDB.UP ;IS THIS NODE UP JRST NTSCS4 ;IF SO, JUMP OFF AND CHECK IT LDB T1,NDBTIM ;GET THE TIMER SOJG T1,NTSCS3 ;COUNT IT DOWN. JUMP IF NOT TIME FOR START PUSHJ P,NCSSSM ;SEND EITHER A START OR STACK TDZA T1,T1 ;START DIDN'T GO. DON'T RESET TIMER MOVEI T1,^D15 ;SET TIMER FOR 15 SECONDS NTSCS3: DPB T1,NDBTIM ;PUT THE NEW VALUE BACK FOR NEXT TIME JRST NTSCS8 ;GO PROCESS THE NEXT NDB ;HERE NODE IS UP. CHECK VARIOUS THINGS NTSCS4: MOVE T1,LOCSEC## ;GET A TIMER TLNN J,NDB.CF ;HAVE WE GOT A CONFIG YET? TRNE T1,7 ;NO, TIME TO ASK?(EVERY 8 SECONDS) CAIA ;YES, OR NOT TIME YET PUSHJ P,NCSRCF ;IF NOT, LETS ASK HIM FOR ONE JFCL ;IF NO CORE, WE'LL GET IT LATER ;NEIGHBORS TLNN J,NDB.NB ;HAS HE HEARD ABOUT OUR FRIENDS PUSHJ P,NCSNBN ;LET'S TELL HIM WHO'S NEXT DOOR JFCL ;BIT DIDN'T GET CLEARED. WE'LL GET IT LATER ;REP CHECK HRRZ T1,NDBQUE(W) ;ANY MESSAGES WAITING FOR AN ACK?? JUMPE T1,NTSCS5 ;IF NONE, DON'T REP, BUT CLEAR TIMER LDB T1,NDBTIM ;GET THE NODE'S REP TIMER CAIG T1,^D10 ;NO ACKS IN 10 SECONDS?? AOJA T1,NTSCS5 ;IF IT HASN'T BEEN THAT LONG, JUST MARK TIME PUSHJ P,NCSREP ;OTHERWISE TRY TO SEND THE REP SKIPA T1,[^D10] ;IF IT DIDN'T GO, THEN DON'T RESET TIMER SETZ T1, ;OTHERWISE SET TIMER TO ZERO NTSCS5: DPB T1,NDBTIM ;STORE TIMER BACK, ;NAK CHECK HLRZ T1,NDBQUE(W) ;GET POINTER TO 'OUT OF ORDER' MESSAGES JUMPE T1,NTSCS6 ;IF NONE, THEN THIS CHECK IS COMPLETE MOVEI T2,1 ;THIS IS THE COUNT OF QUEUED INPUT MSGS HRRZ T1,PCBBLK(T1) ;STEP TO THE NEXT MESSAGE, SKIPE T1 ;SKIP IF THERE ISN'T ONE AOJA T2,.-2 ;IF THERE IS ONE, COUNT IT AND RECURSE. CAIL T2,^D5 ;ARE THERE MORE THAN 5 MSGS QUEUED? PUSHJ P,NCSNAK ;IF SO, WE PROBABLY LOST ONE, SEND A NAK JFCL ;IGNORE ERROR RETURN NTSCS6: PUSHJ P,STCSEC ;GO TIME OUT STATION CONTROL MSGS FOR ; THIS NODE. NTSCS8: HRRZ W,NDBNNM(W) ;STEP TO THE NEXT NODE. JUMPN W,NTSCS2 ;IF THERE IS A NEXT NODE, GO PROCESS IT PUSHJ P,PCBSEC ;LET PCB MANAGEMENT CONTROL FREE-LISTS PUSHJ P,TSKSEC## ;CALL TSKSER ONCE/SEC FOR ITS STORAGE MGT PUSHJ P,NMCSEC## ;CALL NETMCR ONCE/SEC SO IT CAN DO ITS AUTO- ; DISCONNECT TIMING POPJ P, ;DONE WITH ONCE/SECOND STUFF. BACK TO TOP LEVEL SUBTTL CHKNCA - CHECK ACK'S FOR MESSAGE SENT BUT BEING HELD ;SUBROUTINE CHKNCA - DELETE MESSAGES THAT HAVE ACK'S ;CALL MOVEI W,NDB ; MOVEI T1,NCA - MESSAGE ACK NUMBER ;RETURN CPOPJ CHKNCA: ;ENTRY JUMPE W,CPOPJ## ;EXIT IN NDB UNDEFINED LDB T2,NDBLMS ;GET THE LAST MESSAGE SENT SUB T2,T1 ; AND MAKE SURE THAT WE ARE LESS TRNE T2,200 ; THAN IT. IF NOT, THEN THIS POPJ P, ; IS A VERY OUT-OF-DATE ACK LDB T2,NDBLAP ;GET THE LAST ACK PROCESSED, AND SUB T2,T1 ; SEE IF THIS ACK IS .GT. THE OLD TRNN T2,200 ;IF "LAP" IS .GE. THIS ACK, POPJ P, ; THEN THIS ACK IS OLD NEWS DPB T1,NDBLAR ;SAVE THE LAST ACK RECEIVED PUSH P,U ;SAVE THE OLD PCB CHKNC1: LDB T2,NDBLAP ;GET THE LAST ACK PROCESSED LDB T1,NDBLAR ;GET LAST ACK RECEIVED CAIN T1,(T2) ;IF LAP = LAR, THEN WE'VE PROCESSED ALL JRST UPOPJ ; IF PROCESSED ALL, RESTORE "U" AND EXIT ADDI T2,1 ;GET NUMBER OF NEXT ACK TO PROCESS ANDI T2,377 ; JUST 8 BITS OF IT PLEASE HRRZ U,NDBQUE(W) ;GET THE NEXT MESSAGE ON THE AWAIT ACK QUEUE JUMPE U,UPOPJ ; IF NONE, GET OUT WITHOUT UP-INT LAP LDB T1,PCBMSN ;GET THIS MESSAGE'S NUMBER CAIE T1,(T2) ; AND MAKE SURE IT IS THE ONE WE WANT JRST UPOPJ ;IF NEXT MSG IS AT FEK, GET OUT AND WAIT FOR ; IT TO COME BACK (WE MUSTA GOT NAK'ED) DPB T1,NDBLAP ;WE'RE GOING TO FREE THIS MSG, UPDATE LAP HRRZ T1,PCBBLK(U) ;GET ADDRESS OF NEXT MESSAGE ON THE QUEUE HRRM T1,NDBQUE(W) ; AND MAKE IT THE FIRST S0PSHJ RMVPCB ;FREE THIS MESSAGE SETZ T1, ;GET A ZERO, AND USE IT DPB T1,NDBTIM ; CLEAR THE REP TIMER. (THINGS SEEM TO BE OK) AOS NDBMOM(W) ;INDICATE ONE LESS MESSAGE OUTSTANDING JRST CHKNC1 ;GO TRY TO PROCESS ANOTHER ACK SUBTTL PROCESS NEXT MESSAGE INCTNM: ;HERE TO PROCESS A NUMBERED MESSAGE LDB T1,P1 ;GET THIS MESSAGE'S NUMBER BACK AGAIN DPB T1,NDBLMP ;SAVE LAST MESSAGE PROCESSED INCTUN: HLRZ T1,NTFLGS ;GET THE FLAGS ANDI T1,NCT.TP ;GET THE TYPE FIELD NTDBUG ;VERIFY THE INTERLOCK CAIE T1,NCT.ID ;IF THIS IS A NODE ID, OR CAIN T1,NCT.DM ; A DATA MESSAGE, THEN WE NEED NOT CHECK JRST INCTU1 ; FOR DUPLICATE NODES MOVEI T2,NETNDB## ;IF MSG IS START, STACK ETC. CAIE T2,(W) ; SEE IF NODE HAS THE SAME NUMBER AS US. JRST INCTU1 ; IF NOT, THEN EVERYTHING'S OK PUSHJ P,TYPILT ;TELL THE OPERATOR ABOUT THE ILLEGAL TOPOLOGY PUSHJ P,INCTBP ; AND THROW AWAY THE MESSAGE JRST INCTU2 ; AND DON'T PROCESS THE MESSAGE. INCTU1: HRRZS U ;CLEAR FLAG BITS SO U CAN BE USED IN SECTION 1 PUSHJ P,NCTTBL(T1) ;DISPATCH BY THE MESSAGE TYPE INCTU2: JRST NTSCN8 ;GO BACK FOR MORE STOPCD .,STOP,ANFNCT, ;++ NCT PROCESSORS SHOULDN'T SKIP ;NCL MESSAGE TYPE DISPATCH TABLE NCTTBL: JRST INCTDM ;(0) DATA MESSAGE JRST INCTAK ;(1) ACK JRST INCTNK ;(2) NAK JRST INCTRP ;(3) REP JRST INCTST ;(4) START JRST INCTSK ;(5) STACK JRST INCTID ;(6) NODE ID JRST INCTBP ;(7) ILLEGAL INPUT MESSAGE TYPE. FLUSH ;INCTID -- ROUTINE TO PROCESS THE NODE ID MESSAGE ;THIS ROUTINE READS THE NODE NUMBER FROM THE NODE ID MESSAGE. ; IF AN NDB DOES NOT YET EXIST FOR THE NODE ONE IS CREATED AND ; THE OPTIONAL DATA IS READ INTO IT. THEN TOPOLOGY IS ; RECOMPUTED. ; ;MESSAGE = NCT NCA NCN OPD(NNM SNM SID NIT [NIS] NVR) ;SO FAR = NCT.ID /$\ INCTID: PUSHJ P,SAVJW## ;WE SET UP A DIFFERENT "W" UNLIKE OTHER MSGS HLR J,PCBFEK(U) ;GET FEK POINTER FROM PCB IFN PARANOID&P$FEK,< ;MAKE SURE WE DON'T SEND OURSELF A NODE-ID HLLZ T1,FEKBLK(J) ;GET THE FLAGS FOR THE FEK TLNE T1,FK.NUL ;IS THIS A NULL FEK? STOPCD .,STOP,ANFNFK, ;++ THIS IS THE NULL FEK > ;NCA ILDB T1,P1 ;GET THE ACK START ;NCN ILDB T1,P1 ;GET THE STARTING MESSAGE NUMBER ;NNM PUSHJ P,EBI2BI ;READ THE NODE NUMBER HRRM T1,FEKNNM(J) ;STORE THE NODE NUMBER FOR THIS FEK PUSHJ P,SRCNDB ;DOES THE NODE EXIST JRST INCTI5 ;NO, NEED TO CREATE AN NDB PUSHJ P,SKOPDD ;YES, SKIP OVER OPD(,SNM,SID) FIELDS PUSHJ P,EBI2BI ;READ NODEID TYPE JUMPE T1,INCTI8 ;IF 0/BLANK, THEN NOT BROADCAST NODEID IFE FTENET, ;INVALID NODEID "TYPE" FIELD ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;HERE TO HANDLE PERIODIC BROADCAST NODEID MESSAGE IFN FTENET,< CAIE T1,NIT.BC ;ONLY OTHER LEGAL VALUE IS BROADCAST PJSP T1,INCTBD ;INVALID NODEID "TYPE" FIELD HLLZS FEKNNM(J) ;ETHERNET MASTER FEK'S DON'T HAVE A NEIGHBOR HLRZ T4,NDBNNM(W) ;GET NODE NUMBER TO MATCH SKIPA T2,J ;"LOCAL" START OF FEK CHAIN INCTI1: HRRZ T2,FEKBLK(T2) ;ADVANCE TO NEXT FEK IN CHAIN JUMPE T2,INCTI6 ;NO FEK??? TIME FOR A NEW TOPOLOGY! HRRZ T3,FEKNNM(T2) ;PICK UP NODE NUMBER CAIE T4,(T3) ;DOES THIS FEK BELONG TO THIS NODE? JRST INCTI1 ;NO, KEEP LOOKING MOVE T3,FEKBLK(T2) ;GET FEK FLAGS TLNE T3,FK.ETH ;ETHERNET FEK? CAME J,FEKNIF(T2) ;YES, SLAVED TO THE RIGHT MASTER? JRST INCTI1 ;NO (???), BROADCAST NODEID NOT FROM HERE MOVE J,T2 ;POSITION SLAVE FEK ADDRESS IN A SAFE PLACE PUSHJ P,EBI2BI ;READ IN NODEID SERIAL NUMBER CAMG T1,FEKNIS(J) ;MONOTONICALLY INCREASED? JRST INCTI3 ;NO, NODE MUST HAVE RESTARTED MOVEM T1,FEKNIS(J) ;YES, SET NEW SERIAL NUMBER MOVE T1,%NTNIJ ;TIMEOUT (OR KEEP-ALIVE) INTERVAL MOVEM T1,FEKNIT(J) ;RESET SLAVE FEK KEEP-ALIVE TIMER MOVE J,FEKNIF(J) ;RESTORE MASTER FEK ADDRESS POPJ P, ;AND JUST RETURN (NO TOPOLOGY CHANGE) ;HERE WHEN ETHERNET NCL PROTOCOL RESTARTED INCTI3: MOVEI T1,FF.DFK ;FUNCTION: DESTROY DEAD SLAVE FEK XCT FEKDSP(J) ;LET FEK SERVICE DO IT POPJ P, ;JUST RETURN (AWAIT ANOTHER NODEID) > ;END IFN FTENET INCTI5: PUSHJ P,MAKNDB ;MAKE A NODE DATA BLOCK POPJ P, ;NO CORE. DO IT LATER ;OPD PUSHJ P,RDOPDD ;READ THE OPTIONAL DATA POPJ P, ;NO CORE. LATER ;NIT PUSHJ P,EBI2BI ;READ NODEID "CLASS" JUMPE T1,INCTI8 ;IF POINT-TO-POINT (OR BLANK) THEN NORMAL FEK IFE FTENET, ;ONLY OTHER LEGAL VALUE IS "BROADCAST" IFN FTENET,< CAIE T1,NIT.BC ;ONLY OTHER LEGAL VALUE IS "BROADCAST" PJSP T1,INCTBD ;BAD MESSAGE INCTI6: MOVE T2,FEKBLK(J) ;GET FEK FLAGS TLNN T2,FK.ETM ;IS THIS AN ETHERNET MASTER FEK? PJSP T1,INCTBD ;NO (???) MOVEI T1,FF.CFK ;FUNCTION: CREATE NEW SLAVE FEK XCT FEKDSP(J) ;GET A NEW SLAVE FEK (ADDRESS IN T1) JUMPE T1,CPOPJ ;IGNORE IF NO CORE FOR NEW FEK HRRM T1,NDBFEK(W) ;LINK NDB TO SLAVE FEK HLRZ T2,NDBNNM(W) ;RETRIEVE NNM FROM NDB HRRM T2,FEKNNM(T1) ;AND SET SLAVE FEK NNM HLLZS FEKNNM(J) ;AND CLEAR IN MASTER FEK (SINCE NOT A NEIGHBOR) DMOVE T2,PCBNIA(U) ;GET REMOTE ETHERNET ADDRESS DMOVEM T2,FEKNIA(T1) ;AND SAVE IN THE NEW SLAVE FEK PUSHJ P,EBI2BI ;READ BROADCAST "SERIAL" NUMBER HRRZ T2,NDBFEK(W) ;RETRIEVE ADDRESS OF SLAVE FEK MOVEM T1,FEKNIS(T2) ;AND SET LATEST RECEIVED SERIAL NUMBER MOVSI T3,4 ;RESET THE MASTER BROADCAST NODEID TIMER MOVEM T3,FEKNIT(J) ;SO AS TO SEND A NODEID ASAP > ;END IFN FTENET INCTI8: PUSHJ P,EBI2BI ;READ REMOTE'S NCL VERSION NUMBER HRRM T1,NDBNVR(W) ;SAVE FOR LATER EXAMINATION SETOM NTQRCT ;WE MUST RECOMPUTE TOPOLOGY POPJ P, ;ALL DONE ;INCTST -- ROUTINE TO PROCESS A START MESSAGE. ; ;MESSAGE = NCT DNA SNA NCA NCN OPD(NNM SNM SID) NVR ;SO FAR = NCT.ST US HIM IGNORED IGNORED /$\ INCTST: IFN PARANOID&P$NDB, ;MAKE SURE THIS DIDN'T COME FROM US. ;NNM PUSHJ P,EBI2BI ;READ THE NODE NUMBER HLRZ T2,NDBNNM(W) ;GET WHAT SHOULD BE THE NODE NUMBER CAIE T1,(T2) ;BETTER BE THE SAME PJSP T1,INCTBD ;INCONSISTENT MESSAGE PUSHJ P,CLNNDB ;MAKE THIS NDB PRISTINE ;OPD PUSHJ P,RDOPDD ;READ THE OPTIONAL DATA POPJ P, ;NO CORE. DISMISS ;NVR PUSHJ P,EBI2BI ;READ NCL VERSION NUMBER HRRM T1,NDBNVR(W) ;SAVE FOR LATER USE PUSH P,U ;SAVE THE PCB POINTER MOVSI T1,NDB.SK ;GET THE "NOW-SENDING-STACKS" FLAG IORM T1,NDBFLG(W) ; TO TELL NCSSSM NOT TO SEND A START PUSHJ P,NCSSSM ;SEND A STACK BACK JFCL ;TRY LATER ;NOTE! THIS WILL MEAN THAT WE WON'T ;TRY TO OPEN THE CONNECTION FOR ANOTHER ;15 SECONDS OR UNTIL WE RECEIVE THE NEXT ;START. THIS MAY BE A "FEATURE". IF WE ARE ;THIS SHORT OF CORE WE MAY NOT WANT TO ;START ALL THIS RIGHT AWAY JRST UPOPJ## ;RESTORE THE PCB AND GO BACK FOR MORE ;SUBROUTINE INCTSK - STACK MESSAGE PROCESSOR ; ;MESSAGE = NCT DNA SNA NCA NCN OPD(NNM SNM SID) NVR ;SO FAR = NCT.SK US HIM IGNORED IGNORED /$\ INCTSK: ;ENTRY IFN PARANOID&P$NDB, ;MAKE SURE THIS DIDN'T COME FROM US. ;NNM PUSHJ P,EBI2BI ;READ THE NODE NUMBER HLRZ T2,NDBNNM(W) ;GET WHAT SHOULD BE THE NODE NUMBER CAIE T1,(T2) ;BETTER BE THE SAME PJSP T1,INCTBD ;INCONSISTENT MESSAGE ;OPD PUSHJ P,RDOPDD ;READ THE OPTIONAL DATA POPJ P, ;NO CORE. DISMISS ;NVR PUSHJ P,EBI2BI ;READ THE NCL VERSION NUMBER HRRM T1,NDBNVR(W) ;SAVE FOR LATER USE PUSH P,U ;SAVE THE INCOMING MESSAGE PUSHJ P,NCSACK ;ACK THE STACK JFCL ;IF WE CAN'T, THEN SOME-ONE WILL TIME OUT POP P,U ;GET THE INCOMING MESSAGE BACK (TO FREE IT) PJRST NODEUP ;SIGNAL THAT THE NODE IS UP ;SUBROUTINE INCTAK - ACK MESSAGE PROCESSOR ; ;MESSAGE = NCT DNA SNA NCA NCN ;SO FAR = NCT.AK US HIM CHECKED IGNORED /$\ INCTAK: ;ACK'S HAPPEN IN NETSCN IFN PARANOID&P$NDB, ;MAKE SURE THIS DIDN'T COME FROM US. POPJ P, ;GO BACK FOR MORE ;SUBROUTINE INCTNK NAK MESSAGE PROCESSOR ; ;MESSAGE = NCT DNA SNA NCA NCN ;SO FAR = NCT.NK US HIM CHECKED IGNORED /$\ INCTNK: ;RE-SEND ALL UN-ACKED MESSAGES IFN PARANOID&P$NDB, ;MAKE SURE THIS DIDN'T COME FROM US. PUSHJ P,SAVE1 ;SAVE P1 PUSHJ P,SAVJW## ;WE'LL CLOBBER J TOO PUSH P,U ;SAVE THE PCB HRRZ P1,NDBQUE(W) ;GET THE POINTER HLLZS NDBQUE(W) ;NO LONGER WAITING FOR ACK JUMPE P1,INCTN2 ;EMPTY INCTN1: MOVEI U,(P1) ;GET THE NEXT ENTRY HRRZ P1,PCBBLK(U) ;GET THE NEXT ENTRY HLLZS PCBBLK(U) ;BREAK ANY FORWARD LINK NETOFF ;FRCWRT WILL TURN IT BACK ON ... PUSHJ P,FRCWRT ;RESEND THE MESSAGE (SEE FOOTNOTE) JUMPN P1,INCTN1 ;CHECK FOR THE END INCTN2: JRST UPOPJ ;RESTORE U AND RETURN ;SUBROUTINE INCTRP REP PROCESSOR ; ;MESSAGE = NCT DNA SNA NCA NCN ;SO FAR = NCT.RP US HIM CHECKED IGNORED /$\ INCTRP: ;PROPER RESPONSE TO A REP IS TO DELETE ; ALL OUT OF ORDER MESSAGES ON NDBQUE IFN PARANOID&P$NDB, ;MAKE SURE THIS DIDN'T COME FROM US. MOVSI T1,NDB.XN ;GET THE 'NEED TO NAK' FLAG IORM T1,NDBFLG(W) ;AND SET IT. THIS WILL CAUSE POPJ P, ;THE NAK TO GO OUT SOON ENOUGH. ; NCSNAK WILL REMOVE OUT OF ORDER ; MESSAGES FROM NDBQUE. ;***FOOTNOTE*** COMMENT\ It is assumed that any message on the right half of NDBQUE has already been given to NETWRT once before. If this is the case, then we do not need to set up CACHE information by a call to PCBMRK \ ;SUBROUTINE RDOPDD - READ THE SOFTWARE ID FIELDS ; ;MESSAGE = ??? ... ??? OPD(NNM SNM SID) ;SO FAR = READ /$\ ; ;RETURN CPOPJ ;NO CORE ; CPOPJ1 ;W=NDB RDOPDD: ;ENTRY ;SNM MOVEI P2,NDBSN2(W) ;POINTER TO STATION NAME HRLM P2,NDBSNM(W) ;SALT IT AWAY RDOPD1: PUSHJ P,EAS2SX ;COPY THE STATION NAME MOVEM T1,(P2) ;STORE THE STATION NAME ;SID HRRZ P2,NDBSID(W) ;CHECK FOR A POINTER JUMPN P2,RDOPD2 ;YES PUSHJ P,EAS2AZ ;SEE IF WE NEED SPACE FOR THE NAME JRST RDOPD3 ;NO, DON'T GET IT MOVEI T2,^D8 ;YES, ALLOCATE SPACE FOR THE SYSTEM NAME PUSHJ P,GETZWD ;FROM FREE CORE POPJ P, ;NO CORE HRRM T1,NDBSID(W) ;STORE THE POINTER MOVEI P2,(T1) ;GET THE POINTER RDOPD2: PUSHJ P,EAS2AS ;COPY THE SYSTEM NAME RDOPD3: HLRZ P2,NDBSID(W) ;GET THE POINTER JUMPN P2,RDOPD4 ;JUMP IF ONE PUSHJ P,EAS2AZ ;SEE IF NEED SPACE FOR DATE JRST RDOPD5 ;NO, DON'T GET IT MOVEI T2,^D8 ;YES, ALLOCATE SPACE FOR THE CREATION DATE PUSHJ P,GETZWD ;FROM FREE CORE POPJ P, ;NO SPACE HRLM T1,NDBSID(W) ;STORE THE POINTER MOVEI P2,(T1) ;COPY THE POINTER RDOPD4: PUSHJ P,EAS2AS ;COPY THE CREATION DATE RDOPD5: PJRST CPOPJ1## ;EXIT ;SKOPDD - LIKE RDOPDD BUT JUST DISCARDS THE OPD FIELDS SKOPDD: PUSHJ P,EAS2SX ;SLURP UP THE SNM "STATION NAME" FIELD ; PUSHJ P,EAS2AZ ;IS THERE A SID "SYSTEM NAME" FIELD? ; CAIA ;NO PUSHJ P,XSKIP ;YES, SLURP IT UP ; PUSHJ P,EAS2AZ ;IS THERE A SID "SYSTEM DATE" FIELD? ; CAIA ;NO PUSHJ P,XSKIP ;YES, SLURP IT UP POPJ P, ;OPD(...,SNM,SID) READ AND DISCARDED SUBTTL INCTDM - PROCESS OF NUMBERED CONTROL/DATA MESSAGES ;MESSAGE = NCT DNA SNA NCA NCN DLA ... ;SO FAR = NCT.DM US HIM CHECKED CHECKED /$\ INCTDM: ;DLA PUSHJ P,EBI2BI ;GET THE DESTINATION LINK ADDRESS JUMPN T1,IDCCTL ;DEVICE CONTROL MESSAGE ;HERE TO PROCESS NUMBERED CONTROL MESSAGES. THESE HAVE A DLA OF ZERO ; AND HENCE ARE NODE (AS OPPOSED TO DEVICE) CONTROL ; ;MESSAGE = NCT DNA SNA NCA NCN DLA (CNT CM)* ;SO FAR = NCT.DM US HIM ACK NUMBER 0 /$\ ; NCTDSP: JUMPE P4,CPOPJ## ;IF WE'RE AT END-OF-MESSAGE, RETURN ;CNT PUSHJ P,EBI2BI ;GET THE LENGTH OF THE SUB MESSAGE SUB P4,T1 ;P4 := THE LENGTH OF THE REST OF THE MSG SKIPGE P4 ;MAKE SURE THAT SUB-MSG NOT LONGER THAN PJSP T1,INCTBD ; THE REST OF THE MESSAGE. IF SO, ERROR PUSH P,P4 ;SAVE THE LENGTH OF THE "REST" OF THE MSG MOVE P4,T1 ;SET THE LENGTH (P4) TO BE THAT OF THIS SUB-MSG ;CM PUSHJ P,EBI2BI ;GET THE CONTROL MESSAGE TYPE CAILE T1,NC.MAX ;RANGE CHECK IT PJSP T1,NCTDS1 ;GIVE "BAD MESSAGE" IF TYPE OUT OF RANGE AOS NCLRMT(T1) ;COUNT THE MESSAGE TYPE PUSHJ P,[SE1ENT ;CALL IN SECTION 1 PJRST ICMTBL(T1)] ;DISPATCH TO THE MESSAGE PROCESSOR PJSP T1,NCTDS1 ;IF ERROR, RESTORE P4 AND POST BAD MSG JUMPG P4,[PUSHJ P,XSKIP ;MAKE SURE ALL OF SUB MSG WAS JRST .] ; PROCESSED POP P,P4 ;GET LENGTH OF THE REMAINDER OF THE MESSAGE JRST NCTDSP ; AND GO PROCESS IT. NCTDS1: POP P,P4 ;RESTORE P4 (JUST TO CLEAN UP STACK) PJRST INCTBD ;GO MARK THE MESSAGE AS BAD ICMTBL: JRST CPOPJ## ;<0> IS ILLEGAL JRST ICMCNT ;<1> CONNECT JRST ICMDSC ;<2> DISCONNECT JRST ICMNBN ;<3> NEIGHBOURS JRST ICMRCF ;<4> REQUEST CONFIGURATION JRST ICMCNF ;<5> CONFIGURATION JRST ICMDRQ ;<6> DATA REQUEST JRST ICMCTL ;<7> STATION CONTROL COMMENT @ Connect and Disconnect message processing For each device an attempt is made to call the device driver for all the connect/disconnect messages. Here follows a list of the actions taken for the three connect related messages. Connect initiate. This is the complicated one. There are two phases to the processing of the connect initiate message. 1) The Link Address Table (LAT) is scaned for devices in the LAT.CI (waiting for connect) state. When a device is found in that state it is called on its NDPCNI dispatch entry in in the following context. P3 := XWD SLA,OBJ (As read from the message) P1, P4 := Pointer to the first byte after the "OBJ" field. If the driver does not wish to accept the connect it should CPOPJ. If it accepts the connect it should skip return. (Note. The driver MUST respect the P's) 2) If no driver is found to accept the connect, a default connect processor is called. (Via the DCITAB entry indexed by the ofject type of the incoming message.) This default handler is called in the same context as above. If it accepts the connect, it should skip return. If the connect is to be rejected, the handler should CPOPJ return with T1 containing the reason for failure. (RSN field of a disconnect message) Connect confirm The driver will be called via the NDPCNC vectored entry. in the the following context. P1, P4 := Point to the "SLA" byte of the message. A skip return accepts the message. A non-skip return says that the message was bad. (The message will then be given to INCTBD) Disconnect Same as connect confirm, except that the NDPDSC entry is used. Note. When ever any of the NDP vectored connect/disconnect entries are taken the registers P1 and P4 (message pointer and count) will point to the DLA. In other words, the first item to be read by the service routine will be the DLA. @ ;ICMCNT ROUTINE TO PROCESS INCOMING CONNECT MESSAGE. ;CALLED WITH ; P1 := POINTER TO THE DLA SECTION OF THE MESSAGE ; P4 := COUNT OF THE BYTES LEFT IN THE MESSAGE ; W := POINTER TO THE NDB OF THE NODE THAT SENT THIS MESSAGE ;RETURN CPOPJ1 ;WITH EITHER THE DEVICE ACCEPTING THE MESSAGE, ; ; OR A CONNECT REJECT BEING SENT ; ICMCNT:: ;HERE TO PROCESS AN INCOMING CONNECT. ;DLA PUSHJ P,EBI2BI ;GET THE DESTINATION JUMPN T1,ICMCNC ; IF NONZERO, GO PROCESS CONNECT CONFIRM ;SLA PUSHJ P,EBI2BI ;GET THE SOURCE HRLZI P3,(T1) ;COPY IT INCASE WE DECIDE TO DISCONNECT. ;DPN(OBJ) PUSHJ P,EBI2BI ;GET THE DESTINATION OBJECT TYPE. HRRI P3,(T1) ;COPY THE OBJECT TYPE FOR INDEXING ETC. ;LOOP THROUGH NETLAT LOOKING FOR A DDB WAITING FOR THIS CONNECT MOVSI P2,-LATLEN## ;MAKE AN AOBJN POINTER TO THE NETLAT TABLE. ICMCN1: AOBJP P2,ICMCN3 ;NO DDB'S WERE INTERESTED, TRY DEFAULT CI'ER LDB T3,LATSP2 ;GET THE STATE OF THIS LAT ENTRY CAIE T3,LAT.CI ;IS IT CONNECT INITIATE? JRST ICMCN1 ;IF NOT, TRY THE NEXT ENTRY LDB F,LATPP2 ;GET DDB ADDRESS FROM LAT ENTRY MOVEI T1,NDPCNI ;SET UP TO USE THE CONNECT INITIATE ENTRY. MOVE T2,NETLAT(P2) ;WANT NETLAT FLAGS IN T2 PUSHJ P,ICMNDP ;DO THE DISPATCH JRST ICMCN1 ;FAILURE, TRY THE NEXT ONE JRST CPOPJ1## ; SUCCESS. GIVE GOOD RETURN ;HERE WHEN NO LAT ENTRIES WANTED THE CONNECT. CALL THE DEFAULT HANDLER. ICMCN3: MOVEI T1,(P3) ;GET JUST THE OBJECT TYPE. CAIL T1,0 ;RANGE CHECK IT BEFORE CAILE T1,OBJ.MX ; WE TRY USING IT FOR AN INDEX. JRST ICMCN6 ;OUT OF RANGE. SEND A DISCONNECT. SKIPN T2,NDTTAB##(T1) ;ADDRESS OF OBJECT-TYPE'S NDT JRST ICMCN6 ;NONE - WE DON'T HANDLE THAT TYPE HRRZ T2,NDTCNI(T2) ;DISPATCH ADDRESS SE1ENT ;CALL IN SECTION 1 PUSHJ P,(T2) ;PROCESS THE INCOMING CONNECT INIT JRST ICMCN7 ; REJECTED, REASON CODE IS IN T1 JRST CPOPJ1## ;IT LIKED IT. GIVE A GOOD RETURN ICMCN6: MOVEI T1,RSN.OT ;OBJECT TYPE NOT AVAILABLE ; NETPRM WOULD SEEM TO PREFER RSN.NP AS A MORE ; APPROPRIATE RESPONSE - HOWEVER, RSN.OT IS WHAT ; WAS RETURNED IN PREVIOUS MONITORS, AND I DON'T ; HAVE THE COURAGE TO CHANGE IT . . . ICMCN7: ;HERE TO SEND REJECT (DISCONNECT) RSN IN T1. ; (WE CAN'T USE NCSDSC SINCE WE HAVE NO DDB) PUSH P,U ;PROTECT OUR INCOMING MESSAGE HLL T1,P3 ;PUT SLA (NOW DLA) IN LH OF REASON CODE PUSH P,T1 ;SAVE "XWD SLA,RSN" EMRGCY ;SET GLOBAL FLAG TO USE "EMERGENCY" MEMORY PUSHJ P,NCMHDR ;GET A MESSAGE WITH A CONTROL HEADER WRITTEN STOPCD .,STOP,ANFCGM, ;++ CAN'T GET MESSAGE. NETSCN SHOULD CHECK ;TYP MOVEI T1,NC.DSC ;THIS IS A DISCONNECT MESSAGE PUSHJ P,BI2EBI ;WRITE THE CONTROL MESSAGE TYPE ;DLA HLRZ T1,-1(P) ;RECOVER THE SLA (NOW DLA) FROM THE STACK PUSHJ P,BI2EBI ;WRITE THE DLA ;SLA SETZ T1, ;WE NEVER ASSIGNED A SLA PUSHJ P,BI2EBI ;WRITE THE SLA ;RSN HRRZ T1,-1(P) ;RECOVER THE RSN FROM THE STACK PUSHJ P,BI2EBI ;WRITE THE REASON FOR THE DISCONNECT. JSP T1,NETWRC ;SEND THE MESSAGE POP P,(P) ;THROW AWAY THE TEMP ON THE STACK PJRST UPOPJ1## ;RESTORE OUR PCB AND SKIP RETURN ;DUMMY CONNECT INIT PROCESSOR FOR THOSE OBJECTS WHICH DON'T ACCEPT A CI. NJNKCI::MOVEI T1,RSN.OT ;JUNK CI (FAKE WITH NO SUCH OBJECT TYPE) POPJ P, ;"ERROR" RETURN TO ICMCN7 ;ICMCNC ROUTINE TO PROCESS CONNECT CONFIRM MESSAGES. ;CALL AS WITH ICMCNT, EXCEPT THAT P1 AND P4 HAVE BEEN PUSHED ; AND T1 := THE DLA ICMCNC: PUSHJ P,ICMLAF ;GET LAT'S ADDRESS AND FLAGS POPJ P, ;SLA OUT OF RANGE, OR UNASSIGNED MOVEI T1,NDPCNC ;CONNECT CONFIRM DISPATCH ENTRY PJRST ICMNDP ;GO DO THE DISPATCH ;ICMDSC ROUTINE TO HANDLE THE DISCONNECT. ;CALLED WITH ; P1 := BYTE POINTER TO THE DLA OF THE DISCONNECT ; P4 := LENGTH OF THE REST OF THE DISCONNECT MESSAGE ; W := POINTER TO THE NDB OF THE NODE THAT SENT THIS MESSAGE. ;RETURN CPOPJ ;DLA WRONG. MESSAGE IS BAD ; CPOPJ1 ;MESSAGE PROCESSED OK. ICMDSC: PUSHJ P,ICMLAE ;GET LAT'S ADDRESS AND FLAGS POPJ P, ;SLA OUT OF RANGE, OR UNASSIGNED MOVEI T1,NDPDSC ;DISCONNECT VECTOR ENTRY PJRST ICMNDP ;GO DO THE DISPATCH ;SUBROUTINE ICMNBN - NEIGHBOURS ICMNBN: ;ENTRY IFN PARANOID&P$NDB, ;MAKE SURE THIS DIDN'T COME FROM US. PUSHJ P,SAVJW## ;WE WILL CLOBBER THESE PUSH P,U ;SAVE THE PCB MOVE T4,[POINT 9,NDBTOP(W)] ;GET A POINTER TO THE NEIGHBORS TABLE MOVEI T3,NGHMAX ;COUNT OF MAXIMUM NUMBER OF NEIGHBORS. ICMNB1: JUMPLE P4,ICMNB2 ;IF WE'VE READ ALL THE MSG, CLEAR UNUSED SLOTS PUSH P,T3 ;SAVE T3 (EBI2BI CLOBBERS IT) PUSHJ P,EBI2BI ;GET THE NODE NUMBER OF THE NEXT NEIGHBOR IDPB T1,T4 ;PUT IT IN THE TABLE PUSHJ P,EBI2BI ;GET THE COST OF THAT LINK IDPB T1,T4 ;AND PUT THAT IN THE TABLE TOO POP P,T3 ;GET OUR COUNTER BACK SOJG T3,ICMNB1 ;LOOP OVER THE ENTIRE MESSAGE JRST ICMNB4 ;TOO MANY NEIGHBORS. THINGS WILL GET SCREWED. ICMNB2: SETZ T1, ;ZERO THE UNUSED PART OF THE TABLE ICMNB3: IDPB T1,T4 ;CLEAR THE NODE NUMBER IDPB T1,T4 ;CLEAR THE COST SOJG T3,ICMNB3 ;LOOP OVER ALL UNUSED SLOTS ICMNB4: PUSHJ P,RCMPTP ;RECOMPUTE THE TOPOLOGY, PJRST UPOPJ1 ;AND GIVE SUCCESSFUL RETURN ;SUBROUTINE ICMRCF - REQUEST CONFIGURATION ICMRCF: ;ENTRY IFN PARANOID&P$NDB, ;MAKE SURE THIS DIDN'T COME FROM US. PUSH P,U ;SAVE THE PCB PUSHJ P,NCSCNF ;SEND THE CONFIGURATION MESSAGE JFCL ;LATER PJRST UPOPJ1## ;EXIT ;SUBROUTINE ICMCNF - CONFIGURATION ICMCNF: ;ENTRY IFN PARANOID&P$NDB, ;MAKE SURE THIS DIDN'T COME FROM US. PUSHJ P,PSINTC## ;INFORM OF TOPOLOGY (CONFIG) CHANGE MOVSI T1,NDB.CF ;GET THE CONFIGURATION BIT IORM T1,NDBFLG(W) ;SEEN THE CONFIGURATION MESSAGE ZZ==0 REPEAT +1,< SETZM NDBDEV+ZZ(W) ;CLEAR THE DEVICE TABLE(S) ZZ==ZZ+1 >;END OF REPEAT JUMPE P4,CPOPJ1## ;NO DEVICES PUSH P,P2 ;SAVE P2 ICMCF1: ;OBJ PUSHJ P,EBI2BI ;GET THE OBJECT TYPE CAILE T1,OBJ.MX ;CHECK THE RANGE JRST [POP P,P2 ;RESTORE P2 POPJ P,] ;DISMISS THE MESSAGE MOVEI P2,(T1) ;COPY THE DEVICE NUMBER ;NDEV PUSHJ P,EBI2BI ;GET THE COUNT DPB T1,NETCNF##(P2) ;STORE NEW COUNT ;PID ICMCF3: PUSHJ P,EAS2SX ;GET THE PID JUMPG P4,ICMCF1 ;CONTINUE THROUGH THE MESSAGE POP P,P2 ;RESTORE P2 JRST CPOPJ1## ;SUBROUTINE ICMDRQ - DATA REQUEST ICMDRQ: PUSHJ P,ICMLAE ;GET LAT'S ADDRESS AND FLAGS POPJ P, ;SLA OUT OF RANGE, OR UNASSIGNED ;DRQ PUSH P,T2 ;SAVE NETLAT FLAGS PUSHJ P,EBI2BI ;GET THE REQUEST COUNT MOVE T4,T1 ;COPY THE DRQ COUNT FOR THE DRIVER TO HANDLE POP P,T2 ;RESTORE NETLAT FLAGS MOVEI T1,NDPDRQ ;GET THE DATA REQUEST VECTOR OFFSET PJRST ICMNDP ;GO DO THE DISPATCH ;*** FOOTNOTE *** COMMENT @ The Data Request Dispatch is much like the connects. When the driver is called, the following registers are set up. U := The PCB P1, P4 := Pointer to the byte after the "DRQ" (Should be after the end of the message T4 := The Data Request Count. The driver should normally return with a "CPOPJ1" indicating a good return, but if for some reason he thinks that he is being screwed by the remote, he may "CPOPJ" return (error) and the message will be INCTBD'ed @ ;ICMCTL ROUTINE TO PROCESS INCOMING STATION CONTROL MESSAGES ;CALL W := NDB POINTER ; P1 := POINTER TO THE FIRST BYTE OF THE STC MESSAGE ; P4 := LENGTH OF THE STC MESSAGE ;RETURN CPOPJ ;IF SOMETHING'S "WRONG" WITH THE MESSAGE ; CPOPJ1 ;WITH STC-BLOCK ON "NDBICT" OF THE NDB ; ;ACTION THIS ROUTINE COPIES THE INCOMING STATION CONTROL MESSAGE ; INTO AN "STC" BLOCK. IT QUEUES THIS OFF OF THE "NDBICT" ; WORD OF THE NDB. IF THERE IS A JOB WAITING FOR A STATION ; CONTROL RESPONSE ("NDBSTC" /= 0) THEN THAT JOB IS AWOKEN. ; OTHERWISE NETLDR IS STARTED. ICMCTL: SKIPE NDBICT(W) ;SEE IF THERE IS A MESSAGE ALREADY WAITING RETSKP ; IF MSG ALREADY THERE, JUST TOSS THIS ONE JUMPLE P4,CPOPJ## ;MAKE SURE IT HAS DATA, "BAD MSG" IF NOT PUSHJ P,SAVJW## ;SAVE J (STC-BLOCK) AND W MOVEI T1,(P4) ;COPY THE LENGTH OF THE STC MESSAGE PUSHJ P,GETSTC ;GET A STC-BLOCK TO HOLD IT RETSKP ; IF NO CORE, TOSS MSG BUT SAY MSG IS "OK" MOVE T2,[POINT 8,STCDAT(J)] ;GET A BYTE POINTER TO STC DATA PART ICMCT1: ILDB T1,P1 ;LOOP OVER ALL STC-MESSAGE BYTES IDPB T1,T2 ; STORING THEM IN THE STC-BLOCK SOJG P4,ICMCT1 ; P4 HAS COUNT OF BYTES HRLI J,^D10 ;GET A 10 SECOND TIMER, AND QUEUE THIS STC MOVEM J,NDBICT(W) ;MESSAGE ON THE NDB. IF NO ONE READS IT ; IN 10 SECONDS, IT WILL SELF-DESTRUCT SKIPE T1,NDBSTC(W) ;SEE IF ANYONE IS WAITING FOR A MESSAGE JRST [HRRZS T1 ; ONLY WANT JOB NUMBER, THROW AWAY TIMER AOS (P) ; IF SOMEONE'S WAITING, INDICATE MESSAGE "OK" PJRST EWAKE##] ; WAKE THE USER AND SKIP RETURN PUSHJ P,FRCNLD ;IF NO ONE EXPECTING IT, START UP NETLDR ; IN HOPES THAT IT WILL WANT IT. RETSKP ;GIVE GOOD RETURN (ACCEPTING THE MESSAGE) SUBTTL ICM HELPERS ;ICMLAE/ICMLAF - READ IN LINK ADDRESS AND SETUP FOR ICMNDP DISPATCH ;CALL P1/P4 SETUP ; PUSHJ P,ICMLAE/F ; ERROR RETURN ; NORMAL RETURN ; ;ON ERROR RETURN THE LINK ADDRESS WAS OUT OF RANGE, OR UNASSIGNED ; ;ON NORMAL (SKIP) RETURN, THE NETLAT FLAGS ARE IN T2 AND THE ;LINK ADDRESS DDB/LDB ADDRESS IS IN F ICMLAE: PUSHJ P,EBI2BI ;READ IN DESTINATION LINK ADDRESS ICMLAF: CAIL T1,0 ;RANGE CAILE T1,LATLEN## ; CHECK POPJ P, ;LINK ADDRESS OUT OF RANGE SKIPN T2,NETLAT(T1) ;GET NETLAT ENTRY POPJ P, ;UNASSIGNED LDB F,LATPTR ;GET DDB/LDB ADDRESS JRST CPOPJ1## ;SUCCESSFUL RETURN ;ICMNDP ROUTINE TO PERFORM THE NETWORK DISPATCH ;CALL MOVEI T1,DISPATCH FUNCTION ; MOVE T2,NETLAT FLAGS (LAT.TY, LAT.VT) ; MOVE F, DDB/LDB ADDRESS ; PUSHJ P,ICMNDP ;RETURN PJRSTS TO THE ROUTINE SPECIFIED ICMNDP: JUMPGE T2,ICMND2 ;DISPATCH FOR DDBS TLNN T2,LAT.VT ;IF IT'S A LDB, SEE IF IT'S A VTM JRST @MCRNDP##(T1) ;IF IT'S NOT A VTM, USE MCR DISPATCH JRST @TTYNDP##(T1) ;USE NETWORK VIRTUAL TERMINAL DISPATCH ICMND2: HLRZ T2,DEVNET(F) ;GET ADDRESS OF DEVICE SERVICE "NDP" DISPATCH S0JRST @NDTNDP(T2) ;THEN USE THE DEVICE DISPATCH VECTOR SUBTTL IDCCTL - INPUT DEVICE CONTROL (DAP) MESSAGE PROCESSOR ;SUBROUTINE IDCCTL - DEVICE CONTROL (DAP) MESSAGES ;CALL MOVEI U,PCB ; MOVEI W,NDB ; MOVEI T1,DLA ; PUSHJ P,IDCCTL ;RETURN POPJ P, IDCCTL: NTDBUG ;VERIFY THE INTERLOCK ;DLA CAIL T1,0 ;RANGE CAILE T1,LATLEN## ; CHECK DLA PJSP T1,INCTBD ;BAD LINK ADDRESS LDB F,LATPTR ;GET DDB/LDB ADDRESS MOVE T2,NETLAT(T1) ;AND NETLAT FLAGS TLNN T2,LAT.TY ;DDB OR LDB? SKIPA T3,DEVNET(F) ;DDB, GET NODE NUMBER LDB T3,LDPRNF## ;LDB, GET NODE NUMBER ANDI T3,777 ;MASK OUT JUNK HLRZ T1,NDBNNM(W) ;GET OUR NODE NUMBER CAIE T3,(T1) ;BETTER MATCH PJSP T1,INCTBD ;NOPE, DON'T LET MESSAGE BE PROCESSED PUSH P,T2 ;SAVE NETLAT FLAGS MOVE T1,PCBCTR(U) ;GET MESSAGE LENGTH SUBI T1,6 ;DISCOUNT PROTOCOL CAIGE T1,1_ ;IN RANGE FOR JFFO? JFFO T1,.+2 ;GET HIGHEST BIT NUMBER TDZA T1,T1 ;OUT OF RANGE, USE ENTRY 0 MOVNI T1,-^D36(T2) ;IN RANGE, PUT IN PROPER ORDER AOS NCLRDL(T1) ;RECORD DATA LENGTH POP P,T2 ;RESTORE NETLAT FLAGS MOVEI T1,NDPICM ;NOW PASS MESSAGE VIA THE ICM DISPATCH PJRST ICMNDP ;GIVE THE PCB TO THE DEVICE SERVICE ROUTINE. ; IT MAY EITHER BE PROCESSED IMMEDIATLY AT ; "INTERRUPT" LEVEL (E.G., LPT'S) OR QUEUED ; FOR LATER PROCESSING AT UUO LEVEL (CDR'S) ;RCMPTP THIS ROUTINE RECOMPUTES THE NETWORK TOPOLOGY. IT SHOULD BE CALLED ; EVERY TIME THE TOPOLOGY CHANGES (NODEID & NEIGHBORS MESSAGE) ; IT IS RESPONSIBLE FOR GARBAGE COLLECTING UNREACHABLE NDB'S AND ; RE-ALLOCATING FEK'S. IT HAS THREE PHASES. ; 1) RECOMPUTE OUR NEIGHBORS. (FOR NEIGHBORS MSG) ; 2) RESET ALL NDB'S (CLEAR NDB.TP, SET COST TO 777777) ; 3) MARK ALL REACHABLE NODES AND REALLOCATE FEK'S (TPMARK) ; (NODES FOUND BY TRACING OUT PATHS STARTING WITH ; OUR FEK'S) ; 4) GARBAGE COLLECT ALL UNREACHABLE NODES. ;PRESERVES ALL P'S ;CLOBBERS ALL T'S ;RETURNS CPOPJ RCMPTP: NTDBUG ;VERIFY THE INTERLOCK PUSHJ P,SAVE4## ;USES ALL THE P'S ;REBUILD OUR NEIGHBORS TABLE (FOR SENDING NEIGHBORS MSG) MOVE P1,[POINT 9,NETNDB##+NDBTOP] ;POINTER TO NEIGHBORS TABLE MOVEI P2,NGHMAX ;MAXIMUM NUMBER OF NEIGHBORS TABLE CAN HOLD SKIPA J,[FEKFST##] ;LOOP OVER ALL FEK'S STARTING WITH FIRST RCMPT1: HRRZ J,FEKBLK(J) ;GET THE ADDRESS OF THE NEXT FEK JUMPE J,RCMPT2 ;EXIT IF WE'VE LOOKED AT ALL FEKS HRRZ T1,FEKNNM(J) ;GET NODE NUMBER JUMPE T1,RCMPT1 ;NEED ONE IF WE HAVE A NEIGHBOR SKIPL T3,FEKBLK(J) ;SKIP IF THE FEK IS DEAD JRST RCMPT1 ;EITHER FEK IS DEAD, OR NO NODE ID IFN FTENET,< TLNE T3,FK.ETM ;IS THIS AN ETHERNET MASTER FEK? JRST RCMPT1 ;YES, IGNORE IT, ONLY SLAVES ARE NEIGHBORS > ;END IFN FTENET HLRZ T2,FEKCST(J) ;GET COST FOR THIS FEK IDPB T1,P1 ;SAVE HIS NUMBER AS A NEIGHBOR IDPB T2,P1 ;SAVE COST SOJGE P2,RCMPT1 ;LOOP OVER ALL FEK'S STOPCD .,STOP,ANFTMF, ;++ TOO MANY FEK'S. ;HERE TO ZERO THE UNUSED PART OF OUR NEIGHBORS TABLE RCMPT2: SOJL P2,RCMPT3 ;EXIT WHEN WE'VE SET ALL THE NEIGHBORS IDPB J,P1 ;CLEAR THIS NODE NUMBER (J IS ZERO...) IDPB J,P1 ;CLEAR ITS COST ALSO. JRST RCMPT2 ;LOOP OVER ALL UNUSED SLOTS ;HERE TO CLEAR THE MARK BIT (NDB.TP) IN ALL NDB'S RCMPT3: MOVSI T1,NDB.TP ;GET THE MARK BIT MOVEI W,NETNDB## ;GET THE FIRST NODE IN THE LIST RCMPT4: ANDCAM T1,NDBFLG(W) ;CLEAR THE BIT HRRZ W,NDBNNM(W) ;ADVANCE TO THE NEXT NDB JUMPN W,RCMPT4 ;LOOP UNTIL WE REACH A ZERO LINK ;HERE TO MARK ALL REACHABLE NODES AND DETERMINE WHICH FEK THEY SHOULD USE. SKIPA J,[FEKFST##] ;GET THE ADDRESS OF THE FIRST FEK RCMPT5: HRRZ J,FEKBLK(J) ;GET THE ADDRESS OF THE NEXT FEK JUMPE J,RCMPT6 ;EXIT IF WE HAVE DONE ALL FEKS SKIPGE T1,FEKBLK(J) ;SKIP IF FEK IS DEAD IFN FTENET, ;EXIT IF NEIGHBORLESS ETHERNET MASTER FEK TLNE T1,FK.NUL ;EXIT IF THIS IS A NUL FEK JRST RCMPT5 ;EITHER DEAD OR NULL. GO DO NEXT FEK HRRZ T1,FEKNNM(J) ;GET NODE NUMBER JUMPE T1,RCMPT5 ;NO NEIGHBOR PUSHJ P,SRCNDB ;SET UP "W" WITH THE NDB ADDRESS JRST RCMPT5 ;JUST HIT VERY-VERY NARROW CROSS CPU RACE... SETZ T2, ;SAY THAT IT'S A ZERO COST TO HIM ; (JUST A WHITE LIE TO MAKE SURE WE USE FEK) JSP T1,TPMARK ;MARK ALL NODES WHOSE BEST PATH (SO FAR) ; IS THROUGH THIS FEK JRST RCMPT5 ;LOOP OVER ALL FEKS ;NOW WE SWEEP AND DELETE ALL UNREACHABLE NODES RCMPT6: HRRZ W,NETNDB##+NDBNNM ;GET START OF THE LIST RCMPT7: JUMPE W,CPOPJ ;IF AT END, THEN EXIT MOVSI T1,NDB.TP ;GET THE MARK BIT, AND TDNE T1,NDBFLG(W) ;SEE IF IT'S SET. JRST [HRRZ W,NDBNNM(W) ;IF SO, THEN STEP TO NEXT NDB JRST RCMPT7] ;AND CONTINUE HRRZ P1,NDBNNM(W) ;OTHERWISE GET THE LINK TO THE NEXT NDB PUSHJ P,RMVNDB ;BLAST THE NOW USELESS NDB MOVEI W,(P1) ;GET ADDR OF NEXT NDB TO CHECK JRST RCMPT7 ;AND GO DO IT. ;TPMARK THIS SUBROUTINE PERFORMS THE RECURSIVE MARK PHASE AS WELL AS ; THE TASK OF FRONT-END-KONTROLLERS TO EACH NDB. ; BECAUSE OF THE SMALL AMOUNT OF STACK SPACE ALLOWED, AND THE ; POSSIBILITY OF LARGE NETWORKS THE PDL STORAGE FOR THESE ; RECURSIVE ROUTINES IS IN THE NDB'S THEMSELVES. THESE LOCATIONS ; ARE THE THREE 'NDBTMP' LOCATIONS ;REGISTER USAGE ;CALL WITH ; J := THE ADDRESS OF THE FEK THAT STARTED THIS CHAIN ; W := THE ADDRESS OF THE CURRENT NDB ; T1 := THE RETURN ADDRESS (JSP T1, ...) ; T2 := THE 'COST' OF THE PATH FROM NETNDB TO THIS NDB. ; ; P1, P2, AND P3 MUST BE PERSERVED. THEY ARE USED AS FOLLOWS: ; ; P1 OUR NDB(W) ; P2 A BYTE POINTER TO THE CURRENT ENTRY IN OUR NEIGHBORS TABLE ; P3 RETURN ADDR(T1),,THE NUMBER OF NEIGHBOR SLOTS LEFT TO LOOK AT. ; TPMARK: JUMPE W,(T1) ;IF NO NDB, WE CAN'T MARK FROM IT... MOVEI T3,NETNDB## ;GET THE ADDRESS OF THE PROTOTYPE NDB CAIN W,(T3) ; IF THIS IS THE PROTOTYPE JRST (T1) ;THEN LEAVE NOW (DON'T CHANGE ITS FEK) MOVSI T3,NDB.TP ;GET THE "MARK BIT" TDNN T3,NDBFLG(W) ;HAVE WE VISITED THIS NODE BEFORE. JRST [IORM T3,NDBFLG(W) ;IF NOT, MARK THAT WE HAVE NOW. JRST TPMRK1] ; AND DON'T BOTHER TO COMPARE COSTS. HLRZ T3,NDBFEK(W) ;GET HIS BEST COST SO FAR CAIL T2,(T3) ;IS THIS A CHEAPER ROUTE JRST (T1) ; IF NOT CHEAPER, THEN EXIT NOW TPMRK1: HRLM T2,NDBFEK(W) ;SAVE CURRENT COST AT BEST SO FAR HRRM J,NDBFEK(W) ;SAVE THIS FEK AS BEST FEK SO FAR DMOVEM P1,NDBTMP(W) ;SAVE P1 AND P2 MOVEM P3,NDBTMP+2(W) ; DON'T FORGET P3 MOVSI P3,(T1) ;SAVE OUR RETURN ADDRESS IN A PROTECTED LOC HRRZ P1,W ;KEEP OUR NDB POINTER SAFE ALSO MOVE P2,[POINT 9,NDBTOP(P1)] ;BUILD A POINTER TO OUR NEIGHBORS HRRI P3,NGHMAX ;GET A COUNT OF MAX NUMBER OF NEIGHBORS ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;NOW LOOP OVER EACH NEIGHBOR RECURSIVLY CALLING TPMARK. (IF ALL PATHS ; ARE POSITIVE COST, THERE IS NO DANGER OF LOOPING...) TPMRK2: TRNN P3,-1 ;IF WE'VE PROCESSED ALL NEIGHBORS JRST TPMRK4 ;THEN EXIT SUBI P3,1 ;COUNT DOWN ILDB T1,P2 ;GET THE NUMBER OF OUR NEXT NEIGHBOR JUMPE T1,TPMRK3 ;IF THERE ISN'T ONE, SKIP COST AND GO TO NEXT PUSHJ P,SRCNDB ;SET UP "W" WITH NEIGHBORS NDB ADDRESS JRST [PUSHJ P,MAKNDB ;IF HE'S A NEW NEIGHBOR, MAKE AN NDB FOR HIM. JRST TPMRK3 ;IF NO CORE. JUST SKIP IT. WE'LL GET HIM LATER JRST .+1] ;REJOIN MAIN FLOW WITH "W" SET UP. ILDB T1,P2 ;GET THE COST OF THE LINK FROM US TO HIM. HLRZ T2,NDBFEK(P1) ;GET THE COST SO FAR TO GET TO US. ADD T2,T1 ;THE SUM IS TOTAL COST TO HIM VIA THIS PATH JSP T1,TPMARK ;RECURSIVLY MARK HIM JRST TPMRK2 ;LOOP OVER ALL NEIGHBORS. TPMRK3: ILDB T1,P2 ;SKIP COST IF NO NODE, OR NO CORE JRST TPMRK2 ;LOOP OVER ALL NEIGHBORS. TPMRK4: HLRZ T1,P3 ;ALL NEIGHBORS MARKED. GET RETURN ADDRESS MOVE P3,NDBTMP+2(P1) ;RESTORE P3 DMOVE P1,NDBTMP(P1) ;RESTORE P1 AND P2 JRST (T1) ;RETURN TO CALLER ;SUBROUTINE ONLNDB/OFLNDB - TYPE ONLINE/OFFLINE FOR A NETWORK NODE ;CALL MOVEI W,NDB ; PUSHJ P,ONLNDB/OFLNDB ;RETURN CPOPJ ONLNDB: SKIPA T1,[[ASCIZ /up at /]] OFLNDB: MOVEI T1,[ASCIZ /down at /] SKIPN %SIOPR## ;LET OPR REPORT IT IF ORION IS RUNNING SKIPGE DEBUGF## ;CHECK FOR DEBUG POPJ P, ;YES, SKIP THE MESSAGES PUSH P,U PUSH P,T1 ;SAVE THE MODE MOVE U,OPRLDB## ;GET THE OPERATOR LINE PUSHJ P,INLMES## ;TYPE %% ASCIZ /%% Node / PUSHJ P,TYPNOD ;TYPE OUT JUST "SNM(NNM)" POP P,T1 ;GET ONLINE/OFFLINE BACK PUSHJ P,CONMES## ;TYPE IT PUSHJ P,PRDTIM## ;FINISH OFF WITH TIME OF DAY PJRST UPOPJ## ;EXIT ;SUBROUTINE TYPNDB - TYPE OUT THE NODE INFOR ;CALL MOVEI W,NDB ; PUSHJ P,TYPENDB ;RETURN CPOPJ TYPNDB::NTDBUG ;VERIFY THE INTERLOCK MOVEI T1,[ASCIZ \ANF \] ;ASSUME NCL. CAIN W,NETNDB## ;IS THE NDB THE LOCAL NODE NDB? MOVEI T1,[ASCIZ \Local \] ;YES, DISTINGUISH. PUSHJ P,CONMES## ;TYPE OUT QUALIFIER PUSHJ P,TYPNOD ;TYPE "SNM(NNM)" HRRZ T1,NDBSID(W) ;GET THE MONITOR NAME SKIPE T1 ;SKIP IF UNKNOWN PUSHJ P,CONMES## ;TYPE PUSHJ P,PRSPC## ;SPACE HLRZ T1,NDBSID(W) ;GET THE CREATION DATE JUMPE T1,CPOPJ## ;EXIT IF UNKNOWN PJRST CONMES## ;TYPE ;TYPNOD ROUTINE TO TYPE OUT "STA-NAME(NUMBER)" ;CALL MOVX U,LDB ; MOVX W,NDB ; PUSHJ P,TYPNOD ;RETURN CPOPJ TYPNOD::NTDBUG HLRZ T1,NDBSNM(W) ;GET THE STATION NAME POINTER MOVE T2,(T1) ;GET THE STATION NAME SKIPN T1 ;IF THE NAME HASN'T BEEN RECEIVED YET, MOVE T2,[SIXBIT /?????/] ;USE THIS FOR A NAME PUSHJ P,PRNAME## ;PRINT IT MOVEI T3,"(" ;PAREN PUSHJ P,COMTYO## ;PRINT HLRZ T1,NDBNNM(W) ;GET THE NODE NUMBER PUSHJ P,PRTDI8## ;TYPE PJSP T1,CONMES## ;TYPE TERMINATOR AND RETURN ASCIZ /) / ;TYPILT ROUTINE TO "SEND OPR" TO TELL HIM THAT THE TOPOLOGY IS ILLEGAL ;CALL MOVX W,NODE THAT IS DUPLICATED ; PUSHJ P,TYPILT ;RETURN CPOPJ TYPILT: PUSHJ P,FRCSET## ;WE ARE GOING TO DO A SEND ON FRCLIN MOVEI T1,[ASCIZ /SEND OPR Illegal network topology. Two nodes /] PUSHJ P,CONMES## ;SEND THE FIRST PART HLRZ T1,NDBNNM(W) ;GET THE NODE'S NUMBER PUSHJ P,PRTDI8## ; AND TYPE THAT OUT PUSHJ P,PRPER## ;FINISH THE SENTENCE PJRST PCRLF## ;FINISH THE LINE AND RETURN SUBTTL ABMT -- NeTwork Device subroutines. (An imitation UUOCON) COMMENT \ Here is a list of the subroutines that should be used to write network device drivers. It is hoped that the routines are device independant enough to be useful for almost any network device. The routines fall into the following classes. General purpose: NTDSET This routine sets up S, J, and W and makes sure that the device is still connected. (Sets IOSERR and IODERR if not) NTDREL This routine sets IOSREL in the lh of DEVIOS. Primarly used as something to put in the dispatch entry for release. NTDONL This routine checks the bit IOSERR and skips if it's not set. NTDHNG This routine sets DVOFLN, returns "NT" and calls HNGSPT. NTDCLO Close output. This routine returns "NT" and calls "OUT". NTDIBA This routine checks to see if there is an input buffer available. It skips if there is, non-skips if not. NTDOBA This routine checks to see if there is an output buffer available. It skips if there is, non-skips if not. NTDIDA This routine skips if the device has input data available NTDCBC This routine calculates the number of bytes (given a byte pointer in T4) that will fit in a buffer of size "T3". (Kind of random calling convention I know, but this is only meant to be used by a few of the "NTD" routines. NTDPRV This routine skip's if the user has network privs (poke JACCT etc) Input buffer handling. (Interface to UUOCON) NTDSIB Setup Input Buffer. This routine sets up the two DEVAXI words so the device service routine can do buffered "output" into the users "input" buffer... NTDAIB Advance Input Buffer. Returns the input buffer to the user. (Using the buffered I/O analogy, this is just like the "OUT" UUO) Output buffer handling. (Interface to UUOCON) NTDSOB Setup Output Buffer. This routine sets up the two DEVAXO words so the device service routine can do buffered "input" from the users buffer. Also, this is necessary for NTDXMT to run. NTDAOB Advances the users output buffer. Clears DEVAXO to indicate that the buffer is no longer setup. NTDXMT This routine sends one PCB's worth of data from the users buffer. It updates both DEVAXO and DEVAXO+1. Input message handling. (PCB's) NTDISP This is the DAP dispatch routine. Called with P1 pointing to the first "CNT" field of the DAP portion of the NCL message. NTDILD This is the "interrupt" level dispatch. It is meant to be used by drivers that do not wish to queue their input message handling. To use this routine simply make the NDPICM dispatch entry point to NTDILD. NTDQIP This is the routine to queue an incoming PCB to be processed at UUO level. (Use this routine as the NDPICM dispatch if you wish to do low level processing of input messages) NTDDID This is the routine that takes PCB's off the queue (DEVPCB(f)) and calls NTDISP with them. (Use this routine to process messages queued by NTDQIP) Data request handling. NTDCDQ For output devices. This routine checks to see if you have any available data requests. NTDDDQ For output devices. This routine decrements DEVDRQ(F). Should be called only after data has been sent. Stops if no data requests are available. NTDDOD For input devices. This routine takes a PCB in "U" and if it is not an interrupt message, decrements the number of outstanding data-requests (ie. lh(devdrq(f))). NTDXDQ For input devices. This routine looks at the input buffer chain and determines if it should send any data requests. (Must NEVER be called from interrupt level since it doesn't OUCHE the buffer headers.) NTDRDQ Standard "canned" routine for handling incoming data requests. (ie. It is meant to be pointed to by the "DRQ" vector entry.) Delaying jobs for data or data requests. NTDWTI Used to wait for input (understands asynch i/o) NTDWTO Used to wait for output. NTDIAV Called to signify input available NTDOAV Called to signify output data requests available Connect and Disconnect processing. NTDCNC This is the default Connect Confirm dispatch handler. Used for most "normal" (eg LPT, CDR ...) devices NTDCNF This is a subset of NTDCNC (and is called by same) to process the MML and FEA(DCM,RLN,DVT,DVU,DVV) fields. NTDDSC This is the default Disconnect (both Initiate and Confirm) handler. NTDCNT This routine performs the connect function for "normal" devices. It assigns a SLA and waits for the connect to complete. NTDXPN This is a small routine to generate the "DPN" field of a connect for "normal" devices. NTDXSN This is line NTDXPN except that it writes an "SPN" field consisting of OBJ = OBJ.TK, PID = "JOB-NAME[P,PN]" NTDXDS This is a routine to send a disconnect and set the LAT state to "LAT.DC" (ie disconnect confirm wait) NTDNWD This is a routine that handles the NDPNWD (Node Went Down) entry for "normal" devices Message construction and sending NTDHDR This builds a NCL header with the DLA field filled in. NTDHDI This is the same as NTDHDR except that the "interrupt message" bit is turned on in the NCT. NTDSST This is used to send a "set" status bits message NTDCST This is used to send a "clear" status bits message NTDXST This is used to send a status message. NTDWRT This routine gives a PCB to the network for delivery. \ ;NTDSET GENERAL PURPOSE SET-UP ROUTINE ;CALL MOVEI F,DDB ; PUSHJ P,NTDSET ;RETURN CPOPJ ;ALWAYS. ;SETS UP J, S AND W. IF THE DEVICE IS NOT CONNECTED (NODE WENT AWAY, OR ; DISCONNECTED) IOSERR WILL BE SET IN DEVIOS(F) ; NTDSET:: ;GENERAL PURPOSE SET-UP ROUTINE LDB J,PJOBN## ;GET JOB NUMBER FROM THE DDB HRRZ T1,DEVNET(F) ;GET THE NUMBER OF THE NODE OWNING THE DEVICE MOVE S,DEVIOS(F) ;SET UP S TLNE S,IOSCON ;IF WE ARE CONNECTED, THEN PUSHJ P,SRCNDB ; SET UP W WITH THE NDB ADDRESS JRST NTDSE1 ;HERE IF NOT CONNECTED (OR NODE WENT AWAY) POPJ P, ;ALL DONE. RETURN WITH REG'S SETUP NTDSE1: ;HERE IF THE DEVICE IS NO LONGER CONNECTED MOVSI S,IOSCON ;GET AND CLEAR THE ANDCAB S,DEVIOS(F) ; "DEVICE IS CONNECTED" BIT MOVSI S,IOSERR ;SET THIS ERROR IORB S,DEVIOS(F) ; BIT FOR NTDONL TO SEE. POPJ P, ;RETURN WITH REGS SETUP AND ERROR BITS ON. ;NTDCLO ROUTINE TO PERFORM NORMAL "OUTPUT CLOSE" PROCESSING ;CALL MOVE F,DDB ;DEVICE TO CLOSE OUTPUT ON ; PUSHJ P,NTDCLO ;SAVE "NT", CALL OUT AND WAIT FOR COMPLETION ;RETURN CPOPJ ;ALWAYS FROM WAIT1 ; NTDCLO::NTSAVE ;RETURN THE "NT" INTERLOCK (WE BETTER HAVE IT) PUSHJ P,OUT## ;FORCE OUT ANY UN-EMPTIED BUFFERS PJRST WAIT1## ;WAIT FOR EVERYTHING TO GO OUT. ;NTDREL ROUTINE TO SET IOSREL WHEN A DEVICE IS RELEASED ;CALL MOVEI F,DDB ; PUSHJ P,NTDREL ;MARK DEVICE AS RELEASED SO DATA REQUESTS ; ; WON'T CAUSE FALSE "WAKES" TO HAPPEN ;RETURN CPOPJ ;ALWAYS ; NTDREL:: ;HERE ON A RELEASE MOVSI S,IOSTBL!IOSERR ;CLEAR THESE TWO ERROR BITS ANDCAB S,DEVIOS(F) MOVSI S,IOSREL ;SET THE BIT THAT SAYS IORB S,DEVIOS(F) ; WE HAVE RELEASED THIS DEVICE SETZM DEVAXO(F) ;CLEAR POINTER TO OUTPUT BUFFER SETZM DEVAXI(F) ;CLEAR POINTER TO INPUT AS WELL TLNE S,IOSCON ;IF WE ARE STILL CONNECTED, THEN JUST POPJ P, ; RETURN AND LET ZAPNET DISCONNECT LATER. LDB T1,NETSLA ;IF NOT CONNECTED, CHECK FOR A LAT ENTRY JUMPE T1,CPOPJ## ;IF NONE, THEN NO MORE TO DO IFN PARANOID&P$LAT,< ;MAKE SURE WE ARE IN A VALID STATE. LDB T2,LATSTA ;GET THE STATE. CAIN T2,LAT.OK ;WE SHOULDN'T BE "OK" IF NOT CONNECTED STOPCD .,STOP,ANFWLS, ;++ WRONG LAT STATE > PUSHJ P,GIVSLA ;RETURN THE LAT ENTRY (GO TO IDLE STATE) PJRST CLNNET ;CLEAN OUT THE DDB (DRQ, NPD'S ETC...) ;NTDONL ROUTINE TO SEE IF THE DEVICE IS ONLINE (NO ERROR CONDITIONS) ;CALL MOVE S,DEVIOS(F) ;GET DEVIOUS BITS IN S ; PUSHJ P,NTDONL ;SEE IF ON LINE ;RETURN CPOPJ ;DEVICE HAS ERROR BITS SET. ; CPOPJ1 ;EVERYTHING SEEMS OK. ; ;NOTE! THIS ROUTINE MAY BE USED AS THE ONLINE DISPATCH ENTRY. ; NTDONL:: ;CHECK TO SEE IF DEVICE IN ONLINE TLNN S,IOSERR ;EVERYTHING OK? AOS (P) ;LOOKS GOOD (NO ERROR BITS) POPJ P, ;NTDHNG ROUTINE TO PERFORM NORMAL DEVICE OFFLINE PROCESSING ;CALL MOVE F,DDB ;DEVICE TO MARK AS OFF-LINE ; PUSHJ P,NTDHNG ;SET DVOFLN, SAVE "NT", CALL HNGSTP ;RETURN CPOPJ ;FROM HNGSTP ; NTDHNG::MOVSI T1,DVOFLN ;GET THE OFF-LINE BIT AND SET IT IORM T1,DEVCHR(F) ; SO THAT WE WILL GET ON-LINE INTERRUPT NTSAVE ;"SAVE" THE "NT" INTERLOCK PJRST HNGSTP## ;CALL HUNGSTP AND RETURN ;NTDGON ROUTINE TO SET ERROR WHEN DEVICE HAS "GONE" AWAY ;CALL MOVE F,DDB ;DEVICE WHICH IS KROAKED ; PJRST NTDGON ;MARK ERROR AND RETURN TO UUOCON NTDGON::MOVEI S,IODERR!IODTER ;DEVICE AND DATA ERRORS IORB S,DEVIOS(F) ;ASSERT ERROR FLAGS POPJ P, ;RETURN (PRESUMABLY TO UUOCON) ;NTDIBA ROUTINE TO SEE IF THIS DEVICE HAS AN AVAILABLE INPUT BUFFER. ;CALL MOVE F,DDB ; PUSHJ P,NTDIBA ;RETURN CPOPJ ;ALL INPUT BUFFERS ARE IN USE ; CPOPJ1 ;THERE IS AT LEAST ONE FREE INPUT BUFFER. ; NTDIBA:: ;HERE TO SEE IF THERE ARE ANY FREE INPUT BUFFERS MOVE S,DEVIOS(F) ;GET A COPY OF DEVIOUS TLNE S,IOSUSI ;DOES UUOCON WANT INPUT STOPPED (GOING TO SWAP) POPJ P, ; IF SO, THEN GIVE "NO BUFFER" RETURN HRRZ T1,DEVIAD(F) ;GET POINTER TO FIRST INPUT BUFFER JUMPE T1,CPOPJ## ;IF NO BUFFER, NONE ARE FREE EXCTUX ;CHECK THE USE BIT. AOS (P) ;IT'S NOT SET. THE BUFFER IS FREE POPJ P, ;NTDOBA ROUTINE TO SEE IF THIS DEVICE HAS ANY OUTPUT TO GO. ;CALL MOVE F,DDB ; PUSHJ P,NTDOBA ;RETURN CPOPJ ;NO OUTPUT AVAILABLE ; CPOPJ1 ;THERE IS AT LEAST ONE FULL OUTPUT BUFFER ; NTDOBA:: ;HERE TO SEE IF THERE IS A FULL OUTPUT BUFFER MOVE S,DEVIOS(F) ;GET A COPY OF THE STATUS BITS TLNE S,IOSUSO ;DOES UUOCON WANT OUTPUT STOPPED (FOR SWAP) POPJ P, ; IF SO, THEN GIVE THE "NO BUFFER" RETURN HRRZ T1,DEVOAD(F) ;GET A POINTER TO THE USERS BUFFER JUMPE T1,CPOPJ## ;IF NO BUFFER, NONE ARE FREE EXCTUX ;CHECK THE USE BIT AOS (P) ;THE BUFFER HAS DATA. POPJ P, ;NTDIDA ROUTINE THAT SKIPS IF AN "IN" UUO WOULD BE APPROPRIATE ; CALLED FROM MSGSER TO SEE IF INPUT DATA IS AVAILABLE ;CALL MOVEI F,DDB ; PUSHJ P,NTDIDA ;RETURN CPOPJ ;NO PCB'S QUEUED FOR PROCESSING ; CPOPJ1 ;PCB'S ARE QUEUED. MSGSER SHOULD "CALIN" ; NTDIDA::SETZM DEVAXI(F) ;CLEAR BOTH THE AUX INPUT POINTERS SETZM DEVAXI+1(F) ; SINCE MSGSER WILL GIVE US A NEW BUFFER. HRRZ T1,DEVPCB(F) ;FIRST SEE IF THERE IS AN INPUT PCB QUEUED JUMPN T1,CPOPJ1## ; IF THERE IS, TELL MSGSER TO DO AN "IN" MOVE S,DEVIOS(F) ;SEE IF WE ARE STILL TLNN S,IOSCON ; CONNECTED. IF NOT, WE POPJ P, ; HAD BETTER NOT SEND A DRQ. HLRZ T1,DEVDRQ(F) ;SEE IF WE'VE REQUESTED DATA JUMPN T1,CPOPJ## ; IF WE'VE ALREADY SEND DRQ, THEN WE'RE DONE NETDBJ ;THE REST OF THIS CODE MUST BE INTERLOCKED MOVEI T1,1 ;MSGSER WILL SUPPLY US WITH ONLY "1" BUFFER PUSHJ P,NCSDRQ ;SEND THE DATA-REQUEST (FOR 1 MSG) POPJ P, ;IF NO CORE, EXIT WITH-OUT UPDATING DEVDRQ MOVSI T1,1 ;GET A LEFT-HALF "1" ADDM T1,DEVDRQ(F) ;INCREMENT THE OUTSTANDING DATA-REQUEST COUNT POPJ P, ;TELL MSGSER THAT THERE'S NO DATA FOR IT. ;NTDCBC ROUTINE TO CALCULATE BYTE COUNT. ;CALL MOVE T4,BYTE POINTER ;THIS ROUTINE USES ONLY THE "SIZE" FIELD ; MOVEI T3,WORD COUNT ;THIS IS THE SIZE OF THE BUFFER TO FILL ;RETURN CPOPJ WITH T1 := NUMBER OF BYTES THAT WILL FIT IN THE BUFFER. ; ;NOTE!! SORRY ABOUT THE SCREWY AC CONVENTIONS, BUT IT FITS IN WITH ; THE OTHER "NTD" ROUTINES. NTDCBC::LDB T2,[POINT 6,T4,11] ;GET THE "SIZE" OF THE BYTE MOVEI T1,^D36 ;NUMBER OF BITS IN A WORD IDIVI T1,(T2) ;T1 := NUMBER OF BYTES IN A WORD IMULI T1,(T3) ;T1 := NUMBER OF BYTES IN "T3" WORDS POPJ P, ;NTDPRV ROUTINE TO CHECK PRIVS. SKIPS IF USER MAY HACK THE NET ;CALL PUSHJ P,NTDPRV ;RETURN CPOPJ ;DON'T LET HIM HACK ; CPOPJ1 ;HE CAN DO ANYTHING HE WANTS. NTDPRV::PUSH P,J ;SAVE FEK OR WHATEVER SKIPN J,.CPJOB## ;GET THIS JOB'S NUMBER JRST JPOPJ1## ;INTERRUPT LEVEL IS ALWAYS A GOOD GUY PUSH P,T1 ;NTDPRV CLOBBERS NO REGISTERS MOVSI T1,JP.POK ;POKE, 1-2, OR JACCT ARE GOOD GUYS PUSHJ P,PRVBIT## ;CALL COMCON'S ROUTINE AOS -2(P) ;PRVBIT SKIPS IF NOT PRIV POP P,T1 ;RESTORE T1 (AREN'T WE NICE) JRST JPOPJ## ;RESTORE J AND (SKIP) RETURN ;NTDSTP ROUTINE TO GENERATE A STOP CODE. USED WHERE TOO LAZY TO THINK ONE UP. ;CALL PUSHJ P,NTDSTP ;?? HORRIBLE BUG ?? ;RETURN HA. NTDSTP::STOPCD CPOPJ##,STOP,WEM, ;++ DAMN! COMMENT \ Protocol for using the Input Buffer Management routines. When using the input buffer management routines the following words in the DDB are used to contain status information: DEVAXI This word is anaglous to the ".BFPTR" word in a user-mode buffer control block. It is a byte pointer to the users buffer. DEVAXI+1 This word is analogous to the ".BFCTR" word. It is initialized to be the number of bytes that will fit in the user's buffer. Here is a skeleton routine that should indicate how these routines are intended to be used. ;Assume that calling the routine "D.NEXT" will supply the next data byte ; (i.e., from the NCL message) and skip return unless the message is exhausted ; in which case it will error (non-skip) return. ;Before we start copying we must set up the buffer. MOVSI S,IOSUSI ;FIRST CLEAR "UUOCON STOPPED INPUT" ANDCAB S,DEVIOS(F) ; SO THAT NTDSIB WILL WORK PUSHJ P,NTDSIB ;SET UP THE INPUT BUFFER (DEVAXI, +1) ;Now we merely loop copying the data LOOP: ;EVERY PROG SHOULD HAVE A LOOP!! PUSHJ P,D.NEXT ;GET THE NEXT CHARACTER. PJRST NTDA1B ;IF NO MORE, THEN ADVANCE THE BUFFER AND ; GIVE A SKIP RETURN TO NTDISP SO THAT ; IT WILL ADVANCE TO THE NEXT NCL SUB- ; MESSAGE. SOSGE DEVAXI+1(F) ;DECREMENT THE NUMBER OF UN-USED BYTES IN JRST SET-IOBKTL ; THE BUFFER. (IT IS IMPORTANT TO KEEP THIS ; COUNT CORRECT. NTDAIB USES IT TO CALCULATE ; THE ITEM-COUNT TO PUT IN THE BUFFER-HEADER) ; IF THE COUNT COUNTS OUT, THEN SET BLOCK-TO- ; LARGE, OR IF YOU WANT TO KEEP GOING CALL ; "NTDAIB" FOLLOWED BY "NTDSIB" AND CONTINUE EXCTUU ; STORE THE BYTE IN "T1" JRST LOOP ;CONTINUE UNTIL MESSAGE IS EXHAUSTED \ ;NTDSIB ROUTINE TO SET UP AN INPUT BUFFER ;CALL MOVEI F,DDB ;DEVIAD(F) = USERS INPUT BUFFER ; MOVEI T4,BYTE-SIZE ;USED IN CALCULATING ITEM COUNT (DEVAXI+1) ; PUSHJ P,NTDSIB ;SET UP INPUT BUFFER ;RETURN NOT-AT-ALL ;IF THE USERS BUFFER IS NOT IN CORE (PAGE FAULT) ; CPOPJ ;NO USER BUFFER AVAILABLE ; CPOPJ1 ;BUFFER IS SET UP. THE FOLLOWING ANALOG OF ; ; THE STANDARD BUFFER CONTROL BLOCK IS SET UP. ; ; DEVAXI(F) := BYTE POINTER TO USERS BUFFER ; ; DEVAXI+1(F) := COUNT OF BYTES IN BUFFER. ; ;IF YOU JUST PRETEND THAT YOU ARE DOING USER- ; ; LEVEL BUFFERED OUTPUT AND EVERYTHING SHOULD ; ; WORK FINE! ;NOTE IF THIS ROUTINE IS CALLED WITH THE BUFFER ALREADY SET UP, IT WILL ; MERELY RANGE CHECK THE USERS BUFFER AND RETURN. THIS MAKES IT EASY ; TO USE COMMON CODE TO PACK MANY NCL MESSAGES INTO ONE USER BUFFER. ; NTDSIB:: ;SETUP INPUT BUFFER SKIPE DEVAXI(F) ;HAVE WE ALREADY SET THIS UP? JRST [HRRZ T1,DEVIAD(F) ;YES, RANGE CHECK THE BUFFER TO PUSHJ P,BRNGE## ; ENSURE THAT IT IS STILL IN CORE JRST NTDSI1] ;CLEAR USER'S BUFFER AND SKIP RETURN PUSHJ P,NTDIBA ;SINCE NOT SET-UP, SEE IF A BUFFER IS AVAILABLE POPJ P, ; IF NOT, THEN GIVE ERROR RETURN HRRZ T1,DEVIAD(F) ;SECTION-LOCAL ADDRESS OF USER BUFFER PUSHJ P,BRNGE## ;MAKE SURE THAT THE BUFFER'S IN BEFORE POKING IT LSH T4,^D24 ;MAKE T4 INTO A BYTE POINTER HRRZ T1,DEVIAD(F) ; TO THE BYTE JUST BEFORE THE FIRST BYTE HRRI T4,1(T1) ; IN THE USER'S BUFFER. (SO "I"LDB WORKS) EXCTUX ;GET THE BUFFER SIZE+1 (IN WORDS) SUBI T3,1 ;GET THE BUFFER SIZE (IN WORDS) PUSHJ P,NTDCBC ;GET THE BUFFER SIZE (IN BYTES) MOVEM T4,DEVAXI(F) ;SET UP THE ".BFPTR" WORD MOVEM T1,DEVAXI+1(F) ;SET UP THE ".BFCTR" WORD NTDSI1: HRRZ T1,DEVIAD(F) ;GET THE BUFFER ADDRESS PUSHJ P,BUFCLR## ;CLEAR USER'S BUFFER BEFORE RETURNING JFCL ;ZERO LENGTH: SHOULD NEVER OCCUR JRST CPOPJ1## ;SKIP RETURN: THE BUFFER IS SET UP ;NTDAIB ROUTINE TO ADVANCE A USERS BUFFER (ANALOG OF THE OUTPUT UUO) ;CALL MOVEI F,DDB ;DEVAXI(F) := BYTE POINTER ; ;DEVAXI+1(F) := NUMBER OF UNUSED BYTES IN BUFFER ; PUSHJ P,NTDAIB ;ADVANCE INPUT BUFFER (GIVE DATA TO THE USER) ;RETURN CPOPJ ;DEVAXI(F) := 0 INDICATING NO BUFFER IS SETUP ; NTDA1B::AOS (P) ;SPECIAL ENTRY FOR THOSE WHO WANT A SKIP RETURN NTDAIB::SKIPN T4,DEVAXI(F) ;GET BYTE POINTER STOPCD .,STOP,ANFAIB, ;++ NO BUFFER SET UP WHEN ADVACING INPUT HRRZ T1,T4 ;GET A COPY OF THE ADDRESS OF THE LAST WORD SETZ T2, ;GET A ZERO (TO ZERO FILL LAST WORD) NTDAI1: IBP T4 ;INCREMENT T4 ONE MORE BYTE CAIN T1,(T4) ;IF GOES TO NEW WORD, THEN DONE ZERO FILLING JRST [EXCTUU ;STORE A ZERO BYTE JRST NTDAI1] ; AND LOOP OVER REST OF THE WORD HRRZ T1,DEVIAD(F) ;GET POINTER TO BUFFER HEADER LDB T2,PIOMOD## ;GET I/O MODE AND CAIE T2,A8 ; IF IT'S 8-BIT ASCII OR CAIN T2,BYTMOD ; IF IT'S BYTE MODE, THEN JRST NTDAI2 ; COMPUTE ITEM-COUNT IN BYTES SUBI T4,2(T1) ;COMPUTE WORD COUNT (SUBTRACT BYTE POINTERS) EXCTUU ;STORE THE WORD-COUNT IN THE BUFFER HEADER JRST NTDAI3 ;GO REJOIN COMMON CODE NTDAI2: ;HERE IF WE WANT ITEM COUNT TO BE BYTE COUNT EXCTUX ;TO COMPUTE BYTE COUNT, FIRST GET SUBI T3,1 ; THE SIZE OF THE BUFFER IN WORDS. PUSHJ P,NTDCBC ;NOW GET THE SIZE OF THE BUFFER IN BYTES. SUB T1,DEVAXI+1(F) ;SUBTRACT UNUSED BYTE COUNT TO GET BYTES USED. HRRZ T2,DEVIAD(F) ;NOW TO STORE THE COUNT FIRST GET BUFFER HEADER EXCTUU ; AND USE THAT TO STORE THE ITEM COUNT FOR USER. NTDAI3: ;COMMON CODE TO ADVANCE USERS BUFFER. SETZM DEVAXI(F) ;CLEAR ".BFPTR" TO INDICATE BUFFER NOT SETUP PUSHJ P,ADVBFF## ;ADVANCE USERS INPUT BUFFER JRST .+2 ;WE MUST REMEMBER IF UUOCON WANT'S INPUT STOPED POPJ P, ;RETURN WITH USER'S BUFFER ADVANCED MOVSI S,IOSUSI ;GET AND SET "UUOCON STOPED INPUT" BIT. THIS IORB S,DEVIOS(F) ; GETS SET WHEN SWAPING, ^C ETC. POPJ P, ;RETURN. NTDIBA WILL NOTICE THE BIT AND STOP IO COMMENT \ Protocol for using the output buffer management routines. When using the output buffer management routines the following words in the DDB are used to contain status information: DEVAXO This word contains a byte pointer to the unsent portion of the users buffer. DEVAXO+1 This word contains the number of unsent bytes in the users buffer. You may criticize me on the design of this mechanism and contend that this is not the most efficient way to represent the state of the users buffer. To this I will have to agree. (Consider all the trouble that NTDXMT has to go through to convert back and forth from byte to word counts...) I have only two points to make in my defense: 1) Byte mode falls out with no added effort. 2) This scheme forms a symmetry with the input routines that, in my opinion, lends a rather pleasant feeling of completeness to this whole mess. Now for a skeleton routine showing how to use these routines. MOVSI S,IOSUSO ;FIRST CLEAR THE "UUOCON STOPPED OUTPUT" BIT ANDCAB S,DEVIOS(F) ; SO THAT NTDSOB WILL WORK LOOP: ;HERE TO SEND MORE DATA. PUSHJ P,NTDSET ;SET UP W & S. THIS MUST BE DONE AFTER ; A CALL TO ANY OF THE WAIT ROUTINES AS ; THEY ALL CLOBBER W. (ON PURPOSE.) MOVE S,DEVIOS(F) ;RELOAD S (WAIT ROUTINES CLOBBER S) MOVEI T1,^DBYTE-SIZE ;GET THE BYTE SIZE SO THAT ; "NTDSOB" CAN SET UP THE PROPER BYTE COUNT. PUSHJ P,NTDSOB ;SET UP DEVAXO, DEVAXO+1 JRST NO-MORE-DATA ; ERROR RETURN INDICATES WE ARE DONE. PUSHJ P,NTDCDQ ;CHECK TO SEE IF WE HAVE A DATA REQUEST. JRST WAIT-AWHILE ; IF NOT, EITHER WAIT, OR RETURN TO UUOCON. MOVEI T1,COMPRESSION ;COMPRESSION/CONVERSION (PCV.??) PUSHJ P,NTDXMT ;SEND ONE PCB'S WORTH OF DATA. JRST PROCESS ERROR ;IF OUT OF CORE OR IOBKTL PUSHJ P,NTDDDQ ;DECREMENT DRQ NOW THAT MSG HAS GONE SKIPN NETAXO+1(F) ;HAVE WE EMPTIED THIS BUFFER?? PUSHJ P,NTDAOB ; IF SO, THEN ADVANCE THE USERS BUFFER JRST LOOP ;KEEP IT UP TILL WE GET IT WRONG. *** NOTE *** If one were so inclined one could do something like replace the call to NTDXMT by a loop of the form: LOOP1: SOSGE DEVAXO(F) ;DECREMENT ".BFCTR" JRST DONE EXCTUX ;GET NEXT BYTE PUSHJ P,PUT-BYTE-SOMEWHERE JRST LOOP1 ;CONTINUE UNTIL BUFFER IS EXHAUSTED. For network devices I see no reason to do this since NTDXMT is not only faster (It copys a PCB's worth of data with a single BLT) but its a lot easier too! \ ;NTDSOB ROUTINE TO SET UP A BUFFER FOR OUTPUT ;CALL ; MOVEI T1,BYTE SIZE (USUALLY DEPENDS ON THE CONVERSION CODE) ; PUSHJ P,NTDSOB ;SET UP OUTPUT BUFFER ;RETURN NOT-AT-ALL ;IF THE USERS BUFFER WAS NOT INCORE (PAGE FAULT) ; CPOPJ ;NO OUTPUT BUFFER AVAILABLE ; CPOPJ1 ;BUFFER SET UP. ; ; DEVAXO(F) := BYTE POINTER TO USERS BUFFER ; ; DEVAXO+1(F) := COUNT OF BYTES LEFT TO GO. ; NTDSOB:: ;SET UP OUTPUT BUFFER SKIPE DEVAXO(F) ;IS THE BUFFER ALREADY SET UP? JRST [AOS (P) ;IF SO, GIVE A GOOD RETURN. HRRZ T1,DEVOAD(F) ; BUT FIRST MAKE SURE THAT THE BUFFER PJRST BRNGE##] ; IS STILL IN CORE. PUSH P,T1 ;SAVE THE BYTE SIZE OVER CALL TO NTDOBA PUSHJ P,NTDOBA ;IS THERE AN OUTPUT BUFFER AVAILABLE? JRST TPOPJ## ;IF NOT, THEN GIVE THE ERROR RETURN POP P,T4 ;GET BYTE SIZE BACK IN T4 LSH T4,^D24 ;MAKE T4 INTO A BYTE POINTER HRRZ T1,DEVOAD(F) ; THAT WHEN INCREMENTED POINTS TO THE HRRI T4,1(T1) ; FIRST BYTE IN THE USER'S BUFFER. EXCTUX ;GET THE BUFFER ITEM COUNT (WORDS OR BYTES) MOVEI T1,(T3) ;COPY THE WORD COUNT LDB T2,PIOMOD## ;GET THE MODE THAT WE ARE IN. CAIE T2,BYTMOD ; AND IF WE ARE NOT IN BYTE MODE, CAIN T2,A8 ; OR 8-BIT ASCII MODE, TRNA ; (WRONG) PUSHJ P,NTDCBC ; THEN WE NEED TO CONVERT WORD-COUNT TO BYTES. MOVEM T4,DEVAXO(F) ;STORE THE BYTE POINTER MOVEM T1,DEVAXO+1(F) ;STORE THE BYTE COUNT JRST CPOPJ1## ;GIVE GOOD (SKIP) RETURN ;NTDAOB ROUTINE TO ADVANCE THE USERS OUTPUT BUFFER ;CALL MOVEI F,DDB ; PUSHJ P,NTDAOB ;ADVANCE OUTPUT BUFFER ;RETURN CPOPJ ;ALWAYS ; NTDAOB:: ;ADVANCE OUTPUT BUFFER SKIPN DEVAXO(F) ;HAS THE BUFFER BEEN SETUP STOPCD .,STOP,ANFAOB, ;++ NO BUFFER SET UP WHEN CALLING NTDAOB SETZM DEVAXO(F) ;INDICATE THAT IT'S NO LONGER SET UP PUSHJ P,ADVBFE## ;ADVANCE THE BUFFER JRST .+2 ;WE MUST SHUT DOWN OUTPUT FOR SOME REASON POPJ P, ;ALL DONE MOVSI S,IOSUSO ;GET AND SET THE "UUOCON STOPPED OUTPUT" IORB S,DEVIOS(F) ; NTAOBA WILL NOTICE THIS BIT AND SAY POPJ P, ; THAT NO BUFFERS ARE AVAILABLE FOR OUTPUT. ;NTDXMT ROUTINE TO SEND ONE PCB'S WORTH OF DATA FROM A USERS BUFFER. ; UNDERSTANDS ABOUT BREAKING USERS BUFFERS INTO MANY PCB'S ;CALL MOVEI T1,COMPRESSION CODE (PCV.??) ; MOVEI T2,IDCTYP+INTERRUPT BIT(=1B18) ; MOVEI F,DDB ;WITH DEVAXO, +1 SET UP ; PUSHJ P,NTDXMT ;SEND A PCB'S WORTH OF DATA ;RETURN NOT-AT-ALL ;IF A PAGE FAULT OCCURS ; CPOPJ ;IO-ERROR BIT IN T1. IF T1 = 0, NO CORE ; CPOPJ1 ;THE PCB WAS SENT OK. NTDXMT::SKIPE %NTNIP ;DOING ETHERNET-TYPE STUFF? JRST NTDXNT ;YEAH, MUST USE CONTIGUOUS BUFFERS SKIPN DEVAXO(F) ;IS THE BUFFER SET UP? STOPCD .,STOP,ANFXMT, ;++ NO BUFFER SET UP WHEN CALLING NTDXMT PUSH P,T1 ;SAVE THE COMPRESSION CODE (UNTIL THE VERY END) PUSH P,T2 ;SAVE THE INTERRUPT-BIT+IDCTYP HRRZ T1,DEVOAD(F) ;NOW RANGE CHECK THE BUFFER. PUSHJ P,BRNGE## ; JUST INCASE HE DECIDED TO SWAP LDB T2,[POINT 6,DEVAXO(F),11] ;NOW GET NUMBER OF WORDS IN BUFFER. MOVEI T3,^D36 ;START WITH BYTE SIZE AND 36/BYTE SIZE IDIVI T3,(T2) ; YIELDS T3 := NUMBER OF BYTES/WORD MOVE T2,DEVAXO+1(F) ;GET THE NUMBER OF BYTES LEFT. SETZ T1, ;CLEAR THE HIGH ORDER WORD DIVI T1,(T3) ;CONVERT INTO WORDS SKIPE T2 ; BUT BE SURE TO ADDI T1,1 ; ROUND UP TO GET TOTAL WORDS IN BUFFER. PUSH P,T3 ;SAVE THE BYTE/WORD FOR LATER PUSH P,T1 ;SAVE LENGTH FOR A BIT. NTDXM0: MOVEI T1,4 ;GET A PCB START WITH 4 WORDS (MAX) OF HEADER ADD T1,(P) ; ADD IN THE LENGTH OF THE BUFFER CAILE T1,MSGXMW## ;IF THIS IS MORE THAN THE "MAXIMUM" PCB SIZE MOVEI T1,MSGXMW## ; THEN JUST REQUEST THE "MAXIMUM" MOVEI T3,NTDHDR ;ASSUME THAT THIS IS NOT AN INTERRUPT MESSAGE MOVEI T2,(1B0) ;GET THE "INTERRUPT MESSAGE" BIT TDNE T2,-2(P) ; AND IF IT'S SET MOVEI T3,NTDHDI ; THEN GET AN INTERRUPT HEADER. PUSHJ P,(T3) ;GO BUILD THE HEADER JRST [ADJSP P,-4 ;IF NO CORE, CLEAN UP THE STACK SETZ T1, ; CLEAR T1 TO SAY ERROR WAS NO FREE CORE POPJ P,] ; AND GIVE AN ERROR RETURN TO THE CALLER MOVE T2,PCBCTR(U) ;NOW CALCULATE THE NUMBER OF FREE DATA WORDS ADDI T2,3+3(P3) ;START WITH BYTES IN HEADER, ALLOW FOR "CNT" LSH T2,-2 ; AND "TYP", +3 TO ROUND UP. LSH TO GET WORDS. MOVE T4,PCBALN(U) ;GET THE TOTAL NUMBER OF WORDS IN THE PCB SUBI T4,(T2) ;SUBTRACT WORDS USED, GET WORDS LEFT IN PCB. POP P,T1 ;GET THE BUFFER LENGTH BACK POP P,T3 ;GET THE BYTES/WORD BACK CAIL T4,(T1) ;SKIP IF PCB WON'T FINISH OFF THE BUFFER JRST NTDXM1 ;GO TO CODE TO SEND FINAL FRAGMENT OF BUFFER. ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;HERE IF WE HAVE TO "FRACTURE" THE BUFFER INTO SEVERAL MESSAGES, AND THIS ; IS NOT THE LAST FRAGMENT OF THE BUFFER. ; T3 := BYTES/WORD ; T4 := NUMBER OF WORDS LEFT IN THE PCB (WE WILL USE THEM ALL) POP P,T1 ;GET THE IDC TYPE FIELD ANDI T1,77 ;AND JUST THE TYPE (MASK OUT THE INTERRUPT BIT) CAIL T1,DC.DAT ;NOW SEE IF THIS IS A MESSAGE TYPE THAT WE CAILE T1,DC.DAR ; CAN LEGALLY FRAGMENT INTO MULTIPLE MESSAGES JRST [POP P,T1 ;IF WE CAN'T BREAK THE MESSAGE, CLEAN UP STACK MOVEI T1,IOBKTL; TELL OUR CALLER THAT ERROR WAS BLOCK TO LARGE POPJ P,] ; AND GIVE AN ERROR RETURN PUSH P,[DC.DAT] ;CHANGE THE MESSAGE TYPE TO BE DATA W/O E-O-R. MOVEI T1,(T4) ;GET THE NUMBER OF WORDS LEFT IN THE PCB IMULI T1,(T3) ;CONVERT WORD COUNT TO BYTE COUNT MOVN T2,T1 ;GET MINUS THAT BYTE COUNT ADDM T2,DEVAXO+1(F) ; AND UPDATE THE NUMBER OF UNSENT BYTES. SKIPG DEVAXO+1(F) ;JUST A QUICK CHECK OF MY ARITHMETIC STOPCD .,STOP,ANFUBN, ;++ UNSENT BYTES COUNT WENT NEGATIVE JRST NTDXM2 ;GO SEND "CNT", "TYP", AND THE DATA. ;HERE IF THE PCB WILL HOLD ALL THE REST OF THE BUFFER. ; T1 := WORDS LEFT IN THE USER'S BUFFER NTDXM1: MOVEI T4,(T1) ;COPY THE NUMBER OF WORDS TO GO (FOR THE BLT) SETZ T1, ;GET A "ZERO" EXCH T1,DEVAXO+1(F) ;GET THE NUMBER OF "BYTES" TO GO. CLEAR BUF CNT. ; JRST NTDXM2 ;GO SEND "CNT", "TYP", AND THE DATA ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;NTDXM2 HERE TO SEND "CNT", "TYP", AND DATA PORTIONS OF THE MESSAGE. ; -1(P) := CONVERSION CODE (WE MUST HANDLE BINARY MODE ESPECIAL) ; (P) := IDC TYPE ; T1 := NUMBER OF "BYTES" (OF WHATEVER SIZE) TO GO. ; T4 := NUMBER OF "WORDS" TO GO (USED BY THE BLT, AND BINARY MODE) ; ;NOTE. BECAUSE THE FEK IS LAZY WHEN IT DOES THE BINARY CONVERSION, WE MUST ; CALCULATE BY HAND WHAT THE LENGTH OF THE DATA MESSAGE WILL BE AFTER ; CONVERSION. BINARY CONVERSION PACKS THE DATA 2 WORDS INTO 9 BYTES. NTDXM2: MOVEM T1,PCBCT2(U) ;SET THE BYTE COUNT FOR THE SECOND BUFFER MOVE T2,-1(P) ;GET THE CONVERSION CODE CAIE T2,PCV.BN ; AND SEE IF IT'S BINARY. JRST NTDXM3 ;IF NOT BINARY, THEN USE BYTE COUNT IN "T1" MOVEI T1,(T4) ;CALCULATE BYTE COUNT BY HAND. START WITH WORDS. LSHC T1,-1 ;GET DOUBLE-WORDS. (REMAINDER = SIGN OF T2) IMULI T1,^D9 ;2 WORDS FIT IN 9 BYTES. SKIPGE T2 ;IF THERE IS AN "ODD" WORD, THAT WORD ADDI T1,5 ; FIVE EXTRA BYTES. NTDXM3: ADDI T1,1 ;ACCOUNT FOR THE "TYP" FIELD IN THE LENGTH ;CNT XMT T1 ;SEND THE LENGTH OF THE DAP MSG ;TYP POP P,T1 ;GET THE IDC TYPE BACK XMT1 T1 ;THIS WILL MASK THE TYPE TO 8 BITS ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;HERE TO COPY THE DATA FROM THE USERS BUFFER AND INTO THE PCB. AT THIS POINT: ; 1) DEVAXO+1(F) HAS BEEN UPDATED TO REFLECT THE NUMBER OF BYTES COPIED. ; 2) T4 := THE NUMBER OF WORDS TO GO ; 3) (P) := THE CONVERSION CODE (REMEMBER...) ; 4) P3 := THE NUMBER OF BYTES IN PCB'S FIRST BUFFER (FROM "CNT", "TYP" ETC) ; 5) P2 - LAST USED BYTE IN THE PCB ADDB P3,PCBCTR(U) ;FIRST UPDATE THE PRIMARY BUFFER'S LENGTH MOVEI T1,1(P2) ;GET A POINTER TO THE FIRST FREE WORD IN THE PCB LDB T2,[POINT 6,DEVAXO(F),11] ;GET THE BYTE SIZE LSH T2,6 ;POSITION THE SIZE (BYTE POINTER STYLE) TLO T1,440000(T2) ;MAKE T1 INTO A FULL-FLEDGED BYTE POINTER MOVEM T1,PCBPT2(U) ;STORE AS THE SECONDARY BUFFER POINTER MOVE T2,DEVAXO(F) ;GET THE ADDRESS OF THE USER'S DATA ADDM T4,DEVAXO(F) ;UPDATE THE ADDRESS FOR NEXT TIME IBP T2 ;MAKE SURE WE POINT TO THE RIGHT WORD IFE FTXMON,< HRLI T1,(T2) ;SET UP THE BLT'S SOURCE FIELD ADDI T4,-1(T1) ;SET UP THE BLT'S TERMINATING ADDRESS EXCTUX ;COPY THE DATA > ;END IFE FTXMON IFN FTXMON,< HRRZ T3,T1 ;FORM MONITOR DESTINATION ADDRESS XSFM T1 ;GET PCS HRLI T2,(T1) ;FORM USER SOURCE ADDRESS MOVE T1,T4 ;GET LENGTH WHERE WE NEED IT XBLTUX T1 ;COPY THE DATA > ;END IFN FTXMON POP P,T1 ;GET THE CONVERSION CODE (FINALLY) PUSHJ P,NTDWRT ;SEND THE PCB. JRST CPOPJ1## ;GIVE GOOD RETURN ;TEMP SCRATCH FOR TESTING OUT ANF/NI NTDXNT::SKIPN DEVAXO(F) ;IS THE BUFFER SET UP? STOPCD .,STOP,ANIXMT, ;++ NO BUFFER SET UP WHEN CALLING NTDXNT PUSH P,T1 ;SAVE THE COMPRESSION CODE (UNTIL THE VERY END) PUSH P,T2 ;SAVE THE INTERRUPT-BIT+IDCTYP HRRZ T1,DEVOAD(F) ;NOW RANGE CHECK THE BUFFER. PUSHJ P,BRNGE## ; JUST INCASE HE DECIDED TO SWAP MOVE T1,DEVAXO+1(F) ;GET THE NUMBER OF BYTES LEFT. MOVE T3,-1(P) ;RETRIEVE PCV.XX CODE CAIE T3,PCV.BN ;12-BIT BINARY? JRST NTDXN0 ;NO, ONE USER-BYTE PER NETWORK-BYTE THEN LSHC T1,-1 ;YES. T1=PAIRS OF BYTES (=24 BITS) IMULI T1,^D3 ;EACH PAIR OF BYTES IS WORTH 3 NETWORK BYTES CAIGE T2,0 ;DANGLING ("ODD") BYTE? ADDI T1,^D2 ;YES, THAT'S WORTH ANOTHER 1.5 (OR SO) BYTES NTDXN0: PUSH P,T1 ;SAVE BYTE COUNT ADDI T1,3 + <4*4> ;START WITH 4 WORDS (MAX) OF HEADER LSH T1,-2 ;T1=WORD SIZE FOR PCB BUFFER CAILE T1,MSGXMW## ;IF THIS IS MORE THAN THE "MAXIMUM" PCB SIZE MOVEI T1,MSGXMW## ; THEN JUST REQUEST THE "MAXIMUM" MOVEI T3,NTDHDR ;ASSUME THAT THIS IS NOT AN INTERRUPT MESSAGE MOVEI T2,(1B0) ;GET THE "INTERRUPT MESSAGE" BIT TDNE T2,-2(P) ; AND IF IT'S SET MOVEI T3,NTDHDI ; THEN GET AN INTERRUPT HEADER. PUSHJ P,(T3) ;GO BUILD THE HEADER JRST [ADJSP P,-3 ;IF NO CORE, CLEAN UP THE STACK SETZ T1, ; CLEAR T1 TO SAY ERROR WAS NO FREE CORE POPJ P,] ; AND GIVE AN ERROR RETURN TO THE CALLER MOVE T1,PCBALN(U) ;GET THE TOTAL NUMBER OF WORDS IN THE PCB LSH T1,2 ;T1=TOTAL BYTES IN THE PCB SUB T1,PCBCTR(U) ;DISCOUNT BYTES USED IN NCL HEADER CAML T1,0(P) ;SKIP IF PCB WON'T FINISH OFF THE BUFFER JRST NTDXN1 ;GO TO CODE TO SEND FINAL FRAGMENT OF BUFFER. ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;HERE IF WE HAVE TO "FRACTURE" THE BUFFER INTO SEVERAL MESSAGES, AND THIS ; IS NOT THE LAST FRAGMENT OF THE BUFFER. ; T4 := NUMBER OF BYTES LEFT IN THE PCB (WE WILL USE THEM ALL) MOVE T1,-1(P) ;GET THE IDC TYPE FIELD ANDI T1,77 ;AND JUST THE TYPE (MASK OUT THE INTERRUPT BIT) CAIL T1,DC.DAT ;NOW SEE IF THIS IS A MESSAGE TYPE THAT WE CAILE T1,DC.DAR ; CAN LEGALLY FRAGMENT INTO MULTIPLE MESSAGES JRST [ADJSP P,-3 ;CAN'T BREAK THE MESSAGE, CLEAN UP STACK MOVEI T1,IOBKTL ;TELL CALLER ERROR WAS BLOCK-TOO-LARGE POPJ P,] ; AND GIVE AN ERROR RETURN MOVEI T1,DC.DAT ;CHANGE THE MESSAGE TYPE TO BE DATA W/O E-O-R. MOVEM T1,-1(P) ; . . . MOVN T2,0(P) ;GET MINUS THE BYTE COUNT THAT WILL FIT MOVE T3,-2(P) ;RETRIEVE PCV.XX CODE AGAIN CAIN T3,PCV.BN ;12-BIT BINARY? JRST [IDIVI T2,^D3 ;T2:=COUNT OF 12-BIT-BYTE PAIRS CAIE T3,0 ;DANGLING BYTE? SUBI T2,1 ;YES, DISCOUNT IT TOO JRST .+1] ;BLUNDER ONWARDS ADDM T2,DEVAXO+1(F) ; AND UPDATE THE NUMBER OF UNSENT BYTES. SKIPG DEVAXO+1(F) ;JUST A QUICK CHECK OF MY ARITHMETIC STOPCD .,STOP,ANIUBN, ;++ UNSENT BYTES COUNT WENT NEGATIVE JRST NTDXN2 ;GO SEND "CNT", "TYP", AND THE DATA. ;HERE IF THE PCB WILL HOLD ALL THE REST OF THE BUFFER. ; T4 := BYTES LEFT IN THE USER'S BUFFER NTDXN1: SETZM DEVAXO+1(F) ;CLEAR BUFFER BYTE COUNT (THEY'LL ALL FIT) ; JRST NTDXN2 ;GO SEND "CNT", "TYP", AND THE DATA ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;NTDXN2 HERE TO SEND "CNT", "TYP", AND DATA PORTIONS OF THE MESSAGE. ; -2(P) := CONVERSION CODE (WE MUST HANDLE BINARY MODE ESPECIAL) ; -1(P) := IDC TYPE ; -0(P) := NUMBER OF NETWORK BYTES TO GO. ; ;NOTE. BECAUSE THE ETHERNET SERVICE LAYER REQUIRES 8-BIT BYTES ONLY, AND ; CANNOT DEAL WITH ANYTHING ELSE, WE MUST HANDLE ALL THE SHANNANIGANS ; NORMALLY SLOUGHED OFF ONTO THE FEK, LIKE LPT CHARACTER COMPRESSION, ; AND THE HANDWAVING ABOUT 12-BIT "BINARY" DATA CONVERSION. NTDXN2: MOVE P4,P2 ;COPY OF POINTER TO "CNT" FIELD POP P,T4 ;RETRIEVE DATA BYTE COUNT MOVEI T1,1(T4) ;ACCOUNT FOR THE "TYP" FIELD IN THE LENGTH XMT T1 ;SEND THE LENGTH OF THE DAP MSG POP P,T1 ;GET THE IDC TYPE BACK XMT1 T1 ;THIS WILL MASK THE TYPE TO 8 BITS MOVE P1,DEVAXO(F) ;USER'S BYTE POINTER POP P,T1 ;RETRIEVE DATA COMPRESSION TYPE CAIN T1,PCV.BN ;12-BIT BINARY DATA BYTES? JRST [MOVE T3,T4 ;YES, MUST ADJUST BYTE COUNT IDIVI T3,^D3 ;COUNT OF 12-BIT-BYTE-PAIRS LSH T3,1 ;COUNT OF 12-BIT-BYTES CAIE T4,0 ;ODD BYTE LEFT OVER? ADDI T3,1 ;YEAH, ACCOUNT FOR IT TOO MOVE T4,T3 ;REPOSITION DATA BYTE COUNT JRST .+1] ;AND COPY C(T4) USER DATA BYTES INTO PCB ;CONTINUED ON NEXT PAGE ;CONTINUED FROM PREVIOUS PAGE ;HERE TO COPY THE DATA FROM THE USERS BUFFER AND INTO THE PCB. AT THIS POINT: ; 1) DEVAXO+1(F) HAS BEEN UPDATED TO REFLECT THE NUMBER OF BYTES COPIED. ; 2) T1 := THE CONVERSION CODE (PCV.XX) ; 3) T4 := THE NUMBER OF USER N-BIT-DATA-BYTES TO GO ; 4) P1 := BYTE POINTER TO USER DATA BUFFER ; 5) P2 := BYTE POINTER TO PCB DATA BUFFER (PCBPTR) ; 6) P3 := THE NUMBER OF BYTES IN PCB'S FIRST BUFFER (FROM "CNT", "TYP" ETC) ; 7) P4 := BYTE POINTER TO "CNT" FIELD JRST @.+1(T1) ;DISPATCH ON CONVERSION TYPE IFIW NTDX00 ;00 -- PCV.NC -- NO CONVERSION IFIW NTDX10 ;01 -- PCV.LC -- LPT COMPRESSION IFIW NTDX20 ;02 -- PCB.BN -- 12-BIT BINARY BYTES ;HERE AFTER DATA COPY, GIVE PCB TO FEK/NETWORK NTDXN8: MOVEM P1,DEVAXO(F) ;SET BYTE POINTER FOR NEXT TIME (IF ANY) ADDM P3,PCBCTR(U) ;COUNT UP BYTES COPIED SETZM PCBCT2(U) ;NO SECONDARY BUFFER SETZM PCBPT2(U) ; . . . MOVEI T1,PCV.NC ;STRAIGHT 8-BIT BYTES FOR THE FEK PUSHJ P,NTDWRT ;SEND THE PCB. JRST CPOPJ1## ;GIVE GOOD RETURN ;HERE FOR NO CONVERSION TRANSMIT (STRAIGHT BYTE COPY) NTDX00: ADD P3,T4 ;ACCOUNT FOR DATA BYTES ; (IF NULL, THEN WILL MAKE ONE USELESS LOOP) NTDX02: EXCTUX ;GET NEXT USER DATA BYTE IDPB T1,P2 ;STORE IN PCB SOJG T4,NTDX02 ;LOOP FOR REST OF PCB'S WORTH OF DATA JRST NTDXN8 ;CAP OFF PCB AND SEND IT TO NETWORK ;HERE FOR LPT COMPRESSION ; ;COMPRESSED [7-BIT-ASCII] DATA IS FORMATTED AS FOLLOWS: ; ; 1CCCCCCC "CCCCCCC" IS THE 7-BIT ASCII CHARACTER ; 01SSSSSS "SSSSSS" IS THE COUNT OF SPACE CHARACTERS ; 001NNNNN "NNNNN" IS THE REPEAT COUNT FOR THE FOLLOWING CHARACTER NTDX10: ADDM P3,PCBCTR(U) ;ACCOUNT FOR HEADER DATA SETZ P3, ;USED TO COUNT ACTUAL DATA OUTPUT REPEAT 1,< ADD P3,T4 ;ACCOUNT FOR STRAIGHT BYTE COPY NTDX1L: EXCTUX ;GET NEXT USER DATA BYTE TRO T1,200 ;MARK IT UNCOMPRESSED IDPB T1,P2 ;STASH IT IN THE PCB SOJG T4,NTDX1L ;LOOP FOR WHOLE BUFFER JRST NTDXN8 ;OUTPUT THE PCB > ;END REPEAT 1 REPEAT 0,< PUSH P,P4 ;SAVE P4/"CNT" BYTE POINTER SETZ T1, ;INITIALIZE CHAR-REPEAT COUNT SOJL T4,NTDX18 ;COUNT A DATA BYTE EXCTUX ;READ FIRST USER DATA CHARACTER ;LOOP COUNTING CHARACTER REPEATITION NTDX11: SOJL T4,NTDX12 ;END OF USER DATA IS END OF CHAR REPEAT EXCTUX ;READ NEXT USER DATA CHARACTER CAMN T2,T3 ;REPEAT CHARACTER? AOJA T1,NTDX11 ;YES, COUNT NUMBER OF REPEATS ;HERE WHEN CHARACTERS DIDN'T MATCH, FLUSH PREVIOUS REPEATED CHARS NTDX12: JUMPE T1,[IORI T2,200 ;ONLY ONE CHAR, FLAG UNCOMPRESSED JRST NTDX17] ;AND STASH IN THE PCB BUFFER CAIN T2," " ;COMPRESSING SPACES? JRST NTDX15 ;YES, DIFFERENT FROM OTHER CHARACTERS ;T1 := REPEAT COUNT MINUS ONE, T2 := REPEATED CHARACTER NTDX13: CAILE T1,36 ;WAS THIS CHARACTER REPEATED A LOT? AOJA P3,[MOVEI P4,37+040 ;MAX REPEAT COUNT + COMPRESSION FLAG IDPB P4,P2 ;STORE REPEAT COUNT BYTE IDPB T2,P2 ;AND COMPRESSED CHARACTER SUBI T1,37 ;ACCOUNT FOR THIS PASS AOJA P3,NTDX13] ;SEE IF DONE WITH THIS CHARACTER ADDI T1,1+040 ;MAKE T1 INTO REPEAT COUNT + REPEAT FLAG IDPB T1,P2 ;STORE REPEAT COUNT BYTE AOJA P3,NTDX17 ;GO STORE COMPRESSED CHARACTER AND LOOP ;T1 := REPEAT COUNT MINUS ONE, T2 := SPACE CHARACTER NTDX15: CAILE T1,76 ;WAS THIS SPACE REPEATED A LOT? AOJA P3,[MOVEI P4,77+100 ;MAX REPEAT COUNT + COMPRESSION FLAG IDPB P4,P2 ;STORE REPEAT COUNT BYTE IDPB T2,P2 ;AND COMPRESSED SPACE SUBI T1,77 ;ACCOUNT FOR THIS PASS AOJA P3,NTDX15] ;SEE IF CAN FINISH WITH THIS SPACE ADDI T1,1+100 ;MAKE T1 INTO REPEAT COUNT + COMPRESSION FLAG IDPB T1,P2 ;STORE REPEAT COUNT BYTE AOJA P3,NTDX17 ;GO STORE COMPRESSED SPACE AND LOOP ;OUTPUT CHARACTER IN T2, LOOP FOR NEXT USER DATA CHARACTER NTDX17: IDPB T2,P2 ;OUTPUT CHARACTER IN T2 ADDI P3,1 ;ACCOUNT FOR IT SETZ T1, ;CLEAR REPEAT COUNT MOVE T2,T3 ;NEXT CHARACTER BECOMES CURRENT CHARACTER JUMPGE T4,NTDX11 ;LOOP FOR REST OF USER DATA BUFFER ;USER BUFFER EMPTIED, ADJUST OUTPUT BYTE COUNT AND SHIP THE PCB NTDX18: POP P,P4 ;RETRIEVE POINTER TO "CNT" FIELD AOS T1,P3 ;BYTE COUNT (DON'T FORGET THE "TYP" BYTE!) NTDX19: IDIVI T1,200 ;GET SEVEN-BIT'S WORTH OF NEW "CNT" FIELD ILDB T3,P4 ;ORIGINAL "CNT" BYTE TRNE T3,200 ;WAS IT EXTENSIBLIZED? TRO T2,200 ;YES, THEN SO IS NEW "CNT" BYTE DPB T2,P4 ;SET MODIFIED "CNT" BYTE TRNE T3,200 ;MORE "CNT" FIELD TO PROCESS? JRST NTDX19 ;YES, FLUSH IT OUT SOJA P3,NTDXN8 ;GO TRANSMIT THIS PCB > ;END REPEAT 0 ;HERE FOR BINARY (12-BIT BYTES) CONVERSION NTDX20: SOJL T4,NTDXN8 ;IF NO MORE DATA BYTES LEFT, XMIT THE PCB EXCTUX ;GET NEXT USER DATA BYTE SOJL T4,[LSHC T1,-^D4 ;ONLY ONE 12-BIT DATA BYTE LEFT AOJA P3,NTDX25] ;SEND IT AS TWO NETWORK BYTES ADDI P3,2 ;TWO 12-BIT BYTES COUNT AS THREE NETWORK BYTES EXCTUX ;GET NEXT USER DATA BYTE LSH T2,^D24 ;T1 AND T2 CONTIGUOUS DATA BITS LSHC T1,-^D4 ;T1 HAS 8 BITS, T2 HAS 16 BITS IDPB T1,P2 ;STORE FIRST 8 DATA BITS IN PCB LSHC T1,^D8 ;POSITION NEXT 8 DATA BITS NTDX25: IDPB T1,P2 ;AND STASH THEM IN THE PCB ALSO LSHC T1,^D8 ;POSITION LAST 8 DATA BITS IDPB T1,P2 ;AND STUFF THEM IN THE PCB AS WELL AOJA P3,NTDX20 ;LOOP FOR MORE DATA BITS COMMENT @ Protocol for writing Network DisPatch service routines. 1) Service routines should skip return if they liked the message, and non-skip (error) return if the message was bad. 2) Service routines may NOT assume that "U" points to the PCB that contains the current message. In particular, service routines may NOT call INCTBD. To "discard" a bad message, the service routine should simply non-skip (error) return. 3) Upon entry to the service routine the following registers are setup. P1 Contains a byte pointer pointing to the TYP field of this sub-message. (ie. an ILDB will get the first data byte after the TYP) P4 Contains the number of data bytes in the msg. (Excluding the TYP byte. (ie. CNT -1)) 4) Service routines (When called at UUO level) may page fault as long as they don't mind getting called again with the same sub-message. (ie. The sub-message isn't considered delivered until the service routine returns.) Implementation Notes. 1) In order to maintain the information regarding how much of a message we have processed, the following locations in the DDB are used. (Remember that a single NCL message may have numerous "sub-messages" for the designated device. In particular it may have 25 data-with- end-of-record messages. In that the user may have to do 25 "IN" uuo's in order to get all of the data. Another problem to worry about is that the way TOPS-10 handles page faults is to cease executing the uuo, and start the user at his page fault handler. The only way to get back to processing the message is for the PFH to start the uuo over again. Sigh) DEVPCB The rh of this word the head of the queue of unprocessed input PCB's for this device. The queue is in order of the messages' arrival. The top message on the queue is the one currently being processed. It should NOT be removed until all sub-messages have been processed. The lh currently contains the number of bytes left to be read in the current message. DEVPBP This is a byte pointer that points to the "CNT" field of the next NCL sub-message to process. (This will be a sub- message of the top PCB on the DEVPCB queue.) This pointer should not be advanced until the the sub-message has been accepted by the device service routine. In particular, it should not be ILDB'ed. @ ;NTDISP ROUTINE TO PERFORM THE DAP DISPATCH ON MULTIPART MESSAGES. ; CALL ONCE FOR EACH SUB-MESSAGE. ;CALL PUSHJ P,NTDISP ;CALL DEVICE DRIVER WITH NEXT SUB-MESSAGE ;RETURN CPOPJ ;IF MESSAGE WAS BAD. T1 WILL HAVE THE ; ; PC OF THE CODE THAT COMPLAINED. ; CPOPJ1 ;MESSAGE WAS ACCEPTED BY THE SERVICE ROUTINE. ; ;IF DEVPBP(F) = 0 THEN ALL SUBMESSAGES HAVE ; ; BEEN PROCESSED. NTDISP: MOVE P1,DEVPBP(F) ;GET THE POINTER TO THE NEXT SUB-MESSAGE HLRZ P4,DEVPCB(F) ;GET THE NUMBER OF BYTES LEFT IN THE MESSAGE ;CNT PUSHJ P,EBI2BI ;GET THE LENGTH OF THIS SUB-MESSAGE JUMPE T1,NTDIS1 ;IF ZERO, THEN ALL DONE WITH THIS MESSAGE SUBI P4,(T1) ;UPDATE THE NUMBER OF BYTES LEFT BY THIS SUB-MSG JUMPL P4,NTDIS2 ;BAD MESSAGE IF GOES PAST THE END. HRLM P4,DEVPCB(F) ;REMEMBER HOW MANY LEFT AFTER THIS SUB-MSG MOVEI P4,(T1) ;COPY THE LENGTH OF THIS SUBMESSAGE ;TYP PUSHJ P,EBI2BI ;GET THE IDC TYPE CAILE T1,0 ;NOW DO A LITTLE RANGE CHECKING CAILE T1,DC.MAX ; SO WE DON'T GO FLYING OFF INTO TROUBLE JSP T1,NTDIS2 ; BAD MESSAGE TYPE. PUT PC IN T1 AND RETURN MOVE S,DEVIOS(F) ;SET UP DEVICE STATUS HLRZ T2,DEVNET(F) ;GET THE ADDRESS OF THE NETWORK DEVICE TABLE PUSHJ P,@NDTNDP(T2) ;DISPATCH (T1 IS USED IN THE INDIRECTION...) JSP T1,NTDIS2 ;SERVICE ROUTINE CHOKED ON THAT ONE. GIVE ERROR. ;SERVICE ROUTINE ACCEPTED SUB-MESSAGE - MAKE SURE IT ATE IT ALL SOJGE P4,[IBP P1 ;IF THE SERVICE ROUTINE DIDN'T EAT ALL THE MSG, JRST .] ; MAKE SURE THAT P1 GETS ADVANCED. MOVEM P1,DEVPBP(F) ;SAVE POINTER TO NEXT SUB-MSG. HLRZ P4,DEVPCB(F) ;GET THE NUMBER OF BYTES LEFT IN MESSAGE JUMPG P4,CPOPJ1## ;IF THERE ARE SOME LEFT, RETURN TO NTDDID ;HERE WHEN ALL DONE WITH THE MESSAGE NTDIS1: SETZM DEVPBP(F) ;MARK MSG AS DONE PJRST CPOPJ1## ;GIVE GOOD (SKIP) RETURN ;HERE WHEN MSG IS BAD. RETURN WITH T1 := ADDRESS OF THE CODE THAT FOUND ; THE MESSAGE WAS BAD NTDIS2: SETZM DEVPBP(F) ;INDICATE NO PARTIAL MSG TO DO. POPJ P, ;GIVE ERROR RETURN ;NTDILD ROUTINE TO PROCESS INPUT MESSAGES AT "INTERRUPT" LEVEL ;CALL MOVEI U,PCB ; MOVE P1,BYTE POINTER TO FIRST "CNT" ; PUSHJ P,NTDILD ;RETURN CPOPJ ; ;NOTE!! THIS ROUTINE HANDLES DATA-REQUESTS. ;NOTE!! IF THE DEVICE SERVICE ROUTINE FINDS AN ERROR IN THE MESSAGE ; IT WILL PROBABLY ZERO U. NTDILD:: ;INTERRUPT LEVEL DAP DISPATCH PUSHJ P,NTDDOD ;ATTEMPT TO MAINTAIN CORRECT DRQ COUNT. MOVEM P1,DEVPBP(F) ;SET UP "PBP" FOR CALL TO NTDISP HRLM P4,DEVPCB(F) ;SAVE THE NUMBER OF BYTES LEFT IN THE MESSAGE NTDIL1: PUSHJ P,NTDISP ;DO THE DAP LEVEL DISPATCHING. JRST INCTBD ; BAD MESSAGE. T1 HAS PC OF CODE THAT NOTICED. SKIPE DEVPBP(F) ;HAVE WE PROCESSED ALL SUB-MESSAGES? JRST NTDIL1 ; IF NOT, GO BACK AND DO THE NEXT ONE. S0JRST RMVPCB ;ALL DONE WITH THE MESSAGE. RETURN PCB ;NTDQIP ROUTINE TO QUEUE INPUT PCB'S AT INTERRUPT LEVEL FOR PROCESSING AT ; LOWER (UUO) LEVEL. ;CALL MOVEI U,PCB ; MOVEI P1,POINTER TO THE FIRST "CNT" AFTER THE "DLA" ; PUSHJ P,NTDQIP ;RETURN CPOPJ ;WITH PCB QUEUED ; ;NOTE IF THIS IS THE ONLY PCB QUEUED, WE SAVE "P1" SO WE WON'T HAVE TO ; SCAN THE NCL HEADER AGAIN. NTDQIP::PUSHJ P,NTDDOD ;ATTEMPT TO MAINTAIN CORRECT DRQ COUNT. HLLZS PCBBLK(U) ;CLEAR ANY STRAY POINTERS HRRZ T1,DEVPCB(F) ;GET HEADER OF QUEUE OF PCB'S JUMPE T1,NTDQI2 ; IF WE ARE FIRST, THEN WE ARE SPECIAL. CAIA ; OTHERWISE SKIP INTO LOOP TO FIND LAST PCB NTDQI1: MOVEI T1,(T2) ;ADVANCE TO THE NEXT PCB IN THE QUEUE HRRZ T2,PCBBLK(T1) ;GET NEXT PCB (OR NULL) JUMPN T2,NTDQI1 ;IF THERE IS ANOTHER PCB, THEN LOOP DOWN CHAIN HRRM U,PCBBLK(T1) ;LINK THIS PCB ON AS THE LAST IN THE QUEUE SETZ U, ;CLEAR U SO NETSCN WILL KNOW THAT PCB IS GONE. POPJ P, ;AND THAT'S ALL FOR NOW. NTDQI2: HRRM U,DEVPCB(F) ;SAVE THE PCB AS THE HEAD OF THE QUEUE. HRLM P4,DEVPCB(F) ; SAVE THE COUNT OF UN-PROCESSED BYTES IN MSG MOVEM P1,DEVPBP(F) ; STORE THE POINTER TO FIRST NCL SUB-MESSAGE. SETZ U, ;TELL NETSCN NOT TO FREE THE PCB PJRST NTDIAV ;TELL THE JOB THAT INPUT IS AVAILABLE. ;*** NOTE *** WE ONLY WAKE THE JOB ON THE ; THE ARRIVAL OF THE FIRST MESSAGE. ;*** FOOTNOTE *** COMMENT \ Because the count of outstanding data requests is decremented when the message is queued, the routine NTDXDQ should never be called while there are queued input messages. (If it is, it will not realize that there are unprocessed messages, and possibly request more data than the program has buffers for.) This enforces my belief that one does not want to send data requests if one has any unprocessed input data. Remember, these input PCB's are not going to a physical device. They should not have to wait to be processed. If there is some reason that they cannot be processed immediatly (ie the job has swapped) I don't think that it is a good idea to send more requests. \ ;NTDDID HERE TO PERFORM A "DELAYED INPUT DISPATCH" AT UUOLEVEL. ;CALL MOVEI F,DDB ; PUSHJ P,NTDDID ;DO DELAYED DISPATCH ;RETURN CPOPJ ; ;NOTE!! THIS ROUTINE (IN CONTRAST TO NTDILD) ONLY DISPATCHES 1 NCL SUB ; MESSAGE AT A TIME. IN OTHER WORDS, YOU MUST CALL THIS ROUTINE ; ONCE FOR EACH SUB-MESSAGE OF AN NCL MESSAGE. THE REASONING BEHIND ; THIS IS THAT IT ALLOWS THE DEVICE SERVICE ROUTINE TO DETERMING ; IF THERE ARE ENOUGH FREE BUFFERS TO ACCEPT THE DATA. ; ;NOTE!! THE FOLLOWING LOCATIONS IN THE DDB ARE USED TO MAINTAIN INFORMATION ; REGARDING HOW FAR WE HAVE GOTTEN WHEN TAKING APART MULTI-PART NCL ; MESSAGES. ; ; DEVPCB ; THE RIGHT HAND PART OF THIS IS THE HEAD OF THE QUEUE OF UNPROCESSED ; INPUT PCB'S FOR THIS DEVICE. ; THE LEFT HAND PART OF THIS IS THE COUNT OF THE NUMBER OF BYTES LEFT ; TO PROCESS IN THE CURRENT PCB. (IT IS THE GLOBAL "P4" FOR THE DISPATCH ; ROUTINES) IT IS INITIALIZED TO "PCBCTR - HEADER LENGTH" AND IS ; DECREMENTED BY THE LENGTH OF EACH SUB-MESSAGE PROCESSED. ; DEVPBP ; THIS IS A BYTE POINTER INTO THE MESSAGE THAT IS AT THE HEAD OF THE ; DEVPCB QUEUE. IT POINTS TO THE FIRST UN-PROCESSED SUB-MESSAGE. ; ; NTDDID:: ;HERE TO PROCESS NEXT PCB ON DEVPCB MOVE P1,DEVPBP(F) ;SEE IF WE HAVE TO PARSE THE NCL HEADER JUMPN P1,NTDDI1 ; WE WON'T IF WE ALREADY HAVE A POINTER TO DAP HRRZ U,DEVPCB(F) ;GET POINTER TO THE FIRST PCB. JUMPE U,CPOPJ## ; BUT RETURN NOW IF THERE ISN'T ONE. MOVE P1,PCBPTR(U) ;WE HAVE TO PARSE THE NCL, SO GET POINTER TO MSG MOVE P4,PCBCTR(U) ;GET THE LENGTH OF THIS MESSAGE. ;NCT PUSHJ P,SKIP1 ;SKIP OVER THE NCT ;DNA PUSHJ P,XSKIP ;SKIP OVER THE DESTINATION NODE ADDRESS ;SNA PUSHJ P,XSKIP ;SKIP OVER THE SOURCE ;NCA PUSHJ P,SKIP1 ;SKIP OVER THE ACK ;NCN PUSHJ P,SKIP1 ;SKIP OVER THE MESSAGE NUMBER ;DLA PUSHJ P,XSKIP ;SKIP OVER OUR LINK ADDRESS MOVEM P1,DEVPBP(F) ;SET UP PBP FOR NTDISP HRLM P4,DEVPCB(F) ;SAVE THE LENGTH OF THE REST OF THE MESSAGE NTDDI1: ;HERE TO DISPATCH THE NEXT SUB-MESSAGE PUSHJ P,NTDISP ;DO THE DISPATCH JRST NTDDI2 ;ERROR RETURN. GIVE THE BAD PCB TO INCTBD. SKIPE DEVPBP(F) ;HAVE WE READ ALL SUB-MESSAGES? POPJ P, ;IF NOT, DON'T FREE THE PCB YET. HRRZ U,DEVPCB(F) ;ALL DONE WITH THIS PCB. GET ADDRESS TO IT HRRZ T2,PCBBLK(U) ; GET ADDRESS OF THE NEXT ONE HRRZM T2,DEVPCB(F) ; SET THE NEXT ONE UP TO BE PROCESSED NEXT TIME. S0JRST RMVPCB ;RETURN THE PCB AND EXIT NTDDI2: ;HERE IF THE SERVICE ROUTINE DIDN'T LIKE MSG. HRRZ U,DEVPCB(F) ;GET THE OFFENDING PCB HRRZ T2,PCBBLK(U) ;GET POINTER TO NEXT PCB IN THE CHAIN HRRZM T2,DEVPCB(F) ; AND MAKE THAT THE NEXT TO BE PROCESSED. PJRST INCTBD ;SIGNAL ERROR (PC OF ERROR IS IN T1) ;NTDCDQ CHECK FOR OUTPUT DATA REQUESTS. ;CALL MOVEI F,DDB ; PUSHJ P,NTDCDQ ;RETURN CPOPJ ;NO DATA-REQUEST AVAILABLE (DEVDRQ = 0) ; CPOPJ1 ;DATA REQUEST IS AVAILABLE ; NTDCDQ:: ;CHECK FOR OUTPUT DATA REQUESTS HRRZ T1,DEVDRQ(F) ;GET NUMBER OF OUTSTANDING DATA REQUESTS JUMPG T1,CPOPJ1## ;IF POSITIVE, GIVE GOOD RETURN POPJ P, ; RETURN ;NTDDDQ DECREMENT DATA REQUEST COUNT ;CALL MOVEI F,DDB ; PUSHJ P,NTDCDQ ;RETURN CPOPJ ;ALWAYS (DEVDRQ := DEVDRQ-1) ; NTDDDQ::PUSHJ P,NTDCDQ ;MAKE SURE WE HAVE OUTSTANDING DRQ'S STOPCD .,STOP,ANFDDQ, ;++ DATA REQUEST COUNT WENT NEGATIVE SOS DEVDRQ(F) ;COUNT DOWN THIS REQUEST POPJ P, ;AND RETURN ;NTDDOD DECREMENT OUTSTANDING DATA REQUEST COUNT. ;CALL MOVEI U,INCOMING PCB TO CHECK ; MOVEI F,DDB CONTAINING DATA-REQUEST INFORMATION ; PUSHJ P,NTDDOD ;RETURN CPOPJ ;WITH LH(DEVDRQ(F)) DECREMENTED IF NON-INTERRUPT ; NTDDOD: ;HERE TO CHECK IF THIS IS NON-INTERRUPT DATA MOVE T1,PCBPTR(U) ;GET POINTER TO MESSAGE ;NCT ILDB T1,T1 ;GET THE FIRST BYTE OF THE MESSAGE TRNE T1,NCT.IT ;INTERRUPT MESSAGE?? POPJ P, ;YES, DON'T MUNG OUTSTANDING DRQ COUNT MOVSI T1,-1 ;INDICATE ONE LESS ADDM T1,DEVDRQ(F) ; OUTSTANDING DATA-REQUEST POPJ P, ;ALL DONE ;NTDXDQ SUBROUTINE TO SEND DATA REQUESTS BASED ON EMPTY BUFFERS. ;CALL MOVEI F,DDB ; PUSHJ P,NTDXDQ ;TRY TO SEND DATA REQUESTS ;RETURN CPOPJ ;ALWAYS. ; NTDXDQ:: ;HERE TO SEND DATA REQUESTS SETZ T1, ;FIRST COUNT BUFFERS (INITIALIZE COUNT TO ZERO) HRRZ T2,DEVIAD(F) ;GET POINTER TO THE FIRST BUFFER MOVEI T3,(T2) ;REMEMBER START OF RING INCASE ALL ARE EMPTY NTDXD1: UMOVE T2,(T2) ;GET THE BUFFER USE BITS ERJMP NTDXD2 ;PAGED OUT, STOP COUNTING BUFFERS JUMPLE T2,NTDXD2 ;IF IT'S NOT FREE, STOP COUNTING BUFFERS ANDI T2,-1 ;MASK OFF THE "SIZE" PART OF THE HEADER WORD CAIGE T1,MAXODR## ;IF WE HAVE ENOUGH DATA REQUESTS, CAIN T2,(T3) ; OR WE HAVE COUNTED ALL THE BUFFERS. AOJA T1,NTDXD2 ; THEN TRY TO SEND THE REQUEST AOJA T1,NTDXD1 ;OTHER WISE KEEP COUNTING FREE BUFFERS. NTDXD2: ;HERE WITH THE NUMBER OF FREE BUFFERS IN T1 HLRZ T2,DEVDRQ(F) ;GET THE NUMBER OF OUTSTANDING DRQ'S SUBI T1,(T2) ;FIGURE OUT HOW MANY MORE WE CAN SEND JUMPLE T1,CPOPJ## ;IF WE CAN'T SEND ANY, THEN RETURN NOW PUSH P,T1 ;SAVE THE NUMBER WE ARE REQUESTING PUSHJ P,NCSDRQ ;SEND THE DRQ MESSAGE JRST [PUSHJ P,NETSLP ;IF WE CAN'T THEN WAIT (*** SEE FOOTNOTE) MOVE T1,(P) ; GET THE COUNT BACK JRST .-1] ; AND TRY AGAIN POP P,T1 ;GET THE COUNT BACK HRLZI T1,(T1) ;GET IT IN THE LEFT HALF ADDM T1,DEVDRQ(F) ;AND UPDATE THE COUNT OF OUTSTANDING DRQ'S POPJ P, ;ALL DONE ;*** FOOTNOTE *** COMMENT \ I don't think that I can return to the user here since if this is his first DRQ then he may be stuck waiting for messages that will never come. I don't like the idea of making some one wait (he may be doing non-blocking I/O) but until someone devizes a way to give the job an input done interrupt when free core becomes available, I think this will have to be done. Note also that this routine cannot be called from interrupt level. There are reasons for this other than the fact that it's a bad idea to put an interrupt level to "NETSLP". The other main reason is that if this is running on an SMP KL-10 system looking at anything in a users address space at interrupt level screws up the cache. (It sets the cache valid bit in the CPU that may not be running the user.) The solution to this is to "OUCHE" any words that are looked at by interrupt level. Whatta pain. \ ;NTDRDQ ROUTINE TO PROCESS INCOMING DATA REQUESTS (HERE FROM ICMDRQ VIA ; THE DRQ DISPATCH. ;CALL MOVEI T4,"DRQ" COUNT ; PUSHJ P,NTDRDQ ;RETURN CPOPJ1 ;ALWAYS ; ;NOTE THIS ROUTINE WAKES THE JOB ONLY IF THIS WAS THE FIRST DATA REQUEST. ; NTDRDQ:: ;HERE IN RECEIPT OF A DATA REQUEST HRRZ T1,DEVDRQ(F) ;GET THE OLD DATA REQUEST COUNT ADDM T4,DEVDRQ(F) ;ADD IN THE NEW SKIPGE T1 ;SKIP IF THE OLD ONE WAS NON-NEGATIVE STOPCD .,STOP,ANFDRQ, ;++ DRQ WENT NEGATIVE ;*** SKIPG T1 ;SEE IF HE WAS WAITING FOR DATA REQUESTS MOVE S,DEVIOS(F) ;SET UP S PUSHJ P,NTDOAV ; IF HE WAS. THEN WAKE HIM JRST CPOPJ1## ;GIVE GOOD RETURN ;*** FOOTNOTE *** COMMENT \ For some as yet unknown reason, the "SKIPG" that is commented out breaks asynch I/O. Inspite of the fact that everything is interlocked via the "NT" INTERLOCK, there appears to be a race. The symptom is that the job does not get an output done interrupt, and hence is stuck waiting for one, even though he has several data requests. \ ;NTDWTI ;ROUTINE TO WAIT FOR INPUT DATA ;CALL PUSHJ P,NTDWTI ; ;RETURN CPOPJ ;IF NON-BLOCKING, OR IF PROG HAS AT LEAST ; ; ONE BUFFER AVAILABLE FOR READING ; CPOPJ1 ;IF BLOCKING AND WE NOW HAVE INPUT DATA NTDWTI::HRRZ T2,DEVBUF(F) ;IF THE USER HAS AT LEAST JUMPE T2,CPOPJ## ;IF NO BUFFERS, GET OUT NOW EXCTUX ; ONE BUFFER OF DATA AVAILABLE, EXCTUX ; (IE USE BIT IS SET) POPJ P, ;THEN RETURN NOW SO HE CAN RUN MOVE T2,DEVAIO(F) ;IF USER HAS NO DATA, THEN SEE IF TRNE T2,DEPAIO ; NON-BLOCKING I/O. IF SO, POPJ P, ; THEN JUST RETURN. (UUOCON WILL UNDERSTAND) PUSHJ P,NETHIB ;IF BLOCKING, THEN GO SLEEP TILL SOME COMES RETSKP ; (NOTE THAT "W" IS NO LONGER VALID) (NOR "S") ;NTDWTO ;ROUTINE TO WAIT FOR OUTPUT DATA REQUESTS ;CALL PUSHJ P,NTDWTO ;CALL TO SEE IF OUTPUT CAN BE DONE ;RETURN CPOPJ ;NO DATA-REQUESTS. BUT NON-BLOCKING I/O ; CPOPJ1 ;THERE ARE DATA REQUESTS. TRY MORE OUTPUT NTDWTO::MOVE T2,DEVAIO(F) ;IF THIS IS NOT NON-BLOCKING TRNN T2,DEPAIO ; STYLE I/O, THEN JRST [AOS (P) ; GIVE A SKIP (DO MORE OUTPUT RETURN) PJRST NETHIB] ; AFTER WE GET MORE DATA. ; (NOTE THAT "W" IS NO LONGER VAILD) ; HLRZ T2,DEVBUF(F) ;IF THE USER HAS AT LEAST ONE ; EXCTUX ; EMPTY BUFFER AVAILABLE FOR ; EXCTUX ; FOR FILLING, THEN ; JRST [TLNN F,OCLOSB ; UNLESS THIS IS FROM THE "CLOSE" UUO ; POPJ P, ; RETURN TO UUOCON TO LET THE USER CONTINUE. ; JRST .+1] ;IF IT IS THE CLOSE UUO, BLOCK. MOVSI S,IOSTBL ;IF IT IS NONBLOCKING. SET "TROUBLE" SO THAT IORB S,DEVIOS(F) ; UUOCON WON'T ADVANCE DEVBUF, AND POPJ P, ; LET UUOCON RUN THE USER SOME MORE ;*** FOOTNOTE *** COMMENT \ In NTDWTO it would be nice if it could return and let the user run if he had any buffers left to fill, but this appears not to work. What happens is that the WAIT1 code does a CALOUT trying to empty the last few buffers. NTDWTO trying to be clever quickly returns noticing that the user has a few buffers left to fill. The situation is clearly out of hand. The monitor hangs. One might think that we could do this for async I/O, but unfortunatly, under MSGSER, DEVBUF doesn't really mean anything. \ ;NTDIAV ;HERE TO TELL A JOB THAT IT HAS NETWORK INPUT. ;CALL PUSHJ P,NTDIAV ;RETURN CPOPJ ;ALWAYS ; ;THIS ROUTINE FIGURES OUT HOW THE JOB IS WAITING. (SLEEPING, OR NON-BLOCKING) ; AND USES AN APPROPRIATE METHOD TO WAKE IT. ; NTDIAV:: ;HERE TO NOTIFY JOB THAT IT HAS NETWORK INPUT. MOVE T1,DEVAIO(F) ;FIRST CHECK AND SEE IF THIS JOB TRNN T1,DEPAIO ; IS DOING NON-BLOCKING I/O PJRST NETWAK ;IF NOT, THEN IT'S SLEEPING, SO WAKE IT. PUSH P,DEVIOS(F) ;WE NEED TO SET INPUT IO DONE, SO SAVE DEVIOS. MOVSI S,IO!IOSTBL ;AND PRETEND THAT THE DEVICE IS ANDCAB S,DEVIOS(F) ; DOING INPUT (SO WE DON'T PSI OUTPUT DONE) PUSHJ P,SETIOD## ;SAY THAT IO IS DONE SO UUOCON WILL RUN. POP P,DEVIOS(F) ;IF IT'S A TSK, LET IT GO BACK TO OUTPUT. POPJ P, ;AND THAT'S ALL ;NTDOAV ;HERE TO TELL A JOB IT HAS DATA REQUESTS ;CALL PUSHJ P,NTDOAV ;RETURN CPOPJ ; ;THIS ROUTINE FIGURES OUT HOW THE JOB IS WAITING (EW, OR NON-BLOCKING) ; AND USES THE APPROPRIATE METHOD TO WAKE THE JOB. ; NTDOAV:: ;HERE WHEN DATA REQUESTS ARE AVAILABLE TLNE S,IOSREL ;HAS THIS DEVICE BEEN "RELEASED"? POPJ P, ; IF SO, THEN DON'T WAKE ANYONE. MOVE T1,DEVAIO(F) ;NOW SEE IF THIS DEVICE IS TRNN T1,DEPAIO ; OF THE NON-BLOCKING KIND. PJRST NETWAK ;IF NOT, THEN GET JOB OUT OF EVENT WAIT. PUSH P,DEVIOS(F) ;IF NON-BLOCKING, WE MUST GIVE THE JOB AN MOVSI S,IO ; OUTPUT DONE INTERRUPT. SO FIRST WE IORB S,DEVIOS(F) ; SAY WE ARE DOING OUTPUT (PSIIOD LOOKS) TLZ S,IOSTBL ;CLEAR TROUBLE SO PSIIOD WILL BE CALLED PUSHJ P,SETIOD## ;SAY THAT THE OUTPUT IS DONE POP P,DEVIOS(F) ;LET THE JOB CONTINUE INPUT IF IT WANTS POPJ P, ; AND THAT'S ALL. ;NTDCNC ROUTINE TO PROCESS THE CONNECT CONFIRM MESSAGE FOR "NORMAL" (LPT, CDR ; PLT ...) DEVICES. ;CALL MOVEI F,DDB ; MOVE P1,"POINTER TO SLA" ; MOVE P4,"NUMBER OF BYTES LEFT IN MSG" ; PUSHJ P,NTDCNC ;RETURN CPOPJ ;IF WE WEREN'T WAITING FOR A CONNECT CONFIRM ; CPOPJ1 ;AFTER WAKING THE JOB NTDCNC::LDB T1,NETSLA ;GET LOCAL LAT ADDRESS LDB T2,LATSTA ;GET THE CURRENT STATE CAIE T2,LAT.CC ;WAS IT CONNECT CONFIRM WAIT? POPJ P, ;IF NOT, GIVE THE "BAD MESSAGE" RETURN ;SLA PUSHJ P,EBI2BI ;FIND OUT WHAT LAT THE REMOTE GAVE US. DPB T1,NETDLA ;SAVE THE REMOTE LAT NUMBER. ;DPN(OBJ) PUSHJ P,EBI2BI ;WE DON'T REALLY CARE ABOUT THIS FOR NORMAL DEVS ;DPN(PID) PUSHJ P,EBI2BI ;WE SENT HIM THIS UNIT NUMBER ;SPN(OBJ) PUSHJ P,EBI2BI ;THE PARANOID MIGHT VERIFY THAT THIS IS OUR TYPE ;SPN(PID) PUSHJ P,EBI2BI ;READ THE UNIT NUMBER WE GOT. ANDI T1,17 ; WE ONLY ALLOW 16. UNITS DPB T1,PUNIT## ;STORE THE REMOTE UNIT NUMBER CAIG T1,9 ;OUT OF SIMPLE DIGIT RANGE? TROA T1,'0' ;NO, EASY TO MAKE SIXBIT ADDI T1,'A'-'9'-1 ;YES, MAKE SIXBIT THE HARD WAY DPB T1,[POINT 6,DEVNAM(F),35] ;UPDATE THE DEVICE NAME TO REFLECT THE ; NEW UNIT NUMBER ;MML AND FEA PUSHJ P,NTDCNF ;PROCESS REST OF CONNECT CONFIRM (MML & FEA) POPJ P, ;DUH? LDB T1,NETSLA ;GET THIS MESSAGES "DLA" BACK MOVEI T2,LAT.OK ;NOW THAT WE'RE CONNECTED, CHANGE OUR DPB T2,LATSTA ; TO BE "OK". MOVSI S,IOSCON ;ALSO REMEMBER TO SET IOSCON TO IORB S,DEVIOS(F) ; TELL THE REST OF THE WORLD AOS (P) ;SIGNAL GOOD RETURN TLNN S,IOSZAP ;IS THIS DDB STILL IN USE? PJRST NETWAK ;YES, WAKE THE WAITING JOB. EMRGCY ;USE "EMERGENCY" MEMORY IF NECESSARY PUSHJ P,NTDXDS ;NO, SEND A DISCONNECT AND CHANGE STATE STOPCD .,STOP,ANFXDS, ;++ SHOULDN'T FAIL. NETSCN DIDN'T CALL PBCHECK? POPJ P, ;DDB WILL WAIT FOR DISCONNECT CONFIRM ;NTDCNF ROUTINE TO PROCESS THE CONNECT CONFIRM'S MML AND FEA FIELDS ;CALL MOVEI F,DDB ; MOVE P1,"POINTER TO SLA" ; MOVE P4,"NUMBER OF BYTES LEFT IN MSG" ; PUSHJ P,NTDCNF ;RETURN CPOPJ ;IF ERROR (SHOULDN'T HAPPEN) ; CPOPJ1 ;MML AND FEA(DCM,RLN,...) READ, DCM IN T1 NTDCNF::PUSHJ P,EBI2BI ;START OFF WITH MAX MESSAGE LENGTH DPB T1,NETMML ;SAVE IT FOR ANYONE WHO CARES ;FEA(DCM) PUSHJ P,EBI2BI ;EXTRACT THE DATA MODES PUSH P,T1 ;SAVE THE DCM FOR CALLER TO PONDER ;RLN PUSHJ P,EBI2BI ;READ AND STORE THE RECORD LENGTH. (ALTHOUGH I DPB T1,NETRLN ; DON'T THINK ANYONE USES IT...) ;DVT PUSHJ P,EBI2BI ;GET THE DEVICE ATTRIBUTES AND STORE THEM. DPB T1,NETDVT ; (THESE ARE USED...) ;DVU PUSHJ P,EBI2BI ;GET THE DEVICE "UNIT" TYPE DPB T1,NETDVU ;STASH IN THE DDB ;DVV PUSHJ P,EBI2BI ;GET THE DEVICE "CONTROLLER" TYPE DPB T1,NETDVV ;STASH IN THE DDB TOO ;DFT PUSHJ P,EAS2SX ;GET THE "FORMS TYPE" MOVEM T1,DEVDFT(F) ;SAVE IT AWAY POP P,T1 ;RESTORE DCM (IF ANYONE CARES) JRST CPOPJ1## ;RETURN ;NTDDSC ROUTINE TO DO THE "DEFAULT" PROCESSING OF DISCONNECT MESSAGES. ;CALL MOVEI F,DDB ; MOVEI P4,LENGTH OF DISCONNECT MESSAGE ; MOVEI P1,POINTER TO THE "SLA" FIELD OF THE DISCONNECT ; PUSHJ P,NTDDSC ;RETURN CPOPJ ;SOMETHING WAS WRONG WITH THE MESSAGE ; CPOPJ1 ;SUCCESSFUL RETURN. NTDDSC::NTDBUG ;JUST CHECKING ... ;SLA PUSHJ P,EBI2BI ;GET THE SLA (AND IGNORE) ;RSN PUSHJ P,EBI2BI ;GET THE REASON DPB T1,NETRSN ; AND STORE IT IN THE DDB. LDB T2,NETSLA ;GET POINTER INTO OUR LAT LDB T2,LATST2 ;GET CONNECTION'S CURRENT STATE CAIL T2,LAT.CC ; AND MAKE SURE THAT IT'S CAILE T2,LAT.DC ; REASONABLE (CC, OK, OR DC) POPJ P, ;IF IT'S NOT, THEN GIVE "BAD MSG" RETURN AOS (P) ;WE LIKE THE MSG. ACCEPT IT. CAIN T2,LAT.OK ;IF DISCONNECT INITIATE JRST NTDDS1 ;SEND THE DISCONNECT CONFIRM MOVEI T1,IR.DOL ;DEVICE OFFLINE PSI FLAG JRST NTDNW2 ;CONTINUE WITH COMMON CODE ; HERE SEND DISCONNECT CONFIRM NTDDS1: MOVEI T1,RSN.OK ;NORMAL RETURN "REASON" EMRGCY ;USE "EMERGENCY" FREE CORE IF NECESSARY PUSHJ P,NCSDSC ;SEND THE DISCONNECT STOPCD .,STOP,ANFDS1, ;++ SHOULDN'T FAIL. NETSCN DIDN'T CALL PCBECK? MOVEI T1,IR.IER!IR.OER!IR.DOL ;PSI FLAGS FOR UNEXPECTED DISCONNECT MOVEI T2,IONDD% ;REMOTE NETWORK DEVICE DISCONNECTED JRST NTDNW1 ;USE COMMON CODE TO WAKE THE OWNING JOB ;NTDCNT ROUTINE TO PERFORM THE CONNECT FOR "NORMAL" DEVICES. ;CALL MOVEI F,DDB ;WITH NO LAT ASSIGNED ; PUSHJ P,NTDCNT ;ASSIGN LAT AND DO CONNECT ;RETURN CPOPJ ;CONNECT FAILED. LAT FREED. ERROR CODE IN T1. ; CPOPJ1 ;DEVICE CONNECTED. ; NTDCNT:: ;HERE TO CONNECT "NORMAL" DEVICE. LDB T1,NETSLA ;FIRST A BIT OF PARANOIA. SKIPE T1 ;MAKE SURE THAT WE HAVEN'T ASSIGNED THE LAT STOPCD .,STOP,ANFLAA, ;++ LAT ALREADY ASSIGNED. MOVEI T1,(F) ;COPY THE DDB POINTER FOR GETSLA MOVEI T2,LAT.CC ;WE WILL BE IN CONNECT CONFIRM WAIT PUSHJ P,GETSLA ;ALLOCATE A LINK ADDRESS POPJ P, ;ERROR RETURN IF THE LAT IS FULL DPB T1,NETSLA ;STORE THE LINK ADDRESS IN THE DDB MOVE T1,[XWD NTDXSN,NTDXPN] ;ADDRESS OF CODE TO WRITE "SPN" & "DPN" PUSHJ P,NCSCNT ;SEND THE CONNECT PJRST GIVSLA ;CONNECT FAILED. RETURN LAT. ERROR CODE IN T1. NTDCN1: PUSHJ P,NETHIC ;WAIT FOR CONNECT (NTDCNC WILL WAKE US) LDB T1,NETSLA ;GET THE SLA BACK. JUMPE T1,[MOVEI T1,NRTDNA-NODERT ; CONNECT REJECT IF SLA CLEARED. POPJ P,] ; GIVE ERROR RETURN WITH ERROR CODE IN T1 LDB T1,LATSTA ;GET THE CURRENT "STATE" CAIN T1,LAT.OK ;IF IT'S "OK" THEN THE CONNECT SUCCEDED JRST CPOPJ1## ; SO GIVE A GOOD RETURN IFN PARANOID&P$LAT,< CAIE T1,LAT.CC ;ARE WE STILL IN CONFIRM WAIT STOPCD .,STOP,ANFLCC, ;++ NOT IN CONFIRM WAIT. LAT TABLE SCREWED. > JRST NTDCN1 ;WE HAD A SPURIOUS WAKE... ;NTDXPN ROUTINE TO SEND THE "DPN" FIELD OF A NORMAL DEVICE CONNECT. (LPT ETC.) ;CALLED FROM NCSCNT WHERE IT CALLS THE DEVICE DEPENDANT ROUTINE TO WRITE ; THE "DPN" ;RETURN CPOPJ ;ALWAYS ; NTDXPN:: ;HERE TO WRITE A NORMAL "DPN" CONNECT FIELD PUSH P,W ;SAVE THE NDB POINTER FOR A BIT ;DPN(OBJ) HLRZ W,DEVNET(F) ;GET THE POINTER TO THE NDT LDB T1,NDTOBJ ;GET THE OBJECT TYPE OF THIS DEVICE PUSHJ P,BI2EBI ;WRITE THE OBJECT TYPE POP P,W ;RESTORE THE NDB POINTER ;DPN(PID) LDB T1,PUNIT## ;GET THE UNIT NUMBER FOR THE PID PJRST BI2EBI ;WRITE THE UNIT NUMBER ;NTDXMN ROUTINE TO SEND THE "SPN" FIELD OF A NORMAL DEVICE CONNECT CONFIRM ;OPERATION (WHERE THERE IS NOT NECESSARILY A RELATED JOB TO USE . . . ;CALLED FROM NCSCNC WHERE IT CALLS THE DEVICE DEPENDANT ROUTINE TO WRITE ; THE "SPN" ;RETURN CPOPJ ;ALWAYS NTDXMN:: ;SPN(OBJ) MOVEI T1,OBJ.TK ;OBJECT IS A JOB PUSHJ P,BI2EBI ;WELL, THE MONITOR IS SORTA LIKE A JOB, KINDA ;SPN(PID) LDB T1,PJOBN## ;GET DEVICE'S ASSOCIATED JOB, IF ANY JUMPE T1,NTDXS3 ;AND USE IT, IF IT HAS A JOB MOVEI P1,[ASCIZ\The Big Blue Monitor[1,1]\] ;NO JOB PJRST AS2EAS ;SO FAKE UP A PID ;NTDXSN ROUTINE TO SEND THE "SPN" FIELD OF A NORMAL DEVICE CONNECT. (LPT ETC.) ;CALLED FROM NCSCNT WHERE IT CALLS THE DEVICE DEPENDANT ROUTINE TO WRITE ; THE "SPN" ;RETURN CPOPJ ;ALWAYS NTDXSN:: ;SPN(OBJ) MOVEI T1,OBJ.TK ;OBJECT IS A JOB PUSHJ P,BI2EBI ;OUTPUT ;SPN(PID) MOVE T1,.CPJOB## ;GET OUR JOB NUMBER NTDXS3: PUSH P,JBTPPN##(T1) ;SAVE THE JOB'S PPN MOVE T1,JBTNAM##(T1) ;GET THE PROCESS NAME PUSHJ P,SX2EAS ;OUTPUT LDB T1,P2 ;GET THE LAST CHARACTER BACK TRO T1,200 ;SET THE CONTINUE BIT DPB T1,P2 ;STORE AGAIN POP P,T1 ;GET THE PPN PJRST PP2EAS ;OUTPUT AS [P,P] ;NTDXDS ROUTINE TO SEND A DISCONNECT AND SET THE LINK STATUS (LT%STA) TO "DC" ;CALL MOVEI T1,REASON FOR DISCONNECT ; MOVEI F,DDB OF DEVICE TO SEND DISCONNECT TO ; PUSHJ P,NTDXDS ;RETURN CPOPJ ;NO CORE. ; CPOPJ1 ;DISCONNECT SENT. LINK STATE SET TO LAT.DC ; ; (DISCONNECT CONFIRM WAIT) ; NTDXDS:: ;HERE TO SEND A DISCONNECT AND SET LAT STATE PUSHJ P,NCSDSC ;ATTEMPT TO SEND THE DISCONNECT POPJ P, ; IF NO CORE, GIVE ERROR RETURN LDB T2,NETSLA ;GET A POINTER TO THE LAT ENTRY MOVEI T3,LAT.DC ;GET THE NEW STATE (DISCONNECT CONFIRM WAIT) DPB T3,LATST2 ;UPDATE THE STATE RETSKP ;GIVE GOOD RETURN ;NTDNWD ROUTINE TO PROCESS THE "NODE WENT DOWN" NDP DISPATCH ENTRY FOR ; "NORMAL" DEVICES. ;CALL MOVEI P1,NUMBER OF THE NODE THAT CRASHED. ; MOVE F,LAT-ENTRY THAT POINTS TO THIS DEVICE ; PUSHJ P,NTDNWD ;CALLED FROM CLNNDB ;RETURN CPOPJ ;ALWAYS. NTDNWD::MOVEI T1,RSN.XN ;GET A UNDEFINED NODE NUMBER ERROR CODE DPB T1,NETRSN ;AND PRETEND THAT A DISCONNECT PUT IT THERE HRROS DEVNAM(F) ;ZAP THE NAME SO DDBSRC WON'T MATCH IT. MOVEI T1,IOERR!IODEND ;ALL ERROR BITS IORM T1,DEVIOS(F) ;LIGHT THEM FOR DOWN DEVICE MOVEI T1,IR.IER!IR.OER!IR.DOL ;NODE DOWN PSI FLAGS MOVEI T2,IONND% ;NODE DOWN (OR PATH LOST) ERROR CODE ;ENTER HERE TO DISCONNECT THE DDB, ; T1 CONTAINING THE PSI REASON FLAGS, T2 CONTAINING EXTENDED ERROR CODE NTDNW1: DPB T2,PDVESE## ;SET EXTENDED (DEVOP.) ERROR STATUS CODE NTDNW2: LDB T2,PJOBN## ;GET OWNING JOB (IF ANY) JUMPE T2,NTDNW7 ;NO PSI/NETWAK IF NO JOB MOVE T2,DEVMOD(F) ;GET DEVICE ACTIVITY FLAGS TRNN T2,ASSCON!ASSPRG;DEVICE DEFINITELY IN USE BY A JOB? JRST NTDNW4 ;UNCLEAR, MAKE SURE NOT STUCK IN "EW" PUSHJ P,PSIDVB## ;YES, GIVE THE DEVICE OFFLINE/ETC. INTERRUPT MOVE S,DEVIOS(F) ;DEVICE STATUS FLAGS TLZ S,IOSTBL ;CLEAR TROUBLE SO SETIOD CALLS WAKJOB PUSHJ P,SETIOD## ;WAKE UP JOB IF HIBERING ON ASYNC I/O NTDNW4: PUSHJ P,NETWAK ;WAKE UP JOB IF IN ANF "EW" STATE ; NOTE THAT WE MUST CALL NETWAK EVEN IF ; ASSCON!ASSPRG IS OFF, SINCE A DEVICE IN ; CONNECT-CONFIRM WAIT HASN'T SET THESE BITS ; YET (DONE BY UUOCON, NOT NETSER) NTDNW7: PUSHJ P,GIVSLA ;RETURN THE NOW USLESS LAT ENTRY PUSHJ P,GV2NPD ;NPD'S ARE USELESS NOW. SETZM DEVDRQ(F) ;CLEAR STALE TSK DATA REQUESTS MOVSI S,IOSCON ;GET AND CLEAR THE ANDCAB S,DEVIOS(F) ; "DEVICE IS CONNECTED BIT" MOVSI S,IOSERR ;GET THE "NETWORK DEVICE ERROR" BIT IORB S,DEVIOS(F) ;AND SET IT TO STOP IO (NTDONL FAILS...) TLNE S,IOSZAP ;IF THIS DDB IS MARKED FOR DELETION PUSHJ P,UNLDDB ;THEN ZAP THE DDB HERE AND NOW POPJ P, ;ELSE WAIT FOR UUO LEVEL (ZAPNET) ;SUBROUTINE NTDHDR - WRITE A NCS/DEVICE CONTROL HEADER ;CALL MOVEI F,DDB ; MOVEI W,NDB ; MOVEI T1,SIZE OF PCB TO REQUEST (INCLUDING HEADER WORDS) ; PUSHJ P,NTDHDR ;RETURN CPOPJ ;NO CORE ; CPOPJ1 ;HEADER BUILT NTDHDR::TDZA T2,T2 ;CLEAR NCS NEADER BYTE NTDHDI::MOVEI T2,NCT.IT ;INTERRUPT MESSAGE PUSH P,P1 ;SAVE P1 FOR A BIT MOVEI P1,(T2) ;PUT THE NCT IN P1 PUSHJ P,MKNPCB ;MAKE A NUMBERED PCB JRST [POP P,P1 ;IF NO CORE, THEN RESTORE P1, AND POPJ P,] ; GIVE AN ERROR RETURN MOVE P2,PCBPTR(U) ;GET THE BYTE POINTER MOVE T1,P1 ;GET THE NCT PUSHJ P,NCSWHD ;FILL IN NCT, DNA, SNA, NCA & NCN LDB T1,NETDLA ;GET THE DESTINATION LINK ADDRESS IFN PARANOID&P$LAT,< SKIPN T1 ;JUST A BIT OF PARANOIA AGAIN STOPCD .,STOP,ANFMDL, ;++ MUST HAVE A DLA ASSIGNED > PUSHJ P,BI2EBI ;WRITE POP P,P1 ;RESTORE P1 (AREN'T WE NICE...) ADDM P3,PCBCTR(U) ;ACCOUNT FOR THE BYTES IN THE HEADER SETZ P3, ;SIGNAL NO BYTES IN THE MESSAGE JRST CPOPJ1## ;EXIT WITH HEADER WRITTEN ;SUBROUTINE NTDSST/NTDCST SET AND CLEAR STATUS BITS ;CALL MOVEI T1,BITS ;TO BE CLEARED OR SET ; PUSHJ P,NTDSST/NTDCST ; CPOPJ ;NO CORE ; CPOPJ1 ;MESSAGE SENT NTDXST::SETZ T2, ;SEND WITH A ZERO STC JRST NTDST1 ;ON TO MAIN LINE CODE NTDSST::SKIPA T2,[1] ;SET STATUS NTDCST::MOVEI T2,2 ;CLEAR STATUS NTDST1: PUSHJ P,SAVE3## ;SAVE THE P'S PUSH P,T1 ;SAVE STD PUSH P,T2 ;SAVE STC MOVEI T1,^D16 ;GET THE LENGTH OF A STANDARD SHORT PCB PUSHJ P,NTDHDI ;WRITE INTERRUPT HEADER JRST [POP P,T2 ;RESTORE THE MESSAGE TYPE PJRST TPOPJ##] ;AND EXIT ;CNT IBP P2 ;STEP OVER THE COUNT FIELD MOVE P1,P2 ;SAVE THE COUNT POSITION MOVEI T1,DC.STS ;GET STATUS MESSAGE CODE PUSHJ P,BI2EBI ;WRITE ;STC POP P,T1 ;GET THE STC PUSHJ P,BI2EBI ;WRITE ;STD POP P,T1 ;GET THE STD PUSHJ P,BI2EBI ;WRITE ;CNT DPB P3,P1 ;STORE THE COUNT FIELD ADDI P3,1 ;UPDATE THE COUNT FIELD ADDM P3,PCBCTR(U) ;UPDATE THE MESSAGE LENGTH PJRST NETWSR ;WRITE THE MESSAGE ;SUBROUTINE NTDWRT - WRITE A DATA MESSAGE ;CALL MOVEI F,DDB ; MOVEI U,PCB ; MOVEI T1,CONVERSION CODE ; PUSHJ P,NTDWRT ;SEND THE PCB ;RETURN CPOPJ ;WRITTEN ; NTDWRT::DPB T1,PCBPCV ;STORE TYPE ; MOVSI T1,PCB.UR ;GET THE USER DATA FLAG ; IORM T1,PCBBLK(U) ;STORE PJRST NETWRT ;SEND THE MESSAGE SUBTTL LOCKS -- NETSER INTERLOCK MANAGEMENT ROUTINES. COMMENT @ The following routines are used to manage the NETSER interlock. These routines are called ONLY by macros defined in "S". NTLCLK Lock the NETSER database from UUO-level. The job loops waiting for the interlock. (Can only happen on an SMP system when the other CPU has the interlock. NTUNLK Undo's the effects of a call to NTLCLK. NTLCKJ If the job currently owns the interlock this routine does nothing, Otherwise it gets the interlock and co-routines back to the calling routine (ala SAVE4) and frees the interlock when the routine POPJ's. NTLCKI This routine locks the NETSER database from interrupt level. NTULKI This routine undoes the effects of NTLCKI. NTCHCK This routine skips if the calling process owns the interlock. NTSAV This routine returns the interlock until the calling routine POPJ's at which point it gets it back again. (Like RTNEVM) NTLERR This routine gives up the interlock only if the process owns it, and never generates an error. (Used by ERRCON and friends to return the interlock when the owning process gets an error) Variables of interest are. NTLOCK This is the interlock word. -1 implies interlock if free. NTUSER This is the "Process I-D" of the process that owns the interlock. Legal values are. XWD 0,N N is the job number of the owning job XWD -1,n N is the number of the cpu that has the interlock at interrupt level XWD N,-1 N is the number of the cpu that has the interlock at clock (scheduler-comcon) level. %NTCNT This is a vector of counters that keep track of various facets of the interlock management scheme. Elements of the vector are + 0 Number of attempts to get interlock at interrupt level + 1 Number of times interrupt level succeded in getting lock + 2 Number of times interlock gotten at UUO level + 3 Number of times UUO level has failed. (Tries again immediatly. Will hang the machine if gets screwed up) + 4 Number of attempts at command level that failed. + 5 Number of times the interlock given up by error routines @ NTLCKI::NTDBUG EITHER,INT ;MAY HAVE IT AT ANOTHER LEVEL AOS %NTCNT+0 ;COUNT THE NUMBER OF ATTEMPTS AOSE NTLOCK## ;TRY TO GET THE INTERLOCK POPJ P, ;RETURN NOW IF FAILED IFN FTMP,< ;SET THE "OWNING CPU" WORD APRID INONT## > AOS %NTCNT+1 ;COUNT THE SUCCESSFUL ATTEMPTS PUSH P,T1 ;SAVE T1 OVER THE CALL TO NTLPID PUSHJ P,NTLPID ;GET A "UNIQUE" IDENTIFIER MOVEM T1,NTUSER ;SAVE IT IN CASE WE TRY TO RECURSE JRST TPOPJ1## ;AND GIVE A SUCCESSFUL RETURN NTULKI::NTDBUG YES,INT ;BETTER HAVE THE INTERLOCK AT INT LEVEL... PJRST NTLFRE ;FREE INTERLOCK AND RETURN NTLCLK::NTDBUG NO,UUO ;DON'T HAVE INTERLOCK YET, AND AT UUO LEVEL AOSA %NTCNT+2 ;COUNT THE NUMBER OF TIMES GOTTEN AT UUO LEVEL AOS %NTCNT+3 ;COUNT THE FAILURE SKIPGE NTLOCK## ;GIVE OTHER CPUS A CHANCE AOSE NTLOCK## ;TRY TO GET THE INTERLOCK JRST .-3 ;WAIT IFN FTMP,< APRID INONT## > PUSH P,T1 ;NOW THAT WE'VE GOT THE INTERLOCK, SAVE T1 PUSHJ P,NTLPID ;GET OUR "PROCESS ID" MOVEM T1,NTUSER ;SAVE IT IN CASE WE RECURSE JRST TPOPJ## ;RESTORE T1 AND RETURN NTUNLK::NTDBUG YES,UUO ;WE BETTER HAVE THE LOCK AT UUO-LEVEL PUSHJ P,NTLFRE ;FREE THE INTERLOCK SKIPN NTRQST ;DOES NETSCN WANT TO RUN? POPJ P, ;IF NOT, RETURN NOW PJRST NETQUE ;GO CAUSE AN INTERRUPT FOR NETSER NTLCKJ::PUSHJ P,NTCHCK ;SEE IF WE OWN THE INTERLOCK JRST .+2 ;IF NOT, WE MUST GET IT POPJ P, ;IF WE OWN IT, THEN WE CAN RETURN NOW SKIPE .CPISF## ;ARE WE AT "COMCON" LEVEL (COMMAND PROCESSING) JRST NTLCKC ;IF SO, WE MUST HANDLE THE INTERLOCK SPECIAL PUSHJ P,NTLCLK ;GET THE INTERLOCK POP P,(P) ;AND GO THROUGH THESE CONTORTIONS TO PUSHJ P,@1(P) ; CO-ROUTINE BACK TO OUR CALLER JRST .+2 ;RESPECT AOS (P) ; SKIP RETURNS PJRST NTUNLK ; AND FREE THE INTERLOCK ON CALLERS RETURN NTLCKC: PUSHJ P,NTLCKI ;TRY TO GET THE INTERLOCK (ALA INTERRUPT LEVEL) JRST [AOS %NTCNT+4 ;COUNT THE FAILURE JRST DLYCM1##] ; AND DELAY THE COMMAND (TRYS AGAIN SOON...) POP P,(P) ;CO-ROUTINE BACK PUSHJ P,@1(P) ; TO OUR CALLER JRST .+2 ;RESPECT AOS (P) ; SKIP RETURNS PJRST NTULKI ;RETURN THE INTERLOCK NTSAV:: PUSHJ P,NTCHCK ;SEE IF WE OWN THE INTERLOCK POPJ P, ;IF WE DON'T, WE CAN'T RETURN IT PUSHJ P,NTUNLK ;RETURN THE INTERLOCK POP P,(P) ;CO-ROUTINE BACK PUSHJ P,@1(P) ; TO OUR CALLER JRST .+2 ;RESPECT AOS (P) ; SKIP RETURNS PJRST NTLCLK ;AND GET THE INTERLOCK BACK AGAIN NTCHCK::AOSN NTLOCK## ;SEE IF ANYONE HAS THE INTERLOCK JRST [SETOM NTLOCK ; IF NOT, THEN WE CERTAINLY DON'T. POPJ P,] ; SO GIVE THE ERROR RETURN PUSH P,T1 ;WE NEED "T1" AS A TEMP PUSHJ P,NTLPID ;GET OUR "PROCESS ID" CAMN T1,NTUSER ;SEE IF IT'S US THAT HAS THE INTERLOCK AOS -1(P) ;IF SO, SKIP RETURN PJRST TPOPJ## ;RESTORE T1 AND (SKIP) RETURN NTLPID::PUSHJ P,INTLVL## ;IF WE ARE AT UUO LEVEL, JRST [MOVE T1,.CPJOB## ;THEN OUR PID IS OUR JOB NUMBER POPJ P,] ; SO RETURN THAT TO OUR CALLER HRRO T1,.CPCPN## ;ASSUME THAT WE ARE AT INTERRUPT LEVEL SKIPE .CPISF## ;CHECK TO SEE IF WE ARE AT SCHEDULER LEVEL, MOVS T1,T1 ; AND IF WE ARE, SWAP HALFWORDS POPJ P, ;RETURN "PID" TO CALLER NTLERR::PUSHJ P,NTCHCK ;DO WE OWN THE INTERLOCK POPJ P, ;IF NOT, THEN RETURN NOW AOS %NTCNT+5 ;COUNT NUMBER OF ERRORS WITH LOCK NTLFRE: SETZM NTUSER ;CLEAR THE USER. IFN FTMP,< SETOM INONT## ;CLEAR THE "OWNING CPU" WORD > SETOM NTLOCK## ;FREE THE LOCK POPJ P, ; AND RETURN SUBTTL NETWORK SUBROUTINES ;HERE ARE THE TWO ENTRIES TO TURN ON NETWORK INTERRUPTS NTONP1::AOS (P) ;GIVE SKIP RETURN NTONPJ::NETON ;RE-ENABLE NETWORK INTERRUPTS POPJ P, ;RETURN ;ROUTINES TO INTERFACE CERTIAN ERROR ROUTINES TO NETSER. THESE ARE ROUTINES ; THAT ARE NOT EXPECTED TO RETURN, AND HENCE MUST NOT BE CALLED WITH THE ; NETSER INTERLOCK. DEFINE X(A,B),< ;;MACRO TO MAKE THE CORRISPONDENCE MORE OBVIOUS A:: NTSAVE ;;FIRST "SAVE" THE INTERLOCK PJRST B##> ;;THEN GO TO ROUTINE "B" THAT DOES THE WORK X(NTDILI,ILLINP) ;ILLEGAL INPUT UUO X(NTDILO,ILLOUT) ;ILLEGAL OUTPUT UUO SUBTTL BYTE POINTERS FOR NETSER ;BYTE POINTERS INTO NETWORK DDB'S (INDEXED BY "F") NETRSN::EXP NT%RSN ;REASON BYTE NETSLA::EXP NT%SLA ;SOURCE LINK ADDRESS NETDLA::EXP NT%DLA ;DESTINATION LINK ADDRESS NETZWD::EXP NT%ZWD ;DDB LENGTH (A LA GETZWD/GIVZWD) NETRLN::EXP NT%RLN ;LOGICAL RECORD LENGTH ("RLN" - SEE NETPRM) NETMML::EXP NT%MML ;MAXIMUM MESSAGE LENGTH ("MML" - SEE NETPRM) NETDVT::EXP NT%DVT ;DEVICE ATTRIBUTE BITS ("DVT" - SEE NETPRM) NETDVU::EXP NT%DVU ;DEVICE "UNIT" TYPE ("DVU" - SEE NETPRM) NETDVV::EXP NT%DVV ;DEVICE "CONTROLLER" TYPE ("DVV" - SEE NETPRM) ;BYTE POINTERS INTO PCB'S. INDEXED BY "U" PCBPCV::EXP PC%PCV ;CONVERSION TYPE PCBMSN::EXP PC%MSN ;MESSAGE NUMBER ;BYTE POINTERS INTO NDB'S (GETTAB TABLE .GTNDB==161) $LOW NDBTBL::EXP NDBLEN ;(00) LENGTH OF NDB EXP NB%NXT ;(01) ADDRESS OF NEXT NDB EXP NB%NNM ;(02) NODE NUMBER EXP NB%SNM ;(03) ADDRESS OF STATION NAME NDBTIM::EXP NB%TIM ;(04) TIMER. EXP NB%NGH ;(05) FIRST NEIGHBOR ENTRY EXP NB%NGL ;(06) LAST NEIGHBOR ENTRY EXP NB%NGN ;(07) NODE NUMBER FROM NB%NGH EXP NB%OPR ;(10) ADDRESS OF OPR LDB EXP NB%CTJ ;(11) JOB NUMBER OF STATION CTL ; OUTPUT MESSAGE NUMBERS NDBLAR::EXP NB%LAR ;(12) LAST ACK RECEIVED NDBLAP::EXP NB%LAP ;(13) LAST OUTPUT MESSAGE# ACK'ED NDBLMS::EXP NB%LMS ;(14) LAST MESSAGE SENT NDBLMA::EXP NB%LMA ;(15) LAST MESSAGE NUMBER ASSIGNED NDBLAS::EXP NB%LAS ;(16) LAST ACK SENT ; INPUT MESSAGE NUMBERS NDBLMR::EXP NB%LMR ;(17) LAST INPUT MESSAGE RECEIVED NDBLMP::EXP NB%LMP ;(20) LAST MESSAGE PROCESSED EXP NB%SDT ;(21) SYSTEM BUILD DATE ADDRESS EXP NB%SID ;(22) SYSTEM ID ADDRESS EXP NB%MOM ;(23) MAXIMUM OUTSTANDING MESSAGE COUNT EXP NB%DEV ;(24) FIRST DEVICE EXP NB%NVR ;(25) NCL VERSION OF REMOTE NDBMXL==:<.-NDBTBL-1>_^D9 ;LENGTH OF NDB POINTERS TABLE (GETTAB) $HIGH ;POINTERS INTO A NETWORK DEVICE TABLE NDTTYP::EXP ND%TYP ;-10 DEVTYP BYTE NDTBFZ::EXP ND%BFZ ;-10 BUFFER SIZE NDTSPL::EXP ND%SPL ;-10 SPOOL BITS NDTDVT::EXP ND%DVT ;-11 DEVICE ATTRIBUTES NDTOBJ::EXP ND%OBJ ;-11 DEVICE TYPE CODE NDTDCM::EXP ND%DCM ;NETWORK DEVICE MODES ;POINTER TO THE "STATE" FIELD OF A LAT ENTRY (INDEXED BY T1) LATSTA::LT%STA+NETLAT(T1) ;POINT TO THE "STATE" FIELD OF A LAT ENTRY LATST2::LT%STA+NETLAT(T2) ;DITTO, INDEX BY T2 LATSP2::LT%STA+NETLAT(P2) ;DITTO, INDEX BY P2 LATPTR::LT%PTR+NETLAT(T1) ;POINT TO DDB/LDB ADDRESS LATPT2::LT%PTR+NETLAT(T2) ;DITTO, INDEX BY T2 LATPP2::LT%PTR+NETLAT(P2) ;DITTO, INDEX BY P2 SUBTTL LOCAL STORAGE FOR NETSER $LOW DIALDB::BLOCK 1 ;ADDRESS OF LDB WAITING FOR A DIAL REQUEST DIALJB::BLOCK 1 ;JOB NUMBER OWNING C(DIALDB) DIALFL::BLOCK 1 ;ASSOCIATED DIAL REQUEST FLAGS ; 1B0 DIALOUT COMPLETE ; LRLADR DIALOUT IN PROGRESS (REQ ISSUED) ; LRLCHR REMOTE PROCESSING DIALOUT REQ ; LRLDSR DIALOUT SUCCEEDED (IF 1B0) DIALNM::BLOCK 2 ;PHONE NUMBER FOR REMOTE DIALER NTUSER::EXP 0 ;LAST (CURRENT) OWNER OF THE NETSER INTERLOCK %NTCNT::BLOCK 10 ;A SET OF COUNTERS REGARDING THE USE OF THE ; NETSER INTERLOCK NTFLGS: 0 ;FLAGS FROM LAST MESSAGE RECIEVED NTFACK: 0 ;NONZERO IF WE RECEIVED A MESSAGE IN ; THE LAST JIFFY. THIS WILL CAUSE THE JIFFY ; CODE TO SEE IF ANY ACK'S NEED TO BE SENT ; TO ANY NODE. THIS IS AN EFFICIENCY HACK ; THAT ALLOWS US TO CUT-DOWN ON THE NUMBER OF ; TIMES WE MUST SCAN THE NDB-CHAIN DURING ; THE JIFFY CYCLE NTQRCT: 0 ;NONZERO IF WE NEED TO RECOMPUTE TOPOLOGY NTQJIF: 0 ;NONZERO IF NETSCN SHOULD DO JIFFY CHECK NTQOUT: 0 ;LIST OF OUTPUT MESSAGES BACK FROM THE FEK'S NTQINP: 0 ;LIST OF INPUT MESSAGES TO PROCESS NTQNDB: 0 ;CHAIN OF NDB'S THAT NEED SERVICE NTQSEC: 0 ;NON-ZERO IF NETSCN SHOULD DO ONCE/SEC NTRQST::0 ;NON-ZERO TO REQUEST AN INTERRUPT WHEN THE ; INTERLOCK IS NEXT FREE'D (NETLKU CHECKS IT) PCBECT: EXP 0 ;COUNT OF "EMERGENCY" PCB'S (GOTTEN BY ; CALLING "PCBEGT" PCBELS: EXP 0 ;LIST OF EMERGENCY PCB'S NTUEFC::EXP 0 ;NeTwork Use Emergency Free Core. THE ; NEXT CALL TO NCSHDR WILL USE "EMERGENCY" ; FREE CORE IF NECESSARY. IFN PARANOID&P$EAT,< ;DEBUGGING CODE TO EAT A RANDOM NCL MESSAGE %TROLL: EXP 77777777777 ;GOVERNS THE EATING OF MESSAGES. ;START BIG TO GIVE THINGS A CHANCE > IFN PARANOID&P$NTR,< ;CHECKING CODE TO SEE WHO GOT THE INTERLOCK %NTUSR: 0 ;PC OF THE LAST OWNER OF THE "NT" INTERLOCK > IFN PARANOID&P$COR,< ;ASSEMBLE IF WE ARE LOSING FREE CORE %NTMEM: 0 ;A POINTER TO ALL THE NETWORK CORE. > SUBTTL NETWORK STATISTICS DEFINE SUBTBL(MAX,LOC)<<B8+LOC-NETGTT>> ;ANF NETWORK STATISTICS NETGTT:: %NTCOR: 0 ;# WORDS OF FREE SPACE NOW IN USE %NTMAX: 0 ;MAXIMUM %NTCOR HAS GONE %NTAVG: 0 ;EXPONENTIAL AVERAGE OF %NTCOR * 10^4 %NTBAD: 0 ;# BAD MESSAGES RECEIVED AND IGNORED %NTRTP: SUBTBL(NCT.TP,NCLRTP) %NTRMT: SUBTBL(NC.MAX,NCLRMT) %NTRDL: SUBTBL(NETLNH-1,NCLRDL) %NTXTP: SUBTBL(NCT.TP,NCLXTP) %NTXMT: SUBTBL(NC.MAX,NCLXMT) %NTXDL: SUBTBL(NETLNH-1,NCLXDL) %NTBLC: 0 ;LH - PC OF DETECTION OF BAD MESSAGE ;RH - PCB ADDRESS OF BAD MESSAGE %NTBYI::BLOCK 1 ;COUNT OF BYTES INPUT BY THE NETWORK %NTBYO::BLOCK 1 ;COUNT OF BYTES OUTPUT BY THE NETWORK %NTNIP::EXP ANFNIP## ;ANF NI/ETHERNET PROTOCOL (0 IF NOT SELECTED) %NTNIM::EXP 526000040000 ;ANF NI/ETHERNET MULTICAST ADDRESS EXP ANFNIM## ; (SECOND HALF) ADDRESS = AB-00-04-00-XX-XX %NTNII::EXP ^D64 ;ANF NI/ETHERNET BROADCAST INTERVAL MAXIMA %NTNIJ::EXP ^D64*5 + 5 ;ANF NI/ETHERNET FEK KEEP-ALIVE TIMER VALUE ;ADD FIXED-OFFSET ENTRIES ABOVE THIS POINT NCLRTP: BLOCK NCT.TP+1 ;RECEIVED NCL TYPES NCLRMT: BLOCK NC.MAX+1 ;RECEIVED NUMBERED TYPES NCLRDL: BLOCK NETLNH ;RECEIVED DATA LENGTHS BY POWERS OF 2 NCLXTP: BLOCK NCT.TP+1 ;TRANSMITTED COUNTER PARTS OF ABOVE NCLXMT: BLOCK NC.MAX+1 NCLXDL: BLOCK NETLNH ;ADD SUBTABLE DATA ENTRIES ABOVE THIS POINT .NTMXL==:<.-NETGTT-1>_9 XLIST ;DON'T LIST THE LITERALS $LIT LIST NETEND::!END