Google
 

Trailing-Edge - PDP-10 Archives - BB-F492Z-DD_1986 - 10,7/703anf/dnnsp3.p11
There are 3 other files named dnnsp3.p11 in the archive. Click here to see a list.
.SBTTL	DECNET COMPATIBLE PORT FOR NSP  18 NOV 81
.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=013			;FILE EDIT 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 TO 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
.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

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 INT 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	DPN		;DESTINATION DEVICE THAT NCL IS ASKING ABOUT

	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 NSP3,A,B,NOD
	.IRP	Q,<B>
		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.RTE	=1		;ROUTE THROUGH

;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
	JSR	PC,NSPBMG	;WE WILL ALWAYS NEED A NSP MESSAGE
	BCS	40$		;NO MEMORY - DEFER UNTIL THERE IS SOME
	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
	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 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
.IIF NE FTDCPT,JSR PC,NSPBTP	;NOTE WE DISLIKED IT
	MOV	NSPSP,SP	;RESTORE STACK TO NSPINP LEVEL
NSPIFL:	MOV	NSPPMG,R0	;CHANGE THIS TO POSSIBLY
	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.

NSPIRT:	BIT	#NM.RFMT/4,R0	;ASCII ROUTING HEADERS?
	BEQ	10$		;BINARY - CAN'T HANDLE!
	BIT	#NM.EFMT/4,R0	;WHICH VERSION OF ROUTING FORMAT?
	BNE	10$		;ONLY 0 IS DEFINED
	MOV	#NSPDNA,R0	;POINT TO DESTINATION STRING
	JSR	PC,NSPIR1	;FIND DESTINATION NODE
	MOV	SB,DNA		;SAVE IT
	MOV	R0,DNAOFF	;AND OFFSET IF IT IS SEQUENTIAL
	MOV	#NSPSNA,R0	;DO SAME WITH SOURCE NODE
	JSR	PC,NSPIR1	;FIND SOURCE NODE
	MOV	SB,SNA
	MOV	R0,SNAOFF
	RTS	PC		;DONE

10$:	NSPIBD

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
	.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
	BIC	#^C77,R0	;LIMIT TO 6 BITS (ALA REST OF OUR NET)
	BNE	20$		;STILL SOMETHING LEFT, USE IT
	MOVB	LB.VNN(J),R0	;MUST BE AN END NODE, GET USER SUPPLIED NUMBER
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
	MOV	SB,R0		;GET ADDR OF SCB FOR NSP NODE
	ADD	#SB.SNM,R0	;POINT TO NAME AREA
	JSR	PC,NSPIIM	;COPY IMAGE DATA
	.WORD	SNMSIZ		;MAXIMUM AMOUNT TO COPY
	JSR	PC,GETEXN	;DISCARD FUNCTIONS
	JSR	PC,GETEXN	;GET REQUESTS
	BIT	#NM.RNT!NM.LNT,R0 ;IS OTHER END A LEVEL 1 IMPLEMENTATION?
	BNE	45$		;NO, NOTHING SPECIAL TO DO
	BIS	#LBS.L1,LB.NSS(J) ;MARK IT (ONLY USE IS TO NOT TIME OUT MSGS)
45$:	MOV	#6,R1		;DISCARD A LOT OF THE MESSAGE
50$:	JSR	PC,NSPI2B	; NAMELY MSG LENGTHS, MAX LINKS, AND VERSIONS
	SOB	R1,50$		;(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,NSPBM1	;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	#OURNNM,R0	;OUR NODE NUMBER
	JSR	PC,NSPPEX	;TELL HIM WHO WE ARE
	MOV	#OURSCB+SB.SNM,R1 ;GET ADDR OF OUR NAME
	JSR	PC,NSPOIM	;COPY TO MESSAGE
	MOV	#NM.RNT!NM.LNT!NM.RTE,R0;PROVIDE ALL BUT UNBLOCKING
	JSR	PC,NSPPEX
	CLR	R0
	JSR	PC,NSPPEX	;AND ASK FOR NOTHING
	MOV	#MSGMAX,R0	;MAX 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
70$:	JSR	PC,NSPO2B	;PUT INTO MESSAGE
	MOV	#2,R2		;ONCE FOR COMM VER AND ONCE FOR ROUTE VER
80$:	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,80$		;BACK TO DO ROUT VER
	MOV	#OURSCB+SB.SID,R1 ;GET ADDR OF OUR NODE IDENT
	JSR	PC,NSPOIM	;COPY IT TO MESSAGE

	MOV	NSPLB,J		;MESSAGE COMPLETE, GET LBLK BACK
	MOV	NSPPMG,R0	;GET FIRST CHUNK BACK
	MOV	R4,CN.LEN(R0)	;SET LENGTH FOR DDCMP OUTPUT
	MOV	LB.SCB(J),SB	;POINT TO SCB AGAIN
	JSR	PC,DDQNSP	;SHIP IT OUT !
	MOV	#LBS.IC,LB.NSS(J);SET LINE BLOCK IN CONTACT
	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
	.SBTTL		NSPICI AND NSPICC  NSP CONNECT MESSAGES

;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	60$		;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	40$		;CAN'T USE IT IF WE GOT THE MAGIC LEB
30$:	MOV	R1,NSPLEB	;IN CASE WE FOUND ON ON THE LIST
	TST	NSPSB		;DO WE KNOW DESTINATION YET?
	BEQ	50$		;NO, BE UPSET
	MOVB	LE.STT(R1),R0	;GET STATE WE'RE IN
	JSR	PC,@NSPCIX(R0)	;AND CALL APPROPRIATE ROUTINE
	RTS	PC

40$:	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 TO DEST. (AT LEAST TO US!)
	JSR	PC,NSPO2B	;FINISH DC MESSAGE
	JMP	NSPSNS		;SEND IT

50$:	JSR	PC,NSPFLB	;WE DON'T KNOW DEST, CAN'T KNOW SOURCE, FREE LEB
60$:	NSPIBD			;(ACTUALLY, PROBABLY SHOULD RETURN DC)


NSPCIX:	.WORD	NSPCI0		;A NEW LINK IS BEING SETUP
	.WORD	NSPERP		;WE DON'T HAVE A NCL LINK, WE SHOULD NEVER GET HERE
	.WORD	NSPCI0		;SEND ANOTHER CONNECT FOR THIS DSTADDR
	.WORD	NSPIFL		;ASSUME NSP TIMED OUT AFTER NCL CONNECTED
	.WORD	NSPIFL		;ASSUME SAME AFTER NCL DISCONNECTED
;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,@NSPCCX(R0)	;CALL PROCESSOR
	RTS	PC

NSPCCX:	.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
	.ENABL	LSB
NSPCC0:	JSR	PC,NSPI2B	;GET SRCADDR
	MOV	R0,LE.NSP(R1)	;NEW INFO, SAVE IT
	MOVB	#LES.RN,LE.STT(R1) ;ENTER RUN STATE
	BR	20$		;JOIN COMMON CODE

NSPCI0:	MOVB	#LES.PI,LE.STT(R1) ;ENTER PI (NSP INITTING LINK) STATE
20$:	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?
	BNE	NSPRCN		;DISALLOW
	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
	BNE	NSPRCN		;NOT LOGICAL LINK, CAN'T HANDLE
	JSR	PC,GETEXN	;GET INFO AND DISCARD
	JSR	PC,NSPI2B	;GET SEGSIZE
	MOV	R0,-(SP)	;SAVE IT SINCE MML IS A WAYS AWAY
	TST	LE.NCL(R1)	;MAKING A CONNECT CONFIRM?
	BNE	30$		;YES, FUDGE DPN AND SPN
	JSR	PC,NSPIC1	;GET DSTNAME, PUT DPN
	JSR	PC,NSPIC1	;GET SRCNAME, PUT SPN
	BR	40$		;CONTINUE WITH MML
;HERE ON A CONNECT CONFIRM, FAKE THE DPN AND SPN FIELD IN THE NCL MSG
30$:	MOV	#OBJTSK+<0*400>,R0 ;NSP DIDN'T SEND US ALL THE DATA WE NEED TO
				; PROPERLY BUILD THE DPN AND SPN FIELDS.
				; HOWEVER, SINCE NETSER IGNORES THEM ON A CONNECT
				; CONFIRM, PUT IN NULL FIELDS AND EVERYTHING
				; SHOULD WORK!
	JSR	PC,NSPO2B	;PUT DPN
	BIT	#LES.DV,LE.STS(R1);IF IT WAS A DEVICE CONNECT
	BEQ	35$		;NO - CONTINUE
	MOV	LE.DPN(R1),R0	;DEVICE CONNECT - GET CONNECT TYPE
35$:
	JSR	PC,NSPO2B	;PUT SPN
	JSR	PC,NSPCNR	;SEE IF A NO SEGMENT REQUEST CONNECTION HAS BEEN SET UP
40$:	MOV	@SP,R0		;RECOVER SEGSIZE
	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

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
;ROUTINE CALLED BY NSPICI AND NSPICN TO COPY NAME FIELDS

NSPIC1:	JSR	PC,GETBYT	;GET FORMAT OF NAME FIELD
	MOV	R0,R1		;SAVE FOR AFTER OBJTYPE TEST
	JSR	PC,GETBYT	;GET OBJTYPE
	TST	R0		;0 IS GENERAL TASK
	BNE	5$		;DISALLOW ALL OTHERS
	MOV	#OBJTSK,R0	;ALWAYS CONNECT TO A TSK
	JSR	PC,NSPPBY	;PUT OBJTYPE
	DEC	R1		;ONLY ALLOW 1 AND 2
	BEQ	10$		;1: (OBJTYPE, DESCRP)
	DEC	R1
	BEQ	40$		;2: (OBJTYPE, GRPCODE, USRCODE, DESCRPT)
5$:	MOV	NSPLEB,R1	;POINT TO LEB AGAIN
	MOVB	#NM.IPN,LE.RSN(R1) ;TO SAY INVALID DESTINATION
	JSR	PC,NSPERL	;SEND A DC BACK
	NSPIBD

10$:	JSR	PC,GETBYT	;GET LENGTH OF DESCRP
	MOV	R0,R1		;COPY TO SAFE AC
	BEQ	70$		;NOTHING! JUST STORE 0
20$:	JSR	PC,GETBYT	;GET A BYTE OF IT
	CMP	R1,#1		;LAST CHARACTER?
	BEQ	30$		;YES, DON'T MAKE EXTENSIBLE
	BIS	#200,R0		;CONVERT TO EXTENSIBLE
30$:	JSR	PC,NSPPBY	;PUT IN NCL MESSAGE
	SOB	R1,20$		;DO REST
	RTS	PC		;DONE WITH NAME FIELD FOR FORMAT 1

40$:	JSR	PC,NSPI2B	;GET GRPCODE
	MOV	R0,-(SP)	;SAVE FOR AFTER DESCRPT
	JSR	PC,NSPI2B	;GET USRCODE
	MOV	R0,-(SP)	;SAVE THAT TOO
	JSR	PC,GETBYT	;GET LENGTH OF DESCRPT
	MOV	R0,R1		;COPY TO SAFE AC
	BEQ	60$		;NOTHING! JUST STORE PPN
50$:	JSR	PC,GETBYT	;GET NEXT BYTE OF NAME
	BIS	#200,R0		;MAKE ALL BYTES EXTENSIBLE
	JSR	PC,NSPPBY	;PUT INTO DPN OR SPN
	SOB	R1,50$		;LOOP OVER REST OF NAME
60$:	MOV	#'[!200,R0	;FOR START OF PPN FIELD
	MOV	2(SP),R1	;RECOVER GRPCODE
	JSR	PC,NSPIC2	;MAKE "[10"
	MOV	#',!200,R0	;SEPARATE WITH COMMA
	MOV	(SP)+,R1	;RECOVER USRCODE
	JSR	PC,NSPIC2	;PUT INTO MESSAGE
	TST	(SP)+		;DISCARD GRPCODE
	MOV	#'],R0		;TERMINATE PPN AND EXTENSIBLE STRING
70$:	JMP	NSPPBY
;ROUTINE CALLED ONLY BY NSPIC1 TO STORE PART OF A PPN

NSPIC2:	JSR	PC,NSPPBY	;PUT OPEN BRACKET OR COMMA ON NAME
10$:	MOV	R1,R0		;PUT NUMBER TO BE CONVERTED IN R0
	BIC	#^C7,R0		;ISOLATE LAST DIGIT
	CLC			;TO ENSURE WE ONLY GET VISIBLE BITS
	ROR	R1		;REMOVE LAST DIGIT FROM R1
	 ASR	 R1
	  ASR	  R1
	BEQ	20$		;REACHED FIRST DIGIT, PUT DATA IN MESSAGE
	MOV	R0,-(SP)	;PROTECT DIGIT TO BE PRINTED
	JSR	PC,10$		;LOOP OVER NUMBER
	MOV	(SP)+,R0	;RECOVER DIGIT
20$:	BIS	#'0!200,R0	;ASCIIZE AND EXTENSIBLIZE IT
	JMP	NSPPBY		;STUFF IN MESSAGE
	.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	NSPFLB		;NOTHING TO DO BUT IGNORE IT
	.WORD	NSPID0		;CONVERT DC INTO DISC
	.WORD	NSPFLB		;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


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.IPN,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
.IIF NE FTDCPT,JSR PC,NSPBTP	;NOTE IF ANYONE'S INTERESTED
	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	#NSP$MX/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
;ROUTINE TO PROCESS NCL CONNECT MESSAGES. NOTE THAT NCL USES THE SAME
;MESSAGE TYPE FOR CONNECT INITS AND CONNECT CONFIRMS, SO THIS ROUTINE
;HAS TO DETERMINE WHICH TO SEND. IN THE CASE OF A CONNECT INIT, A DISCONNECT
;IS RETURNED IF ALL NORMAL LEBS ARE IN USE. IN EITHER CASE, THE NSP MESSAGE
;GENERATED WILL REQUEST SEGMENT DATA REQUESTS SINCE THEY HAVE A ONE TO ONE
;CORRESPONDENCE WITH NCL'S DATA REQUEST MECHANISM.

NSPOCN:	JSR	PC,NSPBMG	;BUILD NSP AS WE DECODE NCL
	BCS	70$		;FORGET IT IF COULDN'T GET SPACE
	JSR	PC,NSPORT	;PUT RTHDR
	JSR	PC,GETEXN	;GET DLA
	MOV	R0,-(SP)	;SAVE WHILE WE DECIDE WHAT SORT MSG
	BEQ	10$		;ZERO - MEANS CONNECT INIT
	MOV	#NM.CC-NM.CI,R0	;THIS WILL BECOME A CONNECT CONFIRM
10$:	ADD	#NM.CI,R0	;MAKE MSGFLG FOR INIT OR CONF
	JSR	PC,NSPPBY	;PUT MSGFLG
	MOV	(SP)+,R0	;RECOVER DLA
	BEQ	20$		;CONNECT INIT
	JSR	PC,NSPSLB	;FIND LEB, DLA IS KNOWN DCP LINK ADDR
	CMPB	LE.STT(R1),#LES.PI ;ONLY ALLOW CONNECT CONFIRM WHEN EXPECTING IT
	BNE	60$		;OTHERWISE DISALLOW
	MOV	LE.NSP(R1),R0	;RECOVER THE NSP LINK ADDRESS
	JSR	PC,NSPO2B	;PUT DSTADDR
	JSR	PC,GETEXN	;GET SLA
	MOV	R0,LE.NCL(R1)	;SAVE NCL'S LINK ADDRESS
	MOVB	#LES.RN,LE.STT(R1) ;ENTER RUN STATE
	JSR	PC,NSPCNR	;CHECK TO SEE IF THIS IS A NO SEGMENT REQUEST NODE
	BR	40$		;CONTINUE WITH COMMON CODE
20$:	JSR	PC,GETEXN	;GET SLA
	MOV	NSPLB,R1	;FIND START OF LEB LIST FOR THAT SOURCE
	MOV	LB.LEB(R1),R1	;ADDRESS OF FIRST LEB ON LIST
25$:	BEQ	30$		;IF NOTHING THERE, LINK ADDR CAN'T BE IN USE
	CMP	R0,LE.NCL(R1)	;RIGHT LINK ADDRESS?
	BEQ	35$		;YES, USE THIS LEB
	MOV	LE.LNK(R1),R1	;GET NEXT LEB
	BR	25$		;AND TRY AGAIN

30$:	MOV	R0,-(SP)	;PROTECT SLA
	JSR	PC,NSPALB	;TRY TO ALLOCATE A LEB
	MOV	(SP)+,LE.NCL(R1) ;REMEMBER LINK ADDRESS
	BCS	65$		;CAN'T USE MAGIC LEB
35$:	MOV	R1,NSPLEB	;REMEMBER LEB WE FOUND
	CLR	R0		;USE A ZERO DESTADDR
	JSR	PC,NSPO2B	;PUT DSTADDR
	CMPB	LE.STT(R1),#LES.LI ;ONLY ALLOW IF IDLE OR LI STATES
	BGT	65$		;NOPE, ONE OF THESE HAPPENED:
				; PI  - CONN TO LINK IN PI STATE (IMPOSSIBLE)
				; RUN - NCL SHOULD GUARANTEE THAT EXTRA CONNECTS DON'T ARRIVE
				; DSC - LIKEWISE, DISCONNECTS SHOULD COMPLETE FIRST
	MOVB	#LES.LI,LE.STT(R1) ;ENTER LI STATE
40$:	MOV	LE.DCP(R1),R0	;USE INTERNAL ADDRESS FOR SOURCE
	JSR	PC,NSPO2B	;PUT SRCADDR
	MOV	#NM.LNK!NM.SRQ+<0*400>,R0
	JSR	PC,NSPO2B	;PUT SERVICES, INFO  BOTH AT ONCE
	MOV	#MSGMAX,R0	;TOPS-10 DOESN'T USE MML FIELD, BETTER FAKE IT
	JSR	PC,NSPO2B	;PUT SEGSIZE
	CMPB	#LES.RN,LE.STT(R1) ;IF WE'RE IN RUN STATE
	BEQ	50$		;THEN WE'RE SENDING A CC AND HAVE TO ADD DATA FIELD
	JSR	PC,NSPOC1	;COPY PROCESS NAME TO NSP MESSAGE
	CLR	R1		;NO LEB INDICATES DON'T SET LES.DV THIS CALL
	JSR	PC,NSPOC1	;AGAIN FOR SOURCE NAME
50$:	CLR	R0		;NO MENU ITEMS IN NCL OR NULL DATA FIELD
	JSR	PC,NSPPBY	;PUT MENU
	RTS	PC		;RELEASE NCL MESSAGE AND SEND NSP

60$:	JSR	PC,GETEXN	;GET SLA
	MOV	R0,LE.NCL(R1)	;NEED SO WE CAN BUILD DISC MSG.
65$:	JMP	NSPERP		;LINK ERROR

70$:	NSPODF			;DEFER UNTIL MEMORY FREES UP
;ROUTINE TO COPY PROCESS NAME FROM NCL MESSAGE INTO NSP MESSAGE

NSPOC1:	JSR	PC,GETBYT	;GET OBJECT TYPE
	TST	R1		;IS IT A DESTINATION PROCESS NAME
	BEQ	5$		;NO - DONT SAVE IT
	MOVB	R0,LE.DPN(R1)	;YES - SAVE IT FOR LATER
5$:
	CMP	#11,R0		;TASK?
	BEQ	30$		;YES, GO COPY IN NAME
	MOV	R0,-(SP)	;SAVE OBJECT TYPE FOR LATER
	MOV	#1!<0*400>,R0	;USE FMT 1, OBJTYPE 0
	JSR	PC,NSPO2B	;PUT FORMAT, OBJTYPE
	TST	R1		;WERE WE PASSED A LEB?
	BEQ	10$		;NO, DON'T TRY SETTING LES.DV
	BIS	#LES.DV,LE.STS(R1);SET DEVICE BIT IN LINK ENTRY
10$:	MOV	R5,-(SP)	;SAVE POSITION OF COUNT FIELD IN IMAGE STRING
	MOV	#5,R0		;WILL BE AT LEAST 5 CHARACTERS
	JSR	PC,NSPPBY	;PUT THAT IN AS INITIAL COUNT
	MOV	#'O,R0		;PUT TASK NAME OBJOON INTO MESSAGE
				;WHERE "OO" IS OBJECT TYPE, "N" IS NUMBER
	JSR	PC,NSPPBY	;PUT "O" INTO MESSAGE
	MOV	#'B,R0		;GET "B"
	JSR	PC,NSPPBY	;PUT "B" INTO MESSAGE
	MOV	#'J,R0		;GET "J"
	JSR	PC,NSPPBY	;PUT "J" INTO MESSAGE
	MOV	2(SP),R0	;GET OBJECT TYPE
	ROR	R0		;SHIFT
	ROR	R0		; SECOND DIGIT
	ROR	R0		;  TO CORRECT PLACE
	BIC	#177770,R0	;CLEAR EXTRA BITS
	ADD	#'0,R0		;MAKE INTO ASCII NUMBER
	JSR	PC,NSPPBY	;PUT 1ST DIGIT OF OBJECT TYPE INTO NAME
	MOV	2(SP),R0	;GET OBJECT TYPE BACK
	BIC	#177770,R0	;CLEAR EXTRANEOUS BITS
	ADD	#'0,R0		;MAKE INTO NUMBER
	JSR	PC,NSPPBY	;STORE IN MESSAGE
	JSR	PC,GETBYT	;GET UNIT NUMBER
	TST	R1		;IS IT THE DPN FIELD?
	BEQ	15$		;NO - SKIP THIS
	MOVB	R0,LE.DPN+1(R1)	;SAVE THE UNIT FIELD FOR LATER
15$:
	MOV	(SP)+,R1	;RESTORE ADDRESS OF IMAGE COUNT
	CMP	R0,#177		;GENERIC UNIT?
	BEQ	20$		;DONE WITH CONNECT
	JSR	PC,NSPOC2	;PUT NUMBER INTO FIELD, INC LENGTH
20$:	TST	(SP)+		;DISCARD OBJTYPE
	RTS	PC		;DONE WITH THIS NAME FIELD
30$:	MOV	#NSPLPN,R1	;SAVE PROCESS NAME HERE WHILE WE HANDLE PPN
40$:	JSR	PC,GETBYT	;GET BYTE OF NAME
	CMPB	R0,#'[!200	;START OF PPN?
	BEQ	50$		;YES, FIX NAME, PARSE PPN
	MOVB	R0,(R1)+	;SAVE IT
	BMI	40$		;LOOP ON REST OF NAME
	BR	60$		;REACHED END OF PROCESS NAME, STOP

50$:	BICB	#200,-1(R1)	;YES, END OF NAME, CLEAR EXTENSIBLE BIT
				;HERE TO PROCESS PPN
	MOV	#2,R0		;USE FORMAT 2 (OBJ, NAME, GROUP, USER)
	JSR	PC,NSPO2B	;FORMAT, OBJTYPE
	JSR	PC,NSPOCP	;CONVERT ASCII PROJECT TO GROUP
	JSR	PC,NSPOCP	;AND PROGRAMMER TO USER
	TST	R0		;SHOULD BE CLOSE BRACKET NOW
	BPL	70$		;WHICH SHOULD BE END OF STRING
	JSR	PC,NSPERR	;WE AREN'T, CALL IT A LINK ERROR TO DISCONNECT
	NSPOBD			;DISCARD THIS AND THE NSP MESSAGE

60$:	MOV	#1,R0		;USE FORMAT 1 (OBJ, NAME)
	JSR	PC,NSPO2B	;FORMAT, OBJTYPE
70$:	MOV	#NSPLPN,R1	;ADDR WHERE WE HID NAME
	JMP	NSPOIM		;NOW WE CAN COPY IT


NSPOC2:	MOV	R0,-(SP)	;SAVE NUMBER WE'RE STORING
	ASR	R0		;DISCARD
	 ASR	 R0		; LOW
	  ASR	  R0		;  DIGIT
	BLE	10$		;STOP IF THAT WAS ALL
	JSR	PC,NSPOC2	;STUFF REST OF NUMBER
10$:	MOV	(SP)+,R0	;RECOVER OUR DIGIT
	BIC	#177770,R0	;JUST OUR DIGIT...
	BIS	#60,R0		;ASCIIZE IT
	INCB	@R1		;COUNT IN LENGTH FIELD
	JMP	NSPPBY		;PUT IN MESSAGE
;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.IPN,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
	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
	MOV	SNA,R1		;DO SAME WITH SOURCE
	ADD	#SB.SNM,R1
	BR	NSPOIM		;SRCNODE


;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:				;TURN ON EXTENSIBLE BIT AND PUT INTO MESSAGE
	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 CALL REST OF CALLER FOR EACK 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

NSPBTP:	BIT	#5,@(PC)+	;ONLY IF INTERESTED IN NSP/NCL INPUT
NSPFGA:	.WORD	NSPTFG		;PATCH THIS TO POINT AT SWITCH REG
	BEQ	NSPTYR
	MOV	#NSPBT0,R1	;GET MESSAGE ABOUT BAD MESSAGE
	MOV	#CT0DDB,J	;PRINT ON CTY
10$:	MOVB	(R1)+,R0	;GET NEXT CHARACTER
	BEQ	NSPTYR		;STOP ON NULL. DON'T BOTHER TO CALL BEGXMT...
	JSR	PC,QTYCHR
	BR	10$		;DO REST

NSPBT0:	.ASCIZ	\NSPBTP CALLED\<15><12>

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.
.IIF NE,FTDCPT,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)

;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