Google
 

Trailing-Edge - PDP-10 Archives - tops10_704_monitoranf_bb-x140c-sb - 10,7/mon/netser.mac
There are 13 other files named netser.mac in the archive. Click here to see a list.
TITLE NETSER - DEVICE INDEPENDENT NETWORK SERVICE ROUTINES - V1200
SUBTTL D. TODD/DRT/EJW/JBS/DRL  27-JUN-88
	SEARCH	F,S,NETPRM
	$RELOC
	$HIGH



;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED
;  OR COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (c) DIGITAL EQUIPMENT CORPORATION
; 1974,1975,1976,1977,1978,1979,1980,1982,1984,1986,1988.
;ALL RIGHTS RESERVED.

.CPYRT<1974,1988>


XP VNETSER,1200		;PUT VERSION NUMBER IN GLOB AND LOADER MAP

NETSER::ENTRY	NETSER		;LOADING IF IN LIBRARY SEARCH MODE
                           Comment @

                          Loose ends.

NETSER problems.

    o	The interface to PSISER with respect to PSINTC (Topology
	change) is poor.  Probably should have a bit in the NDB
	that indicates whether or not the interrupt has been
	given.

Network device (rdx, cdr, lpt, tsk etc) problems


Monitor problems (in routines other than NETSER)

    o	Make COMMON generate the right amount of free core.

DN87 problems (-11 code)

    o	LB.IBF gets filled up too easily.  Make it use a chunk?


Loose ends in general (Programs to write etc...)



				@
SUBTTL	SYSINI INITIALIZATION


	$INIT

NETINI::MOVE	T1,ANFNUM##	;ANF-10 STATION NUMBER
	HRLM	T1,NETNDB##+NDBNNM ;STUFF INTO OUR NDB
	MOVEI	T2,NETNDB##	;GET NDB ADDRESS FOR CENTRAL STATION
	HRRM	T2,.GTNDA##(T1)	;SAVE FOR NETSER
	MOVEM	T1,JBTLOC##	;AND FOR THE REST OF THE MONITOR
	MOVE	T1,ANFNAM##	;STATION NAME
	MOVEM	T1,NETNDB##+NDBSN2 ;SAVE IN NDB
	POPJ	P,		;RETURN

	$HIGH
SUBTTL	INTERFACE TO AUTCON


;ROUTINE TO SET THE COUNT OF VARIOUS DEVICES IN THE HOST'S NDB.
;CALL:	PUSHJ	P,NETDEV
;
; AC USAGE:	T1 - T4

NETDEV::PUSH	P,W		;SAVE W
	XMOVEI	W,NETNDB##	;POINT TO OUR NDB
	MOVEI	T1,M.RMCR##	;SEE IF WE HAVE AN MCR
	MOVEM	T1,DDBCNT##+.TYMCR ;FAKE OUT OUR LOOP BELOW
	HRRZ	T1,NETRTY##	;GET NUMBER OF LOCAL TERMINAL LINES
	MOVEM	T1,DDBCNT##+.TYTTY ;WE DON'T CARE ABOUT SCNN
	MOVSI	T1,-TYPMAX-1	;AOBJN POINTER TO DDB COUNT TABLE
NETDE1:	SKIPN	DDBTAB##(T1)	;IS THERE A PROTOTYPE DDB?
	JRST	NETDE3		;TRY THE NEXT ENTRY
	HRRZ	T2,T1		;GET .TY???
	MOVSI	T3,-<OBJ.MX+1>	;AOBJN POINTER
NETDE2:	HLRZ	T4,OBJTAB##(T3)	;GET A DEVICE TYPE
	CAME	T2,T4		;MATCH?
	AOBJN	T3,NETDE2	;TRY ANOTHER
	JUMPGE	T3,NETDE3	;FOUND ONE?
	MOVE	T2,DDBCNT##(T1)	;GET DEVICE COUNT
	DPB	T2,NETCNF##(T3)	;UPDATE DEVICE COUNT IN THE NDB
NETDE3:	AOBJN	T1,NETDE1	;LOOP FOR ALL DEVICES
	JRST	WPOPJ##		;RESTORE W AND RETURN
SUBTTL INTERFACE TO COMCON
;ENTRY FROM COMCON WITH A NETWORK ASSIGN COMMAND
;
;	ASSIGN	NODENAME_DEV:LOGICAL
;
;COMCON HAS ALREADY SCANNED OF THE NODE NAME
;	T2=NODE NAME
;	T3=DELIMETER
;RETURN
;	CPOPJ			;NOT A NETWORK DEVICE. ERROR MESSAGE PRINTED
;	CPOPJ1			;IT IS A NETWORK DEVICE. T2 := THE "TRANSLATED"
;				; NAME OF THE FORM GGGNNU
NETASG::			;ENTRY
	MOVE	P1,T2		;SAVE THE STATION NAME
	CAIE	T3,"_"		;TERMINATED BY A _
	JRST	CPOPJ1##	;IF NOT, DON'T CONVERT THE NAME
	PUSHJ	P,COMTYS##	;YES, SKIP THE "_"
	NETDBJ			;INTERLOCK THIS CODE
	PUSHJ	P,CTXDEV##	;GET THE DEVICE NAME
	JUMPE	T2,NOTENF##	;ILLEGAL ARG LIST
	MOVE	P2,T2		;SAVE THE DEVICE NAME
	TRNE	T2,505050	;MUST BE NUMERIC GGGU
	JRST	NOTDEV##	;ERROR RETURN

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

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

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

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

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

NODE.A::NETDBJ			;INTERLOCK THIS CODE
	SKIPN	T1,T2		;CHECK FOR AN ARG
	JRST	[PUSHJ P,FNDSTA## ;IF NO ARG, GET THE STATION OUR TTY
		 JRST NODEC1]	; IS AT, AND USE THAT ONE
	PUSHJ	P,CVTOCT##	;TRY TO CONVERT THE NUMBER
	  MOVE	T1,T2		; IF WE CAN'T, USE THE NAME

;TYPE OUT  A PARTICULAR NODE NAME
NODEC1:	PUSHJ	P,SRCNDB	;FIND THE NODE BLOCK
	 POPJ	P,		;NO SUCH NODE, GIVE FAIL RETURN (IF MORE
				; NETWORKS, LET THEM TRY).
;	PJRST	NODEC2		;TYPE THE INFO

NODEC2:	PUSHJ	P,TYPNDB	;TYPE IT
	PUSHJ	P,INLMES##	;TYPE CR-LF-TAB
	ASCIZ	/
	/
	MOVSI	P1,-<OBJ.MX+1>	;GET THE DEVICE TABLE SIZE
NODEC4:	LDB	T1,NETCNF##(P1)	;GET THE NUMBER OF DEVICES
	JUMPE	T1,NODEC5	;NONE DON'T PRINT
	HRLZ	T2,OBJTAB##(P1)	;GET THE DEVICE NAME
	PUSHJ	P,PRNAME##	;PRINT IT OUT
	MOVEI	T3,"["		;BRACKET
	PUSHJ	P,COMTYO##	;TYPE
	LDB	T1,NETCNF##(P1)	;GET THE NUMBER OF ENTRIES
	PUSHJ	P,RADX10##	;PRINT
	PUSHJ	P,INLMES##	;BRACKET SPACE
	ASCIZ	/] /
NODEC5:	AOBJN	P1,NODEC4	;CONTINUE TO THE END
	PUSHJ	P,PCRLF##	;END OF LINE
	JRST	CPOPJ1##	;AND RETURN SUCCESS
SUBTTL	INTERFACE TO COMCON


;WHERE COMMAND

CWHANF::MOVE	U,0(P)		;COMMAND TTY LDB
	PUSHJ	P,SRCNDB	;SEARCH FOR THE NDB
	  MOVEI	W,NETNDB##	;USE THE LOCAL MODE
	PUSHJ	P,TYPNDB	;TYPE THE NODE INFO
	PUSHJ	P,PRSPC##
	MOVE	T2,DEVNAM(F)
	PUSHJ	P,PRNAME##
	MOVSI	T1,DVTTY	;CHECK FOR A TTY
	TDNN	T1,DEVMOD(F)	;IS DEVICE A TTY?
	JRST	[PUSHJ	P,ZAPNET ;NO, FREE DDB IF APPROPRIATE
		 POP	P,U	;RESTORE U
		 PJRST	CRLF##]	;TYPE CRLF AND EXIT
	MOVE	U,DDBLDB##(F)	;YES, GET THE LDB
	LDB	T1,LDPLNO##	;GET THE LOCAL LINE NUMBER
	MOVSI	T2,LTLANF##	;ANF NETWORK VIRTUAL TERMINAL FLAG
	TDNE	T2,LDBTTW##(U)	;CHECK A BIT
	LDB	T1,LDPRLN##	;GET REMOTE LINE NUMBER
	POP	P,U		;RESTORE THE LDB
	PUSH	P,T1		;SAVE LINE NUMBER
	PUSHJ	P,INLMES##	;TYPE OUT
	ASCIZ	/ line # /
	POP	P,T1		;RESTORE LINE NUMBER
	PUSHJ	P,PRTDI8##	;TYPE IT
	PJRST	PCRLF##		;AND CRLF
SUBTTL	INTERFACE TO CPNSER


				COMMENT @

			    PCBMRK and PCBCHK

     These routines are used to manage the transfer of PCB's to and from
Front End Kontrollers in a Multi-Cpu, or Cached environment.  The protocol
for using these two routines is as follows.

   PCBMRK	This routine should be called after the PCB is altered by
		the -10.  In a cached environment, it stores the Cache Sweep
		Serial number so that PCBCHK can tell if cache needs to be
		swept at a later date.  On a non-cached, or multi-cpu machine
		(free core is not cached on a SMP system) this routine is a
		no-op.

   PCBCHK	This routine should be called with J := FEK address, and
		U := PCB address.  It should be immediatly before either
		"FEKRDD" or "FEKWRT" is executed.  If it skips, then that
		implies that the FEK may access the data.  If it does not
		skip, then either the FEK is on the wrong CPU, or the data
		is still in cache, and may not be accessed by the FEK.

				    @
IFN FTKL10,<			;THINGS ARE MESSY FOR KL'S
PCBMRK::CONSO	APR,LP.CSB+LP.CSD ;IF THE CACHE IS ALREADY SWEEPING (BUSY)
	TDZA	T1,T1		;  THEN IT MAY HAVE MISSED SOME OF THE PCB
	MOVEI	T1,1		;  IF SO, ADD ONE TO THE SWEEP SERIAL NUMBER
	ADD	T1,.CPCSN##	;GET SWEEP SERIAL NUMBER (MAYBE PLUS ONE)
	MOVEM	T1,PCBCSN(U)	;SAVE IT FOR PCBCHK TO LOOK AT
	POPJ	P,		;EXIT WITH THE PCB MARKED

PCBCHK::
IFN FTMP,<
	PUSHJ	P,MLSCSH##	;SEE IF FRECOR IS CACHED
	  POPJ	P,		;  RETURN NOW. (NO SWEEP PROBLEMS)
>;END FTMP
	MOVE	T1,.CPCSN##	;GET THE CURRENT CSSN
	CAMG	T1,PCBCSN(U)	; SEE IF WE'VE SWEPT SINCE LAST ACCESS TO PCB
	PUSHJ	P,CSDMP##	;IF NOT, SWEEP NOW.  (TOO SLOW TO WAIT)
	POPJ	P,		;WE'VE SWEPT, GIVE GOOD RETURN

>;END FTKL10
;ROUTINE CALLED FROM CPNSER WHEN A CPU HAS CRASHED.  THIS ROUTINE
;TELLS NETSER WHICH FRONT ENDS HAVE BECOME INACCESSABLE DUE TO THE
;CPU GOING DOWN, AND CRASHES THE APPROPRIATE FEKS.
;CALL:	MOVE	T1, ADDRESS OF THE CRASHED CPU'S CDB
;	PUSHJ	P,BRKFEK
;
;PRESERVES ALL ACS

IFE FTMP,<BRKFEK==:CPOPJ##>
IFN FTMP,<
BRKFEK::SKIPN	[M.CPU##-1]	;MULTI-CPU?
	POPJ	P,		;NO
	PUSHJ	P,SAVT##	;PRESERVE THE T'S
	MOVSI	T2,FK.CPD	;FLAG THAT SAYS "THIS FEK'S CPU DIED"
	SKIPA	T3,[FEKFST##]	;GET ADDRESS OF THE FIRST FEK.
BRKFE1:	HRRZ	T3,FEKBLK(T3)	;GET THE ADDRESS OF THE NEXT FEK.
	JUMPE	T3,CPOPJ##	;ZERO ADDRESS MEANS WEVE DONE THEM ALL
	HLRE	T4,FEKUNI(T3)	;GET THE CPU NUMBER OF THIS FEKS CPU
	CAME	T4,.CPCPN##-.CPCDB##(T1) ;SEE IF THIS IS THE CPU THAT DIED
	JRST	BRKFE1		;NO
	AOS	.CPNBI##-.CPCDB##(T1) ;COUNT THE BROKEN INTERLOCK
	IORM	T2,FEKBLK(T3)	;IF ON JUST-DEAD CPU, SET BIT FOR 1/SECOND
	JRST	BRKFE1		;GO CHECK THE NEXT FEK.
> ;END IFN FTMP
SUBTTL	INTERFACE TO UUOCON FOR DDB DISPATCH


SUBTTL	NETDDB DISPATCH TABLE

	JSP	T4,DSPOBJ	;(-5) ON LINE CHECK
	JSP	T4,DSPOBJ	;(-4) DEVOP. UUO
	JSP	T4,DSPOBJ	;(-3) RETURN BUFFER SIZE
	POPJ	P,		;(-2) DEVICE INITIALIZATION
	JSP	T4,DSPOBJ	;(-1) HUNG DEVICE
NETDSP::JSP	T4,DSPOBJ	;(0)  RELEASE DEVICE
	JSP	T4,DSPOBJ	;(1)  CLOSE
	JSP	T4,DSPOBJ	;(2)  OUTPUT
	JSP	T4,DSPOBJ	;(3)  INPUT
	JSP	T4,DSPOBJ	;(4)  ENTER
	JSP	T4,DSPOBJ	;(5)  LOOKUP
	JSP	T4,DSPOBJ	;(6)  DUMP MODE OUTPUT
	JSP	T4,DSPOBJ	;(7)  DUMP MODE INPUT
	JSP	T4,DSPOBJ	;(10) USETO
	JSP	T4,DSPOBJ	;(11) USETI
	JSP	T4,DSPOBJ	;(12) UGETF UUO
	JSP	T4,DSPOBJ	;(13) RENAME UUO
	JSP	T4,DSPOBJ	;(14) CLOSE INPUT
	JSP	T4,DSPOBJ	;(15) UTPCLR UUO
	JSP	T4,DSPOBJ	;(16) MTAPE UUO

;DISPATCH ON THE OBJECT TYPE
DSPOBJ:	SUBI	T4,NETDSP+1	;RELOCATE THE ENTRY
	HRRES	T4		;MAKE IT A FULL WORD NUMBER
	MOVSI	W,DVCNET	;GET THE NETWORK DEVICE BIT
	TDNN	W,DEVCHR(F)	;IS THIS A NETWORK DEVICE?
	STOPCD	CPOPJ##,DEBUG,DFU,;++DEVICE UNRECOGNIZED

IFN	PARANOID&P$DDB,<	;SOME CONSISTENCY CHECKING
	MOVSI	W,DVLNG		;THE LONG-DISPATCH FLAG
	TDNN	W,DEVMOD(F)	;CAN DEVICE HANDLE WHIZZY FUNCTIONS?
	CAIG	T4,DIN		;NO, WAS WHIZZY ACTION REQUESTED?
	CAIA			;REASONABLE FUNCTION
	PUSHJ	P,NTDSTP	;++ BAD DEVSER DISPATCH
IFN	FTXMON,<
	XMOVEI	W,.		;GET CURRENT PC
	TLNE	W,-1		;NETSER/NETDEV/ET AL REALLY WANT SECTION 0
	PUSHJ	P,NTDSTP	;CALLED FROM NON-ZERO SECTION!
> ;IFN FTXMON
> ;IFN PARANOID&P$DDB
	CAME	T4,[DDVO]	;EXCEPT FOR DEVOP. UUO
	JUMPL	T4,DSPOB1	;DON'T INTERLOCK HUNG DEVICE CHECK ETC.
				; (THEY COME IN AT LEVEL #7)
	NETDBJ			;NETSER INTERLOCK FROM HERE ON..

DSPOB1:	HLRZ	W,DEVNET(F)	;GET THE NDT POINTER
	PJRST	@NDTDSP(W)	;GO TO DEVICE DEPENDENT SERVICE ROUTINE
				; (NDTDSP IS @N---DP(T4))
SUBTTL	INTERFACE TO UUOCON FOR THE NODE UUO

;NODE. UUO SUB-FUNCTIONS
;	.NDALN==1		;ASSIGN LOGICAL NAME
;	.NDRNN==2		;RETURN NODE NUMBER
;	.NDSSM==3		;SEND STATION CONTROL MESSAGE
;	.NDRBM==4		;RECEIVE BOOT REQUEST MESSAGE
;	.NDRCI==5		;RETURN CONFIGURATION INFORMATION
;	.NDOUT==6		;DO DOUTPU WITH E-O-R (NOT IMPLEMENTED)
;	.NDIN==7		;DO INPUT WITH E-O-R (NOT IMPLEMENTED)
;	.NDTCN==10		;TERMINAL CONNECT
;	.NDTDS==11		;TERMINAL DIS-CONNECT
;	.NDLND==12		;LIST KNOWN NODES
;	.NDNDB==13		;RETURN VARIOUS NDB FIELDS
;	.NDGNF==14		;GET NEXT UN-GREETED NODE, CLEAR UN-GREETED FLG


;NODE. UUO ERROR CODES

	ND%IAL==ECOD1##		;ILLEGAL ARGUMENT LIST
	ND%INN==ECOD2##		;ILLEGAL NODE NAME/NUMBER
	ND%PRV==ECOD3##		;CALLER NOT PRIVILEGED
	ND%NNA==ECOD4##		;NODE NOT AVAILABLE
	ND%NLC==ECOD5##		;NOT LOCKED IN CORE
	ND%TOE==ECOD6##		;TIME OUT ERROR
	ND%RNZ==ECOD7##		;RESERVED WORD NOT ZERO
	ND%NND==ECOD10##	;NOT NETWORK DEVICE
	ND%IOE==ECOD11##	;IO ERROR
	ND%NFC==ECOD12##	;NO FREE CORE
	ND%IAJ==ECOD13##	;IN USE BY ANOTHER JOB (TERMINAL)
	ND%NMA==ECOD14##	;NO MESSAGE AVAILABLE
	ND%TNA==ECOD15##	;TERMINAL NOT AVAILABLE
	ND%NLT==ECOD16##	;NOT A LEGAL TERMINAL
	ND%ISF==ECOD17##	;ILLEGAL SUB-FUNCTION
	ND%RBS==ECOD20##	;RECEIVE BUFFER TOO SMALL
	ND%NUG==ECOD21##	;NO UNGREETED NODES
	ND%ILN==ECOD22##	;ILLEGAL LINE NUMBER (IN STC MESSAGE)
	ND%ADC==ECOD23##	;ADDRESS CHECK
NODE.U::			;ENTRY POINT
	HLRZ	T4,T1		;GET THE FUNCTION IN T4
	SKIPE	T4		;NOT ZERO
	CAILE	T4,NUULEN	;CHECK THE LENGTH
	JRST	ND%IAL		;ILLEGAL RETURN 1
	HRRI	M,(T1)		;GET THE ARG LIST IN M
	NETDBJ			;INTERLOCK THIS CODE
	JRST	UUOTAB-1(T4)	;JUMP ON THE FUNCTION TYPE

UUOTAB:				;NODE UUO FUNCTIONS
	JRST	NODE.1		;ASSIGN A DEVICE
	JRST	NODE.2		;RETURN A NODE NUMBER
	JRST	NODE.3		;STATION CONTOL MESSAGE
	JRST	NODE.4		;AUTO RELOAD OF DAS 80 SERIES
	JRST	NODE.5		;RETURN CONFIG INFO FOR NODE
	JRST	CPOPJ##		;OUTPUT DATA TO NETWORK
	JRST	CPOPJ##		;INPUT DATA FROM NETWORK
	JRST	NODE10		;CONNECT A TERMINAL
	JRST	NODE11		;DISCONNECT A TERMINAL
	JRST	NODE12		;GET COUNT AND NUMBERS OF ALL NODES
	JRST	NODE13		;GET SELECTED INFORMATION ABOUT A NODE
	JRST	NODE14		;RETURN/CLEAR UN-GREETED NODE FLAG
NUULEN==.-UUOTAB		;LENGTH OF THE TABLE
SUBTTL	NODE.1 - ASSIGN A REMOTE DEVICE
NODE.1:	POPJ	P,		;RESERVED (UNTIL IT WORKS)
	PUSHJ	P,SAVE4##	;SAVE THE P'S
	PUSHJ	P,GETWRD##	;GET THE ARGUMENT LIST SIZE
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIE	T1,4		;MUST HAVE FOUR ARGS
	JRST	ECOD1##		;ILLEGAL ARG LIST
	PUSHJ	P,GETWR1##	;GET THE NODE NAME
	  JRST	ND%ADC		;ADDRESS CHECK
	PUSHJ	P,SRCNDB	;CHECK IF DEFINED
	  JRST	ECOD2##		;NO, ILLEGAL NODE NAME
	HLRZ	T1,NDBNNM(W)	;GET NODE NUMBER
	CAMN	T1,JBTLOC##	;IS IT LOCAL SITE?
	JRST	ECOD2##		;YES, CAN'T ASSIGN LOCAL DEVICE
	MOVEI	P1,(W)		;SAVE THE NODE DATA BLOCK
	PUSHJ	P,GETWR1##	;GET THE PHYSICAL DEVICE NAME
	  JRST	ND%ADC		;ADDRESS CHECK
	SKIPN	P2,T1		;COPY THE NAME
	JRST	ECOD3##		;ZERO IS ILLEGAL
	HLRZS	T1		;GENERIC NAME ONLY FOR SEARCH
	PUSHJ	P,SRCNDT	;SEARCH THE DEFINED NAME TABLE
	  JRST	ECOD3##		;ILLEGAL DEVICE NAME
	PUSHJ	P,GETWR1##	;GET THE LOGICAL NAME
	  JRST	ND%ADC		;ADDRESS CHECK
	MOVE	P3,T1		;COPY THE NAME
	PUSHJ	P,MAKDDB	;MAKE A REMOTE NETWORK DDB
	  JRST	ECOD4##		;NO CORE AVAILABLE
	PUSHJ	P,NCSCNT	;CONNECT THE DEVICE
	  JRST	[MOVEI	T1,10(T1);STEP UP THE ERROR NUMBER
		 PUSHJ	P,STOTAC##  ;TELL CALLER WHY IT FAILED
		 PJRST	RMVNET]	;REMOVE DDB FROM SYSTEM
	PUSHJ	P,LNKDDB	;LINK NEWLY CONNECTED DDB
	MOVEI	T1,ASSCON	;NOW THAT WE'VE ASSIGNED THE DEVICE, WE
	IORM	T1,DEVMOD(F)	; BETTER MARK IT. (OR ELSE UUOCON WILL ZAP IT)
	MOVE	T1,DEVNAM(F)	;GET NAME OF DEVICE WE CREATED
	PJRST	STOTC1##	;TELL USER AND SUCCEED
SUBTTL	NODE.2	RETURN A NODE NUMBER IN THE AC
NODE.2:				;ENTRY
	PUSHJ	P,GETWRD##	;GET THE ARG COUNT
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIE	T1,2		;MUST BE A 2
	JRST	ND%IAL		;NO ILLEGAL FUNCTION
	PUSHJ	P,GETWR1##	;GET THE NODE NAME
	  JRST	ND%ADC		;ADDRESS CHECK
	PUSHJ	P,NODE.S	;FIND CORRECT NDB
	  PJRST	ND%INN		;ILLEGAL NODE
	MOVE	T2,T1		;COPY ARGUMENT
	HLRZ	T1,NDBSNM(W)	;GET THE NAME POINTER
	MOVE	T1,(T1)		;GET THE NAME
	TLNE	T2,-1		;WANTS A NAME?
	HLRZ	T1,NDBNNM(W)	;NO, GET THE NODE NUMBER
	PJRST	STOTC1##	;RETURN NODE NUMBER IN AC

;SUBROUTINE TO FIND NDB FOR NODE.UUO FUNCTIONS
;CALL T1 = ARGUMENT TO NODE.UUO
;RETURNS CPOPJ IF NOT FOUND
;	CPOPJ1 WITH W SET TO NDB, RESPECTS T1

NODE.S::PUSH	P,T1		;SAVE ARGUMENT
	PUSHJ	P,SRCNDB	;SCAN NDB'S
	  PJRST	TPOPJ##		;NOPE, GIVE ERROR
	MOVSI	T1,NDB.UP	;DON'T DO THE TRANSLATION
	TDNN	T1,NDBFLG(W)	;  UNLESS THE NODE IS UP.
	JRST	TPOPJ##		;  IF WE DO, WE GET GARBAGE NAMES
	PJRST	TPOPJ1##	;RETURN WITH W SET UP
SUBTTL	NODE.3 STATION CONTROL MESSAGES
;FORMAT OF THE UUO ARGS
;	XWD	TIME,ARG-LIST-LENGTH
;	SIXBIT	/NODE NAME/ OR NUMBER
;	XWD	COUNT,ADR OF OUTPUT BUFFER
;	XWD	COUNT,ADR OF RESPONSE BUFFER

NODE.3:	PUSHJ	P,NTDPRV	;  SURE THAT THIS GUY IS PRIVILEDGED
	  PJRST	ND%PRV		;USER HAS INSUFFICIENT PRIVILEDGES
	PUSHJ	P,SAVJW##	;WE WILL CLOBBER THESE
	PUSHJ	P,SAVE4##	;WE COPY THE UUO ARGS IN P1 - P4
	PUSHJ	P,N34SET	;GO READ THE UUO ARGS
	  POPJ	P,		;  IF BAD ARGLIST, PROPAGATE THE ERROR
	MOVE	T1,P2		;GET THE SECOND ARG (NODE ID)
	PUSHJ	P,SRCNDB	;GO SET W := NDB POINTER
	  JRST	ND%INN		;IF NO NDB, GIVE ILLEGAL NODE ERROR
	CAIN	W,NETNDB##	;IF HE SPECIFIED THE LOCAL NODE,
	JRST	NODE3L		;  GO TRY TO SEND MSG TO A FEK
	HRRZ	J,NDBSTC(W)	;SEE IF ANYONE IS USING STATION CONTROL
	CAMN	J,.CPJOB##	;IF WE ARE USING IT, THEN ASSUME A PAGE
	JRST	N34NRD		;  FAULT WHILE READING STC AND TRY AGAIN
	JUMPN	J,ND%NNA	;IF SOMEONE ELSE HAS IT, NOT AVAILABLE

	HRRZ	J,NDBICT(W)	;GET MESSAGE ADDR
	SKIPE	J		;IS THERE ONE?
	PUSHJ	P,GIVSTC	;  FREE IT, SINCE LOOKING FOR A RESPONSE
	SETZM	NDBICT(W)	;CLEAR THE INCOMING MESSAGE POINTER.
;NOW SEND THE STATION CONTROL MESSAGE

	MOVEI	T1,(P3)		;GET THE ADDRESS OF THE OUTPUT MESSAGE
	HLRE	T2,P3		;GET ITS LENGTH IN BYTES
	JUMPLE	T2,ND%IAL	;IF GARBAGE LENGTH, GIVE ILLEGAL ARG ERROR
	ADDI	T2,3		;ROUND UP AND GET THE LENGTH OF THE
	LSH	T2,-2		;MESSAGE IN WORDS
	PUSH	P,T2		;SAVE LENGTH IN WORDS FOR LATER
	HRLI	T1,(IFIW)	;SECTION LOCAL ADDRESSING
	PUSHJ	P,ARNGE##	;MAKE SURE ALL THE DATA IS IN CORE.
	  JRST	ND%ADC		;ADDRESS CHECK
	  JFCL			;ADDR OK BUT ILLEGAL FOR I/O (IGNORED HERE)

	HRRZ	T1,(P)		;GET THE WORD COUNT BACK
	ADDI	T1,5		;ACCOUNT FOR A HEADER AND WORD ALIGNMENT
	PUSHJ	P,MKNPCB	;MAKE A "NUMBERED" PCB
	  JRST	[POP P,T1	;IF NO CORE, CLEAN UP THE STACK
		 JRST ND%NFC]	;  AND GIVE THE "NO FREE CORE" ERROR
	PUSH	P,P2		;SAVE THE NODE'S NAME/NUMBER
	PUSH	P,P3		;SAVE THE OUTPUT BUFFER POINTER
	MOVE	P2,PCBPTR(U)	;GET BYTE POINTER TO THE PCB
	SETZ	T1,		;WE WANT AN NCT OF 0 (NUMBERED MSG)
	PUSHJ	P,NCSWHD	;WRITE THE NCL HEADER
;DLA
	XMTI	0		;DLA = 0 MEANS NOT DEVICE CONTROL
;CNT
	HLRZ	T1,(P)		;GET MESSAGE'S BYTE COUNT
	ADDI	T1,1		;  +1 FOR "NC.CTL" TYPE FIELD
	XMT	T1		;WRITE THE COUNT
;TYP
	XMTI	NC.CTL		;THIS IS A STATION CONTROL MESSAGE
	ADDM	P3,PCBCTR(U)	;THAT'S ALL OF THE "HEADER" UPDATE ITS LENGTH
	POP	P,P3		;GET THE OUTPUT MESSAGE POINTER BACK
;NOW BLT THE DATA INTO THE PCB

	MOVEI	T1,1(P2)	;GET THE ADDRESS OF FIRST "FULL" FREE WORD
	HRLI	T1,(POINT 8,0)	;MAKE ADDRESS A BYTE POINTER
	MOVEM	T1,PCBPT2(U)	;  STORE IT AS "SECONDARY" BUFFER POINTER

	HLRZM	P3,PCBCT2(U)	;SET NUMBER OF BYTES TO WRITE AS SECONDARY SIZE

	HRLI	T1,(P3)		;GET THE USER ADDRESS OF THE DATA IN SRC(BLT)
	POP	P,P2		;GET THE NODE NUMBER BACK
IFE FTXMON,<
	POP	P,T2		;GET THE LENGTH OF THE DATA IN WORDS
	ADDI	T2,-1(T1)	;GET ADDRESS OF LAST WORD TO FILL WITH DATA
	EXCTUX	<BLT T1,(T2)>	;COPY THE DATA TO THE PCB
>
IFN FTXMON,<
	XSFM	T2		;GET PCS
	HRLZS	T2		;IN LH WHERE IT BELONGS
	HLR	T2,T1		;FORM USER SOURCE ADDRESS
	HRRZ	T3,T1		;FORM MONITOR DESTINATION ADDRESS
	POP	P,T1		;GET THE LENGTH OF THE DATA IN WORDS
	XBLTUX	T1		;COPY THE DATA TO THE PCB
>

	MOVEI	T1,PCV.NC	;SAY THAT DATA IS 8 BIT BYTES (NO CONVERSION)
	PUSHJ	P,NTDWRT	;GIVE THE MESSAGE TO THE NETWORK

;NOW CLAIM OWNERSHIP OF STATION CONTROL AND WAIT FOR A RESPONSE

	JUMPE	P4,CPOPJ1##	;IF NO RESPONSE BUFFER, UUO IS DONE.
	HLLZ	T1,P1		;LH OF NDBSTC := SECONDS TO WAIT FOR ANSWER
	SKIPN	T1		;IF USER SPECIFIED A TIME, THEN USE HIS
	MOVSI	T1,10		;  OTHERWISE DEFAULT TO 8 SECONDS
	MOVE	J,.CPJOB##	;GET OUR JOB NUMBER
	HRRI	T1,(J)		;RH OF NDBSTC := JOB TO WAKE UPON ANSWER
	MOVEM	T1,NDBSTC(W)	;STORE NDBSTC SO ICMCTL CAN WAKE US

NODE31:	MOVEI	T1,EV.STC	;GET THE "STATION CONTROL" EVENT WAIT CODE
	PUSHJ	P,[NTSAVE	;  RETURN THE "NT" RESOURCE
		   JRST ESLEEP##] ;  AND SLEEP UNTIL MSG COMES OR WE TIME OUT

	MOVE	T1,P2		;WE'VE BEEN AWAKENED. TRACK THE NDB DOWN AGAIN
	PUSHJ	P,SRCNDB	;  AS IT MAY HAVE CRASHED.
	  PJRST	ND%INN		;IF NDB GONE, GIVE ILLEGAL NODE ERROR
	SKIPE	T1,NDBICT(W)	;SEE IF THERE IS AN INCOMING MESSAGE
	  PJRST	N34NRD		;IF RESPONSE, GO READ IT
	SKIPN	T1,NDBSTC(W)	;SEE IF STATION CONTROL STILL ASSIGNED
	  PJRST	ND%TOE		;IF NOT, WE MUST HAVE TIMED OUT.
	MOVEI	T1,(T1)		;GET JUST THE JOB NUMBER
	CAME	T1,.CPJOB##	;MAKE SURE WE STILL OWN STATION CONTROL
	  PJRST	ND%TOE		;IF NOT, THEN TIME OUT ERROR
	JRST	NODE31		;IF SO, THEN SPURIOUS WAKE, SLEEP SOME MORE
IFE FTKS10,<NODE3L==ND%INN>	;THE KS-10 IS CURRENTLY THE ONLY SYSTEM
				;  THAT CAN DOWN-LINE LOAD ITS FEKS.

IFN FTKS10,<			;IF ON A KS-10, ASSEMBLE STC-FEK STUFF

;HERE WHEN A STC MESSAGE FOR THE "LOCAL" NODE IS SEEN. SEE IF IT'S FOR
;  ONE OF OUR LINES.  IF IT IS, SEND THE STC MESSAGE TO THE FEK.

NODE3L:	MOVEI	T1,(P3)		;GET THE ADDRESS OF THE STC MESSAGE
	MOVE	J,.CPJOB##	;SET UP OUR JOB (SO WE DON'T GET AN LN1)
	PUSHJ	P,UADRCK##	;MAKE SURE IT'S LEGAL AND IN CORE
	EXCTUX	<LDB T1,[POINT 8,(P3),7]> ;GET THE "LINE NUMBER FIELD"
	JUMPE	T1,ND%INN	;DON'T LET STC TO THIS NODE DO ANYTHING
	PUSHJ	P,LN2FEK	;CONVERT LINE NUMBER TO FEK ADDRESS (IN "J")
	  JRST	ND%ILN		;GIVE "ILLEGAL LINE NUMBER" IF NO SUCH FEK
	HRRZ	T1,FEKBJN(J)	;GET THE NUMBER OF JOB OWNING STC ON FEK
	CAMN	T1,.CPJOB##	;IF WE OWN THE STC OF THIS FEK,
	JRST	NODFRD		;  ASSUME PAGE FAULT WHILE STORING RESPONSE
	JUMPN	T1,ND%NNA	;IF SOMEONE ELSE HAS STC, SAY "NOT AVAIL"

;HERE STC FOR THIS FEK IS FREE. COPY THE MESSAGE AND SEND IT

	MOVE	W,J		;USE "W" AS A FEK POINTER FOR A BIT
	SKIPE	J,FEKICT(W)	;SEE IF THERE ARE ANY STALE INPUT MSGS
	PUSHJ	P,GIVSTC	;IF STALE MSGS, FREE THEM
	SETZM	FEKICT(W)	;CLEAR INCOMING MESSAGE POINTER

	MOVEI	T1,(P3)		;GET USER ADDRESS OF MESSAGE TO SEND
	HLRE	T2,P3		;GET LENGTH OF THE MESSAGE (BYTES)
	JUMPLE	T2,ND%IAL	;SAY ILLEGAL ARG LIST IF GARBAGE LENGTH
	ADDI	T2,3		;WE WANT LENGTH IN WORDS. ROUND BYTES UP
	LSH	T2,-2		;  AND DIVIDE BY 4 BYTES PER WORD
	PUSH	P,T2		;SAVE THE WORD COUNT FOR BLT COMING UP
	HRLI	T1,(IFIW)	;SECTION LOCAL ADDRESSING
	PUSHJ	P,ARNGE##	;MAKE SURE WE DON'T IME.
	  JRST	ND%ADC		;ADDRESS CHECK
	  JFCL			;ADDR OK BUT ILLEGAL FOR I/O (IGNORED HERE)

	HLRE	T1,P3		;GET THE BYTE COUNT AGAIN
	PUSHJ	P,GETSTC	;GET A STC BLOCK TO HOLD THE DATA
	  JRST	[POP P,T1	;IF NO CORE, FIXUP THE STACK
		 JRST ND%NFC]	;  AND TELL THE USER

IFE FTXMON,<
	MOVSI	T1,(P3)		;SOURCE(BLT) := USER ADDRESS
	HRRI	T1,STCDAT(J)	;DEST(BLT) := STC BLOCK
	POP	P,T2		;GET THE LENGTH (IN WORDS) BACK
	ADDI	T2,-1(T1)	;GET LAST MONITOR ADDRESS TO FILL
	EXCTUX	<BLT T1,(T2)>	;COPY THE DATA INTO THE STC BLOCK
>
IFN FTXMON,<
	XSFM	T2		;GET PCS
	HRLZS	T2		;IN LH WHERE IT BELONGS
	HRRI	T2,(P3)		;FORM USER SOURCE ADDRESS
	HRRZI	T3,STCDAT(J)	;FORM MONITOR DESTINATION ADDRESS
	POP	P,T1		;GET THE LENGTH (IN WORDS) BACK
	XBLTUX	T1		;COPY DATA TO STC BLOCK
>
;NOW PUT OUR NODE NUMBER IN THE FIRST BYTE, AND GIVE MSG TO THE FEK

	HLRZ	T1,NETNDB##+NDBNNM	;GET OUR NODE NAME
	DPB	T1,[POINT 8,STCDAT(J),7] ;  AND STORE IT AS THE "SNA"

	MOVE	U,J		;SET UP U := STC MESSAGE
	MOVE	J,W		;  AND  J := FEK -- FOR CALL TO FEK

	HLRE	T1,P1		;GET THE USERS TIMER VALUE
	SKIPG	T1		;IF HE DIDN'T SPECIFY A VALUE
	MOVEI	T1,10		;  THEN USE 8 SECONDS
	MOVSI	T1,(T1)		;PUT TIMER IN THE LH
	HRR	T1,.CPJOB##	;GET OUR JOB NUMBER
	MOVEM	T1,FEKBJN(J)	;CLAIM OWNERSHIP OF THE FEK'S STC

	MOVEI	T1,FF.STC	;GET THE "STC" FUNCTION CODE
	XCT	FEKDSP(J)	;CALL THE FEK
	  JRST	[SETZM FEKBJN(J);IF THE FEK DOESN'T ACCEPT THE STC,
		 MOVEI J,(U)	;  CLEAR STC OWNERSHIP, GET STC IN "J"
		 PUSHJ P,GIVSTC	;  FREE THE STC BLOCK
		 JRST ND%ILN]	;  AND GIVE ILLEGAL LINE ERROR
	JUMPE	P4,[SETZM FEKBJN(J) ;IF NO RESPONSE WANTED, CLEAR OWNER
		    RETSKP]	;  AND GIVE GOOD RETURN

;NOW WE'VE SENT THE MESSAGE. CHECK FOR A RESPONSE.  IF NONE, WAIT.

NOD3L1:	HRRZ	T1,FEKBJN(J)	;GET THE OWNER OF THE FEK'S STC
	CAME	T1,.CPJOB##	;MAKE SURE IT'S US.
	JRST	ND%TOE		;IF NOT US, THEN GIVE "TIME OUT ERROR"

	SKIPE	FEKICT(J)	;SEE IF ANY INCOMING MSGS.
	JRST	NODFRD		;IF INCOMING MESSAGE. RETURN IT TO THE USER

	MOVEI	T1,EV.STC	;GET THE "STATION CONTROL" EVENT WAIT CODE
	PUSH	P,J		;SAVE OUR FEK FOR A BIT
	MOVE	J,.CPJOB##	;SET J := JOB NUMBER (FOR ESLEEP)
	PUSHJ	P,[NTSAVE	;RETURN THE "NT" INTERLOCK
		   JRST ESLEEP##] ;  AND SLEEP WAITING FOR A RESPONSE
	POP	P,J		;GET OUR FEK ADDRESS BACK
	JRST	NOD3L1		;UPON AWAKING, SEE IF WE HAVE A MSG.

>;END IFN FTKS10
SUBTTL	NODE.4 RECEIVE STATION CONTROL BOOT MESSAGES
;FORMAT OF THE UUO ARGS
;	EXP	0
;	EXP	0
;	EXP	0
;	XWD	COUNT,ADR OR RESPONSE BUFFER

NODE.4:	PUSHJ	P,NTDPRV	;  SURE THAT THIS GUY IS PRIVILEDGED
	  PJRST	ND%PRV		;USER HAS INSUFFICIENT PRIVILEDGES
	PUSHJ	P,SAVJW##	;WE WILL CLOBBER THESE
	PUSHJ	P,SAVE4##	;WE COPY THE UUO ARGS IN P1 - P4
	PUSHJ	P,N34SET	;GO READ THE UUO ARGS
	  POPJ	P,		;  IF BAD ARGLIST, PROPAGATE THE ERROR

;NOW SEARCH FOR AN NDB WITH AN UNCLAIMED INCOMING CONTROL MESSAGE

	SKIPA	W,[EXP NETNDB##];START WITH OUR NDB
NODE41:	HRRZ	W,NDBNNM(W)	;STEP TO THE NEXT NDB
	JUMPE	W,NODE42	;IF NO MSGS ON NDB'S, CHECK FEKS
	SKIPN	NDBSTC(W)	;IF SOME JOB IS WAITING, OR
	SKIPN	NDBICT(W)	;  IF NO STC MESSAGE HAS COME,
	JRST	NODE41		;  THEN GO TRY NEXT NDB

N34NRD:	HRRZ	J,NDBICT(W)	;GET THE ADDRESS OF THE STC MESSAGE
	JUMPE	J,ND%TOE	;IF NONE, THEN PROBABLY A TIME OUT
	HLL	T1,STCBLK(J)	;THE NUMBER OF BYTES IS THE LH(STCBLK)
	HLLZM	T1,STCLNN(J)	;  SAVE THE COUNT FOR N34RED
	PUSHJ	P,N34RED	;STORE THE MESSAGE IN THE USERS BUFFER
	  SOS	(P)		;PROPAGATE THE ERROR (IF ANY)
	SETZM	NDBSTC(W)	;CLEAR STC OWNERSHIP
	SETZM	NDBICT(W)	;CLEAR (NOW FREED) INCOMING STC MSG
	RETSKP			;GIVE (POSSIBLY BAD) RETURN
IFE FTKS10,<NODE42==ND%NMA>	;IF NO MESSAGES FROM FEKS, SAY NO MSG AVAIL
IFN FTKS10,<			;THE KS-10 GETS STC MSGS FROM ITS D8K FEKS

NODE42:	MOVEI	J,FEKFST##	;GET THE ADDRESS OF THE FIRST FEK.
	CAIA			;SKIP INTO THE LOOP
NODE43:	HRRZ	J,FEKBLK(J)	;ADVANCE TO THE NEXT FEK
	JUMPE	J,ND%NMA	;IF NO MORE, SAY "NO MESSAGE AVAILABLE"
	SKIPN	FEKBJN(J)	;IF SOMEONE IS WAITING, OR
	SKIPN	FEKICT(J)	;  THERE ISN'T A MESSAGE
	JRST	NODE43		;GO TRY THE NEXT FEK.

;NODFRD	ROUTINE TO RETURN THE STC MESSAGE QUEUED ON FEK
;CALL	J := FEK
;RETURN	CPOPJ			;ERROR (BUT STC FREED)
;	CPOPJ1			;OK (STC FREED)

NODFRD:	SKIPN	U,FEKICT(J)	;GET U := INCOMING MESSAGE
	JRST	ND%TOE		;IF NONE, ASSUME A TIME OUT ERROR (?)
	EXCH	J,U		;REMEMBER THE FEK, SET J := STC MSG

;NOW REMOVE ANY NODE NUMBER ON THE MESSAGE AND INSERT THE LINE NUMBER
;  THIS MEANS COPYING THE MESSAGE DOWN IF THE NODE NUMBER IS EXTENSIBLE

	HLRZ	T1,STCBLK(J)	;GET THE INITIAL LENGTH OF THE MESSAGE
	MOVE	T2,[POINT 8,STCDAT(J)] ;A POINTER TO THE MESSAGE
	MOVEI	T3,1		;FINAL VALUE OF MESSAGE (START WITH LINE #)
	MOVE	T4,T2		;"PUTTER" BYTE POINTER FOR COPYING

	SOJGE	T1,[ILDB W,T2	;GET NEXT BYTE
		    TRNE W,200	;AND SEE IF IT'S STILL THE NODE NUMBER
		    JRST .	;STILL EXTENSIBLE, KEEP LOOKING
		    JRST .+1]	;  END OF NODE NUMBER

	HRRZ	W,STCLNN(J)	;GET THE LINE NUMBER
	IDPB	W,T4		;STORE AS THE FIRST BYTE
;	AOS	T3		;COUNT THE BYTE (DONE ABOVE...)

	SOJGE	T1,[ILDB W,T2	;FOR ALL THE REST OF THE BYTES, LOAD
		    IDPB W,T4	;  AND STORE IN THE NEW MESSAGE
		    AOJA T3,.]	;  COUNT THE BYTE AND LOOP

	HRLM	T3,STCLNN(J)	;STORE THE LENGTH FOR N34RED

	MOVEI	W,NETNDB##	;SET UP "W" := NETNDB TO RETURN OUR NODE #
	PUSHJ	P,N34RED	;STUFF THE MESSAGE IN THE USERS BUFFER
	  SOS	(P)		;IF FAIL RETURN, MAKE SURE WE DON'T SKIP
	SETZM	FEKBJN(U)	;CLEAR THE STC OWNER
	SETZM	FEKICT(U)	;CLEAR ANY INCOMING MSG (N34RED FREED IT)
	RETSKP

>; END OF IFN FTKS10
;N34RED	ROUTINE TO COPY STC MESSAGES BACK INTO THE USERS BUFFER
;CALL	P1 - P4 := AS SET UP BY N34SET
;	J := ADDR OF STC MESSAGE TO COPY
;	W := NDB OF NODE WHOS NUMBER WE RETURN AS THE BOOT NODE
;RETURN	NOT AT ALL IF ADDRESS CHECK OR PAGE FAULT
;	CPOPJ			;IF ERROR, WITH STC BLOCK FREED
;	CPOPJ1			;IF OK, WITH STC BLOCK FREED

N34RED:	MOVEI	T1,(P4)		;GET THE ADDRESS OF THE USER'S INPUT BUFFER
	HLRZ	T2,STCLNN(J)	;GET THE LENGTH OF THE STC MESSAGE (BYTES)
	HLRZ	T3,P4		;GET THE LENGTH OF THE USER'S BUFFER (BYTES)
	CAIGE	T3,(T2)		;IF THE MESSAGE IS LONGER THAN THE USER'S
	JRST	[PUSHJ P,GIVSTC	;IF STC WON'T FIT, FREE THE MESSAGE
		 PJRST ND%RBS]	;  AND GIVE A "RECEIVE BUFFER 2 SMALL" ERROR
	ADDI	T2,3		;ROUND BYTES UP TO NEXT EVEN WORD
	LSH	T2,-2		;CONVERT BYTES TO WORDS
	PUSH	P,T2		;SAVE LENGTH IN WORDS
	HRLI	T1,(IFIW)	;SECTION LOCAL ADDRESSING
	PUSHJ	P,ARNGE##	;RANGE CHECK THE USER'S INPUT BUFFER AREA
	  JRST	ND%ADC		;ADDRESS CHECK
	  JRST	ND%ADC		;ILLEGAL FOR I/O

IFE FTXMON,<
	POP	P,T2		;GET LENGTH BACK
	ADDI	T2,-1(T1)	;COMPUTE LAST ADDRESS
	MOVEI	T1,(P4)		;GET THE ADDRESS OF THE USER'S BUFFER BACK
	HRLI	T1,STCDAT(J)	;GET THE ADDRESS OF THE STC MESSAGE
	EXCTXU	<BLT T1,(T2)>	;COPY THE STC MESSAGE
>
IFN FTXMON,<
	XSFM	T3		;GET PCS
	HRLZS	T3		;IN LH WHERE IT BELONGS
	HRRI	T3,(P4)		;FORM USER DESTINATION ADDRESS
	HRRZI	T2,STCDAT(J)	;FORM MONITOR SOURCE ADDRESS
	POP	P,T1		;GET LENGTH IN WORDS BACK
	XBLTXU	T1		;COPY THE STC MESSAGE
>

	PUSH	P,J		;PROTECT STC POINTER FROM DEATH BY PUTWRD
	HRRI	M,1(M)		;SET RH(M) TO THE ADDRESS TO RETURN NODE NUMBER
	HLRZ	T1,NDBNNM(W)	;GET THE NODE NUMBER
	PUSHJ	P,PUTWRD##	;RETURN THE NODE NUMBER
	  JRST	ND%ADC		;ADDRESS CHECK

	MOVEI	T1,(P4)		;GET THE ADDRESS OF THE USERS INPUT BUFFER
	HRRZ	J,(P)		;RECOVER STC POINTER AGAIN
	HLL	T1,STCLNN(J)	;GET THE NUMBER OF VALID BYTES IN IT
	HRRI	M,2(M)		;SET RH(M) TO ADDRESS OF USERS INPUT BUFFER
	PUSHJ	P,PUTWRD##	;STORE UPDATED "XWD LENGTH,ADDR" FOR THE USER
	  JRST	ND%ADC		;ADDRESS CHECK

	POP	P,J		;GET STC POINTER ONCE AGAIN
	PUSHJ	P,GIVSTC	;RETURN THE STC BLOCK
	RETSKP			;GIVE GOOD RETURN
;N34SET	ROUTINE TO SET P1 - P4 TO THE NODE. UUO ARGUMENTS
;CALL	M := UUO
N34SET:	PUSHJ	P,GETWRD##	;P1 GETS THE "XWD TIME,ARG-LIST-LENGTH"
	  JRST	ND%ADC		;ADDRESS CHECK
	MOVE	P1,T1
	HRRZ	T1,T1		;GET JUST THE ARG LIST LENGTH
	CAIGE	T1,4		;MAKE SURE IT'S LONG ENOUGH
	PJRST	ND%IAL		;  IF TOO SHORT, GIVE ILLEGAL ARG LIST
	PUSHJ	P,GETWR1##	;P2 GETS THE NODE NAME
	  JRST	ND%ADC		;ADDRESS CHECK
	MOVE	P2,T1
	PUSHJ	P,GETWR1##	;P3 GETS THE OUTPUT BUFFER POINTER
	  JRST	ND%ADC		;ADDRESS CHECK
	MOVE	P3,T1
	PUSHJ	P,GETWR1##	;P4 GETS THE INPUT BUFFER POINTER
	  JRST	ND%ADC		;ADDRESS CHECK
	MOVE	P4,T1
	SUBI	M,3		;SET THE UUO POINTER BACK
	RETSKP			;SKIP RETURN WITH P1 - P4 SET UP
SUBTTL	NODE.5	RETURN CONFIG INFO FOR NODE
;FORMAT OF THE UUO ARGS
;	XWD	0,,COUNT
;	SIXBIT	/NODE NAME/ OR NUMBER
;	0			RESERVED FOR DATE AND NAME
;	BLOCK	<COUNT-3> INFO RETURNED HERE

NODE.5:	PUSHJ	P,SAVE1##	;SAVE P1 FIRST
	PUSHJ	P,GETWRD##	;GET ARGUMENT LIST SIZE
	  JRST	ND%ADC		;ADDRESS CHECK
	SUBI	T1,3		;DEDUCT OVERHEAD
	CAIG	T1,^D1000	;RIDICULOUS SIZE
	SKIPG	P1,T1		;ANY ROOM LEFT FOR ANSWER
	PJRST	ND%IAL		;NO, ILLEGAL ARG LIST
	PUSHJ	P,GETWR1##	;GET NODE NAME
	  JRST	ND%ADC		;ADDRESS CHECK
	PUSHJ	P,NODE.S	;FIND CORRECT NDB
	  PJRST	ND%INN		;ILLEGAL NODE
	PUSHJ	P,GETWR1##	;GET RESERVED WORD
	  JRST	ND%ADC		;ADDRESS CHECK
	JUMPN	T1,ND%RNZ	;MUST BE ZERO
NODE51:	PUSHJ	P,GETWR1##	;GET DEVICE TYPE REQUEST
	  JRST	ND%ADC		;ADDRESS CHECK
	MOVSI	T2,-<OBJ.MX+1>	;NUMBER OF KNOWN DEVICES
NODE52:	LDB	T3,[EXP OB%TYP##+OBJTAB##+(T2)] ;GET A TYPE
	CAIE	T3,(T1)		;FOUND REQUEST
	AOBJN	T2,NODE52	;NO, TRY ANOTHER TYPE
	SKIPL	T2		;FIND ANY
	TDZA	T2,T2		;NONE OF REQUESTED TYPE
	LDB	T2,NETCNF##(T2)	;GET NUMBER OF THAT TYPE
	HRL	T1,T2		;INSERT COUNT FOR USER
	PUSHJ	P,PUTWRD##	;STORE BACK
	  JRST	ND%ADC		;ADDRESS CHECK
	SOJG	P1,NODE51	;DO FOR ALL ARGS
	PJRST	CPOPJ1##	;GIVE GOOD RETURN
SUBTTL	NODE.6 NETWORK OUTPUT WITH USER CONTROL OF EOR

REPEAT 0,<			;SUPERSEDED BY TSK. UUO
;FORMAT OF THE UUO ARGS
;	XWD	0,NUMBER OF ARGS
;	XWD	FLAGS,IO CHANNEL

NODE.6:	PUSHJ	P,SAVE1##	;HAVE TO SAVE FLAGS SOMEWHERE
	PUSHJ	P,N67SET	;SETUP F, T1
	MOVE	P1,T1		;SAVE PARAMETER WORD FOR EOR CHECK BELOW
	HLR	M,DEVBUF(F)	;GET ADDRESS OF OUTPUT BUFFER HEADER
	PUSHJ	P,GETWRD##	;GET ADDRESS OF CURRENT BUFFER
	  JRST	ND%ADC		;ADDRESS CHECK
	JUMPL	T1,NODE61	;IF BUFFER NEVER SET UP, JUST DO THE OUTPUT
	HRR	M,T1		;TO READ LINKAGE WORD OF BUFFER
	PUSHJ	P,GETWRD##	;GET IT
	  JRST	ND%ADC		;ADDRESS CHECK
	JUMPL	P1,NODE60	;SKIP IF USER WANTS EOR
	TLOA	T1,IONER	;DOESN'T, FLAG BUFFER
NODE60:	TLZ	T1,IONER	;DOES, MAKE SURE THIS IS OFF
	PUSHJ	P,PUTWRD##	;PUT BACK IN BUFFER
	  JRST	ND%ADC		;ADDRESS CHECK
NODE61:	PUSHJ	P,TOUT##	;DO THE OUTPUT
	  PJRST	CPOPJ1##	;SUCCESS, GIVE USER A SKIP RETURN
	JRST	N67IOE		;FAILED, RETURN ERROR STATUS
>;REPEAT 0
SUBTTL	NODE.7 NETWORK INPUT WITH NOTIFICATION OF END OF RECORD
REPEAT 0,<			;SUPERSEDED BY TSK. UUO
;FORMAT OF UUO ARGS
;	XWD	0,NUMBER OF ARGS
;	XWD	0,IO CHANNEL
NODE.7:	PUSHJ	P,N67SET	;VERIFY IO CHANNEL, SETUP F, T1
	PUSHJ	P,TIN##		;DO THE INPUT
	  JRST	NODE70		;SUCCESS, CHECK EOR
N67IOE:	MOVEI	T1,11		;ERROR CODE 11 IS IO ERROR
	HRL	T1,DEVIOS(F)	;AND WE RETURN THE GETSTS INFO TO THE
	PJRST	STOTAC##	; SO THE USER DOESN'T NEED ANOTHER UUO

NODE70:	HRR	M,DEVBUF(F)	;GET ADDRESS OF INPUT BUFFER HEADER
	PUSHJ	P,GETWRD##	;GET IT
	  JRST	ND%ADC		;ADDRESS CHECK
	HRR	M,T1		;GET ADDRESS OF BUFFER LINKAGE WORD
	PUSHJ	P,GETWRD##	;GET THAT
	  JRST	ND%ADC		;ADDRESS CHECK
	TLNE	T1,IONER	;NOW, DOES THIS BUFFER NOT MARK THE END OF RECORD?
	TDZA	T1,T1		;RIGHT, MAKE BIT 0 0
	MOVSI	T1,400000	;END OF RECORD, SET BIT 0
	PJRST	STOTC1##	;RETURN THAT TO USER

;ROUTINE FOR NODE. UUO FUNCTIONS 6 AND 7 TO DO COMMON SETUP.
;	M/RH: ADDR OF ARG LIST (SETUP BY UUOCON)
;	PUSHJ	P,N67SET
;	NORMAL RETURN,
;	F/ DDB ADDRESS, T1/ FLAGS,,IO CHANNEL
;THIS ROUTINE ABORTS THE UUO IF THE IO CHANNEL IS NOT OPEN OR THE DEVICE
;IS NOT A NETWORK DEVICE.

N67SET:	PUSHJ	P,GETWRD##	;GET ARG LIST LENGTH
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIGE	T1,2		;NEED AT LEAST 2 WORDS
	  PJRST	ND%IAL		;TOO SHORT, RETURN LIST LENGTH ERROR
	PUSHJ	P,GETWR1##	;GET FLAGS,,IOCHAN WORD
	  JRST	ND%ADC		;ADDRESS CHECK
	PUSH	P,T1
	PUSHJ	P,SETUF##
	  JRST	[POP	P,(P)
		 PJRST	ND%IAL]
	POP	P,T1
	MOVSI	T2,DVCNET	;MAKE SURE DEVICE IS A NETWORK DEVICE
	TDNN	T2,DEVCHR(F)	; FOR PARANOIA'S SAKE
	PJRST	ND%NND		;IT ISN'T, ABORT UUO
	POPJ	P,		;RETURN TO DO IO
>;REPEAT 0
;NODE10 (.NDTCN) UUO TO ATTEMPT TO CONNECT REMOTE TERMINAL'S TO THE SYSTEM.
;ARGUMENT BLOCK FORMAT.
;
;  1)	XWD 0,LENGTH		;LENGTH ALWAYS = 2
;  2)	XWD NODE,LINE		;NODE AND LINE TO TRY TO CONNECT
;
;RETURNS
;	SIXBIT /TTYNNN/		;THE NAME THE TERMINAL WAS ASSIGNED.
;
;ERRORS
;	NDIAL%==1		;ILLEGAL ARGUMENT LIST
;	NDTNA%==2		;TERMINAL NOT AVAILABLE.
;
NODE10:	PUSHJ	P,GETWRD##	;GET THE ARG-LIST LENGTH
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIE	T1,2		;WE ONLY WANT 1 DATA ARGUMNET
	JRST	ND%IAL		;ILLEGAL ARG LIST (NDIAL%)
	PUSHJ	P,GETWR1##	;GET THE "XWD NODE,LINE" TO CONNECT TO
	  JRST	ND%ADC		;ADDRESS CHECK
	PUSHJ	P,ASGTTY##	;ATTEMPT THE CONNECT.
	  PJRST	ND%TNA		;TERMINAL NOT AVAILABLE (NDTNA%)
	PJRST	STOTC1##	;RETURN TERMINAL NUMBER IN AC
;NODE11 (.NDTDS) DISCONNECT A REMOTE TERMINAL FROM THE SYSTEM (WITH OPTIONAL
;  RECONNECT TO ANOTHER SYSTEM).
;
;NODE. UUO ARGUMENT BLOCK.
;  1)	XWD 0,LENGTH		;EITHER 2 OR 3 (3 IMPLIES AUTO RECONNECT)
;  2)	SIXBIT /TTYNNN/		;NAME OF TERMINAL DO DIS/RE-CONNECT
;  3)	EXP NODE-NUMBER		;OPTIONAL NODE NUMBER TO RECONNECT TO.
;
;RETURNS
;	NOTHING.  ON A SUCCESSFUL RETURN, THE AC IS UNCHANGED
;
;ERRORS
;	NDIAL%==1		;ILLEGAL ARGUMENT LIST (LENGTH WRONG)
;	NDINN%==2		;ILLEGAL NODE NUMBER/NAME (CAN'T HAPPEN...)
;	NDIAJ%==3		;THE TERMINAL IS IN USE BY ANOTHER JOB.
;	NDNLT%==4		;USER DID NOT GIVE A LEGAL TERMINAL NAME AS
;				;  THE SECOND ARGUMENT.
;
NODE11:	PUSHJ	P,SAVE4##	;WE USE THE P'S FOR TEMPS
	PUSHJ	P,GETWRD##	;GET THE LENGTH OF THE ARG-LIST
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIL	T1,2		;RANGE CHECK THE LENGTH.  MUST
	CAILE	T1,3		;  BE BETWEEN 2 AND 3
	JRST	ND%IAL		;ILLEGAL ARG LIST (NDIAL%)
	MOVE	P2,T1		;SAVE THE LENGTH FOR A BIT
	PUSHJ	P,GETWR1##	;GET THE TERMINAL NAME
	  JRST	ND%ADC		;ADDRESS CHECK
	MOVE	P3,T1		;SAVE THE NAME
	MOVEI	P4,RSN.OK	;ASSUME THAT THIS IS SIMPLE DISCONNECT.
	CAIN	P2,3		;BUT IF THIS IS A RE-CONNECT,
	JRST	[PUSHJ P,GETWR1## ;THEN GET NODE NUMBER TO RECONNECT TO.
		   JRST	ND%ADC	  ;ADDRESS CHECK
		 HRLZ P4,T1	  ;AND PUT NODE NUMBER IN LH
		 HRRI P4,RSN.RC	  ;REASON IS "RE-CONNECT"
		 JRST .+1]	;REJOIN MAIN FLOW WITH ALL ARGS READ
NOD11A:	MOVE	T1,P3		;GET THE TERMINAL NAME BACK
	PUSHJ	P,TTYPHY##	;SET U := LDB, F := DDB(IF ANY)
	  PJRST	ND%NLT		;GIVE "NDNLT%" ERROR IF TERMINAL NAME IS BAD
	JUMPN	F,[LDB T1,PJOBN## ;IF WE FOUND A LDB, SEE IF WE GOT A DDB.
		   SKIPE T1	;IF NO-ONE OWNS IT, WE'RE OK
		   CAMN T1,.CPJOB## ;  IF WE OWN IT WE'RE OK
		   CAIA		; WE'RE OK
		   JRST ND%IAJ	;  IF WE DON'T OWN IT, GIVE "NDIAJ%" ERROR
		   PUSHJ P,TTYDTC## ;  DETACH THE TERMINAL
		   JRST .+1]	;  IF WE DO OWN IT, THEN ALL'S OK
	PUSHJ	P,MCRCHK##	;SEE IF IT'S A LEGAL REMOTE TERMINAL.
	  JRST	ND%NLT		;IF NOT, THEN RETURN "NDNLT%"
	LDB	T1,LDPRNN##	;GET THE NODE NUMBER THAT IT'S CONNECTED TO
	PUSHJ	P,SRCNDB	;FIND THE NDB
	 STOPCD	.,STOP,ANFNNT,	;++ NO NBD FOR TERMINAL
	MOVE	T1,P4		;GET "XWD NODE,REASON"
	PUSHJ	P,TRMXDC##	;SEND THE DISCONNECT
	  JRST	[PUSHJ P,NETSLP	;IF NO CORE, SLEEP FOR A BIT
		 JRST NOD11A]	; AND TRY AGAIN
	JRST	CPOPJ1##	;ALL DONE. GIVE GOOD RETURN
;NODE12 (.NDLND) RETURNS THE NUMBER OF NODES IN THE NETWORK, AND A LIST
;  OF THE NODE NUMBERS.
;
;NODE. UUO ARGUMENT BLOCK
;  1)	XWD	0,LENGTH	;LENGTH OF ARG BLOCK (INCLUDES THIS WORD)
;  2)	EXP	RTN-VAL-1	;FILLED IN WITH NODE NUMBER OF FIRST NODE
;  3)	EXP	RTN-VAL-2	;FILLED WITH SECOND NODE NUMBER
;		.
;		.
;		.
;  N+1)	EXP	RTN-VAL-N	;FILLED WITH NUMBER OF N'TH NODE
;
;RETURNS
;	IN THE AC, THE NUMBER OF NODES IN THE NETWORK
;	IN ARG-LIST(2 THRU N+1) THE NUMBERS OF THE NODES IN THE NETWORK
;
;ERRORS
;	NDIAL%==1		;ARG-LIST LENGTH LESS THAN 1
;
;NOTE!	NO ERROR IS RETURNED IF THE ARGUMENT LIST IS TOO SHORT TO HOLD
;	ALL THE NODES.  THE EXTRA ARGUMENTS ARE SIMPLY NOT RETURNED.

NODE12:	PUSHJ	P,GETWRD##	;GET THE LENGTH OF THE ARG LIST
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIGE	T1,1		;MAKE SURE IT'S LEGAL
	JRST	ND%IAL		;NDIAL%  ILLEGAL ARG LIST
	MOVE	T3,T1		;SAVE THE ARG-LIST LENGTH FOR A WHILE
	MOVEI	T2,NETNDB##	;GET THE ADDRESS OF THE FIRST DDB
	SETZ	T4,		;WE'VE COUNTED NO NODES YET.
NOD12A:	JUMPE	T2,[MOVE T1,T4	;IF WE'VE COUNTED ALL THE NODES,
		    JRST STOTC1##] ;COPY THE COUNT AND RETURN IT
	HLRZ	T1,NDBNNM(T2)	;GET THE NUMBER OF THE NODE
	SOJLE	T3,NOD12B	;COUNT DOWN ANOTHER RETURN-VALUE
	PUSHJ	P,PUTWR1##	;RETURN THE NODE NUMBER
	  JRST	ND%ADC		;ADDRESS CHECK
NOD12B:	HRRZ	T2,NDBNNM(T2)	;STEP TO THE NEXT NODE
	AOJA	T4,NOD12A	;COUNT THIS ONE AND LOOP OVER ALL NODES
;NODE13 (.NDNDB) RETURN INFORMATION ABOUT AN NDB.
;
;NODE. UUO ARGUMENT LIST
;  1)	XWD	0,LENGTH	;LENGTH OF ARG LIST. (INCLUDES THIS WORD)
;  2)	EXP	NODE NUMBER/NAME;NUMBER OR NAME OF THE NODE IN QUESTION
;  3)	EXP	SUB-FCN NUMBER	;DESCRIBES WHICH FIELD TO RETURN
;  4)	RTN-VALUE-1		;FIRST RETURN VALUE
;  5)	RTN-VALUE-2		;SECOND RETURN VALUE
;		.
;		.
;		.
;  3+N)	N'TH-RETURN-VALUE	;LAST RETURN VALUE
;
;ERRORS
;	NDIAL%==1		;ARG LIST TOO SHORT
;	NDINN%==2		;ILLEGAL NODE NUMBER/NAME
;	NDISF%==3		;ILLEGAL SUB-FUNCTION
;
;VALID FIELDS (ARG #3)
;	ND.NNM==1		;THE NUMBER OF THE SPECIFIED NODE
;	ND.SNM==2		;THE NAME IN 6 OR LESS SIXBIT CHARACTERS
;	ND.SID==3		;THE SOFTWARE ID IN "ASCIZ"
;	ND.DAT==4		;THE SOFTWARE DATE IN "ASCIZ"
;	ND.LMA==5		;THE NCL LAST MESSAGE ASSIGNED
;	ND.LMS==6		;THE NCL LAST MESSAGE SENT
;	ND.LAR==7		;THE NCL LAST ACK RECEIVED
;	ND.LAP==10		;THE NCL LAST ACK PROCESSED
;	ND.LMR==11		;THE NCL LAST MESSAGE RECEIVED
;	ND.LMP==12		;THE NCL LAST MESSAGE PROCESSED
;	ND.LAS==13		;THE NCL LAST ACK SENT
;	ND.MOM==14		;THE MAXIMUM OUTSTANDING MESSAGE COUNTER
;	ND.TOP==15		;THE TOPOLOGY TABLE. THIS IS "NGHMAX" WORDS
;				;  OF THE FORM "XWD LEVEL,NODE"
;	ND.CNF==16		;THE CONFIGURATION TABLE.  THIS IS VARIABLE
;				;  LENGTH OF THE FORM "XWD OBJ,NUMBER"
;	ND.CTL==17		;RETURN STATION CONTROL JOB
;	ND.OPR==20		;RETURN OPR TTY NUMBER
;	ND.NVR==21		;RETURN NCL VERSION NUMBER
;
NODE13:	PUSHJ	P,SAVE4##	;WE USE THE P'S AS TEMPS
	PUSHJ	P,GETWRD##	;GET THE LENGTH OF THE ARG LIST
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIGE	T1,4		;MAKE SURE THAT IT'S AT LEAST 4 WORDS LONG
	JRST	ND%IAL		;RETURN NDIAL% -- ILLEGAL ARG LIST
	MOVE	P4,T1		;SAVE THE LENGTH FOR A WHILE
	SUBI	P4,3		;ADJUST SO P4 = RETURN LOCATIONS FREE
	PUSHJ	P,GETWR1##	;GET THE NODE NUMBER/NAME
	  JRST	ND%ADC		;ADDRESS CHECK
	PUSHJ	P,SRCNDB	;SET UP W FROM THE NUMBER/NAME
	  JRST	ND%INN		;RETURN NDINN% -- ILLEGAL NODE NUMBER
	PUSHJ	P,GETWR1##	;GET THE SUB-SUB-FUNCTION CODE.
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIL	T1,1		;RANGE CHECK IT
	CAILE	T1,N13LEN	; TO MAKE SURE IT'S VALID
	  JRST	ND%ISF		;RETURN NDISF% -- INVALID SUB-FUNCTION
	XCT	N13TBL-1(T1)	;CALL ROUTINE TO GET FIELD
	  SKIPA			;MUST STORE
	JRST	CPOPJ1##	;DON'T
N13WD1:	PUSHJ	P,PUTWR1##	;NON-SKIP MEANS WE SHOULD STORE ARG HERE
	  JRST	ND%ADC		;ADDRESS CHECK
	JRST	CPOPJ1##	;ALL DONE.


;N13TBL	TABLE OF INSTRUCTIONS TO EXECUTE TO RETURN VARIOUS FIELDS OF AN NDB

N13TBL:	HLRZ	T1,NDBNNM(W)	;(01) GET THE NODE NUMBER FIELD
	MOVE	T1,NDBSN2(W)	;(02) GET NODE NAME
	JRST	N13SID		;(03) CALL SUBROUTINE TO GET SOFTWARE-ID
	JRST	N13DAT		;(04) CALL SUBROUTINE TO GET SOFTWARE-DATE
	LDB	T1,NDBLMA	;(05) GET LAST MESSAGE ASSIGNED
	LDB	T1,NDBLMS	;(06) GET LAST MESSAGE SENT
	LDB	T1,NDBLAR	;(07) GET LAST ACK RECEIVED
	LDB	T1,NDBLAP	;(10) GET LAST ACK PROCESSED
	LDB	T1,NDBLMR	;(11) GET LAST MESSAGE RECEIVED
	LDB	T1,NDBLMP	;(12) GET LAST MESSAGE PROCESSED
	LDB	T1,NDBLAS	;(13) GET LAST ACK SENT
	MOVE	T1,NDBMOM(W)	;(14) GET MAXIMUM OUTSTANDING MESSAGE COUNTER
	JRST	N13TOP		;(15) RETURN TOPOLOGY TABLE
	JRST	N13CNF		;(16) RETURN CONFIGURATION TABLE
	HRRZ	T1,NDBSTC(W)	;(17) GET STATION CONTROL JOB NUMBER
	PUSHJ	P,N13OPR	;(20) GET OPR TTY NUMBER
	HRRZ	T1,NDBNVR(W)	;(21) GET NCL VERSION NUMBER

N13LEN==.-N13TBL		;DEFINE LENGTH FOR RANGE CHECK
;N13SID (03) RETURN THE SOFTWARE ID (VERSION INFO) FOR THE NODE.

N13SID:	HRRZ	P1,NDBSID(W)	;GET A POINTER TO THE SID
	PJRST	N13ASZ		;AND GO USE ASCIZ COPY ROUTINE


;N13DAT (04) RETURN THE SOFTWARE DATE FOR THE NODE

N13DAT:	HLRZ	P1,NDBSID(W)	;GET A POINTER TO THE DATE
;	PJRST	N13ASZ		;AND GO USE THE ASCIZ COPY ROUTINE

N13ASZ:	SOSGE	P4		;COUNT DOWN NEXT WORD TO RETURN
	JRST	ND%IAL		;IF WE COUNT OUT, THEN LIST WAS TO SHORT
	SKIPE	T1,P1		;UNLESS POINTER NOT PRESENT,
	MOVE	T1,(P1)		;GET THE NEXT WORD TO RETURN
	PUSHJ	P,PUTWR1##	;STORE THE WORD IN THE ARG LIST
	  JRST	ND%ADC		;ADDRESS CHECK
	MOVEI	T2,5		;COUNT TO SEE ANY CHAR WAS A NULL
N13AS1:	TLNN	T1,774000	;WAS THIS CHAR A NUL
	JRST	CPOPJ1##	;IF SO, GIVE SKIP RETURN
	LSH	T1,7		;SHIFT TO NEXT CHAR POSITION
	SOJG	T2,N13AS1	;LOOP OVER ALL 5 CHARACTERS
	AOJA	P1,N13ASZ	;LOOP OVER ALL WORDS IN NAME
;N13TOP (15) RETURN THE TOPOLOGY TABLE FOR THE PARTICULAR NODE.
;
;THE TABLE IS RETURNED IN THE FORM OF UP TO 16 WORDS OF
;	XWD LEVEL,NODE
;  WHERE NODE IS THE NEIGHBOR OF THE NODE IN QUESTION, AND LEVEL IS THE
;  "COST" OF THE LINK TO GET THERE

N13TOP:	MOVE	P3,[POINT 9,NDBTOP(W)] ;POINTER TO THE TOPOLOGY TABLE
	MOVEI	P2,NGHMAX	;MAX LENGTH OF THE TOPOLOGY TABLE
N13TP1:	SOSGE	P2		;COUNT DOWN ANOTHER ENTRY
	JRST	N13RZR		;ALL DONE. WRITE TERMINATING ZERO AND RETURN
	ILDB	T1,P3		;GET THE NEXT ENTRY
	ILDB	T2,P3		;GET ITS COST
	JUMPE	T1,N13TP1	;IF NULL, KEEP TRYING
	SOSGE	P4		;COUNT DOWN THE LENGTH OF THE ARGLIST
	JRST	ND%IAL		;ILLEGAL ARGLIST (TOO SHORT)
	HRLI	T1,(T2)		;PUT COST IN LH OF RETURN VALUE
	PUSHJ	P,PUTWR1##	;STORE THE "XWD LEVEL,NODE"
	  JRST	ND%ADC		;ADDRESS CHECK
	JRST	N13TP1		;LOOP OVER ALL TABLE ENTRIES

;N13CNF (16) RETURN THE CONFIGURATION INFORMATION FOR A NODE.
;
;  CONFIGURATION INFORMATION RETURNED AS UP TO "OBJ.MX" WORDS OF THE FORM
;	XWD	OBJECT-TYPE,NUMBER
;  WHERE OBJECT-TYPE IS THE NCL TYPE OF THE DEVICE, AND NUMBER IS THE
;  NUMBER OF SAID DEVICES THAT THE NODE POSSESSES.

N13CNF:	MOVEI	P1,OBJ.MX+1	;MAXIMUM OBJECT TYPE +1
N13CN1:	SOJL	P1,N13RZR	;COUNT DOWN THROUGH ALL OBJECT TYPES
	LDB	T1,NETCNF##(P1)	;GET THE NUMBER OF THIS TYPE
	JUMPE	T1,N13CN1	;IF NONE, KEEP TRYING
	HRLI	T1,(P1)		;PUT OBJECT TYPE IN THE LH
	SOSGE	P4		;COUNT DOWN ANOTHER RETURN VALUE
	JRST	ND%IAL		;ARG LIST TOO SHORT
	PUSHJ	P,PUTWR1##	;RETURN THE VALUE
	  JRST	ND%ADC		;ADDRESS CHECK
	JRST	N13CN1		;LOOP OVER ALL OBJECT TYPES

;N13RZR	ROUTINE TO WRITE THE TERMINATING ZERO FOR N13TOP AND N13CNF

N13RZR:	SOSGE	P4		;COUNT DOWN THE ARGLIST
	JRST	ND%IAL		;ERROR IF LIST TOO SHORT
	SETZ	T1,		;ZERO TO STORE
	JRST	N13WD1		;AND LET NODE13 STORE IT FOR US.


;N13OPR (20) RETURNS OPR TTY NUMBER OR -1 IF NOT CONNECTED

N13OPR:	HRRZ	T1,NDBOPR(W)	;GET LDB OF OPR LINE FOR THIS NODE
	SKIPN	T1		;HAVE ONE?
	SOJA	T1,CPOPJ##	;NO--RETURN -1
	EXCH	U,T1		;SAVE U, GET LDB ADDRESS
	SSX	U,MS.SCN	;USE SCNSER SECTION
	LDB	U,LDPLNO##	;GET THE LOCAL LINE NUMBER
	EXCH	T1,U		;RESTORE U
	POPJ	P,		;AND RETURN
;NODE14	(.NDGNF) READ/SET THE GREETED NODE FLAG
;NODE. UUO ARGUMENT BLOCK
;  1)	XWD 0,LENGTH		;LENGTH MUST BE "2"
;  2)	EXP 0!NODE #		;EITHER 0 (RETURN FIRST UN-GREETED NODE)
;				;  OR NODE # (SET "GREETED BIT")
;RETURNS
;	IN ARG 2 THE NODE # OF THE FIRST "UN-GREETED" NODE)
;
;ERRORS:
;	ND%IAL			;ILLEGAL ARG LIST
;	ND%NUG			;NO UNGREETED NODES
;	ND%INN			;ILLEGAL NODE NUMBER (SET GREETED BIT FCN)
;	ND%PRV			;USER NOT PRIVLEGDED
;
NODE14:	PUSHJ	P,NTDPRV	;CHECK PRVS RIGHT AWAY
	  PJRST	ND%PRV		;USER DOESN'T HAVE PRVS TO DO THIS
	PUSHJ	P,GETWRD##	;GET ARG LIST LENGTH
	  JRST	ND%ADC		;ADDRESS CHECK
	CAIGE	T1,2		;MAKE SURE ITS LONG ENOUGH
	PJRST	ND%IAL		;IF TO SHORT, GIVE "ILLEGAL ARG LIST" ERROR
	PUSHJ	P,GETWR1##	;GET 0 (RETURN) OR NODE # (SET)
	  JRST	ND%ADC		;ADDRESS CHECK
	JUMPE	T1,NOD14A	;IF 0 (RETURN) GO SEARCH NDB'S
	PUSHJ	P,SRCNDB	;GO SEE IF WE CAN FIND AN NDB FOR HIM
	  PJRST	ND%INN		;IF NO NDB, GIVE ILLEGAL NODE NUMBER ERROR
	MOVSI	T1,NDB.GF	;GET THE "GREETED FLAG"
	IORM	T1,NDBFLG(W)	;  AND SET IT
	RETSKP			;GIVE GOOD RETURN TO THE UUO.

NOD14A:	HRRZI	W,NETNDB	;GET THE ROOT OF ALL NODES
NOD14B:	HRRZ	W,NDBNNM(W)	;GO TO NEXT NODE (SKIP NETNDB)
	JUMPE	W,ND%NUG	;IF AT END OF LIST, GIVE NO UN-GREETED NODE ERR
	MOVE	T1,NDBFLG(W)	;GET THE FLAGS
	TLNE	T1,NDB.UP	;IF THE NODE IS NOT UP,
	TLNE	T1,NDB.GF	;  OR IT'S ALREADY BEEN GREETED
	JRST	NOD14B		;THEN GO TRY THE NEXT NODE
	HLRZ	T1,NDBNNM(W)	;THIS IS OUR NODE, GET THE NUMBER
	PUSHJ	P,PUTWRD##	;STORE IT FOR THE USER
	  JRST	ND%ADC		;ADDRESS CHECK
	RETSKP			;GIVE GOOD RETURN TO UUO
SUBTTL	INTERFACE TO UUOCON (RECON. UUO)


;SYSTEM SLEEP ROUTINES

;ROUTINE TO CALL ALL FEKS ON THIS CPU WHEN SYSTEM GOES TO SLEEP
;CALL	PUSHJ	P,FEKCPS
;RETURN	CPOPJ			;CLOBBERS NO REGISTERS

FEKCPS::PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	P1,FF.CPS	;GET THE "CPU SLEEP" BIT
	JRST	FEKCPC		; AND GO TO COMMON CODE TO CALL THE FEKS


;ROUTINE TO CALL ALL FEKS ON THIS CPU WHEN SYSTEM WAKES UP
;CALL	PUSHJ	P,FEKCPW
;RETURN	CPOPJ			;CLOBBERS NO REGISTERS

FEKCPW::PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	P1,FF.CPW	;GET THE "CPU WAKING UP" FUNCTION CODE
;	PJRST	FEKCPC		;GO TO COMMON CODE


;CODE TO CALL ALL FEKS ON THIS CPU WITH THE FUNCTION CODE IN "P1"
FEKCPC:	PUSHJ	P,SAVT##	;SAVE ALL THE TEMPS (FOR SPRINI)
	PUSH	P,U		;  AS WELL AS
	PUSH	P,F		;  SUNDRY OTHER
	PUSH	P,W		;  REGISTERS
	PUSH	P,J		;  ..
	MOVEI	J,FEKFST##	;START WITH THE FIRST FEK
	JUMPE	J,FEKCP2	;  IF NONE AT ALL, WE'RE DONE
FEKCP1:	MOVE	T1,P1		;GET THE FUNCTION CODE
IFN FTMP,<
	HLRZ	T2,FEKUNI(J)	;GET THE CPU THIS FEK IS ON
	CAMN	T2,.CPCPN##	;  AND IF WE ARE ON THE RIGHT ONE.
>
	XCT	FEKDSP(J)	;CALL THE FEK
	HRRZ	J,FEKBLK(J)	;GET THE NEXT FEK
	JUMPN	J,FEKCP1	;  AND CALL HIM TOO.
FEKCP2:	POP	P,J		;RESTORE ALL
	POP	P,W		;  THESE REGISTERS
	JRST	FUPOPJ##	;AND WE'RE DONE
SUBTTL	NDB INTERFACE ROUTINES
;SUBROUTINE NETOPR -- KEEP NETNDB(NDBOPR) UP TO DATE WITH OPRLDB
;CALL	MOVE	U,NEW OPR LDB
;RETURN	CPOPJ1

NETOPR::HRRM	U,NETNDB##+NDBOPR	; STORE THE LDB ADDRESS
	JRST	CPOPJ1##		; SKIP RETURN FOR SCNSER



;SUBROUTINE TO LOAD INTO T1 THE TERMINAL NUMBER OF THE OPR'S
;  TERMINAL FOR THE STATION WHOSE NUMBER IS IN T1.

STBOPR::
	SE1ENT
	PUSH	P,U		;SAVE U
	HRRZS	T1		;STATION NUMBER IN T1
	NETOFF			;PROTECT OURSELF WHEN SCANNING THE NDB'S
				; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB")
	PUSHJ	P,SRCND0	;FIND THE NODE BLOCK (BYPASS 'NT' CHECK)
	  JRST	LCLOPR		;NO, USE LOCAL OPR
	HRRZ	U,NDBOPR(W)	;GET THE OPR LDB
	SKIPN	U		;ANY ASSIGNED
LCLOPR:	MOVE	U,OPRLDB##	;NO, USE LOCAL OPR
	SSX	U,MS.SCN	;USE SCNSER SECTION
	LDB	T1,LDPLNO##	;GET NUMBER OF TERMINAL IN U
	NETON			;INT'S ON AGAIN
	JRST	UPOPJ##		;RESTORE U AND EXIT.

;SUBROUTINE STBSCA - RETURN THE STATION NUMBER
;CALL	MOVEI	T1,NNM
;	PUSHJ	P,STBSCA
;RETURN	CPOPJ			;NO SUCH STATION
;	CPOPJ1			;STATION VALID T1=NNM

STBSCA::			;ENTRY
	PUSHJ	P,SAVJW##	;SAVE J AND W
	NETOFF			;PROTECT WHILE LOOKING AT THE NDB'S
				; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB")
	PUSHJ	P,SRCND0	;SEARCH NDB'S (BYPASS THE 'NT' CHECK)
	  JRST	NTONPJ		;RETURN INVALID STATION
	HLRZ	T1,NDBNNM(W)	;GET THE STATION NUMBER
	NETON			;ALL'S CLEAR
	PJRST	CPOPJ1##	;EXIT
SUBTTL ERROR MESSAGES.

NODERT:	XWD	[ASCIZ  / Assigned/]
NRTDNA:	XWD	[ASCIZ  /Device Not Available/]
NRTNCE::XWD	[ASCIZ	/Network Capacity Exceeded/]
NRTTNA:	XWD	[ASCIZ  /Task Not Available/]
NRTUNT:	XWD	[ASCIZ  /Undefined Network Task/]
NRTUNN::XWD	[ASCIZ  /Undefined Network Node/]
NRTUND:	XWD	[ASCIZ  /Undefined Network Device/]
SUBTTL TSTNET - FIND OUT IF A DEVICE EXISTS ON THE NETWORK
;SUBROUTINE TSTNET - ASSIGN A DDB FOR THE DEVICE IF IT EXISTS
;CALL	MOVE	T1,DEVICE NAME
;	PUSHJ	P,TSTNET
;RETURN	CPOPJ		;DEVICE DOES NOT EXIST/NOT AVAILABLE
;	CPOPJ1		;DEVICE EXISTS AND IS AVAILABLE

TSTNET::			;ENTRY FROM UUOCON
	HLRZ	T2,M		;GET THE UUO
	ANDI	T2,777000	;ONLY THE UUO OP CODE
	CAIE	T2,(OPEN)	;IS IT
	POPJ	P,		;NO, GET THE ASSIGN LATER
	HLRZ	T2,T1		;GET GENERIC PORTION OF NAME
	CAIN	T2,'TTY'	;IS IT A TTY?
	POPJ	P,		;YES, TTY'S DON'T HAVE STATION NUMBERS IN DDBS
	TRNE	T1,7700		;IS THERE A STATION NUMBER
	PUSHJ	P,DVSCVT##	;GET THE STATION NUMBER
	  POPJ	P,		;NOT A STATION NUMBER
	PUSH	P,T1		;SAVE THE NAME
	DPB	T1,[POINT 6,T1,23]	;MOVE THE UNIT NUMBER OVER
	TRZ	T1,7777		;CLEAR THE JUNK
	PUSHJ	P,GENNET	;GO ASSIGN THE DEVICE
	  PJRST	TPOPJ##		;NO SUCH DEVICE
	PJRST	TPOPJ1##	;DEVICE ASSIGNED F=DDB
;SUBROUTINE GENNET - TEST FOR A GENERIC NETWORK DEVICE
;CALL	MOVE	T1,[SIXBIT /DEVNAME/]
;	MOVE	T2,STATION NUMBER
;	PUSHJ	P,GENNET
;RETURN	POPJ			;NO ON THE NETWORK OR AVAILABLE
;	CPOPJ1			;F=DDB ADDRESS

GENNET::			;ENTRY FROM UUOCON
	NETDBJ			;INTERLOCK THIS CODE
	CAMN	T2,JBTLOC##	;LOCAL STATION
	POPJ	P,		;YES, RETURN
	PUSHJ	P,SAVT##	;NO, SAVE THE T'S
	MOVEI	T4,(P)		;GET THE STACK ADDRESS
	CAMG	T4,LOCORE##	;RUNNING AT CLOCK LEVEL
	POPJ	P,		;YES, DON'T TRY THE NETWORK
	PUSHJ	P,SAVJW##	;SAVE J AND W
	PUSHJ	P,SAVE3##	; AND THE P'S
	MOVE	P2,T1		;COPY THE DEVICE NAME
	MOVEI	T1,(T2)		;PUT THE STATION NUMBER IN T1
	PUSHJ	P,SRCNDB	;DOES THE STATION EXIST
	  POPJ	P,		;NO EXIT
	MOVEI	P1,(W)		;SAVE THE NDB POINTER
	SETZ	P3,		;CLEAR THE LOCAL NAME
	HLRZ	T1,P2		;GET THE GENERIC DEVICE NAME
	PUSHJ	P,SRCNDT	;SEE IF IT EXISTS AT THE STATION
	  POPJ	P,		;NO, EXIT
	PUSHJ	P,MAKDDB	;YES, MAKE A DDB
	  POPJ	P,		;CAN'T
	PUSHJ	P,NTDCNT	;USE THE "NORMAL" DEVICE CONNECT ROUTINE (WAITS)
	  PJRST	RMVNET		;FAILED DISCARD DDB
	PUSHJ	P,LNKDDB	;LINK CONNECTED DDB
	PJRST	CPOPJ1##	;WE WON
SUBTTL DDB -- ROUTINES TO MANAGE NETWORK DEVICE DATA BLOCKS

;MAKDDB	ROUTINE TO CREATE A NETWORK DDB
;CALL	MOVE	P1,NDB POINTER
;	MOVE	P2,DEVICE NAME
;	MOVE	P3,LOGICAL NAME
;	MOVE	W,NDT POINTER
;	MOVE	T2,LENGTH	;MAKDDC ONLY
;	PUSHJ	P,MAKDDB/MAKDDC
;RETURN	CPOPJ		;NO SPACE AVAILABLE
;	CPOPJ1		;F=DDB POINTER,U=PCB
;
;NOTE!	THIS ROUTINE DOES   NOT   ASSIGN AN SLA

MAKDDB::MOVEI	T2,NETLEN	;GET THE LENGTH OF THE PROTOTYPE DDB
MAKDDC::NTDBUG			;VERIFY THE INTERLOCK
IFN	PARANOID&P$DDB,<	;SOME CONSISTENCY CHECKING
	CAIL	T2,NETLEN	;CAN'T BE SMALLER THAN MINIMUM NETWORK LENGTH
	CAILE	T2,NETLEN+10	;AND NONE ARE MUCH BIGGER
	STOPCD	.,STOP,ANFUND,	;++ UNREASONABLE NETWORK DDB LENGTH
> ;END IFN PARANOID&P$DDB
	PUSH	P,T2		;SAVE ALLOCATED LENGTH
	PUSHJ	P,GETZWD	;ALLOCATE THE SPACE
	  JRST	T2POPJ##	;SPACE NOT AVAILABLE
	MOVEI	F,(T1)		;COPY THE DDB POINTER
	HRLI	T1,NETDDB##	;BLT POINTER TO COPY THE PROTOTYPE DDB
	BLT	T1,NETLEN-1(F)	;COPY IT
	POP	P,T2		;RETRIEVE LENGTH OF NEW DDB
	DPB	T2,NETZWD	;SET LENGTH OF DDB FOR RMVNET
	CAIG	T2,NETLEN	;IS THIS AN EXTENDED DDB?
	JRST	MAKDD2		;NO
	SETZM	NETLEN(F)	;YES, CLEAR OUT REMAINDER
	MOVSI	T1,NETLEN(F)	;CONCOCT A
	HRRI	T1,NETLEN+1(F)	; BLT POINTER
	ADDI	T2,(F)		;AND A BLT TERMINATOR
	BLT	T1,-1(T2)	;TO CLEAR DDB EXTENSIONS

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

MAKDD2:	MOVEM	P2,DEVNAM(F)	;STORE THE DEVICE NAME
	MOVEM	P3,DEVLOG(F)	;STORE THE LOGICAL NAME
	HLRZ	T1,NDBNNM(P1)	;GET THE NODE NUMBER
	HRRZM	T1,DEVNET(F)	;STORE THE NODE NUMBER
	DPB	T1,PDVSTA##	;STORE AS THE STATION NUMBER
	TRO	T1,77700	;FORCE A STATION NUMBER ALIGNMENT
	S0PSHJ	CVTSBT##	;CONVERT TO SIXBIT
	MOVE	T2,P2		;COPY THE DEVICE NAME
	TRNN	T2,7777		;CHECK FOR GGGU
	LSH	T2,-14		;YES, SHIFT FOR THE UNIT NUMBER
	ANDI	T2,77		;ONLY ONE DIGIT
	IORI	T1,(T2)		;INSERT THE DIGIT
	HRRM	T1,DEVNAM(F)	;STORE IN THE DEVNAME
	ANDI	T1,77		;SAVE LAST DIGIT
	SKIPN	T1		;IS IT A BLANK IE GENERIC
	TROA	T1,177		;YES, USE 177 FOR GENERIC SEARCH
	ANDI	T1,7		;ONLY THREE BITS WORTH (FOR GGGNNU)
	DPB	T1,PUNIT##	;STORE THE UNIT NUMBER
	MOVSI	T1,DVCNET	;GET THE NETWORK OWNERSHIP BIT
	IORM	T1,DEVCHR(F)	;STORE WE OWN IT
	DPB	J,PJOBN##	;STORE THE JOB NUMBER
	LDB	T1,NDTBFZ	;GET THE DEFAULT BUFFER SIZE
	DPB	T1,PBUFSZ##	;STORE THE BUFFER SIZE
	LDB	T1,NDTTYP	;GET THE DEVICE TYPE
	DPB	T1,DEYTYP##	;STORE THE DEVICE TYPE
	LDB	T1,NDTSPL	;GET THE DEFAULT SPOOL BITS
	DPB	T1,DEYSPL##	;  AND STORE THEM IN THE NEW DDB
	MOVE	T1,NDTMOD(W)	;GET THE DEVMOD BITS
	MOVEM	T1,DEVMOD(F)	;STORE DEVMOD
	HRLM	W,DEVNET(F)	;LINK THE NDT TO THE DDB
	JRST	CPOPJ1##	;EXIT WITH F=NETDDB, U=PCB, W=NDT
;SUBROUTINE LNKDDB - ADD THE DDB(F) INTO THE DDB CHAIN
;CALL	MOVEI	F,DDB
;	PUSHJ	P,LNKDDB
;RETURN	CPOPJ

LNKDDB::PUSHJ	P,SAVE1##	;SAVE P1
	PUSH	P,F		;SAVE THE DDB POINTER
	MOVE	T1,DEVLOG(F)	;GET THE LOGICAL NAME
	PUSHJ	P,DEVLG##	;CHECK FOR IN USE
	  JRST	LNKDD1		;NO,
	SETZM	DEVLOG(F)	;YES, CLEAR THE LOGICAL NAME
	MOVE	T2,DEVMOD(F)	;GET THE DEV MODE BITS
	TLNE	T2,DVDSK	;IS IT A DSK
	TRNE	T2,ASSPRG	; AND INIT'ED
	JRST	LNKDD1		;NO IGNORE
	PUSHJ	P,CLRDVL##	;CLEAR JOB NUMBER IN DDB
	PUSHJ	P,CLRDDB##	;YES, CLEAR THE DDB
LNKDD1:	POP	P,F		;RESTORE THE DDB POINTER
	DPB	J,PJCHN##	;SET JCH IN DDB
	SKIPN	DEVLOG(F)	;WAS IT BEING CLEARED
	PUSHJ	P,ASSCK1##	;YES, RECLAIM SPACE
	PJRST	AUTLNK##	;PUT IT IN THE DEVICE CHAIN AND RETURN
;SUBROUTINE UNLDDB  --  ROUTINE TO UNLOAD A NETWORK DDB.  THIS ROUTINE
;	UNLINKS AND DELETES THE DDB.
;CALL	MOVEI	F,DDB
;	PUSHJ	P,UNLDDB
;RETURN	CPOPJ

UNLDDB::			;GLOBAL ENTRY POINT
	NTDBUG			;VERIFY THE INTERLOCK
	PUSHJ	P,UNLKDB	;UNLINK DDB (ALSO WORKS OK IF NOT LINKED!!)
	PJRST	RMVNET		;NO GIVE THE STORAGE BACK TO THE MONITOR


;SUBROUTINE UNLKDB - UNLINK A DDB FROM THE SYSTEM'S LIST
;CALL	MOVE	F,DDB
;RETURN	CPOPJ

UNLKDB:	MOVEI	T2,(F)		;COPY DDB ADDRESS
	SETZ	T1,		;ZERO LENGTH ARG MEANS DON'T DELETE
	PJRST	AUTKIL##	;HALF-KILL THIS DDB


;RMVNET	ROUTINE TO FREE THE STORAGE ASSOCIATED WITH A DDB.
;CALL	MOVEI	F,DDB		;NOT LINKED OR CONNECTED
;	PUSHJ	P,RMVNET
;RETURN	CPOPJ			;DDB FREED
;
RMVNET::			;HERE TO DELETE A DDB
	PUSHJ	P,CLNNET	;CLEAN OUT THE DDB (FREE RANDOM STUFF)
	LDB	T1,NETZWD	;GET LENGTH OF THIS PARTICULAR DDB
	MOVEI	T2,(F)		;GET ADDR OF THE DDB
	PUSHJ	P,GIVZWD	;FREE THE DDB
	SETZ	F,		;MAKE SURE F DOESN'T POINT ANYWHERE
	POPJ	P,		;ALL DONE
;CLNNET	ROUTINE TO INITIALIZE A DDB.
;CALL	MOVEI	F,DDB		;MUST NOT BE CONNECTED
;	PUSHJ	P,CLNNET
;RETURN	CPOPJ			;DDB INITIALIZED

CLNNET::NTDBUG			;JUST CHECKING...

IFN PARANOID&P$DDB,<		;MAKE SURE THAT THIS DDB IS TRULY FREE.

	SKIPN	F		;IS THERE A DDB THERE
	STOPCD	.,STOP,ANFCND	;++ CLNNDB HAS NO DDB
	LDB	T1,NETSLA	;SHOULDN'T HAVE A LAT ASSIGNED
	SKIPE	T1		; ALL THE DISCONNECT ROUTINES CLEAR THIS
	STOPCD	.,STOP,ANFCLA	;++ LAT STILL ASSIGNED IN CLNNDB
>
	PUSHJ	P,GV2NPD	;WE MUST FREE THE NPD'S

	PUSH	P,U		;SAVE "U" FOR A BIT
	HRRZ	U,DEVPCB(F)	;GET THE POINTER TO QUEUED PCB'S (IF ANY)
	PUSHJ	P,RMVALP	;FREE THEM ALL
	POP	P,U		;RESTORE "U"

	SETZM	DEVDRQ(F)	;CLEAR ANY STALE DATA-REQUESTS
	SETZB	T1,DEVPCB(F)	;CLEAR POINTER TO JUST FREED PCB'S
	SETZB	T2,DEVPBP(F)	;  AND CLEAR AUX POINTER TO SUB MSG
	DMOVEM	T1,DEVAXI(F)	;CLEAR INPUT BUFFER POINTERS
	DMOVEM	T1,DEVAXO(F)	;  AND OUTPUT TOO.
	HLLZS	DEVNET(F)	;CLEAR NODE NUMBER
	DPB	T1,NETSLA	;CLEAR SOURCE LINK ADDR
	DPB	T1,NETDLA	;  DESTINATION LINK
	DPB	T1,NETRLN	;  RECORD LENGTH
	DPB	T1,NETDVT	;  AND ATTRIBUTES
	POPJ	P,		;ALL DONE.  RESTORE A CLEAN DDB
;ZAPNET	ROUTINE TO TAKE CARE OF USELESS NETWORK DDB'S
;CALL	MOVEI	F,DDB
;	PUSHJ	P,ZAPNET
;RETURN	CPOPJ, F/0 IF DDB ZAPPED, OTHERWISE UNCHANGED
;
;ZAPNET IS CALLABLE FOR ANY NON-TTY DDB IN F, WILL RETURN HARMLESSLY IF
;THE DDB IS NOT NETWORK-ZAPPABLE (I.E., IS NOT A ANF DEVICE, IS UNDER MDA
;CONTROL, IS RESTRICTED, OR IS ASSIGNED/INITED BY A JOB/PROGRAM)

ZAPNET::MOVE	T1,DEVMOD(F)	;A COLLECTION OF DEVICE FLAGS
	TRNE	T1,ASSCON!ASSPRG;NEVER DISCONNECT A DDB WHICH IS IN USE
	POPJ	P,		;DON'T DISCONNECT THIS DEVICE
ZAPNE0:	MOVE	T1,DEVCHR(F)	;SOME DIFFERENT DEVICE FLAGS
IFN FTMDA,<TLNN T1,DVCMDA>	;NEVER DISCONNECT ONE OF QUASAR'S PLAYTHINGS
	TLNN	T1,DVCNET	;NEVER DISCONNECT A NON-ANF-NETWORK DEVICE
	POPJ	P,		;DON'T DISCONNECT THIS DEVICE
	MOVE	T1,DEVSTA(F)	;OTHER ASSORTED DEVICE FLAGS
	TLNE	T1,DEPRAS	;NEVER DISCONNECT A RESTRICTED DEVICE
	POPJ	P,		;DON'T DISCONNECT THIS DEVICE
	LDB	T1,PDVTYP##	;GET THE DEVICE TYPE
	CAIE	T1,.TYDDP	;IS THIS A DDP DEVICE?
	JRST	ZAPNE1		;NO
	SKIPE	[M.RDDP##]	;DDCMP DEVICE SERVICE LOADED?
	PJRST	ZAPDDP##	;YES, FIRST CHECK WITH DDPSER ON ZAPABILITY

ZAPNE1::NETDBJ			;GET THE NETSER INTERLOCK
	SETCM	T1,NETZAP##	;GET CATASTROPHIC ERROR FLAG
	JUMPN	T1,ZAPNE2	;NORMAL RELEASE
	DPB	T1,NETSLA	;PITCH SLA
	DPB	T1,NETDLA	;AND DLA
ZAPNE2:	MOVE	S,DEVIOS(F)	;GET THE IO STATUS
	TLNE	S,IOSZAP	;HAVE WE FREED THIS DDB ONCE ALREADY??
	STOPCD	.,STOP,ANFRDT,	;++ RELEASING DDB TWICE
	TLNN	S,IOSCON	;IS THIS DEVICE CONNECTED?
	PJRST	UNLDDB		;IF NOT CONNECTED, JUST DELETE THE DDB.
	MOVEI	T1,RSN.OK	;NORMAL DISCONNECT REASON
	PUSHJ	P,NTDXDS	;SEND THE DISCONNECT.
	  JRST	[PUSHJ	P,NETSLP	;IF WE CAN'T, WAIT FOR CORE TO SHOW UP
		JRST	ZAPNET]		;AND TRY AGAIN.
	MOVSI	S,IOSZAP	;SINCE NO-ONE WANTS THIS DEVICE ANY MORE,
	IORB	S,DEVIOS(F)	;INDICATE THAT IT IS FREE TO BE DELETED.
	PUSHJ	P,UNLKDB	;UNLINK THE DDB SO NO-ONE ELSE GETS IT
	SETZ	F,		;INDICATE THAT THE DDB IS GONE, AND
	POPJ	P,		;RETURN. (NTDDSC WILL EVENTUALLY FREE THE DDB)
SUBTTL PCB -- ROUTINES TO HANDLE PROTOCOL CONTROL BLOCKS

;SUBROUTINE MK?PCB - BUILD A PROTOCOL DATA BLOCK
;CALL	MOVEI	F,DDB OR 0 IF AN NCL MESSAGE
;	MOVEI	W,NDB
;	MOVEI	T1,BUFFERSIZE	;SIZE OF ASSOCIATED MESSAGE BUFFER
;	PUSHJ	P,MK?PCB
;RETURN	CPOPJ			;NO SPACE AVAILABLE, OR TOO MANY PENDING MSGS
;	CPOPJ1			;U=PCB POINTER
;
;MKUPCB	THIS SUBROUTINE IS USED TO GET A PCB FOR AN UNNUMBERED MESSAGE.
;	IT DOES NOT DECREMENT, OR CHECK NDBMOM(W)
;MKNPCB	THIS SUBROUTINE IS USED TO ALLOCATE A PCB FOR A NUMBERED MESSAGE.
;	IT CHECKS TO MAKE SURE THAT NDBMOM(W) IS GREATER THAN ZERO.  THIS
;	CHECK IS NECESSARY TO ENSURE THAT WE DO NOT SEND MORE THAN 128 MSGS
;	TO ANY GIVEN NODE.  (IF WE DID THE MSG NUMBERS WOULD WRAP AROUND!)

MKNPCB::			;MAKE NUMBERED PCB
	SKIPE	NTUEFC		;IS THIS AN EMERGENCY?
	JRST	MKNPC1		;YES - SKIP THIS CHECK
	PUSH	P,T1		;NO
	MOVE	T1,NDBMOM(W)	;GET NUMBER OF MESSAGES AVAILABLE
	CAIG	T1,5		;ARE THERE A FEW FREE?
	JRST	TPOPJ##		;NO - SAY WE CAN'T DO IT
	POP	P,T1		;DO IT
MKNPC1:
	SOSLE	NDBMOM(W)	;CHECK ON THE NUMBER OF OUTSTANDING MSGS
	PUSHJ	P,MKUPCB	; IF WE CAN, TRY TO MAKE ANOTHER PCB
	  AOSA	NDBMOM(W)	;HERE IF WE CAN'T SEND ANOTHER PCB NOW.
	JRST	[SETZM NTUEFC	;IF WE GOT ONE, CLEAR "EMERGENCY FLAG"
		 RETSKP]	;  AND GIVE GOOD RETURN
	SKIPN	NTUEFC		;IF WE CAN'T USE EMERGENCY FREE CORE,
	POPJ	P,		;  THEN GIVE BAD RETURN
	SETZM	NTUEFC		;OTHERWIZE CLEAR THE "EMERGENCY" FLAG
	PJRST	PCBEGT		;  AND GO GET "EMERGENCY" MEMORY
MKUPCB::			;MAKE UN-NUMBERED PCB
IFN PARANOID&P$COR,<		;IF WE'RE BEING CAUTIOUS,
	SKIPLE	T1		;  THEN MAKE SURE THAT THE CALLER
	CAILE	T1,MSGMAW##	;  ASKED FOR A POSITIVE, NOT-TOO-LARGE BUFFER
	STOPCD	.,STOP,ANFMRL,	;++ MESSAGE REQUEST TOO LARGE
> ;END IFN PARANOID&P$COR

	ADDI	T1,MSGAGW##-1	;ROUND UP AND
	ANDCMI	T1,MSGAGW##-1	; TRUNCATE MODULO ALLOCATION GRANULARITY
	MOVE	T2,T1		;COPY OF REQUESTED SIZE (WORDS)
	LSH	T2,-MSGAGN##	;REDUCE TO ALLOCATION GRANULARITY
	NETOFF			;NO INTERRUPTS WHILE LOOKING AT FREE-LISTS
	SKIPG	NTFREC##(T2)	;ARE THERE ANY PCB'S ON THIS FREE LIST?
	JRST	MAKPC1		;NO FREE PCB'S, ALLOCATE A NEW ONE
	HRRZ	U,NTFREF##(T2)	;GET THE FIRST FREE PCB
	HRRZ	T1,PCBBLK(U)	;GET THE NEXT FREE PCB
	HRRZM	T1,NTFREF##(T2)	;  AND MAKE THE "NEXT" ONE THE NEW "FIRST" ONE
	SOSG	NTFREC##(T2)	;DECREMENT THE FREE COUNT, IF ZERO, THEN
	SKIPN	NTFREF##(T2)	;MAKE SURE THAT PCB FREE LIST IS NULL
	SKIPG	U		;MAKE SURE THAT WE GOT A VALID PCB
	STOPCD	.,STOP,ANFPCL,	;++ PCB LISTS SCREWED UP
	NETON			;ALL'S CONSISTENT. INTS BACK ON
	HLLZS	PCBBLK(U)	;MAKE SURE WE DON'T KEEP ANY POINTERS

IFN PARANOID&P$COR,<		;IF WE'RE BEING CAUTIOUS,
	MOVE	T1,PCBTAG(U)	;A PCB UNIQUENESS
	CAME	T1,['PCBTAG']	;DOES ALLEGED PCB LOOK LIKE A PCB?
	STOPCD	.,STOP,ANFPCT	;++ PCB TRASHED
	MOVE	T1,PCBALN(U)	;LENGTH OF DATA BUFFER (WORDS)
	LSH	T2,MSGAGN##	;LENGTH IT SHOULD BE
	CAME	T1,T2		;IF LENGTHS DIFFERENT
	STOPCD	.,STOP,ANFBLW,	;++ BUFFER LENGTH WRONG
	MOVE	T1,PCBADR(U)	;ADDRESS OF DATA BUFFER
	PUSH	P,T2		;SAVE LENGTH
	HLRZ	T2,-1(T1)	;GET TOP-END CHECK WORD
	ADD	T1,0(P)		;ADJUST ADDRESS AND
	MOVE	T1,0(T1)	;GET BOTTOM-END CHECK WORD
	CAMN	T1,['NETMEM']	;IS BOTTOM-END CHECK WORD OK?
	CAIE	T2,'NET'	;IS TOP-END CHECK WORD OK?
	STOPCD	.,STOP,ANFDMU,	;++ DATA BUFFER MESSED UP
> ;END IFN PARANOID&P$COR

	MOVE	T1,PCBADR(U)	;DATA BUFFER ADDRESS
	HRLI	T1,(POINT 8,)	;PROTOTYPE BYTE POINTER
	MOVEM	T1,PCBPTR(U)	;RESET PCB BYTE POINTER
				; (CAN BE TRASHED BY ETHSER/D8EINT)
	JRST	TPOPJ1##	;GIVE SUCCESS RETURN
;HERE TO ALLOCATE THE PCB FROM MONITOR FREE CORE

MAKPC1:	NETON			;NOT DIDDLING THE FREE LIST ANYMORE
	PUSH	P,T1		;SAVE THE BUFFER SIZE
	MOVEI	T2,PCBLEN	;GET THE LENGTH OF A PCB
	PUSHJ	P,GETZWD	;GET A ZERO BLOCK OF FREE CORE
	  JRST	TPOPJ		;NO SPACE
	MOVEI	U,(T1)		;COPY THE PCB POINTER
	MOVE	T2,(P)		;GET BUFFER SIZE BACK
	JUMPE	T2,TPOPJ1	;IF NO BUFFER WANTED, LEAVE NOW
	PUSHJ	P,GETZWD	;GET THE BUFFER
	  PJRST	[POP P,T1	;IF NONE AVAILABLE, CLEAN STACK AND
		 PJRST RMVPCB]	;AND RETURN THE PCB
	MOVEM	T1,PCBADR(U)	;SAVE BUFFER ALLOCATED ADDRESS
	HRLI	T1,(POINT 8)	;MAKE BYTE POINTER TO THE BUFFER AREA
	MOVEM	T1,PCBPTR(U)	;AND SAVE IT AWAY
	POP	P,PCBALN(U)	;REMEMBER BUFFER ALLOCATED LENGTH
	AOS	(P)		;GIVE A GOOD RETURN
	PJRST	CLNPCB		; AND SET UP CACHE INFORMATION
;SUBROUTINE RMVPCB - REMOVE THE PCB FROM FREE CORE
;CALL	MOVEI	U,PCB
;	S0PSHJ	RMVPCB
;RETURN	CPOPJ

RMVPCB:	JUMPE	U,CPOPJ##	;DON'T FREE A NON-EXISTANT PCB

IFN PARANOID&P$COR,<		;IF WE ARE BEING CAUTIOUS,
	SKIPE	PCBAL2(U)	;MAKE SURE THAT THE "SECONDARY"
	STOPCD	.,STOP,ANFSBA,	;++ SECONDARY BUFFER ALLOCATED ("OLD FEATURE")
>

	MOVE	T2,PCBALN(U)	;GET THE LENGTH OF THE PRIMARY BUFFER
	JUMPE	T2,ZAPPCB	;IF NO PRIMARY BUFFER, RETURN PCB TO FREE-CORE
	PUSH	P,T2		;HANG ON TO DATA BUFFER LENGTH

IFN PARANOID&P$COR,<		;NOW WE CAN CHECK TO SEE IF SIZE IS REASONABLE
	MOVE	T1,PCBTAG(U)	;A PCB UNIQUENESS
	CAMN	T1,['PCBTAG']	;DOES PCB STILL LOOK LIKE A PCB?
	TRNE	T2,MSGAGW##-1	;IS PCB'S DATA BUFFER LENGTH NICELY GRANULAR?
	STOPCD	.,STOP,ANFPCR,	;++ PCB TAG WORD TRASHED WHEN REMOVING
	LSH	T2,-MSGAGN##	;CONVERT LENGTH TO GRANULARITY INDEX
	CAILE	T2,MSGALN##	;IS PCB'S DATA BUFFER A REASONABLE LENGTH?
	STOPCD	.,STOP,ANFMBL,	;++ BUFFER LENGTH WRONG
	MOVE	T1,PCBADR(U)	;ADDRESS OF DATA BUFFER
	HLRZ	T2,-1(T1)	;GET THE TOP-END CHECK WORD
	ADD	T1,0(P)		;ADVANCE BUFFER ADDRESS AND
	MOVE	T1,0(T1)	;GET THE BOTTOM-END CHECK WORD
	CAMN	T1,['NETMEM']	;IS BOTTOM-END CHECK WORD OK?
	CAIE	T2,'NET'	;IS TOP-END CHECK WORD OK?
	STOPCD	.,STOP,ANFPCM,	;++ PCB MESSAGE CHECK WORDS TRASHED
>

	PUSHJ	P,CLNPCB	;REFRESH THE PCB
	POP	P,T2		;RETRIEVE DATA BUFFER LENGTH
	LSH	T2,-MSGAGN##	;REDUCE TO ALLOCATION GRANULARITY
	NETOFF			;DISABLE INTERRUPTS WHILE HACKING FREE-LISTS
	SKIPG	NTFREC##(T2)	;SEE IF FREE-LIST IS EMPTY
	JRST	[HRRZM U,NTFREF##(T2) ;IF EMPTY, THEN THIS PCB GOES ON FIRST
		 HRRZM U,NTFREL##(T2) ;  AS WELL AS LAST
		 JRST RMVPC1]	;GO TO COMMON CODE TO CLEAN UP
	HRRZ	T1,NTFREL##(T2)	;GET THE ADDRESS OF THE "LAST" PCB IN THE LIST
	HRRM	U,PCBBLK(T1)	;  PUT THIS PCB JUST AFTER IT IN THE FREE LIST
	HRRM	U,NTFREL##(T2)	;  AND ALSO MAKE THIS THE NEW "LAST" PCB
RMVPC1:	AOS	NTFREC##(T2)	;COUNT THE NEW FREE PCB
	SETZ	U,		;CLEAR "U" TO INDICATE PCB HAS BEEN FREED
RMVPC7:	NETON			;RE-ENABLE INTERRUPTS
	POPJ	P,		;RETURN WITH PCB NOW ON PROPER FREE LIST
;THIS ROUTINE DOES NOT 'RECYCLE' THE PCB.  PCB IS RETURNED TO FREE-CORE

ZAPPCB:	HLLZS	PCBTAG(U)	;CLEAR THE 'UNIQUE' TAG
	SKIPE	PCBCT2(U)	;GET THE DATA LENGTH
	STOPCD	.,STOP,ANFOBS,	;++ OBSOLETE FEATURE
ZAPPC2:	SKIPG	T1,PCBALN(U)	;GET THE DATA BUFFER LENGTH
	JRST	ZAPPC3		;NONE
	MOVE	T2,PCBADR(U)	;GET THE INPUT POINTER
	PUSHJ	P,GIVZWD	;REMOVE THE SPACE
ZAPPC3:	MOVEI	T2,(U)		;COPY THE ADDRESS
	MOVEI	T1,PCBLEN	;GET THE LENGTH
	PUSHJ	P,GIVZWD	;RETURN THE SPACE
	SETZ	U,		;CLEAR U TO FLAG PCB AS REMOVED
	POPJ	P,



;CLNPCB	REFRESH A PCB.
;CALL	MOVE	U,PCB
;	PUSHJ	P,CLNPCB
;RETURN	CPOPJ			;ALWAYS
;CLOBBERS T1,T2
CLNPCB:	SETZM	PCBBLK(U)	;CLEAR FLAGS AND LINK FIELDS
	SETZM	PCBFEK(U)	;CLEAR FEK AND NODE NUMBER
	SETZM	PCBCTR(U)	;CLEAR VALID DATA BYTE COUNT
	SETZM	PCBCT2(U)	;CLEAR SECONDARY DATA BYTE COUNT
	SETZM	PCBAL2(U)	;AND THE "ANFSBA" STOPCD GENERATOR
	MOVE	T1,[SIXBIT /PCBTAG/] ;GET AND SET THE "TAG"
	MOVEM	T1,PCBTAG(U)	;  WORD SO WE KNOW IT'S A PCB
IFN FTKL10,<PUSHJ P,PCBMRK>	;SET UP THE CACHE INFORMATION IN THE PCB
	POPJ	P,		;ALL DONE
;"EMERGENCY" PCB MANAGEMENT.

                           Comment @

    One of the most persistant problems with NETSER has been that
    it uses a great deal of free core, and when free core runs low,
    NETSER tends to crash.  What follows it the latest in a
    continuing series of HACKS to ameloiate the effects of finite
    free-core.  The routines involved are:

	PCBECK	This routine checks to make sure that there is
		sufficient "emergency" free core available, and
		that there are message numbers available.  If it
		skips, one may call PCBEGT with out fear.

	PCBEGT	This routine gets an "emergency" numbered pcb.
		If one is not available, it STOPCD's.

    Unfortunatly, NETSER is not organized in a fashion that makes
    using these routines easy.  The problem is that very low-level
    subroutines are the ones that allocate messages (pcbs).
    Figuring out if it is an "emergency" or not is beyond their
    limited abilities.  What is needed is a way for "middle" level
    routines to inform lower level ones that there is an emergency.
    This is currently accomplished by the gross hack of using
    a global flag.  The mechanisims for this are:

	NTUEFC	If this location is non-zero, then the next call
		to MKNPCB will not fail.  It will attempt to
		use "normal" free-core, but if none is available,
		it will use emergency free core.

	EMRGCY	This is a macro that currently SETOM's ntuefc.
		It should be used just before calls to routine
		such as NCSHDR when it is imperative that they
		NOT fail.

                         End Comment @
;ROUTINES TO MANAGE EMERGENCY STORAGE

;PCBECK	ROUTINE TO SEE IF EMERGENCY MESSAGES ARE AVAILABLE.
;CALL	W := NDB POINTER
;RETURN	CPOPJ			;EITHER NO FREE MESSAGE NUMBERS, OR NO CORE
;	CPOPJ1			;THERE ARE FREE MESSAGE NUMBERS, AND
;				;  EMERGENCY STORAGE.  IN PARTICULAR, A
;				;  CALL TO PCBEGT WILL SUCCEED.

PCBECK:	MOVE	T1,NDBMOM(W)	;GET THE COUNT OF FREE MESSAGE NUMBERS,
	CAIG	T1,3		;  AND MAKE SURE IT IS REASONABLY LARGE
	POPJ	P,		;IF NOT ENOUGH FREE NUMBERS GIVE ERROR RETURN
	MOVE	T1,PCBECT	;GET THE COUNT OF "EMERGENCY" PCB'S
	CAIL	T1,2		;  AND IF THERE ARE 2 OR MORE, THEN
	RETSKP			;GIVE "SUCCESS" RETURN

	PUSH	P,U		;"U" IS IN USE BY NETSCN AT THIS POINT
	MOVEI	T1,MSGMAW##	;IF NOT ENOUGH "EMERGENCY" PCB'S, TRY
	PUSHJ	P,MKUPCB	;  TO ALLOCATE THE LARGEST POSSIBLE.
	  PJRST	UPOPJ##		;IF NO MORE FREE-CORE, GIVE ERROR RETURN
	NETOFF			;IF WE GOT ONE, TURN OFF INTERRUPTS
	HRRZ	T1,PCBELS	;  AND INSERT THE NEW ONE
	HRRM	T1,PCBBLK(U)	;  ON THE FRONT OF THE "EMERGENCY"
	HRRM	U,PCBELS	;  PCB FREE LIST (PCBELS).
	AOS	PCBECT		;COUNT THE NEW "EMEGENCY" PCB
	NETON			;RE-ENABLE INTERRUPT, AND
	POP	P,U		;GET NETSCN'S "U" BACK
	JRST	PCBECK		;GO SEE IF WE HAVE ENOUGH NOW.

;PCBEGT	ROUTINE TO GET AN "EMERGENCY" NUMBERED PCB
;CALL	W := NDB POINTER	;(WE NEED TO FIX NDBMOM)
;RETURN	CPOPJ1			;WITH U := "EMERGENCY" PCB
;				;  (IF NONE AVAILABLE, WE STOPCD)

PCBEGT:	NETOFF			;DISABLE INTERRUPTS WHILE HACKING LISTS
	SOSL	NDBMOM(W)	;DECREMENT THE FREE MESSAGE NUMBER COUNTER
	SOSGE	PCBECT		;DECREMENT THE COUNT OF "EMERGENCY" PCB'S
	STOPCD	.,STOP,ANFNFP,	;++ NO FREE PCBS OR NO FREE MESSAGES
	HRRZ	U,PCBELS	;GET THE FIRST FREE PCB,
	HRRZ	T1,PCBBLK(U)	;GET THE ADDRESS OF THE SECOND (IF ANY)
	HRRM	T1,PCBELS	;MAKE THE "SECOND" THE NEW "FIRST"
	PJRST	NTONP1		;RE-ENABLE INTERRUPTS AND SKIP RETURN.
;ONCE/SECOND CODE FOR PCB MANAGEMENT.
;THIS CODE FREES EXCESS PCBS FROM THE FREE LISTS ONCE/SECOND.
;AT LEAST ONE FREE PCB IS ALWAYS KEPT IN ORDER TO MINIMIZE THE
;CONTENTION ON GENERAL MONITOR FREE CORE. IN PARTICULAR, FOR 
;THE MSGMAX-SIZED PCB LIST TWO FREE PCBS ARE KEPT (SINCE ALL
;INPUT OPERATIONS MUST ALMOST-BY-DEFINITION USE THIS SIZE).

PCBSEC:	MOVE	T1,TIME##	;GET SYSTEM TIME (IN JIFFIES)
	IDIV	T1,TICSEC##	;GET SYSTEM TIME (IN SECONDS)
	IDIVI	T1,MSGALN##+1	;TAKE MOD ALLOCATION TABLE MAXIMA
	NETOFF			;MAKE SURE NOT INTERRUPTED
	SKIPG	T1,NTFREC##(T2)	;GOT A FREE PCB HERE?
	PJRST	RMVPC7		;NO, JUST RE-ENABLE AND GO AWAY
	CAIL	T2,MSGALN##	;IS THIS THE MSGMAX-SIZED PCB LIST?
	SUBI	T1,1		;YES, ALLOW FOR ONE MORE
	SOJLE	T1,RMVPC7	;ALWAYS KEEP THE "LAST" ONE
	HRRZ	U,NTFREF##(T2)	;GET THE ADDRESS OF THE "FIRST" FREE ONE
	HRRZ	T1,PCBBLK(U)	;GET THE ADDRESS OF THE "NEXT" FREE ONE
	HRRM	T1,NTFREF##(T2)	;MAKE THE "NEXT" THE "FIRST"
	SOSLE	NTFREC##(T2)	;COUNT DOWN THE PCB COUNTER
	JRST	PCBSE3		;ALL DONE
	SKIPE	NTFREF##(T2)	;THAT WAS THE LAST, BETTER NOT BE ANY MORE
	STOPCD	.,STOP,ANFFCW,	;++ FREE PCB COUNT WRONG
	SETZM	NTFREL##(T2)	;CLEAR POINTER TO LAST
PCBSE3:	NETON			;NO LONGER PLAYING WITH FREE LISTS
	HLLZS	PCBBLK(U)	;NO STRAY POINTERS
	PJRST	ZAPPCB		;RETURN THE PCB TO FREE CORE



;RMVALP	THIS ROUTINE FREES AN ENTIRE CHAIN OF PCB'S
;CALL	U := FIRST ONE TO GO
;RETURN CPOPJ
RMVALP:	PUSHJ	P,SAVE1		;SAVE P1 FOR THE DURATION
RMVAL1:	JUMPE	U,CPOPJ		;ALL DONE IF NO PCB
	HRRZ	P1,PCBBLK(U)	;GET AND KEEP POINTER TO NEXT PCB
	S0PSHJ	RMVPCB		;FREE THIS PCB
	MOVEI	U,(P1)		;CURRENT PCB := NEXT PCB
	PJRST	RMVAL1		;TAIL-RECURSE
SUBTTL NDB -- ROUTINES TO MANAGE NODE DATA BLOCKS

;SUBROUTINE MAKNDB - BUILD A NODE DATA BLOCK (NDB)
;CALL	MOVEI	J,FEK
;	MOVEI	T1,NNM
;	PUSHJ	P,MAKNDB
;RETURN	CPOPJ			;NO CORE
;	CPOPJ1			;W=NDB

MAKNDB:				;ENTRY
	NTDBUG			;VERIFY THE INTERLOCK
	PUSH	P,T1		;SAVE THE NODE NUMBER
	MOVEI	T2,NDBLEN	;LENGTH
	PUSHJ	P,GETZWD	;ALLOCATE
	  PJRST	TPOPJ##		;NO SPACE
	MOVEI	W,(T1)		;COPY THE POINTER
	HRRM	J,NDBFEK(W)	;STORE THE FEK POINTER
	HRRZ	T1,NETNDB##	;GET THE START OF THE NDB CHAIN
	NETOFF			;NEED PROTECTION SINCE 'STBOPR' AND
				; OTHER ROUTINES SCAN THE NDB LIST WITH
				; OUT FIRST GETTING THE 'NT' INTERLOCK
				; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB")
	HRRM	T1,NDBNNM(W)	;LINK THIS NDB TO THE START
	HRRM	W,NETNDB##	;LINK
	POP	P,T1		;GET THE NODE NUMBER
	CAIG	T1,NODMAX	;RANGE CHECK
	HRRM	W,.GTNDA##(T1)	;STORE ADDRESS IF VALID
	HRLM	T1,NDBNNM(W)	;STORE
	NETON			;ALL'S CLEAR
	PJRST	CPOPJ1##	;EXIT
SUBTTL SUBROUTINE TO RETURN STORAGE DATA BLOCK TO THE MONITOR
;SUBROUTINE RMVNDB - REMOVE A NODE DATA BLOCK NDB
;CALL	MOVEI	W,NDB
;	PUSHJ	P,RMVNDB
;RETURN	CPOPJ

RMVNDB:
	NTDBUG			;VERIFY THE INTERLOCK
	PUSHJ	P,SAVJW##	;SAVE J AND W
	PUSHJ	P,SAVE1##	;SAVE P1
	PUSHJ	P,NODEDN	;CALL "NODE DOWN"
	PUSHJ	P,CLNNDB	;CLEAN OUT STUFF HANGING ON THE NDB
	MOVEI	T1,NETNDB##	;GET THE START OF THE NDB CHAIN
	NETOFF			;TURN OFF THE PI'S SINCE STBOPR
				; DOESN'T ALWAYS GET THE 'NT' INTERLOCK
				; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB")
RMVNT2:	HRRZ	T2,NDBNNM(T1)	;GET THE NEXT POINTER
	JUMPE	T2,CPOPJ##	;EXIT IF END
	CAIN	T2,(W)		;IS THIS THE BLOCK
	JRST	RMVNT3		;YES,
	MOVEI	T1,(T2)		;NO, STEP ALONG
	JRST	RMVNT2		;TRY AGAIN
RMVNT3:	HRRZ	T3,NDBNNM(W)	;GET THE FORWARD LINK
	HRRM	T3,NDBNNM(T1)	;STORE BACKWARD
	HLRZ	T1,NDBNNM(W)	;GET NODE REMOVED
	CAIG	T1,NODMAX	;RANGE CHECK
	HLLZS	.GTNDA##(T1)	;AND CLEAR POINTER
	NETON			;ALL'S CLEAR
	MOVEI	T1,NDBLEN	;GET THE LENGTH
	PJRST	GIVZWD		;RELEASE THE BLOCK
;CLNNDB	CLEAN OUT AN NDB.  THIS ROUTINE ZEROS ALL MESSAGE COUNTS
;	AND OTHER APPROPRIATE FIELDS AND FREES ALL DATA HANGING OFF OF
;	AN NDB. (CALL WHEN SENDING OR RECEIVING STARTS)
;CALL	W := NDB
;RETURN	CPOPJ

CLNNDB:
	NTDBUG			;VERIFY THE INTERLOCK
	PUSHJ	P,SAVE4##	;THIS ROUTINE CLOBBERS EVERYTHING
	PUSHJ	P,SAVJW##	;THESE TOO
	PUSH	P,U		;AND EVEN THIS

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE WE DON'T DELETE OUR NDB

;HERE TO CHECK FOR STATION CONTROL RESOURCE
	PUSH	P,W		;SAVE NDB POINTER
	HRRZ	J,NDBICT(W)	;GET MESSAGE ADDR
	SKIPE	J		;IS THERE ONE ?
	PUSHJ	P,GIVSTC	;IF SO, FREE THEM
	SKIPE	T1,NDBSTC(W)	;ANYONE USING STATION CONTROL
	PUSHJ	P,EWAKE##	;IF SO, WAKE HIM UP.
	POP	P,W		;GET NDB POINTER BACK

;HERE TO CALL ALL THE NETWORK DEVICES AND INFORM THEM THAT THIS NODE HAS
; GONE AWAY.  ALL THE SERVICE ROUTINES WILL BE CALLED ON THEIR "NDPNWD"
; ENTRY POINT.  THE INTERFACE FOR A NDPNWD ENTRY SHOULD BEHAVE AS FOLLOWS.
;AT ENTRY
;	P1 := NODE NUMBER OF CRASHED NODE.
;	P2 := LAT INDEX ('SLA')
;	F  := LAT ENTRY FOR THIS DEVICE (XWD FLAGS,DDB/LDB)
;RETURN	CPOPJ	;ALWAYS.

CLNND0:	MOVSI	P2,-LATLEN##	;MAKE AN AOBJN POINTER FOR ACCESSING THE LAT.
	HLRZ	P1,NDBNNM(W)	;GET THE NUMBER OF THE CRASHED NODE.
	PUSH	P,W		;WE MAY CLOBBER "W".  PRESERVE IT FOR NOW.
	JRST	CLNND2		;SKIP OVER THE ZERO'TH ENTRY (NETDDB)

CLNND1:	SKIPN	T2,NETLAT##(P2)	;GET THE NEXT LAT ENTRY
	JRST	CLNND2		;THIS ENTRY NOT IN USE
	LDB	F,LATPP2	;EXTRACT DDB/LDB ADDRESS (INDEX P2)
	TLNN	T2,LAT.TY	;IS THIS A DDB OR LDB
	SKIPA	T1,DEVNET(F)	;DDB, GET NODE NUMBER
	LDB	T1,LDPRNF##	;LDB, GET NODE NUMBER
	ANDI	T1,777		;MASK OFF LH JUNK
	CAIE	T1,(P1)		;IS THIS DEVICE/TTY DOOMED?
	JRST	CLNND2		;NO, LEAVE IT ALONE THEN
	MOVEI	T1,NDPNWD	;GET THE "NODE WENT DOWN" FCN CODE
	PUSHJ	P,ICMNDP	;AND CALL THE DISPATCH ROUTINE
CLNND2:	AOBJN	P2,CLNND1	;LOOP OVER ALL LAT ENTRYS
	POP	P,W		;RECOVER OUR NDB POINTER

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;HERE TO PURGE THE NDB NAME POINTERS

	MOVEI	T1,^D8		;FOUR WORDS TO REMOVE
	HLRZ	T2,NDBSID(W)	;GE THE SOFTWARE ID POINTER
	SKIPE	T2		;ASSIGNED
	PUSHJ	P,GIVZWD	;RETURN THE SPACE
	MOVEI	T1,^D8		;FOUR WORDS TO RETURN
	HRRZ	T2,NDBSID(W)	;GET THE ADDRESS
	SKIPE	T2		;NOT ASSIGNED
	PUSHJ	P,GIVZWD	;REMOVE THE SPACE
;HERE TO REMOVE THE QUEUES FOR THE NODES
	HRRZ	U,NDBQUE(W)	;GET AND FREE THE
	PUSHJ	P,RMVALP	;    THE RIGHT HAND
	HLRZ	U,NDBQUE(W)	;GET AND FREE THE
	PUSHJ	P,RMVALP	;    THE LEFT HAND

	HRLZI	T1,(W)		;BUILD A BLT POINTER
	HRRI	T1,1(W)		;      TO ZERO THE NDB
	NETOFF			;COVER OURSELVES WHILE NDB IS SCREWED UP
				; (SEE FOOTNOTE AFTER ROUTINE "SRCNDB")
	MOVE	T2,NDBNNM(W)	;SAVE "XWD NODE#,LINK" FOR A WHILE
	HRRZ	T3,NDBFEK(W)	;SAVE THE ADDR OF THE FEK
	SETZM	(W)		;ZAP THE FIRST, AND THEN
	BLT	T1,NDBLEN-1(W)	;    THE REST OF THE NDB.
	MOVEM	T3,NDBFEK(W)	;RESTORE THE FEK ADDRESS AND THE
	MOVEM	T2,NDBNNM(W)	;    NODE#,,LINK WORD
	NETON			;ALL IS CONSISTENT NOW
	MOVEI	T1,^D30		;ONLY ALLOW A MAX OF 30 OUTSTANDING (IE.
				; UN-ACKED) MESSAGES
	MOVEM	T1,NDBMOM(W)	;INITIALIZE MAX OUTSTANDING MESSAGES
	PJRST	UPOPJ		;RESTORE U AND RETURN
;ROUTINE TO HANDLE NODE ONLINE EVENTS
;CALL	MOVX	W,NDB-POINTER
;	PUSHJ	P,NODEUP
;RETURN	CPOPJ			;DOES "ONLINE" PROCESSING ONLY IF NODE WAS NOT
;				;  ONLINE BEFORE
NODEUP:	MOVSI	T1,NDB.UP	;GET THE NODE "ONLINE" FLAG
	TDNE	T1,NDBFLG(W)	;SEE IF WE ARE ALREADY ONLINE
	POPJ	P,		;  IF ONLINE ALREADY, RETURN
	IORM	T1,NDBFLG(W)	;MARK THE NODE AS ONLINE
	S0PSHJ	ONLNDB		;PRINT THE ONLINE MESSAGE
	PUSHJ	P,PSINTC##	;SIGNAL THE ONLINE EVENT

	MOVE	T1,[XWD .CSCNO,.ERCSC] ;GET CODE TO TELL DAEMON NODE ONLINE
	PJRST	NODEAM		;  AND GO TELL DAEMON



;ROUTINE TO HANDLE NODE OFFLINE EVENTS
;CALL	MOVX	W,NDBPOINTER
;	PUSHJ	P,NODEDN
;RETURN	CPOPJ			;DOES "OFFLINE" PROCESSING ONLY IF THE
;				;NODE WAS ONLINE.
NODEDN:	MOVSI	T1,NDB.UP	;GET THE "ONLINE" BIT
	TDNN	T1,NDBFLG(W)	;IF WE'RE NOT ONLINE NOW,
	POPJ	P,		;  THEN DON'T DO ANY OFFLINE STUFF
	ANDCAM	T1,NDBFLG(W)	;CLEAR THE ONLINE BIT
	S0PSHJ	OFLNDB		;TELL THE OPERATOR IT WENT AWAY
	PUSHJ	P,PSINTC##	;SIGNAL THE OFFLINE EVENT

	MOVE	T1,[XWD .CSCNF,.ERCSC] ;GET NODE OFF-LINE DAEMON CODE
;	PJRST	NODEAM		;  AND GO TELL DAEMON


;ROUTINE TO CALL DAEMON FOR NODE ON/OFF LINE EVENTS
;CALL	MOVX	T1,"DAEMON CODE"
;	PUSHJ	P,NODEAM
;RETURN	CPOPJ			;ALWAYS
NODEAM:	PUSHJ	P,SAVJW##	;"J" GETS MASHED BY DAEEIM
	PUSH	P,U		;DON'T WANT TO CLOBBER INCTSK'S PCB
	SETZ	F,		;CLEAR "F" SO AS NOT TO CONFUSE DAEMON
	HLRZ	U,NDBNNM(W)	;GET THE NODE NUMBER IN U
	PUSHJ	P,DAEEIM##	;CALL THE NON-BLOCKING FORM OF DAEMON
	PJRST	UPOPJ##		;CLEAN UP AND RETURN
;SRCNDB	ROUTINE TO FIND A NDB GIVEN A NODE NAME OR NUMBER
;CALL	MOVE	T1,NNM		;NODE NUMBER
;OR	MOVE	T1,[SIXBIT /NODE NAME/]	;SIXBIT NODE NAME
;	PUSHJ	P,SRCNDB
;RETURN	CPOPJ			;NOT FOUND
;	CPOPJ1			;FOUND W=NDB POINTER
SRCNDB::NTDBUG			;VERIFY THE INTERLOCK
SRCND0::TLNE	T1,-1		;IF IT'S A NAME,
	JRST	SRCND3		; GO USE A DIFFERENT LOOP
SRCND1:	SKIPN	T1		;IF NODE NUMBER IS ZERO,
	MOVE	T1,JBTLOC##+0	;USE THE LOCAL NODE
	CAILE	T1,NODMAX	;RANGE CHECK
	POPJ	P,		;NOT IN RANGE
	HRRZ	W,.GTNDA##(T1)	;GET NDB ADDRESS
	CAIE	W,0		;ONLINE
	AOS	(P)		;YES, SKIP
	POPJ	P,		;AND RETURN

SRCND3:	PUSHJ	P,SRCND4	;FIRST TRY IT AS A "NAME"
	  CAIA			; AND IF THAT SUCCEEDS,
	JRST	CPOPJ1##	; THEN WE'RE DONE.
	PUSH	P,T1		;OTHERWISE, TRY TO CONVERT
	PUSHJ	P,CVTOCT##	; A SIXBIT NUMBER
	  JRST	TPOPJ##		; IF THAT FAILS, GIVE UP
	PUSHJ	P,SRCND1	;TRY THE NUMBER
	  JRST	TPOPJ##		; FAILED
	JRST	TPOPJ1##	; SUCCEEDED.

SRCND4:	MOVEI	W,NETNDB	;GET THE HEAD OF THE CHAIN
SRCND5:	HLRZ	T3,NDBSNM(W)	;GET THE ADDRESS OF THE NAME
	JUMPE	T3,SRCND6	;IF NO NAME, NO ADDRESS OF NAME
	CAMN	T1,(T3)		; SEE IF THEY MATCH
	JRST	CPOPJ1##	;RETURN SUCCESS IF THEY DO
SRCND6:	HRRZ	W,NDBNNM(W)	;OTHERWISE GO TO THE NEXT
	JUMPN	W,SRCND5	; AND CHECK THAT
	POPJ	P,		;ERROR RETURN IF NO MORE

;*** FOOTNOTE ***

COMMENT \

     The entry SRCND0 should only be called under cover of a "NETOFF"
macro.  One of the fundamental goals in the 7.01 NETSER was to eliminate
crashes caused by "stale" pointers to NDB's that had been destroyed.
In light of this, the only time that a pointer to an NDB is valid is
under the NETSER interlock.  Unfortunatly, ocasionally the need arises
to look at some field in some NDB at a time when it is not convenient
to request the NETSER interlock.  Examples of this are translating
a node name to a node number, and finding the OPR ldb of a particular
remote station.  Both of these routines (STBSCA and STBOPR) are called
from clock and/or interrupt levels, and hence cannot do a NDTDBJ.
Routines like these MUST NETOFF for the period that they possess a
valid NDB address.
\
SUBTTL NDT -- ROUTINES TO SEARCH THE NDT

;SUBROUTINE SRCNDT - SEARCH THE NETWORK DEVICE TABLE
;CALL	HRRZ	T1,(SIXBIT /GENERIC NETWORK DEVICE NAME/)
;	MOVEI	P1,NDB
;	PUSHJ	P,SRCNDT
;RETURN	CPOPJ			;DEVICE NOT FOUND
;	CPOPJ1			;DEVICE FOUND W(RT)=THE NDT POINTER

SRCNDT::CAIE	T1,'MCR'	;THE "MONITOR COMMAND ROUTINE" ISN'T
	CAIN	T1,'MC '	; A REAL DEVICE (TOO BAD, MIGHT BE NEAT)
	POPJ	P,		;REJECT MCR:
	NTDBUG			;VERIFY THE INTERLOCK
	PUSH	P,T4		;SAVE T4
	MOVE	T4,NDTXWD##	;GET THE SEARCH POINTER
SRCNDC:	SKIPN	W,(T4)		;GET AN NDT
	AOBJN	T4,.-1		;KEEP SEARCHING
	JUMPGE	T4,SRCNDX	;DONE?
	HLRZ	T2,NDTNAM(W)	;GET THE -10 DEVICE NAME
	HRRZ	T3,NDTNAM(W)	;GET THE -11 DEVICE NAME
	CAIE	T1,(T2)		;IS IT A -10 DEVICE
	CAIN	T1,(T3)		;OR A -11 DEVICE
	JRST	SRCNDD		;CHECK THE CONFIGURATION
	AOBJN	T4,SRCNDC	;AND TRY AGAIN
SRCNDX:	POP	P,T4		;RESTORE T4
	POPJ	P,		;DEVICE NOT FOUND
SRCNDD:	MOVE	T4,W		;COPY NDT POINTER
	EXCH	T4,(P)		;SAVE NDT AND RESTORE T4
	LDB	T1,NDTOBJ	;GET THE DEVICE TYPE
	MOVEI	W,(P1)		;COPY THE NDB POINTER
	LDB	T1,NETCNF##(T1)	;GET THE CONFIGURATION COUNT
	JUMPE	T1,WPOPJ##	;THAT NODE DOES NOT HAVE ANY
	PJRST	WPOPJ1##	;DEVICE IS OK
SUBTTL LAT -- ROUTINES FOR MANIPULATING THE LINK ADDRESS TABLE

;GETSLA ROUTINE TO GET A SOURCE LINK ADDRESS FROM THE LAT TABLE
;CALL:	MOVE	T1,<ADDR OF DDB/LDB TO BE STORED IN NETLAT>
;	MOVEI	T2,LAT.XX	;LAT STATE
;	PUSHJ	P,GETSLA
;RETURN	CPOPJ			;NONE AVAILABLE
;	CPOPJ1			;T1=SLA

GETSLA::NTDBUG			;VERIFY THE INTERLOCK
	PUSH	P,T1		;SAVE LAT ADDRESS AND FLAGS

IFN PARANOID&P$LAT,<
	TLZ	T1,777700	;MASK OUT ALL BUT ADDRESS
	PUSH	P,T2		;SAVE DESIRED INITIAL STATE
	CAIL	T2,0		;RANGE
	CAILE	T2,LAT.MX	; CHECK
	PUSHJ	P,NTDSTP	;++ INITIAL LAT STATE ILLEGAL
	MOVSI	T2,-LATLEN##	;PROTOTYPE LAT INDEXER
GETSL1:	LDB	T3,LATPT2	;GET THIS SLA'S DDB/LDB ADDRESS
	CAMN	T3,T1		;DOES THIS DDB/LDB ALREADY HAVE A LAT SLOT?
	STOPCD	.,STOP,ANFWLA,	;++ WRONG LAT ASSIGNED
	AOBJN	T2,GETSL1	;LOOP OVER ALL LAT ENTRIES
	POP	P,T2		;RETRIEVE INITIAL LAT STATE
> ;IFN PARANOID&P$LAT

	MOVSI	T1,-LATLEN##	;GET THE LENGTH FOR THE AOBJN
	SKIPE	NETLAT##(T1)	;LOOK FOR AN AVAILABLE SLOT
	AOBJN	T1,.-1		;CONTINUE STEPPING
	JUMPGE	T1,TPOPJ##	;NONE AVAILABLE
	POP	P,NETLAT(T1)	;MARK THE SLOT AS IN USE
	DPB	T2,LATSTA	;SET INITIAL STATE IN LAT ENTRY FOR THIS SLA
	JRST	CPOPJ1		;T1=SLA


;GIVSLA	ROUTINE TO RETURN A LAT ADDRESS
;CALL	MOVEI	F,DDB		;THIS DOESN'T WORK FOR TERMINAL
;	PUSHJ	P,GIVSLA	;RETURN THE LAT ENTRY, ZERO NETSLA
;RETURN	CPOPJ			;UNLESS WE STOPCD.
;
;NOTE!	THIS ROUTINE PRESERVES ALL REGISTERS (ESPECIALLY T1)

GIVSLA::NTDBUG			;VERIFY THE NETSER INTERLOCK
	PUSHJ	P,SAVE2##	;SAVE P1, P2 SO WE DON'T CLOBBER THE T'S
	LDB	P2,NETSLA	;GET THE CURRENT SLA FOR THIS DEVICE
	HRRZ	P1,NETLAT##(P2)	;GET [MOST OF] THE LAT ENTRY.
				;IT'S OK TO ONLY LOOK AT 18 BITS HERE . . .
	CAIE	P1,(F)		;MAKE SURE EVERYTHING IS CONSISTENT.
	STOPCD	.,STOP,ANFLAT,	;++ DDB AND LAT DON'T AGREE
	SETZB	P1,NETLAT##(P2)	;FREE THE LAT
	DPB	P1,NETSLA	;CLEAR THE SLA IN THE DDB
	DPB	P1,NETDLA	;  GET THE DLA SINCE NOT CONNECTED.
	POPJ	P,		;ALL DONE
SUBTTL NPD -- ROUTINES TO MANIPULATE NETWORK CONNECT DISCRIPTORS


;GETNPD	ROUTINE TO ALLOCATE A NETWORK CONNECTION DESCRIPTOR BLOCK
;CALL	MOVEI	T1,LENGTH	;LENGTH OF DESIRED NPD (INCLUDES NPDBLK)
;	PUSHJ	P,GETNPD
;RETURN	CPOPJ			;ERROR RETURN.  NO CORE
;	CPOPJ1			;WITH J := A POINTER TO THE NPD
;
GETNPD::			;HERE TO ALLOCATE AN NPD
	MOVEI	T2,(T1)		;COPY THE LENGTH FOR GETZWD
	PUSH	P,T1		;SAVE IT SO WE CAN PUT IT IN NPDBLK
	PUSHJ	P,GETZWD	;ALLOCATE THE STORAGE
	  JRST	TPOPJ##		;NO STORAGE. ERROR RETURN
	POP	P,T2		;GET THE LENGTH BACK
	MOVEI	J,(T1)		;COPY THE NPD POINTER
	HRLZM	T2,NPDBLK(J)	;SAVE THE LENGTH
	JRST	CPOPJ1##	;GOOD RETURN


;GIVNPD	ROUTINE TO RETURN A NETWORK CONNECTION DESCRIPTOR BLOCK
;CALL	MOVEI	J,NPD ADDRESS
;	PUSHJ	P,GIVNPD
;RETURN	CPOPJ			;ALWAYS
;
GIVNPD::HLRZ	T1,NPDBLK(J)	;GET THE ALLOCATED LENGTH OF THE NPD
	MOVEI	T2,(J)		;GET THE ADDRESS OF THE STORAGE
	PUSHJ	P,GIVZWD	;FREE THE NPD
	SETZ	J,		;INDICATE THAT IT IS GONE
	POPJ	P,		;GIVE A GOOD RETURN


;GV2NPD	RETURN BOTH NPD'S OF A TASK
;CALL	F := DDB
;RETURN	CPOPJ			;ALWAYS

GV2NPD::PUSH	P,J		;PRESERVE "J"
	HRRZ	J,DEVNPD(F)	;GET THE ADDRESS OF THE FIRST NPD
	SKIPE	J		;IF IT'S NOT NULL,
 	PUSHJ	P,GIVNPD	;RETURN IT
	HLRZ	J,DEVNPD(F)	;GET THE ADDRESS OF THE REMOTE NPD
	SKIPE	J		;IF IT'S NOT NULL,
	PUSHJ	P,GIVNPD	;RETURN IT TOO
	SETZM	DEVNPD(F)	;CLEAR THE NPD POINTER
	JRST	JPOPJ##		;RESTORE "J" AND RETURN
SUBTTL	STC -- ROUTINES TO MANIPULATE STATION CONTROL MESSAGE BLOCKS

;GETSTC	ROUTINE TO ALLOCATE A STC (STATION CONTROL MESSAGE) BLOCK
;CALL	T1 := NUMBER OF BYTES IN THE STC MESSAGE
;	W  := POINTER TO THE NDB WE'RE SENDING TO/RECEIVING FROM
;RETURN	CPOPJ			;NO CORE
;	CPOPJ1			;STC ALLOCATED.
;				;  J := POINTER TO STC BLOCK

GETSTC::PUSH	P,T1		;SAVE THE LENGTH (IN BYTES)
	MOVEI	T2,3+<4*STCDAT>(T1)	;SET T2 := LENGTH(BYTES) OF ENTIRE STC
	LSH	T2,-2		;CHANGE WORDS TO BYTES (ROUNDED UP)
	PUSHJ	P,GETZWD	;GET A BLOCK OF FREE-CORE TO HOLD STC MSG
	  JRST	TPOPJ##		;  IF NO MORE MEMORY, GIVE ERROR RETURN
	MOVEI	J,(T1)		;MAKE "J" THE STC POINTER
	POP	P,T1		;GET BACK THE LENGTH OF THE MESSAGE (BYTES)
	HRLZM	T1,STCBLK(J)	;STORE LENGTH IN STC BLOCK
	RETSKP			;GIVE GOOD RETURN

;GIVSTC	ROUTINE TO FREE A STC (STATION CONTROL MESSAGE) BLOCK
;CALL	J := POINTER TO THE STC MESSAGE BLOCK
;RETURN	CPOPJ			;ALWAYS

GIVSTC::MOVEI	T2,(J)		;SET T2 := ADDRESS OF STC BLOCK
	HLRZ	T1,STCBLK(J)	;GET THE LENGTH (BYTES) OF THE STC DATA
	ADDI	T1,3+<STCDAT*4>	;GET THE LENGTH (BYTES) OF STC DATA + STC BLOCK
	LSH	T1,-2		;SET T1 := LENGTH OF BLOCK (WORDS ROUNDED UP)
	PJRST	GIVZWD		;RETURN THE STORAGE AND RETURN
;FEK2LN	ROUTINE TO CONVERT FROM FEK ADDRESS TO STATION CONTROL LINE NUMBER
;CALL	J := FEK
;RETURN	CPOPJ			;T1 := LINE NUMBER
;				;STOPS IF J /= A VALID FEK ADDRESS

FEK2LN:	MOVEI	T1,1		;START WITH LINE NUMBER 1
	MOVEI	T2,FEKFST	;GET THE ADDRESS OF THE FIRST FEK
FEK2L1:	CAIN	T2,(J)		;IF THIS IS THE FEK WE'RE LOOKING FOR
	POPJ	P,		;  RETURN WITH T1 := ITS LINE NUMBER
	HRRZ	T2,FEKBLK(T2)	;GET THE ADDRESS OF THE NEXT FEK
	SKIPN	T2		;IF THERE AREN'T ANY MORE FEKS, THEN
	STOPCD	.,STOP,ANFGFK,	;++ GARBAGE FEK POINTER
	AOJA	T1,FEK2L1	;INCREMENT THE LINE NUMBER AND CHECK NEXT FEK


;LN2FEK	ROUTINE TO CONVERT FROM STATION CONTROL LINE NUMBER TO FEK ADDRESS
;CALL	T1 := LINE NUMBER
;RETURN	CPOPJ			;NO FEK CORRISPONDS TO THAT LINE
;	CPOPJ1			;J := ADDRESS OF FEK.

LN2FEK:	MOVEI	J,FEKFST	;GET THE ADDRESS OF FEK FOR LINE #1
	SOJN	T1,[HRRZ J,FEKBLK(J) ;DECREMENT THE LINE NUMBER, GO TO NEXT FEK
		    JUMPE J,CPOPJ    ;IF DONE ALL FEKS, GIVE FAIL RETURN
		    JRST .]	;KEEP LOOKING
	RETSKP			;RETURN WITH J := ADDRESS OF FEK
REPEAT 0,<			;USELESS CODE (I THINK)

;STCREJ	ROUTINE TO SEND A STATION CONTROL REJECT <13> MESSAGE
;CALL	J := POINTER TO STC MESSAGE WITH
;		1ST BYTE := LINE #
;		LH(STCNNL) := NODE NUMBER TO SEND REJECT TO
;RETURN	CPOPJ	WITH STC FREED

STCREJ:	PUSH	P,W		;SAVE W (WE PUT DESTINATION NDB IN IT)
	PUSH	P,J		;SAVE THE STC POINTER
	HLRZ	T1,STCNNL(J)	;GET THE NODE NUMBER
	PUSHJ	P,SRCNDB	;  AND LOOK UP THE NDB
	  JRST	STCRE1		;IF NODE NOT THERE, JUST FREE THE STC MSG
	PUSHJ	P,NCMHDR	;WRITE THE MESSAGE HEADER
	  JRST	STCRE1		;IF NO CORE, JUST TOSS THE STC BLOCK
;TYP
	XMTI	NC.CTL		;THIS IS A STATION CONTROL MESSAGE
;LINE
	LDB	T1,[POINT 8,STCDAT(J),7] ;GET THE LINE NUMBER
	XMT	T1		;  AND SEND THAT
;CODE
	XMTI	STC.RJ		;SEND THE REJECT CODE
	JSP	T1,NETWRC	;SEND THE MESSAGE (AND CLOBBER "J")
STCRE1:	POP	P,J		;GET THE STC POINTER BACK
	PUSHJ	P,GIVSTC	;RETURN THE STC BLOCK
	JRST	WPOPJ##		;RESTORE "W" AND RETURN

>;END REPEAT 0
;STCSEC	ROUTINE CALLED ONCE/SEC TO DO STATION CONTROL TIMING FUNCTIONS
;CALL	W := NDB POINTER
;RETURN	CPOPJ			;ALWAYS
;ACTION	IF THE "NDBSTC" TIMER GOES OFF, THE JOB OWNING NDBSTC
;	IS WOKEN, AND NDBSTC(W) IS CLEARED
;	IF THE "NDBICT" TIMER GOES OFF, THE INCOMING MESSAGE IS FREED

STCSEC:	SKIPN	T1,NDBSTC(W)	;IS THE STATION CONTROL DEVICE IN USE?
	JRST	STCSE1		;  IF NOT IN USE, GO TIME OUT BOOT REQUESTS
	HLRZ	T1,T1		;GET THE TIMER FOR STATION CONTROL USERS
	SOSLE	T1		;DECREMENT IT, SKIP IF IT COUNT'S OUT
	JRST	[HRLM T1,NDBSTC(W)	;IF NOT COUNTED OUT YET, STORE IT -1
		 JRST STCSE1]	;  BACK AND GO TIME INCOMING MESSAGES
	HRRZ	T1,NDBSTC(W)	;GET THE JOB NUMBER OF THE USER THAT TIMED OUT
	SKIPE	T1		;  IF NO ONE WAS USING IT, DON'T WAKE ANYONE
	PUSHJ	P,EWAKE##	;WAKE THE USER NOW THAT HE'S TIMED OUT
	SETZM	NDBSTC(W)	;CLEAR STC TO SAY THAT NO-ONE IS USING IT.
STCSE1:	SKIPN	T1,NDBICT(W)	;SEE IF ANY BOOT MSGS ARE WAITING
	JRST	STCSE2		;IF NONE, DON'T LOOK ANY FARTHER
	HLRZ	T1,T1		;GET THE COUNTER FOR INCOMING STC MSGS
	SOSLE	T1		;COUNT OFF ONE MORE SECOND. IF NOT TIMED OUT,
	JRST	[HRLM T1,NDBICT(W)	;THEN JUST STORE THE UPDATED COUNT
		 JRST STCSE2]	;BACK AND EXIT
	PUSH	P,J		;IF MESSAGE BEEN HERE TO LONG, FREE IT. SAVE J
	HRRZ	J,NDBICT(W)	;GET A POINTER TO THE STC BLOCK
	PUSHJ	P,GIVSTC	;RETURN THE STC BLOCK
	POP	P,J		;RESTORE "J"
	SETZM	NDBICT(W)	;INDICATE THAT NO STC MESSAGES ARE WAITING
STCSE2:	POPJ	P,		;RETURN.
SUBTTL FEK -- ROUTINES TO MANIPULATE FEKS

;CLNFEK	THIS ROUTINE CLEANS OUT A FEK.  IT RETURNS ALL
;	BUFFER SPACE ASSOCIATED WITH A FEK, AND GIVES THE
;	OUTPUT MESSAGES TO THE OUTPUT DONE ROUTINE IN NETSCN.
;	IN THIS WAY MESSAGES BEING ROUTED THROUGH THE NODE WILL
;	GET RE-ROUTED, AND MESSAGES TO THE NODE WILL BE DISCARDED.
;CALL	MOVE	J,FEK		;GET THE ADDRESS OF THE FEK
;	PUSHJ	P,CLNFEK	;CLEAN IT OUT
;RETURN	CPOPJ			;ALWAYS
;CLOBBERS S,U,T1,T2

NTFONC==:CLNFK2			;FOR NOW.

CLNFEK:				;HERE TO RECYCLE A FEK
IFN PARANOID&P$FEK,<		;DON'T REFURBISH NULL FEK
	HLLZ	S,FEKBLK(J)	;GET THE FEK'S FLAGS
	TLNE	S,FK.NUL	;SEE IF THIS IS A NULL FEK
	STOPCD	.,STOP,ANFNUL,	;++ NULL FEK BEING CLEANED
>
	SKIPE	U,FEKIAD(J)	;GET THE INPUT PCB,  IF THERE WAS ONE
	S0PSHJ	RMVPCB		;  THEN FREE THE INPUT BUFFER
	SETZM	FEKIAD(J)	;CLEAR THE INPUT PCB POINTER


	SKIPG	T2,FEKOCT(J)	;ARE THERE ANY OUTPUT PCBS?
	JRST	CLNFK2		;IF NOT, THEN DON'T FREE ANY.
	HRRZ	U,FEKOAD(J)	;GET THE FIRST OUTPUT PCB

	SKIPA	T1,U		;INITIALIZE WHAT WILL BECOME POINTER TO LAST
CLNFK1:	HRRZ	T1,PCBBLK(T1)	;ADVANCE THE POINTER TOWARDS THE LAST
	SKIPN	T1		;MAKE SURE WE'VE GOT A PCB
	STOPCD	.,STOP,ANFNPL,	;++ NO PCB'S ON LIST
	SOJG	T2,CLNFK1	;ADVANCE N-1 TIMES WHERE N = # OF PCB'S
				; (EXIT WITH T1 POINTING TO LAST PCB)
	HRRZ	T3,PCBBLK(T1)	;GET THE POINTER TO THE NTH+1 PCB
	SKIPE	T3		;  IF IT ISN'T ZERO, THEN
	STOPCD	.,STOP,ANFPCC,	;++ COUNT OF PCB'S ON LIST IS WRONG
	NETOFF			;PROTECTION WHEN DIDDLING QUEUES
	EXCH	U,NTQOUT	;PUT THIS LIST ON THE HEAD OF NTQOUT
	HRRM	U,PCBBLK(T1)	;AND SPLICE THE OLD ONE ON THE END
	NETON			;ALL CLEAR (WASN'T THAT EASY!)

CLNFK2:	MOVSI	S,FK.ONL!FK.NID!FK.STO!FK.STI!FK.OAC!FK.IAC!FK.CPD
	ANDCAB	S,FEKBLK(J)	;IN THE FEK STATUS WORD.
	SETZM	FEKOAD(J)	;CLEAR POINTER TO THE PCB'S
	SETZM	FEKODN(J)	;CLEAR OUTPUT DONE POINTER
	SETZM	FEKOCT(J)	;SET FEK IDLE
	SETZM	FEKHTM(J)	;CLEAR THE HUNG TIMER
	HLLZS	FEKNNM(J)	;CLEAR THE NODE NUMBER
	SETOM	FEKBSO(J)	;CLEAR OUTPUT BUSY
	SETOM	FEKBSI(J)	;CLEAR INPUT BUSY
	POPJ	P,		;AND WE ARE DONE
SUBTTL ROUTINE TO GENERATE THE NCL PROTOCOL HEADERS
;SUBROUTINE NCSHDR - CREATE<NCT><DNA><SNA><NCA><NCN>
;CALL	MOVEI	W,NDB
;	MOVEI	T1,NCT MESSAGE TYPE
;	PUSHJ	P,NCSHDR
;RETURN	CPOPJ			;NO CORE
;	CPOPJ1			;P3=COUNT, P2=CURRENT BYTE POINTER

NCSHDR:				;ENTRY
	NTDBUG			;VERIFY THE INTERLOCK
	PUSHJ	P,SAVE1##	;DON'T CLOBBER P1.
	MOVE	P1,T1		;COPY THE FLAGS
	MOVEI	T1,^D16		;MAKE A PCB WITH 16 WDS OF BUFFER
	TRNE	P1,7		;IS THIS A NUMBERED MESSAGE?
	JRST	[PUSHJ P,MKUPCB	; IF NOT, THEN GET AN 'UN-NUMBERED' PCB
		  POPJ P,	; (EXIT NCSHDR IF NOT AVAILABLE)
		 JRST NCSHD1]	;RETURN TO MAIN FLOW WITH U := PCB
	PUSHJ	P,MKNPCB	;IF IT'S A DATA MESSAGE, GET A NUMBERED PCB
	  POPJ	P,		; BUT GIVE ERROR RETURN IF NOT AVAILABLE.
NCSHD1:	MOVE	P2,PCBPTR(U)	;GET BYTE POINTER
	AOS	(P)		;WE WILL SUCCEED, SO GIVE GOOD RETURN
	MOVE	T1,P1		;GET THE NCT FLAGS BACK FOR NCSWHD
;	PJRST	NCSWHD		;FILL IN THE HEADER
;NCSWHD	ROUTINE TO WRITE THE HEADER OF AN NCS MESSAGE.
;CALL	MOVE	T1,NCT FLAGS
;	MOVE	U,POINTER TO VIRGIN PCB
;	PUSHJ	P,NCSWHD
;RETURN	CPOPJ			;ALWAYS
;				;  PCB HAS NCT, DNA, SNA, NCA, NCN FILLED IN,
;				;  PCBCTR IS UPDATED AND P3 IS ZERO,
;				;  P2 HAS THE POINTER TO THE "DLA" BYTE.
NCSWHD::
	SETZ	P3,		;CLEAR THE COUNT FIELD
	TRO	T1,NCT.RH!NCT.SQ ;INSERT THE ADDITIONAL FLAGS
	PUSH	P,T1		;SAVE THE FLAGS FOR LATER
;NCT
	PUSHJ	P,BI2EBI	;OUTPUT THE FLAGS
;DNA
	HLRZ	T1,NDBNNM(W)	;GET THE DESTINATION NODE ADDRESS
	HRRM	T1,PCBNNM(U)	;STORE IN PCB FOR NETWRT TO USE WHEN SENDING
	PUSHJ	P,BI2EBI	;OUTPUT
;SNA
	MOVEI	T1,NETNDB##	;ADDRESS OF THE NODE DATA BLOCK
	HLRZ	T1,NDBNNM(T1)	;GET THE SOURCE NODE ADDRESS (US)
	PUSHJ	P,BI2EBI	;OUTPUT
;NCA
				;REAL MESSAGE NUMBER IS ASSIGNED BY NETWRT
	MOVEI	T1,0		;DUMMY MESSAGE NUMBER NOW
	PUSHJ	P,DPBBIN	;STORE
;NCN
	MOVEI	T1,0		;AGAIN
	PUSHJ	P,DPBBIN
;EXIT
	MOVSI	T1,PCB.NM	;GET NUMBERED MESSAGE FLAG
	POP	P,T2		;GET THE NCT FLAGS BACK
	TRNN	T2,NCT.TP	;NUMBERED MESSGE??
	IORM	T1,PCBBLK(U)	;YES, SET THE FLAG
	ADDM	P3,PCBCTR(U)	;UPDATE THE CURRENT COUNT
	SETZ	P3,		;CLEAR THE COUNT FIELD
	POPJ	P,
SUBTTL UNNUMBERED NCS CONTOL MESSAGES
;SUBROUTINE NCSSTR/NCSSAK - SEND A START/STACK MESSAGE
;CALL	MOVEI	W,NDB		;WHERE TO SEND THE MESSAGE
;	PUSHJ	P,NCSSSM	;SEND A START OR A STACK MESSAGE
;RETURN	CPOPJ			;CAN'T NO CORE
;	CPOPJ1			;OK

NCSSSM:	MOVE	T1,NDBFLG(W)	;GET THE FLAGS, AND
	TLNE	T1,NDB.SK	;  IF WE ARE SUPPOSED TO SEND A STACK
	JRST	NCSSS1		;  THEN SKIP THE SEND-START CODE
	PUSHJ	P,CLNNDB	;CLEAN OUT THE NDB FOR STARTERS
	MOVEI	T1,NCT.ST	;START FLAGS
	SKIPA
NCSSS1:	MOVEI	T1,NCT.SK	;START ACK FLAGS
	PUSHJ	P,SAVE3##	;SAVE THE P'S

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE WE DON'T SEND OURSELF A START

	PUSH	P,T1		;SAVE THE FLAGS
	SETZ	F,		;NCS MESSAGE
	PUSHJ	P,NCSHDR	;MAKE A HEADER (U = NEW PCB)
	  PJRST	TPOPJ##		;EXIT NO CORE
	PUSHJ	P,NCSOPD	;GET THE <NNM><SNM><SID>
	HRRZ	T1,NETNDB##+NDBNVR	;GET OUR NCL VERSTION
	PUSHJ	P,BI2EBI	;SEND IT IN MESSAGE
	ADDM	P3,PCBCTR(U)	;UPDATE THE COUNT
	POP	P,T2		;RESTORE THE FLAGS
	PUSHJ	P,NETWRT	;SEND THE STACK MESSAGE
	JRST	CPOPJ1		;ALL DONE
;SUBROUTINE NCSNID - SEND A NODE ID MESSAGE
;CALL	MOVEI	J,FEK
;	PUSHJ	P,NCSNID
;RETURN	CPOPJ			;NO CORE ETC
;	CPOPJ1			;SENT

NCSNID:	PUSHJ	P,SAVE3##	;SAVE THE P'S

IFN PARANOID&P$FEK,<		;MAKE SURE WE DON'T SEND THE NULL FEK A NODEID

	HLLZ	T1,FEKBLK(J)	;GET THE FEK'S FLAGS
	TLNE	T1,FK.NUL	;SEE IF IT'S A NULL FEK
	STOPCD	.,STOP,ANFNFI,	;++ SENDING NODE ID TO THE NULL FEK
>
	SETZB	W,F		;NO DDB OR NDB
	MOVEI	T1,^D16		;MESSAGE SIZE
	PUSHJ	P,MKUPCB	;ALLOCATE THE MESSAGE SPACE
	  POPJ	P,		;NO SPACE AVAILABLE
	MOVE	P2,PCBPTR(U)	;GET THE BYTE POINTER
	SETZ	P3,		;CURRENT COUNT IS ZERO
;NCT
	MOVEI	T1,NCT.ID!NCT.SQ ;GET ID FLAGS
	PUSHJ	P,BI2EBI	;WRITE THE FLAGS
;NCA
	MOVEI	T1,0		;NO, ACKS FOR MESSAGE NUMBERS
	PUSHJ	P,BI2EBI	;WRITE
;NCN
	MOVEI	T1,0		;SAME AS ABOVE
	PUSHJ	P,BI2EBI	;WRITE
;OPD
	PUSHJ	P,NCSOPD	;SEND THE <NNM><SNM><SID>

IFN FTENET,<
	MOVE	T1,FEKBLK(J)	;GET FEK FLAGS
	TLNN	T1,FK.ETM	;ETHERNET (MASTER) FEK?
	JRST	NCSNI4		;NO, SEND A P-P NODEID
	MOVEI	T1,NIT.BC	;YES, MARK THIS AS A "BROADCAST" NODEID
	PUSHJ	P,BI2EBI	;AND NCL'IZE IT
	AOS	T1,FEKNIS(J)	;INCREMENT NODEID SERIAL NUMBER
	PUSHJ	P,BI2EBI	;NCL'IZE THE SERIAL NUMBER
	HLRZ	T2,FEKNIT(J)	;GET NEW BROADCAST TIMER INTERVAL
	HRRM	T2,FEKNIT(J)	;AND SET IT FOR ONCE/SECOND CHECKING
	JRST	NCSNI5		;FINISH OFF NODE-ID
> ;END IFN FTENET

NCSNI4:	XMTI	NIT.PP		;THIS IS A POINT-TO-POINT NODE-ID

NCSNI5:	HRRZ	T1,NETNDB##+NDBNVR ;GET OUR NCL VERSION NUMBER
	PUSHJ	P,BI2EBI	;NCL-IZE IT
	MOVSI	T1,FK.NID	;GET NODE ID FLAG
	IORM	T1,FEKBLK(J)	;SET FLAG NODE ID SENT
	ADDM	P3,PCBCTR(U)	;UPDATE THE COUNT
	AOS	NCLXTP+NCT.ID	;COUNT THE MESSAGE TYPE
	AOS	(P)		;SKIP RETURN
IFN FTKL10,<PUSHJ P,PCBMRK>	;SET UP CACHE INFO FOR CALL TO FRCWRT
	NETOFF			;NO RACE, "NETWRT" WILL RESTORE PI'S
	PJRST	FRCWRT		;FORCE WRITE THE MESSSAGE
;SUBROUTINE NCSOPD - GENERATE THE OPTIONAL DATA <NNM><SNM><SID>
;CALL	MOVEI	U,PCB
;	PUSHJ	P,NCSOPD
;RETURN	CPOPJ

NCSOPD:				;ENTRY
	PUSH	P,W		;SAVE THE NDB POINTER
	MOVEI	W,NETNDB##	;GET THE NODE DATA BLOCK
;NNM
	HLRZ	T1,NDBNNM(W)	;GET THE NODE NUMBER
	PUSHJ	P,BI2EBI	;WRITE
;SNM
	HLRZ	T1,NDBSNM(W)	;GET THE POINTER TO THE SYSTEM NAME
	MOVE	T1,(T1)		;GET THE NAME
	PUSHJ	P,SX2EAS	;WRITE
;SID
	HRRZ	P1,NDBSID(W)	;SOFTWARE NAME
	PUSHJ	P,AS2EAZ	;WRITE
	HLRZ	P1,NDBSID(W)	;CREATION DATE
	POP	P,W		;RESTORE THE NDB
	PJRST	AS2EAZ		;WRITE
;NCSNAK	THIS ROUTINE FIRST SCRAPS ALL UNPROCESSED MESSAGES ON
;	LH(NDBQUE(W)). IT THEN SENDS A NAK.
; NOTE	NAK'S ARE  ONLY  SENT AS A RESPONSE TO A REP.
; NOTE	REPS ALWAYS ELICIT A NAK RESPONSE
NCSNAK:	NETOFF			;PROTECT FROM FEK'S
	HLRZ	U,NDBQUE(W)	;GET THE LIST OF PCB'S
	HRRZS	NDBQUE(W)	;SPLICE OUT THE LIST
	NETON			;ALL CLEAR NOW
	PUSHJ	P,RMVALP	;FREE THE LIST
	MOVEI	T1,NCT.NK	;GET THE NAK NCT
	PJRST	NCSANR		;GO TO COMMON CODE

;NCSREP	THIS ROUTINE SENDS A REP MESSAGE (NO OTHER PROCESSING)
;CALL	W := NDB
;RETURN	CPOPJ	FAILED
;	CPOPJ1	SENT IT OK
NCSREP:	MOVEI	T1,NCT.RP	;GET REP NCT
	PJRST	NCSANR		;GO TO COMMON CODE


;SUBROUTINE NCSACK - NCSNAK SEND AN ACK MESSAGE
;CALL	MOVEI	W,NDB
;PUSHJ	P,NCSACK/NCSNAK
;RETURN	CPOPJ			;ERROR
;	CPOPJ1			;OK

NCSACK:	MOVEI	T1,NCT.AK	;GET THE ACK TYPE
;	PJRST	NCSANR		;COMMON CODE TO FINISH ACK/NAK/REP

NCSANR:				;COMMON CODE FOR ACK-NAK-REP
				;CALL WITH T1 := NCT

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE WE AREN'T SENDING IT TO US

	PUSHJ	P,SAVE3##	;SAVE THE P'S
	PUSH	P,F		;SAVE F
	SETZ	F,		;CLEAR F NCS MESSAGE
	PUSHJ	P,NCSHDR	;WRITE THE HEADER
	  JRST	FPOPJ		;RESTORE F AND GIVE ERROR EXIT
	POP	P,F		;RESTORE F
	PJRST	NETWSR		;SEND THE MESSAGE
SUBTTL NCS NUMBERED CONTROL MESSAGE HEADER
;NCMHDR	ROUTINE TO BUILD THE HEADER FOR NUMBERED CONTROL MESSAGES.
;CALL	MOVEI	W,NDB
;	PUSHJ	P,NCMHDR
;RETURN	CPOPJ			;NO CORE
;	CPOPJ1			;U := PCB, -1(P) := BYTE POINTER TO "CNT"
;
;NOTE!!!
;	THIS ROUTINE DOES THE GROSS AND UGLY THING OF MEDDLING WITH THE
;	STACK ON A SUCCESSFUL RETURN.  SINCE EVERY PLACE THIS ROUTINE WAS
;	CALLED IMMEDIATLY DID A "PUSH P,P2" THIS IS FAKED BEFORE NCMHDR
;	GIVES A SUCCESSFUL RETURN.  (THIS LEAVES THE STACK IN THE CORRECT
;	STATE FOR A "JSP T1,NETWRC" TO SEND THE MESSAGE.)
;
NCMHDR::			;HERE TO BUILD A NUMBERED MESSAGE HEADER
	MOVEI	T1,0		;CONTROL MESSAGE
	PUSHJ	P,NCSHDR	;OUTPUT THE HEADER
	  POPJ	P,		;NO CORE, GIVE ERROR RETURN
	MOVEI	T1,0		;NUMBERED CONTROL MESSGE
	PUSHJ	P,BI2EBI	;DEPOSIT
	MOVEI	P3,2		;ACCOUNT FOR THE FIRST
	ADDM	P3,PCBCTR(U)	; TWO BYTES
	SETZ	P3,		;CLEAR THE COUNT FIELD
	IBP	P2		;STEP OVER THE <CNT> FIELD
	POP	P,T1		;GET THE RETURN ADDRESS
	PUSH	P,P2		;SAVE A POINTER TO THE "CNT" FIELD FOR NETWRC
	JRST	1(T1)		;GIVE SKIP (CPOPJ1) RETURN
;NCSCNT	ROUTINE TO SEND A CONNECT CONFIRM MESSAGE.
;CALL	MOVE	T1,[XWD "ROUTINE TO WRITE SPN","ROUTINE TO WRITE DPN"]
;	MOVEI	F,DDB
;	PUSHJ	P,NCSCNC
;RETURN	CPOPJ			;SOMETHING WAS WRONG. ERROR CODE IN T1.
;	CPOPJ1			;CONNECT CONFIRM WAS SENT - DEVICE IS CONNECTED

NCSCNC::PUSHJ	P,NCSCNT	;SEND A "CONNECT" MESSAGE
	 POPJ	P,		;HO HUM
	MOVSI	S,IOSCON	;THE "DEVICE IS CONNECTED" FLAG
	IORB	S,DEVIOS(F)	;TELL THE WORLD THE DEVICE IS GOOD NOW
	JRST	CPOPJ1##	;SUCCESSFUL RETURN WITH HAPPY DEVICE



;NCSCNT	ROUTINE TO SEND A CONNECT INITIATE MESSAGE.
;CALL	MOVE	T1,[XWD "ROUTINE TO WRITE SPN","ROUTINE TO WRITE DPN"]
;	MOVEI	F,DDB
;	PUSHJ	P,NCSCNT
;RETURN	CPOPJ			;SOMETHING WAS WRONG. ERROR CODE IN T1.
;	CPOPJ1			;CONNECT WAS SENT. (BUT NOT CONFIRMED)

NCSCNT::			;HERE TO SEND A CONNECT MESSAGE
	NTDBUG			;JUST CHECKING...
	PUSHJ	P,SAVE3##	;SAVE THE P'S
	PUSHJ	P,SAVJW##	;WE WILL CLOBBER J(FEK) AND W(NDB)
	PUSH	P,U		;SAVE ANY MESSAGE WE MAY BE READING

	MOVE	P1,T1		;SAVE ADDRESS TO WRITE "DPN" & "SPN"
	HRRZ	T1,DEVNET(F)	;GET THE NUMBER OF THE NODE THAT OWNS THIS
	PUSHJ	P,SRCNDB	;SET UP W TO POINT TO ITS NDB.
	  JRST	[MOVEI T1,NRTUNN-NODERT ;GIVE A NODE-WENT-AWAY ERROR
		 JRST UPOPJ##]	;AND AN ERROR ROUTINE SO CALLER WILL NOTICE.
	MOVEI	T1,MSGMAW##	;BECAUSE CONNECT MSGS MAY BE VERY LONG
	PUSHJ	P,MKNPCB	;  WE MUST GET A LARGE PCB
	  JRST	[MOVEI T1,NRTNCE-NODERT ;BUT IF WE ARE OUT OF CORE, THEN
		 JRST UPOPJ##]	;GIVE A NODE-CAPACITY-EXCEEDED ERROR

	MOVE	P2,PCBPTR(U)	;GET THE ADDRESS OF THE MESSAGE BUFFER
	SETZ	T1,		;THIS IS A NUMBERED MSG (TYPE 0)
	PUSHJ	P,NCSWHD	;WRITE THE 5 HEADER BYTES
	XMTI	0		;NO DLA SIGNIFIES NUMBERED CONTROL
	PUSH	P,P2		;REMEMBER WHERE THE "CNT" FIELD IS
	XMT1	T1		;WRITE TWO GARBAGE
	XMT1	T1		;  BYTES. (WILL HOLD AN EXTENSIBLE COUNT)
	ADDM	P3,PCBCTR(U)	;UPDATE THE LENGTH OF THE MESSAGE SO FAR
	SETZ	P3,		;RESET P3 SO WE CAN MEASURE THE CONNECT MSG.

;FALL THROUGH TO WRITE THE BODY OF THE CONNECT MESSAGE
;TYP
	MOVEI	T1,NC.CNT	;THIS IS A CONNECT MESSAGE
	PUSHJ	P,BI2EBI	;WRITE THE TYPE
;DLA
	LDB	T1,NETDLA	;GET THE DLA FROM THE DDB
	PUSHJ	P,BI2EBI
;SLA
	LDB	T1,NETSLA	;GET THE SLA FROM THE DDB

IFN PARANOID&P$LAT,<
	SKIPN	T1		;JUST A BIT OF PARANOIA AGAIN
	STOPCD	.,STOP,ANFSLA,	;++ NO SLA ON A CONNECT
>
	PUSHJ	P,BI2EBI
;DPN(OBJ,PID)
	HRRZ	T1,P1		;GET SUBR
	PUSHJ	P,(T1)		;LET CALLER WRITE THE DEST PROCESS DESCRIPTOR
;SPN(OBJ,PID)
	MOVS	T1,P1		;SWAP THE HALVES
	HRRZS	T1		;WATCH THAT XADDR STUFF!
	PUSHJ	P,(T1)		;LET THE CALLER WRITE THE SOURCE PROCESS DESC.
;MML
	LDB	T1,NETMML	;GET SERVICE-ROUTINE-SPECIFIED MAX MESSAGE SIZE
	CAIG	T1,0		;GO WITH IT IF ANYTHING SPECIFIED
	MOVEI	T1,MSGMAD##	;GET OUR LARGEST PERMISSABLE MESSAGE
				; (*** SEE FOOTNOTE)
	PUSHJ	P,BI2EBI	;OUTPUT
;FEA(DCM)
	PUSH	P,W		;SAVE THE NDB POINTER FOR A BIT
	HLRZ	W,DEVNET(F)
	LDB	T1,NDTDCM	;GET THE DEVICE MODES POSSIBLE
	PUSHJ	P,BI2EBI	;WRITE
;FEA(,RLN)
	MOVEI	T1,0		;RECORD LENGTH IS VARIABLE
	PUSHJ	P,BI2EBI	;WRITE
;FEA(,,DVT)
	LDB	T1,NDTDVT	;GET THE DEVICE ATTRIBUTES
	PUSHJ	P,BI2EBI	;WRITE
;FEA(,,,DVU)
	LDB	T1,NETDVU	;GET DEVICE "UNIT" TYPE
	PUSHJ	P,BI2EBI	;WRITE
;FEA(,,,,DVV)
	LDB	T1,NETDVV	;GET DEVICE "CONTROLLER" TYPE
	PUSHJ	P,BI2EBI	;WRITE
;FEA(,,,,,DFT)
	HRRZ	T1,NDBNVR(W)	;GET REMOTE'S NCL VERSION NUMBER
	JUMPE	T1,NCSCN1	;SKIP DFT FIELD IF OLD NODE
	MOVE	T1,DEVDFT(F)	;GET FORMS TYPE WORD
	PUSHJ	P,SX2EAS	;WRITE IT OUT

;FALL THROUGH TO FIXUP THE LENGTH AND SEND THE MESSAGE
NCSCN1:
;CNT (GO BACK AND FILL IT IN)
	POP	P,W		;RESTORE THE NDB POINTER
	CAILE	P3,^D256	;IS THE MSG LONGER THAN THIS ARBITRARY VALUE
	STOPCD	.,STOP,ANFCIL,	;++ CONNECT INITIATE MESSAGE TOO LONG,
				; SOMEONE (TSKSER) SCREWED UP
	ADDM	P3,PCBCTR(U)	;UPDATE THE TOTAL MESSAGE LENGTH
	POP	P,T2		;GET THE POINTER TO THE TWO BYTE "CNT" BACK
	LDB	T1,[POINT 7,P3,35] ;GET THE LOW 7 BITS
	TRO	T1,200		;  AND MAKE THEM EXTENSIBLE
	IDPB	T1,T2		;STORE THE FIRST BYTE OF THE COUNT
	LDB	T1,[POINT 7,P3,28] ;GET THE REST OF THE LENGTH
	IDPB	T1,T2		;  AND STORE THAT
	PUSHJ	P,NETWRT	;SEND THE MESSAGE
	PJRST	UPOPJ1##	;GIVE GOOD RETURN


;*** FOOTNOTE ***

COMMENT \

     Historically, this value was calculated by taking the minimum of
all message length's for all nodes in the path between the node we were
sending the connect to and ourself.  This seems wrong to me.  Given a
multi-path environment, we may find ourselves using an un-expected path.
The result of this is that the sender must calculate the maximum message
length based on the current minimum of all nodes in the network.  In light
of this, my opinion is that the MML field should represent our "local"
maximum.  It doesn't really matter though since no one uses the field anyway...

						Matson


     And I take offense at the above footnote - I tried to believe the
silly value in the DDP code in the -87, and look where it got me - into
trap service 'cuz the -10 sent some silly number that had no relation
with reality. Boo Hiss! Such are the trials and tribulations of life in
the small country pits . . .

						-RDH
\
;NCSDSC	ROUTINE TO SEND A DISCONNECT MESSAGE.
;CALL	MOVEI	F,DDB
;	MOVEI	T1,"REASON"
;	PUSHJ	P,NCSDSC
;RETURN	CPOPJ			;NO CORE, OR NODE WENT AWAY.
;	CPOPJ1			;DISCONNECT SENT (BUT NOT CONFIRMED!)
;
NCSDSC::			;HERE TO SEND A DISCONNECT MESSAGE
	NTDBUG			;JUST CHECKING
	PUSHJ	P,SAVE3##	;SAVE THE P'S
	PUSHJ	P,SAVJW##	;WE CLOBBER J(FEK) AND W(NDB)
	PUSH	P,U		;SAVE ANY INPUT MESSAGE WE MAY BE PROCESSING

	MOVEI	P1,(T1)		;SAVE THE REASON IN A SAFE REGISTER

IFN PARANOID&P$LAT,<
	LDB	T1,NETSLA	;JUST A QUICK CHECK TO MAKE SURE THAT THE LAT
	HRRZ	T1,NETLAT##(T1)	; AND THE DDB AGREE WITH RESPECT TO THE SLA
				;IT'S OK TO ONLY LOOK AT 18 BITS . . .
	CAIE	T1,(F)		;DOES THE SLA POINT BACK TO THE DDB?
	STOPCD	.,STOP,ANFLDD,	;++ LAT AND DDB DISAGREE
>

	HRRZ	T1,DEVNET(F)	;GET THE NODE NUMBER
	PUSHJ	P,SRCNDB	;SET UP "W" WITH THE NDB POINTER
	 STOPCD	.,STOP,ANFNWA,	;++ NODE WENT AWAY, DRIVER SHOULD HAVE NOTICED
	PUSHJ	P,NCMHDR	;MAKE A CONTROL MESSAGE HEADER.
	  JRST	UPOPJ		; IF NO CORE, GIVE AN ERROR RETURN
;TYP
	MOVEI	T1,NC.DSC	;WE ARE A DISCONNECT MESSAGE
	PUSHJ	P,BI2EBI	;SEND THE DISCONNECT TYPE
;DLA
	LDB	T1,NETDLA	;GET THE REMOTE'S ADDRESS FOR THIS CONNECTION
IFN PARANOID&P$LAT,<
	SKIPN	T1		;JUST A LITTLE PARANOIA
	STOPCD	.,STOP,ANFDLA,	;++ NO DLA ON CONNECT
>
	PUSHJ	P,BI2EBI	;SEND THE DLA
;SLA
	LDB	T1,NETSLA	;GET OUR ADDRESS FOR THIS CONNECTION
	PUSHJ	P,BI2EBI	;SEND THE SLA
;RSN
	MOVEI	T1,(P1)		;GET THE CALLER SPECIFIED REASON
	PUSHJ	P,BI2EBI	;SEND THE REASON
;CNT
	JSP	T1,NETWRC	;SEND THE MESSAGE
	PJRST	UPOPJ1##	;GIVE A GOOD RETURN

;*** FOOTNOTE ***

COMMENT \

     Since this routine must be called under the protection of the
NETSER interlock, there is no reason that the device driver cannot
check to make sure that the node he is attempting to send a disconnect
to is still up.  The reason for replacing the "UPOPJ" with a STOPCD
is to enforce such practices in device drivers

\

;SUBROUTINE	NCSNBN - SEND NEIGHBOR NAME MESSAGE
;CALL	PUSHJ	P,NCSNBN
;RETURN	CPOPJ
;	CPOPJ1

NCSNBN:				;ENTRY
	PUSHJ	P,SAVE3##	;SAVE THE P'S
	PUSHJ	P,SAVJW##	;SAVE J AND W
	SETZ	F,		;CONTROL MESSAGE
	PUSHJ	P,NCMHDR	;WRITE NUMBERED HEADER (PUSH BYTE PTR ON STK)
	  POPJ	P,		;NO CORE
	MOVEI	T1,NC.NBN	;GET HEIGHBOR NAMES
	PUSHJ	P,BI2EBI	;WRITE
	PUSH	P,W		;SAVE THE NDB POINTER
	MOVEI	W,NETNDB##	;GET OUR NDB
	MOVE	T4,[POINT 9,NDBTOP(W)]	;SEARCH THE TOPOLOGY TABLE
	MOVEI	T3,NGHMAX	;GET THE SIZE OF THE NEIGHBORS TABLE
NCSNB1:	ILDB	T1,T4		;GET THE NODE NUMBER
	JUMPE	T1,[IBP T4	;IF NO NODE IN THIS SLOT. SKIP USLESS COST
		    JRST NCSNB2] ;AND GO DO NEXT SLOT
;NNM
	XMT	T1		;SEND THE NODE NUMBER
;LVL
	ILDB	T1,T4		;GET THE COST
	XMT	T1		;SEND THE COST
NCSNB2:	SOJG	T3,NCSNB1	;LOOP OVER ALL NEIGHBORS
	POP	P,W		;RESTORE THE TARGET NODE
	MOVSI	T1,NDB.NB	;GET NEIGHBORS SENT
	IORM	T1,NDBFLG(W)	;SET IT
	JSP	T1,NETWRC	;SEND THE MESSAGE
	JRST	CPOPJ1		;GIVE GOOD (SKIP) RETURN

;SUBROUTINE SETNBN - SET THE BITS TO SEND NEIGHBORS MESSAGES TO ALL
;CALL	PUSHJ	P,SETNBN
;RETURN	CPOPJ

SETNBN:				;ENTRY ON CHANGE OF NEIGHBORS
	PUSHJ	P,SAVJW##	;SAVE J AND W
	MOVSI	T1,NDB.NB	;GET NEIGHBORS BIT
	MOVEI	W,NETNDB	;START OF NDB'S
SETNB1:	HRRZ	W,NDBNNM(W)	;GET THE FIRST NDB LESS OURS
	JUMPE	W,CPOPJ##	;END OF LIST
	ANDCAM	T1,NDBFLG(W)	;CLEAR THE BIT
	JRST	SETNB1		;CONTINUE
;SUBROUTINE NCSRCF - REQUEST CONFIGURATION MESSAGE
;CALL	MOVEI	W,NDB
;	PUSHJ	P,NCSRCF
;RETURN	CPOPJ		;NO CORE
;	CPOPJ1		;RETURN

NCSRCF:				;ENTRY
	PUSHJ	P,SAVE3##	;SAVE THE P'S

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE WE AREN'T SENDING IT TO US

	PUSHJ	P,SAVJW##	;SAVE J AND W
	SETZ	F,		;CONTROL MESSAGE
	PUSHJ	P,NCMHDR	;WRITE A HEADER (AND PUSH "CNT" POSITION ON STK)
	  POPJ	P,		;CAN'T
	MOVEI	T1,NC.RCF	;TYPE REQUEST CONFIGURATION
	PUSHJ	P,BI2EBI	;SEND
	JSP	T1,NETWRC	;SEND THE MESSAGE
	PJRST	CPOPJ1##	;SKIP RETURN
;SUBROUTINE NCSCNF - SEND A CONFIGURATION MESSAGE UPON REQUEST
;CALL	MOVEI	W,NDB
;	PUSHJ	P,NCSCNF
;RETURN	CPOPJ
;	CPOPJ1

NCSCNF:				;ENTERY
	PUSHJ	P,SAVE3##	;SAVE THE P'S

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE WE AREN'T SENDING IT TO THIS NODE

	PUSHJ	P,SAVJW##	;SAVE J W
	SETZ	F,		;CLEAR THE DDB
	PUSHJ	P,NCMHDR	;GET A HEADER (AND PUSH "CNT" POSITION)
	  POPJ	P,		;NO CORE AVAILABLE
	MOVEI	W,NETNDB##	;GET OUR NODE DATA BLOCK
	MOVEI	T1,NC.CNF	;CONFIGURATION MESSAGE
	PUSHJ	P,BI2EBI	;SEND
;OBJ
	MOVSI	P1,-<OBJ.MX+1>	;NUMBER OF DEVICE TYPES
NCSCF1:	LDB	T1,NETCNF##(P1)	;GET THE COUNT
	JUMPE	T1,NCSCF2	;DON'T HAVE ANY
	MOVEI	T1,(P1)		;GET THE TYPE BACK
	PUSHJ	P,BI2EBI	;SEND THE DEVICE TYPE
;NDV
	LDB	T1,NETCNF##(P1)	;DEVICE COUNT
	PUSHJ	P,BI2EBI	;SEND
;PID
	MOVEI	T1,0		;ZERO PID
	PUSHJ	P,BI2EBI	;SEND
NCSCF2:	AOBJN	P1,NCSCF1	;CONTINUE THROUGH THE LIST
	JSP	T1,NETWRC	;SEND THE MESSAGE
	JRST	CPOPJ1##	;GIVE SKIP RETURN
;SUBROUTINE NCSDRQ - SEND A DATA REQUEST
;CALL	MOVEI	T1,DATA REQUEST COUNT
;	MOVEI	F,DDB
;	PUSHJ	P,NCSDRQ
;RETURN	CPOPJ			;NO CORE
;	CPOPJ1			;REQUEST SENT

NCSDRQ::			;ENTRY
	NTDBUG			;VERIFY THE INTERLOCK
	MOVE	S,DEVIOS(F)	;GET THE DEVICE STATUS
	TLNN	S,IOSCON	;ARE WE STILL CONNECTED?
	  JRST	CPOPJ1##	;NO, GOOD RETURN, OUTPUT BETTER TRAP ERROR
	PUSHJ	P,SAVE3##	;SAVE THE P'S
	PUSHJ	P,SAVJW##	;SAVE J AND W
	PUSH	P,U		;SAVE U (SELF CONTAINED ROUTINE)
	MOVEI	P1,(T1)		;SAVE THE DRQ COUNT
	HRRZ	T1,DEVNET(F)	;GET THE NODE NUMBER
	PUSHJ	P,SRCNDB	;SET UP W FROM THE NODE NUMBER
	  SKIPA			;NODE WENT AWAY. GIVE ERROR RETURN
	PUSHJ	P,NCMHDR	;WRITE THE HEADER (AND PUSH "CNT" POINTER)
	  JRST	UPOPJ##		;RESTORE U AND GIVE ERROR RETURN
	MOVEI	T1,NC.DQR	;DATA REQUEST
	PUSHJ	P,BI2EBI	;WRITE
;DLA
	LDB	T1,NETDLA	;GET THE DESTINATION LINK ADDRESS
IFN PARANOID&P$LAT,<
	SKIPN	T1		;JUST A BIT OF PARANOIA AGAIN
	STOPCD	.,STOP,ANFDRZ,	;++ SENDING DRQ'S TO DEVICE ZERO
>
	PUSHJ	P,BI2EBI	;WRITE
;DQR
	MOVEI	T1,(P1)		;GET THE DQR
	PUSHJ	P,BI2EBI	;WRITE
;CNT
	JSP	T1,NETWRC	;SEND THE MESSAGE (POP "CNT" POINTER OFF STK)
	PJRST	UPOPJ1##	;SKIP EXIT
SUBTTL MEMORY CONTROL ROUTINES

;SUBROUTINE GETZWD - GET A BLOCK OF MONITOR FREE CORE AND ZERO
;CALL	MOVEI	T2,#WORDS
;	PUSHJ	P,GETZWD
;RETURN	CPOPJ			;NO CORE AVAILABLE
;	CPOPJ1			;T1=ADDRESS

GETZWD::
IFN PARANOID&P$COR,<		;KEEP A LINKED LIST OF ALL NETWORK CORE

	ADDI	T2,3		;WE USE 3 EXTRA WORDS IF WE ARE PARANOID
>
	PUSH	P,T2		;SAVE THE NUMBER OF WORDS
	HLRE	T1,FREPTR##	;GET LENGTH OF FREE CORE MAP
	MOVNS	T1
	IMULI	T1,^D36		;MAKE #4 WORD BLOCKS (=1/4 FREE CORE TOTAL)
	JFCL
;	CAML	T1,%NTCOR	;DON'T USE MORE THAN 1/4 OF FREE CORE
	PUSHJ	P,GETWDS##	;ALOCATE THE SPACE
	  PJRST	T2POPJ##	;NO, SPACE AVAILABLE
	SETZM	(T1)		;CLEAR THE FIRST WORD
	MOVE	T2,(P)		;GET THE NUMBER OF WORDS
	ADDM	T2,%NTCOR	;ACCOUNT FOR CORE USED
	MOVEM	T1,(P)		;SAVE THE STARTING ADDRESS
	ADDI	T2,-1(T1)	;POINT TO THE END OF THE LIST
	HRLI	T1,1(T1)	;MAKE A BLT POINTER
	MOVSS	T1		;FROM,,TO
	BLT	T1,(T2)		;CLEAR THE BLOCK
	MOVE	T1,%NTCOR	;IF WE NOW ARE USING MORE
	CAMLE	T1,%NTMAX	; CORE THAN EVER BEFORE
	MOVEM	T1,%NTMAX	; RECORD FOR PRYING EYES
IFN PARANOID&P$COR,<		;NOW LINK IN THE BLOCK AND SET LAST WORD

	MOVE	T1,[SIXBIT /NETMEM/]	;"UNIQUE" PATTERN FOR END WORD
	MOVEM	T1,(T2)		;SO WE CAN SEE IF USER WROTE TOO FAR.
	MOVE	T1,(P)		;GET ADDRESS OF START OF THE BLOCK
	NETOFF			;WE NEED PROTECTION WHILE HACKING LISTS
	MOVE	T2,%NTMEM	;GET POINTER TO FIRST BLOCK IN THE CHAIN
	HRLI	T2,%NTMEM	;MAKE OUR BACK LINK POINT TO "%NTMEM"
	MOVEM	T2,(T1)		;STORE DOUBLE LINK IN FIRST WORD
	ANDI	T2,-1		;STRIP OFF IME-INDUCING LH GARBAGE
	MOVEM	T1,%NTMEM	;MAKE THE LIST START WITH US
	HRLM	T1,(T2)		;FIX UP BACKWARD LINK IN NEXT BLOCK
	NETON			;ALL'S CLEAR
	MOVE	T2,-1(P)	;GET THE CALLER'S PC
	HRLI	T2,'NET'	;GET A "UNIQUE" 18 BIT STRING
	MOVEM	T2,1(T1)	;AND PUT "XWD STRING,PC" IN SECOND WORD
	ADDI	T1,2		;MAKE POINTER POINT TO THE THIRD WORD
	MOVEM	T1,(P)		;AND GIVE THAT TO THE USER
>
	PJRST	TPOPJ1##	;RESTORE THE ADDRESS AND EXIT
;SUBROUTINE GIVZWD	- RETURN MONITOR FREE CORE
;CALL	MOVEI	T1,#WORDS
;	MOVEI	T2,ADDRESS
;	PUSHJ	P,GIVZWD
;RETURN	CPOPJ

GIVZWD::
IFN PARANOID&P$COR,<		;CHECK FOR ILLEGAL MEMORY USAGE
	ADDI	T1,3		;ACCOUNT FOR CHECK WORDS
	SUBI	T2,2		;ADJUST POINTER TO BEGINNING OF BLOCK
> ;END IFN PARANOID&P$COR

	PUSH	P,T1		;SAVE THE LENGTH

IFN PARANOID&P$COR,<		;CHECK FOR ILLEGAL MEMORY USAGE
	PUSH	P,T2		;SAVE THE ADDRESS
	NETOFF			;PROTECTION WHILE HACKING LISTS
	MOVE	T1,(T2)		;GET THE "XWD BACKWARD,FORWARD" LINK WORD
	HRRZ	T3,T1		;JUST THE ADDRESS FOR IME-LESS INDEXING
	HLLM	T1,(T3)		;SPLICE OUT ONE POINTER
	MOVS	T1,T1		;SWAP HALVES
	HRRZ	T3,T1		;JUST THE ADDRESS FOR IME-LESS INDEXING
	HLRM	T1,(T3)		;SPLICE OUT THE OTHER POINTER
	NETON			;ALL IS CLEAR. WE ARE SPLICED OUT
	HLRZ	T1,1(T2)	;GET THE LEFT OF THE SECOND CHECK WORD
	CAIE	T1,'NET'	;MAKE SURE IT WASN'T CLOBBERED
	STOPCD	.,STOP,ANFWMB,	;++ USER WROTE IN MEMORY BEFORE BLOCK
	ADD	T2,-1(P)	;GET A POINTER TO ONE PAST THE END OF THE BLOCK
	EXCH	T2,-1(T2)	;GET THE LAST CHECK WORD (AND MAKE IT GARBAGE)
	CAME	T2,[SIXBIT /NETMEM/] ;VERIFY THAT HE DIDN'T CLOBBER IT
	STOPCD	.,STOP,ANFWPE,	;++ USER WROTE PAST THE END OF THE BLOCK
	POP	P,T2		;GET THE ADDRESS BACK
> ;END IFN PARANOID&P$COR

	MOVN	T1,0(P)		;"SUB'ABLE" LENGTH
	ADDB	T1,%NTCOR	;REDUCE CORE USED
	SKIPGE	T1		;CHECK RESULT
	STOPCD	TPOPJ##,DEBUG,CWN,;++CORE ALLOCATION WENT NEGATIVE
	POP	P,T1		;RESTORE WORD COUNT
	PJRST	GIVWDS##	;RETURN THE CORE
;SUBROUTINE SVEVM - SAVE THE JOB EVM AND RESTORE ON POPJ
;CALL	PUSHJ	P,SVEVM
;RETURN	CPOPJ

SVEVM::	JUMPE	F,CPOPJ##	;NO EVM IF NO DDB
	SKIPN	DEVEVM(F)	;ANY EVM
	POPJ	P,		;NO RETURN
	PUSHJ	P,RTEVM##	;YES, RETURN THE EVM
	POP	P,(P)		;PUT THE RETURN ON THE END OF THE STACK
	PUSHJ	P,@1(P)		;RETURN TO THE CALLER
	  PJRST	RSTEVM##	;NON-SKIP RESTORE EVM
	AOS	(P)		;SKIP RETURN
	PJRST	RSTEVM##	;RESTORE EVM AND EXIT
SUBTTL COMMON SUBROUTINES TO CONVERT EXTENSIBLE ASCII/BINARY
;SUBROUTINE EAS2SX CONVERT EXTENSIBLE ASCII TO SIXBIT
;CALL	MOVEI	P1,[INPUT POINTER]
;	PUSHJ	P,EAS2SX
;RETURN	CPOPJ			;T1=SIXBIT

EAS2SX::MOVE	T2,[POINT 6,T1]	;GET BYTE POINTER
	SETZB	T1,T4		;CLEAR OUTPUT
EAS2S1:	SOJL	P4,CPOPJ##	;EXIT IF NO MORE DATA
	ILDB	T3,P1		;GET THE BYTE
	TRZN	T3,200		;CONTINUE BIT
	SETO	T4,		;NO, SET FLAG
	TRNE	T3,140		;MAKE SURE ASCII BEFOR SUBI
	SUBI	T3,40		;CONVERT TO SIXBIT
	TRNN	T3,177		;CHECK FOR A BLANK CHARACTER
	JUMPE	T1,EAS2S2	;AND A LEADING BLANK
	CAIE	T3,'['		;CHECK TO [XXX,XXX]
	CAIN	T3,']'		;AND EXIT
	POPJ	P,		;IF FOUND
	CAIN	T3,','		;ALSO A COMMA
	POPJ	P,		;WILL EXIT
	TLNE	T2,(77B5)	;END OF WORD
	IDPB	T3,T2		;STORE WORD
EAS2S2:	JUMPGE	T4,EAS2S1	;NO CONTINUE
	POPJ	P,		;RETURN

;SUBROUTINE SX2EAS CONVERT SIXBIT TO EXTENSIBLE ASCII
;CALL	MOVE	T1,[SIXBIT /.../]
;	MOVE	P2,[OUTPUT BYTE POINTER]
;	PUSHJ	P,SX2EAS
;RETURN CPOPJ			;P3 COUNTED UP

SX2EAS::SKIPE	T2,T1		;COPY THE SIXBIT NAME
SX2EA1:	SETZ	T1,		;CLEAR THE OUTPUT AC
	LSHC	T1,6		;GET A SIXBIT CHARACTER
	ADDI	T1,240		;CONVERT TO ASCII WITH CONTINUE BIT
	IDPB	T1,P2		;STORE CHARACTER
	ADDI	P3,1		;COUNT THE CHARACTER
	JUMPE	T2,CLRBT8	;EXIT AND CLEAR CONTINUE BIT
	JRST	SX2EA1		;COUNT AND CONTINUE
;SUBROUTINE AS2EAS CONVERT ASCIZ TO EXTENSIBLE ASCII
;CALL	MOVE	P1,[INPUT BYTE POINTER] ASCII 7
;	MOVE	P2,[OUTPUT BYTE POINTER] EXTENSIBLE ASCII 8
;	PUSHJ	P,AS2EAS
;RETURN	CPOPJ		;P3 UPDATED WITH CHARACTER COUNT

AS2EAZ:	JUMPE	P1,CPOPJ##	;DO NOTHING IF NO POINTER

AS2EAS::TLNN	P1,-1		;IS THERE A BYTE POINTER
	HRLI	P1,(POINT 7)	;NO, SUPPLY ONE
AS2EA1:	ILDB	T1,P1		;GET AN ASCII CHARACTER
	JUMPE	T1,CLRBT8	;JUMPE IF END
	TRO	T1,200		;SET HIGH ORDER BIT
	IDPB	T1,P2		;STORE 8 BIT BYTE
	AOJA	P3,AS2EA1	;COUNT CHARACTER TRY AGAIN
CLRBT8::LDB	T1,P2		;GET LAST STORED CHARACTER BACK
	TRZ	T1,200		;CLEAR THE CONTINUE BIT
	DPB	T1,P2		;STORE CHARACTER
	POPJ	P,		;RETURN

;SUBROUTINE EAS2AZ SEE IF ANYTHING FOR EAS2AS TO CONVERT
;CALL	MOVE	P1,[INTPUT BYTE POINTER]
;	PUSHJ	P,EAS2AZ
;RETURN 1	  NOTHING TO COPY
;RETURN 2	SOMETHING FOR EAS2AS TO DO
;USES T1 & T2

EAS2AZ:	JUMPE	P4,CPOPJ##	;RETURN IF NOTHING TO COPY
	MOVE	T1,P1		;COPY BYTE POINTER
	ILDB	T2,T1		;EXAMINE FIRST CHARACTER
	JUMPN	T2,CPOPJ1##	;SKIP IF SOMETHING TO COPY
	MOVE	P1,T1		;NO, COPY INCREMENTED BYTE POINTER
	SOJA	P4,CPOPJ##	;ACCOUNT FOR CHARACTER AND GIVE 'NONE' RETURN

;SUBROUTINE EAS2AS CONVERT AN EXTENSIBLE ASCII STRING TO ASCIZ
;CALL	MOVE	P1,[INPUT BYTE POINTER]
;	MOVE	P2,[OUTPUT BYTE POINTER]
;	PUSHJ	P,EAS2AS
;EXIT

EAS2AS::HRLI	P2,(POINT 7)	;MAKE A BYTE POINTER
	MOVEI	T2,^D37		;ALLOW A MAX CHARACTER COUNT
EAS2A1:	SOJL	P4,CPOPJ##	;EXIT IF NO MORE
	ILDB	T1,P1		;GET AN 8 BIT CHARACTER
	IDPB	T1,P2		;STORE A SEVEN BIT CHARACTER
	TRNE	T1,200		;IS CONTINUE BIT ON
	SOJG	T2,EAS2A1	;YES, CONTINUE
	SETZ	T1,		;SET UP A NULL
	IDPB	T1,P2		;STORE THE NULL
	POPJ	P,		;RETURN
;SUBROUTINE BI2EBI CONVERT A BINARY NUMBER TO EXTENSIBLE BINARY
;CALL	MOVE	T1,[A BINARY NUMBER]
;	MOVE	P2,[OUTPUT BYTE POINTER] 8 BIT
;	PUSHJ	P,BI2EBI,OCT2EBI,DE2EBI
;RETURN	CPOPJ		;P3 UPDATED
BI2EBI::CAIG	T1,177		;GREATER THAN 177
	JRST	DPBBIN		;NO OUTPUT
	LSHC	T1,-7		;SHIFT OFF THE BITS
	ROT	T2,7		;SAVE IN T2
	TRO	T2,200		;SET CONTINUE BIT
	IDPB	T2,P2		;STORE IN MESSAGE
	AOJA	P3,BI2EBI	;CONTINUE



;SUBROUTINE EBI2BI TO CONVERT EXTENSIBLE BINARY TO BINARY
;CALL	MOVE	P1,[INPUT BYTE POINTER
;	PUSHJ	P,EBI2BI
;RETURN	CPOPJ			;T1=BINARY NUMBER

EBI2BI::SETZB	T1,T2		;INITIALIZE FOR NUMBER-BUILDING LOOP
EBI2B1:	SOJL	P4,CPOPJ##	;EXIT IF THE END OF DATA
	ILDB	T3,P1		;GET THE NEXT CHARACTER
	TRZN	T3,200		;IS THE NUMBER EXTENDED
	JRST	EBI2B5		;NO, ACCUMLATE END OF NUMBER AND EXIT
	LSH	T3,(T2)		;POSITION NEXT "DIGIT"
	IOR	T1,T3		;AND ACCUMULATE NUMBER
	ADDI	T2,7		;"ADVANCE" ACCUMULATOR-POSITIONER
	JRST	EBI2B1		;CONTINUE

EBI2B5:	LSH	T3,(T2)		;POSITION NEXT "DIGIT"
	IOR	T1,T3		;AND ACCUMLATE NUMBER
	POPJ	P,		;RETURN WITH BINARY IN T1



;SUBROUTINE BYT2BI READ ONE BYTE FROM THE STREAM
;CALL	JUST LIKE THE REST
;RETURN	CPOPJ			;T1 = THE 8 BIT BYTE

BYT2BI::SOJL	P4,CPOPJ##	;EXIT IF END OF MESSAGE
	ILDB	T1,P1		;GET THE BYTE
	POPJ	P,		;  AND RETURN
;SUBROUTINE XX2EAS CONVERT NUMBER TO EXTENSIBLE ASCII
;CALL	P2/ OUTPUT BYTE POINTER		P3/ COUNTER	T1/ NUMBER
;	PUSHJ	P,OC2EAS/DC2EAS/RX2EAS
;RETURN CPOPJ WITH P2/P3 UPDATED

DC2EAS:	SKIPA	T3,[^D10]	;DECIMAL CONVERSION
OC2EAS::MOVEI	T3,^D8		;OCTAL CONVERSION
RX2EAS:	IDIVI	T1,(T3)		;SEPERATE AGAIN
	PUSH	P,T2		;STORE THE BYTE
	SKIPE	T1		;ANY LEFT NOW
	PUSHJ	P,RX2EAS	;YES, TRY AGAIN
	POP	P,T1		;NO, GET THE LAST DIGIT BACK
	ADDI	T1,"0"		;CONVERT TO ASCII
DPBEAS:	TRO	T1,200		;SET THE CONTINUE BIT
DPBBIN::ADDI	P3,1		;COUNT
	IDPB	T1,P2		;NO STORE THE DIGIT
	POPJ	P,		;RETURN

;SUBROUTINE EAS2 - CONVERT ASCII TO OCTAL/DECIMAL/RADIX
;CALL	P1, P4:= POINTER TO INPUT
;	PUSHJ	P,EAS20C/EAS2DC/2ASRX
;RETURN CPOPJ; T1:= NUMBER, T2:= TERMINATING CHARACTER

EAS2DC::SKIPA	T3,[^D10]	;DECIMAL RADIX
EAS2OC::MOVEI	T3,^D8		;OCTAL RADIX
EAS2RX::SETZB	T1,T4		;ARBITRARY RADIX IN T3
EAS2NN::SOJL	P4,CPOPJ##	;COUNT DOWN INPUT
	ILDB	T2,P1		;NEXT INPUT CHARACTER
	TRZN	T2,200		;END OF FIELD
	SETO	T4,		;YES
	CAIL	T2,"0"		;VALID ?
	CAILE	T2,"0"(T3)	; DIGIT ?
	  POPJ	P,		;NO, END OF NUMBER
	IMULI	T1,(T3)		;ROOM FOR NEXT
	ADDI	T1,-"0"(T2)	;ADD UP NUMBER
	JUMPE	T4,EAS2NN	;LOOP FOR WHOLE NUMBER
	POPJ	P,		;OR UNITL END OF EXTENSIBLE ASCII
;XSKIP	ROUTINE TO SKIP OVER AN EXTENSIBLE FIELD
;CALL	P1, P4 := POINT TO FIELD
;	PUSHJ	P,XSKIP
;RETURN	CPOPJ			;ALWAYS
;
XSKIP::	SOJL	P4,CPOPJ##	;COUNT OFF THIS BYTE
	ILDB	T1,P1		;GET THE BYTE
	TRNE	T1,200		;EXTENSIBLE
	JRST	XSKIP		;YES. KEEP LOOKING FOR THE END
	POPJ	P,		; NO. ALL DONE



;SKIP1	ROUTINE TO SKIP OVER A 1 BYTE FIELD
;CALL	P1, P4 := POINT TO BYTE TO SKIP
;	PUSHJ	P,SKIP1
;RETURN	CPOPJ
;
SKIP1::	SOJL	P4,CPOPJ##	;COUNT DOWN THE BYTE
	IBP	P1		;INCREMENT THE BYTE POINTER
	POPJ	P,		;ALL DONE
;SUBROUTINE PP2EAS - OUTPUT A PPN IN EXTENSIVE ASCII
;CALL	MOVE	T1,[PPN]
;	PUSHJ	P,PP2EAS
;RETURN	CPOPJ

PP2EAS::PUSH	P,T1		;SAVE THE PPN
	MOVEI	T1,"["		;OPEN BRACKET
	PUSHJ	P,DPBEAS	;OUTPUT
	HLRZ	T1,(P)		;GET THE PROGRAMMER NUMBER
	PUSHJ	P,OC2EAS	;OUTPUT
	MOVEI	T1,","		;SEPERATOR
	PUSHJ	P,DPBEAS	;OUTPUT
	POP	P,T1		;RESTORE THE STACK GET PROGRAMMER #
	HRRZS	T1		;RT HALF
	PUSHJ	P,OC2EAS	;OUTPUT
	MOVEI	T1,"]"		;CLOSING BRACKET
	PUSHJ	P,DPBEAS	;OUTPUT
	PJRST	CLRBT8		;CLEAR THE LAST BIT

;SUBROUTINE EAS2PP - INPUT A PROCESS ANEM AND UIC
;CALL	PUSHJ	P,EAS2PP
;RETURN	CPOPJ

EAS2PP::			;ENTRY
	PUSHJ	P,EAS2SX	;GET THE SIXBIT NAME
	PUSH	P,T1		;SAVE
	SETZ	T2,		;CLEAR THE PPN WORD
	CAIE	T3,'['		;DOES A PPN FOLLOW
	PJRST	TPOPJ##		;NO EXIT T1=NAME T2=PPN
	PUSHJ	P,EAS2OC	;GET THE PROJECT NUMBER
	TLNE	T1,-1		;LESS THAN 7 DIGITS ?
	  SETZ	T1,		;ILLEGAL SET TO ZERO
	PUSH	P,T1		;SAVE THE PROJECT NUMBER
	PUSHJ	P,EAS2OC	;GET THE PROGRAMMER NUMBER
	TLNE	T1,-1		;LESS THAN 7 DIGITS ?
	  SETZ	T1,		;ILLEGAL
	HRL	T1,(P)		;GET THE PROGRAMMER NUMBER BACK
	MOVE	T2,T1		;COPY TO T2
	POP	P,(P)		;REMOVE SCRATCH FROM THE STACK
	PJRST	TPOPJ##		;EXIT T1=NAME T2=PPN
SUBTTL NTFSEC - ONCE A SECOND CODE

;CALLED ONCE A SECOND WITH J SET FOR EACH FEK
;
NTFSEC::PUSHJ	P,SAVJW##	;SAVE J AND W ETC
	SKIPN	T1,FEKBJN(J)	;SEE IF SOME NODE OWNS THE BOOT
	JRST	NTFSE1		;  IF NOT BOOT-STRAPING, GO CHECK REST OF FEK
	HLRZ	T1,T1		;GET THE TIMER VALUE
	SOJLE	T1,[HRRZ T1,FEKBJN(J)	;IF TIMER RAN OUT, GET JOB NUMBER
		PUSHJ	P,EWAKE##	;WAKE THE USER
		SETZM	FEKBJN(J)	;RELINQUISH OWNERSHIP OF STC
		JRST	NTFSE1]		;AND GO CHECK FOR STALE MESSAGES
	HRLM	T1,FEKBJN(J)	;STORE THE NEW TIMER VALUE
NTFSE1:	SKIPN	T1,FEKICT(J)	;SEE IF THERE ARE ANY INPUT MSGS QUEUED
	JRST	NTFSE2		;  IF NO INPUT STC MSGS, CHECK REST OF FEK
	HLRZ	T1,T1		;GET JUST THE TIMER VALUE
	SOJLE	T1,[PUSH P,J		;IF THE MESSAGE TIMES OUT, SAVE FEK
		HRRZ	J,FEKICT(J)	;GET J := STC MESSAGE
		PUSHJ	P,GIVSTC	;FREE THE STC MESSAGE
		POP	P,J		;GET THE FEK ADDRESS BACK
		SETZM	FEKICT(J)	;CLEAR THE (NOW FREED) STC MSG POINTER
		JRST	NTFSE2]		;AND CONTINUE WITH OTHER CHECKING
	HRLM	T1,FEKICT(J)	;STORE THE UPDATED COUNTER
NTFSE2:	HLLZ	S,FEKBLK(J)	;SET UP THE FEK'S FLAGS
	JUMPGE	S,CPOPJ		;IF IT'S NOT ONLINE, DON'T CHECK ANY FARTHER
	TLNN	S,FK.NID	;NODE ID SENT
	PUSHJ	P,NCSNID	;NO, SEND IT
	  JFCL			;ERROR NO CORE (SEND NEXT TIME)
	SKIPG	FEKIAD(J)	;IS THERE AN INPUT PCB?
	PUSHJ	P,NETRDD	;IF NO INPUT PCB, TRY TO SET UP READ REQUEST
	SKIPG	FEKOCT(J)	;IS THERE AN OUTPUT PCB BEING SERVICED?
	POPJ	P,		; IF NOT, THEN DON'T DO HUNG CHECK.
	AOS	T1,FEKHTM(J)	;INCREMENT THE TIME THAT THIS PCB HAS TAKEN.
	CAIG	T1,^D5		;HAS THIS PCB TAKEN MORE THAN 5 SECONDS?
	POPJ	P,		; IF NOT, THEN IT'S POSSIBLY ALL RIGHT

;HERE IF FEK HAS TIMED OUT.  (IE. MORE THAN 5 SECONDS TO SEND LAST MESSAGE)

	MOVEI	T1,FF.CRS	;ASK THIS FEK TO "CRASH" ITSELF
	XCT	FEKDSP(J)	;CALL THE FRONT-END SERVICE ROUTINE
	POPJ	P,		;AND RETURN


;NETSEC	THIS ROUTINE IS CALLED ONCE A SECOND FROM CLOCK1
NETSEC::
IFN FTMP,<SKPCPU (1)>		;ONLY DO SECOND STUFF ON THE BOOT CPU
	SETOM	NTQSEC		;SIGNAL THE SECOND
	POPJ	P,		;LET JIFFY CODE CALL NETSCN
SUBTTL NETCTC - CALL ON RESET OR ^C^C AND NOT CCON/CON
NETCTC::			;CALLED BY UUOCON
	NETDBJ			;GET THE INTERLOCK TO DO THIS.
	PUSHJ	P,SAVJW##	;SAVE J AND W
;CHECK FOR REMOTE DIALER IN USE
	CAMN	J,DIALJB	;DOES THIS JOB HAVE THE [NETWORK] DIALER?
	SETZM	DIALDB		;YES, CLEAR THE DIALER


;CLEAR THE STATION CONTROL DEVICE IF THERE
	MOVEI	W,NETNDB##	;GET THE START ON THE NDB CHAIN
NETCT1:	SKIPN	T1,NDBSTC(W)	;IS STATION CONTROL BUSY
	JRST	NETCT2		;NO, CONTINUE
	HRRZS	T1		;GET THE JOB NUMBER FOR COMPARE
	CAIN	T1,(J)		;DOES THIS JOB HAVE THE DEVICE
	SETZM	NDBSTC(W)	;YES, CLEAR THE DEVICE
NETCT2:	HRRZ	W,NDBNNM(W)	;GET THE NEXT STATION POINTER
	JUMPN	W,NETCT1	;CONTINUE UNLESS THE END OF NDB

;HERE TO CHECK FOR STATION CONTROL IN USE ON ANY FEKS
IFN FTKS10,<			;ONLY THE KS-10 HAS SMART FEKS...
	MOVEI	W,FEKFST##	;GET THE ADDRESS OF THE FIRST FEK
	CAIA			;SKIP INTO THE LOOP
NETCT3:	HRRZ	W,FEKBLK(W)	;ADVANCE TO THE NEXT FEK
	JUMPE	W,NETCT4	;IF NO MORE FEKS, GO CHECK FOR OTHER THINGS
	SKIPN	T1,FEKBJN(W)	;SEE IF THIS FEK'S STC IS IN USE
	JRST	NETCT3		;IF NO ONE IS USING IT, GO CHECK NEXT FEK
	HRRZ	T1,T1		;CLEAR OUT THE TIMER VALUE
	CAIN	T1,(J)		;ARE WE THE ONE USING THE STC?
	SETZM	FEKBJN(W)	;  IF WE'RE USING IT, RELEASE IT
	JRST	NETCT3		;GO CHECK THE REST...
>;END IFN FTKS10

;HERE TO CHECK FOR PENDING CONNECT/DISCONNECT MESSAGES
NETCT4:	PUSHJ	P,FNDPDS##	;FIND THE PDB FOR THE JOB
	PUSH	P,F		;SAVE THE DDB POINTER
	HLRZ	F,.PDNET##(W)	;GET THE POSSIBLE DDB POINTER
	JUMPE	F,NETCT5	;NONE
	HRRZS	.PDNET##(W)	;CLEAR THE POINTER
	MOVE	T1,DEVMOD(F)	;GET DEVICE'S DV??? FLAGS
	TRNE	T1,ASSPRG	;IS DEVICE OPEN/INITED?
	JRST	NETCT5		;YES, RELEASE SHOULD GET IT LATER
	TLNE	T1,DVTTY	;IS DEVICE A TTY?
	JRST	[PUSHJ	P,TTYKIL##	;YES, THEN USE A DIFFERENT
		JRST	NETCT5]		; ROUTINE TO FREE THE DDB
	MOVE	S,DEVIOS(F)	;IF THE DEVICE NEVER GOT CONNECTED
	TLNN	S,IOSCON	;OR IS BEING DISCONNECTED,
	JRST	[MOVSI	S,IOSZAP	;THEN SET IOSZAP. THIS CAUSES ALL
		IORB	S,DEVIOS(F)	;RIGHT THINGS TO HAPPEN AS MSGS COME IN
		JRST	NETCT5]		;RETURN TO MAIN LINE
	PUSHJ	P,ZAPNE0	;IF CONNECTED, SEND A DISCONNECT
NETCT5:	JRST	FPOPJ##		;RESTORE F AND EXIT
;NDBCHK ROUTINE TO VERIFY THAT W DOES NOT POINT AT NETNDB.  (THIS
;  ROUTINE CHECKS TO MAKE SURE THAT WE AREN'T TALKING TO OURSELF.)
;CALL	PUSHJ	P,NDBCHK
;RETURN	CPOPJ			;OR STOPCD
;
NDBCHK:	PUSH	P,T1		;SAVE THIS FOR A BIT
	MOVEI	T1,NETNDB##	;GET THE ADDRESS OF NETNDB
	CAIN	T1,(W)		;MAKE SURE RH(W) DOESN'T POINT AT IT
	STOPCD	.,STOP,ANFTLK,	;++ TALKING TO OURSELVES
	PJRST	TPOPJ		;RESTORE T1 AND RETURN


;FRCNLD	ROUTINE TO START UP NETLDR
;CALL	PUSHJ	P,FRCNLD
;RETURN	CPOPJ			;STARTS NETLDR ONLY IF NOT DEBUGGING

FRCNLD:	MOVE	T1,STATES##	;BEFORE WE START UP NETLDR, MAKE SURE
	TRNN	T1,ST.DDL	;  OPR SAYS IT'S OK.  IF NO NETLDR,
	SKIPGE	DEBUGF		;  OR WE'RE DEBUGGING THIS MESS
	POPJ	P,		;DON'T RUN NETLDR
	PUSH	P,U		;FIRST WE NEED AN LDB, SO SAVE POSSIBLE PCB
	PUSH	P,W		;WE ALSO MUST SET UP "W" FOR GETLDB
	SETZ	T1,		;SIGNIFY THAT THERE'S NO REMOTE INVOLVED
	MOVEI	T3,ERRDSP##	;SET UP NULL ISR DISPATCH
	PUSHJ	P,GETLDB##	;GET A FREE LDB
	  JRST	FRCNL1		;IF NO FREE LDB'S JUST IGNORE THE REQUEST
	MOVEI	T1,M.AIDL##	;ANF-10 IDLE MAX
	PUSHJ	P,SCNADT##	;START TIMER LOGIC ON LINE
	MOVEI	T1,TTFCXL##	;GET THE ".NETLD" FORCE COMMAND INDEX
	PUSHJ	P,TTFORC##	;FORCE IT ON THE TERMINAL
	PUSHJ	P,FRELDB##	;*** KROCK *** MUST GET THIS ONTO FRCLIN ***
FRCNL1:	POP	P,W		;RESTORE ANY NDB POINTER
	PJRST	UPOPJ##		;RETURN.  NETLDR WILL START UP SOON.
;NETWRC	ROUTINE TO FINISH OFF A MESSAGE STARTED BY NCMHDR & FRIENDS
;CALL	P3 := "CNT" OF THE CONTROL MESSAGE
;	(P) := A BYTE POINTER TO THE "CNT" FIELD IN THE MESSAGE
;	T1 := RETURN ADDRESS
;RETURN	JRST	(T1)		;VIA A CPOPJ FROM NETWRT
;
NETWRC::			;HERE TO SEND A CONTROL MESSAGE
	EXCH	T1,(P)		;GET THE POINTER TO "CNT" (SAVE RETURN ADDR)
	DPB	P3,T1		;STORE THE COUNT
	ADDM	P3,PCBCTR(U)	;UPDATE THE PCB'S LENGTH
	PJRST	NETWRT		;SEND THE MESSAGE
SUBTTL NETWRT - SEND A MESSAGE TO THE FRONT END KONTROLLER FEK

;SUBROUTINE NETWRT - SEND A MESSAGE
;CALL	MOVEI	U,PCB
;	PUSHJ	P,NETWRT	;CALLED AT INTERRUPT OR UUO LEVEL
;RETURN	CPOPJ			;ALWAYS

NETWSR:	AOS	(P)		;SKIP RETURN
NETWRT::			;ENTRY
	NTDBUG			;VERIFY THE INTERLOCK
	PUSHJ	P,SAVJW##	;SAVE J/W
	HRRZ	T1,PCBNNM(U)	;GET THE NODE NUMBER
	PUSHJ	P,SRCNDB	;SET UP W FROM THE NODE NUMBER
	  S0JRST	RMVPCB	;NODE WENT AWAY, DELETE PCB
	HRRZ	J,NDBFEK(W)	;GET THE FEK POINTER
	MOVE	T4,PCBPTR(U)	;GET ADDRESS OF MESSAGE
	HRLI	T4,(POINT 8,0,7);POINT TO NCT
	LDB	T1,T4		; AND GET IT
	ANDI	T1,NCT.TP	;ISOLATE MESSAGE TYPE
	AOS	NCLXTP(T1)	;COUNT IT
	JUMPN	T1,NETWR2	;WE'RE DONE IF THIS IS UNNUMBERED CONTROL
	ADD	T4,[<POINT 8,1,15>-<POINT 8,0,7>]	;POINT TO DLA
	LDB	T1,T4		; AND GET IT
	JUMPN	T1,NETWR1	;NON-ZERO MEANS DATA MESSAGE
	ILDB	T1,T4		;GET THE COUNT BYTE
	TRNE	T1,200		;  AND IF IT'S EXTENSIBLE
	JRST	.-2		;  KEEP READING
	ILDB	T1,T4		;GET NUMBERED MESSAGE TYPE
	CAIG	T1,NC.MAX	;IN RANGE?
	AOS	NCLXMT(T1)	;YES, COUNT IT
	JRST	NETWR2		;ALL DONE, OKAY TO SEND
NETWR1:	MOVE	T1,PCBCTR(U)	;GET LENGTH OF ENTIRE MESSAGE
	ADD	T1,PCBCT2(U)	;INCLUDING SECONDARY (USER) DATA, IF ANY
	SUBI	T1,6		;DISCARD PROTOCOL OVERHEAD
	CAIGE	T1,1_<NETLNH-1>	; IN RANGE OF TABLE?
	JFFO	T1,.+2		;YES, GET APPROPRIATE RANGE
	TDZA	T1,T1		;OUT OF RANGE, INCREMENT ENTRY 0
	MOVNI	T1,-^D36(T2)	;MAKE ASCENDING ENTRIES MEAN LONGER LENGTHS
	AOS	NCLXDL(T1)	; AND RECORD IT

NETWR2:	NETOFF			;TURN OF THE PI SYS
				;ASSIGN MESAGE NUMBERS HERE
;NCA
	MOVE	T4,PCBPTR(U)	;GET THE OUTPUT POINTER
	HRLI	T4,(POINT 8,0,31)  ;POINT TO THE NCA/NCN FIELDS
	LDB	T1,NDBLMP	;GET THE LAST MESSAGE PROCESSED
	DPB	T1,NDBLAS	;SAVE AS LAST ACK
	DPB	T1,T4		;STORE THE ACK NUMBER
;	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,<PUSHJ P,PCBMRK>	;UPDATE THE CACHE SWEEP SERIAL IN THE PCB

	;DROP THRU INTO FRCWRT
	;DROP THRU FROM NETWRT  (WITH NETOFF)

FRCWRT:				;PCBMRK MUST HAVE BEEN CALLED
IFN PARANOID&P$PCB,<		;IF CHECKING PCB'S
	MOVE	T1,[SIXBIT /PCBTAG/] ;GET THE PCB IDENTIFIER,
	CAME	T1,PCBTAG(U)	; AND MAKE SURE WE HAVE A PCB.
	STOPCD	.,STOP,ANFPCV,	;++ PCB NOT VALID
>
	HLLZS	PCBBLK(U)	;MAKE SURE WE ONLY GOT 1 PCB

	SETZ	T3,		;CLEAR "CHECK" COUNTER
	MOVEI	T1,FEKOAD-PCBBLK(J) ;GET ADDRESS OF OUTPUT LIST
	CAIA			;SKIP INTO LOOP
FRCWR1:	MOVEI	T1,(T2)		;COPY THE PCB ADDRESS
	HRRZ	T2,PCBBLK(T1)	;GET ADDRESS OF  "NEXT" PCB
	SKIPE	T2		;IF NO NEXT PCB, SKIP OUT OF LOOP
	AOJA	T3,FRCWR1	;COUNT THE PCB AND KEEP LOOPING

	CAME	T3,FEKOCT(J)	;MAKE SURE THAT THE COUNT IS RIGHT
	STOPCD	.,STOP,ANFFEK,	;++ FEK BAD, FEKOAD AND FEKOCT IN CONFLICT

	HRRM	U,PCBBLK(T1)	;QUEUE THIS PCB ON THE "END" OF THE LIST
	AOS	T3,FEKOCT(J)	;COUNT THIS PCB.

	NETON			;QUEUES ARE CONSISTENT. INTS OK NOW

	MOVEI	T1,FF.WRT	;GET "FEKWRT" FUNCTION CODE AND CALL KONTROLLER
	XCT	FEKDSP(J)	;  TO SET UP A WRITE REQUEST
	POPJ	P,		;RETURN WITH MESSAGE ON ITS WAY
;ENQPCB	THIS ROUTINE IS USED TO INSERT A PCB INTO A QUEUE SUCH
;	AS NDBQUE.  THE QUEUES ARE MAINTAINED WITH ALL UN-NUMBERED
;	MESSAGES FIRST FOLLOWED BY ALL NUMBERED MESSAGES IN ASCENDING
;	ORDER.
;CALL	T1 := BYTE POINTER TO BEGINNING OF QUEUE
;	U := ADDR OF PCB TO ENQUEUE
;RETURN	CPOPJ

ENQPCB:	LDB	T2,T1		;GET ADDRESS OF FIRST PCB ON LIST
	JUMPE	T2,ENQPC3	;IF EMPTY, THEN PUT THIS FIRST
	MOVSI	T3,PCB.NM	;GET THE "THIS IS A NUMBERED MESSAGE" FLAG
	TDNE	T3,PCBBLK(U)	;IF THIS MSG IS NUMBERED,
	JRST	ENQPC2		;   THEN USE DIFFERENT ROUTINE
	TDNE	T3,PCBBLK(T2)	;IF FIRST MESSAGE IN QUEUE IS NUMBERED,
	JRST	ENQPC3		;   THEN SPLICE THIS IN AT BEGINNING
ENQPC1:	MOVSI	T1,(POINT 18,0,35)  ;ELSE BUILD A BYTE POINTER,
	HRRI	T1,(T2)		;AND RECURSE
	PJRST	ENQPCB		;DOWN THE LIST

ENQPC2:	;HERE FOR NUMBERED MESSAGES
	TDNN	T3,PCBBLK(T2)	;IF FIRST MESSAGE IS UNNUMBERED,
	JRST	ENQPC1		;THEN RECURSE ONWARD
	PUSH	P,U		;SAVE U, WE NEED IT FOR A TEMP
	LDB	T3,PCBMSN	;GET OUR MESSAGE NUMBER
	MOVE	U,T2		;COPY OTHER POINTER TO MAKE -
	LDB	U,PCBMSN	;     THIS LDB WORK RIGHT
	SUB	T3,U		;COMPARE THE TWO MESSAGE NUMBERS,
	POP	P,U		;GET OUR PCB PTR BACK
	TRNN	T3,200		;SKIP IF OUR PCB .LT. FIRST.
	JRST	ENQPC1		;OUR PCB .GT. FIRST,  RECURSE ON
ENQPC3:	HRRM	T2,PCBBLK(U)	;HERE TO INSERT PCB. MAKE LINK ONWARD,
	DPB	U,T1		;AND LINK US IN.
	POPJ	P,		;AND THAT'S ALL



;ENQNDB	THIS ROUTINE QUEUES AN NDB FOR SERVICE ON OR ABOUT THE
;	NEXT CLOCK TICK.  THE QUEUE IS NOT ORDERED, AS ALL NDB'S
;	IN THE QUEUE ARE PROCESSED.  THE HEAD OF THE QUEUE IS
;	CONTAINED IN THE LOCATION "NTQNDB".  THE BIT ".NDBNQ"
;	INDICATES THAT THE NDB HAS ALREADY BEEN QUEUED.
;CALL	W := ADDR OF NDB
;RETURN	CPOPJ

ENQNDB:	NETOFF			;PROTECT OURSELVES FROM FEK'S
	MOVSI	T1,NDB.NQ	;GET THE "THIS NDB IS QUEUED" BIT
	TDOE	T1,NDBFLG(W)	;SEE IF NDB HAS ALREADY BEEN QUEUED
	JRST	NTONPJ		;IF SO, THEN EXIT NOW
	HRR	T1,NTQNDB	;OTHERWISE ENQUEUE THIS ONE BY GETTING
	MOVEM	T1,NDBFLG(W)	;THE CURRENT HEADER AND SAVEING IT HERE
	HRRZM	W,NTQNDB	;AND MAKING THE START OF THE QUEUE US.
	JRST	NTONPJ		;ALL DONE, TURN THE PI'S BACK ON
SUBTTL NETHIB/NETWAK - HIBERNATE AND SLEEP ROUTINE FOR .CPJOB ON THE NET
;SUBROUTINE NETHIB - PUT THE JOB IN THE HIBER STATE
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NETHIB
;RETURN	CPOPJ			;WHEN AWAKEN BY NETWAK

NETHIC:	SKIPA	T1,[EXP EV.NTC]	;CONNECT WAIT (NO ^C ALLOWED)
NETHIB::MOVEI	T1,EV.NET	;GET REASON FOR EVENT WAKE
	SKIPN	F		;MUST HAVE A DDB
	STOPCD	CPOPJ##,STOP,FFU,;++F FOULED UP
	NTSAVE			;RETURN 'NT' FOR REST OF THIS ROUTINE
	LDB	J,PJOBN##	;GET THE JOB NUMBER
	PUSHJ	P,FNDPDS##	;GET THE PDB
	HRLM	F,.PDNET##(W)	;STORE THE DDB POINTER IN CASE OF RESET(^C^C)
	PUSHJ	P,ESLEEP##	;WAIT
	HRRZ	W,JBTPDB##(J)	;GET THE PDB BACK
	HRRZS	.PDNET##(W)	;CLEAR THE POINTER
	POPJ	P,		;YES, EXIT TO CALLER


;SUBROUTINE NETWAK - WAKE THE JOB UP PUT TO SLEEP BY NETHIB
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NETWAK
;RETURN	CPOPJ			;JOB(PC) WILL BE @ NETHIB

NETWAK::PUSHJ	P,SAVJW##	;SAVE J AND W
	LDB	T1,PJOBN##	;GET THE JOB NUMBER
	PJRST	EWAKE##		;WAKE THE JOB

;SUBROUTINE NETSLP - PUT THE JOB TO SLEEP FOR 2 SECONDS
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NETSLP
;RETURN	CPOPJ			;AFTER 2 SECOND

NETSLP::PUSHJ	P,SAVT##	;SAVE THE T'S DON'T KNOW WHO IS CALLING
	NTSAVE			;RETURN 'NT' FOR REST OF THIS ROUTINE
	LDB	J,PJOBN##	;GET THE JOB NUMBER
	MOVEI	T1,2		;TWO SECONDS
	PUSHJ	P,SLEEPF##	;SLEEP
	  JFCL			;MAYBE
	POPJ	P,		;RETURN

;*** FOOTNOTE ***

COMMENT \

     Note that both these routines clobber "J" and "W".  In general
these routines are called with contents(J) pointing to a FEK, and
contents(W) pointing to an NDB.  Since these pointers would no longer
be valid (the FEK or node might have crashed) these registers are
intentionally clobbered.

\
;SUBROUTINE INCTBD - RECORD AND DISCARD A BAD INPUT MESSAGE
;CALL	MOVEI	U,PCB
;	MOVEI	J,FEK
; 	MOVEI	T1,.
;	PUSHJ	P,INCTBD	;USUALLY PJSP	T1,INCTBD
;RETURN CPOPJ

INCTBP:	HRRZ	T1,(P)		;GET PC FOR THOSE WHO PUSHJ
INCTBD::AOS	%NTBAD		;COUNT THIS MESSAGE
	HRLI	U,(T1)		;KEEP PC OF FINDER IN LH OF %NTBLC
	EXCH	U,%NTBLC	;SAVE, GET LAST ONE
	S0JRST	RMVPCB		;FLUSH THE PCB AND RETURN

;SUBROUTINE NETHRU - SEND A ROUTE THROUGH MESSAGE
;CALL	MOVEI	U,PCB
;	MOVE	T1,NODE NUMBER OF DESTINATION
;	JRST	NETHRU
;RETURN	CPOPJ

NETHRU:				;ENTRY
	PUSH	P,J		;SAVE THE INPUT FEK
	PUSHJ	P,SRCNDB	;FIND THE NODE TO SEND THE MESSAGE
	  JSP	T1,[POP P,J	;RESTORE THE INPUT FEK
		JRST INCTBD]	;ILLEGAL MESSAGE
	HRRZ	J,NDBFEK(W)	;GET THE OUTPUT FEK
IFN FTKL10,<PUSHJ P,PCBMRK>	;MSG WAS LOOKED AT, CLEAR VALID BIT
	NETOFF
	PUSHJ	P,FRCWRT	;SEND THE MESSAGE

	JRST	JPOPJ##		;RESTORE THE INPUT FEK AND RETURN
;SUBROUTINE NETRDD - SET UP A READ REQUEST TO THE FRONT END
;CALL	MOVE	J,FEK
;	PUSHJ	P,NETRDD
;RETURN	CPOPJ

NETRDD:	SETZ	W,		;CLEAR THE NODE POINTER
	SKIPE	FEKIAD(J)	;CURSORY CHECK TO SEE IF INPUT BUFFER POSTED
	POPJ	P,		;IF SO, DON'T GO THROUGH THE HAIR

IFN FTENET,<
	MOVE	T1,FEKBLK(J)	;GET FEK FLAGS
	TLNN	T1,FK.ETH	;ETHERNET FEK?
	JRST	NETRD1		;NO, NORMAL DEVICE FEK
	TLNN	T1,FK.ETM	;ETHERNET MASTER FEK?
	POPJ	P,		;NO, SLAVE FEKS DON'T DO INPUT
	MOVE	T1,FEKNIQ(J)	;GET COUNT OF INPUT PCBS ALREADY QUEUED
	CAIL	T1,4		;GOT PLENTY?
	POPJ	P,		;YES, DON'T QUEUE UP TOO MANY
> ;END IFN FTENET

;ALLOCATE AN INPUT PCB

NETRD1:	MOVEI	T1,MSGMAW##	;BUFFER SIZE THAT IS THE LARGEST PCB
	PUSHJ	P,MKUPCB	;GET THE BUFFER SPACE
	  POPJ	P,		;IF NO CORE, JUST EXIT (WILL TRY AGAIN LATER)
	MOVEI	T1,MSGMAX##	;GET, AND SET THE NUMBER OF BYTES
	MOVEM	T1,PCBCTR(U)	;  AVAILABLE IN THE PCB.

;NOW QUEUE THE INPUT REQUEST

	NETOFF			;WE MUST WORRY ABOUT RACES AGAIN.
	SKIPE	FEKIAD(J)	;MAKE SURE WE DIDN'T GET BEAT.  IF SOMEONE SET
	JRST	[NETON		;  A READ REQUEST JUST NOW, TURN INTERRUPTS ON
		 PJRST RMVPCB]	;  RETURN THE BUFFER AND EXIT
	HRRZM	U,FEKIAD(J)	;POST THE INPUT BUFFER
	NETON			;RE-ENABLE INTERRUPTS WITH BUFFER SET UP

;KICK THE FEK (POST READ REQUEST)

	MOVEI	T1,FF.RDD	;GET THE READ-REQUEST FUNCTION CODE
	XCT	FEKDSP(J)	;  AND CALL THE FRONT END KONTROLLER
IFE FTENET,<POPJ P,>		;ALL DONE
IFN FTENET,<JRST NETRDD>	;SEE IF NEED MORE BUFFERS
SUBTTL	FEKINT -- ENTRY POINT TO NETSER FROM FRONT END'S
;CALL	J  := FEK ADDRESS
;	T1 := FUNCTION CODE
;RETURN	CPOPJ		;CALLING ROUTINE MUST DISMISS THE INTERRUPT

FEKINT::TRNN	J,-1		;MAKE SURE FEK IS SET UP
	STOPCD	.,STOP,ANFZFK,	;++ ZERO FEK POINTER
	CAIL	T1,FI.RDD	;RANGE CHECK THE FUNCTION CODE
	CAILE	T1,FI.IDN	;  (BETWEEN 0 & 4)
	STOPCD	.,STOP,ANFIFC,	;++ ILLEGAL FEKINT FUNCTION CODE
	JRST	@.+1(T1)	;DISPATCH TO APPROPRIATE ROUTINE
	IFIW	INPINT		;INPUT DONE INTERRUPT (VIA FEKIAD)
	IFIW	OUTINT		;OUTPUT DONE INTERRUPT
	IFIW	STCINT		;WE JUST GOT A BOOT MESSAGE IN "U"
	IFIW	KONDWN		;FRONT END JUST DIED
	IFIW	INPIDN		;INPUT DONE INTERRUPT (VIA FEKIDN)
;HERE WHEN A KONTROLLER GOES DOWN
KONDWN:
	PUSHJ	P,CLNFEK	;CLEAN OUT THE FEK
	SETOM	NTQRCT		;TELL NETSCN TO RECOMPUTE THE TOPOLOGY
	PJRST	NETQUE		;AND GO DO IT.
SUBTTL OUTINT - OUTPUT INTERRUPT PROCESSOR FOR ALL FEK'S
;OUTINT	ROUTINE TO HANDLE THE OUTPUT DONE INTERRUPT FROM FEK'S
;CALL	J := FEK ADDRESS
;RETURN	CPOPJ

OUTINT:	PUSH	P,U		;SAVE U  (THE FRONT END MAY CARE...)
	SETZ	U,		;GET A ZERO
	EXCH	U,FEKODN(J)	;GET THE PCB, CLEAR OUTPUT-DONE
	SKIPN	U		;MAKE SURE WE GOT A PCB
	STOPCD	.,STOP,ANFOUT,	;++ OUTPUT DONE WITH NO PCB

;NOW QUEUE THE PCB FOR NETSCN TO PROCESS (QUEUE TO WAIT FOR ACK)
	NETOFF			;NO INTERRUPTS WHILE HACKING QUEUES
	HRRZ	T1,NTQOUT	;GET THE "REST" OF THE LIST
	HRRM	T1,PCBBLK(U)	;MAKE OUR PCB POINT TO THE "REST"
	HRRZM	U,NTQOUT	;MAKE THE LIST HEAD BE OUR PCB
	NETON			;INTERRUPTS OK. QUEUE IS CONSISTENT

	SETZM	FEKHTM(J)	;CLEAR THE HUNG TIMER. FEK IS RUNNING
	POP	P,U		;GET FEK'S "U" BACK
;	PUSHJ	P,NETQUE	;QUEUE AN INTERRUPT TO NETSCN AND RETURN
				;  (SEE FOOTNOTE)
	POPJ	P,		;RETURN TO INTERRUPT ROUTINE


;*** FOOTNOTE ***

;We don't really need to run netser at this point.  Since we are
;  just going to queue the output PCB and wait for the ACK, we
;  can postpone queueing the PCB until we get an input messsage.
SUBTTL INPINT - INPUT INTERRUPT PROCESSOR FOR ALL FEK'S
;INPINT	ROUTINE TO HANDLE THE INPUT DONE INTERRUPT FROM FEK'S
;CALL	J := FEK ADDRESS
;RETURN	CPOPJ

INPIDN:	PUSH	P,U		;PRESERVE FEK'S "U"
	SETZ	U,		;GET A ZERO
	EXCH	U,FEKIDN(J)	;GET INPUT PCB, CLEAR INPUT-DONE POINTER
	JRST	INPIN0		;CONTINUE WITH INPUT PROCESSING

INPINT:	PUSH	P,U		;PRESERVE FEK'S "U"
	SETZ	U,		;GET A ZERO
	EXCH	U,FEKIAD(J)	;GET INPUT PCB, CLEAR INPUT BUFFER POINTER
INPIN0:	SKIPN	U		;MAKE SURE THERE WAS ONE
	STOPCD	.,STOP,ANFINP,	;++ INPUT DONE, NO INPUT BUFFER
	MOVE	T2,PCBALN(U)	;ALLOCATED (I.E., MAX) SIZE OF PCB DATA BUFFER
	LSH	T2,2		;CONVERT WORD SIZE TO BYTES
	CAMGE	T2,PCBCTR(U)	;IF DATA OVERFLOWED PCB'S BUFFER ALLOCATION
	STOPCD	CPOPJ##,STOP,PBO,  ;++ PCB BUFFER OVERFLOW

;NOW QUEUE THIS PCB AT THE END OF THE INPUT PCB CHAIN

	HRLM	J,PCBFEK(U)	;REMEMBER THE FEK (FOR NODE ID)
	NETOFF			;PROTECTION
	SKIPE	T1,NTQINP	;GET THE CURRENT INPUT PCB QUEUE
	JRST	INPIN2		;AND PUT THIS PCB AT ITS TAIL
	HRRZM	U,NTQINP	;NO INPUT PCBS, THIS IS THE NEW FIRST PCB
	JRST	INPIN4		;INPUT PCB SAFELY QUEUED

INPIN2:	MOVE	T2,T1		;SAVE POTENTIAL NEW LAST PCB
	HRRZ	T1,PCBBLK(T1)	;LINK TO NEXT PCB IN CHAIN
	JUMPN	T1,INPIN2	;KEEP GOING TILL FIND THE END
	HRRM	U,PCBBLK(T2)	;QUEUE THIS PCB AT THE END OF THE INPUT CHAIN
INPIN4:	NETON			;ALL'S CLEAR NOW

;REPLENISH THE FEK AND START UP NETSER INPUT PROCESSING

	PUSHJ	P,NETRDD	;GIVE THE FEK A NEW INPUT BUFFER
	POP	P,U		;GET THE FEK'S "U" BACK
	PUSHJ	P,NETQUE	;AND QUEUE A CALL TO NETSCN
	POPJ	P,		;RETURN
SUBTTL	STCINT -- ROUTINE TO QUEUE INCOMING MAINTENANCE MESSAGES

;STCINT	ROUTINE TO ACCEPT MAINTENANCE MESSAGES FROM THE FRONT ENDS
;CALL	U := POINTER TO STC BLOCK.  NOTHING IN STCNNL(U) IS SET UP

STCINT:	PUSHJ	P,SAVJW##	;FOR CONVENIENCE...  STC USUALLY GOES IN J
	PUSHJ	P,FEK2LN	;GET T1 := THE LINE NUMBER
	HRRZM	T1,STCLNN(U)	;STORE THE LINE NUMBER TO RETURN TO USER
	MOVE	W,J		;GET W := FEK
	MOVE	J,U		;AND J := STC
	SKIPE	FEKICT(W)	;IF THERE IS ALREADY A MESSAGE QUEUED,
	PJRST	GIVSTC		;  FREE THIS ONE IMMEDIATLY AND RETURN.
	HRLI	J,^D10		;GIVE THE MESSAGE A 10 SEC TIME OUT
	MOVEM	J,FEKICT(W)	;STORE THE NEW "XWD TIMER,MSG" IN THE FEK
	SKIPE	T1,FEKBJN(W)	;IF SOMEONE IS WAITING FOR A STC MSG
	JRST	EWAKE##		;  THEN WAKE HIM AND RETURN
	PJRST	FRCNLD		;OTHERWISE WAKE NETLDR TO LOOK AT IT.
;HERE ONCE A TICK TO START TERMINAL OUTPUT FOR ALL LINES IN THE QUEUE
; WE ALSO CHECK EACH OF THE FEKS ON THIS CPU TO SEE IF THEY
; NEED SUCH THINGS DONE AS THEIR INPUT, OR OUTPUT STARTED

NETSTO::SKIPA	J,[FEKFST##]	;GET THE ADDRESS OF THE FIRST FEK
NETST1:	HRRZ	J,FEKBLK(J)	;GET THE ADDRESS OF THE NEXT FEK
	JUMPE	J,NETST5	;IF NO MORE, GO DO REST OF JIFFY CODE
IFN FTMP,<			;IF A SMP SYSTEM, ONLY TIME ON OWNING CPU
	HLRE	T1,FEKUNI(J)	;GET THE CPU NUMBER
	SKIPGE	T1		;IF IT CAN RUN ON ANY CPU,
	MOVE	T1,BOOTCP##	;  CALL IT ONLY ON THE BOOT CPU
	CAME	T1,.CPCPN##	;  AND SKIP IF IT'S US
	JRST	NETST1		;WRONG CPU. GO CHECK NEXT FEK
>
NETST2:	SKIPL	T1,FEKBLK(J)	;GET THE FLAGS AND CHECK FK.ONL
	JRST	NETST1		;EITHER FEK OFFLINE, OR NOT ON THIS CPU
	MOVSI	T2,FK.STO	;GET THE "FEK WANTS OUTPUT STARTED" BIT
	TDNN	T2,FEKBLK(J)	;DOES THE FEK WANT TO GET POKED
	JRST	NETST3		;DOESN'T WANT A KICK
	ANDCAB	T2,FEKBLK(J)	;CLEAR THE BIT NOW (PREVENTS A RACE)
	MOVEI	T1,FF.WRT	;GET THE FEK WRITE FUNCTION CODE
	XCT	FEKDSP(J)	;DISPATCH TO GIVE THE FEK AN OUTPUT POKE
NETST3:	MOVSI	T2,FK.STI	;GET THE "NEEDS INPUT STARTED" BIT
	TDNN	T2,FEKBLK(J)	;DOES THE FEK WANT TO DO INPUT
	JRST	NETST4		;DOESN'T WANT A KICK
	ANDCAB	T2,FEKBLK(J)	;CLEAR NOW BEFORE OTHER CPU MAY SET IT
	MOVEI	T1,FF.RDD	;GET THE "SET UP READ REQUEST" FUNCTION
	XCT	FEKDSP(J)	;TELL THE FEK IT HAS AN INPUT BUFFER TO FILL
NETST4:	JRST	NETST1		;LOOP OVER ALL FEK'S

NETST5:
IFN FTMP,<
	SKPCPU	(0)		;IF NOT ON THE BOOT CPU
	POPJ	P,		;DON'T CALL NETSCN (NO NEED TO)
>
	SETOM	NTQJIF		;SIGNAL A JIFFY.
	PJRST	NETSCN		;  AND GO PROCESS INTERLOCKED CODE
;NETINT	THIS IS WHERE THE NETSER SOFTWARE GENERATED INTERRUPTS ARE
;	HANDLED.  AN INTERRUPT IS GENERATED BY THE SUBROUTINE
;	"NETLKU" ONLY WHEN THE "NT" INTERLOCK IS RELEASED AND THE
;	FLAG "NTRQST" IS SET.  THE PURPOSE OF THIS IS TO LET
;	NETSER CONTINUE WITH SOMETHING THAT HAPPENED AT INTERRUPT
;	LEVEL WHILE SOME JOB HAD THE "NT" INTERLOCK.  THE ONLY
;	REASON THAT THIS INTERRUPT IS USED, RATHER THAN CALLING
;	NETSCN DIRECTLY, IS THAT IT TURNS OFF THE ACCOUNTING
;	METERS.
NETINT::
	SETZM	.CPNTF##	;CLEAR THE INDICATOR
	CONO	PI,CLRNET##	;CLEAR THE INTERRUPT
	PJRST	NETSCN		;AND SEE WHAT NETSCN WANTED.


;NETQUE	THIS IS THE ROUTINE TO CALL TO SET UP A INTERRUPT TO RUN NETSCN

NETQUE::CONO	PI,PI.OFF	;MAKE SURE THAT DIFFERENT PI LEVELS
	SETOM	.CPNTF##	;  DON'T SET THE FLAG AT ONCE
	CONO	PI,PI.ON+REQNET## ;CAUSE THE INTERRUPT
	POPJ	P,		;  AND RETURN
SUBTTL NETSCN -- QUEUE DRIVEN NETSER 'LOOP' LEVEL

;NETSCN -- THIS ROUTINE IS CALLED AT INTERRUPT LEVEL.  IT PROCESSES ENTRIES
;	IN THE FOLLOWING QUEUES
;   A)	NTQRCT	THIS IS A QUEUE OF FEK REQUESTS.
;   B)	NTQJIF	THIS IS A FLAG THAT IS SET ONCE A JIFFY.  IT
;		ENABLES 'JIFFY' PROCESSING. (IE STARTING TERMINAL OUTPUT)
;   C)	NTQOUT	THIS IS A QUEUE OF UN-SORTED OUTPUT MESSAGES. (IE JUST AS
;		THEY ARE GIVEN BACK BY THE FEK'S).  NETSCN REMOVES MESSAGES
;		FROM THIS QUEUE AND APPENDS THEM TO THE RH(NDBQUE) OF THE
;		APPRIOATE NDB WHERE THEY THEN WAIT FOR AN ACK TO FREE THEM.
;   D)	NTQINP	THIS IS A QUEUE OF UN-SORTED INPUT MESSAGES.
;		NTSC.I ROUTES THEM TO THE APPROPRIATE NDB'S
;   E)	NTQNDB	THIS IS A QUEUE OF NDB'S THAT NEED SERVICE.
;		THE PRIMARY REASON THAT AN NDB WILL NEED SERVICE IS
;		THAT IT HAS INPUT DATA TO PROCESS (FROM NTQINP)
;   F)	NTQSEC	THIS IS A FLAG THAT ENABLES THE ONCE PER SECOND
;		PROCESSING (IE REP TIMING ETC)
;
;	NETSCN IS MEANT TO BE CALLED FROM INTERRUPT LEVEL.  IT FIRST CHECKS
;	THE INTERLOCK (THE NT INTERLOCK) TO SEE IF IT CAN PROCESS IMEDIATLY.
;	IF IT CAN, IT SETS THE INTERLOCK BUSY AND GOES TO WORK.  IF IT CAN'T
;	GET THE INTERLOCK IT RETURNS IMMEDIATLY.
;	NOTE!! ANY ONE WHO REQUESTS THE INTERLOCK SHOULD CALL NETSCN UPON
;	       RELEASING IT.  (THIS WILL BE DONE AUTOMATICALLY BY NETLKU)

NETSCN:	SE1ENT			;ENTER SECTION 1
	PUSHJ	P,INTLVL##	;NETSCN MUST RUN AT A INTERRUPT LEVEL.
	  STOPCD .,STOP,ANFNIL,	;++ NOT AT INTERRUPT LEVEL
	NTDBLI			;TRY TO GET THE INTERLOCK
	  JRST	[SETOM NTRQST	; BUT IF WE CAN'T, JUST SET THIS FLAG
		 POPJ P,]	; AND RETURN
	PUSHJ	P,SAVJW##	;SAVE THESE
	PUSHJ	P,SAVE4		; AND THESE

	SKIPE	NTQRCT		;NEED TO RECOMPUTE TOPOLOGY??
	PUSHJ	P,NTSC.R	; IF SO, GO DO IT.
	SKIPE	NTQJIF		;JIFFY?
	PUSHJ	P,NTSC.J	; IF SO ...
	SKIPE	NTQOUT		;OUTPUT DONE INTERRUPTS??
	PUSHJ	P,NTSC.O	; ...
	SKIPE	NTQINP		;INPUT MESSAGES TO PROCESS??
	PUSHJ	P,NTSC.I	; ...
	SKIPE	NTQNDB		;NDB'S THAT WANT SERVICING??
	PUSHJ	P,NTSC.N	; ...
	SKIPE	NTQSEC		;ONCE/SECOND PROCESSING TO DO???
	PUSHJ	P,NTSC.S

	SETZM	NTRQST		;CLEAR THE "REQUEST INTERRUPT" FLAG
	NTDBUI			;RETURN THE 'NT' INTERLOCK
	POPJ	P,		;AND RETURN
;HERE WHEN NETSCN WANTS TO RECOMPUTE THE TOPOLOGY

NTSC.R:	;HERE TO REBUILD OUR NEIGHBORS TABLE
	SETZM	NTQRCT		;CLEAR THE REQUEST
	PUSHJ	P,RCMPTP	;RECOMPUTE THE TOPOLOGY
	PUSHJ	P,SETNBN	;NOW SEND NEIGHBORS MESSAGES TO EVERYONE
	POPJ	P,		;ALL DONE
;HERE FOR ONCE/JIFFY STUFF.  FUNCTIONS PERFORMED ONCE/JIFFY ARE
;   1)	STARTING TERMINALS AND VTMS
;   2)	WE CHECK TO SEE IF WE OWE ANYONE AN ACK AND IF SO, SEND IT

NTSC.J:	SETZM	NTQJIF		;SAY WE PROCESSED THE JIFFY,

	PUSHJ	P,MCRJIF##	;START UP NETWORK MCRS
	PUSHJ	P,VTMJIF##	;START UP NETWORK VIRTUAL TERMINALS

;NOW SEE IF ANY NODES NEED ACK'S SENT

	SKIPN	NTFACK		;DID WE RECEIVE ANY MSGS LAST JIFFY
	JRST	NTSCJ2		;IF NOT, THEN WE DON'T NEED TO SEND ACKS
	SETZM	NTFACK		;CLEAR THE "MAY NEED TO ACK" FLAG

	MOVEI	W,NETNDB	;GET THE ADDRESS OF THE FIRST NDB
NTSCJ1:	HRRZ	W,NDBNNM(W)	;GET THE NEXT NDB (SKIP NETNDB)
	JUMPE	W,NTSCJ2	;EXIT IF WE'VE CHECKED ALL NODES
	LDB	T1,NDBLMP	;GET THE LAST MESSAGE PROCESSED
	LDB	T2,NDBLAS	;  AND THE LAST ACK SEND
	CAMN	T1,T2		;IF WE OWE THE REMOTE AN ACK,
	JRST	NTSCJ1		;DON'T COUNT NEED ACK FLAG
	PUSHJ	P,NCSACK	;  THEN SEND HIM ONE
	  AOS	NTFACK		;IF WE FAIL, SET THE FLAG AGAIN
	JRST	NTSCJ1		;LOOP OVER ALL NDBS

NTSCJ2:	POPJ	P,		;ALL DONE WITH JIFFY PROCESSING
;NTSC.O -- ROUTINE TO PROCESS MESSAGES THAT HAVE BEEN OUTPUT BY FEK'S
;  AFTER A MESSAGE HAS BEEN SENT OUT BY A FEK, IT IS PUT ON THE QUEUE
;  "NTQOUT".  THIS ROUTINE (UNDER PROTECTION OF THE NETSER INTERLOCK)
;  ATTEMPTS TO PROPERLY DISPOSE OF THESE MESSAGES.  THE BASIC ALGORITHM IS:
;	1)  IF IT IS A NUMBERED MESSAGE, THEN WE MUST QUEUE IT ON
;	    RH(NDBQUE) FOR THE APPROPRIATE NDB.  THE MESSAGE WILL
;	    REMAIN HERE UNTIL DESTROYED BY AN ACK FROM THE OTHER
;	    NODE
;	2)  IF IT IS AN UNNUMBERED MESSAGE THE MESSAGE IS DESTROYED
;	    IMMEDIATLY. (NOTE THAT ROUTE-THROUGH MESSAGES ARE
;	    UNNUMBERED, AND HENCE THEY WILL NOT BE PUT ON THE
;	    THE RH(NDBQUE).  THIS IS A CONVENIENT ARTIFACT OF THE MANNER
;	    IN WHICH THE "NUMBERED MESSAGE FLAG" (PCB.NM) IS KEPT)

NTSC.O:	SKIPN	NTQOUT		;ANYTHING IN THE QUEUE
	POPJ	P,		;NOPE.  LOOK ELSE WHERE FOR BUSY WORK

	NETOFF			;NO PI WHILE MODIFYING THE LIST
	HRRZ	U,NTQOUT	;GET THE HEAD OF THE LIST
	HRRZ	T1,PCBBLK(U)	; GET THE REST OF THE LIST
	HRRZM	T1,NTQOUT	; AND MAKE THAT THE LIST
	NETON
	MOVE	T1,PCBCTR(U)	;GET THE FIRST OUTPUT BYTE COUNT
	ADD	T1,PCBCT2(U)	;PLUS THE SECOND BUFFER'S BYTE COUNT
	ADDM	T1,%NTBYO	;COUNT TOTAL BYTES OUTPUT
	MOVE	T1,PCBBLK(U)	;GET THE FLAGS AND POINTER
	HLLZS	PCBBLK(U)	;CLEAR THE LINK POINTER
	JUMPGE	T1,NTSCO1	;UNNUMBERED.  FLUSH IT NOW
	HRRZ	T1,PCBNNM(U)	;GET THE NODE NUMBER
	PUSHJ	P,SRCNDB	;SET UP W FROM THE NODE NUMBER
	  JRST	NTSCO1		;THE NODE WENT AWAY.  FLUSH THE PCB
	CAIN	W,NETNDB##	;IF THIS MESSAGE IS FROM THE NULFEK,
	JRST	[AOS NDBMOM(W)	;  BUMP OUR MAX OUTSTANDING MSG COUNTER
		 JRST NTSCO1]	;  AND FREE THE PCB (WE DON'T ACK OURSELF...)
	LDB	T1,PCBMSN	;GET THE NUMBER OF THE MESSAGE SENT
	DPB	T1,NDBLMS	;SAVE AS LAST SENT
	MOVSI	T1,(POINT 18,0,35) ;HERE GENERATE A POINTER TO THE
	HRRI	T1,NDBQUE(W)	;RH OF NDBQUE(W)
	S0PSHJ	ENQPCB		;AND USE IT TO QUEUE THE PCB.
	JRST	NTSC.O		;AND GO BACK FOR MORE

NTSCO1:	;HERE DELETE THE PCB AND GO BACK
	S0PSHJ	RMVPCB		;DELETE THE PCB
	JRST	NTSC.O		;AND DO NEXT MESSAGE
;NTSC.I -- ROUTINE TO PROCESS INPUT MESSAGES.
;  WHEN MESSAGES COME IN FROM A FEK, THEY ARE ENQUEUED ON THE QUEUE NTQINP.
;  THIS ROUTINE (UNDER PROTECTION OF THE NETSER INTERLOCK) ATTEMPTS TO
;  ROUTE THE MESSAGES TO THE APPROPRIATE QUEUES FOR FURTHER PROCESSING.  THE
;  BASIC ALGORITHM IS:
;	A)  REMOVE THE MESSAGE AND LOOK AT ITS NCT.  IF IT IS
;	    A NODE ID, THEN QUEUE IT ON THE LH(NDBQUE) OF NETNDB.
;	B)  READ THE DESTINATION ADDRESS, AND IF IT'S NOT FOR US,
;	    ATTEMPT TO ROUTE IT THROUGH TO THE DESTINATION NODE.
;	C)  IF IT IS FOR US, THEN QUEUE IT (ORDERED BY MESSAGE NUMBER)
;	    ON THE LH(NDBQUE) OF THE SOURCE NODE'S NDB.  ALSO MARK
;	    THE NDB AS "NEEDING SERVICE".  NTSC.N WILL TAKE OVER FROM
;	    THERE.
;  POINTS TO NOTE:
;	1)  ACK'S ARE NOT PROCESSED AT THIS TIME.  (THERE MAY BE
;	    SOMETHING "WRONG" WITH THE MESSAGE)
;	2)  THIS CODE SETS THE NUMBERED MESSAGE FLAG (PCB.NM) ON
;	    NUMBERED MESSAGES DESTINED FOR US. (BUT NOT ROUTE-THROUGH
;	    MESSAGES)

NTSC.I:	SKIPN	NTQINP		;ANY MORE INPUT??
	POPJ	P,		;NOPE. GO BACK TO NETSCN.
	NETOFF			;PROTECTION
	HRRZ	U,NTQINP	;GET FIRST PCB
	HRRZ	T1,PCBBLK(U)	;GET REST OF LIST
	HRRZM	T1,NTQINP	; AND SAVE IT FOR LATER.
	NETON			;ALL'S CLEAR
	MOVE	T1,PCBCTR(U)	;GET THE INPUT BYTE COUNT,
	ADDM	T1,%NTBYI	;  AND COUNT THESE INPUT BYTES
	MOVSI	T1,PCB.NM	;ASSUME THAT THIS IS AN UN-NUMBERED
	ANDCAM	T1,PCBBLK(U)	;  MESSAGE.  (IN CASE ROUTE-THROUGH)
	HLLZS	PCBBLK(U)	;CLEAR ANY STRAY POINTERS
	SETZB	W,F		;CLEAR THE NDB POINTER,DDB POINTER
	MOVE	P1,PCBPTR(U)	;GET THE INPUT MESSAGE POINTER
	MOVE	P4,PCBCTR(U)	;GET THE INPUT BYTE COUNT
	JUMPLE	P4,NTSCI3	;ZERO LENGTH IS ILLEGAL
;NCT
	PUSHJ	P,EBI2BI	;READ THE FLAGS
	HRLZM	T1,NTFLGS	;SAVE THE FLAGS
	ANDI	T1,NCT.TP	;JUST THE MESSAGE TYPE
	MOVE	T2,NTFLGS	;GET FLAGS BACK
	TLNE	T2,NCT.RH	;ROUTING HEADER PRESENT
	JRST	NTSCI1		;YES,
	CAIE	T1,NCT.ID	;IS THIS AN ID MESSAGE
	JRST	NTSCI3		;NO, BAD MESSAGE

	MOVEI	W,NETNDB	;QUEUE NODE ID AS INPUT FROM OUR NODE
				;  SINCE WE PROBABLY DON'T HAVE AN NDB FOR IT
	PJRST	NTSCI2		;AND NOW ENQUEUE THE MESSAGE FOR NEXT LEVEL
NTSCI1:
;DNA
	PUSHJ	P,EBI2BI	;GET <DNA>
	HLRZ	T2,NDBNNM+NETNDB;GET OUR NODE NUMBER
	CAIE	T1,(T2)		;IS IT FOR US?
	JRST	[S0PSHJ NETHRU	;IN ONE EAR AND OUT THE OTHER
		 JRST NTSC.I]	;  AND CHECK FOR ANY OTHER INPUT
;SNA
	PUSHJ	P,EBI2BI	;YES, GET THE <SNA>
	PUSHJ	P,SRCNDB	;FIND THE NODE BLOCK
	  JRST	NTSCI3		;SOURCE NODE WENT AWAY
;NCA

	ILDB	T1,P1		;GET THE ACK NUMBER
	PUSHJ	P,CHKNCA	;ACK IT NOW TO PREVENT DEADLOCKS
;NCN
	ILDB	T1,P1		;GET THIS MESSAGE NUMBER
	MOVE	T2,NTFLGS	;FLAGS
	TLNE	T2,NCT.TP	;IS THIS A NUMBERED MESSAGE?
	JRST	NTSCI2		;IF NOT, DON'T PLAY WITH IT ANY MORE
	DPB	T1,PCBMSN	;SAVE THE NUMBER IN THE PCB
	MOVSI	T1,PCB.NM	;GET AND
	IORM	T1,PCBBLK(U)	;    SET THE NUMBERED MESSAGE FLAG
NTSCI2:	MOVSI	T1,(POINT 18,0,17)	;BUILD A BYTE POINTER
	HRRI	T1,NDBQUE(W)	;    TO NDBQUE (LH)
	S0PSHJ	ENQPCB		;AND QUEUE THE PCB FOR CLOCK LEVEL
	S0PSHJ	ENQNDB		;MAKE SURE THE NDB GETS SCANNED
	JRST	NTSC.I		;NOW GO BACK AND SEE IF THERE ARE ANY MORE

NTSCI3:	PUSHJ	P,INCTBP	;HERE WE FLUSH A GARBAGE MESSAGE
	JRST	NTSC.I		;SEE IF THERE ARE ANY MORE
;NTSC.N -- ROUTINE TO SERVICE AN NDB.
;  THIS ROUTINE PROCESSES NDB'S THAT HAVE BEEN "QUEUED" ON THE QUEUE
;  "NTQNDB".  THIS ROUTINE PERFORMS THE FOLLOWING SERVICES FOR AN
;  NDB:
;	1)  IT PROCESSED QUEUED INPUT MESSAGES FROM THE RH(NDBQUE)
;	2)  IT SENDS ANY OTHER "SPECIAL" MESSAGES THAT MAY BE
;	    REQUIRED BY THE NODE. (MOST NOTABLY NAK'S)
;  THE PROCESSING OF INPUT MESSAGES (1) CAN BE BROKEN UP INTO THE
;  FOLLOWING PHASES:
;	A)  TAKE THE FIRST MESSAGE OFF OF THE RH(NDBQUE) AND
;	    GET ITS NETWORK CONTROL TYPE (NCT).  IF IT'S A NODE-ID
;	    THEN GO PROCESS IT IMMEDIATLY
;	B)  IF WE ARE NOT CURRENTLY IN CONTACT (START-STACK EXCHANGED)
;	    WITH THE NODE, THROW AWAY ALL MESSAGES EXCEPT START OR
;	    STACK.
;	C)  PERFORM A CURSORY VALIDITY CHECK OF THE MESSAGE AND
;	    PROCESS THE NETWORK CONTROL ACK (NCA) FIELD.
;	D)  IF IT IS AN UNNUMBERED MESSAGE, THEN PROCESS IT
;	    IMMEDIATLY.
;	E)  IF IT IS A NUMBERED MESSAGE, THEN CHECK TO SEE IF IT IS
;	    THE "NEXT" MESSAGE IN SEQUENCE.  IF IT IS
;		1) A REPEATED MESSAGE, THEN DELETE IT
;		2) THE NEXT MESSAGE, THEN PROCESS IT
;		3) AN "OUT OF ORDER" MESSAGE, THEN RE-QUEUE IT ON THE
;		   RH(NDBQUE) AND EXIT PROCESSING INPUT MESSAGES.
;	    NOTE THAT THERE IS NO 100% SAFE WAY TO TELL WEATHER OR
;	    NOT A MESSAGE IS A REPEATED MESSAGE, OR ONE THAT
;	    ARRIVED EARLY.  THE ALGORITHM IMPLEMENTED HERE ASSUMES
;	    THAT MESSAGES UP TO 127 AHEAD OF THE LAST MESSAGE PROCESSED
;	    ARE MESSAGES THAT ARRIVED EARLY, AND ALL OTHERS (EXCEPT THE
;	    "NEXT") ARE REPEATED MESSAGES.

NTSC.N:	SKIPN	T2,NTQNDB	;ANY NDB'S TO PROCESS
	POPJ	P,		;NO. GO CHECK SOMETHING ELSE
	NETOFF			;GUARD AGAINST UNRULY FEK'S
	HRRZ	W,NTQNDB	;GET THE NDB,
	HRRZ	T1,NDBFLG(W)	;GET THE LINK TO NEXT NDB IN QUEUE
	HRRZM	T1,NTQNDB	;SPLICE THIS NDB OUT OF THE QUEUE
	HRLOI	T1,NDB.NQ	;GET THE 'THIS NDB IS QUEUED' BIT
	ANDCAM	T1,NDBFLG(W)	;AND CLEAR IT IN NDBFLG
	NETON			;NTQNDB QUEUE IS CONSISTENT AGAIN
	HRRZ	J,NDBFEK(W)	;SET UP J FOR INCTNK AND NCSSTR (AND OTHERS)
	;DROP THROUGH
	;FROM ABOVE
	;W := NEXT NDB TO PROCESS

NTSCN1:	;HERE TO PROCESS THE NEXT INPUT MESSAGE ON THE LH OF NDBQUE(W)
	NETOFF			;WATCH OUT. A FEK MIGHT PUT ONE ON
	HLRZ	U,NDBQUE(W)	;GET FIRST PCB
	JUMPE	U,[ NETON	;IF NONE, THEN
		    JRST NTSCN5];  SEE WHAT ELSE NEEDS TO BE DONE
	MOVE	T1,PCBBLK(U)	;GET "XWD FLAGS,LINK" FROM PCB
	HRLM	T1,NDBQUE(W)	; AND SPLICE OUT THE PCB
	NETON			;NDBQUE IS CONSISTENT AGAIN.
	HLLZS	PCBBLK(U)	;IT'S NOT POLITE TO POINT. (TO BE SURE!)

;	NOW PROCESS THE PCB

IFN PARANOID&P$EAT,<		;HACK TO EAT EVERY N'TY MESSAGE
	MOVEI	T1,NETNDB##	;SEE IF THIS IF FROM US
	CAIN	T1,(W)		;IF SO, THEN
	JRST	STARVE		;DON'T EAT THE MESSAGE (WE DON'T REP OURSELF)
	SOSG	%TROLL		;HAVE WE COUNTED DOWN YET?
	  PJSP	T1,[PUSHJ P,INCTBD ;IF SO, TOSS THIS MESSAGE
		    MOVEI T1,100   ;RESET THE
		    MOVEM T1,%TROLL;      COUNTER
		    JRST NTSCN1] ;AND GO GET THE NEXT ONE
STARVE:				;HERE IF WE CAN'T EAT THE MSG

>
	MOVE	P4,PCBCTR(U)	;GET THE LENGTH OF THE MESSAGE.
	MOVE	P1,PCBPTR(U)	;GET THE BYTE POINTER
;NCT
	PUSHJ	P,EBI2BI	;GET THE FLAGS
	HRLZM	T1,NTFLGS	;WEVE ALREADY GOT THEM BUT..

	ANDI	T1,NCT.TP	;ISOLATE THE TYPE
	AOS	NCLRTP(T1)	;TALLY
	MOVE	T2,NTFLGS	;GET FLAGS BACK
	TLNE	T2,NCT.RH	;ROUTING INFORMATION PRESENT?
	JRST	NTSCN2		;YES. CONTINUE
	CAIE	T1,NCT.ID	;NO ROUTINE, IS IT A NODE-ID?
	STOPCD	.,STOP,ANFMSQ,	;++ MESSAGE QUEUES ARE SCREWED UP
	S0PSHJ	INCTID		;GO PROCESS THE NODE-ID MESSAGE
	JRST	NTSCN8		;FREE PCB AND GET NEXT MESSAGE
;HERE FROM ABOVE IF THE MESSAGE HAS A ROUTING HEADER (IE ISN'T A NODE-ID)

NTSCN2:	MOVE	T2,NDBFLG(W)	;IS THIS NODE UP AND RUNNING?
	TLNE	T2,NDB.UP	;SKIP IF NOT
	JRST	NTSCN3		;IT IS, CONTINUE PROCESSING
	CAIE	T1,NCT.ST	;SINCE IT'S NOT RUNNING, SEE IF THIS
	CAIN	T1,NCT.SK	;IS A START OR A STACK
	JRST	NTSCN3		;IF IT IS, OK
	TLNE	T2,NDB.SK	;IF WE ARE SENDING STACKS,
	JRST	[PUSHJ P,NODEUP	;THEN ANY MESSAGE GETS US INTO "RUN" STATE
		 JRST NTSCN3]	;NOW GO PROCESS THE MESSAGE
	PUSHJ	P,INCTBP	;IF WE AREN'T RUNNING, SIGNAL BAD MESSAGE,
	JRST	NTSCN1		;AND GO BACK FOR ANOTHER

NTSCN3:				;HERE TO DO A LITTLE MORE CHECKING
;DNA
	PUSHJ	P,EBI2BI	;GET DNA
	HLRZ	T2,NETNDB+NDBNNM;GET OUR NODE NUMBER
	CAIE	T1,(T2)		;BETTER BE THE SAME
	STOPCD	.,STOP,ANFRDN,	;++ ROUTING HEADER HAS BAD DESTINATION NODE
;SNA
	PUSHJ	P,EBI2BI	;GET SOURCE NODE NUMBER
	HLRZ	T2,NDBNNM(W)	;GET THE NODE NUMBER OF THIS NDB
	CAIE	T1,(T2)		;BETTER BE THE SAME
	STOPCD	.,STOP,ANFRSN,	;++ ROUTING HEADER HAS BAD SOURCE NODE
;NCA
	PUSHJ	P,BYT2BI	;GET THE IMPLICIT ACK (ALREADY PROCESSED)
;NCN
	PUSHJ	P,BYT2BI	;GET THE MESSAGE NUMBER AND
	MOVE	T2,NTFLGS	;GET FLAGS BACK
	TLNE	T2,NCT.TP	;SKIP IF IT IS A NUMBERED MESSAGE
	JRST	INCTUN		;IT'S NOT, GO PROCESS UNNUMBERED MESSAGE
	DPB	T1,NDBLMR	;SET LAST MESSAGE RECEIVED
	LDB	T2,NDBLMP	;GET LAST MESSAGE PROCESSED
	ADDI	T2,1		;GET NUMBER OF NEXT MESSAGE TO PROCESS
	ANDI	T2,377		;MAKE SURE IT'S MOD(1000)
	SUBI	T1,(T2)		;IF THIS IS 'NEGATIVE' WE'VE SEEN IT BEFORE
	TRNE	T1,200		;IF THIS MESSAGE HAS BEEN SEEN
	JRST	NTSCN8		; BEFORE, THEN TOSS IT
	JUMPN	T1,NTSCN4	;AN OUT OF ORDER (EARLY) MESSAGE

;DROP THROUGH IF THIS IS THE NEXT MESSAGE TO PROCESS (IE IS IN
;  SEQUENCE)
;HERE FROM ABOVE WITH U POINTING TO THE NEXT MESSAGE TO PROCESS
;  SINCE SOME MESSAGES REQUIRE IMMEDIATE RESPONSE, MAKE SURE THAT WE CAN
;  SEND AT LEAST ONE RESPONSE BEFORE WE PROCESS THE MESSAGE.  I THINK
;  THAT THIS IS TERRIBLE CODE, BUT I'M AT A LOSS AS TO HOW TO
;  HANDLE THIS PROBLEM.

	PUSHJ	P,PCBECK	;MAKE SURE THERE ARE "EMERGENCY"
				;  RESERVES AVAILABLE
	  JRST	NTSCN4		;NO FREE MESSAGES
	JRST	INCTNM		;ALL'S OK. GO PROCESS THE 'NEXT' MESSAGE.
				;  (INCTNM WILL RETURN TO NTSCN8)

;HERE IF A MESSAGE ARRIVES OUT OF ORDER OR FOR SOME REASON (NO CORE
;  OR NO FREE MESSAGE NUMBERS) WE CAN'T PROCESS IT

NTSCN4:	MOVSI	T1,(POINT 18,0,17);CONJURE UP A POINTER TO THE
	HRRI	T1,NDBQUE(W)	;THE LH OF NDBQUE
	S0PSHJ	ENQPCB		;RE-QUEUE THE PCB (BUT NOT THE NDB)
;	PJRST	NTSCN5		;"FINISH" UP WITH ANY SPECIAL MESSAGES
;HERE WHEN WE ARE DONE PROCESSING MESSAGES FROM NDBQUE. (EITHER
;  BECAUSE THERE ARE NO MORE, WE ARE OUT OF CORE, NO FREE MESSAGE
;  NUMBERS, OR THERE ARE OUT OF ORDER MESSAGES).  THIS CODE CHECKS
;  TO SEE IF ANY SPECIAL (ACK, NAK) MESSAGES ARE REQUIRED.


NTSCN5:	MOVEI	T1,NETNDB##	;FIRST CHECK TO MAKE SURE THAT WE AREN'T
	CAIN	T1,(W)		; TRYING TO SEND ANY ACK'S, NAK'S OR REP'S
	JRST	NTSCN7		; TO OURSELF.
	MOVSI	T1,NDB.XN	;GET THE 'NEED TO NAK' FLAG
	TDNN	T1,NDBFLG(W)	;SKIP IF WE DO.
	JRST	NTSCN6		;NOPE. DON'T NEED TO
	PUSHJ	P,NCSNAK	;TRY TO SEND THE NAK
	  JRST	NTSCN6		;WE CAN'T.  TRY LATER
	MOVSI	T1,NDB.XN	;GET THE FLAG BACK
	ANDCAM	T1,NDBFLG(W)	;AND CLEAR IT
NTSCN6:	AOS	NTFACK		;SET FLAG SAYING WE MAY NEED TO ACK IN JIFFY
NTSCN7:	JRST	NTSC.N		;NOW GO DO THE NEXT NODE


;HERE ON RETURNS FROM INCTUN AND INCTNM AS WELL AS FROM VARIOUS
;  PLACES IN NTSC.N.  FLUSH THE PCB (IF ANY) AND ATTEMPT TO PROCESS
;  THE NEXT MESSAGE IN ORDER.

NTSCN8:	S0PSHJ	RMVPCB		;RETURN THE PCB
	AOS	NTFACK		;SIGNAL THAT AN ACK IS PENDING
	JRST	NTSCN1		;GET NEXT ITEM OFF OF LH(NDBQUE)
;HERE FOR ONCE PER SECOND PROCESSING

NTSC.S:	SETZM	NTQSEC		;CLEAR THE SECOND FLAG

;HERE TO SCAN THE NDB'S

	MOVEI	W,NETNDB##	;FIRST NODE TO CHECK
	JRST	NTSCS6		;JUMP IN TO MIDDLE (DON'T DO NETNDB!)

NTSCS2:	HRRZ	T1,NDBFEK(W)	;GET FEK FOR THIS NODE
	HLL	J,NDBFLG(W)	;CARRY THE BITS IN LH(J)
	TLNE	J,NDB.UP	;IS THIS NODE UP
	JRST	NTSCS4		;IF SO, JUMP OFF AND CHECK IT
	LDB	T1,NDBTIM	;GET THE TIMER
	SOJG	T1,NTSCS3	;COUNT IT DOWN.  JUMP IF NOT TIME FOR START
	PUSHJ	P,NCSSSM	;SEND EITHER A START OR STACK
	  TDZA	T1,T1		;START DIDN'T GO. DON'T RESET TIMER
	MOVEI	T1,^D15		;SET TIMER FOR 15 SECONDS
NTSCS3:	DPB	T1,NDBTIM	;PUT THE NEW VALUE BACK FOR NEXT TIME
	JRST	NTSCS8		;GO PROCESS THE NEXT NDB
;HERE NODE IS UP.  CHECK VARIOUS THINGS

NTSCS4:	MOVE	T1,LOCSEC##	;GET A TIMER
	TLNN	J,NDB.CF	;HAVE WE GOT A CONFIG YET?
	TRNE	T1,7		;NO, TIME TO ASK?(EVERY 8 SECONDS)
	CAIA			;YES, OR NOT TIME YET
	PUSHJ	P,NCSRCF	;IF NOT, LETS ASK HIM FOR ONE
	  JFCL			;IF NO CORE, WE'LL GET IT LATER
;NEIGHBORS
	TLNN	J,NDB.NB	;HAS HE HEARD ABOUT OUR FRIENDS
	PUSHJ	P,NCSNBN	;LET'S TELL HIM WHO'S NEXT DOOR
	  JFCL			;BIT DIDN'T GET CLEARED. WE'LL GET IT LATER
;REP CHECK
	HRRZ	T1,NDBQUE(W)	;ANY MESSAGES WAITING FOR AN ACK??
	JUMPE	T1,NTSCS5	;IF NONE,  DON'T REP, BUT CLEAR TIMER
	LDB	T1,NDBTIM	;GET THE NODE'S REP TIMER
	CAIG	T1,^D10		;NO ACKS IN 10 SECONDS??
	AOJA	T1,NTSCS5	;IF IT HASN'T BEEN THAT LONG, JUST MARK TIME
	PUSHJ	P,NCSREP	;OTHERWISE TRY TO SEND THE REP
	  SKIPA	T1,[^D10]	;IF IT DIDN'T GO, THEN DON'T RESET TIMER
	SETZ	T1,		;OTHERWISE SET TIMER TO ZERO
NTSCS5:	DPB	T1,NDBTIM	;STORE TIMER BACK,
;NAK CHECK
	HLRZ	T1,NDBQUE(W)	;GET POINTER TO 'OUT OF ORDER' MESSAGES
	JUMPE	T1,NTSCS6	;IF NONE, THEN THIS CHECK IS COMPLETE
	MOVEI	T2,1		;THIS IS THE COUNT OF QUEUED INPUT MSGS
	HRRZ	T1,PCBBLK(T1)	;STEP TO THE NEXT MESSAGE,
	SKIPE	T1		;SKIP IF THERE ISN'T ONE
	AOJA	T2,.-2		;IF THERE IS ONE, COUNT IT AND RECURSE.
	CAIL	T2,^D5		;ARE THERE MORE THAN 5 MSGS QUEUED?
	PUSHJ	P,NCSNAK	;IF SO, WE PROBABLY LOST ONE, SEND A NAK
	  JFCL			;IGNORE ERROR RETURN

NTSCS6:	PUSHJ	P,STCSEC	;GO TIME OUT STATION CONTROL MSGS FOR
				;  THIS NODE.
NTSCS8:	HRRZ	W,NDBNNM(W)	;STEP TO THE NEXT NODE.
	JUMPN	W,NTSCS2	;IF THERE IS A NEXT NODE, GO PROCESS IT

	PUSHJ	P,PCBSEC	;LET PCB MANAGEMENT CONTROL FREE-LISTS
	PUSHJ	P,TSKSEC##	;CALL TSKSER ONCE/SEC FOR ITS STORAGE MGT
	PUSHJ	P,NMCSEC##	;CALL NETMCR ONCE/SEC SO IT CAN DO ITS AUTO-
				;  DISCONNECT TIMING

	POPJ	P,		;DONE WITH ONCE/SECOND STUFF. BACK TO TOP LEVEL
SUBTTL CHKNCA - CHECK ACK'S FOR MESSAGE SENT BUT BEING HELD
;SUBROUTINE CHKNCA - DELETE MESSAGES THAT HAVE ACK'S
;CALL	MOVEI	W,NDB
;	MOVEI	T1,NCA - MESSAGE ACK NUMBER
;RETURN	CPOPJ

CHKNCA:				;ENTRY
	JUMPE	W,CPOPJ##	;EXIT IN NDB UNDEFINED
	LDB	T2,NDBLMS	;GET THE LAST MESSAGE SENT
	SUB	T2,T1		; AND MAKE SURE THAT WE ARE LESS
	TRNE	T2,200		; THAN IT.  IF NOT, THEN THIS
	POPJ	P,		; IS A VERY OUT-OF-DATE ACK
	LDB	T2,NDBLAP	;GET THE LAST ACK PROCESSED, AND
	SUB	T2,T1		; SEE IF THIS ACK IS .GT. THE OLD
	TRNN	T2,200		;IF "LAP" IS .GE. THIS ACK,
	POPJ	P,		; THEN THIS ACK IS OLD NEWS
	DPB	T1,NDBLAR	;SAVE THE LAST ACK RECEIVED
	PUSH	P,U		;SAVE THE OLD PCB
CHKNC1:	LDB	T2,NDBLAP	;GET THE LAST ACK PROCESSED
	LDB	T1,NDBLAR	;GET LAST ACK RECEIVED
	CAIN	T1,(T2)		;IF LAP = LAR, THEN WE'VE PROCESSED ALL
	JRST	UPOPJ		;  IF PROCESSED ALL, RESTORE "U" AND EXIT
	ADDI	T2,1		;GET NUMBER OF NEXT ACK TO PROCESS
	ANDI	T2,377		;  JUST 8 BITS OF IT PLEASE
	HRRZ	U,NDBQUE(W)	;GET THE NEXT MESSAGE ON THE AWAIT ACK QUEUE
	JUMPE	U,UPOPJ		;  IF NONE, GET OUT WITHOUT UP-INT LAP
	LDB	T1,PCBMSN	;GET THIS MESSAGE'S NUMBER
	CAIE	T1,(T2)		;   AND MAKE SURE IT IS THE ONE WE WANT
	JRST	UPOPJ		;IF NEXT MSG IS AT FEK, GET OUT AND WAIT FOR
				;  IT TO COME BACK (WE MUSTA GOT NAK'ED)
	DPB	T1,NDBLAP	;WE'RE GOING TO FREE THIS MSG, UPDATE LAP
	HRRZ	T1,PCBBLK(U)	;GET ADDRESS OF NEXT MESSAGE ON THE QUEUE
	HRRM	T1,NDBQUE(W)	;  AND MAKE IT THE FIRST
	S0PSHJ	RMVPCB		;FREE THIS MESSAGE
	SETZ	T1,		;GET A ZERO, AND USE IT
	DPB	T1,NDBTIM	;  CLEAR THE REP TIMER.  (THINGS SEEM TO BE OK)
	AOS	NDBMOM(W)	;INDICATE ONE LESS MESSAGE OUTSTANDING
	JRST	CHKNC1		;GO TRY TO PROCESS ANOTHER ACK
SUBTTL PROCESS NEXT MESSAGE

INCTNM:	;HERE TO PROCESS A NUMBERED MESSAGE
	LDB	T1,P1		;GET THIS MESSAGE'S NUMBER BACK AGAIN
	DPB	T1,NDBLMP	;SAVE LAST MESSAGE PROCESSED
INCTUN:	HLRZ	T1,NTFLGS	;GET THE FLAGS
	ANDI	T1,NCT.TP	;GET THE TYPE FIELD
	NTDBUG			;VERIFY THE INTERLOCK


	CAIE	T1,NCT.ID	;IF THIS IS A NODE ID, OR
	CAIN	T1,NCT.DM	; A DATA MESSAGE, THEN WE NEED NOT CHECK
	JRST	INCTU1		; FOR DUPLICATE NODES

	MOVEI	T2,NETNDB##	;IF MSG IS START, STACK ETC.
	CAIE	T2,(W)		; SEE IF NODE HAS THE SAME NUMBER AS US.
	JRST	INCTU1		; IF NOT, THEN EVERYTHING'S OK

	PUSHJ	P,TYPILT	;TELL THE OPERATOR ABOUT THE ILLEGAL TOPOLOGY
	PUSHJ	P,INCTBP	; AND THROW AWAY THE MESSAGE
	JRST	INCTU2		; AND DON'T PROCESS THE MESSAGE.

INCTU1:	HRRZS	U		;CLEAR FLAG BITS SO U CAN BE USED IN SECTION 1
	PUSHJ	P,NCTTBL(T1)	;DISPATCH BY THE MESSAGE TYPE
INCTU2:	JRST	NTSCN8		;GO BACK FOR MORE
	STOPCD	.,STOP,ANFNCT,	;++ NCT PROCESSORS SHOULDN'T SKIP


;NCL MESSAGE TYPE DISPATCH TABLE

NCTTBL:	JRST	INCTDM		;(0) DATA MESSAGE
	JRST	INCTAK		;(1) ACK
	JRST	INCTNK		;(2) NAK
	JRST	INCTRP		;(3) REP
	JRST	INCTST		;(4) START
	JRST	INCTSK		;(5) STACK
	JRST	INCTID		;(6) NODE ID
	JRST	INCTBP		;(7) ILLEGAL INPUT MESSAGE TYPE.  FLUSH
;INCTID	-- ROUTINE TO PROCESS THE NODE ID MESSAGE
;THIS ROUTINE READS THE NODE NUMBER FROM THE NODE ID MESSAGE.
;  IF AN NDB DOES NOT YET EXIST FOR THE NODE ONE IS CREATED AND
;  THE OPTIONAL DATA IS READ INTO IT.  THEN TOPOLOGY IS
;  RECOMPUTED.
;
;MESSAGE =   NCT    NCA    NCN    OPD(NNM   SNM   SID   NIT   [NIS]   NVR)
;SO FAR  = NCT.ID   /$\

INCTID:
	PUSHJ	P,SAVJW##	;WE SET UP A DIFFERENT "W" UNLIKE OTHER MSGS
	HLR	J,PCBFEK(U)	;GET FEK POINTER FROM PCB

IFN PARANOID&P$FEK,<		;MAKE SURE WE DON'T SEND OURSELF A NODE-ID

	HLLZ	T1,FEKBLK(J)	;GET THE FLAGS FOR THE FEK
	TLNE	T1,FK.NUL	;IS THIS A NULL FEK?
	STOPCD	.,STOP,ANFNFK,	;++ THIS IS THE NULL FEK
>
;NCA
	ILDB	T1,P1		;GET THE ACK START
;NCN
	ILDB	T1,P1		;GET THE STARTING MESSAGE NUMBER
;NNM
	PUSHJ	P,EBI2BI	;READ THE NODE NUMBER
	HRRM	T1,FEKNNM(J)	;STORE THE NODE NUMBER FOR THIS FEK
	PUSHJ	P,SRCNDB	;DOES THE NODE EXIST
	  JRST	INCTI5		;NO, NEED TO CREATE AN NDB
	PUSHJ	P,SKOPDD	;YES, SKIP OVER OPD(,SNM,SID) FIELDS
	PUSHJ	P,EBI2BI	;READ NODEID TYPE
	JUMPE	T1,INCTI8	;IF 0/BLANK, THEN NOT BROADCAST NODEID
IFE FTENET,<PJSP T1,INCTBD>	;INVALID NODEID "TYPE" FIELD

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;HERE TO HANDLE PERIODIC BROADCAST NODEID MESSAGE

IFN FTENET,<
	CAIE	T1,NIT.BC	;ONLY OTHER LEGAL VALUE IS BROADCAST
	PJSP	T1,INCTBD	;INVALID NODEID "TYPE" FIELD
	HLLZS	FEKNNM(J)	;ETHERNET MASTER FEK'S DON'T HAVE A NEIGHBOR
	HLRZ	T4,NDBNNM(W)	;GET NODE NUMBER TO MATCH
	SKIPA	T2,J		;"LOCAL" START OF FEK CHAIN
INCTI1:	HRRZ	T2,FEKBLK(T2)	;ADVANCE TO NEXT FEK IN CHAIN
	JUMPE	T2,INCTI6	;NO FEK??? TIME FOR A NEW TOPOLOGY!
	HRRZ	T3,FEKNNM(T2)	;PICK UP NODE NUMBER
	CAIE	T4,(T3)		;DOES THIS FEK BELONG TO THIS NODE?
	JRST	INCTI1		;NO, KEEP LOOKING
	MOVE	T3,FEKBLK(T2)	;GET FEK FLAGS
	TLNE	T3,FK.ETH	;ETHERNET FEK?
	CAME	J,FEKNIF(T2)	;YES, SLAVED TO THE RIGHT MASTER?
	JRST	INCTI1		;NO (???), BROADCAST NODEID NOT FROM HERE
	MOVE	J,T2		;POSITION SLAVE FEK ADDRESS IN A SAFE PLACE
	PUSHJ	P,EBI2BI	;READ IN NODEID SERIAL NUMBER
	CAMG	T1,FEKNIS(J)	;MONOTONICALLY INCREASED?
	JRST	INCTI3		;NO, NODE MUST HAVE RESTARTED
	MOVEM	T1,FEKNIS(J)	;YES, SET NEW SERIAL NUMBER
	MOVE	T1,%NTNIJ	;TIMEOUT (OR KEEP-ALIVE) INTERVAL
	MOVEM	T1,FEKNIT(J)	;RESET SLAVE FEK KEEP-ALIVE TIMER
	MOVE	J,FEKNIF(J)	;RESTORE MASTER FEK ADDRESS
	POPJ	P,		;AND JUST RETURN (NO TOPOLOGY CHANGE)

;HERE WHEN ETHERNET NCL PROTOCOL RESTARTED

INCTI3:	MOVEI	T1,FF.DFK	;FUNCTION: DESTROY DEAD SLAVE FEK
	XCT	FEKDSP(J)	;LET FEK SERVICE DO IT
	POPJ	P,		;JUST RETURN (AWAIT ANOTHER NODEID)
> ;END IFN FTENET
INCTI5:	PUSHJ	P,MAKNDB	;MAKE A NODE DATA BLOCK
	  POPJ	P,		;NO CORE.  DO IT LATER
;OPD
	PUSHJ	P,RDOPDD	;READ THE OPTIONAL DATA
	  POPJ	P,		;NO CORE. LATER
;NIT
	PUSHJ	P,EBI2BI	;READ NODEID "CLASS"
	JUMPE	T1,INCTI8	;IF POINT-TO-POINT (OR BLANK) THEN NORMAL FEK
IFE FTENET,<PJSP T1,INCTBD>	;ONLY OTHER LEGAL VALUE IS "BROADCAST"

IFN FTENET,<
	CAIE	T1,NIT.BC	;ONLY OTHER LEGAL VALUE IS "BROADCAST"
	PJSP	T1,INCTBD	;BAD MESSAGE
INCTI6:	MOVE	T2,FEKBLK(J)	;GET FEK FLAGS
	TLNN	T2,FK.ETM	;IS THIS AN ETHERNET MASTER FEK?
	PJSP	T1,INCTBD	;NO (???)
	MOVEI	T1,FF.CFK	;FUNCTION: CREATE NEW SLAVE FEK
	XCT	FEKDSP(J)	;GET A NEW SLAVE FEK (ADDRESS IN T1)
	JUMPE	T1,CPOPJ	;IGNORE IF NO CORE FOR NEW FEK
	HRRM	T1,NDBFEK(W)	;LINK NDB TO SLAVE FEK
	HLRZ	T2,NDBNNM(W)	;RETRIEVE NNM FROM NDB
	HRRM	T2,FEKNNM(T1)	;AND SET SLAVE FEK NNM
	HLLZS	FEKNNM(J)	;AND CLEAR IN MASTER FEK (SINCE NOT A NEIGHBOR)
	DMOVE	T2,PCBNIA(U)	;GET REMOTE ETHERNET ADDRESS
	DMOVEM	T2,FEKNIA(T1)	;AND SAVE IN THE NEW SLAVE FEK
	PUSHJ	P,EBI2BI	;READ BROADCAST "SERIAL" NUMBER
	HRRZ	T2,NDBFEK(W)	;RETRIEVE ADDRESS OF SLAVE FEK
	MOVEM	T1,FEKNIS(T2)	;AND SET LATEST RECEIVED SERIAL NUMBER
	MOVSI	T3,4		;RESET THE MASTER BROADCAST NODEID TIMER
	MOVEM	T3,FEKNIT(J)	;SO AS TO SEND A NODEID ASAP
> ;END IFN FTENET

INCTI8:	PUSHJ	P,EBI2BI	;READ REMOTE'S NCL VERSION NUMBER
	HRRM	T1,NDBNVR(W)	;SAVE FOR LATER EXAMINATION
	SETOM	NTQRCT		;WE MUST RECOMPUTE TOPOLOGY
	POPJ	P,		;ALL DONE
;INCTST -- ROUTINE TO PROCESS A START MESSAGE.
;
;MESSAGE =   NCT     DNA     SNA     NCA     NCN      OPD(NNM   SNM   SID)  NVR
;SO FAR  = NCT.ST    US      HIM   IGNORED IGNORED        /$\

INCTST:

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.

;NNM
	PUSHJ	P,EBI2BI	;READ THE NODE NUMBER
	HLRZ	T2,NDBNNM(W)	;GET WHAT SHOULD BE THE NODE NUMBER
	CAIE	T1,(T2)		;BETTER BE THE SAME
	PJSP	T1,INCTBD	;INCONSISTENT MESSAGE
	PUSHJ	P,CLNNDB	;MAKE THIS NDB PRISTINE
;OPD
	PUSHJ	P,RDOPDD	;READ THE OPTIONAL DATA
	  POPJ	P,		;NO CORE. DISMISS
;NVR
	PUSHJ	P,EBI2BI	;READ NCL VERSION NUMBER
	HRRM	T1,NDBNVR(W)	;SAVE FOR LATER USE

	PUSH	P,U		;SAVE THE PCB POINTER
	MOVSI	T1,NDB.SK	;GET THE "NOW-SENDING-STACKS" FLAG
	IORM	T1,NDBFLG(W)	;  TO TELL NCSSSM NOT TO SEND A START
	PUSHJ	P,NCSSSM	;SEND A STACK BACK
	  JFCL			;TRY LATER
				;NOTE!  THIS WILL MEAN THAT WE WON'T
				;TRY TO OPEN THE CONNECTION FOR ANOTHER
				;15 SECONDS OR UNTIL WE RECEIVE THE NEXT
				;START.  THIS MAY BE A  "FEATURE".  IF WE ARE
				;THIS SHORT OF CORE WE MAY NOT WANT TO
				;START ALL THIS RIGHT AWAY
	JRST	UPOPJ##		;RESTORE THE PCB AND GO BACK FOR MORE
;SUBROUTINE INCTSK - STACK MESSAGE PROCESSOR
;
;MESSAGE =   NCT     DNA     SNA     NCA     NCN      OPD(NNM   SNM   SID)  NVR
;SO FAR  = NCT.SK    US      HIM   IGNORED IGNORED        /$\

INCTSK:				;ENTRY

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.

;NNM
	PUSHJ	P,EBI2BI	;READ THE NODE NUMBER
	HLRZ	T2,NDBNNM(W)	;GET WHAT SHOULD BE THE NODE NUMBER
	CAIE	T1,(T2)		;BETTER BE THE SAME
	PJSP	T1,INCTBD	;INCONSISTENT MESSAGE
;OPD
	PUSHJ	P,RDOPDD	;READ THE OPTIONAL DATA
	  POPJ	P,		;NO CORE. DISMISS
;NVR
	PUSHJ	P,EBI2BI	;READ THE NCL VERSION NUMBER
	HRRM	T1,NDBNVR(W)	;SAVE FOR LATER USE

	PUSH	P,U		;SAVE THE INCOMING MESSAGE
	PUSHJ	P,NCSACK	;ACK THE STACK
	  JFCL			;IF WE CAN'T, THEN SOME-ONE WILL TIME OUT
	POP	P,U		;GET THE INCOMING MESSAGE BACK (TO FREE IT)
	PJRST	NODEUP		;SIGNAL THAT THE NODE IS UP
;SUBROUTINE INCTAK - ACK MESSAGE PROCESSOR
;
;MESSAGE =   NCT     DNA     SNA     NCA     NCN
;SO FAR  = NCT.AK    US      HIM   CHECKED IGNORED /$\

INCTAK:				;ACK'S HAPPEN IN NETSCN

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.

	POPJ	P,		;GO BACK FOR MORE






;SUBROUTINE INCTNK NAK MESSAGE PROCESSOR
;
;MESSAGE =   NCT     DNA     SNA     NCA     NCN
;SO FAR  = NCT.NK    US      HIM   CHECKED IGNORED /$\

INCTNK:				;RE-SEND ALL UN-ACKED MESSAGES

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.

	PUSHJ	P,SAVE1		;SAVE P1
	PUSHJ	P,SAVJW##	;WE'LL CLOBBER J TOO
	PUSH	P,U		;SAVE THE PCB
	HRRZ	P1,NDBQUE(W)	;GET THE POINTER
	HLLZS	NDBQUE(W)	;NO LONGER WAITING FOR ACK
	JUMPE	P1,INCTN2	;EMPTY
INCTN1:	MOVEI	U,(P1)		;GET THE NEXT ENTRY
	HRRZ	P1,PCBBLK(U)	;GET THE NEXT ENTRY
	HLLZS	PCBBLK(U)	;BREAK ANY FORWARD LINK
	NETOFF			;FRCWRT WILL TURN IT BACK ON ...
	PUSHJ	P,FRCWRT	;RESEND THE MESSAGE (SEE FOOTNOTE)
	JUMPN	P1,INCTN1	;CHECK FOR THE END
INCTN2:	JRST	UPOPJ		;RESTORE U AND RETURN
;SUBROUTINE INCTRP REP PROCESSOR
;
;MESSAGE =   NCT     DNA     SNA     NCA     NCN
;SO FAR  = NCT.RP    US      HIM   CHECKED IGNORED /$\

INCTRP:				;PROPER RESPONSE TO A REP IS TO DELETE
				; ALL OUT OF ORDER MESSAGES ON NDBQUE
IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.

	MOVSI	T1,NDB.XN	;GET THE 'NEED TO NAK' FLAG
	IORM	T1,NDBFLG(W)	;AND SET IT.  THIS WILL CAUSE
	POPJ	P,		;THE NAK TO GO OUT SOON ENOUGH.
				; NCSNAK WILL REMOVE OUT OF ORDER
				; MESSAGES FROM NDBQUE.

;***FOOTNOTE***
COMMENT\
     It is assumed that any message on the right half of NDBQUE
has already been given to NETWRT once before.  If this is the
case, then we do not need to set up CACHE information by a call
to PCBMRK
\
;SUBROUTINE RDOPDD - READ THE SOFTWARE ID FIELDS
;
;MESSAGE =   ???     ...     ???     OPD(NNM     SNM     SID)
;SO FAR  =                              READ     /$\
;
;RETURN	CPOPJ			;NO CORE
;	CPOPJ1			;W=NDB

RDOPDD:				;ENTRY
;SNM
	MOVEI	P2,NDBSN2(W)	;POINTER TO STATION NAME
	HRLM	P2,NDBSNM(W)	;SALT IT AWAY
RDOPD1:	PUSHJ	P,EAS2SX	;COPY THE STATION NAME
	MOVEM	T1,(P2)		;STORE THE STATION NAME
;SID
	HRRZ	P2,NDBSID(W)	;CHECK FOR A POINTER
	JUMPN	P2,RDOPD2	;YES
	PUSHJ	P,EAS2AZ	;SEE IF WE NEED SPACE FOR THE NAME
	  JRST	RDOPD3		;NO, DON'T GET IT
	MOVEI	T2,^D8		;YES, ALLOCATE SPACE FOR THE SYSTEM NAME
	PUSHJ	P,GETZWD	;FROM FREE CORE
	  POPJ	P,		;NO CORE
	HRRM	T1,NDBSID(W)	;STORE THE POINTER
	MOVEI	P2,(T1)		;GET THE POINTER
RDOPD2:	PUSHJ	P,EAS2AS	;COPY THE SYSTEM NAME
RDOPD3:	HLRZ	P2,NDBSID(W)	;GET THE POINTER
	JUMPN	P2,RDOPD4	;JUMP IF ONE
	PUSHJ	P,EAS2AZ	;SEE IF NEED SPACE FOR DATE
	  JRST	RDOPD5		;NO, DON'T GET IT
	MOVEI	T2,^D8		;YES, ALLOCATE SPACE FOR THE CREATION DATE
	PUSHJ	P,GETZWD	;FROM FREE CORE
	  POPJ	P,		;NO SPACE
	HRLM	T1,NDBSID(W)	;STORE THE POINTER
	MOVEI	P2,(T1)		;COPY THE POINTER
RDOPD4:	PUSHJ	P,EAS2AS	;COPY THE CREATION DATE
RDOPD5:	PJRST	CPOPJ1##	;EXIT


;SKOPDD - LIKE RDOPDD BUT JUST DISCARDS THE OPD FIELDS

SKOPDD:	PUSHJ	P,EAS2SX	;SLURP UP THE SNM "STATION NAME" FIELD
;	PUSHJ	P,EAS2AZ	;IS THERE A SID "SYSTEM NAME" FIELD?
;	 CAIA			;NO
	PUSHJ	P,XSKIP		;YES, SLURP IT UP
;	PUSHJ	P,EAS2AZ	;IS THERE A SID "SYSTEM DATE" FIELD?
;	 CAIA			;NO
	PUSHJ	P,XSKIP		;YES, SLURP IT UP
	POPJ	P,		;OPD(...,SNM,SID) READ AND DISCARDED
SUBTTL INCTDM - PROCESS OF NUMBERED CONTROL/DATA MESSAGES

;MESSAGE =   NCT     DNA     SNA     NCA     NCN     DLA     ...
;SO FAR  = NCT.DM    US      HIM   CHECKED CHECKED   /$\

INCTDM:
;DLA
	PUSHJ	P,EBI2BI	;GET THE DESTINATION LINK ADDRESS
	JUMPN	T1,IDCCTL	;DEVICE CONTROL MESSAGE

;HERE TO PROCESS NUMBERED CONTROL MESSAGES. THESE HAVE A DLA OF ZERO
;  AND HENCE ARE NODE (AS OPPOSED TO DEVICE) CONTROL
;
;MESSAGE =   NCT    DNA    SNA    NCA    NCN    DLA    (CNT    CM)*
;SO FAR  = NCT.DM   US     HIM    ACK   NUMBER   0      /$\
;

NCTDSP:	JUMPE	P4,CPOPJ##	;IF WE'RE AT END-OF-MESSAGE, RETURN

;CNT

	PUSHJ	P,EBI2BI	;GET THE LENGTH OF THE SUB MESSAGE
	SUB	P4,T1		;P4 := THE LENGTH OF THE REST OF THE MSG
	SKIPGE	P4		;MAKE SURE THAT SUB-MSG NOT LONGER THAN
	PJSP	T1,INCTBD	;  THE REST OF THE MESSAGE. IF SO, ERROR
	PUSH	P,P4		;SAVE THE LENGTH OF THE "REST" OF THE MSG
	MOVE	P4,T1		;SET THE LENGTH (P4) TO BE THAT OF THIS SUB-MSG

;CM

	PUSHJ	P,EBI2BI	;GET THE CONTROL MESSAGE TYPE
	CAILE	T1,NC.MAX	;RANGE CHECK IT
	PJSP	T1,NCTDS1	;GIVE "BAD MESSAGE" IF TYPE OUT OF RANGE
	AOS	NCLRMT(T1)	;COUNT THE MESSAGE TYPE
	PUSHJ	P,[SE1ENT	;CALL IN SECTION 1
		   PJRST ICMTBL(T1)]	;DISPATCH TO THE MESSAGE PROCESSOR
	  PJSP	T1,NCTDS1	;IF ERROR, RESTORE P4 AND POST BAD MSG
	JUMPG	P4,[PUSHJ P,XSKIP	;MAKE SURE ALL OF SUB MSG WAS
		    JRST .]	;  PROCESSED
	POP	P,P4		;GET LENGTH OF THE REMAINDER OF THE MESSAGE
	JRST	NCTDSP		;  AND GO PROCESS IT.

NCTDS1:	POP	P,P4		;RESTORE P4 (JUST TO CLEAN UP STACK)
	PJRST	INCTBD		;GO MARK THE MESSAGE AS BAD

ICMTBL:	JRST	CPOPJ##		;<0> IS ILLEGAL
	JRST	ICMCNT		;<1> CONNECT
	JRST	ICMDSC		;<2> DISCONNECT
	JRST	ICMNBN		;<3> NEIGHBOURS
	JRST	ICMRCF		;<4> REQUEST CONFIGURATION
	JRST	ICMCNF		;<5> CONFIGURATION
	JRST	ICMDRQ		;<6> DATA REQUEST
	JRST	ICMCTL		;<7> STATION CONTROL
			     COMMENT @

		Connect and Disconnect message processing

     For each device an attempt is made to call the device driver for all
the connect/disconnect messages.  Here follows a list of the actions taken
for the three connect related messages.

Connect initiate.
	    This is the complicated  one.  There are two phases to the
	processing of the connect initiate message.

	   1)	The Link Address Table (LAT) is scaned for devices in the
		LAT.CI (waiting for connect) state.  When a device is found
		in that state it is called on its NDPCNI dispatch entry in
		in the following context.

		   P3 := XWD SLA,OBJ	(As read from the message)
		   P1, P4 := Pointer to the first byte after the "OBJ" field.

		If the driver does not wish to accept the connect it should
		CPOPJ.  If it accepts the connect it should skip return.
		(Note.  The driver MUST respect the P's)

	   2)	If no driver is found to accept the connect, a default connect
		processor is called. (Via the DCITAB entry indexed by the
		ofject type of the incoming message.)  This default handler
		is called in the same context as above.  If it accepts the
		connect, it should skip return.  If the connect is to be
		rejected, the handler should CPOPJ return with T1 containing
		the reason for failure.  (RSN field of a disconnect message)

Connect confirm
	    The driver will be called via the NDPCNC vectored entry. in the
	the following context.

		P1, P4 := Point to the "SLA" byte of the message.

	A skip return accepts the message.  A non-skip return says that the
	message was bad.  (The message will then be given to INCTBD)

Disconnect
	    Same as connect confirm, except that the NDPDSC entry is used.

	Note.  When ever any of the NDP vectored connect/disconnect entries
are taken the registers P1 and P4 (message pointer and count) will point to
the DLA.  In other words, the first item to be read by the service routine
will be the DLA.

				@
;ICMCNT	ROUTINE TO PROCESS INCOMING CONNECT MESSAGE.
;CALLED WITH
;	P1 := POINTER TO THE DLA SECTION OF THE MESSAGE
;	P4 := COUNT OF THE BYTES LEFT IN THE MESSAGE
;	W  := POINTER TO THE NDB OF THE NODE THAT SENT THIS MESSAGE
;RETURN	CPOPJ1			;WITH EITHER THE DEVICE ACCEPTING THE MESSAGE,
;				; OR A CONNECT REJECT BEING SENT
;
ICMCNT::			;HERE TO PROCESS AN INCOMING CONNECT.
;DLA

	PUSHJ	P,EBI2BI	;GET THE DESTINATION
	JUMPN	T1,ICMCNC	; IF NONZERO, GO PROCESS CONNECT CONFIRM

;SLA

	PUSHJ	P,EBI2BI	;GET THE SOURCE
	HRLZI	P3,(T1)		;COPY IT INCASE WE DECIDE TO DISCONNECT.

;DPN(OBJ)

	PUSHJ	P,EBI2BI	;GET THE DESTINATION OBJECT TYPE.
	HRRI	P3,(T1)		;COPY THE OBJECT TYPE FOR INDEXING ETC.

;LOOP THROUGH NETLAT LOOKING FOR A DDB WAITING FOR THIS CONNECT

	MOVSI	P2,-LATLEN##	;MAKE AN AOBJN POINTER TO THE NETLAT TABLE.
ICMCN1:	AOBJP	P2,ICMCN3	;NO DDB'S WERE INTERESTED, TRY DEFAULT CI'ER
	LDB	T3,LATSP2	;GET THE STATE OF THIS LAT ENTRY
	CAIE	T3,LAT.CI	;IS IT CONNECT INITIATE?
	JRST	ICMCN1		;IF NOT, TRY THE NEXT ENTRY
	LDB	F,LATPP2	;GET DDB ADDRESS FROM LAT ENTRY
	MOVEI	T1,NDPCNI	;SET UP TO USE THE CONNECT INITIATE ENTRY.
	MOVE	T2,NETLAT(P2)	;WANT NETLAT FLAGS IN T2
	PUSHJ	P,ICMNDP	;DO THE DISPATCH
	  JRST	ICMCN1		;FAILURE, TRY THE NEXT ONE
	JRST	CPOPJ1##	; SUCCESS. GIVE GOOD RETURN


;HERE WHEN NO LAT ENTRIES WANTED THE CONNECT.  CALL THE DEFAULT HANDLER.

ICMCN3:	MOVEI	T1,(P3)		;GET JUST THE OBJECT TYPE.
	CAIL	T1,0		;RANGE CHECK IT BEFORE
	CAILE	T1,OBJ.MX	; WE TRY USING IT FOR AN INDEX.
	JRST	ICMCN6		;OUT OF RANGE.  SEND A DISCONNECT.
	SKIPN	T2,NDTTAB##(T1)	;ADDRESS OF OBJECT-TYPE'S NDT
	JRST	ICMCN6		;NONE - WE DON'T HANDLE THAT TYPE
	HRRZ	T2,NDTCNI(T2)	;DISPATCH ADDRESS
	SE1ENT			;CALL IN SECTION 1
	PUSHJ	P,(T2)		;PROCESS THE INCOMING CONNECT INIT
	 JRST	ICMCN7		; REJECTED, REASON CODE IS IN T1
	JRST	CPOPJ1##	;IT LIKED IT.  GIVE A GOOD RETURN
ICMCN6:	MOVEI	T1,RSN.OT	;OBJECT TYPE NOT AVAILABLE
				; NETPRM WOULD SEEM TO PREFER RSN.NP AS A MORE
				; APPROPRIATE RESPONSE - HOWEVER, RSN.OT IS WHAT
				; WAS RETURNED IN PREVIOUS MONITORS, AND I DON'T
				; HAVE THE COURAGE TO CHANGE IT . . .
ICMCN7:				;HERE TO SEND REJECT (DISCONNECT) RSN IN T1.
				; (WE CAN'T USE NCSDSC SINCE WE HAVE NO DDB)
	PUSH	P,U		;PROTECT OUR INCOMING MESSAGE
	HLL	T1,P3		;PUT SLA (NOW DLA) IN LH OF REASON CODE
	PUSH	P,T1		;SAVE "XWD SLA,RSN"
	EMRGCY			;SET GLOBAL FLAG TO USE "EMERGENCY" MEMORY
	PUSHJ	P,NCMHDR	;GET A MESSAGE WITH A CONTROL HEADER WRITTEN
	 STOPCD	.,STOP,ANFCGM,	;++ CAN'T GET MESSAGE. NETSCN SHOULD CHECK

;TYP

	MOVEI	T1,NC.DSC	;THIS IS A DISCONNECT MESSAGE
	PUSHJ	P,BI2EBI	;WRITE THE CONTROL MESSAGE TYPE

;DLA

	HLRZ	T1,-1(P)	;RECOVER THE SLA (NOW DLA) FROM THE STACK
	PUSHJ	P,BI2EBI	;WRITE THE DLA

;SLA

	SETZ	T1,		;WE NEVER ASSIGNED A SLA
	PUSHJ	P,BI2EBI	;WRITE THE SLA

;RSN

	HRRZ	T1,-1(P)	;RECOVER THE RSN FROM THE STACK
	PUSHJ	P,BI2EBI	;WRITE THE REASON FOR THE DISCONNECT.
	JSP	T1,NETWRC	;SEND THE MESSAGE
	POP	P,(P)		;THROW AWAY THE TEMP ON THE STACK
	PJRST	UPOPJ1##	;RESTORE OUR PCB AND SKIP RETURN



;DUMMY CONNECT INIT PROCESSOR FOR THOSE OBJECTS WHICH DON'T ACCEPT A CI.

NJNKCI::MOVEI	T1,RSN.OT	;JUNK CI (FAKE WITH NO SUCH OBJECT TYPE)
	POPJ	P,		;"ERROR" RETURN TO ICMCN7
;ICMCNC	ROUTINE TO PROCESS CONNECT CONFIRM MESSAGES.
;CALL	AS WITH ICMCNT, EXCEPT THAT P1 AND P4 HAVE BEEN PUSHED
;	AND T1 := THE DLA

ICMCNC:	PUSHJ	P,ICMLAF	;GET LAT'S ADDRESS AND FLAGS
	 POPJ	P,		;SLA OUT OF RANGE, OR UNASSIGNED
	MOVEI	T1,NDPCNC	;CONNECT CONFIRM DISPATCH ENTRY
	PJRST	ICMNDP		;GO DO THE DISPATCH



;ICMDSC	ROUTINE TO HANDLE THE DISCONNECT.
;CALLED WITH
;	P1 := BYTE POINTER TO THE DLA OF THE DISCONNECT
;	P4 := LENGTH OF THE REST OF THE DISCONNECT MESSAGE
;	W  := POINTER TO THE NDB OF THE NODE THAT SENT THIS MESSAGE.
;RETURN	CPOPJ			;DLA WRONG. MESSAGE IS BAD
;	CPOPJ1			;MESSAGE PROCESSED OK.

ICMDSC:	PUSHJ	P,ICMLAE	;GET LAT'S ADDRESS AND FLAGS
	 POPJ	P,		;SLA OUT OF RANGE, OR UNASSIGNED
	MOVEI	T1,NDPDSC	;DISCONNECT VECTOR ENTRY
	PJRST	ICMNDP		;GO DO THE DISPATCH
;SUBROUTINE ICMNBN - NEIGHBOURS
ICMNBN:				;ENTRY

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.

	PUSHJ	P,SAVJW##	;WE WILL CLOBBER THESE
	PUSH	P,U		;SAVE THE PCB
	MOVE	T4,[POINT 9,NDBTOP(W)] ;GET A POINTER TO THE NEIGHBORS TABLE
	MOVEI	T3,NGHMAX	;COUNT OF MAXIMUM NUMBER OF NEIGHBORS.

ICMNB1:	JUMPLE	P4,ICMNB2	;IF WE'VE READ ALL THE MSG, CLEAR UNUSED SLOTS
	PUSH	P,T3		;SAVE T3 (EBI2BI CLOBBERS IT)
	PUSHJ	P,EBI2BI	;GET THE NODE NUMBER OF THE NEXT NEIGHBOR
	IDPB	T1,T4		;PUT IT IN THE TABLE
	PUSHJ	P,EBI2BI	;GET THE COST OF THAT LINK
	IDPB	T1,T4		;AND PUT THAT IN THE TABLE TOO
	POP	P,T3		;GET OUR COUNTER BACK
	SOJG	T3,ICMNB1	;LOOP OVER THE ENTIRE MESSAGE
	JRST	ICMNB4		;TOO MANY NEIGHBORS.  THINGS WILL GET SCREWED.

ICMNB2:	SETZ	T1,		;ZERO THE UNUSED PART OF THE TABLE
ICMNB3:	IDPB	T1,T4		;CLEAR THE NODE NUMBER
	IDPB	T1,T4		;CLEAR THE COST
	SOJG	T3,ICMNB3	;LOOP OVER ALL UNUSED SLOTS

ICMNB4:	PUSHJ	P,RCMPTP	;RECOMPUTE THE TOPOLOGY,
	PJRST	UPOPJ1		;AND GIVE SUCCESSFUL RETURN
;SUBROUTINE ICMRCF - REQUEST CONFIGURATION
ICMRCF:				;ENTRY

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.

	PUSH	P,U		;SAVE THE PCB
	PUSHJ	P,NCSCNF	;SEND THE CONFIGURATION MESSAGE
	  JFCL			;LATER
	PJRST	UPOPJ1##	;EXIT


;SUBROUTINE ICMCNF - CONFIGURATION
ICMCNF:				;ENTRY

IFN PARANOID&P$NDB,<PUSHJ P,NDBCHK> ;MAKE SURE THIS DIDN'T COME FROM US.
	PUSHJ	P,PSINTC##	;INFORM OF TOPOLOGY (CONFIG) CHANGE

	MOVSI	T1,NDB.CF	;GET THE CONFIGURATION BIT
	IORM	T1,NDBFLG(W)	;SEEN THE CONFIGURATION MESSAGE
ZZ==0
REPEAT <OBJ.MX/4>+1,<
	SETZM	NDBDEV+ZZ(W)	;CLEAR THE DEVICE TABLE(S)
ZZ==ZZ+1
>;END OF REPEAT
	JUMPE	P4,CPOPJ1##	;NO DEVICES
	PUSH	P,P2		;SAVE P2
ICMCF1:
;OBJ
	PUSHJ	P,EBI2BI	;GET THE OBJECT TYPE
	CAILE	T1,OBJ.MX	;CHECK THE RANGE
	JRST	[POP	P,P2	;RESTORE P2
		POPJ	P,]	;DISMISS THE MESSAGE
	MOVEI	P2,(T1)		;COPY THE DEVICE NUMBER
;NDEV
	PUSHJ	P,EBI2BI	;GET THE COUNT
	DPB	T1,NETCNF##(P2)	;STORE NEW COUNT
;PID
ICMCF3:	PUSHJ	P,EAS2SX	;GET THE PID
	JUMPG	P4,ICMCF1	;CONTINUE THROUGH THE MESSAGE
	POP	P,P2		;RESTORE P2
	JRST	CPOPJ1##
;SUBROUTINE ICMDRQ - DATA REQUEST

ICMDRQ:	PUSHJ	P,ICMLAE	;GET LAT'S ADDRESS AND FLAGS
	 POPJ	P,		;SLA OUT OF RANGE, OR UNASSIGNED

;DRQ

	PUSH	P,T2		;SAVE NETLAT FLAGS
	PUSHJ	P,EBI2BI	;GET THE REQUEST COUNT
	MOVE	T4,T1		;COPY THE DRQ COUNT FOR THE DRIVER TO HANDLE
	POP	P,T2		;RESTORE NETLAT FLAGS
	MOVEI	T1,NDPDRQ	;GET THE DATA REQUEST VECTOR OFFSET
	PJRST	ICMNDP		;GO DO THE DISPATCH


;*** FOOTNOTE ***

				COMMENT @

     The Data Request Dispatch is much like the connects.  When the driver
is called, the following registers are set up.

	U := The PCB
	P1, P4 := Pointer to the byte after the "DRQ"  (Should be after the
		end of the message
	T4 := The Data Request Count.

The driver should normally return with a "CPOPJ1" indicating a good return,
but if for some reason he thinks that he is being screwed by the remote, he
may "CPOPJ" return (error) and the message will be INCTBD'ed

				    @
;ICMCTL	ROUTINE TO PROCESS INCOMING STATION CONTROL MESSAGES
;CALL	W  := NDB POINTER
;	P1 := POINTER TO THE FIRST BYTE OF THE STC MESSAGE
;	P4 := LENGTH OF THE STC MESSAGE
;RETURN	CPOPJ			;IF SOMETHING'S "WRONG" WITH THE MESSAGE
;	CPOPJ1			;WITH STC-BLOCK ON "NDBICT" OF THE NDB
;
;ACTION	THIS ROUTINE COPIES THE INCOMING STATION CONTROL MESSAGE
;	INTO AN "STC" BLOCK.  IT QUEUES THIS OFF OF THE "NDBICT"
;	WORD OF THE NDB.  IF THERE IS A JOB WAITING FOR A STATION
;	CONTROL RESPONSE ("NDBSTC" /= 0) THEN THAT JOB IS AWOKEN.
;	OTHERWISE NETLDR IS STARTED.

ICMCTL:	SKIPE	NDBICT(W)	;SEE IF THERE IS A MESSAGE ALREADY WAITING
	RETSKP			;  IF MSG ALREADY THERE, JUST TOSS THIS ONE
	JUMPLE	P4,CPOPJ##	;MAKE SURE IT HAS DATA, "BAD MSG" IF NOT
	PUSHJ	P,SAVJW##	;SAVE J (STC-BLOCK) AND W
	MOVEI	T1,(P4)		;COPY THE LENGTH OF THE STC MESSAGE
	PUSHJ	P,GETSTC	;GET A STC-BLOCK TO HOLD IT
	  RETSKP		;  IF NO CORE, TOSS MSG BUT SAY MSG IS "OK"
	MOVE	T2,[POINT 8,STCDAT(J)]	;GET A BYTE POINTER TO STC DATA PART
ICMCT1:	ILDB	T1,P1		;LOOP OVER ALL STC-MESSAGE BYTES
	IDPB	T1,T2		;  STORING THEM IN THE STC-BLOCK
	SOJG	P4,ICMCT1	;  P4 HAS COUNT OF BYTES

	HRLI	J,^D10		;GET A 10 SECOND TIMER, AND QUEUE THIS STC
	MOVEM	J,NDBICT(W)	;MESSAGE ON THE NDB.  IF NO ONE READS IT
				;  IN 10 SECONDS, IT WILL SELF-DESTRUCT
	SKIPE	T1,NDBSTC(W)	;SEE IF ANYONE IS WAITING FOR A MESSAGE
	JRST	[HRRZS T1	;  ONLY WANT JOB NUMBER, THROW AWAY TIMER
		 AOS (P)	;  IF SOMEONE'S WAITING, INDICATE MESSAGE "OK"
		 PJRST EWAKE##]	;  WAKE THE USER AND SKIP RETURN

	PUSHJ	P,FRCNLD	;IF NO ONE EXPECTING IT, START UP NETLDR
				;  IN HOPES THAT IT WILL WANT IT.
	RETSKP			;GIVE GOOD RETURN (ACCEPTING THE MESSAGE)
SUBTTL	ICM HELPERS

;ICMLAE/ICMLAF - READ IN LINK ADDRESS AND SETUP FOR ICMNDP DISPATCH
;CALL	P1/P4 SETUP
;	PUSHJ	P,ICMLAE/F
;	 ERROR RETURN
;	NORMAL RETURN
;
;ON ERROR RETURN THE LINK ADDRESS WAS OUT OF RANGE, OR UNASSIGNED
;
;ON NORMAL (SKIP) RETURN, THE NETLAT FLAGS ARE IN T2 AND THE
;LINK ADDRESS DDB/LDB ADDRESS IS IN F

ICMLAE:	PUSHJ	P,EBI2BI	;READ IN DESTINATION LINK ADDRESS
ICMLAF:	CAIL	T1,0		;RANGE
	CAILE	T1,LATLEN##	; CHECK
	POPJ	P,		;LINK ADDRESS OUT OF RANGE
	SKIPN	T2,NETLAT(T1)	;GET NETLAT ENTRY
	POPJ	P,		;UNASSIGNED
	LDB	F,LATPTR	;GET DDB/LDB ADDRESS
	JRST	CPOPJ1##	;SUCCESSFUL RETURN



;ICMNDP	ROUTINE TO PERFORM THE NETWORK DISPATCH
;CALL	MOVEI	T1,DISPATCH FUNCTION
;	MOVE	T2,NETLAT FLAGS (LAT.TY, LAT.VT)
;	MOVE	F, DDB/LDB ADDRESS
;	PUSHJ	P,ICMNDP
;RETURN	PJRSTS TO THE ROUTINE SPECIFIED

ICMNDP:	JUMPGE	T2,ICMND2	;DISPATCH FOR DDBS
	TLNN	T2,LAT.VT	;IF IT'S A LDB, SEE IF IT'S A VTM
	JRST	@MCRNDP##(T1)	;IF IT'S NOT A VTM, USE MCR DISPATCH
	JRST	@TTYNDP##(T1)	;USE NETWORK VIRTUAL TERMINAL DISPATCH

ICMND2:	HLRZ	T2,DEVNET(F)	;GET ADDRESS OF DEVICE SERVICE "NDP" DISPATCH
	S0JRST	@NDTNDP(T2)	;THEN USE THE DEVICE DISPATCH VECTOR
SUBTTL IDCCTL - INPUT DEVICE CONTROL (DAP) MESSAGE PROCESSOR

;SUBROUTINE IDCCTL - DEVICE CONTROL (DAP) MESSAGES
;CALL	MOVEI	U,PCB
;	MOVEI	W,NDB
;	MOVEI	T1,DLA
;	PUSHJ	P,IDCCTL
;RETURN	POPJ	P,

IDCCTL:	NTDBUG			;VERIFY THE INTERLOCK

;DLA

	CAIL	T1,0		;RANGE
	CAILE	T1,LATLEN##	; CHECK DLA
	PJSP	T1,INCTBD	;BAD LINK ADDRESS
	LDB	F,LATPTR	;GET DDB/LDB ADDRESS
	MOVE	T2,NETLAT(T1)	;AND NETLAT FLAGS
	TLNN	T2,LAT.TY	;DDB OR LDB?
	SKIPA	T3,DEVNET(F)	;DDB, GET NODE NUMBER
	LDB	T3,LDPRNF##	;LDB, GET NODE NUMBER
	ANDI	T3,777		;MASK OUT JUNK
	HLRZ	T1,NDBNNM(W)	;GET OUR NODE NUMBER
	CAIE	T3,(T1)		;BETTER MATCH
	PJSP	T1,INCTBD	;NOPE, DON'T LET MESSAGE BE PROCESSED
	PUSH	P,T2		;SAVE NETLAT FLAGS
	MOVE	T1,PCBCTR(U)	;GET MESSAGE LENGTH
	SUBI	T1,6		;DISCOUNT PROTOCOL
	CAIGE	T1,1_<NETLNH-1>	;IN RANGE FOR JFFO?
	JFFO	T1,.+2		;GET HIGHEST BIT NUMBER
	TDZA	T1,T1		;OUT OF RANGE, USE ENTRY 0
	MOVNI	T1,-^D36(T2)	;IN RANGE, PUT IN PROPER ORDER
	AOS	NCLRDL(T1)	;RECORD DATA LENGTH
	POP	P,T2		;RESTORE NETLAT FLAGS
	MOVEI	T1,NDPICM	;NOW PASS MESSAGE VIA THE ICM DISPATCH
	PJRST	ICMNDP		;GIVE THE PCB TO THE DEVICE SERVICE ROUTINE.
				; IT MAY EITHER BE PROCESSED IMMEDIATLY AT
				; "INTERRUPT" LEVEL (E.G., LPT'S) OR QUEUED
				; FOR LATER PROCESSING AT UUO LEVEL (CDR'S)
;RCMPTP	THIS ROUTINE RECOMPUTES THE NETWORK TOPOLOGY.  IT SHOULD BE CALLED
;	EVERY TIME THE TOPOLOGY CHANGES (NODEID & NEIGHBORS MESSAGE)
;	IT IS RESPONSIBLE FOR GARBAGE COLLECTING UNREACHABLE NDB'S AND
;	RE-ALLOCATING FEK'S.  IT HAS THREE PHASES.
;	   1)	RECOMPUTE OUR NEIGHBORS.  (FOR NEIGHBORS MSG)
;	   2)	RESET ALL NDB'S (CLEAR NDB.TP, SET COST TO 777777)
;	   3)	MARK ALL REACHABLE NODES AND REALLOCATE FEK'S (TPMARK)
;		(NODES FOUND BY TRACING OUT PATHS STARTING WITH
;		OUR FEK'S)
;	   4)	GARBAGE COLLECT ALL UNREACHABLE NODES.
;PRESERVES ALL P'S
;CLOBBERS ALL T'S
;RETURNS CPOPJ

RCMPTP:	NTDBUG			;VERIFY THE INTERLOCK
	PUSHJ	P,SAVE4##	;USES ALL THE P'S

;REBUILD OUR NEIGHBORS TABLE (FOR SENDING NEIGHBORS MSG)

	MOVE	P1,[POINT 9,NETNDB##+NDBTOP] ;POINTER TO NEIGHBORS TABLE
	MOVEI	P2,NGHMAX	;MAXIMUM NUMBER OF NEIGHBORS TABLE CAN HOLD
	SKIPA	J,[FEKFST##]	;LOOP OVER ALL FEK'S STARTING WITH FIRST
RCMPT1:	HRRZ	J,FEKBLK(J)	;GET THE ADDRESS OF THE NEXT FEK
	JUMPE	J,RCMPT2	;EXIT IF WE'VE LOOKED AT ALL FEKS
	HRRZ	T1,FEKNNM(J)	;GET NODE NUMBER
	JUMPE	T1,RCMPT1	;NEED ONE IF WE HAVE A NEIGHBOR
	SKIPL	T3,FEKBLK(J)	;SKIP IF THE FEK IS DEAD
	JRST	RCMPT1		;EITHER FEK IS DEAD, OR NO NODE ID
IFN FTENET,<
	TLNE	T3,FK.ETM	;IS THIS AN ETHERNET MASTER FEK?
	JRST	RCMPT1		;YES, IGNORE IT, ONLY SLAVES ARE NEIGHBORS
> ;END IFN FTENET
	HLRZ	T2,FEKCST(J)	;GET COST FOR THIS FEK
	IDPB	T1,P1		;SAVE HIS NUMBER AS A NEIGHBOR
	IDPB	T2,P1		;SAVE COST
	SOJGE	P2,RCMPT1	;LOOP OVER ALL FEK'S
	STOPCD	.,STOP,ANFTMF,	;++ TOO MANY FEK'S.

;HERE TO ZERO THE UNUSED PART OF OUR NEIGHBORS TABLE

RCMPT2:	SOJL	P2,RCMPT3	;EXIT WHEN WE'VE SET ALL THE NEIGHBORS
	IDPB	J,P1		;CLEAR THIS NODE NUMBER (J IS ZERO...)
	IDPB	J,P1		;CLEAR ITS COST ALSO.
	JRST	RCMPT2		;LOOP OVER ALL UNUSED SLOTS
;HERE TO CLEAR THE MARK BIT (NDB.TP) IN ALL NDB'S

RCMPT3:	MOVSI	T1,NDB.TP	;GET THE MARK BIT
	MOVEI	W,NETNDB##	;GET THE FIRST NODE IN THE LIST
RCMPT4:	ANDCAM	T1,NDBFLG(W)	;CLEAR THE BIT
	HRRZ	W,NDBNNM(W)	;ADVANCE TO THE NEXT NDB
	JUMPN	W,RCMPT4	;LOOP UNTIL WE REACH A ZERO LINK

;HERE TO MARK ALL REACHABLE NODES AND DETERMINE WHICH FEK THEY SHOULD USE.

	SKIPA	J,[FEKFST##]	;GET THE ADDRESS OF THE FIRST FEK
RCMPT5:	HRRZ	J,FEKBLK(J)	;GET THE ADDRESS OF THE NEXT FEK
	JUMPE	J,RCMPT6	;EXIT IF WE HAVE DONE ALL FEKS
	SKIPGE	T1,FEKBLK(J)	;SKIP IF FEK IS DEAD
IFN FTENET,<TLNN T1,FK.ETM>	;EXIT IF NEIGHBORLESS ETHERNET MASTER FEK
	TLNE	T1,FK.NUL	;EXIT IF THIS IS A NUL FEK
	JRST	RCMPT5		;EITHER DEAD OR NULL. GO DO NEXT FEK
	HRRZ	T1,FEKNNM(J)	;GET NODE NUMBER
	JUMPE	T1,RCMPT5	;NO NEIGHBOR
	PUSHJ	P,SRCNDB	;SET UP "W" WITH THE NDB ADDRESS
	  JRST	RCMPT5		;JUST HIT VERY-VERY NARROW CROSS CPU RACE...
	SETZ	T2,		;SAY THAT IT'S A ZERO COST TO HIM
				;  (JUST A WHITE LIE TO MAKE SURE WE USE FEK)
	JSP	T1,TPMARK	;MARK ALL NODES WHOSE BEST PATH (SO FAR)
				;  IS THROUGH THIS FEK
	JRST	RCMPT5		;LOOP OVER ALL FEKS

;NOW WE SWEEP AND DELETE ALL UNREACHABLE NODES

RCMPT6:	HRRZ	W,NETNDB##+NDBNNM ;GET START OF THE LIST
RCMPT7:	JUMPE	W,CPOPJ		;IF AT END, THEN EXIT
	MOVSI	T1,NDB.TP	;GET THE MARK BIT, AND
	TDNE	T1,NDBFLG(W)	;SEE IF IT'S SET.
	JRST	[HRRZ W,NDBNNM(W)    ;IF SO, THEN STEP TO NEXT NDB
		 JRST RCMPT7]        ;AND CONTINUE
	HRRZ	P1,NDBNNM(W)	;OTHERWISE GET THE LINK TO THE NEXT NDB
	PUSHJ	P,RMVNDB	;BLAST THE NOW USELESS NDB
	MOVEI	W,(P1)		;GET ADDR OF NEXT NDB TO CHECK
	JRST	RCMPT7		;AND GO DO IT.
;TPMARK	THIS SUBROUTINE PERFORMS THE RECURSIVE MARK PHASE AS WELL AS
;	THE TASK OF FRONT-END-KONTROLLERS TO EACH NDB.
;	BECAUSE OF THE SMALL AMOUNT OF STACK SPACE ALLOWED, AND THE
;	POSSIBILITY OF LARGE NETWORKS THE PDL STORAGE FOR THESE
;	RECURSIVE ROUTINES IS IN THE NDB'S THEMSELVES.  THESE LOCATIONS
;	ARE THE THREE 'NDBTMP' LOCATIONS
;REGISTER USAGE
;CALL WITH
;	J := THE ADDRESS OF THE FEK THAT STARTED THIS CHAIN
;	W := THE ADDRESS OF THE CURRENT NDB
;	T1 := THE RETURN ADDRESS (JSP T1, ...)
;	T2 := THE 'COST' OF THE PATH FROM NETNDB TO THIS NDB.
;
;	P1, P2, AND P3 MUST BE PERSERVED.  THEY ARE USED AS FOLLOWS:
;
;	   P1	OUR NDB(W)
;	   P2	A BYTE POINTER TO THE CURRENT ENTRY IN OUR NEIGHBORS TABLE
;	   P3	RETURN ADDR(T1),,THE NUMBER OF NEIGHBOR SLOTS LEFT TO LOOK AT.
;

TPMARK:	JUMPE	W,(T1)		;IF NO NDB, WE CAN'T MARK FROM IT...
	MOVEI	T3,NETNDB##	;GET THE ADDRESS OF THE PROTOTYPE NDB
	CAIN	W,(T3)		;  IF THIS IS THE PROTOTYPE
	JRST	(T1)		;THEN LEAVE NOW (DON'T CHANGE ITS FEK)
	MOVSI	T3,NDB.TP	;GET THE "MARK BIT"
	TDNN	T3,NDBFLG(W)	;HAVE WE VISITED THIS NODE BEFORE.
	JRST	[IORM T3,NDBFLG(W) ;IF NOT, MARK THAT WE HAVE NOW.
		 JRST TPMRK1]	;  AND DON'T BOTHER TO COMPARE COSTS.
	HLRZ	T3,NDBFEK(W)	;GET HIS BEST COST SO FAR
	CAIL	T2,(T3)		;IS THIS A CHEAPER ROUTE
	JRST	(T1)		;  IF NOT CHEAPER, THEN EXIT NOW
TPMRK1:	HRLM	T2,NDBFEK(W)	;SAVE CURRENT COST AT BEST SO FAR
	HRRM	J,NDBFEK(W)	;SAVE THIS FEK AS BEST FEK SO FAR
	DMOVEM	P1,NDBTMP(W)	;SAVE P1 AND P2
	MOVEM	P3,NDBTMP+2(W)	;  DON'T FORGET P3
	MOVSI	P3,(T1)		;SAVE OUR RETURN ADDRESS IN A PROTECTED LOC
	HRRZ	P1,W		;KEEP OUR NDB POINTER SAFE ALSO
	MOVE	P2,[POINT 9,NDBTOP(P1)] ;BUILD A POINTER TO OUR NEIGHBORS
	HRRI	P3,NGHMAX	;GET A COUNT OF MAX NUMBER OF NEIGHBORS

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;NOW LOOP OVER EACH NEIGHBOR RECURSIVLY CALLING TPMARK.  (IF ALL PATHS
;  ARE POSITIVE COST, THERE IS NO DANGER OF LOOPING...)

TPMRK2:	TRNN	P3,-1		;IF WE'VE PROCESSED ALL NEIGHBORS
	JRST	TPMRK4		;THEN EXIT
	SUBI	P3,1		;COUNT DOWN
	ILDB	T1,P2		;GET THE NUMBER OF OUR NEXT NEIGHBOR
	JUMPE	T1,TPMRK3	;IF THERE ISN'T ONE, SKIP COST AND GO TO NEXT
	PUSHJ	P,SRCNDB	;SET UP "W" WITH NEIGHBORS NDB ADDRESS
	  JRST	[PUSHJ P,MAKNDB	;IF HE'S A NEW NEIGHBOR, MAKE AN NDB FOR HIM.
		   JRST TPMRK3	;IF NO CORE. JUST SKIP IT. WE'LL GET HIM LATER
		 JRST .+1]	;REJOIN MAIN FLOW WITH "W" SET UP.
	ILDB	T1,P2		;GET THE COST OF THE LINK FROM US TO HIM.
	HLRZ	T2,NDBFEK(P1)	;GET THE COST SO FAR TO GET TO US.
	ADD	T2,T1		;THE SUM IS TOTAL COST TO HIM VIA THIS PATH
	JSP	T1,TPMARK	;RECURSIVLY MARK HIM
	JRST	TPMRK2		;LOOP OVER ALL NEIGHBORS.

TPMRK3:	ILDB	T1,P2		;SKIP COST IF NO NODE, OR NO CORE
	JRST	TPMRK2		;LOOP OVER ALL NEIGHBORS.

TPMRK4:	HLRZ	T1,P3		;ALL NEIGHBORS MARKED. GET RETURN ADDRESS
	MOVE	P3,NDBTMP+2(P1)	;RESTORE P3
	DMOVE	P1,NDBTMP(P1)	;RESTORE P1 AND P2
	JRST	(T1)		;RETURN TO CALLER
;SUBROUTINE ONLNDB/OFLNDB - TYPE ONLINE/OFFLINE FOR A NETWORK NODE
;CALL	MOVEI	W,NDB
;	PUSHJ	P,ONLNDB/OFLNDB
;RETURN	CPOPJ

ONLNDB:	SKIPA	T1,[[ASCIZ /up at /]]
OFLNDB:	MOVEI	T1,[ASCIZ /down at /]
	SKIPN	%SIOPR##	;LET OPR REPORT IT IF ORION IS RUNNING
	SKIPGE	DEBUGF##	;CHECK FOR DEBUG
	POPJ	P,		;YES, SKIP THE MESSAGES
	PUSH	P,U
	PUSH	P,T1		;SAVE THE MODE
	MOVE	U,OPRLDB##	;GET THE OPERATOR LINE
	PUSHJ	P,INLMES##	;TYPE %%
	ASCIZ	/%% Node /
	PUSHJ	P,TYPNOD	;TYPE OUT JUST "SNM(NNM)"
	POP	P,T1		;GET ONLINE/OFFLINE BACK
	PUSHJ	P,CONMES##	;TYPE IT
	PUSHJ	P,PRDTIM##	;FINISH OFF WITH TIME OF DAY
	PJRST	UPOPJ##		;EXIT
;SUBROUTINE TYPNDB - TYPE OUT THE NODE INFOR
;CALL	MOVEI	W,NDB
;	PUSHJ	P,TYPENDB
;RETURN	CPOPJ

TYPNDB::NTDBUG			;VERIFY THE INTERLOCK
	MOVEI	T1,[ASCIZ \ANF	\] ;ASSUME NCL.
	CAIN	W,NETNDB##	;IS THE NDB THE LOCAL NODE NDB?
	MOVEI	T1,[ASCIZ \Local	\] ;YES, DISTINGUISH.
	PUSHJ	P,CONMES##	;TYPE OUT QUALIFIER
	PUSHJ	P,TYPNOD	;TYPE "SNM(NNM)"
	HRRZ	T1,NDBSID(W)	;GET THE MONITOR NAME
	SKIPE	T1		;SKIP IF UNKNOWN
	PUSHJ	P,CONMES##	;TYPE
	PUSHJ	P,PRSPC##	;SPACE
	HLRZ	T1,NDBSID(W)	;GET THE CREATION DATE
	JUMPE	T1,CPOPJ##	;EXIT IF UNKNOWN
	PJRST	CONMES##	;TYPE


;TYPNOD	ROUTINE TO TYPE OUT "STA-NAME(NUMBER)"
;CALL	MOVX	U,LDB
;	MOVX	W,NDB
;	PUSHJ	P,TYPNOD
;RETURN	CPOPJ

TYPNOD::NTDBUG
	HLRZ	T1,NDBSNM(W)	;GET THE STATION NAME POINTER
	MOVE	T2,(T1)		;GET THE STATION NAME
	SKIPN	T1		;IF THE NAME HASN'T BEEN RECEIVED YET,
	MOVE	T2,[SIXBIT /?????/] ;USE THIS FOR A NAME
	PUSHJ	P,PRNAME##	;PRINT IT
	MOVEI	T3,"("		;PAREN
	PUSHJ	P,COMTYO##	;PRINT
	HLRZ	T1,NDBNNM(W)	;GET THE NODE NUMBER
	PUSHJ	P,PRTDI8##	;TYPE
	PJSP	T1,CONMES##	;TYPE TERMINATOR AND RETURN
	ASCIZ	/) /


;TYPILT	ROUTINE TO "SEND OPR" TO TELL HIM THAT THE TOPOLOGY IS ILLEGAL
;CALL	MOVX	W,NODE THAT IS DUPLICATED
;	PUSHJ	P,TYPILT
;RETURN	CPOPJ

TYPILT:	PUSHJ	P,FRCSET##	;WE ARE GOING TO DO A SEND ON FRCLIN
	MOVEI	T1,[ASCIZ /SEND OPR Illegal network topology.  Two nodes /]
	PUSHJ	P,CONMES##	;SEND THE FIRST PART
	HLRZ	T1,NDBNNM(W)	;GET THE NODE'S NUMBER
	PUSHJ	P,PRTDI8##	; AND TYPE THAT OUT
	PUSHJ	P,PRPER##	;FINISH THE SENTENCE
	PJRST	PCRLF##		;FINISH THE LINE AND RETURN
SUBTTL	ABMT -- NeTwork Device subroutines.  (An imitation UUOCON)


COMMENT \

Here is a list of the subroutines that should be used to write network
device drivers.  It is hoped that the routines are device independant
enough to be useful for almost any network device.  The routines fall
into the following classes.

General purpose:
    NTDSET
	This routine sets up S, J, and W and makes sure that the device is
	still connected. (Sets IOSERR and IODERR if not)
    NTDREL
	This routine sets IOSREL in the lh of DEVIOS.  Primarly used as
	something to put in the dispatch entry for release.
    NTDONL
	This routine checks the bit IOSERR and skips if it's not set.
    NTDHNG
	This routine sets DVOFLN, returns "NT" and calls HNGSPT.
    NTDCLO
	Close output.  This routine returns "NT" and calls "OUT".
    NTDIBA
	This routine checks to see if there is an input buffer available.
	It skips if there is, non-skips if not.
    NTDOBA
	This routine checks to see if there is an output buffer available.
	It skips if there is, non-skips if not.
    NTDIDA
	This routine skips if the device has input data available
    NTDCBC
	This routine calculates the number of bytes (given a byte pointer
	in T4) that will fit in a buffer of size "T3".  (Kind of random
	calling convention I know, but this is only meant to be used by
	a few of the "NTD" routines.
    NTDPRV
	This routine skip's if the user has network privs (poke JACCT etc)

Input buffer handling.  (Interface to UUOCON)
    NTDSIB
	Setup Input Buffer.  This routine sets up the two DEVAXI words
	so the device service routine can do buffered "output" into the
	users "input" buffer...
    NTDAIB
	Advance Input Buffer.  Returns the input buffer to the user.
	(Using the buffered I/O analogy, this is just like the "OUT" UUO)

Output buffer handling.  (Interface to UUOCON)
    NTDSOB
	Setup Output Buffer.  This routine sets up the two DEVAXO words
	so the device service routine can do buffered "input" from the
	users buffer.  Also, this is necessary for NTDXMT to run.
    NTDAOB
	Advances the users output buffer.  Clears DEVAXO to indicate that
	the buffer is no longer setup.
    NTDXMT
	This routine sends one PCB's worth of data from the users buffer.
	It updates both DEVAXO and DEVAXO+1.

Input message handling. (PCB's)
    NTDISP
	This is the DAP dispatch routine.  Called with P1 pointing to the
	first "CNT" field of the DAP portion of the NCL message.
    NTDILD
	This is the "interrupt" level dispatch.  It is meant to be used
	by drivers that do not wish to queue their input message handling.
	To use this routine simply make the NDPICM dispatch entry point
	to NTDILD.
    NTDQIP
	This is the routine to queue an incoming PCB to be processed at
	UUO level.  (Use this routine as the NDPICM dispatch if you wish
	to do low level processing of input messages)
    NTDDID
	This is the routine that takes PCB's off the queue (DEVPCB(f))
	and calls NTDISP with them.  (Use this routine to process messages
	queued by NTDQIP)

Data request handling.
    NTDCDQ
	For output devices.  This routine checks to see if you have
	any available data requests.
    NTDDDQ
	For output devices.  This routine decrements DEVDRQ(F).  Should
	be called only after data has been sent.  Stops if no data requests
	are available.
    NTDDOD
	For input devices.  This routine takes a PCB in "U" and if it
	is not an interrupt message, decrements the number of outstanding
	data-requests (ie. lh(devdrq(f))).
    NTDXDQ
	For input devices.  This routine looks at the input buffer
	chain and determines if it should send any data requests.
	(Must NEVER be called from interrupt level since it doesn't OUCHE
	the buffer headers.)
    NTDRDQ
	Standard "canned" routine for handling incoming data requests.
	(ie. It is meant to be pointed to by the "DRQ" vector entry.)

Delaying jobs for data or data requests.
    NTDWTI
	Used to wait for input (understands asynch i/o)
    NTDWTO
	Used to wait for output.
    NTDIAV
	Called to signify input available
    NTDOAV
	Called to signify output data requests available

Connect and Disconnect processing.
    NTDCNC
	This is the default Connect Confirm dispatch handler. Used for most
	"normal" (eg LPT, CDR ...) devices
    NTDCNF
	This is a subset of NTDCNC (and is called by same) to process the
	MML and FEA(DCM,RLN,DVT,DVU,DVV) fields.
    NTDDSC
	This is the default Disconnect (both Initiate and Confirm) handler.
    NTDCNT
	This routine performs the connect function for "normal" devices.
	It assigns a SLA and waits for the connect to complete.
    NTDXPN
	This is a small routine to generate the "DPN" field of a connect
	for "normal" devices.
    NTDXSN
	This is line NTDXPN except that it writes an "SPN" field consisting
	of OBJ = OBJ.TK, PID = "JOB-NAME[P,PN]"
    NTDXDS
	This is a routine to send a disconnect and set the LAT state to
	"LAT.DC" (ie disconnect confirm wait)
    NTDNWD
	This is a routine that handles the NDPNWD (Node Went Down) entry
	for "normal" devices

Message construction and sending
    NTDHDR
	This builds a NCL header with the DLA field filled in.
    NTDHDI
	This is the same as NTDHDR except that the "interrupt message"
	bit is turned on in the NCT.
    NTDSST
	This is used to send a "set" status bits message
    NTDCST
	This is used to send a "clear" status bits message
    NTDXST
	This is used to send a status message.
    NTDWRT
	This routine gives a PCB to the network for delivery.

\
;NTDSET	GENERAL PURPOSE SET-UP ROUTINE
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NTDSET
;RETURN	CPOPJ		;ALWAYS.
;SETS UP J, S AND W.  IF THE DEVICE IS NOT CONNECTED (NODE WENT AWAY, OR
;	DISCONNECTED) IOSERR WILL BE SET IN DEVIOS(F)
;
NTDSET::			;GENERAL PURPOSE SET-UP ROUTINE
	LDB	J,PJOBN##	;GET JOB NUMBER FROM THE DDB
	HRRZ	T1,DEVNET(F)	;GET THE NUMBER OF THE NODE OWNING THE DEVICE
	MOVE	S,DEVIOS(F)	;SET UP S
	TLNE	S,IOSCON	;IF WE ARE CONNECTED, THEN
	PUSHJ	P,SRCNDB	; SET UP W WITH THE NDB ADDRESS
	  JRST	NTDSE1		;HERE IF NOT CONNECTED (OR NODE WENT AWAY)
	POPJ	P,		;ALL DONE. RETURN WITH REG'S SETUP

NTDSE1:				;HERE IF THE DEVICE IS NO LONGER CONNECTED
	MOVSI	S,IOSCON	;GET AND CLEAR THE
	ANDCAB	S,DEVIOS(F)	; "DEVICE IS CONNECTED" BIT
	MOVSI	S,IOSERR	;SET THIS ERROR
	IORB	S,DEVIOS(F)	; BIT FOR NTDONL TO SEE.
	POPJ	P,		;RETURN WITH REGS SETUP AND ERROR BITS ON.
;NTDCLO	ROUTINE TO PERFORM NORMAL "OUTPUT CLOSE" PROCESSING
;CALL	MOVE	F,DDB		;DEVICE TO CLOSE OUTPUT ON
;	PUSHJ	P,NTDCLO	;SAVE "NT", CALL OUT AND WAIT FOR COMPLETION
;RETURN	CPOPJ			;ALWAYS FROM WAIT1
;
NTDCLO::NTSAVE			;RETURN THE "NT" INTERLOCK (WE BETTER HAVE IT)
	PUSHJ	P,OUT##		;FORCE OUT ANY UN-EMPTIED BUFFERS
	PJRST	WAIT1##		;WAIT FOR EVERYTHING TO GO OUT.



;NTDREL	ROUTINE TO SET IOSREL WHEN A DEVICE IS RELEASED
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NTDREL	;MARK DEVICE AS RELEASED SO DATA REQUESTS
;				; WON'T CAUSE FALSE "WAKES" TO HAPPEN
;RETURN	CPOPJ			;ALWAYS
;
NTDREL::			;HERE ON A RELEASE
	MOVSI	S,IOSTBL!IOSERR	;CLEAR THESE TWO ERROR BITS
	ANDCAB	S,DEVIOS(F)
	MOVSI	S,IOSREL	;SET THE BIT THAT SAYS
	IORB	S,DEVIOS(F)	; WE HAVE RELEASED THIS DEVICE
	SETZM	DEVAXO(F)	;CLEAR POINTER TO OUTPUT BUFFER
	SETZM	DEVAXI(F)	;CLEAR POINTER TO INPUT AS WELL
	TLNE	S,IOSCON	;IF WE ARE STILL CONNECTED, THEN JUST
	POPJ	P,		; RETURN AND LET ZAPNET DISCONNECT LATER.
	LDB	T1,NETSLA	;IF NOT CONNECTED, CHECK FOR A LAT ENTRY
	JUMPE	T1,CPOPJ##	;IF NONE, THEN NO MORE TO DO

IFN PARANOID&P$LAT,<		;MAKE SURE WE ARE IN A VALID STATE.

	LDB	T2,LATSTA	;GET THE STATE.
	CAIN	T2,LAT.OK	;WE SHOULDN'T BE "OK" IF NOT CONNECTED
	STOPCD	.,STOP,ANFWLS,	;++ WRONG LAT STATE
>
	PUSHJ	P,GIVSLA	;RETURN THE LAT ENTRY (GO TO IDLE STATE)
	PJRST	CLNNET		;CLEAN OUT THE DDB (DRQ, NPD'S ETC...)
;NTDONL	ROUTINE TO SEE IF THE DEVICE IS ONLINE (NO ERROR CONDITIONS)
;CALL	MOVE	S,DEVIOS(F)	;GET DEVIOUS BITS IN S
;	PUSHJ	P,NTDONL	;SEE IF ON LINE
;RETURN	CPOPJ			;DEVICE HAS ERROR BITS SET.
;	CPOPJ1			;EVERYTHING SEEMS OK.
;
;NOTE!	THIS ROUTINE MAY BE USED AS THE ONLINE DISPATCH ENTRY.
;
NTDONL::			;CHECK TO SEE IF DEVICE IN ONLINE
	TLNN	S,IOSERR	;EVERYTHING OK?
	AOS	(P)		;LOOKS GOOD (NO ERROR BITS)
	POPJ	P,


;NTDHNG	ROUTINE TO PERFORM NORMAL DEVICE OFFLINE PROCESSING
;CALL	MOVE	F,DDB		;DEVICE TO MARK AS OFF-LINE
;	PUSHJ	P,NTDHNG	;SET DVOFLN, SAVE "NT", CALL HNGSTP
;RETURN	CPOPJ			;FROM HNGSTP
;
NTDHNG::MOVSI	T1,DVOFLN	;GET THE OFF-LINE BIT AND SET IT
	IORM	T1,DEVCHR(F)	;  SO THAT WE WILL GET ON-LINE INTERRUPT
	NTSAVE			;"SAVE" THE "NT" INTERLOCK
	PJRST	HNGSTP##	;CALL HUNGSTP AND RETURN



;NTDGON	ROUTINE TO SET ERROR WHEN DEVICE HAS "GONE" AWAY
;CALL	MOVE	F,DDB		;DEVICE WHICH IS KROAKED
;	PJRST	NTDGON		;MARK ERROR AND RETURN TO UUOCON

NTDGON::MOVEI	S,IODERR!IODTER	;DEVICE AND DATA ERRORS
	IORB	S,DEVIOS(F)	;ASSERT ERROR FLAGS
	POPJ	P,		;RETURN (PRESUMABLY TO UUOCON)
;NTDIBA	ROUTINE TO SEE IF THIS DEVICE HAS AN AVAILABLE INPUT BUFFER.
;CALL	MOVE	F,DDB
;	PUSHJ	P,NTDIBA
;RETURN	CPOPJ			;ALL INPUT BUFFERS ARE IN USE
;	CPOPJ1			;THERE IS AT LEAST ONE FREE INPUT BUFFER.
;
NTDIBA::			;HERE TO SEE IF THERE ARE ANY FREE INPUT BUFFERS
	MOVE	S,DEVIOS(F)	;GET A COPY OF DEVIOUS
	TLNE	S,IOSUSI	;DOES UUOCON WANT INPUT STOPPED (GOING TO SWAP)
	POPJ	P,		; IF SO, THEN GIVE "NO BUFFER" RETURN
	HRRZ	T1,DEVIAD(F)	;GET POINTER TO FIRST INPUT BUFFER
	JUMPE	T1,CPOPJ##	;IF NO BUFFER, NONE ARE FREE
	EXCTUX	<SKIPL (T1)>	;CHECK THE USE BIT.
	AOS	(P)		;IT'S NOT SET. THE BUFFER IS FREE
	POPJ	P,

;NTDOBA	ROUTINE TO SEE IF THIS DEVICE HAS ANY OUTPUT TO GO.
;CALL	MOVE	F,DDB
;	PUSHJ	P,NTDOBA
;RETURN	CPOPJ			;NO OUTPUT AVAILABLE
;	CPOPJ1			;THERE IS AT LEAST ONE FULL OUTPUT BUFFER
;
NTDOBA::			;HERE TO SEE IF THERE IS A FULL OUTPUT BUFFER
	MOVE	S,DEVIOS(F)	;GET A COPY OF THE STATUS BITS
	TLNE	S,IOSUSO	;DOES UUOCON WANT OUTPUT STOPPED (FOR SWAP)
	POPJ	P,		; IF SO, THEN GIVE THE "NO BUFFER" RETURN
	HRRZ	T1,DEVOAD(F)	;GET A POINTER TO THE USERS BUFFER
	JUMPE	T1,CPOPJ##	;IF NO BUFFER, NONE ARE FREE
	EXCTUX	<SKIPGE (T1)>	;CHECK THE USE BIT
	AOS	(P)		;THE BUFFER HAS DATA.
	POPJ	P,
;NTDIDA	ROUTINE THAT SKIPS IF AN "IN" UUO WOULD BE APPROPRIATE
;	CALLED FROM MSGSER TO SEE IF INPUT DATA IS AVAILABLE
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NTDIDA
;RETURN	CPOPJ			;NO PCB'S QUEUED FOR PROCESSING
;	CPOPJ1			;PCB'S ARE QUEUED. MSGSER SHOULD "CALIN"
;
NTDIDA::SETZM	DEVAXI(F)	;CLEAR BOTH THE AUX INPUT POINTERS
	SETZM	DEVAXI+1(F)	;  SINCE MSGSER WILL GIVE US A NEW BUFFER.
	HRRZ	T1,DEVPCB(F)	;FIRST SEE IF THERE IS AN INPUT PCB QUEUED
	JUMPN	T1,CPOPJ1##	;  IF THERE IS, TELL MSGSER TO DO AN "IN"
	MOVE	S,DEVIOS(F)	;SEE IF WE ARE STILL
	TLNN	S,IOSCON	;  CONNECTED.  IF NOT, WE
	POPJ	P,		;  HAD BETTER NOT SEND A DRQ.
	HLRZ	T1,DEVDRQ(F)	;SEE IF WE'VE REQUESTED DATA
	JUMPN	T1,CPOPJ##	;  IF WE'VE ALREADY SEND DRQ, THEN WE'RE DONE
	NETDBJ			;THE REST OF THIS CODE MUST BE INTERLOCKED
	MOVEI	T1,1		;MSGSER WILL SUPPLY US WITH ONLY "1" BUFFER
	PUSHJ	P,NCSDRQ	;SEND THE DATA-REQUEST (FOR 1 MSG)
	  POPJ	P,		;IF NO CORE, EXIT WITH-OUT UPDATING DEVDRQ
	MOVSI	T1,1		;GET A LEFT-HALF "1"
	ADDM	T1,DEVDRQ(F)	;INCREMENT THE OUTSTANDING DATA-REQUEST COUNT
	POPJ	P,		;TELL MSGSER THAT THERE'S NO DATA FOR IT.
;NTDCBC	ROUTINE TO CALCULATE BYTE COUNT.
;CALL	MOVE	T4,BYTE POINTER	;THIS ROUTINE USES ONLY THE "SIZE" FIELD
;	MOVEI	T3,WORD COUNT	;THIS IS THE SIZE OF THE BUFFER TO FILL
;RETURN	CPOPJ	WITH T1 := NUMBER OF BYTES THAT WILL FIT IN THE BUFFER.
;
;NOTE!!	SORRY ABOUT THE SCREWY AC CONVENTIONS, BUT IT FITS IN WITH
;	THE OTHER "NTD" ROUTINES.

NTDCBC::LDB	T2,[POINT 6,T4,11]  ;GET THE "SIZE" OF THE BYTE
	MOVEI	T1,^D36		;NUMBER OF BITS IN A WORD
	IDIVI	T1,(T2)		;T1 := NUMBER OF BYTES IN A WORD
	IMULI	T1,(T3)		;T1 := NUMBER OF BYTES IN "T3" WORDS
	POPJ	P,



;NTDPRV	ROUTINE TO CHECK PRIVS. SKIPS IF USER MAY HACK THE NET
;CALL	PUSHJ	P,NTDPRV
;RETURN	CPOPJ			;DON'T LET HIM HACK
;	CPOPJ1			;HE CAN DO ANYTHING HE WANTS.

NTDPRV::PUSH	P,J		;SAVE FEK OR WHATEVER
	SKIPN	J,.CPJOB##	;GET THIS JOB'S NUMBER
	JRST	JPOPJ1##	;INTERRUPT LEVEL IS ALWAYS A GOOD GUY
	PUSH	P,T1		;NTDPRV CLOBBERS NO REGISTERS
	MOVSI	T1,JP.POK	;POKE, 1-2, OR JACCT ARE GOOD GUYS
	PUSHJ	P,PRVBIT##	;CALL COMCON'S ROUTINE
	AOS	-2(P)		;PRVBIT SKIPS IF NOT PRIV
	POP	P,T1		;RESTORE T1 (AREN'T WE NICE)
	JRST	JPOPJ##		;RESTORE J AND (SKIP) RETURN



;NTDSTP	ROUTINE TO GENERATE A STOP CODE.  USED WHERE TOO LAZY TO THINK ONE UP.
;CALL	PUSHJ	P,NTDSTP	;?? HORRIBLE BUG ??
;RETURN	HA.

NTDSTP::STOPCD	CPOPJ##,STOP,WEM, ;++ DAMN!
COMMENT	\

	Protocol for using the Input Buffer Management routines.

     When using the input buffer management routines the following
words in the DDB are used to contain status information:

    DEVAXI	This word is anaglous to the ".BFPTR" word in a user-mode
		buffer control block.  It is a byte pointer to the users
		buffer.
    DEVAXI+1	This word is analogous to the ".BFCTR" word.  It is initialized
		to be the number of bytes that will fit in the user's buffer.

Here is a skeleton routine that should indicate how these routines are
intended to be used.


;Assume that calling the routine "D.NEXT" will supply the next data byte
; (i.e., from the NCL message) and skip return unless the message is exhausted
; in which case it will error (non-skip) return.

;Before we start copying we must set up the buffer.

	MOVSI	S,IOSUSI	;FIRST CLEAR "UUOCON STOPPED INPUT"
	ANDCAB	S,DEVIOS(F)	; SO THAT NTDSIB WILL WORK
	PUSHJ	P,NTDSIB	;SET UP THE INPUT BUFFER (DEVAXI, +1)

;Now we merely loop copying the data

LOOP:				;EVERY PROG SHOULD HAVE A LOOP!!
	PUSHJ	P,D.NEXT	;GET THE NEXT CHARACTER.
	  PJRST	NTDA1B		;IF NO MORE, THEN ADVANCE THE BUFFER AND
				; GIVE A SKIP RETURN TO NTDISP SO THAT
				; IT WILL ADVANCE TO THE NEXT NCL SUB-
				; MESSAGE.
	SOSGE	DEVAXI+1(F)	;DECREMENT THE NUMBER OF UN-USED BYTES IN
	  JRST	SET-IOBKTL	; THE BUFFER.  (IT IS IMPORTANT TO KEEP THIS
				; COUNT CORRECT.  NTDAIB USES IT TO CALCULATE
				; THE ITEM-COUNT TO PUT IN THE BUFFER-HEADER)
				; IF THE COUNT COUNTS OUT, THEN SET BLOCK-TO-
				; LARGE, OR IF YOU WANT TO KEEP GOING CALL
				; "NTDAIB" FOLLOWED BY "NTDSIB" AND CONTINUE
	EXCTUU	<IDPB T1,DEVAXI(F)> ; STORE THE BYTE IN "T1"
	JRST	LOOP		;CONTINUE UNTIL MESSAGE IS EXHAUSTED



\
;NTDSIB	ROUTINE TO SET UP AN INPUT BUFFER
;CALL	MOVEI	F,DDB		;DEVIAD(F) = USERS INPUT BUFFER
;	MOVEI	T4,BYTE-SIZE	;USED IN CALCULATING ITEM COUNT (DEVAXI+1)
;	PUSHJ	P,NTDSIB	;SET UP INPUT BUFFER
;RETURN	NOT-AT-ALL		;IF THE USERS BUFFER IS NOT IN CORE (PAGE FAULT)
;	CPOPJ			;NO USER BUFFER AVAILABLE
;	CPOPJ1			;BUFFER IS SET UP. THE FOLLOWING ANALOG OF
;				; THE STANDARD BUFFER CONTROL BLOCK IS SET UP.
;				; DEVAXI(F) := BYTE POINTER TO USERS BUFFER
;				; DEVAXI+1(F) := COUNT OF BYTES IN BUFFER.
;				;IF YOU JUST PRETEND THAT YOU ARE DOING USER-
;				; LEVEL BUFFERED OUTPUT AND EVERYTHING SHOULD
;				; WORK FINE!
;NOTE	IF THIS ROUTINE IS CALLED WITH THE BUFFER ALREADY SET UP, IT WILL
;	MERELY RANGE CHECK THE USERS BUFFER AND RETURN.  THIS MAKES IT EASY
;	TO USE COMMON CODE TO PACK MANY NCL MESSAGES INTO ONE USER BUFFER.
;
NTDSIB::			;SETUP INPUT BUFFER
	SKIPE	DEVAXI(F)	;HAVE WE ALREADY SET THIS UP?
	JRST	[HRRZ T1,DEVIAD(F) ;YES, RANGE CHECK THE BUFFER TO
		 PUSHJ P,BRNGE## ; ENSURE THAT IT IS STILL IN CORE
		 JRST NTDSI1]	;CLEAR USER'S BUFFER AND SKIP RETURN
	PUSHJ	P,NTDIBA	;SINCE NOT SET-UP, SEE IF A BUFFER IS AVAILABLE
	  POPJ	P,		; IF NOT, THEN GIVE ERROR RETURN
	HRRZ	T1,DEVIAD(F)	;SECTION-LOCAL ADDRESS OF USER BUFFER
	PUSHJ	P,BRNGE##	;MAKE SURE THAT THE BUFFER'S IN BEFORE POKING IT
	LSH	T4,^D24		;MAKE T4 INTO A BYTE POINTER
	HRRZ	T1,DEVIAD(F)	; TO THE BYTE JUST BEFORE THE FIRST BYTE
	HRRI	T4,1(T1)	; IN THE USER'S BUFFER.  (SO "I"LDB WORKS)
	EXCTUX	<HLRZ T3,(T1)>	;GET THE BUFFER SIZE+1 (IN WORDS)
	SUBI	T3,1		;GET THE BUFFER SIZE (IN WORDS)
	PUSHJ	P,NTDCBC	;GET THE BUFFER SIZE (IN BYTES)
	MOVEM	T4,DEVAXI(F)	;SET UP THE ".BFPTR" WORD
	MOVEM	T1,DEVAXI+1(F)	;SET UP THE ".BFCTR" WORD
NTDSI1:	HRRZ	T1,DEVIAD(F)	;GET THE BUFFER ADDRESS
	PUSHJ	P,BUFCLR##	;CLEAR USER'S BUFFER BEFORE RETURNING
	  JFCL			;ZERO LENGTH: SHOULD NEVER OCCUR
	JRST	CPOPJ1##	;SKIP RETURN: THE BUFFER IS SET UP
;NTDAIB	ROUTINE TO ADVANCE A USERS BUFFER (ANALOG OF THE OUTPUT UUO)
;CALL	MOVEI	F,DDB		;DEVAXI(F) := BYTE POINTER
;				;DEVAXI+1(F) := NUMBER OF UNUSED BYTES IN BUFFER
;	PUSHJ	P,NTDAIB	;ADVANCE INPUT BUFFER (GIVE DATA TO THE USER)
;RETURN	CPOPJ			;DEVAXI(F) := 0 INDICATING NO BUFFER IS SETUP
;
NTDA1B::AOS	(P)		;SPECIAL ENTRY FOR THOSE WHO WANT A SKIP RETURN
NTDAIB::SKIPN	T4,DEVAXI(F)	;GET BYTE POINTER
	STOPCD	.,STOP,ANFAIB,	;++ NO BUFFER SET UP WHEN ADVACING INPUT

	HRRZ	T1,T4		;GET A COPY OF THE ADDRESS OF THE LAST WORD
	SETZ	T2,		;GET A ZERO (TO ZERO FILL LAST WORD)
NTDAI1:	IBP	T4		;INCREMENT T4 ONE MORE BYTE
	CAIN	T1,(T4)		;IF GOES TO NEW WORD, THEN DONE ZERO FILLING
	JRST	[EXCTUU <DPB T2,T4> ;STORE A ZERO BYTE
		 JRST NTDAI1]	;  AND LOOP OVER REST OF THE WORD

	HRRZ	T1,DEVIAD(F)	;GET POINTER TO BUFFER HEADER
	LDB	T2,PIOMOD##	;GET I/O MODE AND
	CAIE	T2,A8		; IF IT'S 8-BIT ASCII OR
	CAIN	T2,BYTMOD	; IF IT'S BYTE MODE, THEN
	JRST	NTDAI2		; COMPUTE ITEM-COUNT IN BYTES

	SUBI	T4,2(T1)	;COMPUTE WORD COUNT (SUBTRACT BYTE POINTERS)
	EXCTUU	<HRRM T4,1(T1)>	;STORE THE WORD-COUNT IN THE BUFFER HEADER
	JRST	NTDAI3		;GO REJOIN COMMON CODE

NTDAI2:				;HERE IF WE WANT ITEM COUNT TO BE BYTE COUNT
	EXCTUX	<HLRZ T3,(T1)>	;TO COMPUTE BYTE COUNT, FIRST GET
	SUBI	T3,1		; THE SIZE OF THE BUFFER IN WORDS.
	PUSHJ	P,NTDCBC	;NOW GET THE SIZE OF THE BUFFER IN BYTES.
	SUB	T1,DEVAXI+1(F)	;SUBTRACT UNUSED BYTE COUNT TO GET BYTES USED.
	HRRZ	T2,DEVIAD(F)	;NOW TO STORE THE COUNT FIRST GET BUFFER HEADER
	EXCTUU	<HRRM T1,1(T2)>	; AND USE THAT TO STORE THE ITEM COUNT FOR USER.

NTDAI3:				;COMMON CODE TO ADVANCE USERS BUFFER.
	SETZM	DEVAXI(F)	;CLEAR ".BFPTR" TO INDICATE BUFFER NOT SETUP
	PUSHJ	P,ADVBFF##	;ADVANCE USERS INPUT BUFFER
	  JRST	.+2		;WE MUST REMEMBER IF UUOCON WANT'S INPUT STOPED
	POPJ	P,		;RETURN WITH USER'S BUFFER ADVANCED
	MOVSI	S,IOSUSI	;GET AND SET "UUOCON STOPED INPUT" BIT.  THIS
	IORB	S,DEVIOS(F)	;  GETS SET WHEN SWAPING, ^C ETC.
	POPJ	P,		;RETURN.  NTDIBA WILL NOTICE THE BIT AND STOP IO
COMMENT \

	Protocol for using the output buffer management routines.

     When using the output buffer management routines the following
words in the DDB are used to contain status information:

     DEVAXO	This word contains a byte pointer to the unsent portion
		of the users buffer.
     DEVAXO+1	This word contains the number of unsent bytes in the users
		buffer.

     You may criticize me on the design of this mechanism and contend that
this is not the most efficient way to represent the state of the users buffer.
To this I will have to agree.  (Consider all the trouble that NTDXMT has to
go through to convert back and forth from byte to word counts...)  I have
only two points to make in my defense:

   1)	Byte mode falls out with no added effort.

   2)	This scheme forms a symmetry with the input routines that, in my
	opinion, lends a rather pleasant feeling of completeness to this
	whole mess.


Now for a skeleton routine showing how to use these routines.

	MOVSI	S,IOSUSO	;FIRST CLEAR THE "UUOCON STOPPED OUTPUT" BIT
	ANDCAB	S,DEVIOS(F)	; SO THAT NTDSOB WILL WORK
LOOP:				;HERE TO SEND MORE DATA.
	PUSHJ	P,NTDSET	;SET UP W & S. THIS MUST BE DONE AFTER
				;  A CALL TO ANY OF THE WAIT ROUTINES AS
				;  THEY ALL CLOBBER W. (ON PURPOSE.)
	MOVE	S,DEVIOS(F)	;RELOAD S (WAIT ROUTINES CLOBBER S)
	MOVEI	T1,^DBYTE-SIZE	;GET THE BYTE SIZE SO THAT
				; "NTDSOB" CAN SET UP THE PROPER BYTE COUNT.
	PUSHJ	P,NTDSOB	;SET UP DEVAXO, DEVAXO+1
	  JRST	NO-MORE-DATA	; ERROR RETURN INDICATES WE ARE DONE.

	PUSHJ	P,NTDCDQ	;CHECK TO SEE IF WE HAVE A DATA REQUEST.
	  JRST	WAIT-AWHILE	; IF NOT, EITHER WAIT, OR RETURN TO UUOCON.

	MOVEI	T1,COMPRESSION	;COMPRESSION/CONVERSION (PCV.??)
	PUSHJ	P,NTDXMT	;SEND ONE PCB'S WORTH OF DATA.
	  JRST	PROCESS ERROR	;IF OUT OF CORE OR IOBKTL
	PUSHJ	P,NTDDDQ	;DECREMENT DRQ NOW THAT MSG HAS GONE

	SKIPN	NETAXO+1(F)	;HAVE WE EMPTIED THIS BUFFER??
	PUSHJ	P,NTDAOB	; IF SO, THEN ADVANCE THE USERS BUFFER
	JRST	LOOP		;KEEP IT UP TILL WE GET IT WRONG.
*** NOTE ***

     If one were so inclined one could do something like replace the
call to NTDXMT by a loop of the form:

LOOP1:	SOSGE	DEVAXO(F)	;DECREMENT ".BFCTR"
	  JRST	DONE
	EXCTUX	<ILDB T1,DEVAXO(F)> ;GET NEXT BYTE
	PUSHJ	P,PUT-BYTE-SOMEWHERE
	JRST	LOOP1		;CONTINUE UNTIL BUFFER IS EXHAUSTED.


     For network devices I see no reason to do this since NTDXMT is not
only faster (It copys a PCB's worth of data with a single BLT) but its
a lot easier too!

\
;NTDSOB	ROUTINE TO SET UP A BUFFER FOR OUTPUT
;CALL
;	MOVEI	T1,BYTE SIZE (USUALLY DEPENDS ON THE CONVERSION CODE)
;	PUSHJ	P,NTDSOB	;SET UP OUTPUT BUFFER
;RETURN	NOT-AT-ALL		;IF THE USERS BUFFER WAS NOT INCORE (PAGE FAULT)
;	CPOPJ			;NO OUTPUT BUFFER AVAILABLE
;	CPOPJ1			;BUFFER SET UP.
;				; DEVAXO(F) := BYTE POINTER TO USERS BUFFER
;				; DEVAXO+1(F) := COUNT OF BYTES LEFT TO GO.
;
NTDSOB::			;SET UP OUTPUT BUFFER
	SKIPE	DEVAXO(F)	;IS THE BUFFER ALREADY SET UP?
	  JRST	[AOS (P)	;IF SO, GIVE A GOOD RETURN.
		 HRRZ T1,DEVOAD(F) ; BUT FIRST MAKE SURE THAT THE BUFFER
		 PJRST BRNGE##]	; IS STILL IN CORE.
	PUSH	P,T1		;SAVE THE BYTE SIZE OVER CALL TO NTDOBA
	PUSHJ	P,NTDOBA	;IS THERE AN OUTPUT BUFFER AVAILABLE?
	  JRST	TPOPJ##		;IF NOT, THEN GIVE THE ERROR RETURN
	POP	P,T4		;GET BYTE SIZE BACK IN T4
	LSH	T4,^D24		;MAKE T4 INTO A BYTE POINTER
	HRRZ	T1,DEVOAD(F)	; THAT WHEN INCREMENTED POINTS TO THE
	HRRI	T4,1(T1)	; FIRST BYTE IN THE USER'S BUFFER.
	EXCTUX	<HRRZ T3,1(T1)>	;GET THE BUFFER ITEM COUNT (WORDS OR BYTES)
	MOVEI	T1,(T3)		;COPY THE WORD COUNT
	LDB	T2,PIOMOD##	;GET THE MODE THAT WE ARE IN.
	CAIE	T2,BYTMOD	; AND IF WE ARE NOT IN BYTE MODE,
	CAIN	T2,A8		; OR 8-BIT ASCII MODE,
	TRNA			; (WRONG)
	PUSHJ	P,NTDCBC	; THEN WE NEED TO CONVERT WORD-COUNT TO BYTES.
	MOVEM	T4,DEVAXO(F)	;STORE THE BYTE POINTER
	MOVEM	T1,DEVAXO+1(F)	;STORE THE BYTE COUNT
	JRST	CPOPJ1##	;GIVE GOOD (SKIP) RETURN



;NTDAOB	ROUTINE TO ADVANCE THE USERS OUTPUT BUFFER
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NTDAOB	;ADVANCE OUTPUT BUFFER
;RETURN	CPOPJ			;ALWAYS
;
NTDAOB::			;ADVANCE OUTPUT BUFFER
	SKIPN	DEVAXO(F)	;HAS THE BUFFER BEEN SETUP
	STOPCD	.,STOP,ANFAOB,	;++ NO BUFFER SET UP WHEN CALLING NTDAOB
	SETZM	DEVAXO(F)	;INDICATE THAT IT'S NO LONGER SET UP
	PUSHJ	P,ADVBFE##	;ADVANCE THE BUFFER
	  JRST	.+2		;WE MUST SHUT DOWN OUTPUT FOR SOME REASON
	POPJ	P,		;ALL DONE
	MOVSI	S,IOSUSO	;GET AND SET THE "UUOCON STOPPED OUTPUT"
	IORB	S,DEVIOS(F)	;  NTAOBA WILL NOTICE THIS BIT AND SAY
	POPJ	P,		;  THAT NO BUFFERS ARE AVAILABLE FOR OUTPUT.
;NTDXMT	ROUTINE TO SEND ONE PCB'S WORTH OF DATA FROM A USERS BUFFER.
;	UNDERSTANDS ABOUT BREAKING USERS BUFFERS INTO MANY PCB'S
;CALL	MOVEI	T1,COMPRESSION CODE (PCV.??)
;	MOVEI	T2,IDCTYP+INTERRUPT BIT(=1B18)
;	MOVEI	F,DDB		;WITH DEVAXO, +1 SET UP
;	PUSHJ	P,NTDXMT	;SEND A PCB'S WORTH OF DATA
;RETURN	NOT-AT-ALL		;IF A PAGE FAULT OCCURS
;	CPOPJ			;IO-ERROR BIT IN T1.  IF T1 = 0, NO CORE
;	CPOPJ1			;THE PCB WAS SENT OK.

NTDXMT::SKIPE	%NTNIP		;DOING ETHERNET-TYPE STUFF?
	JRST	NTDXNT		;YEAH, MUST USE CONTIGUOUS BUFFERS
	SKIPN	DEVAXO(F)	;IS THE BUFFER SET UP?
	STOPCD	.,STOP,ANFXMT,	;++ NO BUFFER SET UP WHEN CALLING NTDXMT

	PUSH	P,T1		;SAVE THE COMPRESSION CODE (UNTIL THE VERY END)
	PUSH	P,T2		;SAVE THE INTERRUPT-BIT+IDCTYP
	HRRZ	T1,DEVOAD(F)	;NOW RANGE CHECK THE BUFFER.
	PUSHJ	P,BRNGE##	; JUST INCASE HE DECIDED TO SWAP

	LDB	T2,[POINT 6,DEVAXO(F),11] ;NOW GET NUMBER OF WORDS IN BUFFER.
	MOVEI	T3,^D36		;START WITH BYTE SIZE AND 36/BYTE SIZE
	IDIVI	T3,(T2)		; YIELDS T3 := NUMBER OF BYTES/WORD
	MOVE	T2,DEVAXO+1(F)	;GET THE NUMBER OF BYTES LEFT.
	SETZ	T1,		;CLEAR THE HIGH ORDER WORD
	DIVI	T1,(T3)		;CONVERT INTO WORDS
	SKIPE	T2		; BUT BE SURE TO
	ADDI	T1,1		; ROUND UP TO GET TOTAL WORDS IN BUFFER.
	PUSH	P,T3		;SAVE THE BYTE/WORD FOR LATER
	PUSH	P,T1		;SAVE LENGTH FOR A BIT.
NTDXM0:	MOVEI	T1,4		;GET A PCB START WITH 4 WORDS (MAX) OF HEADER
	ADD	T1,(P)		;  ADD IN THE LENGTH OF THE BUFFER
	CAILE	T1,MSGXMW##	;IF THIS IS MORE THAN THE "MAXIMUM" PCB SIZE
	MOVEI	T1,MSGXMW##	;  THEN JUST REQUEST THE "MAXIMUM"
	MOVEI	T3,NTDHDR	;ASSUME THAT THIS IS NOT AN INTERRUPT MESSAGE
	MOVEI	T2,(1B0)	;GET THE "INTERRUPT MESSAGE" BIT
	TDNE	T2,-2(P)	; AND IF IT'S SET
	MOVEI	T3,NTDHDI	; THEN GET AN INTERRUPT HEADER.
	PUSHJ	P,(T3)		;GO BUILD THE HEADER
	  JRST	[ADJSP P,-4		;IF NO CORE, CLEAN UP THE STACK
		SETZ	T1,		; CLEAR T1 TO SAY ERROR WAS NO FREE CORE
		POPJ	P,]		; AND GIVE AN ERROR RETURN TO THE CALLER
	MOVE	T2,PCBCTR(U)	;NOW CALCULATE THE NUMBER OF FREE DATA WORDS
	ADDI	T2,3+3(P3)	;START WITH BYTES IN HEADER, ALLOW FOR "CNT"
	LSH	T2,-2		; AND "TYP", +3 TO ROUND UP. LSH TO GET WORDS.
	MOVE	T4,PCBALN(U)	;GET THE TOTAL NUMBER OF WORDS IN THE PCB
	SUBI	T4,(T2)		;SUBTRACT WORDS USED, GET WORDS LEFT IN PCB.
	POP	P,T1		;GET THE BUFFER LENGTH BACK
	POP	P,T3		;GET THE BYTES/WORD BACK
	CAIL	T4,(T1)		;SKIP IF PCB WON'T FINISH OFF THE BUFFER
	JRST	NTDXM1		;GO TO CODE TO SEND FINAL FRAGMENT OF BUFFER.

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;HERE IF WE HAVE TO "FRACTURE" THE BUFFER INTO SEVERAL MESSAGES, AND THIS
; IS NOT THE LAST FRAGMENT OF THE BUFFER.
; T3 := BYTES/WORD
; T4 := NUMBER OF WORDS LEFT IN THE PCB (WE WILL USE THEM ALL)

	POP	P,T1		;GET THE IDC TYPE FIELD
	ANDI	T1,77		;AND JUST THE TYPE (MASK OUT THE INTERRUPT BIT)
	CAIL	T1,DC.DAT	;NOW SEE IF THIS IS A MESSAGE TYPE THAT WE
	CAILE	T1,DC.DAR	; CAN LEGALLY FRAGMENT INTO MULTIPLE MESSAGES
	JRST	[POP P,T1	;IF WE CAN'T BREAK THE MESSAGE, CLEAN UP STACK
		 MOVEI T1,IOBKTL; TELL OUR CALLER THAT ERROR WAS BLOCK TO LARGE
		 POPJ P,]	; AND GIVE AN ERROR RETURN
	PUSH	P,[DC.DAT]	;CHANGE THE MESSAGE TYPE TO BE DATA W/O E-O-R.

	MOVEI	T1,(T4)		;GET THE NUMBER OF WORDS LEFT IN THE PCB
	IMULI	T1,(T3)		;CONVERT WORD COUNT TO BYTE COUNT
	MOVN	T2,T1		;GET MINUS THAT BYTE COUNT
	ADDM	T2,DEVAXO+1(F)	; AND UPDATE THE NUMBER OF UNSENT BYTES.

	SKIPG	DEVAXO+1(F)	;JUST A QUICK CHECK OF MY ARITHMETIC
	STOPCD	.,STOP,ANFUBN,	;++ UNSENT BYTES COUNT WENT NEGATIVE
	JRST	NTDXM2		;GO SEND "CNT", "TYP", AND THE DATA.



;HERE IF THE PCB WILL HOLD ALL THE REST OF THE BUFFER.
; T1 := WORDS LEFT IN THE USER'S BUFFER

NTDXM1:	MOVEI	T4,(T1)		;COPY THE NUMBER OF WORDS TO GO (FOR THE BLT)
	SETZ	T1,		;GET A "ZERO"
	EXCH	T1,DEVAXO+1(F)	;GET THE NUMBER OF "BYTES" TO GO. CLEAR BUF CNT.
;	JRST	NTDXM2		;GO SEND "CNT", "TYP", AND THE DATA

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;NTDXM2	HERE TO SEND "CNT", "TYP", AND DATA PORTIONS OF THE MESSAGE.
; -1(P) := CONVERSION CODE (WE MUST HANDLE BINARY MODE ESPECIAL)
;   (P) := IDC TYPE
;    T1 := NUMBER OF "BYTES" (OF WHATEVER SIZE) TO GO.
;    T4 := NUMBER OF "WORDS" TO GO (USED BY THE BLT, AND BINARY MODE)
;
;NOTE.  BECAUSE THE FEK IS LAZY WHEN IT DOES THE BINARY CONVERSION, WE MUST
;	CALCULATE BY HAND WHAT THE LENGTH OF THE DATA MESSAGE WILL BE AFTER
;	CONVERSION.  BINARY CONVERSION PACKS THE DATA 2 WORDS INTO 9 BYTES.

NTDXM2:	MOVEM	T1,PCBCT2(U)	;SET THE BYTE COUNT FOR THE SECOND BUFFER
	MOVE	T2,-1(P)	;GET THE CONVERSION CODE
	CAIE	T2,PCV.BN	; AND SEE IF IT'S BINARY.
	JRST	NTDXM3		;IF NOT BINARY, THEN USE BYTE COUNT IN "T1"
	MOVEI	T1,(T4)		;CALCULATE BYTE COUNT BY HAND. START WITH WORDS.
	LSHC	T1,-1		;GET DOUBLE-WORDS. (REMAINDER = SIGN OF T2)
	IMULI	T1,^D9		;2 WORDS FIT IN 9 BYTES.
	SKIPGE	T2		;IF THERE IS AN "ODD" WORD, THAT WORD
	ADDI	T1,5		; FIVE EXTRA BYTES.
NTDXM3:	ADDI	T1,1		;ACCOUNT FOR THE "TYP" FIELD IN THE LENGTH
;CNT
	XMT	T1		;SEND THE LENGTH OF THE DAP MSG
;TYP
	POP	P,T1		;GET THE IDC TYPE BACK
	XMT1	T1		;THIS WILL MASK THE TYPE TO 8 BITS

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;HERE TO COPY THE DATA FROM THE USERS BUFFER AND INTO THE PCB.  AT THIS POINT:
;  1)	DEVAXO+1(F) HAS BEEN UPDATED TO REFLECT THE NUMBER OF BYTES COPIED.
;  2)	T4 := THE NUMBER OF WORDS TO GO
;  3)	(P) := THE CONVERSION CODE (REMEMBER...)
;  4)	P3 := THE NUMBER OF BYTES IN PCB'S FIRST BUFFER (FROM "CNT", "TYP" ETC)
;  5)	P2 - LAST USED BYTE IN THE PCB

	ADDB	P3,PCBCTR(U)	;FIRST UPDATE THE PRIMARY BUFFER'S LENGTH
	MOVEI	T1,1(P2)	;GET A POINTER TO THE FIRST FREE WORD IN THE PCB
	LDB	T2,[POINT 6,DEVAXO(F),11] ;GET THE BYTE SIZE
	LSH	T2,6		;POSITION THE SIZE (BYTE POINTER STYLE)
	TLO	T1,440000(T2)	;MAKE T1 INTO A FULL-FLEDGED BYTE POINTER
	MOVEM	T1,PCBPT2(U)	;STORE AS THE SECONDARY BUFFER POINTER
	MOVE	T2,DEVAXO(F)	;GET THE ADDRESS OF THE USER'S DATA
	ADDM	T4,DEVAXO(F)	;UPDATE THE ADDRESS FOR NEXT TIME
	IBP	T2		;MAKE SURE WE POINT TO THE RIGHT WORD

IFE FTXMON,<
	HRLI	T1,(T2)		;SET UP THE BLT'S SOURCE FIELD
	ADDI	T4,-1(T1)	;SET UP THE BLT'S TERMINATING ADDRESS
	EXCTUX	<BLT T1,(T4)>	;COPY THE DATA
> ;END IFE FTXMON

IFN FTXMON,<
	HRRZ	T3,T1		;FORM MONITOR DESTINATION ADDRESS
	XSFM	T1		;GET PCS
	HRLI	T2,(T1)		;FORM USER SOURCE ADDRESS
	MOVE	T1,T4		;GET LENGTH WHERE WE NEED IT
	XBLTUX	T1		;COPY THE DATA
> ;END IFN FTXMON

	POP	P,T1		;GET THE CONVERSION CODE (FINALLY)
	PUSHJ	P,NTDWRT	;SEND THE PCB.
	JRST	CPOPJ1##	;GIVE GOOD RETURN
;TEMP SCRATCH FOR TESTING OUT ANF/NI

NTDXNT::SKIPN	DEVAXO(F)	;IS THE BUFFER SET UP?
	STOPCD	.,STOP,ANIXMT,	;++ NO BUFFER SET UP WHEN CALLING NTDXNT
	PUSH	P,T1		;SAVE THE COMPRESSION CODE (UNTIL THE VERY END)
	PUSH	P,T2		;SAVE THE INTERRUPT-BIT+IDCTYP
	HRRZ	T1,DEVOAD(F)	;NOW RANGE CHECK THE BUFFER.
	PUSHJ	P,BRNGE##	; JUST INCASE HE DECIDED TO SWAP
	MOVE	T1,DEVAXO+1(F)	;GET THE NUMBER OF BYTES LEFT.
	MOVE	T3,-1(P)	;RETRIEVE PCV.XX CODE
	CAIE	T3,PCV.BN	;12-BIT BINARY?
	JRST	NTDXN0		;NO, ONE USER-BYTE PER NETWORK-BYTE THEN
	LSHC	T1,-1		;YES. T1=PAIRS OF BYTES (=24 BITS)
	IMULI	T1,^D3		;EACH PAIR OF BYTES IS WORTH 3 NETWORK BYTES
	CAIGE	T2,0		;DANGLING ("ODD") BYTE?
	ADDI	T1,^D2		;YES, THAT'S WORTH ANOTHER 1.5 (OR SO) BYTES
NTDXN0:	PUSH	P,T1		;SAVE BYTE COUNT
	ADDI	T1,3 + <4*4>	;START WITH 4 WORDS (MAX) OF HEADER
	LSH	T1,-2		;T1=WORD SIZE FOR PCB BUFFER
	CAILE	T1,MSGXMW##	;IF THIS IS MORE THAN THE "MAXIMUM" PCB SIZE
	MOVEI	T1,MSGXMW##	;  THEN JUST REQUEST THE "MAXIMUM"
	MOVEI	T3,NTDHDR	;ASSUME THAT THIS IS NOT AN INTERRUPT MESSAGE
	MOVEI	T2,(1B0)	;GET THE "INTERRUPT MESSAGE" BIT
	TDNE	T2,-2(P)	; AND IF IT'S SET
	MOVEI	T3,NTDHDI	; THEN GET AN INTERRUPT HEADER.
	PUSHJ	P,(T3)		;GO BUILD THE HEADER
	  JRST	[ADJSP	P,-3		;IF NO CORE, CLEAN UP THE STACK
		SETZ	T1,		; CLEAR T1 TO SAY ERROR WAS NO FREE CORE
		POPJ	P,]		; AND GIVE AN ERROR RETURN TO THE CALLER
	MOVE	T1,PCBALN(U)	;GET THE TOTAL NUMBER OF WORDS IN THE PCB
	LSH	T1,2		;T1=TOTAL BYTES IN THE PCB
	SUB	T1,PCBCTR(U)	;DISCOUNT BYTES USED IN NCL HEADER
	CAML	T1,0(P)		;SKIP IF PCB WON'T FINISH OFF THE BUFFER
	JRST	NTDXN1		;GO TO CODE TO SEND FINAL FRAGMENT OF BUFFER.

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;HERE IF WE HAVE TO "FRACTURE" THE BUFFER INTO SEVERAL MESSAGES, AND THIS
; IS NOT THE LAST FRAGMENT OF THE BUFFER.
; T4 := NUMBER OF BYTES LEFT IN THE PCB (WE WILL USE THEM ALL)

	MOVE	T1,-1(P)	;GET THE IDC TYPE FIELD
	ANDI	T1,77		;AND JUST THE TYPE (MASK OUT THE INTERRUPT BIT)
	CAIL	T1,DC.DAT	;NOW SEE IF THIS IS A MESSAGE TYPE THAT WE
	CAILE	T1,DC.DAR	; CAN LEGALLY FRAGMENT INTO MULTIPLE MESSAGES
	JRST	[ADJSP	P,-3		;CAN'T BREAK THE MESSAGE, CLEAN UP STACK
		MOVEI	T1,IOBKTL	;TELL CALLER ERROR WAS BLOCK-TOO-LARGE
		POPJ	P,]		; AND GIVE AN ERROR RETURN
	MOVEI	T1,DC.DAT	;CHANGE THE MESSAGE TYPE TO BE DATA W/O E-O-R.
	MOVEM	T1,-1(P)	; . . .
	MOVN	T2,0(P)		;GET MINUS THE BYTE COUNT THAT WILL FIT
	MOVE	T3,-2(P)	;RETRIEVE PCV.XX CODE AGAIN
	CAIN	T3,PCV.BN	;12-BIT BINARY?
	JRST	[IDIVI	T2,^D3		;T2:=COUNT OF 12-BIT-BYTE PAIRS
		CAIE	T3,0		;DANGLING BYTE?
		SUBI	T2,1		;YES, DISCOUNT IT TOO
		JRST	.+1]		;BLUNDER ONWARDS
	ADDM	T2,DEVAXO+1(F)	; AND UPDATE THE NUMBER OF UNSENT BYTES.

	SKIPG	DEVAXO+1(F)	;JUST A QUICK CHECK OF MY ARITHMETIC
	STOPCD	.,STOP,ANIUBN,	;++ UNSENT BYTES COUNT WENT NEGATIVE
	JRST	NTDXN2		;GO SEND "CNT", "TYP", AND THE DATA.



;HERE IF THE PCB WILL HOLD ALL THE REST OF THE BUFFER.
; T4 := BYTES LEFT IN THE USER'S BUFFER

NTDXN1:	SETZM	DEVAXO+1(F)	;CLEAR BUFFER BYTE COUNT (THEY'LL ALL FIT)
;	JRST	NTDXN2		;GO SEND "CNT", "TYP", AND THE DATA

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;NTDXN2	HERE TO SEND "CNT", "TYP", AND DATA PORTIONS OF THE MESSAGE.
; -2(P) := CONVERSION CODE (WE MUST HANDLE BINARY MODE ESPECIAL)
; -1(P) := IDC TYPE
; -0(P) := NUMBER OF NETWORK BYTES TO GO.
;
;NOTE.	BECAUSE THE ETHERNET SERVICE LAYER REQUIRES 8-BIT BYTES ONLY, AND
;	CANNOT DEAL WITH ANYTHING ELSE, WE MUST HANDLE ALL THE SHANNANIGANS
;	NORMALLY SLOUGHED OFF ONTO THE FEK, LIKE LPT CHARACTER COMPRESSION,
;	AND THE HANDWAVING ABOUT 12-BIT "BINARY" DATA CONVERSION.

NTDXN2:	MOVE	P4,P2		;COPY OF POINTER TO "CNT" FIELD
	POP	P,T4		;RETRIEVE DATA BYTE COUNT
	MOVEI	T1,1(T4)	;ACCOUNT FOR THE "TYP" FIELD IN THE LENGTH
	XMT	T1		;SEND THE LENGTH OF THE DAP MSG
	POP	P,T1		;GET THE IDC TYPE BACK
	XMT1	T1		;THIS WILL MASK THE TYPE TO 8 BITS
	MOVE	P1,DEVAXO(F)	;USER'S BYTE POINTER
	POP	P,T1		;RETRIEVE DATA COMPRESSION TYPE
	CAIN	T1,PCV.BN	;12-BIT BINARY DATA BYTES?
	JRST	[MOVE	T3,T4		;YES, MUST ADJUST BYTE COUNT
		IDIVI	T3,^D3		;COUNT OF 12-BIT-BYTE-PAIRS
		LSH	T3,1		;COUNT OF 12-BIT-BYTES
		CAIE	T4,0		;ODD BYTE LEFT OVER?
		ADDI	T3,1		;YEAH, ACCOUNT FOR IT TOO
		MOVE	T4,T3		;REPOSITION DATA BYTE COUNT
		JRST	.+1]		;AND COPY C(T4) USER DATA BYTES INTO PCB

;CONTINUED ON NEXT PAGE
;CONTINUED FROM PREVIOUS PAGE

;HERE TO COPY THE DATA FROM THE USERS BUFFER AND INTO THE PCB.  AT THIS POINT:
;  1)  DEVAXO+1(F) HAS BEEN UPDATED TO REFLECT THE NUMBER OF BYTES COPIED.
;  2)  T1 := THE CONVERSION CODE (PCV.XX)
;  3)  T4 := THE NUMBER OF USER N-BIT-DATA-BYTES TO GO
;  4)  P1 := BYTE POINTER TO USER DATA BUFFER
;  5)  P2 := BYTE POINTER TO PCB DATA BUFFER (PCBPTR)
;  6)  P3 := THE NUMBER OF BYTES IN PCB'S FIRST BUFFER (FROM "CNT", "TYP" ETC)
;  7)  P4 := BYTE POINTER TO "CNT" FIELD

  	JRST	@.+1(T1)	;DISPATCH ON CONVERSION TYPE
	  IFIW	NTDX00		;00 -- PCV.NC -- NO CONVERSION
	  IFIW	NTDX10		;01 -- PCV.LC -- LPT COMPRESSION
	  IFIW	NTDX20		;02 -- PCB.BN -- 12-BIT BINARY BYTES


;HERE AFTER DATA COPY, GIVE PCB TO FEK/NETWORK

NTDXN8:	MOVEM	P1,DEVAXO(F)	;SET BYTE POINTER FOR NEXT TIME (IF ANY)
	ADDM	P3,PCBCTR(U)	;COUNT UP BYTES COPIED
	SETZM	PCBCT2(U)	;NO SECONDARY BUFFER
	SETZM	PCBPT2(U)	; . . .
	MOVEI	T1,PCV.NC	;STRAIGHT 8-BIT BYTES FOR THE FEK
	PUSHJ	P,NTDWRT	;SEND THE PCB.
	JRST	CPOPJ1##	;GIVE GOOD RETURN
;HERE FOR NO CONVERSION TRANSMIT (STRAIGHT BYTE COPY)

NTDX00:	ADD	P3,T4		;ACCOUNT FOR DATA BYTES
				; (IF NULL, THEN WILL MAKE ONE USELESS LOOP)
NTDX02:	EXCTUX	<ILDB T1,P1>	;GET NEXT USER DATA BYTE
	IDPB	T1,P2		;STORE IN PCB
	SOJG	T4,NTDX02	;LOOP FOR REST OF PCB'S WORTH OF DATA
	JRST	NTDXN8		;CAP OFF PCB AND SEND IT TO NETWORK
;HERE FOR LPT COMPRESSION
;
;COMPRESSED [7-BIT-ASCII] DATA IS FORMATTED AS FOLLOWS:
;
;   1CCCCCCC	"CCCCCCC" IS THE 7-BIT ASCII CHARACTER
;   01SSSSSS	"SSSSSS" IS THE COUNT OF SPACE CHARACTERS
;   001NNNNN	"NNNNN" IS THE REPEAT COUNT FOR THE FOLLOWING CHARACTER

NTDX10:	ADDM	P3,PCBCTR(U)	;ACCOUNT FOR HEADER DATA
	SETZ	P3,		;USED TO COUNT ACTUAL DATA OUTPUT

REPEAT	1,<
	ADD	P3,T4		;ACCOUNT FOR STRAIGHT BYTE COPY
NTDX1L:	EXCTUX	<ILDB T1,P1>	;GET NEXT USER DATA BYTE
	TRO	T1,200		;MARK IT UNCOMPRESSED
	IDPB	T1,P2		;STASH IT IN THE PCB
	SOJG	T4,NTDX1L	;LOOP FOR WHOLE BUFFER
	JRST	NTDXN8		;OUTPUT THE PCB
> ;END REPEAT 1

REPEAT	0,<
	PUSH	P,P4		;SAVE P4/"CNT" BYTE POINTER
	SETZ	T1,		;INITIALIZE CHAR-REPEAT COUNT
	SOJL	T4,NTDX18	;COUNT A DATA BYTE
	EXCTUX	<ILDB T2,P1>	;READ FIRST USER DATA CHARACTER

;LOOP COUNTING CHARACTER REPEATITION

NTDX11:	SOJL	T4,NTDX12	;END OF USER DATA IS END OF CHAR REPEAT
	EXCTUX	<ILDB T3,P1>	;READ NEXT USER DATA CHARACTER
	CAMN	T2,T3		;REPEAT CHARACTER?
	AOJA	T1,NTDX11	;YES, COUNT NUMBER OF REPEATS

;HERE WHEN CHARACTERS DIDN'T MATCH, FLUSH PREVIOUS REPEATED CHARS

NTDX12:	JUMPE	T1,[IORI T2,200		;ONLY ONE CHAR, FLAG UNCOMPRESSED
		JRST	NTDX17]		;AND STASH IN THE PCB BUFFER
	CAIN	T2," "		;COMPRESSING SPACES?
	JRST	NTDX15		;YES, DIFFERENT FROM OTHER CHARACTERS

;T1 := REPEAT COUNT MINUS ONE, T2 := REPEATED CHARACTER

NTDX13:	CAILE	T1,36		;WAS THIS CHARACTER REPEATED A LOT?
	AOJA	P3,[MOVEI P4,37+040	;MAX REPEAT COUNT + COMPRESSION FLAG
		IDPB	P4,P2		;STORE REPEAT COUNT BYTE
		IDPB	T2,P2		;AND COMPRESSED CHARACTER
		SUBI	T1,37		;ACCOUNT FOR THIS PASS
		AOJA	P3,NTDX13]	;SEE IF DONE WITH THIS CHARACTER
	ADDI	T1,1+040	;MAKE T1 INTO REPEAT COUNT + REPEAT FLAG
	IDPB	T1,P2		;STORE REPEAT COUNT BYTE
	AOJA	P3,NTDX17	;GO STORE COMPRESSED CHARACTER AND LOOP
;T1 := REPEAT COUNT MINUS ONE, T2 := SPACE CHARACTER

NTDX15:	CAILE	T1,76		;WAS THIS SPACE REPEATED A LOT?
	AOJA	P3,[MOVEI P4,77+100	;MAX REPEAT COUNT + COMPRESSION FLAG
		IDPB	P4,P2		;STORE REPEAT COUNT BYTE
		IDPB	T2,P2		;AND COMPRESSED SPACE
		SUBI	T1,77		;ACCOUNT FOR THIS PASS
		AOJA	P3,NTDX15]	;SEE IF CAN FINISH WITH THIS SPACE
	ADDI	T1,1+100	;MAKE T1 INTO REPEAT COUNT + COMPRESSION FLAG
	IDPB	T1,P2		;STORE REPEAT COUNT BYTE
	AOJA	P3,NTDX17	;GO STORE COMPRESSED SPACE AND LOOP

;OUTPUT CHARACTER IN T2, LOOP FOR NEXT USER DATA CHARACTER

NTDX17:	IDPB	T2,P2		;OUTPUT CHARACTER IN T2
	ADDI	P3,1		;ACCOUNT FOR IT
	SETZ	T1,		;CLEAR REPEAT COUNT
	MOVE	T2,T3		;NEXT CHARACTER BECOMES CURRENT CHARACTER
	JUMPGE	T4,NTDX11	;LOOP FOR REST OF USER DATA BUFFER

;USER BUFFER EMPTIED, ADJUST OUTPUT BYTE COUNT AND SHIP THE PCB

NTDX18:	POP	P,P4		;RETRIEVE POINTER TO "CNT" FIELD
	AOS	T1,P3		;BYTE COUNT (DON'T FORGET THE "TYP" BYTE!)
NTDX19:	IDIVI	T1,200		;GET SEVEN-BIT'S WORTH OF NEW "CNT" FIELD
	ILDB	T3,P4		;ORIGINAL "CNT" BYTE
	TRNE	T3,200		;WAS IT EXTENSIBLIZED?
	TRO	T2,200		;YES, THEN SO IS NEW "CNT" BYTE
	DPB	T2,P4		;SET MODIFIED "CNT" BYTE
	TRNE	T3,200		;MORE "CNT" FIELD TO PROCESS?
	JRST	NTDX19		;YES, FLUSH IT OUT
	SOJA	P3,NTDXN8	;GO TRANSMIT THIS PCB
> ;END REPEAT 0
;HERE FOR BINARY (12-BIT BYTES) CONVERSION

NTDX20:	SOJL	T4,NTDXN8	;IF NO MORE DATA BYTES LEFT, XMIT THE PCB
	EXCTUX	<ILDB T1,P1>	;GET NEXT USER DATA BYTE
	SOJL	T4,[LSHC T1,-^D4	;ONLY ONE 12-BIT DATA BYTE LEFT
		AOJA	P3,NTDX25]	;SEND IT AS TWO NETWORK BYTES
	ADDI	P3,2		;TWO 12-BIT BYTES COUNT AS THREE NETWORK BYTES
	EXCTUX	<ILDB T2,P1>	;GET NEXT USER DATA BYTE
	LSH	T2,^D24		;T1 AND T2 CONTIGUOUS DATA BITS
	LSHC	T1,-^D4		;T1 HAS 8 BITS, T2 HAS 16 BITS
	IDPB	T1,P2		;STORE FIRST 8 DATA BITS IN PCB
	LSHC	T1,^D8		;POSITION NEXT 8 DATA BITS
NTDX25:	IDPB	T1,P2		;AND STASH THEM IN THE PCB ALSO
	LSHC	T1,^D8		;POSITION LAST 8 DATA BITS
	IDPB	T1,P2		;AND STUFF THEM IN THE PCB AS WELL
	AOJA	P3,NTDX20	;LOOP FOR MORE DATA BITS
				COMMENT @

	Protocol for writing Network DisPatch service routines.

    1)	Service routines should skip return if they liked the message,
	and non-skip (error) return if the message was bad.

    2)	Service routines may NOT assume that "U" points to the PCB
	that contains the current message.  In particular, service
	routines may NOT call INCTBD.  To "discard" a bad message,
	the service routine should simply non-skip (error) return.

    3)	Upon entry to the service routine the following registers are setup.
	    P1	Contains a byte pointer pointing to the TYP field of this
		sub-message. (ie. an ILDB will get the first data byte
		after the TYP)
	    P4	Contains the number of data bytes in the msg. (Excluding
		the TYP byte.  (ie. CNT -1))

    4)	Service routines (When called at UUO level) may page fault
	as long as they don't mind getting called again with the
	same sub-message.  (ie. The sub-message isn't considered delivered
	until the service routine returns.)

	Implementation Notes.

    1)	In order to maintain the information regarding how much of a message
	we have processed, the following locations in the DDB are used.
	(Remember that a single NCL message may have numerous "sub-messages"
	for the designated device.  In particular it may have 25 data-with-
	end-of-record messages.  In that the user may have to do 25 "IN" uuo's
	in order to get all of the data.  Another problem to worry about
	is that the way TOPS-10 handles page faults is to cease executing
	the uuo, and start the user at his page fault handler.  The only way
	to get back to processing the message is for the PFH to start the
	uuo over again.  Sigh)

	DEVPCB	The rh of this word the head of the queue of unprocessed
		input PCB's for this device.  The queue is in order of the
		messages' arrival.  The top message on the queue is the
		one currently being processed.  It should  NOT  be removed
		until all sub-messages have been processed.
		The lh currently contains the number of bytes left to be
		read in the current message.

	DEVPBP	This is a byte pointer that points to the "CNT" field of
		the next NCL sub-message to process.  (This will be a sub-
		message of the top PCB on the DEVPCB queue.)  This pointer
		should not be advanced until the the sub-message has been
		accepted by the device service routine.  In particular,
		it should not be ILDB'ed.

				    @
;NTDISP	ROUTINE TO PERFORM THE DAP DISPATCH ON MULTIPART MESSAGES.
;	CALL ONCE FOR EACH SUB-MESSAGE.
;CALL	PUSHJ	P,NTDISP	;CALL DEVICE DRIVER WITH NEXT SUB-MESSAGE
;RETURN	CPOPJ			;IF MESSAGE WAS BAD. T1 WILL HAVE THE
;				; PC OF THE CODE THAT COMPLAINED.
;	CPOPJ1			;MESSAGE WAS ACCEPTED BY THE SERVICE ROUTINE.
;				;IF DEVPBP(F) = 0 THEN ALL SUBMESSAGES HAVE
;				; BEEN PROCESSED.

NTDISP:	MOVE	P1,DEVPBP(F)	;GET THE POINTER TO THE NEXT SUB-MESSAGE
	HLRZ	P4,DEVPCB(F)	;GET THE NUMBER OF BYTES LEFT IN THE MESSAGE

;CNT

	PUSHJ	P,EBI2BI	;GET THE LENGTH OF THIS SUB-MESSAGE
	JUMPE	T1,NTDIS1	;IF ZERO, THEN ALL DONE WITH THIS MESSAGE
	SUBI	P4,(T1)		;UPDATE THE NUMBER OF BYTES LEFT BY THIS SUB-MSG
	JUMPL	P4,NTDIS2	;BAD MESSAGE IF GOES PAST THE END.
	HRLM	P4,DEVPCB(F)	;REMEMBER HOW MANY LEFT AFTER THIS SUB-MSG
	MOVEI	P4,(T1)		;COPY THE LENGTH OF THIS SUBMESSAGE

;TYP

	PUSHJ	P,EBI2BI	;GET THE IDC TYPE
	CAILE	T1,0		;NOW DO A LITTLE RANGE CHECKING
	CAILE	T1,DC.MAX	; SO WE DON'T GO FLYING OFF INTO TROUBLE
	JSP	T1,NTDIS2	; BAD MESSAGE TYPE. PUT PC IN T1 AND RETURN
	MOVE	S,DEVIOS(F)	;SET UP DEVICE STATUS
	HLRZ	T2,DEVNET(F)	;GET THE ADDRESS OF THE NETWORK DEVICE TABLE
	PUSHJ	P,@NDTNDP(T2)	;DISPATCH (T1 IS USED IN THE INDIRECTION...)
	  JSP	T1,NTDIS2	;SERVICE ROUTINE CHOKED ON THAT ONE. GIVE ERROR.

;SERVICE ROUTINE ACCEPTED SUB-MESSAGE - MAKE SURE IT ATE IT ALL

	SOJGE	P4,[IBP P1	;IF THE SERVICE ROUTINE DIDN'T EAT ALL THE MSG,
		    JRST .]	; MAKE SURE THAT P1 GETS ADVANCED.
	MOVEM	P1,DEVPBP(F)	;SAVE POINTER TO NEXT SUB-MSG.
	HLRZ	P4,DEVPCB(F)	;GET THE NUMBER OF BYTES LEFT IN MESSAGE
	JUMPG	P4,CPOPJ1##	;IF THERE ARE SOME LEFT, RETURN TO NTDDID

;HERE WHEN ALL DONE WITH THE MESSAGE

NTDIS1:	SETZM	DEVPBP(F)	;MARK MSG AS DONE
	PJRST	CPOPJ1##	;GIVE GOOD (SKIP) RETURN


;HERE WHEN MSG IS BAD. RETURN WITH T1 :=  ADDRESS OF THE CODE THAT FOUND
; THE MESSAGE WAS BAD

NTDIS2:	SETZM	DEVPBP(F)	;INDICATE NO PARTIAL MSG TO DO.
	POPJ	P,		;GIVE ERROR RETURN
;NTDILD	ROUTINE TO PROCESS INPUT MESSAGES AT "INTERRUPT" LEVEL
;CALL	MOVEI	U,PCB
;	MOVE	P1,BYTE POINTER TO FIRST "CNT"
;	PUSHJ	P,NTDILD
;RETURN	CPOPJ
;
;NOTE!!	THIS ROUTINE HANDLES DATA-REQUESTS.
;NOTE!!	IF THE DEVICE SERVICE ROUTINE FINDS AN ERROR IN THE MESSAGE
;	IT WILL PROBABLY ZERO U.
NTDILD::			;INTERRUPT LEVEL DAP DISPATCH
	PUSHJ	P,NTDDOD	;ATTEMPT TO MAINTAIN CORRECT DRQ COUNT.
	MOVEM	P1,DEVPBP(F)	;SET UP "PBP" FOR CALL TO NTDISP
	HRLM	P4,DEVPCB(F)	;SAVE THE NUMBER OF BYTES LEFT IN THE MESSAGE
NTDIL1:	PUSHJ	P,NTDISP	;DO THE DAP LEVEL DISPATCHING.
	  JRST	INCTBD		; BAD MESSAGE. T1 HAS PC OF CODE THAT NOTICED.
	SKIPE	DEVPBP(F)	;HAVE WE PROCESSED ALL SUB-MESSAGES?
	JRST	NTDIL1		; IF NOT, GO BACK AND DO THE NEXT ONE.
	S0JRST	RMVPCB		;ALL DONE WITH THE MESSAGE. RETURN PCB
;NTDQIP	ROUTINE TO QUEUE INPUT PCB'S AT INTERRUPT LEVEL FOR PROCESSING AT
;	LOWER (UUO) LEVEL.
;CALL	MOVEI	U,PCB
;	MOVEI	P1,POINTER TO THE FIRST "CNT" AFTER THE "DLA"
;	PUSHJ	P,NTDQIP
;RETURN	CPOPJ			;WITH PCB QUEUED
;
;NOTE	IF THIS IS THE ONLY PCB QUEUED, WE SAVE "P1" SO WE WON'T HAVE TO
;	SCAN THE NCL HEADER AGAIN.

NTDQIP::PUSHJ	P,NTDDOD	;ATTEMPT TO MAINTAIN CORRECT DRQ COUNT.
	HLLZS	PCBBLK(U)	;CLEAR ANY STRAY POINTERS
	HRRZ	T1,DEVPCB(F)	;GET HEADER OF QUEUE OF PCB'S
	JUMPE	T1,NTDQI2	; IF WE ARE FIRST, THEN WE ARE SPECIAL.
	CAIA			; OTHERWISE SKIP INTO LOOP TO FIND LAST PCB
NTDQI1:	MOVEI	T1,(T2)		;ADVANCE TO THE NEXT PCB IN THE QUEUE
	HRRZ	T2,PCBBLK(T1)	;GET NEXT PCB (OR NULL)
	JUMPN	T2,NTDQI1	;IF THERE IS ANOTHER PCB, THEN LOOP DOWN CHAIN
	HRRM	U,PCBBLK(T1)	;LINK THIS PCB ON AS THE LAST IN THE QUEUE
	SETZ	U,		;CLEAR U SO NETSCN WILL KNOW THAT PCB IS GONE.
	POPJ	P,		;AND THAT'S ALL FOR NOW.

NTDQI2:	HRRM	U,DEVPCB(F)	;SAVE THE PCB AS THE HEAD OF THE QUEUE.
	HRLM	P4,DEVPCB(F)	; SAVE THE COUNT OF UN-PROCESSED BYTES IN MSG
	MOVEM	P1,DEVPBP(F)	; STORE THE POINTER TO FIRST NCL SUB-MESSAGE.
	SETZ	U,		;TELL NETSCN NOT TO FREE THE PCB
	PJRST	NTDIAV		;TELL THE JOB THAT INPUT IS AVAILABLE.
				;*** NOTE *** WE ONLY WAKE THE JOB ON THE
				; THE ARRIVAL OF THE FIRST MESSAGE.


;*** FOOTNOTE ***

COMMENT \
     Because the count of outstanding data requests is decremented when
the message is queued, the routine NTDXDQ should never be called while
there are queued input messages.  (If it is, it will not realize that there
are unprocessed messages, and possibly request more data than the program
has buffers for.)  This enforces my belief that one does not want to send
data requests if one has any unprocessed input data.  Remember, these input
PCB's are not going to a physical device.  They should not have to wait
to be processed.  If there is some reason that they cannot be processed
immediatly (ie the job has swapped) I don't think that it is a good
idea to send more requests.

\
;NTDDID	HERE TO PERFORM A "DELAYED INPUT DISPATCH" AT UUOLEVEL.
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NTDDID	;DO DELAYED DISPATCH
;RETURN	CPOPJ
;
;NOTE!!	THIS ROUTINE (IN CONTRAST TO NTDILD) ONLY DISPATCHES 1 NCL SUB
;	MESSAGE AT A TIME.  IN OTHER WORDS, YOU MUST CALL THIS ROUTINE
;	ONCE FOR EACH SUB-MESSAGE OF AN NCL MESSAGE.  THE REASONING BEHIND
;	THIS IS THAT IT ALLOWS THE DEVICE SERVICE ROUTINE TO DETERMING
;	IF THERE ARE ENOUGH FREE BUFFERS TO ACCEPT THE DATA.
;
;NOTE!!	THE FOLLOWING LOCATIONS IN THE DDB ARE USED TO MAINTAIN INFORMATION
;	REGARDING HOW FAR WE HAVE GOTTEN WHEN TAKING APART MULTI-PART NCL
;	MESSAGES.
;
;   DEVPCB
;	THE RIGHT HAND PART OF THIS IS THE HEAD OF THE QUEUE OF UNPROCESSED
;	INPUT PCB'S FOR THIS DEVICE.
;	THE LEFT HAND PART OF THIS IS THE COUNT OF THE NUMBER OF BYTES LEFT
;	TO PROCESS IN THE CURRENT PCB. (IT IS THE GLOBAL "P4" FOR THE DISPATCH
;	ROUTINES)  IT IS INITIALIZED TO "PCBCTR - HEADER LENGTH" AND IS
;	DECREMENTED BY THE LENGTH OF EACH SUB-MESSAGE PROCESSED.
;   DEVPBP
;	THIS IS A BYTE POINTER INTO THE MESSAGE THAT IS AT THE HEAD OF THE
;	DEVPCB QUEUE.  IT POINTS TO THE FIRST UN-PROCESSED SUB-MESSAGE.
;
;
NTDDID::			;HERE TO PROCESS NEXT PCB ON DEVPCB
	MOVE	P1,DEVPBP(F)	;SEE IF WE HAVE TO PARSE THE NCL HEADER
	JUMPN	P1,NTDDI1	; WE WON'T IF WE ALREADY HAVE A POINTER TO DAP

	HRRZ	U,DEVPCB(F)	;GET POINTER TO THE FIRST PCB.
	JUMPE	U,CPOPJ##	; BUT RETURN NOW IF THERE ISN'T ONE.
	MOVE	P1,PCBPTR(U)	;WE HAVE TO PARSE THE NCL, SO GET POINTER TO MSG
	MOVE	P4,PCBCTR(U)	;GET THE LENGTH OF THIS MESSAGE.
;NCT
	PUSHJ	P,SKIP1		;SKIP OVER THE NCT
;DNA
	PUSHJ	P,XSKIP		;SKIP OVER THE DESTINATION NODE ADDRESS
;SNA
	PUSHJ	P,XSKIP		;SKIP OVER THE SOURCE
;NCA
	PUSHJ	P,SKIP1		;SKIP OVER THE ACK
;NCN
	PUSHJ	P,SKIP1		;SKIP OVER THE MESSAGE NUMBER
;DLA
	PUSHJ	P,XSKIP		;SKIP OVER OUR LINK ADDRESS

	MOVEM	P1,DEVPBP(F)	;SET UP PBP FOR NTDISP
	HRLM	P4,DEVPCB(F)	;SAVE THE LENGTH OF THE REST OF THE MESSAGE
NTDDI1:				;HERE TO DISPATCH THE NEXT SUB-MESSAGE
	PUSHJ	P,NTDISP	;DO THE DISPATCH
	  JRST	NTDDI2		;ERROR RETURN. GIVE THE BAD PCB TO INCTBD.
	SKIPE	DEVPBP(F)	;HAVE WE READ ALL SUB-MESSAGES?
	POPJ	P,		;IF NOT, DON'T FREE THE PCB YET.

	HRRZ	U,DEVPCB(F)	;ALL DONE WITH THIS PCB.  GET ADDRESS TO IT
	HRRZ	T2,PCBBLK(U)	; GET ADDRESS OF THE NEXT ONE
	HRRZM	T2,DEVPCB(F)	; SET THE NEXT ONE UP TO BE PROCESSED NEXT TIME.
	S0JRST	RMVPCB		;RETURN THE PCB AND EXIT

NTDDI2:				;HERE IF THE SERVICE ROUTINE DIDN'T LIKE MSG.
	HRRZ	U,DEVPCB(F)	;GET THE OFFENDING PCB
	HRRZ	T2,PCBBLK(U)	;GET POINTER TO NEXT PCB IN THE CHAIN
	HRRZM	T2,DEVPCB(F)	; AND MAKE THAT THE NEXT TO BE PROCESSED.
	PJRST	INCTBD		;SIGNAL ERROR (PC OF ERROR IS IN T1)
;NTDCDQ	CHECK FOR OUTPUT DATA REQUESTS.
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NTDCDQ
;RETURN	CPOPJ			;NO DATA-REQUEST AVAILABLE (DEVDRQ = 0)
;	CPOPJ1			;DATA REQUEST IS AVAILABLE
;
NTDCDQ::			;CHECK FOR OUTPUT DATA REQUESTS
	HRRZ	T1,DEVDRQ(F)	;GET NUMBER OF OUTSTANDING DATA REQUESTS
	JUMPG	T1,CPOPJ1##	;IF POSITIVE, GIVE GOOD RETURN
	POPJ	P,		; RETURN



;NTDDDQ	DECREMENT DATA REQUEST COUNT
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NTDCDQ
;RETURN	CPOPJ			;ALWAYS (DEVDRQ := DEVDRQ-1)
;
NTDDDQ::PUSHJ	P,NTDCDQ	;MAKE SURE WE HAVE OUTSTANDING DRQ'S
	  STOPCD .,STOP,ANFDDQ,	;++ DATA REQUEST COUNT WENT NEGATIVE
	SOS	DEVDRQ(F)	;COUNT DOWN THIS REQUEST
	POPJ	P,		;AND RETURN



;NTDDOD	DECREMENT OUTSTANDING DATA REQUEST COUNT.
;CALL	MOVEI	U,INCOMING PCB TO CHECK
;	MOVEI	F,DDB CONTAINING DATA-REQUEST INFORMATION
;	PUSHJ	P,NTDDOD
;RETURN	CPOPJ			;WITH LH(DEVDRQ(F)) DECREMENTED IF NON-INTERRUPT
;
NTDDOD:				;HERE TO CHECK IF THIS IS NON-INTERRUPT DATA
	MOVE	T1,PCBPTR(U)	;GET POINTER TO MESSAGE
;NCT
	ILDB	T1,T1		;GET THE FIRST BYTE OF THE MESSAGE
	TRNE	T1,NCT.IT	;INTERRUPT MESSAGE??
	POPJ	P,		;YES, DON'T MUNG OUTSTANDING DRQ COUNT
	MOVSI	T1,-1		;INDICATE ONE LESS
	ADDM	T1,DEVDRQ(F)	; OUTSTANDING DATA-REQUEST
	POPJ	P,		;ALL DONE
;NTDXDQ	SUBROUTINE TO SEND DATA REQUESTS BASED ON EMPTY BUFFERS.
;CALL	MOVEI	F,DDB
;	PUSHJ	P,NTDXDQ	;TRY TO SEND DATA REQUESTS
;RETURN	CPOPJ			;ALWAYS.
;
NTDXDQ::			;HERE TO SEND DATA REQUESTS
	SETZ	T1,		;FIRST COUNT BUFFERS (INITIALIZE COUNT TO ZERO)
	HRRZ	T2,DEVIAD(F)	;GET POINTER TO THE FIRST BUFFER
	MOVEI	T3,(T2)		;REMEMBER START OF RING INCASE ALL ARE EMPTY

NTDXD1:	UMOVE	T2,(T2)		;GET THE BUFFER USE BITS
	  ERJMP	NTDXD2		;PAGED OUT, STOP COUNTING BUFFERS
	JUMPLE	T2,NTDXD2	;IF IT'S NOT FREE, STOP COUNTING BUFFERS
	ANDI	T2,-1		;MASK OFF THE "SIZE" PART OF THE HEADER WORD
	CAIGE	T1,MAXODR##	;IF WE HAVE ENOUGH DATA REQUESTS,
	CAIN	T2,(T3)		; OR WE HAVE COUNTED ALL THE BUFFERS.
	AOJA	T1,NTDXD2	; THEN TRY TO SEND THE REQUEST
	AOJA	T1,NTDXD1	;OTHER WISE KEEP COUNTING FREE BUFFERS.

NTDXD2:				;HERE WITH THE NUMBER OF FREE BUFFERS IN T1
	HLRZ	T2,DEVDRQ(F)	;GET THE NUMBER OF OUTSTANDING DRQ'S
	SUBI	T1,(T2)		;FIGURE OUT HOW MANY MORE WE CAN SEND
	JUMPLE	T1,CPOPJ##	;IF WE CAN'T SEND ANY, THEN RETURN NOW

	PUSH	P,T1		;SAVE THE NUMBER WE ARE REQUESTING
	PUSHJ	P,NCSDRQ	;SEND THE DRQ MESSAGE
	  JRST	[PUSHJ P,NETSLP	;IF WE CAN'T THEN WAIT (*** SEE FOOTNOTE)
		 MOVE T1,(P)	; GET THE COUNT BACK
		 JRST .-1]	; AND TRY AGAIN
	POP	P,T1		;GET THE COUNT BACK
	HRLZI	T1,(T1)		;GET IT IN THE LEFT HALF
	ADDM	T1,DEVDRQ(F)	;AND UPDATE THE COUNT OF OUTSTANDING DRQ'S
	POPJ	P,		;ALL DONE


;*** FOOTNOTE ***

COMMENT \

     I don't think that I can return to the user here since if this is his
first DRQ then he may be stuck waiting for messages that will never come.
I don't like the idea of making some one wait (he may be doing non-blocking
I/O) but until someone devizes a way to give the job an input done interrupt
when free core becomes available, I think this will have to be done.
    Note also that this routine cannot be called from interrupt level.  There
are reasons for this other than the fact that it's a bad idea to put an
interrupt level to "NETSLP".  The other main reason is that if this is running
on an SMP KL-10 system looking at anything in a users address space at interrupt
level screws up the cache.  (It sets the cache valid bit in the CPU that
may not be running the user.)  The solution to this is to "OUCHE" any words
that are looked at by interrupt level.  Whatta pain.

\
;NTDRDQ	ROUTINE TO PROCESS INCOMING DATA REQUESTS (HERE FROM ICMDRQ VIA
;	THE DRQ DISPATCH.
;CALL	MOVEI	T4,"DRQ" COUNT
;	PUSHJ	P,NTDRDQ
;RETURN	CPOPJ1			;ALWAYS
;
;NOTE	THIS ROUTINE WAKES THE JOB ONLY IF THIS WAS THE FIRST DATA REQUEST.
;
NTDRDQ::			;HERE IN RECEIPT OF A DATA REQUEST
	HRRZ	T1,DEVDRQ(F)	;GET THE OLD DATA REQUEST COUNT
	ADDM	T4,DEVDRQ(F)	;ADD IN THE NEW
	SKIPGE	T1		;SKIP IF THE OLD ONE WAS NON-NEGATIVE
	STOPCD	.,STOP,ANFDRQ,	;++ DRQ WENT NEGATIVE
;***	SKIPG	T1		;SEE IF HE WAS WAITING FOR DATA REQUESTS
	MOVE	S,DEVIOS(F)	;SET UP S
	PUSHJ	P,NTDOAV	; IF HE WAS. THEN WAKE HIM
	JRST	CPOPJ1##	;GIVE GOOD RETURN

;*** FOOTNOTE ***

COMMENT \

     For some as yet unknown reason, the "SKIPG" that is commented out
breaks asynch I/O.  Inspite of the fact that everything is interlocked
via the "NT" INTERLOCK, there appears to be a race.  The symptom is that
the job does not get an output done interrupt, and hence is stuck waiting
for one, even though he has several data requests.

\
;NTDWTI				;ROUTINE TO WAIT FOR INPUT DATA
;CALL	PUSHJ	P,NTDWTI	;
;RETURN	CPOPJ			;IF NON-BLOCKING, OR IF PROG HAS AT LEAST
;				;  ONE BUFFER AVAILABLE FOR READING
;	CPOPJ1			;IF BLOCKING AND WE NOW HAVE INPUT DATA

NTDWTI::HRRZ	T2,DEVBUF(F)	;IF THE USER HAS AT LEAST
	JUMPE	T2,CPOPJ##	;IF NO BUFFERS, GET OUT NOW
	EXCTUX	<HRRZ T2,(T2)>	; ONE BUFFER OF DATA AVAILABLE,
	EXCTUX	<SKIPGE (T2)>	; (IE USE BIT IS SET)
	POPJ	P,		;THEN RETURN NOW SO HE CAN RUN
	MOVE	T2,DEVAIO(F)	;IF USER HAS NO DATA, THEN SEE IF
	TRNE	T2,DEPAIO	; NON-BLOCKING I/O.  IF SO,
	POPJ	P,		; THEN JUST RETURN. (UUOCON WILL UNDERSTAND)
	PUSHJ	P,NETHIB	;IF BLOCKING, THEN GO SLEEP TILL SOME COMES
	RETSKP			; (NOTE THAT "W" IS NO LONGER VALID) (NOR "S")

;NTDWTO				;ROUTINE TO WAIT FOR OUTPUT DATA REQUESTS
;CALL	PUSHJ	P,NTDWTO	;CALL TO SEE IF OUTPUT CAN BE DONE
;RETURN	CPOPJ			;NO DATA-REQUESTS. BUT NON-BLOCKING I/O
;	CPOPJ1			;THERE ARE DATA REQUESTS. TRY MORE OUTPUT

NTDWTO::MOVE	T2,DEVAIO(F)	;IF THIS IS NOT NON-BLOCKING
	TRNN	T2,DEPAIO	; STYLE I/O, THEN
	JRST	[AOS (P)	; GIVE A SKIP (DO MORE OUTPUT RETURN)
		 PJRST NETHIB]	; AFTER WE GET MORE DATA.
				; (NOTE THAT "W" IS NO LONGER VAILD)
;	HLRZ	T2,DEVBUF(F)	;IF THE USER HAS AT LEAST ONE
;	EXCTUX	<HRRZ T2,(T2)>	; EMPTY BUFFER AVAILABLE FOR
;	EXCTUX	<SKIPL (T2)>	; FOR FILLING, THEN
;	JRST	[TLNN F,OCLOSB	; UNLESS THIS IS FROM THE "CLOSE" UUO
;		 POPJ P,	; RETURN TO UUOCON TO LET THE USER CONTINUE.
;		 JRST .+1]	;IF IT IS THE CLOSE UUO, BLOCK.
	MOVSI	S,IOSTBL	;IF IT IS NONBLOCKING. SET "TROUBLE" SO THAT
	IORB	S,DEVIOS(F)	; UUOCON WON'T ADVANCE DEVBUF, AND
	POPJ	P,		; LET UUOCON RUN THE USER SOME MORE


;*** FOOTNOTE ***

COMMENT \

     In NTDWTO it would be nice if it could return and let the user run
if he had any buffers left to fill, but this appears not to work.  What
happens is that the WAIT1 code does a CALOUT trying to empty the last few
buffers.  NTDWTO trying to be clever quickly returns noticing that the
user has a few buffers left to fill.  The situation is clearly out of hand.
The monitor hangs.  One might think that we could do this for async I/O,
but unfortunatly, under MSGSER, DEVBUF doesn't really mean anything.

\
;NTDIAV				;HERE TO TELL A JOB THAT IT HAS NETWORK INPUT.
;CALL	PUSHJ	P,NTDIAV
;RETURN	CPOPJ			;ALWAYS
;
;THIS ROUTINE FIGURES OUT HOW THE JOB IS WAITING. (SLEEPING, OR NON-BLOCKING)
; AND USES AN APPROPRIATE METHOD TO WAKE IT.
;
NTDIAV::			;HERE TO NOTIFY JOB THAT IT HAS NETWORK INPUT.
	MOVE	T1,DEVAIO(F)	;FIRST CHECK AND SEE IF THIS JOB
	TRNN	T1,DEPAIO	; IS DOING NON-BLOCKING I/O
	PJRST	NETWAK		;IF NOT, THEN IT'S SLEEPING, SO WAKE IT.
	PUSH	P,DEVIOS(F)	;WE NEED TO SET INPUT IO DONE, SO SAVE DEVIOS.
	MOVSI	S,IO!IOSTBL	;AND PRETEND THAT THE DEVICE IS
	ANDCAB	S,DEVIOS(F)	; DOING INPUT (SO WE DON'T PSI OUTPUT DONE)
	PUSHJ	P,SETIOD##	;SAY THAT IO IS DONE SO UUOCON WILL RUN.
	POP	P,DEVIOS(F)	;IF IT'S A TSK, LET IT GO BACK TO OUTPUT.
	POPJ	P,		;AND THAT'S ALL


;NTDOAV				;HERE TO TELL A JOB IT HAS DATA REQUESTS
;CALL	PUSHJ	P,NTDOAV
;RETURN	CPOPJ
;
;THIS ROUTINE FIGURES OUT HOW THE JOB IS WAITING (EW, OR NON-BLOCKING)
; AND USES THE APPROPRIATE METHOD TO WAKE THE JOB.
;
NTDOAV::			;HERE WHEN DATA REQUESTS ARE AVAILABLE
	TLNE	S,IOSREL	;HAS THIS DEVICE BEEN "RELEASED"?
	POPJ	P,		; IF SO, THEN DON'T WAKE ANYONE.
	MOVE	T1,DEVAIO(F)	;NOW SEE IF THIS DEVICE IS
	TRNN	T1,DEPAIO	; OF THE NON-BLOCKING KIND.
	PJRST	NETWAK		;IF NOT, THEN GET JOB OUT OF EVENT WAIT.
	PUSH	P,DEVIOS(F)	;IF NON-BLOCKING, WE MUST GIVE THE JOB AN
	MOVSI	S,IO		; OUTPUT DONE INTERRUPT. SO FIRST WE
	IORB	S,DEVIOS(F)	; SAY WE ARE DOING OUTPUT (PSIIOD LOOKS)
	TLZ	S,IOSTBL	;CLEAR TROUBLE SO PSIIOD WILL BE CALLED
	PUSHJ	P,SETIOD##	;SAY THAT THE OUTPUT IS DONE
	POP	P,DEVIOS(F)	;LET THE JOB CONTINUE INPUT IF IT WANTS
	POPJ	P,		; AND THAT'S ALL.
;NTDCNC	ROUTINE TO PROCESS THE CONNECT CONFIRM MESSAGE FOR "NORMAL" (LPT, CDR
; PLT ...) DEVICES.
;CALL	MOVEI	F,DDB
;	MOVE	P1,"POINTER TO SLA"
;	MOVE	P4,"NUMBER OF BYTES LEFT IN MSG"
;	PUSHJ	P,NTDCNC
;RETURN	CPOPJ			;IF WE WEREN'T WAITING FOR A CONNECT CONFIRM
;	CPOPJ1			;AFTER WAKING THE JOB

NTDCNC::LDB	T1,NETSLA	;GET LOCAL LAT ADDRESS
	LDB	T2,LATSTA	;GET THE CURRENT STATE
	CAIE	T2,LAT.CC	;WAS IT CONNECT CONFIRM WAIT?
	POPJ	P,		;IF NOT, GIVE THE "BAD MESSAGE" RETURN

;SLA

	PUSHJ	P,EBI2BI	;FIND OUT WHAT LAT THE REMOTE GAVE US.
	DPB	T1,NETDLA	;SAVE THE REMOTE LAT NUMBER.

;DPN(OBJ)

	PUSHJ	P,EBI2BI	;WE DON'T REALLY CARE ABOUT THIS FOR NORMAL DEVS

;DPN(PID)

	PUSHJ	P,EBI2BI	;WE SENT HIM THIS UNIT NUMBER

;SPN(OBJ)

	PUSHJ	P,EBI2BI	;THE PARANOID MIGHT VERIFY THAT THIS IS OUR TYPE

;SPN(PID)

	PUSHJ	P,EBI2BI	;READ THE UNIT NUMBER WE GOT.
	ANDI	T1,17		; WE ONLY ALLOW 16. UNITS
	DPB	T1,PUNIT##	;STORE THE REMOTE UNIT NUMBER
	CAIG	T1,9		;OUT OF SIMPLE DIGIT RANGE?
	TROA	T1,'0'		;NO, EASY TO MAKE SIXBIT
	ADDI	T1,'A'-'9'-1	;YES, MAKE SIXBIT THE HARD WAY
	DPB	T1,[POINT 6,DEVNAM(F),35] ;UPDATE THE DEVICE NAME TO REFLECT THE
				; NEW UNIT NUMBER
;MML AND FEA

	PUSHJ	P,NTDCNF	;PROCESS REST OF CONNECT CONFIRM (MML & FEA)
	  POPJ	P,		;DUH?
	LDB	T1,NETSLA	;GET THIS MESSAGES "DLA" BACK
	MOVEI	T2,LAT.OK	;NOW THAT WE'RE CONNECTED, CHANGE OUR
	DPB	T2,LATSTA	; TO BE "OK".
	MOVSI	S,IOSCON	;ALSO REMEMBER TO SET IOSCON TO
	IORB	S,DEVIOS(F)	; TELL THE REST OF THE WORLD
	AOS	(P)		;SIGNAL GOOD RETURN
	TLNN	S,IOSZAP	;IS THIS DDB STILL IN USE?
	PJRST	NETWAK		;YES, WAKE THE WAITING JOB.
	EMRGCY			;USE "EMERGENCY" MEMORY IF NECESSARY
	PUSHJ	P,NTDXDS	;NO, SEND A DISCONNECT AND CHANGE STATE
	  STOPCD .,STOP,ANFXDS,	;++ SHOULDN'T FAIL. NETSCN DIDN'T CALL PBCHECK?
	POPJ	P,		;DDB WILL WAIT FOR DISCONNECT CONFIRM
;NTDCNF	ROUTINE TO PROCESS THE CONNECT CONFIRM'S MML AND FEA FIELDS
;CALL	MOVEI	F,DDB
;	MOVE	P1,"POINTER TO SLA"
;	MOVE	P4,"NUMBER OF BYTES LEFT IN MSG"
;	PUSHJ	P,NTDCNF
;RETURN	CPOPJ			;IF ERROR (SHOULDN'T HAPPEN)
;	CPOPJ1			;MML AND FEA(DCM,RLN,...) READ, DCM IN T1

NTDCNF::PUSHJ	P,EBI2BI	;START OFF WITH MAX MESSAGE LENGTH
	DPB	T1,NETMML	;SAVE IT FOR ANYONE WHO CARES
;FEA(DCM)
	PUSHJ	P,EBI2BI	;EXTRACT THE DATA MODES
	PUSH	P,T1		;SAVE THE DCM FOR CALLER TO PONDER
;RLN
	PUSHJ	P,EBI2BI	;READ AND STORE THE RECORD LENGTH.  (ALTHOUGH I
	DPB	T1,NETRLN	; DON'T THINK ANYONE USES IT...)
;DVT
	PUSHJ	P,EBI2BI	;GET THE DEVICE ATTRIBUTES AND STORE THEM.
	DPB	T1,NETDVT	; (THESE ARE USED...)
;DVU
	PUSHJ	P,EBI2BI	;GET THE DEVICE "UNIT" TYPE
	DPB	T1,NETDVU	;STASH IN THE DDB
;DVV
	PUSHJ	P,EBI2BI	;GET THE DEVICE "CONTROLLER" TYPE
	DPB	T1,NETDVV	;STASH IN THE DDB TOO
;DFT
	PUSHJ	P,EAS2SX	;GET THE "FORMS TYPE"
	MOVEM	T1,DEVDFT(F)	;SAVE IT AWAY

	POP	P,T1		;RESTORE DCM (IF ANYONE CARES)
	JRST	CPOPJ1##	;RETURN
;NTDDSC	ROUTINE TO DO THE "DEFAULT" PROCESSING OF DISCONNECT MESSAGES.
;CALL	MOVEI	F,DDB
;	MOVEI	P4,LENGTH OF DISCONNECT MESSAGE
;	MOVEI	P1,POINTER TO THE "SLA" FIELD OF THE DISCONNECT
;	PUSHJ	P,NTDDSC
;RETURN	CPOPJ			;SOMETHING WAS WRONG WITH THE MESSAGE
;	CPOPJ1			;SUCCESSFUL RETURN.

NTDDSC::NTDBUG			;JUST CHECKING ...

;SLA

	PUSHJ	P,EBI2BI	;GET THE SLA (AND IGNORE)

;RSN

	PUSHJ	P,EBI2BI	;GET THE REASON
	DPB	T1,NETRSN	; AND STORE IT IN THE DDB.
	LDB	T2,NETSLA	;GET POINTER INTO OUR LAT
	LDB	T2,LATST2	;GET CONNECTION'S CURRENT STATE
	CAIL	T2,LAT.CC	; AND MAKE SURE THAT IT'S
	CAILE	T2,LAT.DC	; REASONABLE (CC, OK, OR DC)
	POPJ	P,		;IF IT'S NOT, THEN GIVE "BAD MSG" RETURN
	AOS	(P)		;WE LIKE THE MSG. ACCEPT IT.
	CAIN	T2,LAT.OK	;IF DISCONNECT INITIATE
	JRST	NTDDS1		;SEND THE DISCONNECT CONFIRM
	MOVEI	T1,IR.DOL	;DEVICE OFFLINE PSI FLAG
	JRST	NTDNW2		;CONTINUE WITH COMMON CODE


; HERE SEND DISCONNECT CONFIRM

NTDDS1:	MOVEI	T1,RSN.OK	;NORMAL RETURN "REASON"
	EMRGCY			;USE "EMERGENCY" FREE CORE IF NECESSARY
	PUSHJ	P,NCSDSC	;SEND THE DISCONNECT
	  STOPCD .,STOP,ANFDS1,	;++ SHOULDN'T FAIL.  NETSCN DIDN'T CALL PCBECK?
	MOVEI	T1,IR.IER!IR.OER!IR.DOL  ;PSI FLAGS FOR UNEXPECTED DISCONNECT
	MOVEI	T2,IONDD%	;REMOTE NETWORK DEVICE DISCONNECTED
	JRST	NTDNW1		;USE COMMON CODE TO WAKE THE OWNING JOB
;NTDCNT	ROUTINE TO PERFORM THE CONNECT FOR "NORMAL" DEVICES.
;CALL	MOVEI	F,DDB		;WITH NO LAT ASSIGNED
;	PUSHJ	P,NTDCNT	;ASSIGN LAT AND DO CONNECT
;RETURN	CPOPJ			;CONNECT FAILED. LAT FREED. ERROR CODE IN T1.
;	CPOPJ1			;DEVICE CONNECTED.
;
NTDCNT::			;HERE TO CONNECT "NORMAL" DEVICE.

	LDB	T1,NETSLA	;FIRST A BIT OF PARANOIA.
	SKIPE	T1		;MAKE SURE THAT WE HAVEN'T ASSIGNED THE LAT
	STOPCD	.,STOP,ANFLAA,	;++ LAT ALREADY ASSIGNED.

	MOVEI	T1,(F)		;COPY THE DDB POINTER FOR GETSLA
	MOVEI	T2,LAT.CC	;WE WILL BE IN CONNECT CONFIRM WAIT
	PUSHJ	P,GETSLA	;ALLOCATE A LINK ADDRESS
	  POPJ	P,		;ERROR RETURN IF THE LAT IS FULL
	DPB	T1,NETSLA	;STORE THE LINK ADDRESS IN THE DDB
	MOVE	T1,[XWD NTDXSN,NTDXPN] ;ADDRESS OF CODE TO WRITE "SPN" & "DPN"
	PUSHJ	P,NCSCNT	;SEND THE CONNECT
	  PJRST	GIVSLA		;CONNECT FAILED. RETURN LAT. ERROR CODE IN T1.
NTDCN1:	PUSHJ	P,NETHIC	;WAIT FOR CONNECT (NTDCNC WILL WAKE US)
	LDB	T1,NETSLA	;GET THE SLA BACK.
	JUMPE	T1,[MOVEI T1,NRTDNA-NODERT ; CONNECT REJECT IF SLA CLEARED.
		    POPJ P,]	; GIVE ERROR RETURN WITH ERROR CODE IN T1
	LDB	T1,LATSTA	;GET THE CURRENT "STATE"
	CAIN	T1,LAT.OK	;IF IT'S "OK" THEN THE CONNECT SUCCEDED
	JRST	CPOPJ1##	; SO GIVE A GOOD RETURN

IFN PARANOID&P$LAT,<

	CAIE	T1,LAT.CC	;ARE WE STILL IN CONFIRM WAIT
	STOPCD	.,STOP,ANFLCC,	;++ NOT IN CONFIRM WAIT. LAT TABLE SCREWED.
>
	JRST	NTDCN1		;WE HAD A SPURIOUS WAKE...
;NTDXPN	ROUTINE TO SEND THE "DPN" FIELD OF A NORMAL DEVICE CONNECT. (LPT ETC.)
;CALLED	FROM NCSCNT WHERE IT CALLS THE DEVICE DEPENDANT ROUTINE TO WRITE
;	THE "DPN"
;RETURN	CPOPJ			;ALWAYS
;
NTDXPN::			;HERE TO WRITE A NORMAL "DPN" CONNECT FIELD
	PUSH	P,W		;SAVE THE NDB POINTER FOR A BIT
;DPN(OBJ)
	HLRZ	W,DEVNET(F)	;GET THE POINTER TO THE NDT
	LDB	T1,NDTOBJ	;GET THE OBJECT TYPE OF THIS DEVICE
	PUSHJ	P,BI2EBI	;WRITE THE OBJECT TYPE
	POP	P,W		;RESTORE THE NDB POINTER
;DPN(PID)
	LDB	T1,PUNIT##	;GET THE UNIT NUMBER FOR THE PID
	PJRST	BI2EBI		;WRITE THE UNIT NUMBER


;NTDXMN ROUTINE TO SEND THE "SPN" FIELD OF A NORMAL DEVICE CONNECT CONFIRM
;OPERATION (WHERE THERE IS NOT NECESSARILY A RELATED JOB TO USE . . .
;CALLED FROM NCSCNC WHERE IT CALLS THE DEVICE DEPENDANT ROUTINE TO WRITE
;	THE "SPN"
;RETURN	CPOPJ			;ALWAYS

NTDXMN::
;SPN(OBJ)
	MOVEI	T1,OBJ.TK	;OBJECT IS A JOB
	PUSHJ	P,BI2EBI	;WELL, THE MONITOR IS SORTA LIKE A JOB, KINDA
;SPN(PID)
	LDB	T1,PJOBN##	;GET DEVICE'S ASSOCIATED JOB, IF ANY
	JUMPE	T1,NTDXS3	;AND USE IT, IF IT HAS A JOB
	MOVEI	P1,[ASCIZ\The Big Blue Monitor[1,1]\]  ;NO JOB
	PJRST	AS2EAS		;SO FAKE UP A PID


;NTDXSN	ROUTINE TO SEND THE "SPN" FIELD OF A NORMAL DEVICE CONNECT. (LPT ETC.)
;CALLED	FROM NCSCNT WHERE IT CALLS THE DEVICE DEPENDANT ROUTINE TO WRITE
;	THE "SPN"
;RETURN	CPOPJ			;ALWAYS

NTDXSN::
;SPN(OBJ)
	MOVEI	T1,OBJ.TK	;OBJECT IS A JOB
	PUSHJ	P,BI2EBI	;OUTPUT
;SPN(PID)
	MOVE	T1,.CPJOB##	;GET OUR JOB NUMBER
NTDXS3:	PUSH	P,JBTPPN##(T1)	;SAVE THE JOB'S PPN
	MOVE	T1,JBTNAM##(T1)	;GET THE PROCESS NAME
	PUSHJ	P,SX2EAS	;OUTPUT
	LDB	T1,P2		;GET THE LAST CHARACTER BACK
	TRO	T1,200		;SET THE CONTINUE BIT
	DPB	T1,P2		;STORE AGAIN
	POP	P,T1		;GET THE PPN
	PJRST	PP2EAS		;OUTPUT AS [P,P]
;NTDXDS	ROUTINE TO SEND A DISCONNECT AND SET THE LINK STATUS (LT%STA) TO "DC"
;CALL	MOVEI	T1,REASON FOR DISCONNECT
;	MOVEI	F,DDB OF DEVICE TO SEND DISCONNECT TO
;	PUSHJ	P,NTDXDS
;RETURN	CPOPJ			;NO CORE.
;	CPOPJ1			;DISCONNECT SENT. LINK STATE SET TO LAT.DC
;				; (DISCONNECT CONFIRM WAIT)
;
NTDXDS::			;HERE TO SEND A DISCONNECT AND SET LAT STATE
	PUSHJ	P,NCSDSC	;ATTEMPT TO SEND THE DISCONNECT
	  POPJ	P,		;  IF NO CORE, GIVE ERROR RETURN
	LDB	T2,NETSLA	;GET A POINTER TO THE LAT ENTRY
	MOVEI	T3,LAT.DC	;GET THE NEW STATE (DISCONNECT CONFIRM WAIT)
	DPB	T3,LATST2	;UPDATE THE STATE
	RETSKP			;GIVE GOOD RETURN
;NTDNWD	ROUTINE TO PROCESS THE "NODE WENT DOWN" NDP DISPATCH ENTRY FOR
;	"NORMAL" DEVICES.
;CALL	MOVEI	P1,NUMBER OF THE NODE THAT CRASHED.
;	MOVE	F,LAT-ENTRY THAT POINTS TO THIS DEVICE
;	PUSHJ	P,NTDNWD	;CALLED FROM CLNNDB
;RETURN	CPOPJ			;ALWAYS.

NTDNWD::MOVEI	T1,RSN.XN	;GET A UNDEFINED NODE NUMBER ERROR CODE
	DPB	T1,NETRSN	;AND PRETEND THAT A DISCONNECT PUT IT THERE
	HRROS	DEVNAM(F)	;ZAP THE NAME SO DDBSRC WON'T MATCH IT.
	MOVEI	T1,IOERR!IODEND	;ALL ERROR BITS
	IORM	T1,DEVIOS(F)	;LIGHT THEM FOR DOWN DEVICE
	MOVEI	T1,IR.IER!IR.OER!IR.DOL  ;NODE DOWN PSI FLAGS
	MOVEI	T2,IONND%	;NODE DOWN (OR PATH LOST) ERROR CODE

;ENTER HERE TO DISCONNECT THE DDB,
;  T1 CONTAINING THE PSI REASON FLAGS, T2 CONTAINING EXTENDED ERROR CODE

NTDNW1:	DPB	T2,PDVESE##	;SET EXTENDED (DEVOP.) ERROR STATUS CODE
NTDNW2:	LDB	T2,PJOBN##	;GET OWNING JOB (IF ANY)
	JUMPE	T2,NTDNW7	;NO PSI/NETWAK IF NO JOB
	MOVE	T2,DEVMOD(F)	;GET DEVICE ACTIVITY FLAGS
	TRNN	T2,ASSCON!ASSPRG;DEVICE DEFINITELY IN USE BY A JOB?
	JRST	NTDNW4		;UNCLEAR, MAKE SURE NOT STUCK IN "EW"
	PUSHJ	P,PSIDVB##	;YES, GIVE THE DEVICE OFFLINE/ETC. INTERRUPT
	MOVE	S,DEVIOS(F)	;DEVICE STATUS FLAGS
	TLZ	S,IOSTBL	;CLEAR TROUBLE SO SETIOD CALLS WAKJOB
	PUSHJ	P,SETIOD##	;WAKE UP JOB IF HIBERING ON ASYNC I/O
NTDNW4:	PUSHJ	P,NETWAK	;WAKE UP JOB IF IN ANF "EW" STATE
				;  NOTE THAT WE MUST CALL NETWAK EVEN IF
				;  ASSCON!ASSPRG IS OFF, SINCE A DEVICE IN
				;  CONNECT-CONFIRM WAIT HASN'T SET THESE BITS
				;  YET (DONE BY UUOCON, NOT NETSER)
NTDNW7:	PUSHJ	P,GIVSLA	;RETURN THE NOW USLESS LAT ENTRY
	PUSHJ	P,GV2NPD	;NPD'S ARE USELESS NOW.
	SETZM	DEVDRQ(F)	;CLEAR STALE TSK DATA REQUESTS
	MOVSI	S,IOSCON	;GET AND CLEAR THE
	ANDCAB	S,DEVIOS(F)	; "DEVICE IS CONNECTED BIT"
	MOVSI	S,IOSERR	;GET THE "NETWORK DEVICE ERROR" BIT
	IORB	S,DEVIOS(F)	;AND SET IT TO STOP IO (NTDONL FAILS...)
	TLNE	S,IOSZAP	;IF THIS DDB IS MARKED FOR DELETION
	PUSHJ	P,UNLDDB	;THEN ZAP THE DDB HERE AND NOW
	POPJ	P,		;ELSE WAIT FOR UUO LEVEL (ZAPNET)
;SUBROUTINE NTDHDR - WRITE A NCS/DEVICE CONTROL HEADER
;CALL	MOVEI	F,DDB
;	MOVEI	W,NDB
;	MOVEI	T1,SIZE OF PCB TO REQUEST (INCLUDING HEADER WORDS)
;	PUSHJ	P,NTDHDR
;RETURN	CPOPJ			;NO CORE
;	CPOPJ1			;HEADER BUILT



NTDHDR::TDZA	T2,T2		;CLEAR NCS NEADER BYTE
NTDHDI::MOVEI	T2,NCT.IT	;INTERRUPT MESSAGE
	PUSH	P,P1		;SAVE P1 FOR A BIT
	MOVEI	P1,(T2)		;PUT THE NCT IN P1
	PUSHJ	P,MKNPCB	;MAKE A NUMBERED PCB
	  JRST	[POP P,P1	;IF NO CORE, THEN RESTORE P1, AND
		 POPJ P,]	; GIVE AN ERROR RETURN
	MOVE	P2,PCBPTR(U)	;GET THE BYTE POINTER
	MOVE	T1,P1		;GET THE NCT
	PUSHJ	P,NCSWHD	;FILL IN NCT, DNA, SNA, NCA & NCN
	LDB	T1,NETDLA	;GET THE DESTINATION LINK ADDRESS
IFN PARANOID&P$LAT,<
	SKIPN	T1		;JUST A BIT OF PARANOIA AGAIN
	STOPCD	.,STOP,ANFMDL,	;++ MUST HAVE A DLA ASSIGNED
>
	PUSHJ	P,BI2EBI	;WRITE
	POP	P,P1		;RESTORE P1 (AREN'T WE NICE...)
	ADDM	P3,PCBCTR(U)	;ACCOUNT FOR THE BYTES IN THE HEADER
	SETZ	P3,		;SIGNAL NO BYTES IN THE MESSAGE
	JRST	CPOPJ1##	;EXIT WITH HEADER WRITTEN
;SUBROUTINE NTDSST/NTDCST SET AND CLEAR STATUS BITS
;CALL	MOVEI	T1,BITS		;TO BE CLEARED OR SET
;	PUSHJ	P,NTDSST/NTDCST
;	CPOPJ			;NO CORE
;	CPOPJ1			;MESSAGE SENT

NTDXST::SETZ	T2,		;SEND WITH A ZERO STC
	JRST	NTDST1		;ON TO MAIN LINE CODE
NTDSST::SKIPA	T2,[1]		;SET STATUS
NTDCST::MOVEI	T2,2		;CLEAR STATUS
NTDST1:	PUSHJ	P,SAVE3##	;SAVE THE P'S
	PUSH	P,T1		;SAVE STD
	PUSH	P,T2		;SAVE STC
	MOVEI	T1,^D16		;GET THE LENGTH OF A STANDARD SHORT PCB
	PUSHJ	P,NTDHDI	;WRITE INTERRUPT HEADER
	  JRST	[POP P,T2	;RESTORE THE MESSAGE TYPE
		PJRST	TPOPJ##]	;AND EXIT
;CNT
	IBP	P2		;STEP OVER THE COUNT FIELD
	MOVE	P1,P2		;SAVE THE COUNT POSITION
	MOVEI	T1,DC.STS	;GET STATUS MESSAGE CODE
	PUSHJ	P,BI2EBI	;WRITE
;STC
	POP	P,T1		;GET THE STC
	PUSHJ	P,BI2EBI	;WRITE
;STD
	POP	P,T1		;GET THE STD
	PUSHJ	P,BI2EBI	;WRITE
;CNT
	DPB	P3,P1		;STORE THE COUNT FIELD
	ADDI	P3,1		;UPDATE THE COUNT FIELD
	ADDM	P3,PCBCTR(U)	;UPDATE THE MESSAGE LENGTH
	PJRST	NETWSR		;WRITE THE MESSAGE



;SUBROUTINE NTDWRT - WRITE A DATA MESSAGE
;CALL	MOVEI	F,DDB
;	MOVEI	U,PCB
;	MOVEI	T1,CONVERSION CODE
;	PUSHJ	P,NTDWRT	;SEND THE PCB
;RETURN	CPOPJ			;WRITTEN
;
NTDWRT::DPB	T1,PCBPCV	;STORE TYPE
;	MOVSI	T1,PCB.UR	;GET THE USER DATA FLAG
;	IORM	T1,PCBBLK(U)	;STORE
	PJRST	NETWRT		;SEND THE MESSAGE
SUBTTL LOCKS -- NETSER INTERLOCK MANAGEMENT ROUTINES.

				COMMENT @

The following routines are used to manage the NETSER interlock.
These routines are called ONLY by macros defined in "S".

   NTLCLK	Lock the NETSER database from UUO-level.  The job loops
		waiting for the interlock.  (Can only happen on an SMP system
		when the other CPU has the interlock.
   NTUNLK	Undo's the effects of a call to NTLCLK.
   NTLCKJ	If the job currently owns the interlock this routine does
		nothing,  Otherwise it gets the interlock and co-routines back
		to the calling routine (ala SAVE4) and frees the interlock
		when the routine POPJ's.
   NTLCKI	This routine locks the NETSER database from interrupt level.
   NTULKI	This routine undoes the effects of NTLCKI.
   NTCHCK	This routine skips if the calling process owns the interlock.
   NTSAV	This routine returns the interlock until the calling routine
		POPJ's at which point it gets it back again. (Like RTNEVM)
   NTLERR	This routine gives up the interlock only if the process owns
		it, and never generates an error.  (Used by ERRCON and friends
		to return the interlock when the owning process gets an error)

Variables of interest are.

   NTLOCK	This is the interlock word.  -1 implies interlock if free.
   NTUSER	This is the "Process I-D" of the process that owns the
		interlock.  Legal values are.
		  XWD 0,N	N is the job number of the owning job
		  XWD -1,n	N is the number of the cpu that has the
				interlock at interrupt level
		  XWD N,-1	N is the number of the cpu that has the
				interlock at clock (scheduler-comcon) level.
   %NTCNT	This is a vector of counters that keep track of various facets
		of the interlock management scheme.  Elements of the vector are
		 + 0	Number of attempts to get interlock at interrupt level
		 + 1	Number of times interrupt level succeded in getting lock
		 + 2	Number of times interlock gotten at UUO level
		 + 3	Number of times UUO level has failed. (Tries again
			immediatly.  Will hang the machine if gets screwed up)
		 + 4	Number of attempts at command level that failed.
		 + 5	Number of times the interlock given up by error routines


				    @
NTLCKI::NTDBUG	EITHER,INT	;MAY HAVE IT AT ANOTHER LEVEL
	AOS	%NTCNT+0	;COUNT THE NUMBER OF ATTEMPTS
	AOSE	NTLOCK##	;TRY TO GET THE INTERLOCK
	POPJ	P,		;RETURN NOW IF FAILED
IFN FTMP,<			;SET THE "OWNING CPU" WORD
	APRID	INONT##
>
	AOS	%NTCNT+1	;COUNT THE SUCCESSFUL ATTEMPTS
	PUSH	P,T1		;SAVE T1 OVER THE CALL TO NTLPID
	PUSHJ	P,NTLPID	;GET A "UNIQUE" IDENTIFIER
	MOVEM	T1,NTUSER	;SAVE IT IN CASE WE TRY TO RECURSE
	JRST	TPOPJ1##	;AND GIVE A SUCCESSFUL RETURN

NTULKI::NTDBUG	YES,INT		;BETTER HAVE THE INTERLOCK AT INT LEVEL...
	PJRST	NTLFRE		;FREE INTERLOCK AND RETURN
NTLCLK::NTDBUG	NO,UUO		;DON'T HAVE INTERLOCK YET, AND AT UUO LEVEL
	AOSA	%NTCNT+2	;COUNT THE NUMBER OF TIMES GOTTEN AT UUO LEVEL
	AOS	%NTCNT+3	;COUNT THE FAILURE
	SKIPGE	NTLOCK##	;GIVE OTHER CPUS A CHANCE
	AOSE	NTLOCK##	;TRY TO GET THE INTERLOCK
	JRST	.-3		;WAIT
IFN FTMP,<
	APRID	INONT##
>
	PUSH	P,T1		;NOW THAT WE'VE GOT THE INTERLOCK, SAVE T1
	PUSHJ	P,NTLPID	;GET OUR "PROCESS ID"
	MOVEM	T1,NTUSER	;SAVE IT IN CASE WE RECURSE
	JRST	TPOPJ##		;RESTORE T1 AND RETURN
NTUNLK::NTDBUG	YES,UUO		;WE BETTER HAVE THE LOCK AT UUO-LEVEL
	PUSHJ	P,NTLFRE	;FREE THE INTERLOCK
	SKIPN	NTRQST		;DOES NETSCN WANT TO RUN?
	POPJ	P,		;IF NOT, RETURN NOW
	PJRST	NETQUE		;GO CAUSE AN INTERRUPT FOR NETSER


NTLCKJ::PUSHJ	P,NTCHCK	;SEE IF WE OWN THE INTERLOCK
	JRST	.+2		;IF NOT, WE MUST GET IT
	POPJ	P,		;IF WE OWN IT, THEN WE CAN RETURN NOW
	SKIPE	.CPISF##	;ARE WE AT "COMCON" LEVEL (COMMAND PROCESSING)
	JRST	NTLCKC		;IF SO, WE MUST HANDLE THE INTERLOCK SPECIAL
	PUSHJ	P,NTLCLK	;GET THE INTERLOCK
	POP	P,(P)		;AND GO THROUGH THESE CONTORTIONS TO
	PUSHJ	P,@1(P)		; CO-ROUTINE BACK TO OUR CALLER
	  JRST	.+2		;RESPECT
	AOS	(P)		; SKIP RETURNS
	PJRST	NTUNLK		; AND FREE THE INTERLOCK ON CALLERS RETURN

NTLCKC:	PUSHJ	P,NTLCKI	;TRY TO GET THE INTERLOCK (ALA INTERRUPT LEVEL)
	  JRST	[AOS %NTCNT+4	;COUNT THE FAILURE
		 JRST DLYCM1##]	; AND DELAY THE COMMAND (TRYS AGAIN SOON...)
	POP	P,(P)		;CO-ROUTINE BACK
	PUSHJ	P,@1(P)		; TO OUR CALLER
	  JRST	.+2		;RESPECT
	AOS	(P)		; SKIP RETURNS
	PJRST	NTULKI		;RETURN THE INTERLOCK

NTSAV::	PUSHJ	P,NTCHCK	;SEE IF WE OWN THE INTERLOCK
	  POPJ	P,		;IF WE DON'T, WE CAN'T RETURN IT
	PUSHJ	P,NTUNLK	;RETURN THE INTERLOCK
	POP	P,(P)		;CO-ROUTINE BACK
	PUSHJ	P,@1(P)		; TO OUR CALLER
	  JRST	.+2		;RESPECT
	AOS	(P)		; SKIP RETURNS
	PJRST	NTLCLK		;AND GET THE INTERLOCK BACK AGAIN
NTCHCK::AOSN	NTLOCK##	;SEE IF ANYONE HAS THE INTERLOCK
	JRST	[SETOM NTLOCK	;  IF NOT, THEN WE CERTAINLY DON'T.
		 POPJ P,]	;  SO GIVE THE ERROR RETURN
	PUSH	P,T1		;WE NEED "T1" AS A TEMP
	PUSHJ	P,NTLPID	;GET OUR "PROCESS ID"
	CAMN	T1,NTUSER	;SEE IF IT'S US THAT HAS THE INTERLOCK
	AOS	-1(P)		;IF SO, SKIP RETURN
	PJRST	TPOPJ##		;RESTORE T1 AND (SKIP) RETURN

NTLPID::PUSHJ	P,INTLVL##	;IF WE ARE AT UUO LEVEL,
	JRST	[MOVE T1,.CPJOB## ;THEN OUR PID IS OUR JOB NUMBER
		 POPJ P,]	; SO RETURN THAT TO OUR CALLER
	HRRO	T1,.CPCPN##	;ASSUME THAT WE ARE AT INTERRUPT LEVEL
	SKIPE	.CPISF##	;CHECK TO SEE IF WE ARE AT SCHEDULER LEVEL,
	MOVS	T1,T1		; AND IF WE ARE, SWAP HALFWORDS
	POPJ	P,		;RETURN "PID" TO CALLER

NTLERR::PUSHJ	P,NTCHCK	;DO WE OWN THE INTERLOCK
	  POPJ	P,		;IF NOT, THEN RETURN NOW
	AOS	%NTCNT+5	;COUNT NUMBER OF ERRORS WITH LOCK
NTLFRE:	SETZM	NTUSER		;CLEAR THE USER.
IFN FTMP,<
	SETOM	INONT##		;CLEAR THE "OWNING CPU" WORD
>
	SETOM	NTLOCK##	;FREE THE LOCK
	POPJ	P,		; AND RETURN
SUBTTL NETWORK SUBROUTINES

;HERE ARE THE TWO ENTRIES TO TURN ON NETWORK INTERRUPTS

NTONP1::AOS	(P)		;GIVE SKIP RETURN
NTONPJ::NETON			;RE-ENABLE NETWORK INTERRUPTS
	POPJ	P,		;RETURN



;ROUTINES TO INTERFACE CERTIAN ERROR ROUTINES TO NETSER.  THESE ARE ROUTINES
; THAT ARE NOT EXPECTED TO RETURN, AND HENCE MUST NOT BE CALLED WITH THE
; NETSER INTERLOCK.

DEFINE	X(A,B),<		;;MACRO TO MAKE THE CORRISPONDENCE MORE OBVIOUS
A::	NTSAVE			;;FIRST "SAVE" THE INTERLOCK
	PJRST	B##>		;;THEN GO TO ROUTINE "B" THAT DOES THE WORK

	X(NTDILI,ILLINP)	;ILLEGAL INPUT UUO
	X(NTDILO,ILLOUT)	;ILLEGAL OUTPUT UUO
SUBTTL	BYTE POINTERS FOR NETSER


;BYTE POINTERS INTO NETWORK DDB'S (INDEXED BY "F")

NETRSN::EXP	NT%RSN		;REASON BYTE
NETSLA::EXP	NT%SLA		;SOURCE LINK ADDRESS
NETDLA::EXP	NT%DLA		;DESTINATION LINK ADDRESS
NETZWD::EXP	NT%ZWD		;DDB LENGTH (A LA GETZWD/GIVZWD)
NETRLN::EXP	NT%RLN		;LOGICAL RECORD LENGTH ("RLN" - SEE NETPRM)
NETMML::EXP	NT%MML		;MAXIMUM MESSAGE LENGTH ("MML" - SEE NETPRM)
NETDVT::EXP	NT%DVT		;DEVICE ATTRIBUTE BITS ("DVT" - SEE NETPRM)
NETDVU::EXP	NT%DVU		;DEVICE "UNIT" TYPE ("DVU" - SEE NETPRM)
NETDVV::EXP	NT%DVV		;DEVICE "CONTROLLER" TYPE ("DVV" - SEE NETPRM)



;BYTE POINTERS INTO PCB'S.  INDEXED BY "U"

PCBPCV::EXP	PC%PCV		;CONVERSION TYPE
PCBMSN::EXP	PC%MSN		;MESSAGE NUMBER



;BYTE POINTERS INTO NDB'S (GETTAB TABLE .GTNDB==161)

	$LOW
NDBTBL::EXP	NDBLEN		;(00) LENGTH OF NDB
	EXP	NB%NXT		;(01) ADDRESS OF NEXT NDB
	EXP	NB%NNM		;(02) NODE NUMBER
	EXP	NB%SNM		;(03) ADDRESS OF STATION NAME
NDBTIM::EXP	NB%TIM		;(04) TIMER.
	EXP	NB%NGH		;(05) FIRST NEIGHBOR ENTRY
	EXP	NB%NGL		;(06) LAST NEIGHBOR ENTRY
	EXP	NB%NGN		;(07) NODE NUMBER FROM NB%NGH
	EXP	NB%OPR		;(10) ADDRESS OF OPR LDB
	EXP	NB%CTJ		;(11) JOB NUMBER OF STATION CTL
				;	OUTPUT MESSAGE NUMBERS
NDBLAR::EXP	NB%LAR		;(12) LAST ACK RECEIVED
NDBLAP::EXP	NB%LAP		;(13) LAST OUTPUT MESSAGE# ACK'ED
NDBLMS::EXP	NB%LMS		;(14) LAST MESSAGE SENT
NDBLMA::EXP	NB%LMA		;(15) LAST MESSAGE NUMBER ASSIGNED
NDBLAS::EXP	NB%LAS		;(16) LAST ACK SENT
				;	INPUT MESSAGE NUMBERS
NDBLMR::EXP	NB%LMR		;(17) LAST INPUT MESSAGE RECEIVED
NDBLMP::EXP	NB%LMP		;(20) LAST MESSAGE PROCESSED
	EXP	NB%SDT		;(21) SYSTEM BUILD DATE ADDRESS
	EXP	NB%SID		;(22) SYSTEM ID ADDRESS
	EXP	NB%MOM		;(23) MAXIMUM OUTSTANDING MESSAGE COUNT
	EXP	NB%DEV		;(24) FIRST DEVICE
	EXP	NB%NVR		;(25) NCL VERSION OF REMOTE

NDBMXL==:<.-NDBTBL-1>_^D9	;LENGTH OF NDB POINTERS TABLE (GETTAB)
	$HIGH
;POINTERS INTO A NETWORK DEVICE TABLE

NDTTYP::EXP	ND%TYP		;-10 DEVTYP BYTE
NDTBFZ::EXP	ND%BFZ		;-10 BUFFER SIZE
NDTSPL::EXP	ND%SPL		;-10 SPOOL BITS
NDTDVT::EXP	ND%DVT		;-11 DEVICE ATTRIBUTES
NDTOBJ::EXP	ND%OBJ		;-11 DEVICE TYPE CODE
NDTDCM::EXP	ND%DCM		;NETWORK DEVICE MODES


;POINTER TO THE "STATE" FIELD OF A LAT ENTRY (INDEXED BY T1)

LATSTA::LT%STA+NETLAT(T1)	;POINT TO THE "STATE" FIELD OF A LAT ENTRY
LATST2::LT%STA+NETLAT(T2)	;DITTO, INDEX BY T2
LATSP2::LT%STA+NETLAT(P2)	;DITTO, INDEX BY P2
LATPTR::LT%PTR+NETLAT(T1)	;POINT TO DDB/LDB ADDRESS
LATPT2::LT%PTR+NETLAT(T2)	;DITTO, INDEX BY T2
LATPP2::LT%PTR+NETLAT(P2)	;DITTO, INDEX BY P2
SUBTTL LOCAL STORAGE FOR NETSER
	$LOW

DIALDB::BLOCK	1		;ADDRESS OF LDB WAITING FOR A DIAL REQUEST
DIALJB::BLOCK	1		;JOB NUMBER OWNING C(DIALDB)
DIALFL::BLOCK	1		;ASSOCIATED DIAL REQUEST FLAGS
				;   1B0    DIALOUT COMPLETE
				;  LRLADR  DIALOUT IN PROGRESS (REQ ISSUED)
				;  LRLCHR  REMOTE PROCESSING DIALOUT REQ
				;  LRLDSR  DIALOUT SUCCEEDED (IF 1B0)
DIALNM::BLOCK	2		;PHONE NUMBER FOR REMOTE DIALER

NTUSER::EXP	0		;LAST (CURRENT) OWNER OF THE NETSER INTERLOCK

%NTCNT::BLOCK	10		;A SET OF COUNTERS REGARDING THE USE OF THE
				; NETSER INTERLOCK

NTFLGS:	0			;FLAGS FROM LAST MESSAGE RECIEVED
NTFACK:	0			;NONZERO IF WE RECEIVED A MESSAGE IN
				;  THE LAST JIFFY.  THIS WILL CAUSE THE JIFFY
				;  CODE TO SEE IF ANY ACK'S NEED TO BE SENT
				;  TO ANY NODE.  THIS IS AN EFFICIENCY HACK
				;  THAT ALLOWS US TO CUT-DOWN ON THE NUMBER OF
				;  TIMES WE MUST SCAN THE NDB-CHAIN DURING
				;  THE JIFFY CYCLE
NTQRCT:	0			;NONZERO IF WE NEED TO RECOMPUTE TOPOLOGY
NTQJIF:	0			;NONZERO IF NETSCN SHOULD DO JIFFY CHECK
NTQOUT:	0			;LIST OF OUTPUT MESSAGES BACK FROM THE FEK'S
NTQINP:	0			;LIST OF INPUT MESSAGES TO PROCESS
NTQNDB:	0			;CHAIN OF NDB'S THAT NEED SERVICE
NTQSEC:	0			;NON-ZERO IF NETSCN SHOULD DO ONCE/SEC
NTRQST::0			;NON-ZERO TO REQUEST AN INTERRUPT WHEN THE
				; INTERLOCK IS NEXT FREE'D (NETLKU CHECKS IT)

PCBECT:	EXP	0		;COUNT OF "EMERGENCY" PCB'S (GOTTEN BY
				;  CALLING "PCBEGT"
PCBELS:	EXP	0		;LIST OF EMERGENCY PCB'S

NTUEFC::EXP	0		;NeTwork Use Emergency Free Core. THE
				;  NEXT CALL TO NCSHDR WILL USE "EMERGENCY"
				;  FREE CORE IF NECESSARY.

IFN PARANOID&P$EAT,<		;DEBUGGING CODE TO EAT A RANDOM NCL MESSAGE
%TROLL:	EXP	77777777777	;GOVERNS THE EATING OF MESSAGES.
				;START BIG TO GIVE THINGS A CHANCE
>

IFN PARANOID&P$NTR,<		;CHECKING CODE TO SEE WHO GOT THE INTERLOCK
%NTUSR:	0			;PC OF THE LAST OWNER OF THE "NT" INTERLOCK
>

IFN PARANOID&P$COR,<		;ASSEMBLE IF WE ARE LOSING FREE CORE
%NTMEM:	0			;A POINTER TO ALL THE NETWORK CORE.
>
	SUBTTL	NETWORK STATISTICS

	DEFINE	SUBTBL(MAX,LOC)<<<MAX>B8+LOC-NETGTT>>

;ANF NETWORK STATISTICS

NETGTT::

%NTCOR:	0			;# WORDS OF FREE SPACE NOW IN USE
%NTMAX:	0			;MAXIMUM %NTCOR HAS GONE
%NTAVG:	0			;EXPONENTIAL AVERAGE OF %NTCOR * 10^4
%NTBAD:	0			;# BAD MESSAGES RECEIVED AND IGNORED

%NTRTP:	SUBTBL(NCT.TP,NCLRTP)
%NTRMT:	SUBTBL(NC.MAX,NCLRMT)
%NTRDL:	SUBTBL(NETLNH-1,NCLRDL)
%NTXTP:	SUBTBL(NCT.TP,NCLXTP)
%NTXMT:	SUBTBL(NC.MAX,NCLXMT)
%NTXDL:	SUBTBL(NETLNH-1,NCLXDL)

%NTBLC:	0			;LH - PC OF DETECTION OF BAD MESSAGE
				;RH - PCB ADDRESS OF BAD MESSAGE

%NTBYI::BLOCK	1		;COUNT OF BYTES INPUT BY THE NETWORK
%NTBYO::BLOCK	1		;COUNT OF BYTES OUTPUT BY THE NETWORK

%NTNIP::EXP	ANFNIP##	;ANF NI/ETHERNET PROTOCOL (0 IF NOT SELECTED)
%NTNIM::EXP	526000040000	;ANF NI/ETHERNET MULTICAST ADDRESS
	EXP	ANFNIM##	; (SECOND HALF) ADDRESS = AB-00-04-00-XX-XX
%NTNII::EXP	^D64		;ANF NI/ETHERNET BROADCAST INTERVAL MAXIMA
%NTNIJ::EXP	^D64*5 + 5	;ANF NI/ETHERNET FEK KEEP-ALIVE TIMER VALUE


;ADD FIXED-OFFSET ENTRIES ABOVE THIS POINT

NCLRTP:	BLOCK	NCT.TP+1	;RECEIVED NCL TYPES
NCLRMT:	BLOCK	NC.MAX+1	;RECEIVED NUMBERED TYPES
NCLRDL:	BLOCK	NETLNH		;RECEIVED DATA LENGTHS BY POWERS OF 2
NCLXTP:	BLOCK	NCT.TP+1	;TRANSMITTED COUNTER PARTS OF ABOVE
NCLXMT:	BLOCK	NC.MAX+1
NCLXDL:	BLOCK	NETLNH

;ADD SUBTABLE DATA ENTRIES ABOVE THIS POINT

.NTMXL==:<.-NETGTT-1>_9
	XLIST			;DON'T LIST THE LITERALS
	$LIT
	LIST

NETEND::!END