Google
 

Trailing-Edge - PDP-10 Archives - BB-F493Z-DD_1986 - 10,7/703mon/netser.mac
Click 10,7/703mon/netser.mac to see without markup as text/plain
There are 10 other files named netser.mac in the archive. Click here to see a list.
TITLE NETSER - DEVICE INDEPENDENT NETWORK SERVICE ROUTINES - V1151
SUBTTL D. TODD/DRT/EJW/JBS/DRL  28 JAN 85
	SEARCH	F,S,NETPRM
	$RELOC
	$HIGH



;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
.CPYRT<1974,1986>
;COPYRIGHT (C) 1974,1975,1976,1977,1978,1979,1980,1982,1984,1986
;BY DIGITAL EQUIPMENT CORP., MAYNARD, MASS.
;ALL RIGHTS RESERVED.
;
;
XP VNETSER,1151		;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	INTERFACE TO AUTCON


; ROUTINE TO SET THE COUNT OF VARIOUS DEVICES IN THE HOST'S NDB.
; CALL:	MOVE	T1,<.TYXXX/.TYEST>,,NUMBER OF DEVICES
;	PUSHJ	P,NETDEV
;
; AC USAGE:	T1 - T4
;
IFN FTAUTC,<
NETDEV::HRRZ	T2,T1		;GET NUMBER OF DEVICES
	HLRZS	T1		;ISOLATE DEVICE TYPE
	MOVSI	T3,-<OBJ.MX+1>	;AOBJN POINTER
NETDE1:	HLRZ	T4,OBJTAB##(T3)	;GET A DEVICE TYPE
	CAME	T1,T4		;MATCH?
	AOBJN	T3,NETDE1	;TRY ANOTHER
	JUMPGE	T3,CPOPJ##	;RETURN IF NOT AN ANF-10 DEVCE
	PUSH	P,W		;SAVE FROM DESTRUCTION
	MOVEI	W,NETNDB##	;POINT TO OUR NDB
	DPB	T2,NETCNF##(T3)	;UPDATE DEVICE COUNT IN THE NDB
	JRST	WPOPJ##		;RESTORE W AND RETURN

> ;END IFN FTAUTC
SUBTTL INTERFACE TO COMCON
;ENTRY FROM COMCON WITH A NETWORK ASSIGN COMMAND
;
;	ASSIGN	NODENAME_DEV:LOGICAL
;
;COMCON HAS ALREADY SCANNED OF THE NODE NAME
;	T2=NODE NAME
;	T3=DELIMETER
;RETURN
;	CPOPJ			;NOT A NETWORK DEVICE. ERROR MESSAGE PRINTED
;	CPOPJ1			;IT IS A NETWORK DEVICE. T2 := THE "TRANSLATED"
;				; NAME OF THE FORM GGGNNU
NETASG::			;ENTRY
	MOVE	P1,T2		;SAVE THE STATION NAME
	CAIE	T3,"_"		;TERMINATED BY A _
	JRST	CPOPJ1##	;IF NOT, DON'T CONVERT THE NAME
	PUSHJ	P,COMTYS##	;YES, SKIP THE "_"
	NETDBJ			;INTERLOCK THIS CODE
	PUSHJ	P,CTXDEV##	;GET THE DEVICE NAME
	JUMPE	T2,NOTENF##	;ILLEGAL ARG LIST
	MOVE	P2,T2		;SAVE THE DEVICE NAME
	TRNE	T2,505050	;MUST BE NUMERIC GGGU
	JRST	NOTDEV##	;ERROR RETURN

	TLNE	P2,505050	;IS LH A NUMBER?
	JRST	NETAS1		;NO, CONTINUE NORMALLY
	MOVSS	P2,P2		;FLIP HALVES OF ARGUMENT
NETAS0:	TRNE	P2,77		;IS RIGHTMOST DIGIT ZERO?
	JRST	NETAS2		;NO, DONE
	LSH	P2,-6		;SHIFT RIGHT ONE DIGIT
	JRST	NETAS0		; AND TRY AGAIN
NETAS2:	TRO	P2,202020	;MAKE IT ALL DIGITS
	HRLI	P2,(SIXBIT/TTY/); AND MAKE LH SAY TTY
;HERE TO VALIDATE THE STATION NAME
NETAS1:	MOVE	T1,P1		;COPY THE STATION NAME
	PUSHJ	P,SRCNDB	;DOES THE STATION EXIST
	  JRST	[HRRZ T1,NRTUNN	;TYPE AN ERROR
		PJRST	ERRMES##]	;ERROR MESSAGE
	MOVE	T2,P2		;RESTORE DEVICE NAME
	HLRZ	P1,NDBNNM##(W)	;GET NODE #
	CAMN	P1,JBTLOC##	;LOCAL NODE?
	JRST	CPOPJ1##	;YES, JUST RETURN
	MOVEI	P1,(W)		;SAVE THE NDB POINTER

;HERE TO VALIDATE THE REMOTE DEVICE NAME
	HLRZ	T1,P2		;GENERIC NAME ONLY FROM THE LEFT HALF
	CAIN	T1,(SIXBIT /TTY/) ;IS IT TERMINAL?
	JRST	NETAST		;YES, ASSIGN IT
	TRNN	P2,505050	;MUST BE LEFT HALF NUMERIC OR ZEROS
	PUSHJ	P,SRCNDT	;SEARCH THE NDT
	  PJRST	NOTDEV##	;NOT A LEGAL DEVICE
	HLL	P2,NDTNAM(W)	;COPY THE GENERIC -10 DEVICE NAME
	MOVEI	W,(P1)		;GET THE NODE POINER BACK
	HLRZ	P1,NDBNNM(W)	;PUT THE NODE NUMBER IN P1

;HERE TO CONVERT THE DEVICE NAME INTO A NETWORK NAME
;DEVICE MUST BE OF THE FORM(S) GGGU,OR GGG

	MOVEI	T1,(P1)		;CONVERT THE NODE NUMBER TO SIXBIT
	TRO	T1,77700	;FORCE CONVERSION TO LINE UP
	S0PSHJ	CVTSBT##	;IN UUOCON
	MOVEI	T2,(P2)		;COPY THE UNIT NUMBER IF ANY
	LSH	T2,-14		;SHIFT THE UNIT NUMBER IF ANY TO LOW ORDER
	IORI	T2,(T1)		;COMBINE THE RIGHT HALF
	HLL	T2,P2		;AND INSERT THE GENERIC NAME
	JRST	CPOPJ1##	;RETURN WITH T2 := GGGNNU

NETAST:				;HERE IF NETWORK TERMINAL BEING ASSIGNED
	HRLZ	T1,P2		;GET THE REMOTE LINE NUMBER
	PUSHJ	P,CVTOCT##	;CONVERT IT TO OCTAL
	  JRST	NOTDEV##	;NON NUMERIC LINE NUMBER
	HLL	T1,NDBNNM(W)	;GET THE NODE NUMBER
	PUSHJ	P,ASGTTY##	;AND TRY TO CONVERT THE PAIR TO "TTYNNN"
	  JRST	NOTDEV##	;COULDN'T CONNECT. DEVICE NOT AVAILABLE
	MOVE	T2,T1		;COPY THE DEVICE NAME
	JRST	CPOPJ1##	;WE SUCCEDED.  T2 := DEVICE NAME
SUBTTL INTERFACE TO COMCON FOR THE "NODE" COMMAND

NODE.A::NETDBJ			;INTERLOCK THIS CODE
	PUSHJ	P,CTEXT1##	;GET THE ARGUMNET
	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 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	CPOPJ##		;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:	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)
	PJRST	CPOPJ1##	;CONNECT OK EXIT
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
;
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
	PUSHJ	P,N13SNM	;(02) CALL SUBROUTINE TO 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

N13LEN==.-N13TBL		;DEFINE LENGTH FOR RANGE CHECK
;N13SNM (02) RETURN THE SOFTWARE NAME (UP TO 6 SIXBIT CHARACTERS)

N13SNM:	HLRZ	T1,NDBSNM(W)	;GET ADDRESS OF THE SNM
	MOVE	T1,(T1)		;GET THE SNM
	POPJ	P,		;RETURN AND LET MAIN CODE STORE THE ANSWER.


;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	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 LOGICAL NAME TABLE ENTRY
	PUSHJ	P,CLRDDB##	;YES, CLEAR THE DDB
LNKDD1:	POP	P,F		;RESTORE THE DDB POINTER
	PUSHJ	P,SETDVL##	;SET UP THE LOGICAL NAME
	SKIPN	DEVLOG(F)	;WAS IT BEING CLEARED
	PUSHJ	P,ASSCK1##	;YES, RECLAIM SPACE
	DDBSRL
	HLRZ	T2,NETDDB+DEVSER  ;GET THE LINK POINTER
	HRLM	T2,DEVSER(F)	;LINK THIS TO THE PROTOTYPE
	HRLM	F,NETDDB+DEVSER  ;AND THE PROTOTYPE
	DDBSRU
	POPJ	P,		;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,NETDDB##	;GET THE STARTING DDB
UNLDB1:	MOVE	T1,T2		;FOLLOW THE DDB CHAIN
	HLRZ	T2,DEVSER(T1)	;NEXT DDB
	JUMPE	T2,CPOPJ##	;WENT AWAY
	CAIE	T2,(F)		;IS THIS THE ONE
	JRST	UNLDB1		;NO CONTINUE
	DDBSRL
	HLRZ	T2,DEVSER(F)	;GET THE NEXT DDB
	HRLM	T2,DEVSER(T1)	;REMOVE THE DDB LINKS
	DDBSRU
	POPJ	P,		;RETURN WITH LINK BROKEN



;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

IFN FTDDP,<			;IF DDPS ARE AROUND,
	LDB	T1,PDVTYP##	;GET THE DEVICE TYPE
	CAIN	T1,.TYDDP/.TYEST;IS THIS A DDP DEVICE?
	PJRST	ZAPDDP##	;YES, FIRST CHECK WITH DDPSER ON ZAPABILITY
> ;END IFN FTDDP

ZAPNE1::NETDBJ			;GET THE NETSER INTERLOCK
	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##	;GUVE 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
	MOVE	T1,[SIXBIT /PCBTAG/] ;GET AND SET THE "TAG"
	MOVEM	T1,PCBTAG(U)	;  WORD SO WE KNOW IT'S A PCB
IFN FTKL10,<			;ONLY NEED WORRY ABOUT CACHE ON A KL
	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
	HRRZ	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
	NETON			;ALL'S CLEAR
	HRLM	T1,NDBNNM(W)	;STORE
	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
IFN FTPI,<
	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
IFN FTPI,<
	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
	MOVE	W,NDTXWD##	;GET THE SEARCH POINTER
SRCNDC:	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
	ADDI	W,NDTLEN-1	;NO, STEP TO THE NEXT ENTRY
	AOBJN	W,SRCNDC	;AND TRY AGAIN
	POPJ	P,		;DEVICE NOT FOUND
SRCNDD:	PUSH	P,W		;SAVE THE NDT POINTER
	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
	SETZM	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>
	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, END OF NODEID THEN
	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
> ;END IFN FTENET

NCSNI4:	MOVSI	T1,FK.NID	;GET NODE ID FLAG
	IORM	T1,FEKBLK(J)	;SET FLAG NODE ID SENT
	ADDM	P3,PCBCTR(U)	;UPDATE THE COUNT
IFN FTCMSR,<AOS	NCLXTP+NCT.ID>
	AOS	(P)		;SKIP RETURN
IFN FTKL10,<			;ONLY NEED WORRY ABOUT CACHE ON A KL
	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)
	PUSHJ	P,(P1)		;LET CALLER WRITE THE DEST PROCESS DESCRIPTOR
;SPN(OBJ,PID)
	MOVS	P1,P1		;SWAP THE HALVES
	PUSHJ	P,(P1)		;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

;FALL THROUGH TO FIXUP THE LENGTH AND SEND THE MESSAGE
;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
	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
IFN FTCMSR,<
	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
RX2EBI:	IDIVI	T1,(T3)		;SEPERATE AGAIN
	HRLM	T2,(P)		;STORE THE BYTE
	SKIPE	T1		;ANY LEFT NOW
	PUSHJ	P,RX2EBI	;YES, TRY AGAIN
	HLRZ	T1,(P)		;GET THE LAST DIGIT BACK
	ADDI	T1,"0"		;NO 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

IFN FTTSK,<
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
>;END FTTSK
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
IFN FTCMSR,<
	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
;STILL IFN FTCMSR

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
;	MOVSI	T1,NDB.XA	;GET AND CLEAR THE "NEED TO ACK" FLAG
;	ANDCAB	T1,NDBFLG(W)	;  TO SIGNIFY ACK WENT THIS JIFFY
;NCN
	LDB	T1,NDBLMA##	;GET THE LAST MESSAGE NUMBER ASSIGNED
	SKIPGE	PCBBLK(U)	;NUMBERED MESSAGE
	ADDI	T1,1		;YES, UPATE THE COUNT
	DPB	T1,NDBLMA##	;STORE AS LAST ASSIGNED
	IDPB	T1,T4		;STORE THIS MESSAGE NUMBER
	DPB	T1,PCBMSN##	;SAVE THE MESSAGE NUMBER OF THIS PCB
IFN FTKL10,<			;ONLY NEED WORRY ABOUT CACHE ON A KL
	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