Trailing-Edge
-
PDP-10 Archives
-
bb-bt99q-bb
-
netser.x23
There is 1 other file named netser.x23 in the archive. Click here to see a list.
TITLE NETSER - DEVICE INDEPENDENT NETWORK SERVICE ROUTINES - V1204
SUBTTL D. TODD/DRT/EJW/JBS/DRL 02-MAY-89
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,1204 ;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: NONE
NETDEV::SETOM NTQCNF ;SET FLAG FOR NETSCN TO SCAN DDBTAB
POPJ P, ;RETURNT TO AUTCON
;HERE FROM NETSCN TO RECOMPUTE THE CONFIGURATION FROM DDBTAB/DDBCNT
NTSC.C: XMOVEI W,NETNDB ;POINT TO OUR NDB
SETZM NTQCNF ;CLEAR FLAG THAT BROUGHT US HERE
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
MOVEI T1,M.RJOB## ;SEE IF WE HAVE TSK SERVICE
CAMLE T1,DDBCNT##+.TYTSK ;IF MORE SERVICE THAN DDB'S,
MOVEM T1,DDBCNT##+.TYTSK ;THEN UPDATE THE COUNT
SETZ P1, ;CLEAR COUNT OF CHANGES
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,-<OBJ.MX+1> ;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
LDB T4,NETCNF##(T3) ;SAVE OLD DEVICE COUNT
DPB T2,NETCNF##(T3) ;UPDATE DEVICE COUNT IN THE NDB
CAIE T4,(T2) ;IF THE COUNTS ARE DIFFERENT,
AOS P1 ;NOTE THE CHANGE
NETDE3: AOBJN T1,NETDE1 ;LOOP FOR ALL DEVICES
PJUMPE P1,CPOPJ## ;NOTHING TO DO IF NO CONFIGURATION CHANGES
MOVSI P1,NDB.XC ;FLAG TO CAUSE SENDING A CONFIGURATION MESSAGE
NETDE4: HRRZ W,NDBNNM(W) ;POINT TO NEXT NDB (SKIP NETNDB)
PJUMPE W,CPOPJ## ;LEAVE IF DONE
IORM P1,NDBFLG(W) ;SET THE FLAG FOR NETSCN
JRST NETDE4 ;LOOP OVER ALL NODES
SUBTTL INTERFACE TO COMCON
;ENTRY FROM COMCON WITH A NETWORK ASSIGN COMMAND
;
; ASSIGN NODENAME_DEV:LOGICAL
;
;COMCON HAS ALREADY SCANNED OFF 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,-<OBJ.MX+1> ;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,<BRKFEK==:CPOPJ##>
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 <BLT T1,(T2)> ;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,<NODE3L==ND%INN> ;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 <LDB T1,[POINT 8,(P3),7]> ;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 <BLT T1,(T2)> ;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,<NODE42==ND%NMA> ;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 <BLT T1,(T2)> ;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 <COUNT-3> 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,-<OBJ.MX+1> ;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,<TLNN T1,DVCMDA> ;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,<PUSHJ P,PCBMRK> ;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,<PUSHJ P,NDBCHK> ;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,<ADDR OF DDB/LDB TO BE STORED IN NETLAT>
; 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+<STCDAT*4> ;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<NCT><DNA><SNA><NCA><NCN>
;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,<PUSHJ P,NDBCHK> ;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 <NNM><SNM><SID>
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 <NNM><SNM><SID>
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,<PUSHJ P,PCBMRK> ;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 <NNM><SNM><SID>
;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,<PUSHJ P,NDBCHK> ;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 <CNT> 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,<PUSHJ P,NDBCHK> ;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,<PUSHJ P,NDBCHK> ;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
MOVSI T1,NDB.XC ;WE GOT THE CORE,
ANDCAM T1,NDBFLG(W) ;CLEAR THE BIT THAT BROUGHT US HERE
MOVEI W,NETNDB## ;GET OUR NODE DATA BLOCK
MOVEI T1,NC.CNF ;CONFIGURATION MESSAGE
PUSHJ P,BI2EBI ;SEND
;OBJ
MOVSI P1,-<OBJ.MX+1> ;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,<SKPCPU (1)> ;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 8,1,15>-<POINT 8,0,7>] ;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_<NETLNH-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
;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,<PUSHJ P,PCBMRK> ;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,<PUSHJ P,PCBMRK> ;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,<POPJ P,> ;ALL DONE
IFN FTENET,<JRST NETRDD> ;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)
; G) NTQCNF THIS IS A FLAG THAT REQUESTS THE RECOMPUTATION AND
; RETRANSMISSION OF OUR CONFIGURATION.
;
; 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 NTQCNF ;NEED TO RECOMPUTE THE CONFIGURATION?
PUSHJ P,NTSC.C ; IF SO, DO IT
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
LDB T2,NDBLMS ;GET NUMBER OF LAST MESSAGE SENT
SUB T2,T1 ;SETUP FOR 8-BIT BYTE COMPARISON
TRNE T2,200 ;IF THIS PCB IS NEWER,
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 <DNA>
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 <SNA>
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'TH 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 ROUTING, 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
;SEND UPDATED CONFIGURATION
TLNE J,NDB.XC ;HAS HE HEARD ABOUT OUR CHANGED CONFIGURATION?
PUSHJ P,NCSCNF ;IF NOT, LET'S TELL HIM
JFCL ;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,<PJSP T1,INCTBD> ;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,<PJSP T1,INCTBD> ;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,<PUSHJ P,NDBCHK> ;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,<PUSHJ P,NDBCHK> ;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,<PUSHJ P,NDBCHK> ;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,<PUSHJ P,NDBCHK> ;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
LDB T1,NDBLAP ;GET LAST ACK'ED MESSAGE NUMBER
DPB T1,NDBLMS ;RESET SO WE DON'T ACCEPT ACKS UNTIL THE FEK IS
; DONE WITH THE MESSAGE AGAIN
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,<PUSHJ P,NDBCHK> ;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,<PUSHJ P,NDBCHK> ;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,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.
MOVSI T1,NDB.XC ;SEND-CONFIGURATION FLAG
IORM T1,NDBFLG(W) ;SET IT FOR NETSEC
RETSKP ;RETURN SUCCESS
;SUBROUTINE ICMCNF - CONFIGURATION
ICMCNF: ;ENTRY
IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;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 <OBJ.MX/4>+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_<NETLNH-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,<TLNN T1,FK.ETM> ;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 <SKIPL (T1)> ;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 <SKIPGE (T1)> ;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 <IDPB T1,DEVAXI(F)> ; 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 <HLRZ T3,(T1)> ;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 <DPB T2,T4> ;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 <HRRM T4,1(T1)> ;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 <HLRZ T3,(T1)> ;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 <HRRM T1,1(T2)> ; 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 <ILDB T1,DEVAXO(F)> ;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 <HRRZ T3,1(T1)> ;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 <BLT T1,(T4)> ;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 <ILDB T1,P1> ;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 <ILDB T1,P1> ;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 <ILDB T2,P1> ;READ FIRST USER DATA CHARACTER
;LOOP COUNTING CHARACTER REPEATITION
NTDX11: SOJL T4,NTDX12 ;END OF USER DATA IS END OF CHAR REPEAT
EXCTUX <ILDB T3,P1> ;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 <ILDB T1,P1> ;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 <ILDB T2,P1> ;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 <HRRZ T2,(T2)> ; ONE BUFFER OF DATA AVAILABLE,
EXCTUX <SKIPGE (T2)> ; (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 <HRRZ T2,(T2)> ; EMPTY BUFFER AVAILABLE FOR
; EXCTUX <SKIPL (T2)> ; 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
NTQCNF: 0 ;NONZERO IF WE NEED TO RECOMPUTE CONFIGURATION
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)<<<MAX>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