Google
 

Trailing-Edge - PDP-10 Archives - BB-X140B-BB_1986 - 10,7/703anf/dndcp4.p11
There are 3 other files named dndcp4.p11 in the archive. Click here to see a list.
.SBTTL	DECNET COMPATIBLE PORT (MARK IV) FOR NSP  15 JAN 85
.IF NE,FT.DCP

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1977, 1978,1979,1980,1981,1984 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.

VRNSP3=036			;FILE EDIT NUMBER
;RDH	.PRINT	VRNSP3		;VERSION NUMBER

.REPT	0
NOTES ON READING THIS MODULE

REGISTER USAGE

R0	ABOUT THE ONLY REGISTER THAT IS TRULY USED AS A TEMPORARY. A THIRD OF THE
	CODE TRIES TO LOAD THIS WITH THE RIGHT VALUES, ANOTHER THIRD STORES IT.

R1	EXCEPT WHEN THE CODE NEEDS TWO TEMPORARIES, THIS USUALLY POINTS TO THE
	LEB FOR THE LINK WE ARE HANDLING.

R2,R3	THESE POINT TO THE MESSAGE WE ARE DECODING. THE STANDARD NCL ROUTINES
	GETBYT, GETEXN, ETC. ARE HOW WE LOOK AT EACH FIELD.

R4,R5	ONLY RARELY DO THESE POINT TO THE STATION AND LINE BLOCKS (SB AND J).
	MOST OF THE TIME THEY POINT TO THE MESSAGE THE CODE IS GENERATING.
	FIELDS ARE ADDED VIA ANALOGUES OF SIMILAR NCL ROUTINES (NSPPBY, NSPPEX
	INSTEAD OF PUTBYT, PUTEXN).

IMPORTANT LOCATIONS
THESE MUST BE SETUP AS SOON AS POSSIBLE. DUE TO THE DEARTH OF REGISTERS, SEVERAL
VARIABLES ARE USED TO KEEP TRACK OF IMPORTANT DATA.

NSPLEB	THIS POINTS TO THE CURRENT LEB. THE LEB SEARCH ROUTINES SET IT
	UP SO IT'S ALMOST ALWAYS AVAILABLE.

NSPSB	THIS IS THE ADDRESS OF THE NCL SCB RELATED TO THE LEB. (NOTE: IT COULD
	JUST AS EASILY BE ANOTHER NSP SCB, HOWEVER ALL THE COMMENTS WILL REFER
	TO IT AS AN NCL NODE.)

NSPLB	THIS IS THE ADDRESS OF THE LINK BLOCK ASSOCIATED WITH THE NSP SIDE OF
	THE LEB.

DNA,SNA	THESE ARE USUALLY WHAT ONE WOULD EXPECT. THEY HARDLY EVER HAVE TO BE
	REFERENCED.

NSPLMG	THIS IS THE ADDRESS OF THE FIRST CHUNK OF THE CURRENT NCL MESSAGE,
	BE IT FOR READING OR CREATING. ONCE MESSAGES ARE DECODED COMPLETELY,
	THEY MUST BE PASSED TO NCLODN TO BE FREED.

NSPPMG	SAME AS NSPLMG, BUT FOR NSP MESSAGES. SEVERAL PLACES IN THE CODE USE
	THE "L" SUFFIX TO DENOTE INVOLVEMENT WITH NCP AND "P" FOR INVOLVEMENT
	WITH NSP.
.ENDR
	.SBTTL	CONNECT MESSAGE TRANSLATION CONVENTIONS

.REPT	0

NSP -> NCL
    CI
	THE "DPN" OF THE NCL CONNECT MESSAGE IS CONSTRUCTED FROM THE
	VARIOUS FIELDS OF THE NSP MESSAGE AS FOLLOWS
	<0>DST-OBJ<0>SRC-OBJ<0>RQID<0>PSWD<0>ACCT<0>USRDATA<0>

	THE "SPN" FIELD IS USELESS

    CC
	THE "DPN" FIELD IS USELESS

	THE "SPN" FIELD IS OF THE FORM
	<0>USER-DATA<0>


NCL -> NSP

    CI
	THE DPN, IF IT'S A "TSK" OBJECT GETS PARSED AS
	<0>DST-OBJ<0>SRC-OBJ<0>RQID<0>PSWD<0>ACCT<0>USRDATA<0>
	AND IS USED TO BUILD THE NSP CONNECT-INIT

	THS "SPN" IS IGNORED.

    CC
	THE "DPN" IS IGNORED

	THE "SPN" IS PARSED AS
	<0>USER-DATA<0>
	AND FORMS THE USER DATA FIELD OF THE CONNECT-CONFIRM

.ENDR
.REPT	0

HENCE, ONE SHOULD USE THE TSK. UUO AS FOLLOWS.

  PASSIVE CASE.
	NPD1 <- THE "USER DATA" TO RETURN UPON A SUCCESSFUL CONNECT
	NPD2 <- THE "PATTERN" TO MATCH (NULLS SEPARATE THE FIELDS)
    AFTER A SUCCESSFUL CONNECT,
	NPD1 <- THE "ACTUAL" THAT MATCHED THE "PATTERN"
	NPD2 <- RANDOM TRASH CONSTRUCTED BY THE PORT

  ACTIVE CASE
	NPD1 <- GETS IGNORED BY THE PORT
	NPD2 <- IS THE BIG MESS (PUNCTUATED BY ZEROS) TO SEND
    AFTER A SUCCESSFUL CONNECTION
	NPD1 <- TRASH CONSTRUCTED BY THE PORT
	NPD2 <- THE CONNECT CONFIRM "USER-DATA"

.ENDR
.REPT	0

This version of the DCP node-init's as a Phase II DECnet node

The NSPLST macro is

	DCP4LN	QLN,QOB,QVN,QVS,QHN,QHS,QCN

Where

	QLN	Sync line number
	QOB	Object-type,count pairs, enclosed in <>
	QVN	ANF node number to present DCP node
	QVS	ANF node name to present DCP node
	QHN	DECNET node number to claim to be
	QHS	DECNET node name to claim to be
	QCN	ANF node number to receive DECNET connects

The following rules hold.

    1)	All NSP connect init's get sent to the node "QCN"
    2)	All NCL connects will appear to the NSP system to come
	from the node QHN/QHS, but the DCP will remember who
	really sent them and process all data on that link
	accordingly.

.ENDR


.IF EQ,FTDCP4
.PRINT	7;Wrong version of DCP software loaded!!!!!!
.ENDC
	.SBTTL	MACROS

.IIF LT SNMSIZ-6,.PRINT SNMSIZ; MUST BE GEQ 6 FOR NSP V3.0

;MACRO TO USE TO ABORT A RECEIVED NSP MESSAGE
.MACRO	NSPIBD
	  JSR	  PC,NSPIB0
.ENDM

;MACRO TO ABORT PROCESSING OF A NCL MESSAGE
.MACRO	NSPOBD
	  JSR	  PC,NSPOB0
.ENDM


;MACRO TO DEFER PROCESSING OF A NCL MESSAGE UNTIL THERE IS ROOM
.MACRO	NSPODF
	  JMP	  NSPOD0
.ENDM


CN.SEG	=CN.SCB			;WHERE WE KEEP NSP SEGMENT NUMBER

ND	NSPMAX,MSGMAX		;MAXIMUM NSP MESSAGE SIZE (AND YOU TAKES YOUR
				; CHANCES IF NSPMAX .NE. MSGMAX!!!)
ND	NSPTIM,10.		;SECONDS BEFORE WE RETRANSMIT NUMBERED MESSAGES
	.SBTTL		DCP LINK ENTRY DEFS


	BLOCK	LE

	X	STS		;STATUS FOR THIS LINK
	    LES.DC=B4		;HAVE TO SEND DC
	    LES.DV=B5		;CONNECTION IS FOR A DEVICE, NOT TASK
	    LES.DS=B6		;CONNECTION IS BEING BROKEN
	    LES.LA=B7		;HAVE TO SEND LS/INT ACK
	    LES.LN=B8		;HAVE TO SEND LS/INT NAK
	    LES.DA=B9		;HAVE TO SEND DATA ACK
	    LES.DN=B10		;HAVE TO SEND DATA NAK
	    LES.LS=B11		;HAVE TO SEND LS TO REQUEST AN INTERRUPT MSG
	    LES.DR=B12		;HAVE TO SEND AN EXTRA DATA REQUEST
	    LES.MD=B13		;THIS IS 1 IF WE ARE IN THE MIDDLE OF A DIALOG MSG
	    LES.MR=B14		;THIS IS 1 IF OTHER SIDE IS DOING MESSAGE REQUESTING
	    LES.NR=B15		;THIS IS 1 IF OTHER SIDE IS DOING NO REQUESTING AT ALL
	X	LNK		;LINK TO NEXT LEB FOR THIS NODE
	X	SCB		;POINTER TO SCB FOR THIS NODE
	X	NCL		;LINK ADDRESS NCL IS USING
	X	DCP		;LINK ADDRESS THAT NCL AND NSP THINK IS THE DESTINATION
	X	NSP		;LINK ADDRESS NSP IS USING
	X	LIL		;LAST INPUT LS/INT MESSAGE
	X	LID		;LAST INPUT DATA MESSAGE
	X	LOL		;LAST OUTPUT INT/LS MESSAGE
	X	LOD		;LAST OUTPUT DATA MESSAGE
	X	OQL		;LIST HEADER OF OUTPUT BUT NOT ACKNOWLEDGED LS/INT MSGS
	X	OQD		;SAME, BUT FOR DATA MESSAGES
	X	BUF		;BUFFER FOR THIS LINK
	X	IIK		;LAST INPUT INTERUPT ACKED
	X	IDK		;LAST INPUT DATA ACKED
	X	ODK		;LAST OUTPUT DATA ACKED
	X	ODS		;LAST OUTPUT DATA SENT
	XX	STT		;CURRENT LINK STATE:
	    LES.ID=0		;IDLE HARDLY A STATE, BUT WHY NOT?
	    LES.LI=2		;NCL IS TRYING TO INIT A LOGICAL LINK
	    LES.PI=4		;NSP IS TRYING TI INIT A LOGICAL LINK
	    LES.RN=6		;LINK SETUP, DATA MESSAGES SHOULD BE FLYING
	    LES.DS=10		;EITHER SIDE IS TRYING TO DISCONNECT
	XX	RSN		;REASON TO SEND TO NSP FOR DISCONNECT
	XX	MDR		;MAXX NUMBER OF DATA REQUESTS FOR THIS LINK
	XX	ODR		;OUTSTANDING DATA REQUESTS FOR THIS LINK
	XX	TIM		;LOGICAL LINK TIMER
	X	SIZ,0		;SIZE OF LEB
	.SBTTL		DATA BASE FOR LINKS

.MACRO	DCP4LN,QLN,QOB,QVN,QVS,QHN,QHS,QCN
	.IRP	Q,<QOB>
		COUNT	Q
	.ENDR
.ENDM

.MACRO	COUNT,TYPE,NUM
	QQQ=QQQ+NUM
.ENDM

QQQ=0

NSPLST


;LEBS ARE ALLOCATED BELOW HERE
LEBMGC:	.BLKB	LE.SIZ		;SPECIAL LEB TO USE WHEN ALL OUT OF THE REST

.MACRO	LEBX,A
	.EVEN
LEB'A:	.=.+LE.DCP
	.WORD	A
	.=LEB'A+LE.SIZ
.ENDM

QQN=1				;START LEBS AT 1 SINCE LINK ADDR 0 IS ILLEGAL

.REPT	QQQ			; BUILD ALL THE LEB'S
	LEBX	\QQN
	QQN=QQN+1
.ENDR

LEBEND:


;NOW DEFINE SYMBOLS USED TO LET US INDEX INTO THE LEBS. LE.DCP, THE INTERNAL
;LINK ADDRESS, HAS THE LEB NUMBER IN THE LOW BITS AND AN INCREMENTING
;NUMBER IN THE HIGH BITS. TOGETHER THEY FORM A LINK ADDRESS LEGAL TO
;BOTH NCL AND NSP. LED.OF IS A MASK THAT EXTRACTS THE LEB NUMBER,
;LED.IN IS THE INCREMENT TO AFFECT THE HIGH BITS.

	LED.IN	=100000		;THERE MAY BE A FASTER WAY OF DOING THIS...
.REPT	20
.IIF EQ LED.IN&QQQ, LED.IN=LED.IN/2
.ENDR

	LED.IN	=-LED.IN*2	;MAKE LED.IN BE FIRST BIT NOT USED IN LEB NUMBER
	LED.OF	=LED.IN-1	;THEREFORE THE NUMBER MASK IS ONE LESS THAN LED.OF
	.SBTTL		FLAGS, MASKS, AND VALUES FOR NSP MESSAGES

;RTHDR FIELD (OPTIONAL)
;EXTEN BIT MASK

	NM.RTF	=2		;BIT THAT SAYS ROUTING HEADER
	 NM.RFMT=100		;SET INDICATES ASCII ROUTE HEADER FORM
	 NM.EFMT=60		;PROTOCOL TYPE
	  NM.EF3=0		; PROTOCOL IS NSP 3.0
	 NM.MPRI=4		;VALUE FOR A PRIORITY 1 MESSAGE
	 NM.ROU	=NM.RTF!NM.RFMT!NM.EF3!<NM.MPRI*1> ;WE BUILD ASCII ROUTING HEADERS

;MSGFLG FIELD (REQUIRED)
;1 BYTE BIT MASK
;FOR TYPE 0
	NM.BOM	=40		;BEGIN OF MESSAGE SEGMENT
	NM.EOM	=100		;END OF MESSAGE SEGMENT
	NM.IDT	=60		;INTERRUPT DATA
	NM.LS	=20		;LINK SERVICES
				;LSFLAGS FIELD:
	 NM.MAK	=60		;MASK FOR MSGFLG FIELD
	 NM.STP	=1		;STOP DATA FLOW
	 NM.FCI	=14		;MASK FOR FCVAL INTERPRETATION FIELD
	 NM.IRC	=4		;INTERRUPT REQUEST COUNT

;FOR TYPE 1
	NM.LSA	=24		;LS/INT ACK
	 NM.NAK	=B12		;BIT IN ACKNUM THAT SAYS NAK
	NM.DTA	=4		;DATA ACK

;FOR TYPE 2
	NM.CI	=30		;CONNECT INIT
	NM.CC	=50		;CONNECT CONFIRM
				;SERVICES FIELD:
	 NM.MSA	=20		;MESSAGE ACKNOWLEDGEMENT REQUESTED IF SET
	 NM.LNK	=1		;LOGICAL LINK (NOT SINGLE MESSAGE)
	 NM.SRQ	=4		;SEGMENT REQUEST COUNTS
	NM.DI	=70		;DISCONNECT INITIATE
	NM.DC	=110		;DISCONNECT CONFIRM
		NM.NOE	=0	;NO ERROR
		NM.RAF	=1	;RESOURCE ALLOCATION FAILURE
		NM.NDE	=2	;DESTINATION NODE DOES NOT EXIST
		NM.NSD	=3	;NODE SHUTTING DOWN
		NM.PDE	=4	;PROCESS DOESN'T EXIST
		NM.IPN	=5	;INVALID PROCESS NAME
		NM.UEC	=7	;UNSPECIFIED ERROR CONDITION
		NM.TCN	=32.	;TOO MANY CONNECTIONS TO NODE
		NM.TCP	=33.	;TOO MANY CONNECTIONS TO PROCESS
		NM.SER	=35.	;CI, CC SERVICES MISMATCH
		NM.NPT	=39.	;NO PATH TO DESTINATION
		NM.NXL	=41.	;DSTADDR LOGICAL LINK DOES NOT EXIST
		NM.CNF	=42.	;CONFIRMATION OF DI
	NM.STRT	=130		;START UP MESSAGE
				;SUB TYPES
	 NM.INI	=1		;INIT MESSAGE
	 NM.VER	=2		;VERIFY
				;FUNCTIONS FIELD
	 NM.RNT	=4		;ROUTE INTERCEPT
	 NM.LNT	=2		;LINK INTERCEPT
	 NM.RVF	=1		;REQUEST VERIFICATION MESSAGE

;MISC
	NM.NMM	=4095.		;MASK FOR MSG NUMBER FIELDS
	.SBTTL		STATE MACHINE DESCRIPTION

.REPT	0
A STATE MACHINE IS USE TO CONTROL LOGICAL LINK USAGE. SEVERAL STATES AND
STATE CONDITIONS DESCRIBED IN NSP HAVE NO MEANING HERE AND NCL IS SIMPLE
ENOUGH TO NOT COMPLICATE MATTERS TOO FAR. THEREFORE, WE CAN GET AWAY WITH
ONLY 5 STATES. ABOUT HALF OF THE STATE CONDITIONS CAN BE HANDLED WITHOUT
RESORTING TO A STATE DISPATCH TABLE.

STATE DESCRIPTIONS:
IDLE	WHILE THIS HAS A STATE CODE, A LEB IS REALLY IDLE IF IT IS NOT
	ALLOCATED. IN FACT, A COMMON WAY TO ENTER THE IDLE STATE IN THE
	CODE IS TO LEAVE LE.STT ALONE BUT CALL NSPFLB.
LI	A LEB IS IN THIS STATE AFTER PROCESSING A NCL CONNECT INIT MESSAGE
	AND BEFORE THE REPLY COMES BACK FROM NSP.
PI	THIS IS THE CONVERSE OF THE LI STATE AND IS IN EFFECT WHEN NSP HAS
	SENT A CI MESSAGE.
RUN	AS ONE MIGHT EXPECT, THIS STATE IS WHEN THE LINK IS FULLY CONNECTED
	AND DATA, LS/INT, ACK, AND DRQ MESSAGES ARE EXPECTED IN EITHER
	DIRECTION.
DSC	THIS STATE IS ENTERED WHENEVER EITHER SIDE INITIATES A LINK DISCONNECT.
	WHILE IT TAKES A BIT OF HAND WAVING, WE DON'T NEED SEPARATE STATES
	FOR BOTH NCL AND NSP INITIATED DISCONNECTS.

BELOW IS THE STATE TRANSITION TABLE USED FOR LOGICAL LINKS. STATE EVENTS ARE
THE VERTICAL AXIS, THE ACTUAL STATES ARE THE X AXIS. EACH ENTRY IS OF THE FORM
"NEW STATE"/"NSP RESPONSE"/"NCL RESPONSE" ALTHOUGH SPECIAL HANDLING NOT
INDICATED WILL TAKE PLACE IF ERRORS OCCUR.

		IDLE		LI		PI		RUN		DSC
NSP RCVD:
 CI		PI/ACK/CONN	CAN'T HAPPEN	PI/ACK/CONN	RUN//		DSC//
 CC		IDLE/DC/	RUN/ACK/CONN	IDLE/DC/	RUN//		DSC//
 DI		IDLE/DC		IDLE/DC/DISC	IDLE/DC/	DSC//DISC	DSC//DISC
 DC		IDLE//		IDLE//DISC	IDLE//		IDLE//DISC	IDLE//DISC
 ACK		IDLE/DC/	LI//		PI//		RUN//		DSC//
 NAK		IDLE/DC/	LI//		PI//		RUN//		DSC//
 DATA, LS	IDLE/DC/	LI//		PI//		RUN/ACK/DATA,DRQ DSC//

NCL RCVD:
 CONN INIT	LI/CI/		LI/CI/		CAN'T HAPPEN	IDLE/DC/DISC	IDLE//DISC
 CONN CONF	IDLE//DISC	IDLE//DISC	RUN/CC/		IDLE/DC/DISC	IDLE/DC/DISC
 DISC		IDLE//		IDLE//		DSC/DI/		DSC/DI/		IDLE/DC/

NODE OFFLINE	IDLE//		IDLE/DC/	IDLE/DC/	IDLE/DC/	IDLE/DC/
STATE TIMEOUT	IDLE//		IDLE/DC/DISC	IDLE/DC/DISC	RUN//		IDLE/DC/DISC

THERE ARE NO SUBROUTINES DEDICATED TO CONTROLLING LINK STATES, RATHER ALL
CODE CHANGING STATES WILL EITHER MOVE THE NEW STATE DIRECTLY INTO THE LEB
OR CALL NSPFLB TO FREE THE LEB WHICH IMPLIES ENTERING THE IDLE STATE.
.ENDR
	.SBTTL		INITIALIZATION

;THE ONLY THING WE HAVE TO DO FOR INITIALIZATION IS MARK ALL THE LEBS AS
;FREE AND THE EASIEST WAY TO DO THAT IS TO CLEAR OUT ALL OF THEM. SOME OF
;THE LBLK LOCATIONS WE USED ALSO HAVE TO BE ZEROED, BUT THE LBLK INITIALIZATION
;CODE WILL DO THAT.

NSPINI:	MOV	#LEBMGC,R1	;THE MAGIC LEB IS THE FIRST LEB
10$:	JSR	PC,NSPZLB	;CLEAR LEB
	ADD	#LE.SIZ,R1	;POINT TO NEXT
	CMP	R1,#LEBEND	;DONE WHOLE AREA?
	BLO	10$		;NO, DO REST
	CLR	NSPOQL		;INITIALLY NOTHING TO PROCESS
	RTS	PC
	.SBTTL		LOOP LEVEL CODE

;ROUTINE CALLED AT LOOP LEVEL TO CHECK FOR NSP ACTIVITY
;THIS EXTRACTS A LINE BLOCK FROM THE CIRCULAR QUEUE OF ACTIVE ONES AND
;ATTEMPTS TO SERVICE ALL REQUESTED FUNCTIONS. ALL FUNCTIONS ARE RELATED TO
;SENDING MESSAGES SO IF MEMORY IS NOT AVAILABLE THE LINE BLOCK IS REQUEUED
;AND NSPCHK EXITS SINCE THERE IS NO POINT IN GOING ANY FURTHER.

NSPCHK:	JSR	PC,NSPOUX	;TRANSLATE ANY NCL MESSAGES IN NSPOQL
	CMP	NS.PTR,NS.TKR	;ANYTHING IN QUEUE?
	BEQ	90$		;NO, RETURN
	CMP	NS.TKR,#<NS.QUE+NS.SIZ> ;TAKER PAST END?
	BLO	10$		;NO, SKIP NEXT INSTRUCTION
	MOV	#NS.QUE,NS.TKR	;POINT IT BACK TO BEGINNING
10$:	MOV	@NS.TKR,J	;GET LINE BLOCK ADDRESS
	ADD	#2,NS.TKR	;ADVANCE TAKER
	BIC	#LBS.NQ,LB.NSS(J) ;CLEAR QUEUED BIT
	SAVE	<SB>		;SAVE POINTER TO SCB
	MOV	LB.SCB(J),SB	;GET OUR SCB
	MOV	J,NSPLB		;MOST OF THE CODE EXPECTS THIS HERE
	MOV	LB.LEB(J),R1	;GET POINTER TO LINK TABLE
20$:	MOV	R1,NSPLEB	;SAVE LEB WE ARE LOOKING AT NOW
	BEQ	80$		;STOP WHEN LIST RUNS OUT
				;LOOP THROUGH LINKS
	MOV	SB,DNA		;FIRST SET UP WHERE WE'RE GOING
	MOV	LE.SCB(R1),SNA	;AND WHERE WE'RE COMING FROM
	MOV	#NSPCBT,R0	;GET LIST OF BITS TO LOOK AT
	;..
	;..
30$:	BIT	@R0,@R1		;NEED TO DO THIS FUNCTION?
	BEQ	50$		;NO, LOOK AT NEXT
	SAVE	<R1,R0>		;WE NEED THESE LATER
	CMP	@R0,#LES.DR	;IF THIS IS FOR SENDING NCL DRQS
	BEQ	35$		; THEN LET THAT ROUTINE ALLOCATE THE MSG
	JSR	PC,NSPBMG	;THE REST ALWAYS NEED A NSP MESSAGE
	BCS	40$		;NO MEMORY - DEFER UNTIL THERE IS SOME
35$:	MOV	@SP,R0		;RECOVER TABLE INDEX
	JSR	PC,@NSPCDS-NSPCBT(R0) ; CALL FUNCTION ROUTINE TO SEND MESSAGE
40$:	RESTORE	<R0,R1>
	BCS	70$		;CAN'T DO IT NOW, REQUE LEB, SKIP REST
	BIC	@R0,@R1		;DONE WITH THAT ONE
50$:	TST	(R0)+		;STEP TO NEXT
	BNE	30$		;MAY BE MORE TO DO, CHECK
	MOV	LE.LNK(R1),-(SP) ;STEP TO NEXT LEB
	TSTB	LE.STT(R1)	;DID OLD LEB BECOME IDLE?
	BNE	60$		;NO, KEEP GOING
	JSR	PC,NSPFLB	;IT DID, REMOVE IT FROM FUTURE CONSIDERATION
60$:	MOV	(SP)+,R1	;RECOVER NEXT LEB
	BR	20$		;LOOP

70$:	JSR	PC,NSPQ		;CAN'T ALLOCATE A MESSAGE, REDO THIS LEB LATER
80$:	RESTOR	<SB>		;DONE WITH LINE
90$:	RTS	PC

NSPCBT:	.WORD	LES.DR,LES.LN,LES.LA,LES.DN,LES.DA,LES.LS,LES.DC,0

NSPCDS:	.WORD	NSPCDR,NSPCLN,NSPCLA,NSPCDN,NSPCDA,NSPCLS,NSPCDC
;ROUTINES TO DO VARIOUS LOW LEVEL FUNCTIONS TO NSP NODES
;CALL WITH R4, R5 POINTING TO A NSP MESSAGE READY TO BE FILLED
;FUNCTIONS:
;NSPCLS - SEND A LS MESSAGE TO REQUEST A NEW INTERRUPT MESSAGE. WILL BE CALLED
;NSPCLS - CALLED AFTER THE RECEIPT OF AN INTERRUPT MESSAGE FROM NSP. THIS WILL
;	REQUEST ANOTHER, KEEPING WITH THE GOAL OF HAVING ONE OUTSTANDING REQUEST
;	AT ALL TIMES
;NSPCDC - PART OF THE LINK ERROR MECHANISM AND ANYTHING ELSE THAT NEEDS TO
;	SEND A DC TO NSP. THIS SENDS A DC AND PUTS THE LINK INTO IDLE STATE
;	WHICH WILL CAUSE NSPCHK TO FREE THE LEB
;NSPCLN - THIS SENDS A LS/INT NAK BASED ON THE HIGHEST MESSAGE NUMBER WE'VE PROCESSED.
;	SINCE NAKS ALSO ACK MESSAGES, THE ACK REQUEST BIT IS CLEARED.
;NSPCLA - SENDS A LS/INT ACK
;NSPCDN,NSPCLA - ANALOGS TO THE ABOVE BUT FOR THE DATA STREAM

	.ENABL	LSB
NSPCLS:	JSR	PC,NSPBLS	;START A LS MESSAGE UP TO SEGNUM
	MOV	#NM.IRC,R0	;INTERRUPT REQUEST COUNT
	JSR	PC,NSPPEX	;PUT LSFLAGS
	MOV	#1,R0		;REQUEST ONE MESSAGE AT A TIME
	JSR	PC,NSPPBY	;PUT FCVAL
	BR	90$		;SEND IT

NSPCDC:	CLRB	LE.STT(R1)	;MAKE LEB IDLE (NSPCHK WILL FREE)
	TST	LE.NSP(R1)	;DO WE HAVE A NSP LINK ADDRESS?
	BNE	5$		;YES, GO AHEAD AND SEND IT
	MOV	NSPPMG,R0	;DISCARD MESSAGE WE WERE BUILDING
	JSR	PC,FRECKS
	BR	95$		;AND DON'T SEND IT

5$:	MOV	#NM.DC,R0	;TO SEND A DISCONNECT CONFIRM
	JSR	PC,NSPRML	;BUILD MOST OF IT
	MOVB	LE.RSN(R1),R0	;GET REASON FOR DISCONNECT
	BR	80$		;SEND MESSAGE

NSPCLN:	MOV	LE.LIL(R1),-(SP) ;SEND THIS IN ACKNUM FIELD
	BIS	#B12,@SP	;MAKE IT A NAK
	BIC	#LES.LA,@R1	;NO NEED TO SEND ACK NOW
	BR	10$		;GET ACK CODE AND SEND

NSPCLA:	MOV	LE.LIL(R1),-(SP) ;SEND THIS IN ACKNUM FIELD
10$:	MOV	#NM.LSA,-(SP)	;MSGFLG FOR LS/INT ACK
	BR	30$		;BUILD AND TRANSMIT MESSAGE
NSPCDN:	MOV	LE.LID(R1),-(SP) ;ACK UP TO THIS FAR FOR DATA MESSAGES
	BIS	#B12,@SP	;BUT NAK ANY OTHERS
	BIC	#LES.DA,@R1	;AND DON'T BOTHER TO SEND USELESS ACK
	BR	20$		;GET MSGFLG AND TRANSMIT

NSPCDA:	MOV	LE.LID(R1),-(SP) ;ACK UP TO HERE
20$:	MOV	#NM.DTA,-(SP)	;MSGFLG FOR DATA ACK
30$:	MOV	(SP)+,R0	;GET MSGFLG FOR WHAT EVER ACK WE'RE BUILDING
	JSR	PC,NSPMDS	;BUILD UP TO SRCADDR
	MOV	(SP)+,R0	;RECOVER ACK VALUE
	BIS	#B15,R0		;MAKE IT INTO ACKNUM
80$:	JSR	PC,NSPO2B	;PUT ACKNUM (OR DC REASON)
90$:	JSR	PC,NSPSNS	;SEND IT TO NSP
95$:	CLC			;TELL NSPCHK WE DID IT
	RTS	PC
	.DSABL	LSB


;ANOTHER NSPCHK ROUTINE, THIS ONE TO SEND A FREE DATA REQUEST TO THE
;-10 FOR MESSAGES WE PASSED TO THE THE -11 THAT WILL NOT RESULT IN NEW REQUESTS
;FROM LINK SERVICES MESSAGES.

NSPCDR:	JSR	PC,NSPPTP	;REVERSE MESSAGE DIRECTION, SEND TO NCL
	MOV	#NCLDRQ,R1	;WE NEED A DATA REQUEST MESSAGE
	JSR	PC,NSPBNC	;START NCL HEADER
	BCS	10$		;NO MEMORY AVAILABLE, TRY LATER
	MOV	NSPLEB,R1	;NEED THIS AGAIN
	MOV	LE.NCL(R1),R0	;RETRIEVE DLA TO REQUEST DATA FROM
	JSR	PC,NSPPEX	;PUT DLA
	MOVB	LE.ODR(R1),R0	;RETRIEVE DATA REQUESTS THAT HAVE PILED UP
	CLRB	LE.ODR(R1)	;NO MORE
	JSR	PC,NSPPEX	;PUT COUNT
	JSR	PC,NSPSNC	;SEND REQUEST
	JSR	PC,NSPPTP	;RIGHT DIRECTION
	CLC			;MARK SUCCESS
10$:	RTS	PC		;CHEAT - NSPCHK WILL NOT NEED SNA AND DNA IF WE RETURN CS
	.SBTTL		HOOKS TO REST OF DN8X

;HERE WHEN DDCMP ENTERS THE RUNNING STATE ON A LINE MARKED AS A NSP LINE.
;THIS ROUTINE ALLOCATES A SCB TO THE LINE IN PREPARATION FOR RECEIVING THE
;NODE INIT MESSAGE

NSPRSS:	ASSERT EQ LB.SCB(J)	;I THINK THIS WILL NEVER DIE....
	JSR	PC,MAKSCB	;TRY TO ALLOCATE A SCB
	BCS	10$		;NONE AVAILABLE, TAKE DOWN LINE
	MOV	#SBF.IU!SF.NSP,@SB ;MARK SCB IN USE AS A NSP NODE
	MOV	SB,LB.SCB(J)	;SAVE ASSOCIATION BETWEEN SCB AND LBLK
	MOV	J,SB.LBA(SB)	;BOTH WAYS
	MOV	J,NSPLB		;REMEMBER THIS FOR SENDING NODE INIT
	JSR	PC,NSPXNI	;SEND A NODE INIT
	RTS	PC		;RETURN

10$:	JMP	L.DOWN		;TAKE DOWN LINE SO A RESTART WILL OCCUR


;HERE WHEN DDCMP ON A LINE TERMINATES. THE ONLY SIGNIFICANT
;ACTION TAKEN IS TO FREE LEBS IN USE AT THE TIME.

NSPDWN:	CLR	LB.NSS(J)	;CLEAR STATUS OUT
	MOV	LB.LEB(J),-(SP)	;SAVE ADDRESS OF FIRST LEB TO FREE
	CLR	LB.LEB(J)	;NO ACTIVE LEBS NOW
10$:	MOV	(SP)+,R1	;GET ADDRESS OF NEXT LEB TO FREE
	BEQ	20$		;STOP WHEN DONE
	MOV	LE.LNK(R1),-(SP) ;SAVE NEXT TO FREE
	JSR	PC,NSPZLB	;FREE CURRENT ONE
	BR	10$		;DO REST

20$:	RTS	PC		;ALL GONE
;CALLED WHENEVER A NCL NODE GOES OFFLINE. SINCE NSP HAS NO PROVISION TO
;TELL OTHER NODES THAT NODES HAVE GONE OFFLINE, WE HAVE TO INVENT DC MESSAGES
;FOR EACH OF THE LEBS THAT ARE INVOLVED. WE HAVE NO DATABASE TO DO THIS
;EASILY, SO WE HAVE TO SEARCH ALL LBLKS LOOKING FOR NSP LINES AND THEN ALL
;LEBS LOOKING FOR CONNECTIONS TO THE DOWN NODE. NOTE THAT AN ALTERNATE
;IMPLEMENTATION WOULD HAVE BEEN TO DO THIS WHEN WE RECEIVE THE NEIGHBORS
;MESSAGE FROM OUR NODE, BUT THAT HAS THE RACE CONDITION WHERE A NODE COULD
;HAVE GONE DOWN AND COME BACK UP BEFORE THE NEIGHBORS MESSAGE IS SENT.
;THIS IMPLEMENTS ROUTING INTERCEPT.

NSPDWL:	JSR	PC,NSPALL	;CALL THE REST OF US FOR ALL NSP LOGICAL LINKS
	CMP	SB,LE.SCB(R1)	;IS THIS LINK TO THE NODE THAT JUST WENT DOWN?
	BNE	10$		;NO, LEAVE IT ALONE
	MOV	#NM.NPT,R0	;SEND A NO PATH MESSAGE TO NSP SIDE
	JSR	PC,NSPQDC	;QUEUE IT
10$:	RTS	PC


;ROUTINE TO HANDLE LINK TIMING, I. E. MESSAGE RETRANSMISSION.
;THIS IS DONE ONLY FOR NUMBERED MESSAGES SINCE DOING THINGS LIKE LOOKING FOR
;REPLYS TO CONNECTS AND WHATNOT AREN'T ALL THAT EASY. WHENEVER ANY
;NUMBERED MESSAGE IS SENT, BE IT DATA OR LS/INT, THE LINK TIMER IS SET
;TO ITS FULL VALUE. ONCE A SECOND ALL NON-ZERO TIMERS
;ARE DECREMENTED AND IF THEY REACH ZERO THEN ALL MESSAGES IN THE OUTPUT BUT
;NOT ACKNOWLEDGED QUEUES ARE RETRANSMITTED.
;THE TIMER IS NEVER CLEARED. WHEN BOTH OUTPUT
;QUEUES EMPTY AND CONTROL COMES HERE, THE CALLS TO NSPRT? SEND NOTHING
;SO THE TIMERS STAY AT 0.

;THIS APPROACH DIFFERS MARKEDLY FROM WHAT THE NSP SPEC DESCRIBES
;UNDER LINK INTERCEPT BUT NO SIGNIFICANT LOSS OF PERFORMANCE SHOULD OCCUR.

NSPSEC:	JSR	PC,NSPALL	;CALL REST OF ROUTINE FOR ALL NSP LOGICAL LINKS
	TSTB	LE.TIM(R1)	;TIMIMG THIS LINK?
	BEQ	10$		;NO, IGNORE IT
	DECB	LE.TIM(R1)	;TIME IT OUT
	BNE	10$		;STILL RUNNING, IGNORE IT
	JSR	PC,NSPRTL	;EXPIRED! RETRANSMIT LS/INT MESSAGES
	JSR	PC,NSPRTD	;AND DATA MESSAGES. TRANSMIT WILL SETUP LE.TIM
10$:	RTS	PC
;CALLED FROM DDCMP AFTER IT HAS SUCCESSFULLY TRANSMITTED A NSP MESSAGE.
;UNNUMBERED MESSAGES ARE FREED IMMEDIATELY, NUMBERED ONES ARE QUEUED UNTIL
;ACKNOWLEDGED FROM THE OTHER END. THIS ROUTINE IS RESPONSIBLE FOR
;KEEPING THE MESSAGE QUEUES ORDERED, BOTH TO MAKE THE ACKNOWLEDGEMENT CODE
;SIMPLER AND TO LET RETRANSMITTED MESSAGES GO OUT IN ORDER.
;INCIDENTALLY, EVEN THOUGH MESSAGES GO OUT ON ONLY ONE LINK, IT IS NOT HARD
;TO GET MESSAGES HERE OUT OF ORDER. SUPPOSE MSG 1 IS QUEUED AND MSG 2 IS IN
;DDCMP. FOR SOME REASON (RECEIPT OF A NAK IS THE MOST LIKELY), MSG 1 GETS
;RETRANSMITTED. IF NOTHING ELSE HAPPENS FOR A WHILE, DDCMP WILL RETURN FIRST
;MSG 2 THEN MSG 1 AND THE QUEUE WILL BE DISORDERED.

NSPODN:	MOV	R0,NSPPMG	;REMEMBER WHICH MESSAGE
.IIF NE FTDCPT,JSR PC,NSPOTP	;PRINT MSG IF DEBUGGING
	TST	CN.SEG(R0)	;IS THIS AN IMPORTANT MESSAGE?
	BEQ	99$		;NO, JUST FREE IT
	MOV	SP,NSPSP	;IN CASE NSPIMS CALLS NSPIBD
	CLR	SNA		;NO IDEA WHERE IT CAME FROM YET
	MOV	J,NSPLB		;MOST OF THE CODE REFERENCES THIS
	JSR	PC,NSPIMS	;READ UP TO MSGFLG
	MOV	SNA,NSPSB	;IN CASE WE FOUND OUT WHO IS THE NCL NODE
	JSR	PC,NSPI2B	;SKIP DSTADDR
	JSR	PC,NSPI2B	;GET SRCADDR (US)
	JSR	PC,NSPSLB	;CONVERT THAT TO LEB
	CMPB	#LES.RN,LE.STT(R1) ;IS LINK RUNNING?
	BNE	30$		;NO, NO POINT IN SAVING MESSAGE
	CLRB	LE.TIM(R1)	;CLEAR TIMER IN CASE THE OTHER NSP IS LEVEL 1
	BIT	#LBS.L1,LB.NSS(J) ;IS IT LEVEL 1?
	BEQ	5$		;YES, SPEC. DOESN'T LET US TIME OUT MESSAGES
	MOVB	#NSPTIM,LE.TIM(R1) ;SET TIMER TO RETRANSMIT MESSAGE
5$:	ADD	#LE.OQD-CN.MLK,R1 ;MAKE A FAKE LIST HEADER TO QUEUED MESSAGES
	MOV	NSPPMG,R0	;HAVE TO GO BACK AND CHECK TO SEE IF MSG IS LS/INT
	TST	CN.SEG(R0)	;WHICH IS SIGN BIT HERE
	BPL	10$		;DATA MESSAGE, ALL SET
	ADD	#LE.OQL-LE.OQD,R1 ;CONVERT POINTER TO LS/INT QUEUE
10$:	MOV	CN.MLK(R1),R2	;GET NEXT MESSAGE IN QUEUE
	BEQ	20$		;REACHED END, PUT MESSAGE HERE
	MOV	CN.SEG(R0),R3	;GET THIS MESSAGE'S NUMBER
	SUB	CN.SEG(R2),R3	;SEE WHICH WAY THEY'RE SEPARATED
	ASSERT	NE		;BETTER NOT BE FOR A MESSAGE IN THE QUEUE!
	BIT	#<NM.NMM+1>/2,R3 ;BIT WILL BE ON IF WE'VE FOUND A MESSAGE
	BNE	20$		; BEYOND THE ONE R0 REFERENCES
	MOV	R2,R1		;NOT YET, POINT TO MESSAGE WE JUST CHECKED
	BR	10$		;AND SEE IF WE CAN INSERT AFTER IT

20$:	MOV	R0,CN.MLK(R1)	;PUT NEW MESSAGE AT LIST'S END
	CLR	CN.MLK(R0)	;MAKE SURE NEW MESSAGE IS END
	RTS	PC

30$:	TSTB	LE.STT(R1)	;IDLE LINK?
	BNE	40$		;NO, KEEP LEB AROUND
	JSR	PC,NSPFLB	;MESSAGE MUST BE FROM DISCONNECTED LINK!
40$:	MOV	NSPPMG,R0	;RECOVER ADDRESS OF MESSAGE
99$:	JMP	FRECKS		;FREE IT
	.SBTTL		NSP PROCESSOR

;	CALLED BY:	DDCINP IN DNDCMP
;
;	STACK LOOKS LIKE:
;		0	DDCMP RETURN ADDR
;
;	CALLED WITH:
;		J	LINE BLOCK
;		R0	ADDR OF FIRST CHUNK
;
;	DESTROYED:	R1, R2, R3, CARRY
;
;THIS ROUTINE LOOKS AT EVERY NSP MESSAGE RECEIVED AND SETS UP ALL DEFAULTS,
;PROCESSES THE COUNT AND RTHDR FIELDS IF THEY EXIST AND DISPATCHES TO THE
;PROCESSING ROUTINE TO HANDLE EACH MSGFLG TYPE.

NSPINP:
.IIF NE FTDCPT,JSR PC,NSPITP	;PRINT MESSAGE IF DEBUGGING
	MOV	R0,NSPPMG	;SAVE STARTING CHUNK OF MESSAGE
	MOV	J,NSPLB		; SAVE POINTER TO LINE BLOCK
	CLR	DNA		;SAY WE DON'T KNOW NCL ASSOCIATION YET
	CLR	NSPLEB		;INDICATE NO LEB YET (NSPIER NEEDS TO KNOW)
	MOV	LB.SCB(J),SNA	;SETUP DEFAULT SOURCE NODE
	MOV	SP,NSPSP	;SAVE SP FOR MESSAGE FORMAT ERROR RECOVERY
	JSR	PC,NSPIMS	;READ UP TO MSGFLG
	MOV	DNA,NSPSB	;SETUP NCL ASSOCIATION IF NSPIRT WAS CALLED
	MOV	R0,R1		;MSGFLG - POSITION SUBTYPE FIELD
	ASR	R0		;TO DO A WORD DISPATCH
	BIC	#^C16,R0	;THESE BITS SHOULD BE OFF ALREADY
	ASL	R1		;SHIFT TYPE FIELD FOR WORD DISPATCH
	BIC	#^C6,R1		;JUST THE TYPE
	JSR	PC,@40$(R1)	;PROCESS MESSSAGE
	RTS	PC		;DONE

40$:	.WORD	NSPIDA,NSPIAC,NSPICM,NSPIB0


NSPICM:	JMP	@10$(R0)	; DISPATCH ON TYPE


10$:	.WORD	NSPIFL		;NOPS ARE EASY, JUST DISCARD
	.WORD	NSPICI
	.WORD	NSPICC
	.WORD	NSPIDI
	.WORD	NSPIDC
	.WORD	NSPSTR
	.WORD	NSPIB0		;UNKNOWN MSG TYPES
	.WORD	NSPIB0
;ROUTINE TO READ NSP FROM BEGINNING TO MSGFLG

NSPIMS:	MOV	CN.LEN(R0),R2	;GET LENGTH
	MOV	R0,R3		;POINT TO FIRST CHUNK
	ADD	#CN.NCT,R3	;NOW MAKE THAT FIRST DATA
10$:	JSR	PC,GETEXN	;GET FIRST FIELD
	ASR	R0		;IS THIS A COUNT FIELD?
	BCC	20$		;NO, LOOK AT NEXT BIT
	NSPIBD			;WE DON'T SUPPORT UNBLOCKING

20$:	ASR	R0		;IS THIS A RTHDR?
	BCC	30$		;NO, MUST BE MSGFLG
	JSR	PC,NSPIRT	;FIGURE OUT SNA AND CLEAR DNA
	BR	10$		;NEXT FIELD

30$:	RTS	PC


;ROUTINE CALLED BY NSPIBD MACRO TO LOG ERROR AND DISCARD MESSAGE
;NSPIB0- THIS MAY BE CALLED BY ANY ROUTINE CALLED FROM NSPINP NO MATTER
; HOW MUCH DATA HAS BEEN PUT ON THE STACK. IT WILL LOG THE ERROR AND DISCARD
; THE MESSAGE. THE PREFERED CALLING MECHANISM IS VIA THE NSPIBD MACRO.
;NSPIFL- CALL (USUALLY VIA JMP) WHEN WHEN DONE WITH A RECEIVED NSP MESSAGE
; TO RETURN IT TO THE FREELIST. DO NOT CALL THIS IF THE MESSAGE HAS BEEN
; REUSED FOR A NCL TRANSLATION OR NSP REPLY UNLESS NSPPMG IS ZEROED.

NSPIB0:	TWIDDLE	(SP)+		;SAVE PC OF CALLER
	TWIDDLE
.IF NE FTDCPT
	MOV	NSPPMG,R0	;GIVE THE PRINT ROUTINES SOMETHING TO PRINT
	JSR	PC,NSPBTP	;NOTE WE DISLIKED IT
.ENDC
	MOV	NSPSP,SP	;RESTORE STACK TO NSPINP LEVEL
NSPIFL:	MOV	NSPPMG,R0	;CHANGE THIS TO POSSIBLY
	CLR	NSPPMG		;MAKE SURE WE DON'T POINT
	JMP	FRECKS		;SAVE THE LAST BAD MESSAGE
;ROUTINE TO DECODE NSP ROUTING HEADER. IF EITHER NODE ISN'T KNOWN, THE
;MESSAGE IS IGNORED. WHAT REALLY SHOULD HAPPEN IS FOR A DC MESSAGE TO BE
;GENERATED AND RETURNED TO THE SENDER BUT THAT MEANS PARSING MOST OF THE
;MESSAGE ANYWAY TO DETERMINE THE SOURCE AND DESTINATION ADDRESSES.
;IT IS A PUZZLEMENT.

	.ENABL	LSB
NSPIRT:	BIT	#NM.RFMT/4,R0	;ASCII ROUTING HEADERS?
	BEQ	20$		;BINARY - CAN'T HANDLE!
	BIT	#NM.EFMT/4,R0	;WHICH VERSION OF ROUTING FORMAT?
	BNE	20$		;ONLY 0 IS DEFINED
	MOV	#NSPDNA,R0	;POINT TO DESTINATION STRING
	JSR	PC,NSPIIM	;COPY THE IMAGE NAME INTO NSPDNA
	.WORD	SNMSIZ		;MAX OF THIS LONG
	MOV	#NSPDNA,R0	;GET ADDRESS OF DNA AGAIN
	MOV	NSPLB,R1	;ADDRESS OF LINE BLOCK
	ADD	#LB.HNM,R1	;GET ADDRESS OF "OUR" NAME
	JSR	PC,NSPCEA	;COMPARE THE STRINGS
	BCS	20$		;IF NOT FOR US, TOSS IT.
	CLR	DNA		;NO DNA SET UP YET.
	CLR	DNAOFF		; NOR OFFSET EITHER
	MOV	#NSPSNA,R0	;DO SAME WITH SOURCE NODE
	JSR	PC,NSPIR1	;FIND SOURCE NODE
	MOV	SB,SNA
	MOV	R0,SNAOFF
	RTS	PC		;DONE

NSPIR1:	MOV	R0,-(SP)	;SAVE START OF DEST. STRING
	JSR	PC,NSPIIM	;GET NAME OF DEST OR SOURCE NODE
	.WORD	SNMSIZ		;NO MORE THAN THIS MANY CHARACTERS
	MOV	#OURSCB,SB	;SEARCH SCBS LOOKING FOR NODE
10$:	MOV	@SP,R0		;GET START OF NAME FROM MESSAGE
	MOV	SB,R1		;CALCULATE ADDR OF NAME OF THIS NODE
	ADD	#SB.SNM,R1
	JSR	PC,NSPCEA	;COMPARE THE TWO EXTENSIBLE STRINGS
	BCC	30$		;A MATCH, GREAT.
	SB.ADV	10$		;TRY NEXT SCB
20$:	NSPIBD			;NO MATCH, DISCARD MESSAGE

30$:	TST	(SP)+		;NO NEED FOR THIS NOW
	CLR	R0		;R0 WILL RETURN WINDOW ADDRESS WITHIN SCB
	BIT	#SBF.SQ,@SB	;SEQUENTIAL NODE?
	BEQ	50$		;NO, ALL SET
	MOV	#SQNTAB,R1	;SEARCH SQNTAB FOR THIS NODE
40$:	CMP	SB,@R1		;THIS ONE?
	BEQ	50$		;YES, RETURN
	ADD	#SQNSIZ,R0	;STEP TO NEXT WINDOW
	TST	(R1)+		;AND NEXT SEQUENTIAL NODE
	BEQ	20$		;RAN OUT!
	BR	40$		;TRY NEXT
50$:
NSPRTS:	RTS	PC
	.EVEN
	.DSABL	LSB
	.SBTTL		NSPSTR	NSP START UP MESSAGE

;	CALLED BY:	NSPICM IN DNNSP3
;
;	STACK LOOKS LIKE:
;		0	POINTER TO FIRST CHUNK OF MESSAGE
;		2	DDCMP RETURN ADDRESS

NSPSTR:	JSR	PC,GETBYT	;GET START TYPE
	CMP	R0,#NM.INI	;ONLY ONE WE KNOW NOW
	BEQ	10$		;OKAY, HANDLE IT
	NSPIBD			;DISCARD MESSAGE
10$:	BIT	#LBS.IC,LB.NSS(J) ;ARE WE IN CONTACT ?
	BNE	30$		;YES, RESTART DDCMP TO CLEANUP EVERYTHING
	JSR	PC,GETEXN	;GET NODE ADDR
;RDH	BIC	#^C77,R0	;LIMIT TO 6 BITS (ALA REST OF OUR NET)
;RDH	BNE	20$		;STILL SOMETHING LEFT, USE IT
	MOVB	LB.VNN(J),R0	;MUST BE AN END NODE, GET USER SUPPLIED NUMBER
	ASSERT	NE		;BETTER BE THERE!
20$:	MOV	LB.SCB(J),SB	;POINT TO SCB
	MOV	R0,SB.NNM(SB)	;OK TO SAVE NNM NOW, FNDSCB WON'T SEE IT
	JSR	PC,FNDSCB	;SEE IF ANOTHER NODE BY SAME NAME
	BEQ	40$		;NO, OKAY TO LET THIS ONE IN
30$:	JSR	PC,L.DOWN	;REALLY SHOULD BE MORE CLEVER
	NSPIBD			;DISCARD MESSAGE
40$:	MOV	LB.SCB(J),SB	;RESTORE THIS
	CLRB	SB.DAT(SB)	;NODE INIT WON'T GIVE US THIS
	JSR	PC,GETBYT	;GET LENGTH OF ASCII NAME (IF ANY)
	MOV	R0,R1		;SAVE IN R1 FOR GETBYT CALLS
	BEQ	42$		;DONE IF NO STRING PRESENT
41$:	JSR	PC,GETBYT	;READ ASCII NAME BYTE FROM NSP MESSAGE
	SOB	R1,41$		;LOOP FOR REST OF THE NAME FIELD
42$:	MOV	SB,R0		;GET ADDR OF SCB FOR NSP NODE
	ADD	#SB.SNM,R0	;POINT TO NAME AREA
	MOV	J,R1		;ADDRESS OF LINE BLOCK
	ADD	#LB.VNM,R1	;OFFSET TO NAME STRING
	MOV	#SNMSIZ,-(SP)	;SIZE OF NAME STRING
44$:	MOVB	(R1)+,(R0)+	;COPY ANF NAME TO USE FOR DECNET NODE
	DEC	(SP)		;COUNT DOWN STRING
	BGT	44$		;LOOP BACK FOR THE REST OF THE NAME STRING
	JSR	PC,GETEXN	;DISCARD FUNCTIONS
	JSR	PC,GETEXN	;GET REQUESTS
	MOV	R0,(SP)		;SAVE REQUESTS TO CHECK FOR VERIFICATION
	BIT	#NM.RNT!NM.LNT,R0 ;IS OTHER END A LEVEL 1 IMPLEMENTATION?
	BNE	50$		;NO, NOTHING SPECIAL TO DO
	BIS	#LBS.L1,LB.NSS(J) ;MARK IT (ONLY USE IS TO NOT TIME OUT MSGS)
50$:	MOV	#6,R1		;DISCARD A LOT OF THE MESSAGE
60$:	JSR	PC,NSPI2B	; NAMELY MSG LENGTHS, MAX LINKS, AND VERSIONS
	SOB	R1,60$		;(WE REALLY SHOULD HANDLE MSG LENGTHS...)
	MOV	LB.SCB(J),R0	;GET ADDR OF SCB AGAIN
	ADD	#SB.SID,R0	;NOW READ IN STATION ID
	JSR	PC,NSPIIM
	.WORD	SIDSIZ		;LIMIT OF HOW MANY BYTES TO COPY
	JSR	PC,NSPIFL	;DELETE THE INPUT MSG .. WE'RE DONE WITH IT
	BIT	#NM.RVF,(SP)+	;IF VERIFICATION WASN'T REQUESTED
	BEQ	70$		; THEN DON'T SEND THE PASSWORD MSG
	JSR	PC,NSPBMG	;START A NEW NSP MESSAGE
	MOV	#NM.STR+<400*NM.VER>,R0 ;GET THE START-TYPE, VERIFY
	JSR	PC,NSPO2B	;SEND THE TWO BYTES
	JSR	R1,NSPOC7	;WRITE THE 8 CHARACTER PASSWORD
	.ASCIZ	/PISSWORD/	;...
	.EVEN
	JSR	PC,NSPSNS	;SEND THE VERIFICATION MESSAGE

70$:	MOV	#LBS.IC,LB.NSS(J);SET LINE BLOCK IN CONTACT
	MOV	LB.SCB(J),SB	;SET UP THE SCB ADDRESS
	JSR	PC,ADDSQN	;ADD SEQUENTIAL NODE
	BIS	#SF.NSP,@SB	;GOT TURNED OFF, IT DID!
	JSR	PC,ROUTE	;DO ROUTING
	JSR	PC,SNDNGH	;SEND NEIGHBORS 'BOUT US
	RTS	PC
;ROUTINE TO SEND A NSP NODE INITIALIZATION

NSPXNI:	JSR	PC,NSPBMG	;USE THAT MESSAGE TO MAKE A RETURN NODE INIT
	MOV	#NM.STRT,R0	;MESSAGE TYPE 2, START UP SUB TYPE
	JSR	PC,NSPPEX	;PUT MSGFLG
	MOV	#NM.INI,R0	;INIT START UP
	JSR	PC,NSPPEX
	MOV	NSPLB,R0	;ADDRESS OF DECNET (NSP) LINE BLOCK
	MOVB	LB.HNN(R0),R0	;"OUR" NODE NUMBER
	ASSERT	NE		;BETTER BE THERE!
	JSR	PC,NSPPEX	;TELL HIM WHO WE ARE
	JSR	PC,NSPOC4	;START AN IMAGE FIELD
	MOV	NSPLB,R1	;ADDRESS OF LINE BLOCK
	ADD	#LB.HNM,R1	;ADDRESS OF "OUR" NODE NAME
	JSR	PC,NSPOEI	;OUTPUT NODE NAME (EXTENSIBLE TO IMAGE)
	JSR	PC,NSPOC5	; AND WRITE IN THE LENGTH OF OUR NAME
	CLR	R0		;WE PROVIDE NO FUNCTIONS.
	JSR	PC,NSPPEX
	CLR	R0
	JSR	PC,NSPPEX	;AND ASK FOR NONE
	MOV	#NSPMAX,R0	;MAX NSP MESSAGE SIZE
	JSR	PC,NSPO2B	;PUT IT OUT
	JSR	PC,NSPO2B	;AND REPEAT FOR NSP MESSAGES
	MOV	#4095.,R0	;SAY WE SUPPORT THIS MANY TO DEFEAT POSSIBLE MASKING
				; AND LINK TABLE INDEXING BY THE OTHER NSP
	JSR	PC,NSPO2B	;PUT INTO MESSAGE
	MOV	#2,R2		;ONCE FOR COMM VER AND ONCE FOR ROUTE VER
10$:	MOV	#3+<400*1>,R0	;3,1
	JSR	PC,NSPO2B	;STORE FIRST 2 BYTES
	CLR	R0		;I FORGET WHAT THE THIRD BYTE IS
	JSR	PC,NSPPBY	;BUT HAVE TO STORE IT ANYWAY
	SOB	R2,10$		;BACK TO DO ROUT VER
	MOV	#OURSCB+SB.SID,R1 ;GET ADDR OF OUR NODE IDENT
	JSR	PC,NSPOIM	;COPY IT TO MESSAGE

	JSR	PC,NSPSNS	;SEND THE MESSAGE
	RTS	PC		; AND WE'RE DONE
	.SBTTL		NSPICI NSP CONNECT INITIATE MESSAGE

;ROUTINE TO TRANSLATE NSP CONNECT MESSAGES INTO NCL CONNECT MESSAGES.
;IF A CONNECT INITIATE, THERE IS A CHANCE THAT NSPSLP WILL RETURN THE MAGIC
;LEB. IF SO, WE RETURN A DC.

NSPICI:	JSR	PC,NSPI2B	;GET DESTADDR
	TST	R0		;ON AN INIT DEST MUST BE 0
	BNE	70$		;IT ISN'T, IGNORE MESSAGE
	JSR	PC,NSPI2B	;GET SRCADDR
	MOV	LB.LEB(J),R1	;SEE IF LINK ADDRESS ALREADY IN USE
10$:	BEQ	20$		;NO LEBS, CAN'T IN USE, OKAY TO ALLOCATE ONE
	CMP	R0,LE.NSP(R1)	;SAME?
	BEQ	30$		;YES, THIS SHOULD BE AN EXTRA CI
	MOV	LE.LNK(R1),R1	;POINT TO NEXT LEB
	BR	10$		;AND TRY IT

20$:	MOV	R0,-(SP)	;PROTECT SRCADDR
	JSR	PC,NSPALB	;ALLOCATE A LEB
	MOV	(SP)+,LE.NSP(R1);REMEMBER NSP ADDRESS
	BCS	50$		;CAN'T USE IT IF WE GOT THE MAGIC LEB
30$:	MOV	R1,NSPLEB	;IN CASE WE FOUND ON ON THE LIST
	MOVB	LB.CNN(J),R0	;NODE TO WHOM CI'S ARE TO BE DIRECTED
	ASSERT	NE		;BETTER HAVE SOMETHING!
	JSR	PC,FNDSCB	;SEE IF DESIGNATED HOST IS UP
	BEQ	60$		;IF NOT, WE CAN'T FOWARD THE CONNECT
	MOV	SB,NSPSB	;OTHERWISE, REMEMBER THE SCB
	MOV	SB,DNA		; AND THE DESTINATION OF THIS MSG
	MOV	NSPSB,LE.SCB(R1);REMEMBER THE NCL SIDE IN THE LEB
	MOVB	LE.STT(R1),R0	;GET STATE WE'RE IN
	JSR	PC,@40$(R0)	;AND DISPATCH ON IT.
	RTS	PC

40$:	.WORD	NSPCI0		;A NEW LINK IS BEING SETUP
	.WORD	NSPERP		;WE DON'T HAVE A NCL LINK, WE SHOULD NEVER GET HERE
	.WORD	NSPIFL		;DON'T PASS THE SECOND CONNECT. NCL DOESN'T LOSE THEM
	.WORD	NSPIFL		;ASSUME NSP TIMED OUT AFTER NCL CONNECTED
	.WORD	NSPIFL		;ASSUME SAME AFTER NCL DISCONNECTED

50$:	JSR	PC,NSPI2B	;GET SRCADDR
	MOV	R0,LE.NSP(R1)	;NEED FOR NSPRML
	JSR	PC,NSPPTP	;REVERSE MESSAGE FLOW DIRECTION SO WE CAN REPLY
	JSR	PC,NSPBM1	;USE THIS MESSAGE FOR REPLY
	MOV	#NM.DC,R0	;START A DC MESSAGE
	JSR	PC,NSPRML	;BUILD MOST OF MESSAGE
	MOV	#NM.TCN,R0	;SAY TOO MANY CONNECTS  (AT LEAST TO US!)
	JSR	PC,NSPO2B	;FINISH DC MESSAGE
	JMP	NSPSNS		;SEND IT

60$:	JSR	PC,50$		;SEND A "DC" TO UN-HANG THE MCB
	PJMP	NSPFLB		;FREE THE USLESS LEB
70$:	NSPIBD			;(ACTUALLY, PROBABLY SHOULD RETURN DC)
;HERE TO FINISH PROCESSING A CONNECT INITIATE FROM NSP
NSPCI0:	MOVB	#LES.PI,LE.STT(R1) ;ENTER "PI" STATE
	JSR	PC,NSPCX1	;GO PROCESS ALL FIELDS UP TO "USER-CTL"
	SAVE	<R0,R1>		;SAVE THE SEGSIZ.  NCL'S MML COMES LATER

;NOW TRANSLATE THE PROCESS NAMES.  FIRST THE "DPN" (A BITCH)
	JSR	PC,NSPCX6	;FIRST WRITE THE "TSK" OBJECT TYPE
	JSR	PC,NSPCX5	;START THE "NAME" OFF WITH A ZERO BYTE
	JSR	PC,NSPCX8	;COPY THE "DESTINATION" PROCESS NAME
	JSR	PC,NSPCX5	;USE ANOTHER ZERO AS PUNCTUATION
	JSR	PC,NSPCX8	;WRITE THE "SOURCE" PROCESS NAME
	JSR	PC,NSPCX5	;CLOSE THAT OFF WITH A ZERO ALSO

;NOW TRANSLATE THE USERID -> USERDATA FIELDS
	JSR	PC,GETEXN	;GET THE MENU FIELD
	MOV	R0,-(SP)	; AND PUT IT IN A RELATIVLY SAFE PLACE
	BIT	#^C3,(SP)	;MAKE SURE THAT ALL THE "RESERVED" BITS
	BNE	99$		; ARE ZERO. IF NOT, THEN TOSS THIS BAD MSG
	MOV	#3,R1		;GET THE LOOP COUNT
10$:	BIT	#1,(SP)		;SEE IF MENU SPECIFIED  USR,PISS&ACCT
	BEQ	20$		; IF NOT CONTAINED IN MSG, JUST PUNCTUATE
	JSR	PC,NSPCX3	;COPY THE IMAGE FIELD
	.WORD	16.		;FIELDS ARE 16. BYTES LONG
20$:	JSR	PC,NSPCX5	;PUNCTUATE WITH A ZERO
	SOB	R1,10$		;LOOP OVER ALL SUB-FIELDS

	BIT	#2,(SP)+	;SEE IF WE'RE SUPPOSED TO COPY USERDATA
	BEQ	30$		; IF NO USER DATA, JUST CLOSE OFF THE "DPN"
	JSR	PC,NSPCX3	;COPY THE IMAGE "USER DATA" FIELD
	.WORD	16.		;16 BYTES OF USER DATA
30$:	CLR	R0		;GET A ZERO
	JSR	PC,NSPPBY	; AND WRITE IT WITH THE EXTENSIBLE BIT OFF

;NOW DO THE "SPN" FIELD OF THE NCL MESSAGE.  (AFTER THE DPN IT'S A BREEZE)
	JSR	PC,NSPCX6	;THE OBJECT TYPE IS 11 (OBJTSK)
	JSR	R1,NSPCX4	;THE SPN IS JUST ALONG FOR THE RIDE
	.ASCIZ	/The EJW memorial FROB lives!/
	.EVEN
	BICB	#200,-1(R5)	;MAKE SURE WE CLEAR THE EXTENSIBLE BIT

	RESTORE	<R1,R0>		;GET THE SEGSIZ BACK
	JSR	PC,NSPCX2	;FILL OUT THE REST OF THE CONNECT MSG
	RTS	PC		; AND WE'RE DONE.

99$:	JSR	PC,NSPERR	;SIGNAL AN ERROR
	NSPIBD			; AND TOSS THE MESSAGE
	.SBTTL		NSPICI NSP CONNECT CONFIRM MESSAGE

;ROUTINE TO TRANSLATE CONNECT CONFIRM MESSAGES
NSPICC:	JSR	PC,NSPI2B	;GET DESTADDR
	JSR	PC,NSPSLB	;FIND LEB FOR IT
	MOVB	LE.STT(R1),R0	;GET CURRENT STATE
	JSR	PC,@10$(R0)	;CALL PROCESSOR
	RTS	PC

10$:	.WORD	NSPERP		;SHOULDN'T GET FREE CONNECT CONFIRMS
	.WORD	NSPCC0		;NSP LIKED THE CI WE PASSED ON!
	.WORD	NSPERP		;WE JUST GOT A CI FROM NSP!!??
	.WORD	NSPIFL		;ASSUME A LATE ARRIVAL
	.WORD	NSPIFL		;ASSUME A LATE ARRIVAL
;HERE TO CONTINUE PROCESSING A CONNECT CONFIRM
NSPCC0:	JSR	PC,NSPI2B	;GET THE SRCADDR
	MOV	R0,LE.NSP(R1)	;WE DIDN'T KNOW THIS BEFORE. SAVE IT
	MOVB	#LES.RN,LE.STT(R1) ;ENTER THE RUN STATE
	JSR	PC,NSPCX1	;GO READ EVERYTHING UPTO THE SEGSIZ

	SAVE	<R0,R1>		;PRESERVE SEGSIZ AND LEB ADDRESS
;DPN
	JSR	PC,NSPCX6	;WRITE THE OBJECT TYPE (OBJTSK = 11)
	JSR	R1,NSPCX4	;WRITE THE "DPN" OF THE NCL MESSSAGE
				; (IF TSKSER EVER CARES ABOUT THE "DPN"
				;  ON A CC, WE WILL HAVE TO TO THIS RIGHT)
	.ASCIZ	/Courtesy of the EJW memorial FROB./
	.EVEN
	BICB	#200,-1(R5)	;CLEAR THE LAST EXTENSIBLE BIT
;SPN
	JSR	PC,NSPCX6	;WRITE THE OBJECT TYPE (= 11)
	JSR	PC,NSPCX5	;START THE "SPN" NAME WITH A ZERO
	JSR	PC,NSPCX3	;COPY THE IMAGE "USER DATA"
	.WORD	16.		; WHICH IS AT MOST 16 BYTES LONG
	CLR	R0		;GET ANOTHER ZERO
	JSR	PC,NSPPBY	;AND CLOSE OFF THE "SPN" WITH IT.

	RESTORE	<R1,R0>		;GET THE LEB AND SEGSIZ BACK
	JSR	PC,NSPCX2	;WRITE THE REST OF THE NCL MSG AND SEND IT
	RTS	PC		;ALL DONE WITH THE CONNECT CONFIRM
	.SBTTL	NSP CONNECT SURBOUTINES

;NSPCX1	PROCESS NSP MSG UP TO SEGSIZ. WRITES NCL MSG UP TO DPN
;NSPCX2	PUTS SEGSIZ IN NCL MESSAGE AND FINISHES IT OFF
;NSPCX3	COPIES AN IMAGE FIELD FROM THE NSP MESSAGE TO THE NCL
;NSPCX4	COPIES AN INLINE ASCIZ FIELD TO THE NCL MESSAGE
;NSPCX5	WRITES AN EXTENSIBLE ZERO INTO THE NCL MESSAGE (PUNCTUATION)
;NSPCX6	WRITES A NON-EXTENSIBLE "11" INTO THE MSG (OBJTSK)
;NSPCX7	OUTPUTS AN OCTAL NUMBER TO THE NCL MESSAGE
;NSPCX8	COPIES A "PROCESS" NAME FROM THE NSP MESSAGE TO THE NCL ONE.

;ROUTINE CALLED ONLY BY THE CI & CC PROCESSORS.  THIS ROUTINE PROCESSES
; THE NSP MSG UP TO THE "DATA-CTL" FIELD.
; RETURNS R0 := SETSIZ.
	.ENABL	LSB
NSPCX1:	MOV	#NCLCON,R1	;START BUILDING AN NCL MESSAGE
	JSR	PC,NSPBNC	;BUILD HEADER
	BCS	99$		;THROW IT AWAY FOR NOW
	MOV	NSPLEB,R1	;RECOVER LEB ADDRESS
	MOV	LE.NCL(R1),R0	;SINCE NCL IS DESTINATION, THIS MUST BE DLA
	JSR	PC,NSPPEX	;PUT DLA (MAY BE ZERO)
	MOV	LE.DCP(R1),R0	;USE INTERNAL LINK FOR SOURCE
	JSR	PC,NSPPEX	;PUT SLA
	JSR	PC,GETEXN	;GET SERVICES
	BIT	#NM.MSA,R0	;REQUESTING MESSAGE ACKNOWLEDGEMENT?
	BEQ	10$		;IF SO, THEN OK
	JMP	NSPRCN		;OTHERWISE DON'T ALLOW IT
10$:	SAVE	<R0>
	BIC	#^C14,R0	;ONLY FCOPT FIELD
	ASR	R0		;MAKE INTO WORD INDEX
	BIS	NSPFCB(R0),@R1	;REMEMBER FLOW CONTROL TYPE
	RESTORE	<R0>
	BIC	#^C3,R0		;ISOLATE LTYPE FIELD
	DEC	R0		;SET Z BIT IF LOGICAL LINK TYPE
	BEQ	20$		;SEE IF THE Z BIT IS SET
	JMP	NSPRCN		;NOT LOGICAL LINK, CAN'T HANDLE
20$:	JSR	PC,GETEXN	;GET INFO AND DISCARD
	JSR	PC,NSPI2B	;GET SEGSIZE
	RTS	PC		;RETURN R0 := SEGSIZ
;ROUTINE TO FINISH OFF A NCL CONNECT MESSAGE.  CALLED WITH R0 = SEGSIZ BY
; THE CI & CC PROCESSORS AFTER THE SPN & DPN HAVE BEEN WRITTEN
NSPCX2:	MOV	R0,-(SP)	;PRESERVE SEGSIZE FOR A BIT
	JSR	PC,NSPPEX	;PUT MML
	MOV	#B2,R0		;SAY DATA IS IN IMAGE MODE
	JSR	PC,NSPPEX	;PUT DCM
	MOV	(SP)+,R0	;USE SEGSIZE IN RLN FIELD TOO
	JSR	PC,NSPPEX	;PUT RLN
	CLR	R0		;TASKS HAVE NO ATTRIBUTES
	JSR	PC,NSPPEX	;PUT DVT
	JSR	PC,NSPSNC	;SEND IT
99$:	JMP	NSPIFL		;DISCARD NSP MESSAGE
	.DSABL	LSB
;ROUTINE TO READ THE IMAGE DATA FIELDS FROM THE NSP CONNECT
; MESSAGE AND OUTPUT THEM AS PART OF THE EXTENSIBLE ASCII
; "DPN" STRING IN THE NCL CONNECT MSG.
;CALL:
;	JSR	PC,NSPCX3
;	.WORD	MAX-FIELD-LENGTH ;FLUSHES CONNECTION IF TOO LONG
NSPCX3:	MOV	@0(SP),-(SP)	;GET THE MESSAGE LENGTH
	ADD	#2,2(SP)	;SKIP OVER LENGTH UPON RETURNING
	JSR	PC,GETBYT	;GET THE LENGTH OF THE IMAGE FIELD
	CMP	R0,(SP)+	;SEE IF THE LENGTH IS REASONABLE
	BGT	30$		;IF UNREASONABLE LENGTH, LOG THE ERROR
	SAVE	<R1>		;WE RESPECT R1
	MOV	R0,R1		;PUT THE FIELD LENGTH IN A SAFER PLACE
	BEQ	20$		;IF A NULL FIELD, DON'T COPY ANYTHING
10$:	JSR	PC,GETBYT	;GET THE NEXT BYTE
	JSR	PC,NSPEXB	; AND COPY IT TO THE MESSAGE
	SOB	R1,10$		;LOOP OVER THE ENTIRE STRING
20$:	RESTORE	<R1>		;PUT R1 BACK.
	RTS	PC		; AND WE'RE DONE

30$:	JSR	PC,NSPERR	;IF BAD LENGTH, QUEUE DISCONNECTS
	NSPIBD			; AND GET OUT OF HERE VIA THE "ESCAPE"
;ROUTINE TO COPY AN INLINE ASCIZ STRING INTO THE OUTPUT STREAM
;CALL
;	JSR	R1,NSPCX4

NSPCX4:	MOVB	(R1)+,R0	;GET THE NEXT BYTE TO GO
	BEQ	10$		;IF NO MORE, CLEAN UP AND RETURN
	JSR	PC,NSPEXB	;STORE THE BYTE (WITH THE EXTENSIBLE BIT ON)
	BR	NSPCX4		; AND GO BACK FOR MORE
10$:	INC	R1		;MAKE SURE THAT R1 POINTS TO THE
	BIC	#1,R1		; NEXT WHOLE WORD ADDRESS
	RTS	R1		; AND RETURN

;ROUTINE TO PUT AN EXTENSIBLE "NULL" INTO THE OUTPUT STREAM
NSPCX5:	CLR	R0		;GET THE NULL
	PJMP	NSPEXB		; AND WRITE IT OUT.

;ROUTINE TO OUTPUT A NON-EXTENSIBLE "11" (= OBJTSK)
NSPCX6:	MOV	#11,R0		;GET THE TSK OBJECT TYPE
	PJMP	NSPPBY		; AND WRITE IT OUT

;ROUTINE TO OUTPUT AN EXTENSIBLE OCTAL NUMBER.
NSPCX7:	SAVE	<R0>		;SAVE THE NUMBER. ACTUALLY ONLY LOW 3 BITS
	ASR	R0		;ONE
	ASR	R0		; TWO
	ASR	R0		;  THREE (TO DIVIDE BY EIGHT)
	BIC	#160000,R0	;MAKE SURE CARRY DOESN'T PROPOGATE
	BEQ	10$		;IF NO MORE, WE ARE DONE
	JSR	PC,NSPCX7	; OTHERWISE RECURSE ON THE REST OF THE NUMBER
10$:	RESTORE	<R0>		;GET THE DIGIT BACK
	BIC	#^C7,R0		; AND ISOLATE THE 3 IMPORTANT BITS
	BIS	#'0,R0		;ASCII-IZE IT
	PJMP	NSPEXB		; AND OUTPUT IT AS ANOTHER EXTENSIBLE CHAR
;ROUTINE TO PROCESS THE NSP "NAME FIELD FORMATS" (PROCESS NAMES)
; THE ENTIRE FORMAT FIELD IS COPIED INTO ONE "SUBFIELD" IN THE NCL
; MESSAGE.  THE CONVENTIONS ARE.
;	FORMAT			BECOMES THE STRING
;	  0		 OCTAL-OBJECT-NUMBER
;	  1		 OCTAL-OBJECT-NUMBER.IMAGE-DESCRIPTOR
;	  2		 OCTAL-OBJECT-NUMBER.[PROG,PPN].IMAGE-DESCRIPTOR

NSPCX8:	JSR	PC,GETBYT	;GET THE FORMAT TYPE
	DEC	R0		;SET THE CONDITION CODE
	BMI	10$		;BRANCH IF FORMAT 0
	BEQ	20$		;BRANCH IF FORMAT 1
	DEC	R0		;JUST TO MAKE SURE THE FORMAT IS REASONABLE
	BEQ	30$		;BRANCH IF FORMAT 2
	JSR	PC,NSPERR	;IF UNKNOWN FORMAT, DECLARE A LINK ERROR
	NSPIBD			; AND GET THE HELL OUT OF HERE

;FORMAT 0 - OBJ
10$:	JSR	PC,GETBYT	;GET THE OBJECT TYPE
	BIC	#^C377,R0	;JUST ONE BYTE'S WORTH PLEASE.
	PJMP	NSPCX7		; AND PUT IT OUT AS AN OCTAL NUMBER

;FORMAT 1 - OBJ.DESC
20$:	JSR	PC,10$		;FIRST OUTPUT THE "OBJ" PART
	JSR	R1,NSPCX4	;NOW OUTPUT AN ASCIZ STRING
	.ASCIZ	/./		; CONSISTING OF A DOT
	.EVEN
	JSR	PC,NSPCX3	;NOW COPY THE IMAGE DATA FIELD
	.WORD	16.		; WHICH IS AT MOST 16 BYTES LONG
	RTS	PC		;RETURN WITH FIELD COMPLETE.

;FORMAT 2 - OBJ.[P,PN].DESC
30$:	JSR	PC,10$		;FIRST DO THE OBJECT PART
	JSR	R1,NSPCX4	;NOW OUTPUT THE
	.ASCIZ	/.[/		; DOT AND THE OPEN SQUARE.
	.EVEN
	JSR	PC,NSPI2B	;READ THE GRPCODE
	JSR	PC,NSPCX7	; AND WRITE THE "P" (OF PN FAME)
	JSR	R1,NSPCX4	;NOW OUTPUT THE
	.ASCIZ	/,/		; SEPARATING COMMA
	.EVEN
	JSR	PC,NSPI2B	;GET THE USRCODE
	JSR	PC,NSPCX7	; AND WRITE THE "PN"
	JSR	R1,NSPCX4	;NOW OUTPUT THE
	.ASCIZ	/]./		; CLOSE SQUARE AND THE DOT
	.EVEN
	JSR	PC,NSPCX3	;COPY THE IMAGE DESCRIPTOR DATA
	.WORD	12.		;WHICH IS AT MOST 12 WORDS LONG
	RTS	PC		;ALL DONE
;HERE TO REJECT THE CONNECT.  REASON IS "SERVICES MISMATCH"
NSPRCN:	MOVB	#NM.SER,LE.RSN(R1) ;SAY WHY WE'RE SENDING A DC
	MOV	NSPLMG,R0	;HAVE TO FREE BOTH NCL AND NSP MESSAGES
	JSR	PC,FRECKS	;DISCARD NCL
	JMP	NSPERP		;FREE NSP MESSAGE AND DISCONNECT LINK

NSPFCB:	.WORD	LES.NR		;NO FLOW CONTROL
	.WORD	0		;SEGMENT REQUEST COUNTS
	.WORD	LES.MR		;MESSAGE REQUEST COUNTS
	.WORD	0		;UNDEFINED, DON'T WORRY ABOUT IT
	.SBTTL		NSPIDI AND NSPIDC  NSP DISCONNECT MESSAGES

	.ENABL	LSB
NSPIDI:	MOV	#NSPDIX,-(SP)	;SAVE WHICH DISPATCH TABLE TO USE
	BR	10$		;JOIN COMMON CODE

NSPIDC:	MOV	#NSPDCX,-(SP)	;LIKEWISE
10$:	JSR	PC,NSPI2B	;GET DSTADDR
	JSR	PC,NSPSLB	;FIND LEB FOR LINK
	JSR	PC,NSPI2B	;GET SRCADDR FIELD
	MOV	R0,LE.NSP(R1)	;IF THIS HAPPENS IN LI STATE, THEN WE HAVE TO USE
				; THIS LINK ADDRESS WHEN WE MAKE THE DC
				; WE SEND BACK
	MOVB	LE.STT(R1),R0	;GET CURRENT STATE
	ADD	(SP)+,R0	;CONVERT TO ADDRESS OF SERVICE ROUTINE
	JMP	@(R0)+		;DO WHATEVER IS APPROPRIATE
	.DSABL	LSB

NSPDIX:	.WORD	NSPERP		;SAY WE CAN'T DO IT AND FORGET IT
	.WORD	NSPID2		;CAN'T CREATE LINK, FORWARD A DISC, REPLY WITH DC
	.WORD	NSPERP		;WE SHOULDN'T GET DI RIGHT AFTER CI!
	.WORD	NSPID1		;NSP WANTS TO BRING DOWN LINK. START IT
	.WORD	NSPID2		;BOTH ENDS SHUTTING DOWN SIMULTANEOUSLY?

NSPDCX:	.WORD	NSPFGC		;NOTHING TO DO BUT IGNORE IT
	.WORD	NSPID0		;CONVERT DC INTO DISC
	.WORD	NSPFGC		;DC AFTER CI!?
	.WORD	NSPID0		;ABORTING
	.WORD	NSPID0		;NSP DC AFTER NCL DISC
NSPID2:	MOV	#NM.CNF,R0	;RETURN NORMAL CONFIRMATION
	JSR	PC,NSPQDC	; FROM LOW LEVEL
	BR	NSPID1		;DON'T CALL NSPFLB, LOW LEVEL WILL DO SO

NSPID0:	MOV	#NSPFLB,-(SP)	;TO CLEANUP AFTER OURSELVES
	TST	LE.NCL(R1)	;IF THIS IS A CONFIRM FROM A
	BNE	NSPID1		; CONNECT REJECT, THEN NCL-DLA IS ZERO
	RTS	PC		; AND WE ARE DONE WITH THIS LINK FOREVER
NSPID1:	MOVB	#LES.DS,LE.STT(R1) ;ENTER DSC STATE
	MOV	NSPPMG,R0	;RECOVER START OF NSP MESSAGE
	MOV	#NCLDIS,R1	;TO BUILD DISCONNECT MESSAGE
	JSR	PC,NSPBN2	;USE IT FOR NCL MESSAGE
	MOV	NSPLEB,R1	;RECOVER LEB
	MOV	LE.NCL(R1),R0	;RECOVER DSTADDR
	JSR	PC,NSPPEX	;PUT DLA
	MOV	LE.DCP(R1),R0	;USE INTERNAL ADDRESS FOR SRCADDR
	JSR	PC,NSPPEX	;PUT SLA
	JSR	PC,NSPI2B	;GET REASON CODE
	MOV	#NSPRST,R1	;PREPARE TO SEARCH REASON PAIR TABLE
10$:	CMPB	R0,(R1)+	;MATCH HERE?
	BEQ	20$		;YES, USE NEXT BYTE
	TSTB	(R1)+		;AT END OF TABLE?
	BGE	10$		;YES
20$:	MOVB	@R1,R0		;USE NEXT BYTE AS REASON
	JSR	PC,NSPPEX	;PUT RSN
	JMP	NSPSNC		;SEND IT

NSPFGC: MOV	NSPPMG,R0	;GET THE CHUNK ADDRESS
	JSR	PC,FRECKS	;AND FREE IT, WE ARE DISCARDING MESSAGE
	JMP	NSPFLB		;GO FREE THE LINK BLOCK

NSPRST:	.BYTE	NM.NOE,0	;TABLE THAT MATCHES NSP REASONS WITH NCL
	.BYTE	NM.RAF,2	; ENTRIES NOT IN TABLE WILL RESULT IN "NO SUCH
	.BYTE	NM.NSD,2	; PROCESS" CODES
	.BYTE	NM.PDE,1
	.BYTE	NM.TCN,2
	.BYTE	NM.TCP,3
	.BYTE	-1,-1,4
	.EVEN
	.SBTTL		NSP ACKNOWLEDGEMENT MESSAGE PROCESSING

;ALL THIS HAS TO DO IS GET THE ACKNUM FIELD AND PASS IT TO THE RIGHT
;MESSAGE FREEING ROUTINE

NSPIAC:	BIT	#14,R0		;MAKE SURE HIGH 2 BITS OF SUBTYPE FIELD ARE 0
	BNE	20$		;THEY AREN'T, DISCARD MESSAGE
	MOV	30$(R0),-(SP)	;PUT ACTION ROUTINE ON STACK TO HANDLE MESSAGE
	JSR	PC,NSPI2B	;GET DSTADDR
	JSR	PC,NSPSLB	;SEARCH FOR NCL LINK
	JSR	PC,NSPI2B	;NSP ADDR BETTER MATCH
	CMP	R0,LE.NSP(R1)	;COMPARE
	BNE	10$
	JSR	PC,NSPI2B	;GET ACKNUM
	JSR	PC,@(SP)+	;CALL FREEING ROUTINE
	JMP	NSPIFL		;DISCARD THIS ONE TOO

10$:	TSTB	LE.STT(R1)	;IDLE?
	BNE	20$		;NO, SOMEONE ELSE WILL CLEANUP (?)
	MOV	R0,LE.NSP(R1)	;NEED THIS FOR DC
	MOV	#NM.NXL,R0	;RETURN A DC FOR THIS TYPE
	JSR	PC,NSPQDC
20$:	NSPIBD

30$:	.WORD	NSPFRD,NSPFRL
	.SBTTL		NSP NUMBERED MESSAGE PROCESSING

;CALLED TO PROCESS NSP TYPE 0 MESSAGES (DATA, INTERRUPT, LS). THE DISPATCH DONE
;IS BASED ON THE SUBTYPE BITS, INCLUDING BOM AND EOM. IF THE MESSAGE IS DATA,
;THE STATE OF THE EOM BIT IS USED TO DETERMINE WHICH OF THE DAP DATA TYPES WILL
;BE USED TO START THE MESSAGE.

NSPIDA:	MOV	30$(R0),-(SP)	;SAVE ROUTINE TO CALL TO PROCESS MESSAGE
	JSR	PC,NSPI2B	;GET DSTADDR
	JSR	PC,NSPSLB	;FIND LEB FOR IT
	CMPB	LE.STT(R1),#LES.RN ;ONLY ACCEPT WHEN RUNNING
	BNE	10$		;BUT DON'T GET TOO UPSET
	JSR	PC,NSPI2B	;GET SRCADDR
	CMP	R0,LE.NSP(R1)	;PARANOIA
	BNE	20$
	RTS	PC		;"RETURN" TO CALLER

10$:	TSTB	LE.STT(R1)	;IDLE?
	BNE	20$		;NO, OKAY TO IGNORE MESSAGE
	JSR	PC,NSPI2B	;GET SRCADDR
	MOV	R0,LE.NSP(R1)	;NEED FOR DC
	MOV	#NM.NXL,R0	;SAY NON EXISTANT LINK
	JSR	PC,NSPQDC	;SEND IT SOON
20$:	NSPIBD

30$:	.WORD	NSPDA0,NSPILS
	.WORD	NSPDA0,NSPIIN
	.WORD	NSPDA1,NSPIB0
	.WORD	NSPDA1,NSPIB0
	.ENABL	LSB
NSPDA0:	MOVB	#1,NSPDAP	;USE DATA WITHOUT EOR
	BR	10$

NSPDA1:	MOVB	#2,NSPDAP	;OR USE DATA WITH EOR
10$:	JSR	PC,NSPI2B	;GET ACKNUM
	TST	R0		;OR IS IT SEGNUM?
	BPL	20$		;IT IS
	JSR	PC,NSPFRD	;FREE ANY DATA MESSAGES IT ACKS
	JSR	PC,NSPI2B	;GET SEGNUM
20$:	MOV	#LE.LID,R1	;CHECK TO SEE IF WELL SEQUENCED
	JSR	PC,NSPISC	;LET INPUT SEQUENCE CHECKER CHECK
	BCS	30$		;NOT THIS ONE!
	MOV	#B15,R1		;TO SAY WE NEED A DATA MESSAGE
	JSR	PC,NSPICD	;COPY DATA INTO NCL MESSAGE AND SEND IT
	BCS	30$		;NO BUFFERS, NAK IT
	JSR	PC,NSPQDA	;REMIND OURSELVES TO SEND AN ACK
	BR	NSPINC		;INCREMENT HIGHEST MESSAGE NUMBER RECEIVED

30$:	JSR	PC,NSPQDN	;REQUEST A NAK EVEN THOUGH DDCMP WOULDN'T
	NSPIBD			;THROW IT AWAY
	.DSABL	LSB
;HERE IF MESSAGE IS INTERRUPT. IT WILL BE TRANSLATED INTO A NCL INTERRUPT
;MESSAGE WITH THE DAP DATA WITH EOR MESSAGE TYPE.

NSPIIN:	MOVB	#2,NSPDAP	;ALWAYS USE DATA WITH END OF RECORD
	JSR	PC,NSPI2B	;GET ACKNUM
	TST	R0		;OR IS IT SEGNUM?
	BPL	10$		;IT IS
	JSR	PC,NSPFRL	;FREE LS/INT MESSAGES MAYBE
	JSR	PC,NSPI2B	;GET SEGNUM
10$:	MOV	#LE.LIL,R1	;READY TO CHECK THIS SIDE
	JSR	PC,NSPISC	;CHECK INPUT SEQUENCING
	BCS	20$		;OUT OF ORDER, SEND NAK
	MOV	#B15!NCFINT,R1	;TO REQUEST INTERRUPT HEADER
	JSR	PC,NSPICD	;COPY DATA AND SEND TO NCL
	BCS	20$		;HAVE TO NAK IT
	JSR	PC,NSPQLA	;TO SEND LS/INT ACK SOMEDAY
	JSR	PC,NSPQLS	;ALSO SEND LS TO REQUEST ANOTHER INTERRUPT MESSAGE
	BR	NSPINC		;INCREMENT RECEIVED MESSAGE NUMBER AND DISCARD MSG

20$:	JSR	PC,NSPQLN	;REQUEST A NAK
	NSPIBD			;CALL IT A BAD MESSAGE SO SOMEONE SEES IT
;ROUTINE TO PROCESS THE NSP LS MESSAGE. IF IT ASKS FOR SOME DATA MESSAGES,
;IT WILL TRANSLATE INTO A DATA REQUEST. PRESENTLY REQUESTS FOR INTERRUPT MESSAGES
;ARE IGNORED, IF NCL INTERRUPT MESSAGES ARRIVE, THEY WILL ALWAYS BE PASSED TO
;NSP.

NSPILS:	JSR	PC,NSPI2B	;GET ACKNUM
	TST	R0		;SIGH, DO IT ALL AGAIN
	BPL	10$		;SEGNUM
	JSR	PC,NSPFRL	;FREE LS/INT MESSAGES
	JSR	PC,NSPI2B	;GET SEGNUM
10$:	MOV	#LE.LIL,R1	;TO CHECK LS/INT SEQUENCING
	JSR	PC,NSPISC	;CHECK IT
	BCS	30$		;OUT OF ORDER
	JSR	PC,GETEXN	;GET LSFLAGS
	BIT	#NM.MAK!NM.STP,R0 ;DISALLOW MESSAGE ACK OR STOPPING DATA FLOW
	BNE	40$		;CALL IT A LINK ERROR IF IT HAPPENS
	BIT	#NM.IRC,R0	;REQUESTING INTERRUPT MESSAGE?
	BNE	20$		;ALWAYS ALLOW THOSE
	JSR	PC,GETBYT	;GET FCVAL
	TST	R0
	ASSERT	PL		;CAN'T HANDLE NEGATIVE YET
	BEQ	20$		;AND IGNORE NO CHANGE
	MOV	R0,-(SP)	;SAVE VALUE
	MOV	#NCLDRQ,R1	;ASK FOR DATA REQUEST HEADER
	MOV	NSPPMG,R0	;REUSE NSP MESSAGE FOR NCL
	JSR	PC,NSPBN2	;BUILD HEADER
	MOV	NSPLEB,R3	;OKAY TO USE R3 HERE
	MOV	LE.NCL(R3),R0	;GET DEST LINK ADDR
	JSR	PC,NSPPEX	;PUT DLA
	MOV	(SP)+,R0	;RECOVER DATA REQUEST COUNT
	JSR	PC,NSPPEX	;PUT DRQ
	JSR	PC,NSPSNC	;SEND IT TO NCL
	CLR	NSPPMG		;THIS PREVENTS NSPINC FROM FREEING REUSED MESSAGE
20$:	JSR	PC,NSPQLA	;REQUEST TO SEND LS/INT ACK SOMETIME
	BR	NSPINC		;INCREMENT RECEIVED MESSAGE NUMBER

30$:	JSR	PC,NSPQLN	;HAVE TO NAK IT
	NSPIBD			;NOTE WE COULDN'T PROCESS IT

40$:	JSR	PC,NSPERR	;DECLARE A LINK ERROR
	NSPIBD
	.SBTTL		INPUT SEQUENCING

;ROUTINE TO ENFORCE MESSAGE SEQUENCING. EVEN THOUGH WE TALK ONLY TO END
;NODES, WE STILL HAVE TO DO MESSAGE SEQUENCING BECAUSE THERE ARE CASES (USUALLY
;NO MEMORY) WHERE WE HAVE TO DISCARD A MESSAGE AND SEND A NAK TO TRY IT AGAIN
;LATER. THIS ROUTINE ENFORCES MESSAGE ORDERING AND IS DESIGNED FOR AN
;ENVIRONMENT WHERE OUT OF ORDER MESSAGES ARE DISCARDED INSTEAD OF QUEUED.
;(AFTER ALL, WE COULDN'T PROCESS THE PREVIOUS MESSAGE OR SO BECAUSE WE WERE
;OUT OF MEMEORY). THIS ROUTINE NEITHER DISPOSES MESSAGES NOR INCREMENTS
;SEGMENT COUNTERS, SEE NSPINC FOR THAT.

;CALL:
;	R0/ SEGNUM FIELD
;	R1/ #LE.LID OR #LE.LIL (LAST INPUT MESSAGE PROCESSED)
;	JSR	PC,NSPISC
;	CARRY SET IF OUT OF ORDER

NSPISC:	ADD	NSPLEB,R1	;MAKE ADDRESS OF SEGMENT COUNTER
	MOV	R1,NSPLIX	;REMEMBER FOR NSPINC
	MOV	@R1,R1		;GET LAST MESSAGE PROCESSED
	INC	R1		;FIGURE WHAT THIS SHOULD BE
	SUB	R0,R1		;IF RIGHT, THIS WILL BE ZERO
	BIT	#NM.NMM,R1	;WELL, THESE BITS WILL BE
	BEQ	10$		;THEY ARE, RETURN CARRY CLEAR
	SEC			;OUT OF ORDER, RETURN CARRY SET
10$:	RTS	PC


;HERE WHEN INPUT MESSAGE HAS BEEN SUCCESSFULLY PROCESSED. THIS WILL FINALLY
;INCREMENT THE MESSAGE COUNTER TO MAKE US LOOK FOR THE NEXT AND JUMP OFF
;TO NSPIFL TO DISCARD THE MESSAGE. (IF THE MESSAGE SPACE HAS BEEN REUSED,
;NSPPMG MAY BE ZEROED AND EVERYTHING WILL WORK, I. E. NOTHING WILL BE FREED.)
;CALL:
;	NSPPMG/ MESSAGE TO FREE OR ZERO IF NONE
;	JSR	PC,NSPISC	(TO SET NSPLIX)
;	<PROCESS MESSAGE>
;	JSR	PC,NSPINC	(IF MESSAGE SUCCESSFULLY PROCESSED)

NSPINC:	INC	@NSPLIX		;INCREMENT MESSAGE COUNTER
	BIC	#^CNM.NMM,@NSPLIX ;RESTRICT TO 12 BITS OR WHATEVER
	JMP	NSPIFL		;DISCARD MESSAGE
	.SBTTL		NSP TO NCL DATA COPY
;ROUTINE CALLED BY NSP DATA AND INTERRUPT PROCESSORS TO COPY DATA PORTION
;OF MESSAGES. THE RESULT WILL BE SENT TO NCL FOR ROUTING TO THE DESTINATION.
;CARRY IS SET ON RETURN IF SPACE FOR THE NCL MESSAGE IS NOT AVAILABLE

NSPICD:	JSR	PC,NSPBNC	;START A NCL MESSAGE
	BCS	99$		;NAK IT SOMETIME
	MOV	NSPLEB,R1	;GET LEB AGAIN
	MOV	LE.NCL(R1),R0	;RECOVER DSTADDR
	JSR	PC,NSPPEX	;PUT DLA
	BIT	#LES.DV,@R1	;HAVE TO ADD DAP?
	BNE	20$		;NO, SHOULD ALREADY BE ON
	MOV	R2,R0		;GET LENGTH
	INC	R0		;AUGMENT FOR TYPE
	JSR	PC,NSPPEX	;STORE COUNT
	MOVB	NSPDAP,R0	;GET DAP MSG TYPE
	JSR	PC,NSPPBY	;STORE TYPE
20$:	TST	R2		;ANY MORE TO COPY?
	BEQ	30$		;NO, FINISH UP
	JSR	PC,GETBYT	;GET NEXT BYTE
	JSR	PC,NSPPBY	;PUT INTO MESSAGE
	BR	20$		;KEEP LOOKING FOR DATA
30$:	JSR	PC,NSPSDL	;SEND IT TO NCL
	CLC			;TELL CALLER IT WORKED
99$:	RTS	PC
	.SBTTL		NCL MESSAGE PROCESSOR

;CALLED BY DDCMP WHEN NCL GIVES IT A MESSAGE TO SEND TO A NSP
;NODE. THIS ROUTINE PERFORMS DIFFERENT ACTIONS DEPENDING ON THE SORT
;OF MESSAGE RECEIVED. ONLY NUMBERED MESSAGES REACH HERE, UNNUMBERED
;ONES ARE PROCESSED BY THE SEQUENTIAL CODE IN NCL.

;NSPOUT QUEUES MESSAGES AS IT RECEIVES THEM. IT CANNOT PROCESS THEM IMMEDIATELY
;BECAUSE SEVERAL NCL VARIABLES MAY BE CHANGED (NOTABLY SNA, SNAOFF, ETC.)
;THE LIST USED HAS BOTH A LIST HEADER THAT POINTS TO THE FIRST MESSAGE
;QUEUED AND A CELL THAT POINTS TO THE LAST MESSAGE QUEUED SO MESSAGES MAY BE
;ADDED TO THE LIST WITHOUT SEARCHING IT.

;NSPOUX IS CALLED BY NSPCHK TO PROCESS ANY QUEUED NCL MESSAGES. IT SETS UP ALL
;VARIBALES FROM THE DATA IN THE NCL HEADER THEN CALLS NSPOU1 TO GET AND
;DISPATCH ON THE DLA FIELD. MESSAGE PROCESSING USUALLY REQUIRES AT LEAST A
;CHUNK OF MEMORY TO BUILD THE TRANSLATION OR REPLY. IF IT ISN'T AVAILABLE,
;THE MESSAGE IS REQUEUED IN HOPES OF MEMORY FREEING UP SOON.

;MESSAGE TYPE	TRANSLATE	REPLY		MODIFY DATA BASE
;DATA		*
;CONNECT	*				*
;DISCONNECT	*				*
;NEIGHBORS
;REQ CONFIG			*
;CONFIG
;DATA REQ	*
;STATION CTRL			*

;CALL:
;	R0/ ADDRESS OF NCL MESSAGE
;	JSR	PC,NSPOUT
;	...			;MESSAGE QUEUED FOR NSPOUX
;	JSR	PC,NSPOUX	;PROCESS QUEUED MESSAGES

NSPOUT:
.IF NE DEBUG
	BITB	#7,CN.NCT(R0)	;MAKE SURE WE WEREN'T PASSED AN UNNUMBERED CTRL MSG
	ASSERT	EQ		;NCL SHOULD FILTER THEM ALL
.ENDC
	CLR	CN.MLK(R0)	;THIS MESSAGE WILL BE LAST ON QUEUE
	TST	NSPOQL		;ANYTHING THERE NOW?
	BNE	10$		;YES, ADD TO END
	MOV	R0,NSPOQL	;NO, MAKE THIS MESSAGE BE THE QUEUE
	BR	20$		;SET POINTER TO LAST MESSAGE

10$:	MOV	NSPOQL+2,R1	;GET ADDRESS OF LAST MESSAGE
	MOV	R0,CN.MLK(R1)	;PUT THIS ON END OF QUEUE
20$:	MOV	R0,NSPOQL+2	;AND REMEMBER IT'S END OF QUEUE
	SEC			;NEVER PASS ANYTHING (CODED THIS WAY FOR DCP1 COMPATIBILITY)
	RTS	PC		;GO AWAY, RETURN BELOW FROM NSPCHK
	.ENABL	LSB
NSPOUX:	MOV	NSPOQL,R0	;GET ADDRESS OF FIRST MESSAGE
	BEQ	99$		;NOTHING THERE, RETURN
	MOV	CN.MLK(R0),NSPOQL ;DELINK IT (NOTE - WE MAY PUT IT BACK ON AT NSPOD0)
	MOV	R0,NSPLMG	;THIS WILL BE THE MESSAGE OF THE HOUR
	CLR	NSPPMG		;WE'LL SEND ANYTHING THIS BECOMES
.IIF NE FTDCPT,JSR PC,NSPITL	;PRINT IT
	MOV	SP,NSPSP	;IN CASE NSPOBD IS CALLED
	MOV	CN.LEN(R0),R2	;SETUP LENGTH AND ADDRESS OF
	MOV	R0,R3		; MSG FOR FOR GETBYT AND FRIENDS
	ADD	#CN.NCT,R3
	JSR	PC,GETBYT	;SKIP NCT
	JSR	PC,GETBYT	;GET DNA
	JSR	PC,FNDSCB	;FIND ITS SCB
	BEQ	10$		;WENT OFFLINE? DISCARD MSG
	MOV	SB,DNA		;REMEMBER WHERE IT'S HEADING
	MOV	SB.LBA(SB),NSPLB ;REMEMBER THE PATH TO IT (THERE SHOULD ONLY BE THIS ONE)
	JSR	PC,GETBYT	;GET SNA
	JSR	PC,FNDSCB	;FIND ITS SCB
	BEQ	10$		;SIGH
	MOV	SB,SNA		;REMEMBER SOURCE
	MOV	SB,NSPSB	;AND NCL NODE ASSOCIATED WITH MESSAGE
	JSR	PC,NSPI2B	;SKIP NCA AND NCN
.IF LT DEBUG			;IF WE WANT TO DIE ON EVERYTHING,
	MOV	NSPLMG,R0	;GET START OF MESSAGE AGAIN
	CMP	R2,CN.CNT(R0)	;WE SHOULD MATCH THESE NOW
	ASSERT	EQ		;COMPLAIN IF NOT
.ENDC
	JSR	PC,NSPOU1	;PROCESS CONTROL OR DATA PORTION OF MESSAGE
10$:	MOV	NSPLMG,R0	;GET ADDRESS OF FIRST CHUNK
	JSR	PC,NCLODN	;RETURN TO NCL TO FREE IT
	BR	NSPOUX		;DO ANOTHER
;HERE VIA NSPOBD MACRO TO ABORT PROCESSING OF THE CURRENT NCL MESSAGE WHEN
;PROTOCOL ERORS ARE DETECTED. (NOTE THIS MAY REFLECT MERELY ONE OF THE UNAVOIDABLE
;RACE CONDITIONS INHERENT IN NETWORKS SO THE TWIDDLE DATA SHOULD BE TAKEN
;WITH A GRAIN OF SALT. THE STACK IS RESET TO THE NSPOUX LEVEL, BOTH NCL AND NSP
;MESSAGES ARE DISCARDED AND THE NEXT MESSAGE ON THE QUEUE IS PROCESSED.

NSPOB0:	TWIDDLE	(SP)+		;REMEMBER WHO CALLED US
	TWIDDLE			;AND HOW OFTEN
.IF NE FTDCPT
	MOV	NSPLMG,R0	;FETCH THE IMCOMING MESSAGE
	JSR PC,NSPBTP		;NOTE IF ANYONE'S INTERESTED
.ENDC
	MOV	NSPSP,SP	;RESET STACK TO WHERE WE WERE
	MOV	NSPPMG,R0	;DISCARD NSP MESSAGE WE WERE TRYING TO BUILD
	JSR	PC,FRECKS	;SINCE THERE'S NO POINT IN SENDING IT
	BR	10$		;GO DISCARD THE NCL MESSAGE


;HERE VIA THE NSPODF MACRO TO DEFER PROCESSING OF THE CURRENT NCL MESSAGE IF
;MEMORY IS NOT AVAILABLE. THIS RESETS THE STACK, REQUES THE MESSAGE ON THE BEGINNING
;OF THE NSPOQL, AND RETURNS TO NSPOUX'S CALLER SINCE THERE IS NO POINT IN
;CONTINUING NCL PROCESSING (I.E. ANY OTHER MESSAGES WILL MEET THE SAME FATE).

NSPOD0:	MOV	NSPSP,SP	;RESET STACK
.IIF NE DEBUG,ASSERT EQ NSPPMG	;WE SHOULDN'T HAVE BEEN ABLE TO SET THIS
	MOV	NSPLMG,R0	;TAKE THE NCL MESSAGE WE WERE WORKING ON
	MOV	NSPOQL,CN.MLK(R0) ; AND QUEUE IT BACK ON TO START OF NCL MESSAGES
	MOV	R0,NSPOQL
99$:	RTS	PC		;NO POINT IN TRYING TO CONTINUE NCL PROCESSING
	.DSABL	LSB


;ROUTINE TO READ DLA AND DISPATCH TO EITHER DATA OR CONTROL MESSAGE PROCESSOR

NSPOU1:	JSR	PC,GETEXN	;GET DLA
	TST	R0		;NONZERO IF DATA
	BEQ	NSPOCM		;IT'S ZERO, A CONTROL MESSAGE
	.SBTTL		DATA MESSAGES

;ROUTINE TO HANDLE TRANSLATION OF DATA MESSAGE. THIS WILL RESULT IN
;GENERATING A NSP DATA OR INTERRUPT MESSAGE.
;IF IT MAKES A DATA MESSAGE, THE BOM AND EOM BITS WILL BE DETERMINED BY
;THE DAP DATA TYPE AND PAST HISTORY OF THE MESSAGES.

NSPODM:	JSR	PC,NSPSLB	;FIND LEB
	CMPB	LE.STT(R1),#LES.RN ;ONLY ALLOW IN RUN STATE
	BEQ	10$		;BUT RACES LET THIS HAPPEN OFTEN (DSC STATE)
	TSTB	LE.STT(R1)	;IDLE?
	BNE	5$		;NO, SOMEONE ELSE IS INVOLVED
	JSR	PC,NSPFLB	;NO ONE NEEDS THIS NOW
5$:	NSPOBD			;THROW IT AWAY IF IT HAPPENS

10$:	JSR	PC,NSPBMG	;START NSP MESSAGE
	BCC	15$		;GOT A CHUNK, CONTINUE PROCESSING
	NSPODF			;FAILED, DEFER FOR A WHILE

15$:	MOV	NSPLMG,R0	;LOOK BACK AT START OF NCL MESSAGE
	BITB	#NCFINT,CN.NCT(R0) ; TO SEE IF IT IS AN INTERRUPT MESSAGE
	BEQ	20$		;NO, ORDINARY DATA MESSAGE
	JSR	PC,NSPBID	;START BUILDING A INT. DATA MESSAGE
	BR	70$		;REJOIN COMMON CODE

20$:	SAVE	<R2,R3>		;HAVE TO SNEAK A PEAK AT DAP TYPE
	CLR	-(SP)		;SAVE A CELL TO MAKE MSGFLG IN
	BIT	#LES.MD,@R1	;ARE WE IN THE MIDDLE OF A MESSAGE?
	BNE	30$		;YES, CAN'T SET BOM
	MOV	#NM.BOM,@SP	;NOTE THIS IS BEGINNING OF MESSAGE
30$:	JSR	PC,GETEXN	;SKIP COUNT
	JSR	PC,GETBYT	;GET TYPE
	CMP	R0,#1		;IS THIS DATA WITH-OUT END-OF-RECORD?
	BEQ	40$		;IF NOT END-OF-RECORD, DON'T SET EOM BITS

;*** NOTE ***
;    BECAUSE NO MONITORS BEFORE THE 700 SERIES EVER SEND DATA
;WITH END-OF-RECORD, THE ABOVE "BEQ" MUST BE NO-OP'ED TO WORK
;PROPERLY WITH 6.03A AND BEFORE.

	BIC	#LES.MD,@R1	;WE'RE AT END OF MESSAGE, NO LONGER IN MIDDLE
	BIS	#NM.EOM,@SP	;NOTE IN MSGFLG
	BIT	#LES.NR,@R1	;DOES NSP REQUEST MESSAGES?
	BEQ	60$		;YES, DON'T HAVE TO SEND A FREE DATA REQUEST
	BR	50$		;REQUEST LOW LEVEL SEND A DATA REQUEST

40$:	BIS	#LES.MD,@R1	;NOTE WE'RE SOMEWHERE IN THE MIDDLE NOW
	BIT	#LES.NR!LES.MR,@R1 ;IF NSP IS REQUESTING MESSAGES OR NOTHING
	BEQ	60$
50$:	JSR	PC,NSPQDR	;THEN WE HAVE TO TELL NCL TO KEEP SENDING
60$:	MOV	(SP)+,R0	;RECOVER MSGFLG
	RESTORE	<R3,R2>		;REREAD DATA
	;..
	;..
	JSR	PC,NSPBDT	;BUILD START OF DATA MESSAGE
70$:	CLR	-(SP)		;CLEAR COUNTER (FOR STRIPPING)
80$:	TST	R2		;ANY MORE DATA IN NCL MESSAGE?
	BEQ	100$		;NO, BRANCH WHEN DONE
	BIT	#LES.DV,@R1	;ARE WE STRIPPING COUNT AND TYPE FIELDS?
	BNE	90$		;NO, CONTINUE
	TST	@SP		;CHECK COUNT OF SUB-MESSAGE
	BNE	90$		;IF MORE TO GO, BRANCH
	JSR	PC,GETEXN	;GET NEXT COUNT FIELD
	DEC	R0		;SUBTRACT ONE FOR TYPE BYTE
	MOV	R0,@SP		;SAVE COUNT
	JSR	PC,GETBYT	;THROW AWAY TYPE
	BR	80$		;AND TRY AGAIN (IN CASE NO DATA)

90$:				;HERE FOR REAL DATA BYTE
	JSR	PC,GETBYT	;GET DATA BYTE
	DEC	@SP		;DECREMENT SUB-MESSAGE BYTE COUNTER
	JSR	PC,NSPPBY	;STORE BYTE IN NSP MESSAGE
	BR	80$		;CONTINUE LOOPING ON DATA

100$:	TST	(SP)+		;DISCARD COUNT LOCATION
	BR	NSPONS		;SEND IT
	.SBTTL		NCL CONTROL MESSAGE PROCESSING

;CONTROL MESSAGE PROCESSING AND DDCMP RETURN.
;NSPOCM CONTROLS THE CONTROL MESSAGE DISPATCH AND FALLS INTO
;NSPONS, THE CODE WHICH RETURNS THE TRANSLATED MESSAGE TO DDCMP.
;THE NCL MESSAGE WE STARTED WITH IS PASSED TO NCLODN WHERE IT GETS FREED
;PRETTY QUICKLY.
;ONE THING WE SHOULD DO BUT DON'T IS PROCESS MORE THAN ONE CONTROL MESSAGE.

NSPOCM:	JSR	PC,GETEXN	;GET LENGTH OF CONTROL PORTION
	JSR	PC,GETBYT	;GET TYPE
	CMP	R0,#NSPMNC	;IN RANGE?
	BLO	10$		;IN RANGE, KEEP AT IT
	NSPOBD			;SOMEONE CHANGED THE PROTOCOL!

10$:	ASL	R0		;WORD INDEXING
	JSR	PC,@NSPOCD(R0)	;CALL APPROPRIATE ROUTINE
NSPONS:	MOV	NSPPMG,R0	;GET ADDRESS OF FIRST CHUNK OF NSP MESSAGE
	BEQ	10$		;NO MESSAGE TO SEND
	JSR	PC,NSPSNS	;FINISH OFF MESSAGE
10$:	RTS	PC		;RETURN TO DDCMP TO SEND MSG

NSPOCD:	.WORD	NSPOB0		;ILLEGAL
	.WORD	NSPOCN		;CONNECT - SEND CI OR CC
	.WORD	NSPODC		;DISCONNECT - SEND DI OR DC
	.WORD	NSPRTS		;NEIGHBORS - IGNORE
	.WORD	NSPORC		;REQ CONFIG - RETURN CONFIG
	.WORD	NSPRTS		;CONFIG - IGNORE (WE DIDN'T REQUEST IT)
	.WORD	NSPODR		;DATA REQUEST - SEND LS
	.WORD	NSPOST		;STATION CTRL - RETURN STC REJECTION
NSPMNC=<.-NSPOCD>/2
;ROUTINES TO HANDLE STATION CONTROL AND REQ CONFIG MESSAGES. BOTH OF THESE
;RESULT IN NO NSP MESSAGE TO PASS ON BUT DO RESULT IN REPLIES TO THE SOURCE.
;THESE ROUTINES SWITCH THE DIRECTION OF MESSAGE FLOW, PROTECT NSPLMG (THE ADDRESS
;OF THE ORIGINAL MESSAGE) BUILD A NEW NCL MESSAGE AND SEND IT. ALL STATION
;CONTROL MESSAGES ARE REJECTED, ALL REQ CONFIG MESSAGES RESULT IN A CONFIG MESSAGE
;BASED ON DATA ORGINALLY PROVIDED IN THE NSP MACRO.

	.ENABL	LSB
NSPOST:	JSR	PC,NSPLTL	;SWITCH DIRECTIONS
	SAVE	<NSPLMG>	;HAVE TO SAVE THIS WHILE WE REPLY
	MOV	#NCLSTC,R1	;MAKE A STATION CTRL MESSAGE
	JSR	PC,NSPBNC	;WELL, TRY TO AT LEAST
	BCS	30$		;NO SPACE, IGNORE IT
	JSR	PC,GETBYT	;GET LINE NUMBER
	JSR	PC,NSPPBY	;PUT LINE NUMBER
	MOV	#13,R0		;REJECT STATION CTRL MSG TYPE
	JSR	PC,NSPPBY	;PUT CODE
	BR	20$		;SEND IT


NSPORC:	JSR	PC,NSPLTL	;CHANGE A NCL MESSAGE INTO A REPLY
	MOV	#NCLCNF,R1	;TO MAKE A CONFIG MESSAGE
	SAVE	<NSPLMG>	;JUGGLE TWO NCL MESSAGES
	JSR	PC,NSPBNC	;WRITE NEW HEADER
	BCS	30$		;FORGET IT IF NO ROOM
	MOV	NSPLB,R2	;NSPBMG WIPED OUT J ON US
	ADD	#LB.CNF,R2	;POINT TO CONFIG DATA
	MOV	#NSPTCF/2,R1	;NUMBER OF CONFIG PAIRS
10$:	MOVB	(R2)+,R0	;GET OBJTYPE
	BLT	20$		;NEGATIVE MEANS WE'VE RUN OUT
	JSR	PC,NSPPBY	;PUT INTO NCL MESSAGE
	MOVB	(R2)+,R0	;GET COUNT
	JSR	PC,NSPPBY
	CLR	R0		;ZERO PID
	JSR	PC,NSPPBY
	SOB	R1,10$		;DO REST
20$:	JSR	PC,NSPSNC	;SEND CONFIG MESSAGE
30$:	RESTORE	<NSPLMG>	;RESTORE MESSAGE TO FREE
	RTS	PC
	.DSABL	LSB
;HERE TO PROCESS A NCL CONNECT MESSAGE.
;FIRST DETERMINE IF IT IS A CONNECT INIT, OR CONFIRM, THEN SEND THE
;PROPER NSP MESSAGE.  ALWAYS REQUEST SEGMENT DATA REQUESTS SINCE THAT
;IS DIRECTLY COMPATIBLE WITH NCL'S WAY OF DOING BUSINESS.  IF THERE
;ARE NO FREE LEBS, A DISCONNECT IS SENT TO NCL.
NSPOCN:	JSR	PC,NSPBMG	;START THE NCL MSG SO WE CAN COPY AS WE GO
	BCS	30$		;IF NO CORE, DELAY PROCESSING FOR A BIT
	JSR	PC,NSPORT	;WRITE THE NSP ROUTINE HEADER
	JSR	PC,GETEXN	;GET THE LINK ADDRESS
	TST	R0		; AND IF IT'S NON-ZERO,
	BNE	10$		; THEN GO PROCESS THIS CONNECT-CONFIRM

;HERE WHEN MESSAGE IS A CONNECT INIT
	JSR	PC,GETEXN	;GET THE "SLA"
	MOV	R0,-(SP)	; AND PROTECT IT FOR A BIT
	JSR	PC,NSPALB	;ALLOCATE A LEB FOR THIS LINK, AND COPY IN
	MOV	(SP)+,LE.NCL(R1); THE SLA INCASE WE NEED TO ABORT (DISCONNECT)
	BCS	20$		;IF NO FREE LEBS USE MAGIC TO REJECT THE CNCT

	MOVB	#LES.LI,LE.STT(R1) ;ENTER "LI" STATE
	MOV	#NM.CI,R0	;GET THE "CONNECT INITIATE" MESSAGE FLAG
	JSR	PC,NSPOC1	;WRITE MSGFLG -> SEGSIZ
	JSR	PC,NSPOC2	;WRITE THE "DATA-CTRL" FIELD
	RTS	PC		;ALL DONE, SEND NSP MSG, FREE NCL.
;HERE IF WE ARE PROCESSING A CONNECT CONFIRM
10$:	JSR	PC,NSPSLB	;FIND THIS GUY'S LINE BLOCK
	JSR	PC,GETEXN	;GET THE "SLA"
	MOV	R0,LE.NCL(R1)	; AND REMEMBER IT
	CMPB	#LES.PI,LE.STT(R1) ;IF WE'RE NOT IN "PI" STATE,
	BNE	20$		; THEN SOMETHING'S WRONG.  ABORT THE CONNECTION
	MOVB	#LES.RN,LE.STT(R1) ;GO TO THE "RUN" STATE
	MOV	#NM.CC,R0	;GET THE CONNECT CONFIRM MESSAGE TYPE
	JSR	PC,NSPOC1	;WRITE THE FIELDS MSGFLG -> SEGSIZ
	JSR	PC,NSPOC3	;COPY THE "USER DATA" FROM THE "SPN"
	RTS	PC		;ALL DONE.

;HERE TO JUST ABORT THE CONNECTION
20$:	JSR	PC,NSPLTL	;REVERST THE DIRECTION TO SEND "REJECT"
	JMP	NSPERR		;QUEUE UP THE NECESSARY DISCONNECTS

;HERE TO DELAY PROCESSING UNTIL MEMORY BECOMES AVAILABLE
30$:	NSPODF			;DEFER OUTPUT PROCESSING
	.SBTTL	NCL CONNECT MESSAGE SUBROUTINES.

;NSPOC1	WRITES THE NSP MESSAGE FROM "MSGFLAG" -> "SIGSIZE"
;NSPOC2	TRANSLATES A CONNECT INITIATE'S "HAIRY" DPN
;NSPOC3	TRANSLATES A CONNECT CONFIRMS "SPN" INTO IMAGE "USRDATA"
;NSPOC4	STARTS AN NSP IMAGE DATA FIELD (WRITES THE "COUNT" BYTE)
;NSPOC5	CLOSES AN NSP IMAGE DATA FIELD (FIXED UP THE "COUNT" BYTE)
;NSPOC6	OUTPUTS AN OCTAL NUMBER (EXTENSIBLE BITS OFF)
;NSPOC7	COPY AN ASCII STRING FROM "INLINE" TO THE NSP MESSAGE
;NSPOC8	PARSES A "PROCESS NAME" FROM THE NCL MSG AND WRITES IT TO NSP
;NSPOC9	COPIES AN IMAGE FIELD FROM THE NCL MESSAGE TO THE NSP MESSAGE
;NSPOCT	PARSES AN OCTAL FIELD FROM THE NCL MESSAGE
;GETBY7	GETS A BYTE FROM THE NCL MESSAGE AND CLEARS THE EXTENSIBLE BIT

;ROUTINE TO WRITE THE "MSGFLG" THRU "SEGSIZ" FIELDS OF A NSP CI/CC MSG
;CALL WITH R0 := "MSGFLG"

NSPOC1:	JSR	PC,NSPPBY	;WRITE THE "MSGFLG" FIELD
	MOV	R1,NSPLEB	;REMEMBER THE LEB
	MOV	LE.NSP(R1),R0	;GET THE "DSTADDR"
	JSR	PC,NSPO2B	; AND WRITE THAT
	MOV	LE.DCP(R1),R0	;GET THE "SRCADDR"
	JSR	PC,NSPO2B	; AND WRITE THAT
	MOV	#NM.LNK!NM.SRQ+<1*400>,R0 ;GET BOTH "SERVICES" AND "INFO"
	JSR	PC,NSPO2B	; AND WRITE THEM BOTH IN ONE FELL SWOOP
	MOV	#MSGMAX,R0	;USE MSGMAX FOR "SEGSIZ"
	JSR	PC,NSPO2B	; WRITE SEGSIZE
	RTS	PC		;ALL DONE
NSPOC2:	JSR	PC,GETBYT	;GET OBJECT TYPE
	CMP	#11,R0		;TASK?
	BEQ	20$		;YES, GO PARSE HAIRY NCL "DPN" FIELD
	BIS	#LES.DV,LE.STS(R1);SET DEVICE BIT IN LINK ENTRY
	MOV	R0,R1		;SAVE OBJECT TYPE FOR LATER
	MOV	#1!<0*400>,R0	;USE FMT 1, OBJTYPE 0
	JSR	PC,NSPO2B	;PUT FORMAT, OBJTYPE
	JSR	PC,NSPOC4	;START AN IMAGE SUB-FIELD
	SAVE	<R1>		;PROTECT OUR OBJECT TYPE
	JSR	R1,NSPOC7	;COPY THE FIRST PART OF THE NAME
	.ASCIZ	/OBJ/		; FOR "OBJOON" WHERE OO = OBJECT TYPE
	.EVEN			; AND N = UNIT NUMBER
	RESTORE	<R1>		;GET THE OBJECT TYPE BACK IN R1
	MOV	R1,R0		;GET OBJECT TYPE
	ROR	R0		;SHIFT
	ROR	R0		; SECOND DIGIT
	ROR	R0		;  TO CORRECT PLACE
	BIC	#^C7,R0		;CLEAR EXTRA BITS
	BIS	#'0,R0		;MAKE INTO ASCII NUMBER
	JSR	PC,NSPPBY	;PUT 1ST DIGIT OF OBJECT TYPE INTO NAME
	MOV	R1,R0		;GET OBJECT TYPE BACK
	BIC	#^C7,R0		;CLEAR EXTRANEOUS BITS
	BIS	#'0,R0		;MAKE INTO NUMBER
	JSR	PC,NSPPBY	;STORE IN MESSAGE
	JSR	PC,GETEXN	;GET UNIT NUMBER
	CMP	R0,#177		;GENERIC UNIT?
	BEQ	10$		;YES, DON'T WRITE UNIT NUMBER
	JSR	PC,NSPOC6	;WRITE THE OCTAL NUMBER
10$:	JSR	PC,NSPOC5	; AND CLOSE OFF THE IMAGE FIELD.

;NOW WRITE SRCNAME FIELD (USLESS FIELD. JUST PUT JUNK THERE)
	MOV	#1!<0*400>,R0	;USE FMT 1, OBJTYPE 0
	JSR	PC,NSPO2B	;PUT FORMAT, OBJTYPE
	JSR	PC,NSPOC4	;START THE OBJECT FIELD FOR THE NAME
	JSR	R1,NSPOC7	;COPY THE NAME
	.ASCIZ	/EJW's Compatible FROB says hello./
	.EVEN
	JSR	PC,NSPOC5	;CLOSE OFF THE NAME FIELD
	CLR	R0		;GET A ZERO
	JSR	PC,NSPPBY	; AND WRITE AN EMPTY MENU
	MOV	NSPLEB,R1	;RESTORE R1
	RTS	PC		; AND WE'RE DONE.
;HERE IF WE ARE PARSING A NCL "DPN" AS A FULL NSP CONNECT MESSAGE

20$:	JSR	PC,GETBY7	;GET THE FIRST BYTE FROM THE MESSAGE
	BNE	99$		; IF "DPN" DOESN'T START WITH ZERO, BAD MSG
	JSR	PC,NSPOC8	;COPY THE "DSTNAM" FIELD
	JSR	PC,NSPOC8	; AND THE "SRCNAM" (THEY LOOK ALIKE)
	MOV	#3,R0		;FOR THE "MENU", SAY WE
	JSR	PC,NSPPEX	; HAVE ALL 4 FIELDS (RQSTRID -> USRDATA)
.REPT	4
	JSR	PC,NSPOC9	;COPY ALL 4 FIELDS
.ENDR
	RTS	PC		; AND WE'RE DONE

99$:	JSR	PC,NSPLTL	;REVERSE DIRECTION FOR "DISCONNECT"
	JSR	PC,NSPERR	;DECLARE AN ERROR
	NSPOBD			; AND TOSS THE MESSAGE
;ROUTINE TO PARSE THE NCL MESSAGE TO EXTRACT THE "USER DATA" IN THE "SPN"
; CALLED ONLY BY A CONNECT CONFIRM.
NSPOC3:	JSR	PC,GETEXN	;SKIP DEST OBJECT TYPE.
10$:	JSR	PC,GETBYT	;GET A BYTE FROM THE "DPN"
	BIT	#200,R0		;IF IT IS STILL EXTENSIBLE
	BNE	10$		; KEEP SKIPPING OVER THE "DPN"
	JSR	PC,GETEXN	;GET THE "OBJECT" OF THE SPN
	CMP	#11,R0		;AND MAKE SURE IT'S "TSK"
	BNE	99$		;IF NOT, WE CAN'T PARSE THIS MESSAGE
	JSR	PC,NSPOC9	;GO COPY THE IMAGE FIELD
	RTS	PC		; AND RETURN

99$:	JSR	PC,NSPLTL	;GET READY TO SEND A DISCONNECT TO NCL
	JSR	PC,NSPERR	;SIGNAL THE ERROR
	NSPOBD			; AND GET THE HELL OUT OF HERE
;ROUTINE TO START AN "IMAGE" FIELD.  WRITES THE COUNT BYTE,
; AND RETURNS WITH IT'S ADDRESS ON THE STACK
NSPOC4:	JSR	PC,NSPPBY	;WRITE A USELESS BYTE (AND COUNT IT)
	MOV	(SP)+,R0	;GET OUR RETURN ADDRESS OFF THE STACK
	MOV	R5,-(SP)	; AND SAVE THE ADDRESS OF THE COUNT
	DEC	(SP)		; FIXUP THE ADDRESS (WAS ONE TO FAR)
	MOVB	R4,@0(SP)	;SAVE THE COUNT SO FAR.
	JMP	@R0		;RETURN WITH "ADDR" ON THE STACK

;ROUTINE TO CLOSE OFF AN "IMAGE" FIELD.  EXPECTS THE ADDRESS
; OF THE "COUNT BYTE TO BE ON THE STACK.
NSPOC5:	MOVB	@2(SP),R0	;GET THE CONTENTS OF THE "COUNT" BYTE
	SUB	R4,R0		;GET MINUS THE NUMBER OF BYTES WRITTEN
	NEG	R0		;COMPLEMENT TO GET THE REAL NUMBER
	MOVB	R0,@2(SP)	;STORE THE CORRECTED COUNT
	MOV	(SP)+,R0	;GET OUR RETURN ADDRESS
	RTS	R0		;CLEAN UP THE STACK AND RETURN

;ROUTINE TO WRITE OUT AN OCTAL NUMBER (EXTENSIBLE BITS OFF)
NSPOC6:	MOV	R0,-(SP)	;SAVE THE LOW ORDER DIGIT (THE REST AS WELL...)
	ASR	R0		;SHIFT THE
	ASR	R0		; NEXT DIGIT
	ASR	R0		; DOWN
	BIC	#160000,R0	;MAKE SURE WE DIDN'T PROPAGATE THE SIGN
	BEQ	10$		;IF ALL DONE, GO PRINT THE DIGIT
	JSR	PC,NSPOC6	; OTHERWISE PRINT THE REST OF THE NUMBER
10$:	MOV	(SP)+,R0	;GET THE DIGIT TO PRINT
	BIC	#^C7,R0		; AND JUST THE DIGIT
	BIS	#'0,R0		;ASCII-IZE IT
	JMP	NSPPBY		; AND PUT IT IN THE MESSAGE

;ROUTINE TO COPY AN INLINE ASCIZ STRING INTO THE OUTPUT STREAM
;CALL
;	JSR	R1,NSPOC7

NSPOC7:	MOVB	(R1)+,R0	;GET THE NEXT BYTE TO GO
	BEQ	10$		;IF NO MORE, CLEAN UP AND RETURN
	JSR	PC,NSPPBY	;STORE THE BYTE
	BR	NSPOC7		; AND GO BACK FOR MORE
10$:	INC	R1		;MAKE SURE THAT R1 POINTS TO THE
	BIC	#1,R1		; NEXT WHOLE WORD ADDRESS
	RTS	R1		; AND RETURN

;ROUTINE TO COPY EXTENSIBLE STRING FROM R1 INTO IMAGE OUTPUT STREAM

NSPOEI:	MOVB	(R1)+,R0	;NEXT BYTE
	BPL	90$		;HANDLE LAST BYTE
	BIC	#^C177,R0	;REDUCE TO NORMAL ASCII CHARACTER
	BEQ	99$		;END IF NULL ASCII CHARACTER
	JSR	PC,NSPPBY	;STASH THIS BYTE
	BR	NSPOEI		;LOOP BACK FOR MORE

90$:	BEQ	99$		;IGNORE IF NULL
	JMP	NSPPBY		;OUTPUT FINAL IMAGE BYTE
99$:	RTS	PC		;JUST RETURN
;ROUTINE TO PARSE THE "SRC/DST-NAME" FIELD OF THE NCL "DPN"
; AND WRITE THE APPROPRIATE NSP "DST/SRC-NAME".

NSPOC8:	SAVE	<R1,R2,R3>	;SAVE R1, REMEMBER OUR "READER" POSITION

;FIRST LOOP OVER THE NAME COUNTING THE PERIODS (GIVES US THE FORMAT TYPE)
	CLR	R1		;START WITH NO PERIODS
10$:	JSR	PC,GETBY7	;GET THE NEXT BYTE
	BEQ	20$		;IF IT'S ZERO, WE'VE FINISHED THE FIELD
	CMP	#'.,R0		;SEE IF IT'S A PERIOD
	BNE	10$		;IF NOT A PERIOD, JUST KEEP SCANNING
	INC	R1		;IF IT IS A PERIOD, COUNT IT
	BR	10$		; AND SCAN FOR MIRE

20$:	RESTORE	<R3,R2>		;JUMP BACK TO THE BEGINNING OF THE FIELD
	CMP	R1,#3		;MAKE SURE THE COUNT OF PERIODS IS REASONABLE
	BGE	99$		;DIE IF TOO MANY PERIODS
	MOV	R1,R0		;GET THE NUMBER (= THE FORMAT)
	JSR	PC,NSPPBY	; AND WRITE THE FORMAT TYPE BYTE
	ASL	R1		;CONVERT INTO A WORD OFFSET.
	JSR	PC,@30$(R1)	;DISPATCH TO THE ROUTINE TO PROCESS THE FORMAT
	RESTORE	<R1>		;GET THE REGISTER BACK
	RTS	PC		; AND WE'RE DONE

30$:	.WORD	40$, 50$, 60$	;DISPATCH VECTOR INDEXED BY FORMAT TYPE.
;FORMAT 0 = "OBJECT"
40$:	JSR	PC,NSPOCT	;PARSE THE OCTAL NUMBER
	TST	R0		;SEE WHAT CHARACTER ENTED THE FIELD
	BNE	99$		;IF FIELD DOESN'T END WITH ZERO, MSG IS BAD
	MOV	R1,R0		;COPY THE OBJECT TYPE
	JMP	NSPPBY		;WRITE THE OBJECT TYPE

;FORMAT 1 = "OBJECT", "DESCRPT"
50$:	JSR	PC,55$		;PROCESS THE "OBJECT" FIELD
	JMP	NSPOC9		; GO COPY THE IMAGE "DESCRPT" FIELD

55$:	JSR	PC,NSPOCT	;PARSE THE OCTAL NUMBER
	CMP	#'.,R0		;MAKE SURE THE NUMBER ENDED WITH A "."
	BNE	99$		;IF GARBAGE IN MESSAGE, TOSS IT
	MOV	R1,R0		;COPY THE OBJECT TYPE FOR NSPPBY
	JMP	NSPPBY		;OUTPUT THE OBJECT TYPE AND RETURN
;FORMAT 2 = "OBJECT", "GRPCODE", "USRCODE", "DESCRPT"
60$:	JSR	PC,55$		;GO PROCESS THE "OBJECT"
	JSR	PC,GETBY7	;GET THE NEXT BYTE OF THE MESSAGE
	CMP	#'[,R0		; AND MAKE SURE IT'S THE OPEN SQUARE
	BNE	99$		;IF NOT, CHUCK THE MESSAGE

	JSR	PC,NSPOCT	;GET THE OCTAL "P"
	CMP	#',,R0		;MAKE SURE THE PUNCTUATION IS RIGHT
	BNE	99$		;IF NOT, TOSS THE MESSAGE
	MOV	R1,R0		;COPY THE PROJECT
	JSR	PC,NSPO2B	; AND CALL IT A GROUP

	JSR	PC,NSPOCT	;GET THE OCTAL "PN"
	CMP	#'],R0		;MAKE SURE THE TERMINATOR IS A CLOSE SQUARE
	BNE	99$		;IF IT ISN'T, THE MESSAGE IS TRASH
	MOV	R1,R0		;COPY THE PROGRAMMER-NUMBER
	JSR	PC,NSPO2B	; AND CALL IT A "USRCODE"

	JSR	PC,GETBY7	;GET THE FIELD SEPARATOR
	CMP	#'.,R0		; AND MAKE SURE THAT IT'S A PERIOD
	BNE	99$		;IF IT ISN'T, THE MESSAGE IS BAD
	PJMP	NSPOC9		;COPY THE IMAGE "DESCRPT" FIELD AND RETURN

99$:	JSR	PC,NSPLTL	;REVERSE THE DIRECTION FOR THE DISCONNECT
	JSR	PC,NSPERR	;DECLARE THE LINK AS DEAD
	NSPOBD			;DECLARE THE MESSAGE AS BAD TOO.
;ROUTINE TO COPY AN IMAGE FIELD FROM THE NCL MESSAGE TO THE NSP ONE
NSPOC9:	JSR	PC,NSPOC4	;START THE OUTPUT IMAGE FIELD
10$:	JSR	PC,GETBY7	;GET THE NEXT BYTE
	BEQ	20$		;IF IT'S THE TERMINATOR, RETURN
	JSR	PC,NSPPBY	;WRITE THE NEXT BYTE OF THE MESSAGE
	BR	10$		; AND GO BACK FOR MORE
20$:	JSR	PC,NSPOC5	;CLOSE OFF THE IMAGE FIELD
	RTS	PC		; AND RETURN

;ROUTINE TO PARSE AN OCTAL NUMBER FROM THE NCL MESSAGE
NSPOCT:	CLR	R1		;WE RETURN THE NUMBER IN R1
10$:	JSR	PC,GETBY7	;GET THE NEXT BYTE
	CMP	#'0,R0		;SEE IF IT IS LESS THAN
	BGT	20$		; ZERO, IF SO, IT'S NOT A NUMBER
	CMP	#'7,R0		;IF ITS GREATER THAN SEVEN
	BLT	20$		; IT'S NOT A NUMBER EITHER
	BIC	#^C7,R0		;GET JUST THE "NUMBER" PART
	ASL	R1		;MULTIPLY
	ASL	R1		; THE ACCUMULATOR
	ASL	R1		; BY EIGHT
	BIS	R0,R1		; AND ADD IT THIS DIGIE
	BR	10$		;GO SEE IF THERE ARE ANY MORE
20$:	RTS	PC		;RETURN WITH R1 = NUMBER, R0 = LAST CHAR


;ROUTINE TO GET THE NEXT CHAR FROM THE NCL MESSAGE AND CLEAR THE EXT BIT
GETBY7:	JSR	PC,GETBYT	;GET THE NEXT BYTE
	BIC	#^C177,R0	; AND CLEAR THE EXTENSIBLE BIT
	RTS	PC		;RETURN WITH THE "Z" BIT SET/CLEAR
;HERE TO TRANSLATE DISCONNECT MESSAGE. THE REASON FIELD WILL BE TRANSLATED
;INTO SOMETHING FAIRLY CLOSE TO WHAT NSP SHOULD GET.

NSPODC:	JSR	PC,GETEXN	;GET DLA
	JSR	PC,NSPSLB	;FIND LEB FOR IT
	JSR	PC,GETEXN	;GET SLA (IN NCL (WELL, NETSER) THIS IS 0)
	MOVB	LE.STT(R1),R0	;RECOVER CURRENT STATE
	JMP	@NSPDSX(R0)	;FIGURE OUT WHAT TO DO

NSPDSX:	.WORD	NSPFLB		;IGNORE
	.WORD	NSPFLB		;DISCONNECT AFTER CONNECT? NSP SAYS TO FORGET LINK
	.WORD	NSPDS2		;REQUEST REJECTED, RETURN DI
	.WORD	NSPDS2		;RUNNING - SEND DI, ENTER DSC
	.WORD	NSPDS1		;DSC - SEND DC, ENTER IDLE

	.ENABL	LSB
NSPDS1:	MOV	#NM.DC,R0	;SEND DC
10$:	CLRB	LE.STT(R1)	;ENTER IDLE STATE
	MOV	#NSPFLB,-(SP)	;FREE LEB WHEN DONE
	BR	20$		;JOIN COMMON CODE

NSPDS2:	MOV	#NM.DI,R0	;SEND DI
	MOVB	#LES.DS,LE.STT(R1) ;AND ENTER DSC STATE
20$:	SAVE	<R0>
	JSR	PC,NSPBMG	;MAKE NSP MESSAGE BUFFER
	RESTORE	<R0>		;RECOVER MSGFLG
	BCS	50$		;JUST HAVE LOW LEVEL SEND DC
	JSR	PC,NSPRML	;BUILD MOST OF MESSAGE
	TSTB	LE.STT(R1)	;HAVE WE GONE BACK TO IDLE?
	BEQ	40$		;YES, GET CONFIRM REASON
	JSR	PC,GETEXN	;GET NCL REASON
	CMP	R0,#NSPRSM	;BEYOND RANGE?
	BLO	30$		;NO, MAP REASON CODE
	CLR	R0		;YES, USE A NO ERROR CODE
30$:	MOVB	NSPRXL(R0),R0	;MAP REASON
	JSR	PC,NSPO2B	;PUT RSN
	CLR	R0		;DI NEEDS A DATA FIELD
	JMP	NSPPBY		;PUT DATA

40$:	MOV	#NM.CNF,R0	;USE CONFIRM REASON FOR DC
	JMP	NSPO2B		;JUST PUT IT ON

50$:	NSPODF			;DEFER UNTIL MEMORY IS AVAILABLE TO SEND A PROPER DISCONNECT


NSPRXL:	.BYTE	NM.NOE,NM.PDE,NM.TCN,NM.TCP,NM.PDE
	NSPRSM	=.-NSPRXL
	.EVEN
	.DSABL	LSB
;ROUTINE TO CONVERT DATA REQUESTS INTO LINK SERVICE MESSAGES. SINCE THE PORT
;SPECIFIES SEGMENT REQUESTS ON ALL CONNECTIONS IT SETS UP, THERE IS A ONE
;TO ONE CORRESPONDENCE BETWEEN DATA REQUESTS AND SEGMENT REQUESTS.

NSPODR:	JSR	PC,GETEXN	;GET DLA
	JSR	PC,NSPSLB	;FIND LEB FOR IT
	CMPB	LE.STT(R1),#LES.RN ;ONLY ALLOW WHEN RUNNING
	BNE	10$		;PROBABLY IN DSC STATE, THROW IT AWAY
	JSR	PC,GETEXN	;GET DRQ
	SAVE	<R0>
	JSR	PC,NSPBMG	;MAKE HEADER
	BCS	30$		;DEFER IT
	JSR	PC,NSPBLS	;BUILD MOST OF LS MESSAGE
	CLR	R0		;SAY NO CHANGE IN FLOW CONTROL PARAMS
	JSR	PC,NSPPEX	;PUT LSFLAGS
	RESTORE	<R0>		;RECOVER DRQ
	JMP	NSPPBY		;PUT FCVAL

10$:	TSTB	LE.STT(R1)	;IDLE?
	BNE	20$		;NO
	JSR	PC,NSPFLB	;NO ONE ELSE WILL FREE THIS, SO WE MUST
20$:	NSPOBD

30$:	NSPODF			;WAIT FOR MEMORY
	.SBTTL		LINK ERROR PROCESSOR

;HERE TO HANDLE LINK ERROR EVENTS. ENTER AT NSPERL IF THERE IS A NCL
;MESSAGE THAT MUST BE FREED OR AT NSPERP IF THERE IS A NSP MESSAGE THAT
;MUST BE FREED. IN EITHER CASE, A DC MESSAGE WILL BE SENT FROM LOW LEVEL
;AND A NCL DISCONNECT WILL BE SENT IMMEDIATELY IF THERE IS SPACE TO BUILD IT.

NSPERL:	MOV	NSPLMG,R0	;GET NCL MESSAGE WE WERE READING
	JSR	PC,NCLODN	;TRY TO FREE IT
	BR	NSPERR		;ENTER AT COMMON CODE

NSPERP:	MOV	NSPPMG,R0	;RECOVER OLD NSP MESSAGE
	JSR	PC,FRECKS	;DISCARD IT
	CLR	NSPPMG		;SO NSPOUT WON'T TRY TO SEND FREE CORE
NSPERR:	MOV	#NM.UEC,R0	;TRY TO SEND RANDOM REASON DC
	JSR	PC,NSPQDC	; AT LOW LEVEL IF NOTHING ELSE HAS BEEN SPECIFIED
	TST	LE.NCL(R1)	;IS THERE A NCL SIDE?
	BEQ	30$		;NO, RETURN
	SAVE	<NSPLMG>	;IF CALLED BY NSPOUT, IT WILL FREE THIS LATER
	MOV	#NCLDIS,R1	;GET MESSAGE TYPE TO SEND
	JSR	PC,NSPBNC	;TRY TO START NCL MESSAGE
	BCS	20$		;SIGH
	MOV	NSPLEB,R1
	MOV	LE.NCL(R1),R0	;RECOVER DLA
	JSR	PC,NSPPEX	;PUT DLA
	MOV	LE.DCP(R1),R0	;AND OUR END
	JSR	PC,NSPPEX	;PUT SLA
	MOV	#3,R0		;NETWORK CAPACITY EXCEEDED?
	JSR	PC,NSPPBY	;PUT RSN
	JSR	PC,NSPSNC	;SEND IT
20$:	RESTORE	<NSPLMG>
30$:	RTS	PC		;DONE, LOW LEVEL WILL FREE LEB
	.SBTTL		SEGMENT ACKNOWLEDGEMENT/ RETRANSMISSION

;ROUTINES TO FREE ACKED MESSAGES FROM LEB QUEUES
;ENTER AT NSPFRL TO FREE LS MESSAGES OR AT NSPFRD TO FREE DATA MESSAGES.
;CALL WITH R0/ ACKNUM FIELD OF LS, DATA, OR ACK MESSAGE.
;ACKNUM FIELDS ALSO IMPLY NEGATIVE ACKNOWLEDGEMENT AND THESE ROUTINES WILL
;DETECT THAT AND RETRANSMIT ALL NAKED MESSAGES.
;WHEN THE OUTPUT QUEUES ARE EMPTIED THE LINK TIMER SHOULD BE CLEARED BUT
;ISN'T (THE CODE THIS SAVES SHOULD BE ADDED IF ANY TROUBLE IS SEEN AT
;ALL). WHEN LINK TIMEOUT OCCURS, THE RETRANSMIT ROUTINES WILL BE CALLED BUT
;SINCE THEY WON'T DO ANYTHING THE LINK TIMER WILL BE LEFT AT ZERO.

	.ENABL	LSB
NSPFRL:	MOV	#LE.OQL,R1	;OFFSET FOR A LIST POINTER TO MSGS
	BR	10$		;JOIN COMMON CODE

NSPFRD:	MOV	#LE.OQD,R1	;OR FOR DATA QUEUE
10$:	SAVE	<R2>		;DON'T LET COUNT BE WIPED OUT
	ADD	NSPLEB,R1	;POINT TO LIST HEAD
	MOV	R0,-(SP)	;SAVE ACKNUM FIELD
	MOV	R1,-(SP)	;SAVE FOR REUSE IN NAK PROCESSING
20$:	MOV	@R1,R0		;GET NEXT MESSAGE
	BEQ	30$		;STOP WHEN WE RUN OUT
	MOV	CN.SEG(R0),R2	;GET SEGNUM FOR THIS MESSAGE
	DEC	R2		;TO MAKE SUB BELOW SET "SIGN" IF FREEABLE
	SUB	2(SP),R2	;HOW FAR AWAY ARE THE 2?
	BIT	#<NM.NMM+1>/2,R2 ;IF SET, THEN IT'S WITHIN THE LAST 2048
	BEQ	30$		;NOT SET, LEAVE IT
	MOV	CN.MLK(R0),@R1	;DEQUEUE MESSAGE
	JSR	PC,FRECKS	;DISCARD IT
	BR	20$		;LOOK FOR MORE

30$:	MOV	(SP)+,R0	;GET LIST HEADER ADDR FOR NAK PROCESSING
	BIT	#NM.NAK,(SP)+	;WHAT SORT OF ACK WAS THAT?
	BEQ	80$		;PURE ACK, ALL DONE
	BR	50$		;MAKE ABS. AND RETRANSMIT REMAINDER
;ROUTINES TO RETRANSMIT OUTPUT QUEUES. THESE ARE ENTERED FROM TWO
;PLACES. FIRST, THE ACKNUM PROCESSORS (NSPFRL, NSPFRD) WILL ENTER HERE IF
;THE NAK BIT IS ON IN ACKNUM, SECOND, THE LINK TIMING CODE WILL ENTER HERE
;TO RETRANSMIT MESSAGES WHEN LINK TIMEOUT OCCURS.

NSPRTL:	MOV	#LE.OQL,R0	;HEADER OF LS/INT MESSAGES
	BR	40$		;MAKE ABS. ADDR AND RETRANSMIT

NSPRTD:	MOV	#LE.OQD,R0	;HEADER OF DATA MESSAGES
40$:	ADD	NSPLEB,R0	;MAKE ABSOLUTE ADDR
	SAVE	<R2>		;PROTECT ALL REGISTERS
50$:	SAVE	<R3,R4,R5>	;R2 ON STACK FROM ALL ENTRIES HERE
	MOV	NSPLB,J		;POINT TO LINE BLOCK
	MOV	LB.SCB(J),SB	; AND SCB
	MOV	@R0,-(SP)	;SAVE ADDR OF FIRST MESSAGE TO BE RETRANSMITTED
	CLR	@R0		;WIPEOUT LIST OF UNACKNOWLEDGED MESSAGES IN LEB
	BR	70$		;GO TRANSMIT THEM OVER AGAIN

60$:	MOV	CN.MLK(R0),-(SP) ;SAVE ADDRESS OF NEXT MESSAGE
	JSR	PC,DDQNSP	;SEND IT, LE.TIM WILL BE SET AT NSPODN
70$:	MOV	(SP)+,R0	;RECOVER ADDRESS OF NEXT MESSAGE
	BNE	60$		;SEND IT IF THERE IS ONE
	RESTORE	<R5,R4,R3>
80$:	RESTORE	<R2>
	RTS	PC		;ALL DONE
	.DSABL	LSB
	.SBTTL		NSP DECODING SUBROUTINES

;ROUTINE TO READ IMAGE FORMAT FIELDS IN NSP MESSAGES
;CALL:
;	R0/ADDR OF DEST STRING
;	JSR	PC,NSPIIM
;	.WORD	LENGTH OF DEST STRING

NSPIIM:	MOV	R0,R4		;PROTECT DEST STRING ADDR
	MOV	@0(SP),-(SP)	;GET LENGTH OF DEST STRING
	ADD	#2,2(SP)	;SO RTS HITS AN INSTRUCTION
	JSR	PC,GETBYT	;GET LENGTH OF SOURCE STRING
	MOV	R0,R1		;PROTECT FROM FUTURE GETBYT CALLS
	BEQ	15$		;IF A NULL STRING, JUST CLEAR OUTPUT AREA
	SUB	R1,@SP		;@SP_HOW MUCH LONGER DEST IS THAN SOURCE
	BGT	10$		;WELL, IT SHOULD BE
	ADD	@SP,R1		;SOURCE LONGER, LIMIT COPY TO DEST'S LENGTH (@SP-R1)+R1
10$:	JSR	PC,GETBYT	;GET BYTE FROM SOURCE
	BIS	#200,R0		;MAKE EXTENSIBLE
	MOVB	R0,(R4)+	;PUT IN OUTPUT STRING
	SOB	R1,10$		;DO REST
	BICB	#200,-1(R4)	;LAST BYTE MUSTN'T HAVE EXTENSIBLE BIT ON
15$:	MOV	(SP)+,R1	;GET STRING LENGTH DIFFERENCE
	BLE	30$		;TRIM NSP MESSAGE IF SOURCE WAS LONGER
20$:	CLRB	(R4)+		;OTHERWISE CLEAR REST OF DESTINATION STRING
	SOB	R1,20$		;LOOP
99$:	RTS	PC		;AND DONE

30$:	BEQ	99$		;STOP WHEN READ WHOLE STRING
	JSR	PC,GETBYT	;GET NEXT BYTE
	INC	R1		;COUNT IT
	BR	30$		;LOOP


;ROUTINE TO READ B-2 FIELDS IN NSP MESSAGES:
;CALL:
;	JSR	PC,NSPI2B
;	R0/	CONTENTS OF 2 BYTE FIELD

NSPI2B:	JSR	PC,GETBYT	; GET LOW HALF OF FIELD
	MOV	R0,-(SP)
	JSR	PC,GETBYT	; AND HIGH HALF
	MOVB	R0,1(SP)	;BUILD FULL WORD
	MOV	(SP)+,R0	;GET TOTAL RESULT
	RTS	PC		;RETURN IT
	.SBTTL		NSP BUILDING SUBROUTINES

NSPBMG:	JSR	PC,GETCNK	;GET A CHUNK
	BNE	10$		;GOT ONE, USE IT
	TRAP			;FOR NOW
;	SEC			;SAY WE LOST
	RTS	PC

10$:	MOV	R0,NSPPMG	;SAVE START OF MESSAGE
NSPBM1:	MOV	NSPPMG,R0	;GET ADDRESS OF CURRENT MESSAGE
	CLR	R4		;CLEAR COUNT REGISTER
	MOV	R0,R5		;PUT START ADDRESS INTO BYTE POINTER REGISTER
	CLR	CN.MLK(R5)	;CLEAR MESSAGE LINK
.IIF NE,DEVN, CLR CN.DDB(R5)	;AND DDB LINK
	ADD	#CN.NCT,R5	;POINT TO FIRST DATA BYTE
	CLR	CN.SEG(R0)	;SAY MESSAGE NEED NOT BE QUEUED AT NSPODN FOR NOW
	RTS	PC		;RETURN CARRY CLEAR


NSPSNS:	MOV	#DDQNSP,-(SP)	;SHORTEST WAY TO CALL BELOW THEN DDQNSP
NSPSN0:	MOV	NSPPMG,R0	;GET MESSAGE ADDRESS
	ASSERT	NE
	MOV	R4,CN.LEN(R0)	;TELL DDCMP HOW LONG IT IS
	MOV	NSPLB,J		;AND ADDR OF LINE BLOCK
	MOV	LB.SCB(J),SB	;AND SCB OF DEST
	RTS	PC		;NSPODM WILL SET LE.TIM
;ROUTINE TO BUILD A INTERRUPT OR LS MESSAGE UP TO SEGNUM FIELD. ONLY IF
;IT WILL DO ANY GOOD WILL AN ACKNUM FIELD BE GENERATED. IF ONE IS GENERATED,
;THE REQUEST BIT WILL BE CLEARED SO THAT LOW LEVEL WILL NOT SEND A DUPLICATE.

	.ENABL	LSB
NSPBID:	MOV	#NM.IDT,R0	;INTERRUPT DATA MESSAGE TYPE
	BR	10$

NSPBLS:	MOV	#NM.LS,R0	;GET APPROPRIATE MSGFLG
10$:	JSR	PC,NSPMDS	;PUT MSGFLG, DSTADDR, SRCADDR
	BIT	#LES.LA,@R1	;HAVE TO SEND AN ACK SOMEDAY?
	BEQ	20$		;NOPE, DON'T WASTE THE SPACE
	MOV	LE.LIL(R1),R0	;GET LAST LS MESSAGE WE SAW
	BIS	#B15,R0		;MAKE ACKNUM VALUE
	JSR	PC,NSPO2B	;PUT ACKNUM
	BIC	#LES.LA,@R1	;NO NEED TO SEND AN ACK
20$:	MOV	LE.LOL(R1),R0	;TAKE LAST SEGNUM WE USED
	INC	R0		;STEP TO NEXT
	BIC	#^CNM.NMM,R0	;MODULO 4096
	MOV	R0,LE.LOL(R1)	;MAKE SURE THE LEB VERSION IS SAME
	JSR	PC,NSPO2B	;PUT SEGNUM
	MOV	NSPPMG,R0	;PUT INTO MESSAGE BLOCK
	MOV	LE.LOL(R1),CN.SEG(R0) ;SO NSPODN QUEUES THIS
	BIS	#B15,CN.SEG(R0)	;FLAG A LS/INT MSG
	RTS	PC		;LET CALLER FILL REST
	.DSABL	LSB


;ROUTINE TO BUILD A DATA MESSAGE UP TO SEGNUM FIELD. ESSENTIALLY A
;CARBON COPY OF NSPBID/BLS

NSPBDT:	JSR	PC,NSPMDS	;PUT MSGFLG, DSTADDR, SRCADDR
	BIT	#LES.DA,@R1	;HAVE TO SEND AN ACK?
	BEQ	10$		;NO, SAVE THE SPACE
	MOV	LE.LID(R1),R0	;GET LAST DATA MESSAGE WE SAW
	BIS	#B15,R0		;MAKE ACKNUM VALUE
	JSR	PC,NSPO2B	;PUT ACKNUM
	BIC	#LES.DA,@R1	;NO NEED TO SEND AN ACK
10$:	MOV	LE.LOD(R1),R0	;TAKE LAST SEGNUM WE USED
	INC	R0		;STEP TO NEXT
	BIC	#^CNM.NMM,R0	;MODULO 4096
	MOV	R0,LE.LOD(R1)	;MAKE SURE THE LEB VERSION IS SAME
	JSR	PC,NSPO2B	;PUT SEGNUM
	MOV	NSPPMG,R0	;PUT INTO MESSAGE BLOCK
	MOV	LE.LOD(R1),CN.SEG(R0) ;SO NSPODN QUEUES THIS
	RTS	PC		;LET CALLER FILL REST
;ROUTINE TO PUT ON RTHDR, MSGFLG, DSTADDR, AND SRCADDR FIELDS OF NSP
;CALL:
;	R0/MSGFLG VALUE,  R4,R5/ POINTING TO START OF NSP MSG
;	JSR	PC,NSPRML	;FOR RTHDR, MSGFLG, DSTADDR, SRCADDR
;	JSR	PC,NSPMDS	;FOR MSGFLG, DSTADDR, SRCADDR

NSPRML:	SAVE	<R0>		;PROTECT MSGFLG
	JSR	PC,NSPORT	;PUT ON RTHDR
	RESTORE	<R0>		;RECOVER MSGFLG
NSPMDS:	JSR	PC,NSPPBY	;PUT MSGFLG
	MOV	NSPLEB,R1	;RECOVER LEB ADDRESS
	MOV	LE.NSP(R1),R0	;RECOVER LINK ADDRESSES
	JSR	PC,NSPO2B	;PUT DESTADDR
	MOV	LE.DCP(R1),R0
	BR	NSPO2B		;PUT SRCADDR


;ROUTINE TO PUT NSP ASCII ROUTING HEADER ON MESSAGE

NSPORT:	MOV	#NM.ROU,R0	;READY ROUTING FLAG BYTE
	JSR	PC,NSPPEX	;RTFLG
	MOV	DNA,R1		;POINT TO DESTINATION NAME
	ADD	#SB.SNM,R1
	JSR	PC,NSPOIM	;DSTNODE  PUT INTO NSP MESSAGE
	JSR	PC,NSPOC4	;START THE IMAGE FIELD FOR OUR NAME
	MOV	NSPLB,R1	;ADDRESS OF LINE BLOCK
	ADD	#LB.HNM,R1	;ADDRESS OF "OUR" NODE NAME
	JSR	PC,NSPOEI	;OUTPUT NODE NAME (EXTENSIBLE TO IMAGE)
	JSR	PC,NSPOC5	;CLOSE OFF THE FIELD
	RTS	PC		; AND RETURN
;ROUTINE TO CONVERT EXTENSIBLE ASCII INTO IMAGE DATA IN OUTPUT STRING
;CALL:
;	R1/ADDRESS OF STRING
;	JSR	PC,NSPOIM
;	R2, R3 PRESERVED, ALL ELSE CLOBBERED

NSPOIM:	SAVE	<R2>		;SOME CALLERS USE THIS
	MOV	R5,-(SP)	;SAVE PLACE IN OUTPUT
	JSR	PC,NSPPBY	;OUTPUT LENGTH FIELD
	CLR	R2
10$:	MOVB	(R1)+,R0	;GET NEXT BYTE OF NAME
	INC	R2		;COUNT IT
	BITB	#200,R0		;LAST ?
	BEQ	20$		;YES, DONE
	BICB	#200,R0		;CLEAR EXTEND BIT
	JSR	PC,NSPPBY	;PUT IT OUT
	BR	10$		;AND GET THE NEXT ONE

20$:	JSR	PC,NSPPBY	;OUTPUT LAST ONE
	MOVB	R2,@(SP)+	;SET COUNT RIGHT
	RESTORE	<R2>
	RTS	PC
;ROUTINE TO TRANSLATE AN ASCII, EXTENSIBLE OCTAL NUMBER INTO BINARY
;AND STORE THAT IN A 2 BYTE NSP FIELD.
;CALL:
;	R2,R3/POINT TO NCL MESSAGE
;	R4,R5/POINT TO NSP MESSAGE
;	JSR	PC,NSPOCP
;	R0/TERMINATING CHARACTER WITH EXTENSIBLE BIT STILL ON

NSPOCP:	CLR	R1		;ACCUMULATE NUMBER HERE
10$:	JSR	PC,GETBYT	;GET A DIGIT
	CMPB	R0,#'0!200	;SMALLER THAN A 0?
	BLO	20$		;YES, TERMINATOR
	CMPB	R0,#'7!200	;BIGGER THAN A 7?
	BHI	20$		;YES, ALSO MUST BE A TERMINATOR
	ASL	R1		;SHIFT
	 ASL	 R1		 ;A
	  ASL	  R1		  ;DIGIT
	BIC	#^C7,R0		;EXTRACT BINARY VALUE
	ADD	R0,R1		; AND MERGE WITH WHAT WAS THERE
	BR	10$		;BACK FOR MORE

20$:	SAVE	<R0>		;MUST RETURN TERMINATOR TO CALLER
	MOV	R1,R0		;COPY TO RIGHT REGISTER
	JSR	PC,NSPO2B	;PUT GROUP OR USER
	MOV	(SP)+,R0	;RESTORE TERMINATOR
	RTS	PC		;GIVE TO CALLER


;ROUTINE TO PUT A 16 BIT VALUE INTO A NSP MESSAGE AS TWO BYTES
;CALL:
;	R0/VALUE
;	JSR	PC,NSPO2B
;	R0,R1 RETURNED UNCHANGED

NSPO2B:	JSR	PC,NSPPBY
	SWAB	R0		; GET HIGH HALF
	JSR	PC,NSPPBY
	SWAB	R0		;RETURN R0 UNCHANGED
	RTS	PC
	.SBTTL		NCL BUILD SUBROUTINES

;ROUTINE TO CALL WHEN PROCESSING A MESSAGE AND WANT TO GENERATE A REPLY
;TO THE ORIGINAL SENDER. ENTER AT NSPLTL IF NCL, NSPPTP IF NSP. PRESENTLY
;BOTH ROUTINES ARE THE SAME.

NSPPTP:
NSPLTL:	MOV	SNA,R0		;EXCHANGE SNA AND DNA
	MOV	DNA,SNA
	MOV	R0,DNA
	RTS	PC

;ROUTINE CALLED TO BUILD THE HEADER OF A NCL NUMBERED CONTROL
;MESSAGE
;CALL:
;	R0/ADDRESS OF FIRST CHUNK OF OLD MESSAGE IF CALLING NSPBN1
;	R1/MESSAGE TYPE:
;		IF B15 SET, THEN NCT BITS,
;		IF B15 CLEAR THEN NUMBERED CONTROL MSG TYPE
;	JSR	PC,NSPBNC	;TO ALLOCATE NEW MESSAGE
;	JSR	PC,NSPBN1	;IF OLD MESSAGE EXISTS
;	R4/COUNT; R5/ADDR FOR NSPPBY AND FRIENDS
;	NSPCNT,NSPADR POINTING TO END OF HEADER, USED BY NSPSNC

NSPBNC:	JSR	PC,GETCNK	;GET A CHUNK TO START THE MESSAGE
NSPBN2:	MOV	R0,NSPLMG	;REMEMBER START OF NCL MESSAGE
	BNE	NSPBN1		;FILL IT
	SEC			;SAY ERROR
	RTS	PC

NSPBN1:	MOV	NSPLMG,R0	;GET START OF NCL MESSAGE
	MOV	R1,-(SP)	;SAVE CONTROL MSG TYPE
	BMI	10$		;MINUS MEANS DATA MESSAGE
	CLR	R1		;TO TELL NCLBM1 WE WANT A NUMBERED MESSAGE
10$:	SAVE	<R2,R3>		;PROTECT POINTERS TO MESSAGE WE MAY BE READING
	JSR	PC,NCLBM1	;WRITE HEADER
	TST	(SP)+		;REMOVE START OF MESSAGE NCLBM1 HID ON STACK
	MOV	R2,R4		;MOVE STRING POINTER TO OUTPUT ACS
	MOV	R3,R5
	RESTORE	<R3,R2>		;RESTORE POINTERS TO POSSIBLE INPUT MESSAGE
	MOV	(SP)+,R0	;RECOVER MESSAGE TYPE
	BMI	20$		;MINUS MEANS DATA, SO WE'RE DONE
	MOV	R0,-(SP)	;SAVE IT A WHILE LONGER
	CLR	R0
	JSR	PC,NSPPBY	;PUT IN ZERO DLA
	MOV	R4,NSPCNT	;REMEMBER WHERE COUNT FIELD IS
	MOV	R5,NSPADR
	JSR	PC,NSPPBY	;HOLD A BYTE FOR COUNT LATER
	CLR	R4		;REINIT COUNT
	MOV	(SP)+,R0	;GET BACK MESSAGE TYPE
	JSR	PC,NSPPBY	;STORE AND RETURN. CALL NSPSNC TO SEND
20$:	RTS	PC
;ROUTINE TO SEND NCL MESSAGE TO DNNCL. CALL AFTER LAST BYTE IS PUT
;IN NCL MESSAGE AND READY TO SEND. THIS WILL FIX THE COUNT FIELD AND PASS
;IT ON.
;CALL:
;	NSPCNT/ LENGTH OF CONTROL SUBMESSAGE
;	NSPADR/ ADDR OF COUNT FIELD
;	JSR	PC,NSPSNC
;	<MESSAGE GIVEN TO NCL>

NSPSNC:	ASSERT	#^C177 CLEAR IN R4 ;CHECK SIZE
	MOVB	R4,@NSPADR	;PUT COUNT IN MESSAGE
	INC	R4		;THAT PLUS LENGTH OF COUNT FIELD
	ADD	NSPCNT,R4	; PLUS LENGTH OF HEADER IS LENGTH OF WHOLE
NSPSDL:	MOV	NSPLMG,R0	;GET START OF MESSAGE
	MOV	R4,CN.LEN(R0)	;SAVE LENGTH FOR DDCMP
.IIF NE FTDCPT,JSR PC,NSPOTL	;PRINT IT FIRST
	JSR	PC,NCLIN1	;SEND IT
	RTS	PC
	.SBTTL		RANDOM SUBROUTINES

;ROUTINE TO STORE EXTENSIBLE FILED IN A LITTLE SPACE AS POSSIBLE.
;THIS WILL HANDLE UP TO 16 BITS OF EXTENSIBLE DATA.

NSPPEX:	BIT	#^C177,R0	;MORE THAN 7 BITS?
	BEQ	NSPPBY		;NO, JUST USE ONE BYTE
	MOV	R0,-(SP)	;SAVE VALUE
	JSR	PC,NSPEXB	;PUT BYTE WITH EXTENSIBLE BIT ON
	MOV	(SP)+,R0	;GET VALUE BACK
	SWAB	R0		;SHIFTING 7 BITS IS A TASK AN -11
	ASL	R0		; JUST WASN'T DESIGNED TO DO....
	ADC	R0		;DON'T LET THAT HIGH BIT ESCAPE!
	BIC	#^C777,R0	;CLEAR BITS WE'VE DONE
	BR	NSPPEX		;DO NEXT BYTE OR TWO


NSPEXB:	BIS	#200,R0		;TURN ON EXTENSIBLE BIT


;ROUTINE TO PUT A BYTE OF DATA INTO THE OUTPUT MESSAGE. EXTRA CHUNKS WILL BE
;ALLOCATED AS NECESSARY.
;CALL:
;	R0/BYTE TO BE OUTPUT
;	JSR	PC,NSPPBY
;	R0-R3 PRESERVED

NSPPBY:	BIT	#CNKSIZ-1,R5	;IS IT NOW POINTING PAST END?
	BNE	20$		;NO, OKAY TO STORE BYTE
	MOV	R0,-(SP)	;SAVE DATA ON STACK
	MOV	-CNKSIZ(R5),R0	;IS THERE A CHUNK ALREADY?
	BNE	10$		;YES, USE IT
	JSR	PC,ERSGET	;GET A CHUNK IF AT ALL POSSIBLE
	MOV	R0,-CNKSIZ(R5)	;LINK NEW CHUNK TO OLD
10$:	MOV	R0,R5		;POINT TO NEW CHUNK
	TST	(R5)+		;CLEAR ITS FOWARD LINK
	MOV	(SP)+,R0	;RESTORE DATA
20$:	MOVB	R0,(R5)+	;PUT BYTE AT POINTER AND ADVANCE POINTER
	INC	R4		;INCREMENT BYTE COUNT
	RTS	PC		;RETURN
;ROUTINE TO COMPARE TWO EXTENSIBLE STRINGS
;CALL:
;	R0/	ADDR OF ONE STRING
;	R1/	ADDR OF OTHER STRING
;	JSR	PC,NSPCEA
;	BCS	MISMATCH

NSPCEA:	CMPB	(R0)+,@R1	;COMPARE FIRST TWO BYTES
	BNE	10$		;MISMATCH, SET CARRY AND RETURN
	TSTB	(R1)+		;MORE OF STRING TO LOOK AT?
	BMI	NSPCEA		;YES
	RTS	PC		;RETURN CARRY CLEAR

10$:	SEC			;SET CARRY BIT
	RTS	PC


;ROUTINE TO "BLT" EXTENSIBLE BYTE STRING
;CALL:
;	R0/	ADDR OF SOURCE STRING
;	R1/	ADDR OF DESTINATION STRING
;	JSR	PC,NSPBLB

NSPBLB:	MOVB	(R0)+,(R1)+	;MOVE FIRST/NEXT BYTE
	BMI	NSPBLB		;LOOP WHILE EXTENSIBLE BIT IS SET
	RTS	PC		;RETURN HAVING COPIED STRING
;ROUTINE TO CALL REST OF CALLER FOR EACH NSP LOGICAL LINK IN WE KNOW
;OPERATION IS BY RUNNING DOWN THE LIST OF LEBS FOUND IN THE LIST OF LINE BLOCKS
;AND PASSING THEM TO THE CALLER OF THIS ROUTINE. WHEN THE SEARCH IS
;COMPLETE WE RETURN TO THE CALLER'S CALLER TO AVOID PASSING A BAD LEB TO THE
;CALLER. NOTE THAT THERE IS NO WAY TO CALL THIS WITH DATA SAVED
;ON THE STACK.

NSPALL:	SAVE	<J,SB>
	MOV	#FRSTLB,J	;HAVE TO SCAN ALL LINE BLOCKS
10$:	MOV	LB.LNK(J),J	; TO FIND LEBS THAT CALLER MAY HAVE TO PROCESS
	BEQ	90$		;NO MORE LINE BLOCKS
	MOV	LB.LEB(J),R1	;GET LEB LIST FOR THIS LINE BLOCK
	BEQ	10$		;NO LIST
	MOV	J,NSPLB		;ROUTINES WE CALL LOOK IN MEMORY
20$:	MOV	R1,NSPLEB	;ROUTINES LOOK HERE
	JSR	PC,@4(SP)	;CALL CALLER
	MOV	NSPLEB,R1	;R1 PROBABLY GOT CLOBBERED...
30$:	MOV	LE.LNK(R1),R1	;STEP TO NEXT LEB
	BNE	20$		;LOOP IF MORE TO LOOK AT
	MOV	NSPLB,J		;J PROBABLY GOT CLOBBERED TOO
	BR	10$		;LOOP ON LINE BLOCKS

90$:	RESTORE	<SB,J>		;CALLER NEEDS THESE
	TST	(SP)+		;REMOVE CALLER'S RETURN ADDRESS
	RTS	PC		;RETURN TO CALLER'S CALLER
	.SBTTL		LEB (LINK ENTRY BLOCK) CONTROL

;ROUTINE TO MATCH THE PASSED NSP DSTADDR OR NCL DLA  WITH ONE OF THE ACTIVE
;LEBS. THE LEB NUMBER IS EXTRACTED FROM THE LINK ADDRESS (SEE NSPALB FOR FORMAT)
;AND IS CONVERTED INTO THE LEB ADDRESS. IF THE LEB IS NOT ASSIGNED, WHICH
;GENERALLY MEANS THAT THE SENDER IS CONFUSED, IT WILL BE ASSIGNED AND THE
;CALLER WILL DECIDE WHETHER TO IGNORE IT OR RETURN A DISCONNECT. IF THE LEB
;IS ALREADY ASSIGNED, WE VERIFY THAT THE COMPLETE ADDRESS MATCHES AND THAT
;THE NCL SCBS MATCH BEFORE RETURNING THE LEB TO THE CALLER. IF THE MATCH FAILS
;OR IF THE LEB IS NOT ADDRESSIBLE, WE RETURN THE MAGIC LEB SO THAT MESSAGE
;PROCESSING CAN CONTINUE EVEN THOUGH WE MAY NOT BE ABLE TO REPLY.
;CALL:
;	R0/ DLA OR DSTADDR
;	JSR	PC,NSPSLB
;	R1/ LEB ADDRESS MATCHED OR ASSIGNED
;	NSPLEB/ DITTO

NSPSLB:	SAVE	<R0>		;SAVE OUR SIDE LINK ADDRESS
	MOV	#LEB1-LE.SIZ,R1	;CAN'T DO A MULTIPLY, SO LETS ADD A LOT
	BIC	#^C<LED.OF>,R0	;EXTRACT LEB NUMBER
	BEQ	40$		;ZERO ISN'T LEGAL
10$:	ADD	#LE.SIZ,R1	;STEP TO NEXT
	SOB	R0,10$		;COUNT SOME MORE
	TST	LE.SCB(R1)	;ASSIGNED?
	BEQ	50$		;NO, TRY TO ASSIGN IT (CALLERS WILL HANDLE)
	CMP	@SP,LE.DCP(R1)	;ADDRESSES BETTER MATCH
	BNE	40$		;CAN'T HANDLE, RETURN MAGIC LEB
	TST	NSPSB		;KNOW WHO NCL END IS YET?
	BEQ	30$		;NO, USE ADDRESS IN LEB
	CMP	LE.SCB(R1),NSPSB ;NCL SIDES MATCH?
	BNE	40$		;NO, CAN'T HANDLE, RETURN MAGIC
30$:	MOV	LE.SCB(R1),NSPSB ;ASSUME THIS IS NCL SIDE
	TST	DNA		;HAVE WE FIGURED OUT A DESTINATION YET?
	BNE	60$		;YES
	MOV	LE.SCB(R1),DNA	;HERE IF PROCESSING A NSP MESSAGE (NCL
				; MESSAGES ALWAYS HAVE ROUTING HEADERS)
				; THAT DOES NOT HAVE A ROUTING HEADER. THEREFORE
				; THE DESTINATION MUST BE THE NCL SIDE. THIS
				; INSTRUCTION IS USUALLY HIT FOR DATA MESSAGES
				; AND ITS RELATIVES
	BR	60$		;CLEANUP AND RETURN

40$:	MOV	#LEBMGC,R1	;HAVE TO RETURN MAGIC LEB
50$:	JSR	PC,NSPAL1	;ASSIGN LEB
	MOV	@SP,LE.DCP(R1)	;USE MESSAGE'S ADDRESS FOR OURS
60$:	MOV	R1,NSPLEB	;CALLERS NEED THIS HERE
	RESTORE	<R0>		;RECOVER TRUE LINK ADDRESS
	RTS	PC
;ROUTINE TO ALLOCATE A FREE LEB TO A NEW LOGICAL LINK. CALLED BY THE CONNECT
;INITIATE PROCESSERS, THIS ROUTINE SEARCHES FOR A FREE LEB, CLEARS OUT OLD
;INFORMATION AND FILLS IN WHAT IT CAN INCLUDING A DCP LINK ADDRESS. THIS THIRD
;ADDRESS SERVES TWO PURPOSES. FIRST, IF WE PASSED NCL LINK ADDRESSES TO THE
;NSP NODE, A CASE COULD ARISE WHERE TWO LINKS COULD SHARE THE SAME NCL LINK
;ADDRESS BY TERMINATING IN DIFFERENT NCL NODES. THIS INTRODUCES AMBIGUITIES
;IF WE RECEIVE A MESSAGE FROM NSP WITHOUT A ROUTING HEADER FOR ONE OF THESE
;LINKS. THE DCP LINK ADDRESS SOLVES THE PROBLEM BY GUARANTEEING A UNIQUE
;LINK ADDRESS FOR EACH LINK IN USE. SECOND, THE LINK ADDRESS CAN BE MADE
;TO FIT THE RESTRICTIONS OF BOTH NCL AND NSP, I. E. A LOW REUSE RATE FOR NSP
;AND A 14 BIT LENGTH FOR NCL. THE HIGH 2 BITS ARE 0, THE NEXT N INCREMENT
;EACH TIME THAT LEB IS ASSIGNED, AND THE LOW 14-N BITS ARE THE LEB NUMBER. IF
;ALL LEBS ARE IN USE, THE MAGIC LEB IS RETURNED BUT NOT LINKED INTO A LBLK
;LIST. THIS IS SO IT IS ALWAYS AVAILABLE TO BE RETURNED. THE PURPOSE OF
;THE MAGIC LEB IS TO PROVIDE A PIECE OF LINK DATA BASE TO ANY CALLER SO IT
;CAN CONTINUE TO PROCESS THE MESSAGE AS NEARLY LIKE ANY OTHER MESSAGE AS
;POSSIBLE.

NSPALB:	MOV	#LEB1,R1	;TRY TO FIND A FREE LEB
10$:	TST	LE.SCB(R1)	;FREE?
	BEQ	NSPAL1		;YES, USE IT
	ADD	#LE.SIZ,R1	;STEP TO NEXT
	CMP	R1,#LEBEND	;TOO FAR?
	BLO	10$		;NO, TRY NEXT
	MOV	#LEBMGC,R1	;ALL IN USE, RETURN THE MAGIC LEB
NSPAL1:	JSR	PC,NSPZLB	;CLEAR OUT WHOLE THING
	CMP	R1,#LEBMGC	;MAGIC LEB?
	BEQ	20$		;YES, DON'T LINK IT
	SAVE	<J>		;HAVE TO PRESERVE J HERE
	ADD	#LED.IN,LE.DCP(R1) ;CREATE NEXT FREE LINK ADDRESS
	BIC	#140000,LE.DCP(R1) ;RESTRICT RANGE FOR NCL (NETSER'S) SAKE
	MOV	NSPLB,J		;HAVE TO REFERENCE LBLK
	MOV	LB.LEB(J),LE.LNK(R1) ;PUT OLD LEB LIST AFTER NEW GUY
	MOV	R1,LB.LEB(J)	;MAKE NEW LIST WITH NEW GUY AT HEAD
	RESTORE	<J>
20$:	MOV	NSPSB,LE.SCB(R1) ;TRY TO ASSIGN IT
	BNE	30$		;OKAY IF WE ASSIGNED IT
	COM	LE.SCB(R1)	;DON'T KNOW THE NCL SIDE YET, MAKE IT NONZERO ANYWAY
30$:	MOV	R1,NSPLEB	;SAVE FOR FUTURE USERS
	CMP	R1,#LEBMGC+1	;SET CARRY IF MAGIC LEB WITH A MAGIC INSTRUCTION
	RTS	PC		; ADDRESS OF CMP INSTRUCTION FROM STACK
;ROUTINE TO FREE A LEB WE NO LONGER NEED. THIS ROUTINE FREES THE LEB
;CURRENTLY IN USE. (PROBABLY THE LAST ON NSPSL? RETURNED). IT ALSO FREES
;ANY MESSAGES STILL ON THE OUTPUT BUT NOT ACKNOWLEDGED QUEUES SINCE OTHERWISE
;THEIR SPACE WILL BECOME DEALLOCATED.

NSPFLB:	SAVE	<R0>		;A COUPLE CALLERS REALLY WANT THIS INTACT
	MOV	NSPLEB,R1	;GET LEB EVERYONE'S BEEN USING
	MOV	NSPLB,R0	;MAKE A PSEUDO LEB POINTER
	ADD	#LB.LEB-LE.LNK,R0 ; SO LOOP CAN HANDLE IT
10$:	CMP	LE.LNK(R0),R1	;IS NEXT LEB THE ONE WE'RE FREEING?
	BEQ	20$		;YES, DIDDLE LINKS
	MOV	LE.LNK(R0),R0	;NOT YET, STEP TO NEXT
	BNE	10$		;KEEP LOOKING
	CMP	R1,#LEBMGC	;MAGIC LEB?
	BEQ	30$		;NO WONDER WE DIDN'T FIND IT
	STOPCD	DCP		;SHOULDN'T GET HERE

20$:	MOV	LE.LNK(R1),LE.LNK(R0) ;REMOVE LEB FROM LIST
30$:	MOV	LE.OQL(R1),R0	;GET LIST OF UNACKED LS/INT MSGS
	JSR	PC,NSPFL0	;AND FREE THEM
	MOV	LE.OQD(R1),R0	;ALSO FREE UNACKED DATA MESSAGES
	JSR	PC,NSPFL0
	CLR	LE.SCB(R1)	;LET SOMEONE ELSE GET LEB
	RESTORE	<R0>
	RTS	PC


;ROUTINE TO RELEASE LIST OF MESSAGES
NSPFL0:	BEQ	10$		;NOTHING TO DO IF NONE ALREADY
	MOV	CN.MLK(R0),-(SP) ;SAVE ADDRESS OF NEXT
	JSR	PC,FRECKS	;RELEASE FIRST MSG
	MOV	(SP)+,R0	;GET NEXT
	BNE	NSPFL0		;RELEASE IT TOO
10$:	RTS	PC


;ROUTINE TO CLEAR OUT THE LEB POINTED AT BY R1. MAINLY CALLED BY NSPSL? TO
;CLEAR A LEB BEFORE RETURNING IT TO THE CALLER.

NSPZLB:	SAVE	<LE.DCP(R1),R1>
	MOV	#<<LE.SIZ+1>/2>,R0 ;NUMBER OF WORDS TO STUFF
10$:	CLR	(R1)+
	SOB	R0,10$
	RESTORE	<R1,LE.DCP(R1)>
	RTS	PC
	.SBTTL		LEB QUEUING ROUTINES

;THESE ROUTINES ARE CALLED WHENVER A PIECE OF CODE WANTS SOMETHING PERFORMED
;AT LOW LEVEL, USUALLY SOMETHING THAT NEEDS VERY LITTLE DATA ASSOCIATED WITH
;WITH THE ACTION AND MUST BE DONE SOMETIME, EVEN IF THERE IS NO MEMORY
;AVAILABLE NOW. EACH ACTION HAS ITS OWN REQUEST BIT AND THE QUEUING MECHANISM
;HAS A GLOBAL BIT TO KEEP LEBS QUEUED ONLY ONCE (EXACTLY LIKE ALL THE OTHER
;CIRCULAR QUEUES IN THE DN8X). BITS ARE CHECKED BY THE NSPCHK ROUTINE AND IF
;THE ACTIONS CAN BE PERFORMED, THE BITS WILL BE TURNED OFF. SOME BITS,
;NOTABLY THE MESSAGE ACKNOWLEDGEMENT BITS, ARE TURNED OFF IF THERE IS NO NEED
;TO PERFORM THE FUNCTION ANY MORE.
;ROUTINES:
;NSPQDA	REQUEST A DATA ACK BE SENT
;NSPQDN		REQUEST A DATA NAK BE SENT
;NSPQLA		REQUEST A LS/INT ACK BE SENT
;NSPQLN		REQUEST A LS/INT NAK BE SENT
;NSPQDR		REQUEST ONE MORE DATA REQUEST BE SENT TO THE NCL NODE. THIS IS
;		IS USED BY CODE THAT IMPLEMENTS MESSAGE REQUESTING AND NO REQUESTING
;		NSPCNR IS SPECIAL CODE TO SEND INITIAL DRQS.
;NSPQDC		REQUEST A DC MESSAGE BE SENT TO THE NSP NODE. R0 SHOULD BE SETUP
;		WITH A PLAUSIBLE REASON CODE WHICH WILL BE USED IF ONE HASN'T
;		ALREADY BEEN STORED IN THE LEB.
;NSPQLS		THE CODE TRIES TO KEEP THE NSP SIDE ALWAYS READY TO SEND ANOTHER
;		INTERRUPT MESSAGE IF IT SO DESIRES. THIS IS CALLED EVERYTIME
;		WE RECEIVE AN INTERRUPT MESSAGE TO REQUEST ANOTHER.

	.ENABL	LSB
NSPQDA:	MOV	#LES.DA,R0	;TO SEND A DATA ACK
	BR	10$		;SET BIT AND QUEUE LBLK

NSPQDN:	MOV	#LES.DN,R0	;DATA NAK
	BR	10$

NSPQLA:	MOV	#LES.LA,R0	;LS/INT ACK
	BR	10$

NSPQLN:	MOV	#LES.LN,R0	;LS/INT NAK
	BR	10$

NSPCNR:	BIT	#LES.NR,@R1	;IS THIS LINK USING NO REQUESTING?
	BEQ	99$		;USING SEGMENT OR MESSAGE REQUESTS, SKIP THIS
	INCB	LE.ODR(R1)	;START LINK OFF WITH 2 DATA REQUESTS
NSPQDR:	INCB	LE.ODR(R1)	;INCREMENT NUMBER OF FREE DATA REQS WE HAVE TO SEND
	MOV	#LES.DR,R0	;THEN REMIND LOW LEVEL TO REQUEST THEM
	BR	10$

NSPQDC:	MOV	NSPLEB,R1	;CALLERS EXPECT THIS BACK
	TSTB	LE.RSN(R1)	;HAS A REASON ALREADY BEEN SPECIFIED?
	BNE	5$		;YES, DON'T USE NEW ONE
	MOVB	R0,LE.RSN(R1)	;SAVE IT
5$:	MOV	#LES.DC,R0	;REMINDER FOR LOW LEVEL
	BR	10$

NSPQLS:	MOV	#LES.LS,R0	;LINK SERVICES
10$:	BIS	R0,@NSPLEB	;LIGHT BIT IN LEB
NSPQ:	SAVE	<J,R2>		;SAVE REGS
	MOV	NSPLB,J		;POINT TO LINE BLOCK
	PIOFF			;TURN OFF INTERRUPTS
	BIT	#LBS.NQ,LB.NSS(J) ;ALREADY SOMETHING QUEUED?
	BNE	30$		;YES, EXIT
	BIS	#LBS.NQ,LB.NSS(J) ;NO, INDICATE THAT NOW THERE IS
	CMP	NS.PTR,#<NS.QUE+NS.SIZ> ;IS PUTTER PAST END?
	BLO	20$		;NO, BRANCH
	MOV	#NS.QUE,NS.PTR	;YES, RESET TO BEGINNING
20$:				;HERE TO ACTUALLY PUT DATA INTO QUEUE
	MOV	J,@NS.PTR	;LINE BLOCK ADDRESS AT PUTTER
	ADD	#2,NS.PTR	;ADVANCE PUTTER
30$:				;HERE WHEN DONE
	PION			;ALLOW INTERRUPTS AGAIN
	RESTORE	<R2,J>		;RESTORE REGS
99$:	RTS	PC		;RETURN
	.DSABL	LSB
	.SBTTL		DEBUGGING ROUTINES

.IF NE FTDCPT			;ASSEMBLE IF WANT MESSAGE TRACE
;THESE ROUTINES ARE MEANT FOR DEBUGGING ONLY AND SHOULD NOT BE USED DURING
;NORMAL OPERATION AS THEY CAN WIPE OUT FREE MEMORY IN NOTHING FLAT.
;CONTROLLED BY THE CONTENTS OF NSPTFG, THEY WILL PRINT MESSAGES RECEIVED AS FOLLOWS:
;BIT		MESSAGE CASE
;1		NSP RECEPTION
;2		NSP TRANSMIT DONE (AT NSPOBD, NOT DDQNSP!)
;4		NCL RECEPTION
;10		NCL TRANSMISSION
;20		BAD MESSAGES.

NSPBTP:	BIT	#25,@NSPFGA	;ONLY IF INTERESTED IN NSP/NCL INPUT
	BEQ	NSPTYR
	JSR	R1,NSPTYP
	.ASCIZ	\NSPBTP CALLED \
	.EVEN

NSPITP:	BIT	#1,@NSPFGA	;INPUT BIT ON IN FLAG WORD?
	BEQ	NSPTYR
	JSR	R1,NSPTYP
	.ASCIZ	\NSP RCV \
	.EVEN

NSPOTP:	BIT	#2,@NSPFGA	;OUTPUT BIT ON?
	BEQ	NSPTYR
	JSR	R1,NSPTYP
	.ASCIZ	\NSP XMT \
	.EVEN

NSPITL:	BIT	#4,@NSPFGA
	BEQ	NSPTYR
	JSR	R1,NSPTYP
	.ASCIZ	\NCL REC \
	.EVEN

NSPOTL:	BIT	#10,@NSPFGA
	BEQ	NSPTYR
	JSR	R1,NSPTYP
	.ASCIZ	\NCL XMT \
	.EVEN
;STILL IN .IF NE FTDCPT
NSPTYP:	SAVE	<R0,R2,R3,R4,R5>
	MOV	#CT0DDB,J	;OUTPUT TO CTY
	JSR	PC,NSPCRL	;START OFF WITH A BLANK LINE
10$:	MOVB	(R1)+,R0
	BEQ	20$
	JSR	PC,QTYCHR
	BR	10$

20$:	MOV	10(SP),R0
	MOV	CN.LEN(R0),R2	;SETUP GET POINTERS
	MOV	R0,R3		;COPY MESSAGE START
	ADD	#CN.NCT,R3	;POINT TO FIRST BYTE
30$:	JSR	PC,GETBYT	;GET A CHAR
	SAVE	<R2,R3>		;DISTRUST QTYCHR
	MOV	#214,R1		;MAGIC NUMBER
	SEC
40$:	ROLB	R0		;GET A BIT
50$:	ROLB	R1		;PUT IN CHAR
	BCS	60$		;DO IT AGAIN
	SAVE	<R0>		;SAVE REST OF BYTE
	MOV	R1,R0		;PUT WHERE QTYCHR EXPECTS
	JSR	PC,QTYCHR	;PUT IN CTY BUFFER
	RESTORE	<R0>		;RECOVER REST OF BYTE
	MOV	#306,R1		;MORE MAGIC
60$:	ASLB	R0		;TIME TO STOP?
	BEQ	70$		;YES, DO NEXT BYTE
	BR	50$		;NOT YET

70$:	MOV	#40,R0		;NEED SPACE BETWEEN BYTES
	JSR	PC,QTYCHR
	RESTORE	<R3,R2>
	BNE	30$		;LOOP WHILE MORE TO DO
	JSR	PC,NSPCRL	;AND END WITH CRLF
	JSR	PC,BEGXMT	;START OUTPUT
	RESTORE	<R5,R4,R3,R2,R0,R1>
NSPTYR:	RTS	PC

NSPCRL:	SAVE	<R2,R3>
	MOV	#15,R0
	JSR	PC,QTYCHR
	MOV	#12,R0
	JSR	PC,QTYCHR
	RESTORE	<R3,R2>
	RTS	PC
.ENDC ;.IF NE FTDCPT
	.SBTTL		NSPDAT	WORKING DATA AREAS

;WORD DATA:
NSPADR:	.BLKW	1		;ADDRESS OF COUNT FIELD TO FILL  WHEN FINISHED WITH MESSAGE
NSPCNT:	.WORD	0		;COUNT OF NCL SUBMESSAGE
NSPPMG:	.WORD	0		;ADDRESS OF MESSAGE FOR NSP
NSPLMG:	.BLKW	1		;ADDRESS OF MESSAGE FOR/FROM NCL
NSPLEB:	.BLKW	1		;ADDR OF LEB FOR THE LINK FOR CURRENT MSG
NSPLIX:	.BLKW	1		;LOC OF LE.LID OR LE.LIL TO INCREMENT AFTER NSP PROCESSING
NSPLB:	.WORD	0		;ADDRESS OF LINE BLOCK FOR DCP LINE
NSPSP:	.BLKW	1		;SP TO RESTORE IF WE FIND MSG FORMAT ERROR
NSPSB:	.BLKW	1		;NCL NODE ASSOCIATED WITH MESSAGE
NSPOQL:	.BLKW	2		;QUEUE HEADER OF NCL MESSAGES WAITING TO BE TRANSLATED.
				; THE FIRST WORD IS A LIST HEAD, THE SECOND POINTS
				; TO THE LAST CHUNK IN THE LIST, IF ANY.
.IF NE,FTDCPT
NSPFGA:	.WORD NSPTFG		;PATCH TO 177570 FOR SWITCHES
NSPTFG: .WORD FTDCPT		;PATCH THIS TO BE WHICH MESSAGES YOU WANT LOGGED, OR
				; PATCH NSPFGA TO POINT TO THE CONSOLE SWITCHES
				; IF YOU HAVE A PDP-11/40 AND YOU CAN CONTROL WHAT
				; PRINTS WITH THEM. (SWITCH REG IS LOC 177570)
.ENDC;.IF NE FTDCPT

;BYTE DATA:
NSPLPN:	.BLKB	12.		;TEMPORARY STORAGE FOR PROCESS NAME
NSPDNA:	.BLKB	SNMSIZ		;DESTINATION OF NSP MESSAGE
NSPSNA:	.BLKB	SNMSIZ		;SOURCE OF NSP MESSAGE
NSPDAP:	.BLKB	1		;TYPE DAP TO SEND TO NCL

	.EVEN

.ENDC;.IF NE FTDCP3