Google
 

Trailing-Edge - PDP-10 Archives - bb-jr93e-bb - 7,6/ap014/dndcmp.x14
There are 3 other files named dndcmp.x14 in the archive. Click here to see a list.
.SBTTL	DNDCMP - DDCMP PROTOCOL  29 APR 86

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

VRDCMP=070			;FILE EDIT NUMBER

.IF NE NTLINE
.IF NE,<NTLINE-DMCN>

SYNCHS:
	.REPT	FTNSYN
		.BYTE	SYN
	.ENDR
	.EVEN
.ENDC;.IF NE,<NTLINE-DMCN>

;HERE AT ONCE ONLY TIME
DDCINI:					;HERE TO INITIALIZE DDCMP
.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	CMPB	#LS..DM,LB.DVS(J)	;IS IT DMC11?
	BNE	77$			;NO, SKIP
.ENDC;.IF NE,<NTLINE-DMCN>
	JMP	DMCINI			;REALLY PJMP: GO HANDLE DMC11 DIFFERENTLY
77$:
.ENDC;..IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	MOV	LB.SLA(J),R3		;GET THE HARDWARE ACCESS ADDRESS FOR LINE
	MOV	#DDCI20,LB.XDN(J)	;SET DEFAULT FOR XMIT INTERRUPT SERVICE
	JSR	PC,SL.INI		;INITIALIZE THE LINE DRIVER
	BIC	#^C<LS..RQ!LS..XQ!LS.MPT>,@J	;CLEAR MOST STATUS BITS
	BIS	#LS..ST,@J		;NEED TO SEND START
	MOV	#REPSEC,LB.REP(J)	;RESET REP TIMER
	CLRB	LB.RDC(J)		; CLEAR REP COUNT
	MOV	#LB.IBF,LB.ITK(J)	;INITIALIZE INPUT TAKER
	MOV	#LB.IBF,LB.IPT(J)	;INITIALIZE INPUT PUTTER
	CLR	LB.IBF+10(J)		;CLEAR CHUNK POINTER
	JSR	PC,DDCQRQ		;POKE TRANSMITTER
	QUEPUT	QI 91$
	RTS	PC
.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC ; .IF NE NTLINE
.SBTTL		TIMER ACTION
.IF NE NTLINE
;HERE WHEN DDCMP SECOND TIMER GOES OFF

DDCSEC:					;HERE ONCE PER SECOND FOR DDCMP
.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	CMPB	#LS..DM,LB.DVS(J)	;IS IT DMC11?
	BNE	77$			;NO, ACT NORMAL
.ENDC;.IF NE,<NTLINE-DMCN>
	JMP	DMCTMR			;PJMP TO DMC11 TIMEOUT ROUTINE
77$:
.ENDC;.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	INCB	LB.RDC(J)		;COUNT TIMES WE SEND REP'S
.IIF NE <REPDWN&377>, CMPB #REPDWN,LB.RDC(J)
	BNE	10$
	JSR	PC,L.DOWN		;LINE DOWN - SET FLAGS ETC.
10$:	MOV	#REPSEC,LB.REP(J)	;RESET REP TIMER
	BIT	#LS..ST!LS.STK,(J)	;IF START OR STACK, SPEED UP TIMER
	BEQ	15$
	MOV	#JIFSEC/REPSPD*17,LB.REP(J) ; SEND START EVERY 15 SECONDS
15$:	BIS	#LS.NRP!LS.XRP,@J	;SET REP FLAG
.IIF NE FT.MPT,BISB	#MP.SNM,LB.MPS(J) ;SET SELECT
	JMP	DDCQRQ			;WAKE TRANSMITTER
.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC ;.IF NE NTLINE
.SBTTL		QUEUE DATA OUT

;HERE TO QUEUE A MESSAGE OUT
;	FORMAT OF 1ST CHUNK IS <LINK><MSG LINK><LENGTH>DATA
;	FORMAT OF 2ND CHUNK IS <LINK>DATA
; CALL	SETUP DNA
;	MOV	#ADR,R0
;	JSR	PC,DDQDAT
.IF NE NTLINE
DDQBOO:	MOV	R0,LB.BOO(J)		;SAVE ADR OF MSG
	MOV	#DLE,R2			;BEGIN BOOTSTRAP MSGS WITH DLE
.IF NE FT.RDM				;IF RDE DEVICE WHICH CAN'T HANDLE
.IF EQ RDEDLL				;MAINTAINANCE MODE MESSAGES, WE
					;MUST CHUCK THEM
	BIT	#LS.MPT,(J)		;IF NOT MULTIPOINT LINE
	BEQ	DDQ.00			;THEN OK TO DO MAINT MODE
.IF NE FTTRIB
	TST	LB.MPL(J)		;AND IF NOT TRIBUTARY
	BEQ	DDQ.00			;THEN OK TO DO MAINT MODE
.ENDC; NE,FTTRIB
	TWIDDLE				;COUNT AND CHUCK MSGS
	JMP	FRECKS
.IFF; EQ,RDEDLL
	BR	DDQ.00
.ENDC; EQ,RDEDLL
.IFF; NE,FT.RDM
	BR	DDQ.00
.ENDC; NE,FT.RDM

.IF NE,FTDCP1!FTDCP3!FTDCP4
	BIT	#LS.NSP,(J)		;NSP LINE?
	BEQ	DDQ.00			;NO, ACT NORMAL
	TWIDDLE				;COUNT PEOPLE TRYING TO BOOT NSP
	PJMP	FRECKS			;AND THROW MESSAGE AWAY
.ENDC; NE,FTDCP1!FTDCP3!FTDCP4
DDQDAT:	MOV	#SOH,R2			;BEGIN DATA MSGS WITH SOH
	MOV	DNA,SB			;GET DESTINATION SCB ADR
	ASSERT	NE
DDQ.00:	CMP	#DLE,R2			;WAS THIS A BOOTSTRAP MSG ?
	BEQ	DDQ.02
	SAVE	<J,#JRSTR>		;SAVE "J" REGISTER AND ADDRESS
					;WHERE IT WILL BE RESTORED
	MOV	SB.LBA(SB),J		;GET PRIMARY ROUTE
	ASSERT	NE
.IF NE FTDL10!FT.DTE
	CMP	TENSCB,SB		;IS THIS FOR THE 10 ?
	BNE	20$
10$:	JMP	DLQDAT			;QUEUE DATA TO 10
20$:	CMP	TENSCB,J		;IF PRIMARY ROUTE IS THROUGH 10,
	BEQ	10$			;USE THE DL10 HIGH SPEED CHANNEL
.ENDC;.IF NE FTDL10!FT.DTE

.IF NE,FTDCP1!FTDCP3!FTDCP4
	BIT	#LS.NSP,(J)		;NSP LINE?
	BEQ	DDQSOL			;NO, FALL THROUGH
	JSR	PC,NSPOUT		;YES, GO MAKE NSP VERSION
.IF NE FTDCP3!FTDCP4
	RTS	PC			;DCP3 QUEUES NCL MESSAGES INSIDE ITSELF
.ENDC; NE,FTDCP3!FTDCP4
DDQNSP:	MOV	#SOH,R2			;PROBABLY CLOBBERED BY NOW
.ENDC ;.IF NE FTDPC1!FTDCP3!FTDCP4
;NOW SELECT AN OUTPUT LINE
DDQSOL:	TST	LB.OBF(J)		;IF OUTPUT QUEUE EMPTY
	BEQ	20$			; THEN USE THIS LINE
	MOV	J,R1			;ELSE LOOK FOR A SHORTER QUEUE
10$:	MOV	LB.2ND(R1),R1		;GET NEXT LINE IN THE LIST
	BEQ	20$			;IF NO MORE, THEN "J" IS THE BEST
	CMP	LB.OBF+4(J),LB.OBF+4(R1);SEE WHO HAS THE SHORTEST QUEUE
	BLE	10$			;IF "J" DOES, THEN KEEP LOOKING
	MOV	R1,J			;OTHERWISE, REMEMBER NEW "BEST"
	BR	DDQSOL			; AND START FROM THERE

;NOW FOR A LITTLE CONGESTION CONTROL!

20$:	CMP	#40,LB.OBF+4(J)		;THIS SEEMS LIKE A GOOD LIMIT.
	BGT	DDQDDP			;IF WE'RE UNDER IT, QUEUE THE MSG

XXX=0
.IIF NE FT.MPT,XXX=XXX!LS.MPT
.IIF NE FTDCP1!FTDCP3!FTDCP4,XXX=XXX!LS.NSP
.IF NE XXX
	BIT	#XXX,@J			;IS THIS DATA IRREPLACABLE?
	BNE	DDQDDP			;YES, CAN'T TOSS IT
.ENDC ;.IF NE FT.MPT!FTDCP1!FTDCP3!FTDCP4

.IF NE FT.DDP
	TST	LB.ST2(J)		;IF THIS IS A DDP LINE
	BLT	DDQDDP			;THEN DATA IS IRREPLACABLE
.ENDC ;.IF NE FT.DDP

	JSR	PC,NCLOKT		;OTHERWISE SEE IF IT'S OK TO TOSS
	BCS	DDQDDP			;IF IT'S ONE OF OURS, DON'T DELETE
	TWIDDLE				;COUNT THE NUMBER LOST
	JMP	FRECKS			;AND JUNK IT
DDQDDP:	INCB	LB.HSN(J)		;NEW HIGHEST MSG # SENT
DDQ.02:	ASSERT CHUNK R0			;BE SURE WE DIDN'T GET TRASH
	MOV	R0,-(P)			;SAVE MSG ADDR
	MOV	#DDHTIM,CN.TIM(R0)	;NUMBER OF SECONDS TO HOLD
.IF NE FT.MPT
	BIT	#LS.MPT,(J)		;IF A MULTIPOINT LINE
	BEQ	05$
	MOV	#MPHTIM,CN.TIM(R0)	;NUMBER OF SECONDS TO HOLD
    .IF NE FTTRIB
	TST	LB.MPL(J)		;IF NOT A TRIBUTARY WE MUST
	BEQ	04$
    .ENDC
    .IF EQ FTSLCT-2
	JSR	PC,SLCTDO		;RECOMPUTE THE WEIGHTING FOR SELECTION
	;				;GIVEN THAT DATA WAS XMITTED
    .ENDC
	BITB	#MP.OFF!MP.SOL,LB.MPS(J) ;IF OFFLINE
	BEQ	05$
	JSR	PC,SLCT.1		;PUT IT ONLINE
    .IF NE FTTRIB
04$:	BICB	#MP.OFF!MP.SOL,LB.MPS(J) ;CLEAR THE OFFLINE STATE
    .ENDC
05$:	MOV	(P),R0			;BE SURE R0 IS NOT ALTERED
.ENDC
	MOV	CN.LEN(R0),R1		;GET SIZE OF MSG
	ADD	#CN.DDC,R0		;POINT TO SOH
	MOVB	R2,(R0)+		;PUT SOH OR DLE ON MESSAGE
.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	CMPB	#LS..DM,LB.DVS(J)	;IS IT DMC11?
	BEQ	68$			;YES, DON'T CALCULATE CRC
.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC;.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	MOVB	R1,(R0)+		;PUT LOW ORDER PART IN HEADER
	SWAB	R1
	MOV	R1,(R0)+		;PUT HIGH ORDER BITS OF COUNT IN
	;MOVB	LB.ROK(J),(R0)+		; TRASH FOR LB.ROK(FIXED LATER)
	MOVB	LB.HSN(J),(R0)+		;PUT MESSAGE # INTO HEADER
.IF NE FT.MPT
	MOVB	LB.MPA(J),(R0)+		;PUT DEST STATION ADDRESS IN HEADER
.IFF
	MOVB	#A0,(R0)+		;PUT DEST STATION ADDRESS IN HEADER
.ENDC
	CLR	(R0)+			;CLEAR BCC SLOT

;CALCULATE CRC FOR A MESSAGE TO BE TRANSMITTED
	SWAB	R1			;GET COUNT BACK
	JSR	PC,DDDBCC
.IF EQ DGUTS
	TRAP			;ZAPPED IF MESSAGE IS LONGER THAN ALLOCATION
.IFF
	BR	70$		;ZAPPED IF MESSAGE IS LONGER THAN ALLOCATION
.ENDC ; .IF EQ DGUTS
	BR	67$			;DUP DEVICE RETURN
65$:	ADVCNK	R0 EXTEND		;ADVANCE TO NEXT CHARACTER
;					;ALLOCATING A NEW CHUNK IF REQUIRED
	MOVB	(P),(R0)+		;PUT 1ST HALF OF BCC IN MESSAGE
	SWAB	(P)
	ADVCNK	R0 EXTEND		;ADVANCE TO NEXT CHARACTER
;					;ALLOCATING A NEW CHUNK IF REQUIRED
	MOVB	(P)+,(R0)+		;PUT LAST HALF OF BCC INTO MESSAGE
.ENDC;.IF NE,<NTLINE-DMCN>
68$:	MOV	(P)+,R0			;GET ADR OF 1ST CHUNK AGAIN
	CMPB	#DLE,CN.DDC(R0)		;IS THIS A BOOTSTRAP MSG ?
	BEQ	DDCQRQ
	TST	LB.OBF(J)		;IS THE QUEUE EMPTY ?
	BNE	50$
	MOV	R0,LB.OBF(J)
	MOV	R0,LB.OBF+2(J)
	BR	60$
50$:	MOV	LB.OBF+2(J),R2		;GET ADDRESS OF LAST MESSAGE IN QUE
	MOV	R0,CN.MLK(R2)		;SET LINK IN PREV LAST MSG
	MOV	R0,LB.OBF+2(J)		;REMEMBER NEW LAST MSG
60$:	INC	LB.OBF+4(J)		;COUNT MESSAGE INTO QUEUE
62$:	BR	DDCQRQ			;WAKE TRANSMITTER
67$:	TST	(P)+			;CLEAR CRC
	BR	68$			;FROM STACK AND DONT STORE IN MESSAGE

.IF NE,<NTLINE-DMCN>
.IF NE DGUTS
70$:    DECB	LB.HSN(J)		;RESTORE MESSAGE COUNT
	TST	(P)+			;POP WOULD BE BCC
	MOV	(P)+,R0			;GET BACK MSG POINTER
	JSR	PC,FRECKS		;CHUCK MSG AND LOG ERROR
	CTYMSG	IML
	RTS	PC
.ENDC ;.IF NE DGUTS
.ENDC;IF NE,<NTLINE-DMCN>


;HERE TO QUEUE OUT TRANSMITTER
DDCQRQ:	TRACE	DD
	PIOFF
	BIT	#LS.XDT!LS.XCT!LS..XQ,@J;IS XMITTER ALREADY QUEUED UP ?
	BNE	70$			;IF SO DONE FOR NOW
	QUEPUT	QO 69$
70$:	PION
	RTS	PC

.ENDC ; .IF NE NTLINE
.SBTTL		RECEIVER SERVICE
.IF NE,NTLINE
.IF NE,<NTLINE-DMCN>
;HERE TO PREPARE TO RESTART DDCMP LINE

DDCIGO:	MOV	J,R1			;COPY LINE BLOCK ADR
	ADD	LB.IPT(J),R1		;POINT TO NEW BUFFER
	MOV	10(R1),R0		;GET STALE CHUNK STRING IF ANY
	BEQ	10$
	TWIDDLE
	CLR	10(R1)			;FORGET POINTER
	TWIDDLE	R0			;REMEMBER STALE CHUNKS
	JSR	PC,FRECKS		;RELEASE THEM
10$:	PIOFF				;DISABLE INTERRUPTS
	JSR	PC,SLRBEG		;RESTART  SYNCHRONOUS LINE RECEIVER
	PION
	RTS	PC

.ENDC;IF NE,<NTLINE-DMCN>
;HERE AT LOW LEVEL TO CHECK INPUT

DDCINP:
.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	CMPB	#LS..DM,LB.DVS(J)	;IS IT DMC11?
	BNE	6$			;NO, SKIP NEXT INSTRUCTION
.ENDC;.IF NE,<NTLINE-DMCN>
	JMP	DMCINP			;HANDLE DMC11 DIFFERENTLY
6$:
.ENDC;.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	MOV	LB.ITK(J),R0		;GET INPUT TAKER
	CMP	R0,LB.IPT(J)		;ANY INPUT LEFT TO CHECK ?
	BNE	5$			;BRANCH IF MORE TO DO
	BIT	#LS..RG,@J		;IS RECEIVER ENABLED ?
	BNE	99$			;IF SO WE ARE DONE
	JSR	PC,DDCIGO		;RELEASE STALE CHUNKS
99$:	JMP	LOOP

;HERE WITH QUEUED DDCMP DATA MESSAGE WAITING

5$:	HDRDMP	I,DMPHLN,DMPHIN
	ADD	J,R0			;MAKE ABS ADR OF MESSAGE
	MOV	R0,R3			;COPY ADDRESS OF BUFFER
	MOV	R0,R2			;COPY ADDRESS

;CHECK BCC ON 8 CHAR HEADER OR CONTROL MSG
.IF NE FT.CHK
	CMPB	#SOH,@R0		;WAS FIRST CHAR OF HEADER SOH ?
	BEQ	DDCIN0
	CMPB	#ENQ,@R0		;PERHAPS THIS IS CONTROL MSG ?
	BEQ	DDCIN0
	CMPB	#DLE,@R0		;BOOTSTRAP MESSAGE ?
	BEQ	DDCIN0
.IF NE FTDUP11				;GROSS HACK TO HANDLE DUP BCC
	CMPB	#252,@R0		;WAS THIS THE ERROR FLAG FROM INT LEVEL?
	BNE	20$			; IF SO, THEN
	JMP	DDRADV			; CHUCK THE MESSAGE. (INT LEVEL HAS
					; ALREADY SENT THE NAK)
20$:
.ENDC
	TRAP				;INTERRUPT LEVEL GOOFED
.ENDC;.IF NE FT.CHK
DDCIN0:
;	DONE AT INTERRUPT LEVEL FOR PROPER OPERATION
;	JSR	PC,DDBBCC		;CALCULATE THE BCC FOR THE HEADER
;	CMP	(P)+,(R0)+		;CHECK BCC ON DDCMP HEADER
;	BEQ	.+6
;	JMP	DDRBBC			;IF NOT EQUAL MESSAGE IS TRASH
.IF EQ FT.CHK
.IF NE FTDUP11
	CMPB	#252,@R0		;DUP CRC ERROR?
	BNE	5$			;IF NOT
	JMP	DDRADV			;YES-CHUCK THE MSG.
5$:
.ENDC ;.IF NE FTDUP11
.ENDC ;.IF EQ FT.CHK
.IF NE FT.MPT
	BIT	#LS.MPT,(J)		;IF A MULTIPOINT LINE
	BEQ	14$
.IF NE FTTRIB
	TST	LB.MPL(J)		;IF TRIBUTARY
	BNE	11$
	CMPB	LB.MPA(J),5(R2)		;AND ITS NOT FOR ME
	BNE	$29.1	;(BNE DDRADV)  ;CHUCK MSG
	BR	12$
.ENDC	;.IF NE FTTRIB
11$:	BITB	#MP.OFF,LB.MPS(J)	;OR I AM AN OFFLINE MASTER
	BNE	$29.1	;(BNE DDRADV)	;CHUCK MSG
12$:
	BITB	#SELBIT,2(R2)		;AND IF SELECT SET IN MSG
	BEQ	14$
	BICB	#MP.SFF!MP.OFF!MP.SOL,LB.MPS(J)
					;RESET THE SELECT FAILURE COUNTER
	BISB	#MP.SFC,LB.MPS(J)
13$:	JSR	PC,SELNXT		;THIS STATION IS NO LONGER IT
;					;OR IF TRIBUTARY THIS STATION IS NOW IT
.IF NE FTTRIB
	TST	LB.MPL(J)		;IF TRIBUTARY
	BNE	14$
	BIS	#LS.XAK,(J)		;WE MUST SEND A RESPONSE
	JSR	PC,DDCQRQ
.ENDC
14$:
.ENDC
	MOV	(R2)+,R0		;GET 1ST TWO CHARS
	CMPB	#DLE,R0			;WAS THIS A BOOTSTRAP MSG ?
	BEQ	23$			;IF SO DON'T CHECK MSG NUMBERS
	CLRB	LB.BNN+1(J)		;NOT A BOOT MSG, SO RESET BOOT MODE
	CMPB	#ENQ,R0			;WAS MESSAGE CONTROL ?
	BEQ	$50
	BIT	#LS..ST!LS.STK,(J)	;IF WE AREN'T STARTING OR STACKING
	BEQ	20$			;GO PROCESS THE MESSAGE
	BIT	#LS..ST,(J)		;IF WE ARE STARTING
	BNE	$29.1	;(BNE DDRADV) 	;CHUCK THE MSG IF IN THE START STATE
	BIC	#LS.STK,(J)		;CLEAR SENDING STACKS
	BIS	#LS.XAK,(J)		;SAY WE NEED TO ACK AND PROCESS MSG

;HERE IF GOOD HEADER ON A DATA MESSAGE
20$:	MOVB	3(R3),R0		;GET LAST MSG # HE GOT OK
	JSR	PC,DDRESP		;VERIFY SEQ NUMBERING
	   BR	$29.1	;(BR DDRADV)	;DUMP THE MSG IF BAD SEQUENCING
	MOVB	4(R3),R0		;GET MSG # FROM HEADER
	DEC	R0			;MAKE PREVIOUS MSG #
	CMPB	R0,LB.ROK(J)		;IF THESE DON'T MATCH
	BNE	DDRADV			; JUST IGNORE
23$:
.IIF EQ FTSLCT-2, JSR PC,SLCTDI ;RECOMPUTE THE WEIGTHING FOR
;					;SELECTION ON DATA RECEPTION
	MOV	10(R3),R0		;GET ADR OF 1ST CHUNK IN DATA PORTION
	BEQ	DDRNRM			;IF NO CHUNKS NAK MESSAGE
	ADD	#CN.NCT,R0		;MAKE ADR OF 1ST CHAR OF MESSAGE
	MOV	CN.LEN-CN.NCT(R0),R1	;GET LENGTH OF MESSAGE
	CMP	R1,#MSGMAX		;IF MSG TO LOOOONG
	BHI	DDRTLM			;NAK IT
	ADD	#2,R1			;ADD 2 CHARS FOR BCC
	JSR	PC,DDDBCC		;CALCULATE BCC
	BR	DRNRM0			;INCONSISTENT LENGTHS FAILURE
	BR	25$			;DUP RET  CLEAR STACK
	TST	(P)+			;CHECK BCC
	BNE	DDRDBC
26$:	MOV	10(R3),R0		;GET ADR OF 1ST MSG CHUNK
	CMPB	#SOH,(R3)		;IF ORDINARY DATA
	BEQ	27$			;CONTINUE
	JMP	DRBOOT			;ELSE PROCESS BOOTSTRAP MSG
25$:	TST	(P)+			;CLEAR STACK
	BR	26$			;RETURN TO NORMAL ROUTINE
27$:	INCB	LB.ROK(J)		;INCREMENT MSG # RECEIVED
	BIS	#LS.XAK,@J		;FLAG TO SEND AN ACK
	INC	LB.ICN(J)		;COUNT ALL MESSAGES IN
;	JSR	PC,DDCQRQ		;WAKE XMITTER SO IT SENDS ACK
	CLR	10(R3)			;SO DDRADV DOESN'T TOUCH CHUNKS
	MOV	J,-(P)			;SAVE J IN CASE NCLINP WIPES IT
.IF NE FT.RDM
	BIT	#LS.MPT,(J)		;IF MULTIPOINT, MUST SIMULATE DEVICE
	BEQ	$28
.IF NE FTTRIB
	TST	LB.MPL(J)		;BUT NOT IF TRIBUTARY
	BEQ	$28
.ENDC
	MOVB	LB.MPA(J),CN.NCT-1(R0)	;SAVE THE STATION'S ADDRESS
	JSR	PC,RDEINP
	BR	$29.1
.ENDC ;.IF NE FT.RDM
$28:
	JSR	PC,DDDGIV		;GIVE DATA TO WHOMEVER
	MOV	(P)+,J			;RESTORE J
$29.1:	BR	DDRADV
.ENDC;.IF NE,<NTLINE-DMCN>
.IF NE,<NTLINE-DMCN>
;HERE IF CONTROL MESSAGE HAS GOOD BCC
$50:	SWAB	R0			;WANT CODE IN RH
	BIT	#LS..ST!LS.STK,@J	;HAVE WE EXCHANGED START/STACK ?
	BEQ	15$
	CMPB	R0,#STRT		;IS IT STRT OR STACK ?
	BGE	15$			;IF SO, THEN IT'S OK
	BIT	#LS..ST,(J)		;IF SENDING STARTS
	BNE	DDRADV			;THEN IGNORE MESSAGE
	BIC	#LS.STK,(J)		;OTHERWISE CLEAR SENDING STACKS
	BIS	#LS.XAK,(J)		;SAY WE NEED TO ACK AND PROCESS MSG
15$:
	TRACE	DD			;PUT MESSAGE TYPE IN TRACE
	CMPB	#STACK,R0		;CHECK TYPE IS SMALL ENOUGH
	BLO	DRNAK0			;BRANCH IF TOO LARGE
	ASL	R0			;MULTIPLY BY 2
	JMP	@DDCDSP-<ENQ*1000>(R0)
DDCDSP:	DRNAK0			;(0) = FORMAT ERROR
	DDRACK			;(1) = ACK
	DDRNAK			;(2) = NAK
	DDRREP			;(3) = REP
	DDREST			;(4) = RESET
	DDRSAK			;(5) = RESAK
	DDRSTR			;(6) = START
	DDRSTK			;(7) = STACK
;HERE BECAUSE WE RECEIVED A MESSAGE BUT DIDN'T HAVE ROOM FOR IT
DRNRM0:	TST	(P)+			;POP BCC FROM STACK FIRST
	TWIDDLE
DDRNRM:	MOVB	#NCDNRM,LB.NCD(J)	;REASON CODE
	TWIDDLE
	BR	DDRKRD			;AND FLUSH MESSAGE
;HERE BECAUSE MSG TO LONG TO HANDLE
DDRTLM:	MOVB	#NCDTLM,LB.NCD(J)	;DATA LENGTH CODE
	TWIDDLE
	BR	DDRKRD
;HERE BECAUSE BAD BCC ON HEADER OR DATA
DDRDBC:	MOVB	#NCDDBC,LB.NCD(J)	;DATA BCC CODE
	TWIDDLE
	BR	DRNAK0
DDRBBC:	MOVB	#NCDBCC,LB.NCD(J)	;NAK CODE
	TWIDDLE
DRNAK0:	INC	LB.ICN+2(J)		;COUNT BUM MESSAGES
	TWIDDLE
DDRKRD:	BIS	#LS.XNK,@J		;SEND NAK TO OTHER GUY
	TWIDDLE
;	JSR	PC,DDCQRQ		;SEND NAK

;HERE WHEN DONE PROCESSING LAST DDCMP MSG
DDRADV:	MOV	LB.ITK(J),R1		;GET REL ADR OF BUFFER AGAIN
	ADD	J,R1			;MAKE ABSOLUTE ADR
	MOV	10(R1),R0		;GET ADDR OF CHUNKS IF ANY
	JSR	PC,FRECKS		;AND THROW THEM AWAY
	CLR	10(R1)			;SO WE DON'T NOTICE THEM LATER
	MOV	LB.ITK(J),R0		;GET INPUT SLOT ADR AGAIN
	ADD	#12,R0			;ADVANCE INPUT SLOT
	CMP	R0,#LB.IBF+<NINBUF*12>	;TIME TO WRAP AROUND ?
	BNE	84$
	MOV	#LB.IBF,R0		;YES
84$:	MOV	R0,LB.ITK(J)		;RESET TAKER
	JSR	PC,DDCQRQ		;POKE TRANSMITTER
	JMP	DDCINP			;SEE IF ANY MORE INPUT READY
;
;HERE BECAUSE BAD BCC ON HEADER AT INTERRUPT LEVEL
DRBCC0:	MOVB	#NCDBCC,LB.NCD(J)	;HEADER BCC NAK CODE
	TWIDDLE
DRNAK1:	INC	LB.ICN+2(J)		;COUNT BUM MESSAGES
	TWIDDLE
	BIS	#LS.XNK,@J		;SEND NAK TO OTHER GUY
	JMP	DDCQRQ			; AND RETURN TO INTERRUPT HANDLER
;HERE WHEN WE RECEIVE A NAK MESSAGE

DDRNAK:	BIS	#LS..RN,@J		;REMEMBER WE RECEIVED NAK
	INC	LB.OCN+2(J)		;COUNT ALL NAKS RECEIVED
	MOVB	(R2),R1			;GET REASON AND STRIP EXTRA BITS
	BIC	#^C77,R1
	BEQ	40$			;ZERO CODE ???
	CMP	R1,#NCDREP		;IF BCC ERROR
	BHIS	10$
	INC	LB.OCN+6(J)		;COUNT IT
.IF NE FT.BIG
	INCB	LB.TRY(J)		;COUNT BCC ERRORS FOR THIS MESSAGE
	CMPB	LB.TRY(J),#10		;AN UNREASONABLE NUMBER YET?
	BLT	40$			;NO, GIVE IT A SECOND CHANCE
	TWIDDLE	J			;WE PROBABLY CLOBBERED OUR OWN MESSAGE
	PJMP	L.DOWN			; SO WE BETTER START FROM SCRATCH
.IFF
	BR	40$			;SEE IF ANYTHING WAS ACKED
.ENDC

10$:	BHI	20$
	CMPB	3(R3),LB.LAP(J)		;IS LAST ACKED OUR LAST SENT?
	BEQ	DDRACK			;YES, TREAT IT NOT AS A NAK, BUT AS
					; A DMC'S "FUNNY-LOOKING ACK"
	INC	LB.OCN+4(J)		;COUNT NAKS
15$:	BR	40$

20$:	CMP	#NCDTLM,R1		;CHECK FOR REASON IS MSG LENGTH
	BEQ	25$
	CMP	#NCDNRM,R1		;CHECK FOR REASON IS NO ROOM
	BNE	40$
25$:	INC	LB.OCN+10(J)
40$:

;HERE WHEN WE RECEIVE AN ACK MESSAGE

DDRACK:	TSTB	(R2)+			;SKIP A FILL
	MOVB	@R2,R0			;LAST MSG # HE GOT
	JSR	PC,DDRESP		;CHECK SEQUENCE NUMBERING
	BR	DDRADV			;CHUCK MSG IF SEQUENCING IS OFF
	BR	DDRADV			;ALSO IF SEQUENCING IS OK
;HERE WHEN RECEIVE A REP MESSAGE
DDRREP:	TST	(R2)+			;SKIP FILLERS
	CMPB	@R2,LB.ROK(J)		;WAS LAST MSG HE SENT LAST I GOT?
	BEQ	10$
	BIS	#LS.XNK,@J		;NEED TO SEND NAK
	MOVB	#NCDREP,LB.NCD(J)	;REASON CODE
	INC	LB.ICN+4(J)		;COUNT TIMES THIS HAPPENED
	BR	20$
10$:	BIS	#LS.XAK,@J		;NEED TO SEND ACK
20$:	BR	DDRADV			;SEND MESSAGE TO HIM

;HERE WHEN WE RECEIVE A RESET MESSAGE
DDREST:

;HERE WHEN WE RECEIVE A RESET-ACK MESSAGE
DDRSAK:	BR	DRNAK0

;HERE TO CHECK SEQUENCING, R0 HAS # FROM ACK/NAK/DATA
DDRESP:	CLR	-(P)
	MOVB	LB.LAP(J),(P)		;GET LAST ACKED NUMBER
.IF NE FTWHYD
	MOVB	@P,LB.WHS(J)		;SAVE LAST ACKED #
	MOVB	LB.OBF+4(J),LB.WHN(J)	;SAVE QUEUE LENGTH BEFORE ITS CLEARED
.ENDC
	MOVB	R0,LB.LAR(J)		;UPDATE LAST ACKED NUMBER
.IF NE FT.BIG
	CLRB	LB.TRY(J)		;SUCCESSFULLY SENT LEAD MESSAGE
.ENDC
	BIC	#^C377,R0		;ACKED NUMBER MUST BE POSITIVE
	SUB	(P)+,R0			;GET NUMBER OF MSGS ACKED THIS TIME
	BEQ	75$
	BIC	#^C377,R0		;NUMBER MUST BE POSITIVE
					;IF GREATER THAN ZERO, MUST CHECK THE MSG QUEUE
	CMP	LB.OBF+4(J),R0		;COMPARE NUMBER ACKED WITH QUEUE LENGTH
	BHIS	70$			;IF QUEUE IS NOT SHORTER, WE'RE OK
	BR	75$			;ELSE IGNORE (AS PER DDCMP SPEC)
70$:
	JSR	PC,DDXMTD		;CLEAN OUT THE XMITTED MSGS
	BR	78$			;AND RESET REP TIMER
75$:	TST	LB.OBF+4(J)		;IF MORE REMAINS TO XMIT,
	BNE	80$			;THERE MAY HAVE BEEN AN ERROR
					;SO LET REP TIMER RUN
78$:	MOV	#REPSEC,LB.REP(J)	;RESET REP TIMER
80$:	CLRB	LB.RDC(J)		;RESET LINE TIMEOUT COUNT DOWN
	ADD	#2,(P)			;SKIP ON RETURN
	RTS	PC
;HERE WHEN WE RECEIVE A START MESSAGE
DDRSTR:
.IF EQ FT3.02
.IF NE FT.MPT
	BIT	#LS.MPT,(J)	;IF NOT MULTIPOINT, THEN NOT DDCMP 3.02 SPEC
	BNE	DDRSSC
.ENDC	;.IF NE FT.MPT
	BIT	#LS..ST!LS.STK,(J)	;IF WE'RE NOT SENDING STARTS BEGIN NOW
	BNE	DDRSSB
	JSR	PC,L.DOWN		;BY DECLARING THE LINE DOWN
	BR	DDRQCT

DDRSSB:	TST	(R2)+			;SKIP FILLERS
DDRSSK:	MOVB	@R2,LB.ROK(J)		;GET NEXT MESSAGE NUMBER TO RECEIVE
	DECB	LB.ROK(J)		;ADJUST IT
	BIT	#LS..ST,@J		;WAS I ALSO SENDING STARTS ?
	BNE	DDRSSD			;IF SO THEN JUST CLEAN UP
	JSR	PC,L.DOWN		;CLEAN OUT LINE BLOCK
.IFF
	BIT	#LS..ST!LS.STK,(J)	;TRYING TO START UP DDCMP?
	BNE	DDRSSC			;YES, JUST SEND A STACK
	JSR	PC,L.DOWN		;NO, DECLARE LINE "DOWN" FIRST
					; BEFORE TRYING TO SEND A STACK
.ENDC	;.IF EQ FT3.02

DDRSSC:
.IF NE FT.MPT!FT3.02
.IF EQ FT3.02
	BIT	#LS.MPT,(J)		;IF A MULTIPOINT LINE
	BEQ	20$
.ENDC	;.IF EQ FT3.02
	CLR	LB.ROK(J)		;RESET MSG NUMBERS
	CLR	LB.LAP(J)
	TST	LB.OBF+2(J)		;IF QUEUED MSGS,WE MUST FIX THEIR #S
	BEQ	11$
	MOV	LB.OBF(J),R1
10$:	INCB	LB.HSN(J)
	MOVB	LB.HSN(J),CN.DDC+4(R1)
	MOV	CN.MLK(R1),R1
	BNE	10$
11$:	MOVB	LB.OBF+4(J),LB.HSN(J)	;COUNT MSGS IN THE QUEUE AS UNACKED
	CLR	LB.COB(J)		;THERE IS NO LONGER A CURRENT DATA MSG
.ENDC	;.IF NE FT.MPT!FT3.02

DDRSSD:
	JSR	PC,DDRSNB		;ARE WE FORBIDDEN TO BRING THIS LINE UP?
	BCS	DD0ADV			;YES, CAN'T STACK YET, EAT THE START
10$:	BIS	#LS.XRP!LS.NRP!LS.STK,@J;SET SEND-STACK FLAG
	BIC	#LS..ST,@J		;CLEAR SEND-START FLAG
	Z=REPSEC/2
	.IIF EQ Z,Z=1
DDRSSE:	MOV	#Z,LB.REP(J)		;RESET REP TIMER & COUNTER
	CLRB	LB.RDC(J)		; CLEAR REP COUNT
.IF NE FT.RDM
	BIT	#LS.MPT,(J)		;IF MULTIPOINT,DON'T INFORM NCL
.IF NE FTTRIB
	BEQ	30$
	TST	LB.MPL(J)		;BUT NOT IF TRIBUTARY
.ENDC
	BNE	DDRQCT
.ENDC ;.IF NE FT.RDM
;
;******* here is a convenient place for a sink macro call *****
;
30$:	JSR	PC,DDDRSS		;TELL WHOEVER THAT WE RESTARTED

DDRQCT:
.IF NE FT.RDM!FT.RDP
	JSR	R0,RDESST		;IF A RDE DEV, REPORT THE STATE
	.BYTE	0,210
.ENDC
DD0ADV:	JMP	DDRADV
;HERE WHEN A STACK IS RECEIVED

DDRSTK:	BIT	#LS..ST!LS.STK,@J	;WERE WE IN START/STACK STATE?
	BEQ	DD0ADV			;NO, FUNNY PLACE FOR A STACK, EAT IT
.IF EQ FT3.02
.IF NE FT.MPT
	BIT	#LS.MPT,(J)		;IF A MULTIPOINT LINE
	BNE	DDRSSC			;RESTART THE RECEIVER
.ENDC
	MOV	(R2)+,R0		;GET MSG # HE EXPECTS TO RECEIVE
	SWAB	R0			;PUT # IN RH
	DECB	R0
	CMPB	R0,LB.LAP(J)		;DID HE HEAR ME ?
	BEQ	DDRSSK			;YES FOLLOWING CODE SAME AS START
	TRAP
.IFF
	JSR	PC,DDRSNB		;ARE ANY [NCL] MEIGHBORS MSGS PENDING?
	BCS	DD0ADV			;YES, CAN'T ACK YET, EAT THE STACK
	BIC	#LS..ST!LS.STK,(J)	;NO LONGER SENDING START/STACK
	BIS	#LS.XAK,(J)		;BUT HAD BETTER SEND AN ACK
	BR	DDRSSE			;TELL NCL/NSP ABOUT NEW LINE
.ENDC
.ENDC;.IF NE,<NTLINE-DMCN>
;HERE TO TELL NEXT HIGHER LEVEL (NSP ! NCL) WE'VE RESTARTED

DDDRSS:

.IF NE FT.DDP
	TST	LB.ST2(J)		;IS THIS A DDP-DEDICATED LBLK?
	BPL	10$			;NO, NCL (OR NSP)
	MOV	J,-(P)			;YES, SAVE LBLK ADDRESS
	MOV	LB.DDB(J),J		;CONTEXT SWITCH TO DDB-NESS
	JSR	PC,DDPONL		;FLAG DDP DEVICE IS "ONLINE"
	MOV	(P)+,J			;CONTEXT SWITCH BACK TO LBLK-NESS
	RTS	PC			;ALL DONE
.ENDC ;.IF NE FT.DDP

10$:

.IF NE,FTDCP1!FTDCP3!FTDCP4
	BIT	#LS.NSP,(J)		;IS IT NSP LINE?
	BEQ	20$			;NO, GIVE TO NCL
	PJMP	NSPRSS			;TELL NSP ABOUT LINE STARTING
.ENDC; NE,FTDCP1!FTDCP3!FTDPC4

20$:	PJMP	NCLRSS			;TELL NCL ABOUT LINE STARTING
;DDRSNB  --  SEE IF ANY NCL NEIGHBORS MESSAGES ARE PENDING.
;CALL IS:
;	JSR	PC,DDRSNB
;	BCS	NEIGHBORS-STILL-PENDING
;
;THIS ROUTINE IS USED BY START/STACK TO DEFER STARTING UP A DDCMP LINE UNTIL
;THE NET KNOWS THAT THIS LINE NO LONGER HAS A NEIGHBOR (I.E., SO THAT THE
;PROPOSED NEW NEIGHBOR TRYING TO COME UP ON THIS LINE WILL BE SURE TO BE
;SEEN COMING UP FRESH).
;
;IF THE LINE IS IN USE AS A "DDP" DEVICE, THEN UNLESS THE DEVICE IS CONNECTED
;TO A HOST THEN PROTOCOL STARTUP IS DEFERRED.
;
;ON RETURN, IF CARRY IS SET THEN THERE IS AT LEAST ONE NCL NEIGHBORS MESSAGE
;PENDING TRANSMISSION (WE DON'T COUNT A QUEUED NCL MESSAGE, ONLY THE SBF.NB
;"WANT TO SEND A NEIGHBORS MESSAGE" FLAG SINCE AS LONG AS THAT IS CLEAR A NEW
;NEIGHBORS MESSAGE REQUEST WILL GET THE NEW CONFIGURATION).

DDRSNB:	MOV	SB,-(P)			;PRESERVE THE REGS

.IF NE FT.DDP
	TST	LB.ST2(J)		;DEVICE OR COMM LINE?
	BGE	50$			;COMM LINE, CHECK FOR NCL NEIGHBORS
	MOV	LB.DDB(J),SB		;ADDRESS OF DDB
	BIT	#DS.DIE,@SB		;KROAKING OFF?
	BNE	57$			;YES, DON'T ACKNOWLEDGE THE START
	BIT	#DS.CON,@SB		;NO, IS CONNECTED TO A HOST?
	BNE	15$			;YES - MAYBE
12$:	MOV	J,-(P)			;NO, SAVE J FOR A BIT
	MOV	SB,J			;POSITION ADDRESS OF DDB FOR
	JSR	PC,QUEDEV		;QUEDEV TO NUDGE THE SERVICE ROUTINE
	MOV	(P)+,J			;RESTORE LB ADDRESS
	BR	57$			;AND REJECT THE START REQUEST FOR NOW

15$:	TST	DB.RLA(SB)		;IS DEVICE FULLY CONNECTED YET?
	BEQ	57$			;NO, NOT YET, HOLD OFF STARTING UP
;DAMN	BIT	#DS.XDS,@SB		;YES, CAN WE REPORT STATUS CHANGE?
;DAMN	BNE	12$			;NO, CAN'T CHANGE STATE JUST YET
;DAMN					; (BUT ENSURE STATUS GETS SENT SOON)
	NOP				;DAMN
	NOP				;DAMN
	NOP				;DAMN
	BR	92$			;YES, HAPPY, SEND STACKS
.ENDC ;.IF NE FT.DDP

50$:	TST	NEGNGH			;ANY "NEGATIVE" NEIGHBORS RECENTLY?
	BEQ	92$			;NO, OK TO BRING UP A NEW NEIGHBOR
	MOV	#OURSCB,SB		;PRESET THE SCB LOOP
52$:	BIT	#SBF.IU,@SB		;THIS SCB ACTIVE?
	BEQ	59$			;NO, SKIP IT
	BIT	#SBF.NB,@SB		;YES, IS A NEIGHBORS MSG PENDING?
	BEQ	59$			;NO, SKIP TO NEXT CANDIDATE
57$:	TWIDDLE				;YES, THIS IS AN INTERESTING OCCURANCE
	SEC				;NEIGHBORS MESSAGES PENDING
	BR	93$			;TAKE EXCEPTION RETURN

59$:	SB.ADV	52$			;ADVANCE TO NEXT SCB

91$:	CLR	NEGNGH			;NO "NEGATIVE" NEIGHBORS MSGS PENDING
92$:	CLC				;NO NEIGHBORS PENDING
93$:	MOV	(P)+,SB			;RESTORE REGS
	RTS	PC			;RETURN HAPPILY
;HERE WHEN RECEIVE A BOOTSTRAP MESSAGE

DRBOOT:	TWIDDLE

.IF NE FT.STC
	MOVB	#1,LB.BNN+1(J)		;SET BOOT MODE FOR LINE
.IF NE FT.RDM
.IF EQ RDEDLL
	BIT	#LS.MPT,(J)		;IF RDE STATION, ZAP MSG
	BEQ	10$
	TST	LB.MPL(J)
	BNE	99$
10$:
.ENDC ;.IF EQ RDEDLL
.ENDC ;.IF NE FT.RDM
	MOV	R0,R3			;COPY ADR OF MSG
	MOV	CN.LEN(R3),R2		;GET LENGTH
	ADD	#CN.NCT,R3		;POINT TO FIRST BYTE

;HERE TO DECIDE WHO GETS THE MESSAGE
	JSR	PC,GETEXN		;GET FIRST FIELD = DNA
	TST	R0			;DID BOOT NODE INCLUDE IT ?
	BEQ	22$
	JSR	PC,FNDSCB		;IS THERE SUCH A NODE ?
	BEQ	99$			;IF UNKNOWN IGNORE MSG
	MOVB	SB.NNM(SB),LB.BNN(J)	;LOCK WHO DOES BOOTS TO NODE
	BR	50$

22$:	MOVB	LB.BNN(J),R0		;GET WHO MIGHT CARE ABOUT THIS MESSAGE
	BEQ	26$			;IF NONE SEE WHO MIGHT CARE
	JSR	PC,FNDSCB		;SEE IF SUCH A NODE EXISTS
	BNE	50$			;IF SO USE IT
26$:	MOV	PATRON,SB		;GET PREVIOUS PATRONS ADR
	BNE	32$			;TRY FOR A NEW PATRON
	MOV	#FIRSCB,SB		;START WITH FIRST SCB
30$:	BIT	#SBF.IC,@SB		;ARE WE IN CONTACT ?
.IF NE FTHOST
	BEQ	32$			;NO, TRY NEXT SCB
	BIT	#SF.MCR,@SB		;ONLY SEND TO INTELLIGENT NODES
	BNE	38$
.IFF
	BNE	38$			;IN CONTACT, SEND IT
.ENDC
32$:	SB.ADV	30$
	CLR	PATRON			;START FROM BEGINNING ON NEXT MESSAGE
	BR	99$			;IF NO MORE SCBS, IGNORE MSG

38$:	MOV	SB,PATRON		;REMEMBER WHO GOT THE LAST BOOT MSG
;HERE WHEN HAVE DECIDED WHO TO SEND MSG TO
50$:	MOV	SB,SNA			;SAVE DESTINATION SCB ADR
					; NCRESP WILL SWAP WITH DNA
	MOV	R3,SB			;COPY MSG POINTER
	MOV	R2,TEMP2		;SAVE COUNT IN TEMPORARY LOCATION
	MOV	#OURSCB,DNA		;SOURCE NODE IS US (NCRESP WILL SWAP)
	JSR	PC,NCRESP		;BUILD MSG
	BCS	99$			;CHUCK MSG IF CANT FORWARD IT
	MOV	#7,R0			;CODE FOR STATION CONTROL IS 7
	JSR	PC,PUTBYT		;PUT IT INTO MSG
	MOV	#FRSTLB,R1
	CLR	R0
52$:	MOV	LB.LNK(R1),R1
	ASSERT	NE			;TRAP IF J DOESN'T POINT TO LINE BLOCK
	INC	R0
	CMP	R1,J
	BNE	52$			;KEEP LOOKING IF WE HAVEN'T REACHED HOME
	MOV	TEMP2,R1		;GET COUNT FOR BOOT MSG
	JSR	PC,PUTBYT		;PUT LINE NUMBER INTO MSG
54$:	ADVCNK	SB			;MOVE TO NEXT CHUNK IF NECESSARY
56$:	MOVB	(SB)+,R0		;GET NEXT BYTE
	JSR	PC,PUTBYT
	CMP	#177,R2			;HAS COUNT GOTTEN LARGE ?
	BEQ	60$			;IF SO PUNT
	SOB	R1,54$			;LOOP BACK FOR REST OF BYTES
60$:	JSR	PC,@(P)+		;FINISH MSG
.ENDC;.IF NE FT.STC

99$:					;HERE WHEN DONE
.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	CMPB	#LS..DM,LB.DVS(J)	;IS IT DMC11?
	BNE	98$			;NO, ACT NORMAL
.ENDC;.IF NE,<NTLINE-DMCN>
	RTS	PC			;RETURN TO DMC11 CODE
.ENDC;.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
98$:	JMP	DDRADV			;IGNORE MESSAGE IF WE GET HERE
.ENDC;.IF NE,<NTLINE-DMCN>
;DDDGIV  --  GIVE DDCMP DATA TO NEXT HIGHER LEVEL
;
;R0 IS ADDRESS OF FIRST CHUNK IN LINKED LIST OF RECEIVED DDCMP DATA

DDDGIV:

;
;******* here is a convenient place for a sink macro call *****
;

.IF NE FT.DDP
	TST	LB.ST2(J)		;IS THIS A DDP-CONTROLLED LBLK?
	BPL	10$			;NO, NCL (OR NSP)
	MOV	J,-(P)			;SAVE LBLK ADDRESS
	MOV	LB.DDB(J),J		;CONTEXT SWITCH TO DDP DEVICE LEVEL
	JSR	PC,DDPINP		;PROCESS DDP DATA INPUT READY
	MOV	(P)+,J			;RESTORE LBLK ADDRESS
	RTS	PC			;RETURN HAVING PROCESSED INPUT MESSAGE
.ENDC ;.IF NE FT.DDP

10$:	MOV	LB.SCB(J),SNA		;SET DEFAULT SOURCE NODE
.IF EQ DGUTS
	ASSERT	NE
.IFF
	BNE	20$
	JSR	PC,FRECKS		;CHUCK THE MSG AND LOG THE ERROR
	CTYMSG	NCL
	RTS	PC
20$:
.ENDC ; .IF EQ DGUTS

.IF NE,FTDCP1!FTDCP3!FTDCP4
	BIT	#LS.NSP,(J)		;IS IT NSP LINE?
	BEQ	40$			;NO, CONTINUE NORMALLY
	PJMP	NSPINP			;YES, PASS INPUT TO NSP FIRST
.ENDC; NE,FTDCP1!FTDCP3!FTDPC4

40$:	PJMP	NCLIN0			;GIVE IT TO NCL
.SBTTL		DATA BCC
.IF NE,<NTLINE-DMCN>
;	HERE TO CALCULATE  BCC FOR A MSG
;
;	CALLING SEQUENCE:
;	R1=BYTE COUNT OF MESSAGE
;	JSR	PC,DDDBCC
;	ERROR RETURN	MESSAGE TOO LONG
;	DUP DRIVER RETURN
;	NORMAL RETURN
DDDBCC:	MOV	(P),-(P)		;COPY RETURN TO MAKE ROOM FOR BCC VALUE
	CLR	2(P)			;FOR NULL MSGS
	TST	R1			;IF LENGTH ZERO
	BEQ	16$			;ALL DONE
	CMP	R1,#MSGMAX		;ZAP HIM IF MSG TOO LOOOOONG
	BHI	17$
.IF NE FTDUP11
	CMPB	#LS..UP,LB.DVS(J)	;IS THIS A DUP-11 DEVICE??
	BNE	5$			; GUESS NOT.
	ADD	#2,(P)			;EXIT DONT BOTHER WITH CRC STACK HAS 0
	RTS	PC
5$:
.ENDC ;IF NE FTDUP11
	SAVE	<R3,R1>
	KGLOAD	#0,WORD
					;NOTE WE TAKE ADVANTAGE OF FACT THAT
;					;ALL MSGS BEGIN ON WORD BOUNDARY!!!
	MOV	#KGDATA,R3		;HERE'S WHERE THE MSG GETS STUFFED
	ASR	R1			;MAKE THE COUNT A WORD COUNT
	BEQ	13$			;SKIP WORD STUFFING IF ONE BYTE MSG
10$:	ADVCNK	R0			;SKIP TO NEXT CHUNK IF NECESSARY
	BNE	12$
11$:	SUB	#4,4(P)			;NO CHUNK?? BETTER ZAP HIM
	BR	14$			;FIX STACK, AND REGS THEN EXIT AT 14$
12$:	MOV	(R0)+,(R3)		;POINTER'S OK,SO FEED THE KG11
	SOB	R1,10$			;AND LOOP IF STILL HUNGRY
	BIT	#1,(P)			;WAS LENGTH ODD
	BEQ	14$
13$:	ADVCNK	R0			;MOVE TO NEXT CHUNK IF NECESSARY
	BEQ	11$			;ZAP HIM IF THERE'S NO BYTE
	BIC	#KG.DDB,KG.STS		;EXTRA BYTE SO CHANGE KG TO BYTE MODE
	MOVB	(R0)+,(R3)		;AND FEED HIM THE LAST BYTE
14$:	MOV	(P)+,R1			;RESTORE R1
	MOV	-(R3),4(P)		;SAVE THE BCC FROM THE KG
	MOV	(P)+,R3			;RESTORE R3
16$:	ADD	#4,(P)			;FIX THE RETURN ADDRESS
17$:	RTS	PC			;AND EXIT
.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC ; .IF NE NTLINE
.SBTTL		LINK DOWN
.IF NE NTLINE
;HERE WHEN LINE GOES DOWN

L.DOWN:	TRACE				;LEAVE TRACKS
	TWIDDLE	J			;LINE WHICH WENT DOWN
	TWIDDLE
.IIF NE FTWHYD, MOV @P,LB.WHA(J)	;SAVE WHO CALLED US
;FIRST RESET THE  LINE
;
	;JSR	PC,SL.INI
05$:
;FLUSH OUTPUT QUEUE
	MOV	#JIFSEC/REPSPD*17,LB.REP(J); DONT COME BACK FOR 15 SECONDS
	CLRB	LB.RDC(J)		;CLEAR REP DOWN COUNT
	CMP	LB.COB(J),LB.BOO(J)	;MUSTNT CAUSE BOOT MSG TO GET LOST
.IF NE FT.MPT
	BNE	6$
	BIT	#LS.MPT,(J)		;EXCEPT IF MULTI POINT
	BEQ	10$
	MOV	LB.BOO(J),R0		;IN WHICH CASE BE SURE IT GETS LOST
	JSR	PC,FRECKS
	CLR	LB.BOO(J)
	CLR	LB.BNN(J)
.IFF
	BEQ	10$
.ENDC; .IF NE FT.MPT
6$:	CLR	LB.COB(J)		;NO CURRENT BUFFER
10$:	MOV	LB.OBF(J),R0		;GET FIRST OUTPUT BUFFER
	BEQ	20$			;BRANCH IF HAVE DONE ALL
	MOV	CN.MLK(R0),LB.OBF(J)	;DELINK MSG

.IF NE FT.DDP
	TST	LB.ST2(J)		;IS THIS A DDP-CONTROLLED LBLK?
	BPL	11$			;NO - MAYBE NCL DATA
	JSR	PC,FRECKS		;YES, JUST TOSS THE DATA
	BR	14$			;AND LET IT GO AT THAT
.ENDC ;.IF NE FT.DDP

11$:

.IF NE FTDCP1!FTDCP3!FTDCP4		;DON'T REQUEUE DCP MSGS
	BIT	#LS.NSP,(J)		;IF THIS ISN'T A DCP LINE
	BEQ	12$			; THEN REQUEUE THE MSG
	JSR	PC,FRECKS		;OTHERWISE FREE THE MSG
	BR	14$			; AND CONTINUE
.ENDC; NE,FTDCP1!FTDCP3!FTDPC4

12$:	JSR	PC,MSGREQ		;REQUEUE THE MESSAGE NEXT TICK
					;(WILL BE RE-ROUTED IF POSSIBLE)
14$:	DEC	LB.OBF+4(J)		;COUNT OUT MESSAGE WE FINISHED
	BR	10$			;AND LOOPBACK FOR REST OF MESSAGES

20$:
.IF NE FT.MPT
	BIT	#LS.MPT,(J)		;IF MULTIPOINT LINE
	BEQ	27$
	BISB	#MP.OFF,LB.MPS(J)	;SET THE STATION OFFLINE
	BICB	#MP.SOL,LB.MPS(J)	;SET THE STATION OFFLINE
	BITB	#MP.SEL,LB.MPS(J)	;AND IF SELECTED
	BEQ	25$
	JSR	PC,SELNXT		;SELECT THE NEXT STATION IF POSSIBLE
25$:
.IIF NE FTRDED, JSR PC,RPTDWN		;TELL CTY DROP IS DOWN
.IF NE FT.RDM!FTTRIB
.IF NE FTTRIB
	TST	LB.MPL(J)		;DO DIFFERENT THINGS FOR TRIBS AND RDES
	BEQ	26$
	JSR	R0,RDESST		;IF A RDE DEV, REPORT THE STATE
	.BYTE	0,203
	BR	27$
26$:
	MOVB	#MP.OFF,LB.MPS(J)	;IF TRIB BE SURE ITS OFFLINE
.IFF  ;.IF NE FTTRIB
	JSR	R0,RDESST		;IF A RDE DEV, REPORT THE STATE
	.BYTE	0,203
.ENDC ;.IF NE FTTRIB
.IFF
	TST	LB.MPL(J)		;DO DIFFERENT THINGS FOR TRIBS AND RDES
	BNE	27$
	MOVB	#MP.OFF,LB.MPS(J)	;IF TRIB BE SURE ITS OFFLINE
.ENDC ;.IF NE FT.RDM!FTTRIB
27$:
.ENDC ;.IF NE FT.MPT
	ASSERT EQ LB.OBF+4(J)		;SHOULD HAVE RESET TO 0
	CLR	LB.OBF+2(J)		;CLEAR POINTER TO END OF QUEUE
	BIC	#^C<LS..RQ!LS..XQ!LS..XG!LS..RG!LS.MPT>,@J	;CLEAR MOST FLAGS
	BIS	#LS..ST!LS.XRP,@J	;NEED TO SEND A START

;***	ALTHOUGH THERE IS NOTHING REALLY IN THE DDCMP SPEC TO PROHIBIT SIMPLY
;***	STARTING OFF WITH WILD AND WIERD NUMBERS, SOME LESS-THAN-ROBUST SYSTEMS
;***	TEND TO GET TERRIBLY CONFUSED IF THE FIRST DDCMP NUMBERED MESSAGE ISN'T
;***	NUMBER "1", SO . . . RESET THE MESSAGE NUMBERS
;***	MOVB	LB.LAP(J),LB.HSN(J)	;HIGHEST PROC IS HIGHEST SENT
;***	MOVB	LB.HSN(J),LB.LAR(J)
	CLR	LB.ROK(J)		;RESET MSG NUMBERS
	CLR	LB.LAP(J)

.IF NE FT.DDP
	TST	LB.ST2(J)		;IS THIS A DDP-CONTROLLED LINE?
	BPL	40$			;NO, DO NCL'ISH STUFF
	MOV	J,-(P)			;YES, SAVE LBLK ADDRESS
	MOV	LB.DDB(J),J		;CONTEXT SWITCH TO DDP DEVICE LEVEL
	JSR	PC,DDPOFL		;SAY THE DDP DEVICE IS "OFFLINE"
	MOV	(P)+,J			;RESTORE DDB ADDRESS
	RTS	PC			;NOTHING MORE TO DO
.ENDC ;.IF NE FT.DDP

40$:	MOV	LB.SCB(J),SB		;IS THERE A SCB ADR
	BEQ	90$			;IF NOT DON'T NEED TO ROUTE
;	CLR	SB.LBA(SB)		;CLEAR ROUTE TO GET TO SCB
.IF NE FTDCP3!FTDCP4
	BIT	#LS.NSP,@J		;NSP LINE?
	BEQ	60$			;NO, NCL DOESN'T CARE
	JSR	PC,NSPDWN		;NSP HAS LBLK STUFF IT HAS TO CLEAR
60$:
.ENDC; NE,FTDCP3!FTDPC4
	CLR	LB.SCB(J)		;NEED DIFFERENT SCB FOR THIS LINE BLOCK
	BIT	#SF.HID,@SB		;DID I KNOW WHO HE WAS ?
	BNE	70$			;IF WE DID SEND NEIGHBOURS TO WORLD
	CLR	@SB			;RELEASE SCB
	BR	90$
70$:	JSR	PC,ROUTE		;REROUTE THE WORLD
	JSR	PC,SNDNGD		;TELL EVERYONE A NEIGHBOR WENT DOWN
90$:	RTS	PC
COMODN:				;COMMON OUTPUT DONE

;
;******* here is a convenient place for a sink macro call *****
;

.IF NE FT.DDP
	TST	LB.ST2(J)		;IS THIS A DDP-CONTROLLED LINE?
	BPL	10$			;NO, NCL OR SIMILAR
	MOV	J,-(P)			;SAVE LBLK ADDRESS
	MOV	LB.DDB(J),J		;CONTEXT SWITCH TO DDP-DEVICE LEVEL
	JSR	PC,DDPODN		;PROCESS OUTPUT DONE
	MOV	(P)+,J			;RESTORE LBLK ADDRESS
	RTS	PC			;ALL DONE WITH THAT DATA
.ENDC ;.IF NE FT.DDP

10$:

.IF NE FT.RDM
	BIT	#LS.MPT,(J)		;IF RDE STATION, CHUCK MSGS
	BEQ	20$
	TST	LB.MPL(J)
	BEQ	20$
	PJMP	RDEODN
.ENDC ;.IF NE FT.RDM
20$:

.IF NE,FTDCP1!FTDCP3!FTDCP4
	BIT	#LS.NSP,(J)		;IS IT AN NSP LINE?
	BEQ	30$			;NO, PASS MESSAGES TO NCL
	PJMP	NSPODN			;TELL NSP THAT OUTPUT IS DONE
.ENDC; NE,FTDCP1!FTDCP3!FTDPC4

30$:	PJMP	NCLODN			;GIVE MESSAGE BACK TO NCL
.ENDC ; .IF NE NTLINE
.IF NE FTRDED
;ROUTINE TO TELL CTY A DROP WENT OFFLINE
RPTDWN:	MOV	LOWUP,LB.OFA+2(J)	;GET LOW ORDER UPTIME AND SAVE IT
	MOV	HIGHUP,LB.OFA(J)	;SAVE HIGH ORDER
	MOVB	LB..LN(J),R0		;GET DH LINE #
	JSR	PC,GIVASC		;GET IT BACK PRINTABLE
	MOVB	R0,L.RDXL+1		;SAVE IT AS RDA #
	SWAB	R0			;GET HIGH ORDER
	MOVB	R0,L.RDXL		;SAVE IT
	MOVB	LB.MPA(J),R0		;GET DROP #
	JSR	PC,GIVASC		;GET IT BACK PRINTABLE
	MOVB	R0,L.RDXD+1		;SAVE IT IN STRING
	SWAB	R0			;GET HIGH ORDER
	MOVB	R0,L.RDXD		;SAVE IT
	SAVE	<J,R1,R3>
	MOV	#CTYDDB,J		;THIS IS FOR THE CTY
	MOV	#DWNSTR,R1		;NOW TYPE THE STRING AT THE CTY
10$:	MOVB	(R1)+,R0		;GET A CHAR
	BEQ	20$			;DONE IF ZERO
	JSR	PC,QTYCHR		;TYPE IT
	BR	10$			;KEEP GOING

20$:	JSR	PC,BEGXMT		;START IT
	RESTORE	<R3,R1,J>
	RTS	PC			;RETURN

;ROUTINE TO RETURN IN R0 PRINTABLE CHARS. UP TO 77
GIVASC:	MOV	R0,-(P)			;SAVE VALUE
	ASR	R0			;GET HIGH ORDER FIRST
	ASR	R0
	ASR	R0
	BIC	#^C7,R0			;JUST 3 BITS PLEASE
	ADD	#60,R0			;MAKE IT ASCII
	MOVB	R0,1(P)			;SAVE IT IN HIGH ORDER BYTE
	MOV	(P),R0			;GET BACK VALUE
	BIC	#^C7,R0			;JUST LOW ORDER 3 BITS
	ADD	#60,R0			;MAKE IT ASCII
	MOVB	R0,(P)			;SAVE IT
	MOV	(P)+,R0			;GET BACK BOTH BYTES
	RTS	PC			;AND RETURN THEM TO USER

DWNSTR:	.BYTE	15,12,7,7		;CR, LF,BELL,BELL
	.ASCII	/Drop /
L.RDXD:	.BYTE	0,0			;DROP #
	.ASCII	/ on RDE /
L.RDXL:	.BYTE	0,0			;DH LINE # HERE
	.ASCII	/ went offline?/
	.BYTE	15,12,7,7,0		;THATS ALL...
	.EVEN
.ENDC	;.IF NE FTRDED
.SBTTL	DDPSER - DDP DEVICE SERVICE ROUTINES

.IF NE FT.DDP
  .IF NE DDPN

;THE DDP DEVICE ALLOWS THE VARIOUS DDCMP LINES TO BE USED AS DIRECT I/O
;DEVICES RATHER THAN AS NCL (OR NSP) NETWORK COMM LINES.

;DDP DEVICE PARAMETERS SETTABLE ON A "PER-LINE" BASIS
;
;	DPnnWID		;"RECORD SIZE" OR MESSAGE SIZE
;	DPnnCHK		;CHUNKS-PER-DATAREQUEST WEIGHTING
;	DPnnRNN		;RESTRICTED HOST ASSIGNMENT
;	DPnnPFH		;PREFERRED HOST ASSIGNMENT


;DB.DCS DDP-SPECIFIC DEVICE STATUS BITS

	DPS.AV=000001	;DDCMP IS AVAILABLE (L2.DDP IS SET)
	DPS.RN=000002	;DDCMP IS RUNNING (PROTOCOL UP)

	DPS.BL=010000	;"BLOCK TOO LARGE" ERROR
	DPS.DT=020000	;"DATA" ERROR (*)
	DPS.DE=040000	;"DEVICE" ERROR (*)
	DPS.IP=100000	;"IMPROPER MODE" ERROR (*)
			; * = NOT USED BY -11, ONLY WITHIN THE -10
	DPS.ER=DPS.BL!DPS.DT!DPS.DE!DPS.IP


;GENERATE DDB "UNIT-SPECIFIC" VALUES

.MACRO	X	XZZ

DP'XZZ'STS=LBLK'XZZ	;DUMMY DB.HDW "HARDWARE ADDRESS"

.IIF NDF DP'XZZ'WID,DP'XZZ'WID=DDPMML
.IIF NDF DP'XZZ'CHK,DP'XZZ'CHK=DDPCHK
.IIF NDF DP'XZZ'RNN,DP'XZZ'RNN=DDPRNN
.IIF NDF DP'XZZ'PFH,DP'XZZ'PFH=DDPPFH
.ENDM ;.MACRO X

ZZ=0

.REPT	DDPN
	X	\ZZ
	ZZ=ZZ+1
.ENDR ;.REPT DDPN
;DDB ENTRIES SPECIFIC TO DDP DEVICES

DB.LBK=DB.SIZ+00	;ADDRESS OF ASSOCIATED LBLK
DB.DDI=DB.SIZ+02	;ADDRESS OF FIRST QUEUED (TO -10) DDCMP DATA MESSAGE
;      DB.SIZ+04	;ADDRESS OF LAST QUEUED
;      DB.SIZ+06	;COUNT OF QUEUED DDCMP DATA MESSAGES WAITING


;MACRO TO GENERATE DEVICE-SPECIFIC DDB STUFF

.MACRO	DDXGEN	DEV,DV,DRQ,QBITS,XZZ
	.WORD	LBLK'XZZ	;DB.LBK
	.WORD	0, 0, 0		;DB.DDI
.ENDM ;.MACRO DDXGEN


;FINALLY, GENERATE THE DDP DDBS

DRESET=0

DDBGEN	DDP,DP,DDPN,DDPDRQ,<DS.OUT>


;CLEAR DEFINITION OF DDXGEN

.MACRO	DDXGEN	DEV,DV,DRQ,QBITS,XZZ
.ENDM
;DDPSER - DDP DEVICE SERVICE ROUTINE

DDPSER:	CMP	#DDPMML,DB.MML(J)  ;GOT A REASONABLE MESSAGE LIMIT?
	BGE	10$		;YES
	MOV	#DDPMML,DB.MML(J)  ;NO, SET OUR MAXIMA
10$:	MOV	#DCM.IM,DB.DCM(J)  ;SET DATA TYPE "IMAGE" DATA ONLY
	MOVB	#OBJDDP,DB.ROT(J)  ;FORCE CONNECT TO HOST'S DDP SERVICE
	MOV	DB.LBK(J),R0	;ADDRESS OF ASSOCIATED LINE BLOCK
	MOVB	LB.DVS(R0),R0	;DEVICE-SERVICE TYPE
	ASR	R0		;CONVERT TO DIRECT TYPE
	CMPB	#LS..AL,R0	;SYNC OR ASYNC?
	BHI	11$		;SYNC, USE VALUE AS IS
	ADD	#200-LS..AL,R0	;ASYNC, "SHIFT"
11$:	MOVB	R0,DB.DVV(J)	;SET "CONTROLLER" TYPE

	JSR	PC,DVCCFM	;DO DIS/CONNECT CONFIRM AS NEEDED

;REGENERATE DPS.AV AND DPS.RN ON GENERAL PRINCIPLES (THIS ENSURES NO
;SYNCHRONIZATION ERRORS BETWEEN ON/OFF LINE AND DIS/CONNECT, AS WELL AS
;PEOPLE POKING THE -11 WITH DDT11 . . .)

	MOV	DB.LBK(J),R0	;ADDRESS OF ASSOCIATED LINE BLOCK
	MOV	DB.DCS(J),-(P)	;SAVE COPY OF CURRENT DCS
	BIS	#DPS.AV!DPS.RN,DB.DCS(J)  ;ASSUME GOODNESS
	BIT	#LS..ST!LS.STK,LB.STS(R0) ;IS  DDCMP RUNNING?
	BEQ	17$		;YES, GOOD SO FAR
	BIC	#DPS.RN,DB.DCS(J)  ;DDCMP IS NOT RUNNING AFTER ALL
17$:	BIT	#L2.DDP,LB.ST2(R0) ;IS THE LBLK AVAILABLE TO US?
	BNE	18$		;YES
	BIC	#DPS.AV!DPS.RN,DB.DCS(J)  ;NO, THE DDP IS USELESS
18$:	CMP	(P)+,DB.DCS(J)	;DID WE CHANGE ANYTHING?
	BEQ	19$		;NO, NO NEED TO BOTHER THE -10
	BIS	#DS.XDS,@J	;YES, MUST TELL THE -10
19$:	JSR	PC,DVXDCS	;SEND DEVICE STATUS IF NEEDED
	BIC	#DPS.ER,DB.DCS(J)  ;CLEAR ERROR STATUS
	TST	DB.OBF(J)	;GOT ANY OUTPUT PROCESSING TO DO?
	BNE	DDPOS0		;YES, PROCESS OUTPUT FIRST
DDPSI0:	JMP	DDPIS0		;NO, JUST CHECK THE INPUT SIDE OF THINGS
;DDPOS0 - OUTPUT SERVICE

DDPOS0:	CMP	#ERSCNT,FRECNT	;GOT ROOM TO DO ANYTHING?
	BLT	DDPOS1		;YES, FIRE UP NCL OUTPUT COPY
	TWIDDLE			;NO, THIS IS INTERESTING
	BIS	#DS.COR,@J	;FLAG WE'D LIKE TO RUN AGAIN SOMEDAY
	JMP	DDPIS1		;DON'T EVEN SEND DATA REQUESTS FOR NOW

DDPOS1:	JSR	PC,DVGBYT	;START READING NEXT NCL SUBMESSAGE
	  TRAP			;BETTER NOT STILL BE IN A MESSAGE!
	  BR	10$		;START OF NEW SUBMESSAGE (EXPECTED)
	  BR	DDPSI0		;ALL DONE, DO INPUT SERVICE (AND DRQ)

;PROCESS NCL MESSAGE

10$:	DEC	R0		;1
	BEQ	13$		;  = DATA WITHOUT EOM
	DEC	R0		;2
	BEQ	DDPOS5		;  = DATA WITH EOM
	DEC	R0		;3
	BEQ	30$		;  = STATUS
	DEC	R0		;4
	BEQ	40$		;  = CONTROL

;HERE IF UNKNOWN NCL MESSAGE TYPE

13$:
.IF EQ DGUTS
	TRAP
.IFF
	CTYMSG	NCL		;COMPLAIN
15$:	JSR	PC,DVGBYT	;LOOP EATING UNKNOWN NCL
	  BR	15$		;KEEP EATING
	  BR	10$		;TRY NEXT NCL MESSAGE
	  BR	DDPSI0		;ALL DONE WITH OUTPUT SERVICE
.ENDC ;.IF EQ DGUTS


;HERE TO PROCESS RECEIVED NCL STATUS

30$:	JSR	PC,DVRSTS	;PROCESS RECEIVED STATUS
	BR	DDPOS1		;SO MUCH FOR THAT


;HERE TO PROCESS RECEIVED NCL CONTROL

40$:	BR	13$		;NO CONTROL IMPLEMENTED YET
;HERE TO PROCESS RECEIVED DATA

DDPOS5:	MOV	DB.OCN(J),R2	;COUNT OF SUBMESSAGE BYTES LEFT
	BEQ	DDPOS0		;LOOP BACK FOR MORE DATA
	BIT	#DPS.AV,DB.DCS(J)  ;CAN WE GIVE THE DATA TO THE LBLK?
	BNE	50$		;YES
10$:	JSR	PC,DVGBYT	;NO, EAT THE DATA MESSAGE
	  BR	11$		;EAT EXPECTED DATA BYTE
	  TRAP			;++ START OF NEW MESSAGE BEFORE COUNT RAN OUT!
	  TRAP			;++ OUT OF DATA BEFORE COUNT RAN OUT!
11$:	SOB	R2,10$		;EAT ANY DATA REMAINING
	BIS	#DPS.IP,DB.DCS(J)  ;IT IS "IMPROPER" TO SEND DATA
	PJMP	QUEXDS		;COMPLAIN TO THE -10

50$:	MOV	DB.OAD(J),R1	;ADDRESS OF SUBMESSAGE BYTES LEFT
	JSR	PC,CABLTB	;ALLOCATE AND COPY BYTE STREAM
	MOV	R1,DB.OAD(J)	;SET NEW CURRENT SUBMESSAGE BYTE GETTER
	MOV	R2,DB.OCN(J)	;AND NEW CURRENT SUBMESSAGE BYTE COUNTER
	ASSERT	EQ		;WHICH HAD BETTER BE EMPTY!
	MOV	J,-(P)		;SAVE DDB ADDRESS
	MOV	DB.LBK(J),J	;CONTEXT SWITCH TO LBLK
	MOV	#SOH,R2		;DECLARE DDCMP "DATA" MESSAGE
	JSR	PC,DDQDDP	;GIVE OUTPUT (IN R0) TO DDCMP
	MOV	(P)+,J		;BACK TO DDB-LEVEL
	BR	DDPOS0		;LOOP BACK FOR MORE OUTPUT
;DDPIS0 - INPUT SERVICE

DDPIS0:	MOV	DB.LBK(J),R3	;ASSOCIATED LBLK
	CMP	#10,LB.OBF+4(R3)  ;GOT TOO MUCH DATA BACKED UP?
	BLE	DDPIS1		;YES, NO DATA REQUESTS YET
	JSR	PC,DVXDRQ	;SEND DATA REQUESTS AS NEEDED

;PROCESS ANY INPUT WAITING FOR THE -10

DDPIS1:	MOV	DB.DDI(J),R0	;GOT ANY INPUT DATA TO SEND
	BEQ	20$		;NO
	TST	DB.RLA(J)	;YES, GOT ANYWHERE TO SEND IT?
	BNE	DDPIS5		;YES, SEND INPUT DATA TO HOST

;DATA BUT NO HOST YET. EAT THE DATA

;RDH	JSR	PC,FRECNL	;CLEAR UP THE INPUT QUEUE
;RDH	CLR	DB.DDI(J)	;NO INPUT LEFT ANYMORE
;RDH	CLR	DB.DDI+2(J)	; . . .
;RDH	CLR	DB.DDI+4(J)	; ! ! !

;MAKE SURE A HOST IS FORTHCOMING

20$:	BIT	#DS.CON,@J	;ARE WE CONNECTED?
	BNE	DDPIS9		;YES, FINE, JUST DISAPPEAR
	JSR	PC,FNDPFH	;LOOK FOR A SUITABLE HOST
	  BEQ	DDPIS9		;NONE, PLAIN OUT OF LUCK
	JSR	PC,SNDCON	;TRY TO CONNECT TO LUCKY HOST
	BR	DDPIS9		;WAIT AND SEE WHAT HAPPENS
;PROCESS INPUT DATA QUEUED FOR THE HOST

DDPIS5:	TSTB	DB.IDR(J)	;GOT ANYTHING TO DO WITH THE DATA?
	BEQ	DDPIS9		;NO DATA REQUESTS, NO FURTHER WE CAN GO

;PACKAGE THE DATA AND SHIP IT TO THE HOST

10$:	MOV	#002,R0		;NCL DATA WITH EOM MESSAGE TYPE
	JSR	PC,DVDBSM	;BEGIN BIG NCL DATA SUBMESSAGE
	  BCS	DDPIS9		;ON SECOND THOUGHT, PUT IT OFF FOR AWHILE

20$:	MOV	DB.IAD(J),R0	;NCL CHUNK-BYTE ADDRESS TO RECEIVE DATA
	MOV	DB.DDI(J),R1	;CURRENT CHUNK ADDRESS HOLDING DATA
	ASSERT	NE		;MAKE SURE IT IS STILL THERE
	MOV	CN.MLK(R1),DB.DDI(J)  ;DELINK FROM INPUT QUEUE
	BNE	23$		;SKIP IF STILL MORE TO COME
	CLR	DB.DDI+2(J)	;IF NO MORE FIRST, THEN NO LAST EITHER
23$:	DEC	DB.DDI+4(J)	;COUNT DOWN PENDING "DATAGRAMS"
	MOV	CN.LEN(R1),R2	;COUNT OF DATA BYTES IN INPUT MESSAGE
	BEQ	55$		;NO DATA TO COPY
;***	CMP	DB.MML(J),R2	;MESSAGE SIZE WITHIN BOUNDS?
	CMP	#DDPMML,R2	;*** SIGH CONNECT CONFIRM LEAVES THIS 0!
	BHIS	25$		;YES, STILL SAFE
	BIS	#DPS.BL,DB.DCS(J)  ;NO, NOTE "BLOCK TOO LARGE" ERROR
;***	MOV	DB.MML(J),R2	;SEND AS MUCH AS WE CAN
	MOV	#DDPMML,R2	;*** SIGH

25$:	ADD	R2,DB.ICN+4(J)	;PRECOUNT DATA BYTES TO BE COPYED
	ADD	#CN.NCT,R1	;POINT R1 TO ACTUAL DATA BYTES
	JSR	PC,CNBLTB	;COPY DDCMP DATA INTO NCL MESSAGE
	MOV	R0,DB.IAD(J)	;UPDATE NCL MESSAGE POINTER
55$:	MOV	R1,R0		;TRAILING CHUNK ADDRESS FOR DDCMP MESSAGE
	BIC	#CNKSIZ-1,R0	;BACK UP TO START OF CHUNK
	JSR	PC,FRECKS	;DEALLOCATE WHATEVER'S LEFT
	JSR	PC,DVDSBM	;CLOSE OFF NCL SUBMESSAGE
	JSR	PC,NCLIN1	;GIVE IT TO NCL TO ROUTE TO HOST
	CLR	DB.IBF(J)	;WE DON'T OWN IT ANYMORE
	DECB	DB.IDR(J)	;USED UP ONE DATA REQUEST
	BIT	#DPS.ER,DB.DCS(J)  ;GOT ANY ERRORS PENDING?
	BNE	59$		;NO
	PJMP	QUEXDS		;YES, NEED TO TELL -10
59$:	TST	DB.DDI(J)	;GOT ANY MORE DDCMP DATA MESSAGES?
	BNE	DDPIS5		;YES, TRY TO SHIP MORE
DDPIS9:	RTS	PC		;ALL DONE WITH THIS DDP DEVICE
;DDPINI - DDP SERVICE INITIALIZATION

DDPINI:	RTS	PC		;YAWN



;DDPTIM - DDP SERVICE TIMEOUT

DDPTIM:	TRAP			;NOT IN USE



;DDPODN - DDP SERVICE "OUTPUT DONE" FROM DRIVER LEVEL
;
;CALLED WITH FINISHED MESSAGE IN R0

DDPODN:	JSR	PC,FRECKS	;FREE OUTPUT MESSAGE
	JMP	QUEDEV		;NUDGE THE DEVICE (DATA REQUESTS, ETC.)



;DDPINP - DDP SERVICE "INPUT DONE" FROM DRIVER LEVEL
;
;CALLED WITH NEWLY-RECEIVED MESSAGE IN R0

DDPINP:	MOV	DB.DDI+2(J),R1	;ADDRESS OF LAST MESSAGE QUEUED UP
	BNE	12$		;ADD NEW MESSAGE TO END OF QUEUE
	MOV	R0,DB.DDI+0(J)	;SET NEW FIRST MESSAGE QUEUED UP
.IF EQ DGUTS
	TST	DB.DDI+4(J)	;IS COUNT ALSO 0?
	BEQ	17$		;YES, ALL SET
	TRAP			;DISCREPANCY
.IFF
	BR	17$		;CAP OFF QUEUE
.ENDC ;.IF EQ DGUTS

12$:	MOV	R0,CN.MLK(R1)	;PUT LATEST ENTRY AT END OF INPUT QUEUE
17$:	MOV	R0,DB.DDI+2(J)	;SET NEW LAST MESSAGE IN QUEUE
	INC	DB.DDI+4(J)	;COUNT NUMBER OF QUEUED MESSAGES
	PJMP	QUEDEV		;QUEUE DEVICE FOR SERVICE
;DDPONL - DDP SERVICE "ONLINE CONDITION" FROM DRIVER LEVEL

DDPONL:	BIS	#DPS.RN,DB.DCS(J)  ;NOTE THAT DDCMP IS RUNNING
	PJMP	QUEXDS		;TELL THE -10 THE GOOD NEWS



;DDPOFL - DDP SERVICE "OFFLINE CONDITION" FROM DRIVER LEVEL

DDPOFL:	BIC	#DPS.RN,DB.DCS(J)  ;NOTE THAT DDCMP IS DOWN
	PJMP	QUEXDS		;TELL THE -10 THE BAD NEWS

  .ENDC ;.IF NE DDPN
.ENDC ;.IF NE FT.DDP


;*****	END OF THE DDP SERVICE ROUTINES
.SBTTL		RECEIVER INTERRUPT
.IF NE NTLINE
.IF NE,<NTLINE-DMCN>
;HERE AT INTERRUPT LEVEL WHEN FINISH RECEIVING 8 CHARS OF JUNK

DDRJNK:	MOV	LB.IPT(J),R1		;GET PUTTER ADR
	ADD	J,R1			;MAKE ADR OF BUFFER
	MOVB	(R1)+,R0		;GET 1ST CHAR OF HDR
	CMPB	R0,#SOH			;DID 1ST CHAR SAY DATA MSG ?
	BEQ	DRJ.30
	CMPB	R0,#DLE			;BEGINING OF BOOTSTRAP MSG ?
	BEQ	DRJ.30			;IF SO TREAT AS NUMBERED MSG
	CMPB	R0,#ENQ			;DID 1ST CHAR SAY CONTROL MSG ?
	BNE	DRJ.26			;IF NOT CONTROL CLEAR RECEIVER
	CMPB	(R1),#7			;IF NOT A CONTROL CODE
	BHI	DRJ.26			;CLEAR THE RECEIVER

;HERE AT WHEN HAVE RECEIVED 1ST 4 CHARS OF CONTROL MSG
	MOV	#DRJ.20,LB.RDN(J)	;WHERE TO GO WHEN FINISH MSG
DRJ.10:	MOV	LB.IPT(J),R0		;GET INPUT PUTTER
.IIF NE FT.QSB,	MOV	R0,R1
	ADD	#12,R0			;ADVANCE TO NEXT BUFFER
	CMP	R0,#LB.IBF+<NINBUF*12>	;TIME TO WRAP AROUND YET ?
	BNE	10$
	MOV	#LB.IBF,R0		;YES
10$:	CMP	R0,LB.ITK(J)		;HAVE WE CAUGHT UP TO TAKER ?
	BEQ	DRJ.25			;IF SO CAN'T ACCEPT LAST
	ADD	J,R0			;MAKE ABS ADR OF BUFFER
	CLR	(R0)
.IF NE FT.QSB
	ADD	J,R1
	CMPB	#DLE,@R1		;WAS THIS A MAINT MSG
	BEQ	20$			; THEN ALWAYS ASSUME QSYNC EVEN
					; THOUGH THE DMC WON'T SET IT.
	BITB	#QSYNC,2(R1)
	BEQ	30$
.ENDC
20$:	BIS	#LS.SSY,@J		;STRIP SYNCH AGAIN
30$:	MOV	#-4,R1			;LENGTH OF WHAT TO READ
.IF NE NALINE+NMPTL+NTRIB
	CMPB	#LS..AL,LB.DVS(J)	;IF ASYNC LINE, SCAN BY CHAR
	BHI	40$
	ADD	#3,R1
40$:
.ENDC
	RTS	PC			;BACK TO SYNCHRONOUS LINE HANDLR
;HERE WHEN HAVE RECEIVED LAST 4 CHARS OF CONTROL MSG
DRJ.20:	JSR	PC,DRJ.90		;CHECK THE BCC
DRJ.21:	MOV	LB.IPT(J),R0		;GET REL ADR JUST FINISHED
	ADD	#12,R0			;ADVANCE TO NEXT BUFFER
	CMP	R0,#LB.IBF+<NINBUF*12>	;TIME TO WRAP AROUND ?
	BNE	22$
	MOV	#LB.IBF,R0		;YES
22$:	MOV	R0,LB.IPT(J)		;REMEMBER NEW PUTTER
	ADD	J,R0			;MAKE POINTER ABSOLUTE
	CLR	10(R0)			;CLEAR CHUNK POINTER
	QUEPUT	QI 24$			;PUT LAST MSG INTO QUEUE
.IF NE NALINE+NMPTL+NTRIB
	CMPB	#LS..AL,LB.DVS(J)	;IF ASYNC LINE, MUST FORCE RESYNCH
	BHI	23$
.IF NE NDHMPT!NADHLN!NDHTRB
	MOV	#DHRSYN,R1
.IFF
	MOV	#ERRINT,R1		;IF THIS HAPPENS, ITS DOOMERS
.ENDC
.IF NE NDZMPT!NADZLN!NDZTRB
	CMPB	#LS..DZ,LB.DVS(J)	;DZ LINE?
	BNE	24$			;NO
	MOV	#DZRSYN,R1		;YES, SET FOR DZ SYNC INPUT
24$:
.ENDC
	MOV	R1,LB.RDN(J)		;SAVE ADDRESS
	INC	R0			;NEXT BUFFER IS +1 CHAR
	MOV	#-1,R1			;AND ITS LENGTH IS 1
	RTS	PC
23$:
.ENDC
	ADD	#4,R0			;POINT TO 2ND HALF OF HEADER
	MOV	#-4,R1			;TO FINISH NEXT HALF
	MOV	#DDRJNK,LB.RDN(J)	;WHEN THIS ONE FINISHES
	RTS	PC
;HERE IF LOW LEVEL CAN'T KEEP UP
DRJ.25:	TWIDDLE				;COUNT TIMES THIS HAPPENS
	MOVB	#NCDNRM,LB.NCD(J)	;WE RAN OUT OF ROOM,
	JSR	PC,DRNAK1		; SO SEND A NAK
	BR	DRJ.27
;HERE IF RECEIVED NONVALID MESSAGE BEGINER
DRJ.26:
	TWIDDLE	R0
	TWIDDLE
DRJ.27:	TRACE	DD			;TRACE TIMES THIS HAPPENS
.IF NE NMPTL+NTRIB+NALINE
	CMPB	#LS..AL,LB.DVS(J)
	BHI	19$
	CLR	LB.SRR+2(J)		;MUST FORCE THE RECEIVER TO STOP
					;FOR RESYNCH ON MSG BOUNDARY
19$:
.ENDC
	CLR	R1			;PASS 0 DATA COUNT
	RTS	PC
;HERE WHEN HAVE JUST RECEIVED 1ST FOUR CHARS OF DATA HEADER
DRJ.30:	MOVB	(R1)+,-(P)		;GET LOW ORDER BITS OF MSG SIZE
	MOVB	(R1)+,1(P)		;GET HIGH ORDER BITS OF SIZE
	JSR	PC,GETCNK		;GET A CHUNK TO PUT DATA INTO
	BEQ	30$			;IF NO CORE CAN'T HANDLE MESSAGE
	MOV	LB.IPT(J),R1		;GET REL HEADER ADR
	ADD	J,R1			;MAKE ABSOLUTE
	ASSERT EQ 10(R1)
	MOV	R0,10(R1)		;SAVE LINK TO FIRST CHUNK
	MOV	R0,LB.CIB(J)		;ALSO SAVE CURRENT CHUNK ADR
	CLR	CN.MLK(R0)		;CLEAR LINK TO NEXT MSG
	MOV	(P)+,R1			;GET SIZE BACK
	BIC	#<SELBIT!QSYNC>*400,R1	;STRIP EXTRA BITS
	MOV	R1,CN.LEN(R0)		;PUT SIZE INTO THE CHUNK
	ADD	#2,R1			;ADD 2 FOR BCC
	MOV	R1,LB.CIB+2(J)		;SAVE PARTIAL COUNT
	ADD	#CN.NCT,R0		;ADR TO BEGIN PUTTING DATA
	MOV	#DRJ.39,LB.RDN(J)	;WHERE TO GO WHEN FINISH GETTING HDR
	CMP	R1,#CNKSIZ-CN.NCT	;WILL MESSAGE FIT IN A CHUNK ?
	BLE	DRJ.45
	MOV	#CN.NCT-CNKSIZ,R1	;CHARS TO READ INTO 1ST CHUNK
	ADD	R1,LB.CIB+2(J)		;ADJUST CHARS LEFT
	RTS	PC
;HERE BECAUSE NO CHUNKS FOR MESSAGE
30$:	MOV	(P)+,R0			;POP COUNT OFF STACK
;HERE WHEN RECEIVING A DATA MESSAGE AND RUN OUT OF CORE
DRJ.35:	TWIDDLE
	MOV	LB.IPT(J),R0		;GET INPUT PUTER
	ADD	#12,R0			;ADVANCE IT
	CMP	R0,#LB.IBF+<12*NINBUF>	;HIT END ?
	BNE	10$
	MOV	#LB.IBF,R0		;YES SO POINT TO BEGINING
10$:	CMP	R0,LB.ITK(J)		;DID PUTER HIT TAKER ?
	BEQ	DRJ.27			;IF SO CAN'T HELP HIM
	MOV	R0,LB.IPT(J)		;SO LOW LEVEL WILL NAK MESSAGE
	BR	DRJ.27
;HERE TO CHECK  HEADER BCC FOR DATA
DRJ.39:	JSR	PC,DRJ.90		;CHECK THE HEADER
	MOV	#DRJ.40,LB.RDN(J)	;CONTINUE READING DATA IF OK
;
;HERE TO READ ANOTHER BLOCK OF DATA
DRJ.40:	JSR	PC,GETCNK		;GET A CHUNK TO PUT STUFF IN
	BEQ	DRJ.35			;IF NO CORE CAN'T HANDLE MESSAGE
	MOV	R0,@LB.CIB(J)		;SET LINK IN CURRENT CHUNK
	MOV	R0,LB.CIB(J)		;AND THIS BECOMES CURRENT CHUNK
	TST	(R0)+			;SKIP LINK WORD
	MOV	LB.CIB+2(J),R1		;LETS TRY TO READ EVERYTHING
	CMP	R1,#CNKSIZ-2		;CHECK FOR TOO LARGE
	BLE	DRJ.46			;BRANCH IF THAT FITS
	MOV	#2-CNKSIZ,R1		;READ ONE CHUNK INSTEAD
	ADD	R1,LB.CIB+2(J)		;UPDATE COUNT LEFT TO GO
	RTS	PC			;AND RETURN TO SYNCHRONOUS LINE HANDLER
;HERE WHEN THIS CHUNK WILL SWALLOW REST OF MESSAGE
DRJ.45:	MOV	#DRJ.09,LB.RDN(J)	;GO HERE WHEN DONE WITH THIS
DRJ.50:	CLR	LB.CIB+2(J)		;SET NO MSG INCREMENT LEFT
	NEG	R1			;COUNT IS JUST WHAT WE NEED
	RTS	PC
DRJ.46:	MOV	#DRJ.08,LB.RDN(J)	;WHERE TO GO WHEN FINISHED
	BR	DRJ.50
;
;HERE TO CHECK HEADER BCC
DRJ.90:
	MOV	LB.IPT(J),R0		;GET THE BUFFER
	ADD	J,R0
.IF NE FTDUP11
	CMPB	#LS..UP,LB.DVS(J)	;IS THIS IS DUP-11. IF SO SKIP
	BNE	5$			; IF NOT, GO DO NORMAL BCC.
	BIT	#UP$RCC,LB.STY(J)	;IF IT IS A DUP. 'UP$BCC' => GOOD BCC
	BEQ	DRJ.94			; IF IT ISN'T SET, WE HAVE BCC ERROR
	RTS	PC			;IF IT IS SET, GIVE GOOD RETURN
5$:
.ENDC	;IF NE FTDUP11
	MOV	#KG.STS,R1		;SAVE KG11 STATE SINCE ALSO USED AT LOW LEVEL
	MOV	(R1)+,-(SP)		;KG.STS: SAVE OLD MODE
	MOV	@R1,-(SP)		;KGBCC: SAVE PARTIAL BCC
	MOV	#KG.INI!KG.DDB,-(R1)	;KG.STS: REINIT FOR CRC-16
	MOV	#KGDATA,R1		;FASTER THAN CMP (R1)+,(R1)+
	MOV	(R0)+,@R1		;KGDATA: CALCULATE HEADER BCC
	MOV	(R0)+,@R1
	MOV	(R0)+,@R1
	MOV	-(R1),R2		;KGBCC: SAVE FOR COMPARE AFTER RESTORING STATE
	MOV	#KG.SEN!KG.CLR!KG.DDB!KG.LRC,-(R1);KG.STS: PREPARE TO RELOAD KGBCC
	MOV	(SP)+,KGDATA		;THIS RELOADS SAVED PARTIAL BCC
	MOV	(SP)+,@R1		;KG.STS THIS RESTORES THE MODE
	CMP	R2,(R0)+		;FINALLY - DO THE BCC'S MATCH?
	BNE	DRJ.91
	RTS	PC			;RETURN EVERY THING IS AOK
DRJ.94:	ADD	#10,R0			;BUMP POINTER TO CHUNK LINK
DRJ.91:	MOVB	#NCDBCC,LB.NCD(J)	;HEADER ERROR
	TWIDDLE
	MOV	#DRJ.27,(P)		;WHERE TO RETURN BECAUSE BCC IS SICK
	MOV	R0,R1
	MOV	(R0),R0			;IF NON ZERO THERE IS A BUFFER TO DEAL WITH
	BEQ	11$
	CLR	(R1)
	JSR	PC,FRECKS		;FREE THE BUFFER
	CLR	R0
11$:	JMP	DRNAK1			;BE SURE TO NAK THE ERROR


;HERE TO CHECK BCC ON HEADER ON SHORT MSGS
DRJ.09:	JSR	PC,DRJ.90		;CHECK THE BCC
DRJ.08:	MOV	#DRJ.21,LB.RDN(J)	;WHERE TO GO WHEN MESSAGE IS FINISHED
.IF NE FTDUP11			;GROSS HACK FOR DUP-11 BCC
	CMPB	#LS..UP,LB.DVS(J)	;IS THIS A DUP-11?
	BNE	10$			; NO. 
	MOV	#DRJ.07,LB.RDN(J)	;SPECIAL CODE TO CHECK BCC ON THE DUP
10$:
.ENDC ;END OF FTDUP11
	JMP	DRJ.10		;NOW GO GIVE THE RECIEVER A SECOND 4 BYTE BUFFER



.IF NE FTDUP1				;HACK FOR BCC
;SPECIAL HACK FOR THE DUP-11. SNICE THE BCC FOR DUP-11 DATA CAN (AND MUST)
; BE CHECKED AT INTERRUPT LEVEL, THIS CODE CHECKS THE BCC, AND IF IT IS
; VALID, JUST PASSES THE MESSAGE TO LOOP LEVEL.  IF IT IS INVALID, THE
; HEADER CHAR IS SET TO #252 (CROCK) AND LOW-LEVEL NOTICES AND DISCARDS
; THE MESSAGE.

DRJ.07:				;HERE TO CHECK THE BCC ON A DUP-11 DATA MESSAGE
	BIT	#UP$RCC,LB.STY(J) ;IS THE CHECK SUM GOOD?
	BNE	10$		;IF GOOD BCC SKIP SENDING NAK

	TWIDDLE			;COUNT THE NUMBER OF ERRORS
	MOVB	#NCDDBC,LB.NCD(J) ;SIGNAL A DATA BCC ERROR
	JSR	PC,DRNAK1	;QUEUE THE XMITTER TO SEND THE NAK
	MOV	LB.IPT(J),R0	;NOW GET THE ADDRESS OF THE
	ADD	J,R0		; HEADER CHAR FOR THE MESSAGE, AND
	MOVB	#252,(R0)	; SMASH IT TO TELL LOW-LEVEL THAT MSG IS BAD
10$:	JMP	DRJ.21		;GO TO COMMON END-OF-MESSAGE CODE

.ENDC ; .IF NE FTDUP11

.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC ; .IF NE NTLINE
.SBTTL		DEQUEUE ACKED DATA
.IF NE NTLINE
.IF NE,<NTLINE-DMCN>
;HERE WHEN SOME MSGS HAVE BEEN ACKED
DDXMTD:
	SAVE	<R3>			;MUST PRESERVE R3 FOR DDCIN0...
	MOV	R0,-(P)			;DISCARD ACKED MESSAGES
10$:	MOV	LB.OBF(J),R0		;LOCATE THE FIRST MSG IN THE QUEUE
.IF EQ DGUTS
	ASSERT	NE
.IFF
	BEQ	70$
.ENDC ; .IF EQ DGUTS
	CMP	R0,LB.COB(J)		;IS IT THE LAST DATA TRANSMISSION
	BNE	20$
	TST	LB.COB+4(J)		;IF SO, IS IT STILL BEING TRANSMITTED
	BGT	70$			;IF SO, STOP DEQUEUING THE MSGS
	CLR	LB.COB(J)		;ELSE CLEAR THE LAST TRANSMISSION POINTER
20$:	DEC	LB.OBF+4(J)		;AND REMOVE THE MSG FROM THE QUEUE
	MOV	CN.MLK(R0),LB.OBF(J)	;IF THE QUEUE IS EMPTY
	BNE	30$			;STILL SOMETHING IN THE QUEUE
	CLR	LB.OBF+2(J)		;RESET THE QUEUE END POINTER
30$:	JSR	PC,COMODN
	INCB	LB.LAP(J)		;ADVANCE ONE MESSAGE
	INC	LB.OCN(J)		;COUNT MESSAGES ACKED
	DEC	(P)
	BNE	10$
70$:	TST	(P)+			;POP EXHAUSTED COUNT FROM STACK
	RESTORE	<R3>
	RTS	PC
.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC ; .IF NE NTLINE
.SBTTL		XMITTER SERVICE
.IF NE NTLINE
;HERE WHEN HAVE FINISHED TRANSMITTING A DDCMP MESSAGE
DDCXMT:
.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
	CMPB	#LS..DM,LB.DVS(J)	;IS IT DMC11?
	BNE	3$			;NO, CONTINUE
.ENDC;.IF NE,<NTLINE-DMCN>
	JMP	DMCXMT			;GO HANDLE DMC11
3$:
.ENDC;.IF NE,DMCN
.IF NE,<NTLINE-DMCN>
.IF NE FT.MPT
	BIT	#LS.MPT,(J)		;IF MULTIPOINT LINE
	BEQ	DDCX10
	BITB	#MP.OFF!MP.SOL,LB.MPS(J) ;AND IF OFFLINE
	BNE	DDCX49			;DO NOTHING AT ALL
	BITB	#MP.SEL,LB.MPS(J)	;IF NOT SELECTED
	BEQ	DDCX49			;DO NOTHING AT ALL
.ENDC
DDCX10:	BIT	#LS.XCT!LS.XDT,@J	;ALREADY TRANSMITTING ?
	BNE	DDCX49			;YES, NOTHING FURTHER WE CAN DO HERE
	BIT	#LS..RN,@J		;WAS LAST ACK A NAK ?
	BEQ	12$
	BIC	#LS..RN,@J		;FORGET FLAG
	CLR	LB.COB(J)		;SO WE RETRANSMIT MESSAGE
	CLR	LB.COB+2(J)
12$:
.IF NE FT.MPT
	MOVB	LB.MPA(J),LB.CTL+5(J)	;SET DESTINATION ADDRESS
.IFF
	MOVB	#A0,LB.CTL+5(J)		;SET DEFAULT DESTINATION ADDRESS
.ENDC	;.IF NE FT.MPT
	BIT	#LS..ST!LS.STK!LS.XNK,@J ;BRANCH IF WE NEED
	BNE	DDCX15			; A HIGH PRIORITY CNTL MSG
	BIT	#LS.XRP,(J)		; If REP not queued, check for data or ACK.
	BEQ	DDCX50
	CMPB	LB.LAP(J),LB.HSN(J)	; If IDLE REP, send with low priority.
	BEQ	DDCX50
DDCX15:	BIT	#LS.XCT,@J		;SENDING CONTROL MESSAGE ALREADY ?
	BNE	DDCX50			;DON'T WRITE OVER THE LAST
	MOV	#ENQ,LB.CTL(J)		;PUT ENQ AT HEAD
	CLR	LB.CTL+2(J)		;PUT FILLS IN PROTOTYPE CONTROL MESSAGE
	CLRB	LB.CTL+4(J)		;PUT FILL IN PROTOTYPE CONTROL MESSAGE
	BIT	#LS..ST!LS.STK,@J	;BRANCH IF DON'T NEED
	BEQ	40$			; START OR STACK
	BIT	#LS.XRP,@J		;USE THIS SO DON'T SEND TOO MANY
	BEQ	DDCX50			;IF DON'T NEED START SEND BOOT MSG'S
	BIC	#LS.XRP,@J		;SO WE DON'T SEND ANOTHER FOR A WHILE
.IF EQ FT3.02
.IF NE FT.MPT
	TST	#LS.MPT,(J)		;IF MULTIPOINT, ACT AS THOUGH DDCMP
					;3.02 WAS IMPLEMENTED
	BEQ	100$
.ENDC	;.IF NE FT.MPT
	MOVB	LB.LAP(J),LB.CTL+4(J)	;LAST MESSAGE NUMBER SENT
	INCB	LB.CTL+4(J)		;START & STACK ARE +1
	BR	101$
100$:	BISB	#SELBIT,LB.CTL+2(J)	;MUST SET SELECT BIT IN START AND STACK
					;FOR DDCMP 3.02 COMPATIBILITY
101$:
.IFF
	BISB	#SELBIT,LB.CTL+2(J)	;MUST SET SELECT BIT IN START AND STACK
					;FOR DDCMP 3.02 COMPATIBILITY
.ENDC	;.IF EQ FT3.02
	MOVB	#STRT,R0
	BIT	#LS..ST,@J		;ARE WE GOING TO SEND A START ?
	BEQ	39$			;IF SO -
.IF NE FT.RDM!FT.RDP
	JSR	R0,RDESST		;IF A RDE DEV, REPORT THE STATE
	.BYTE	0,210
.ENDC ;.IF NE FT.RDM!FT.RDP
.IF NE,FTDCP1
	BIT	#LS.NSP,@J		;IS IT AN NSP LINE?
	BEQ	49$			;NO, CONTINUE
	INCB	LB.CTL+4(J)		;MESSAGE TO SEND
.ENDC
	BR	49$

39$:	BIC	#LS.STK,@J		;HERE TO SEND STACK
	INC	R0			;MAKE START CODE INTO STACK
.IF EQ FT3.02
.IF NE FT.MPT
	TST	#LS.MPT,(J)		;IF MULTIPOINT, ACT AS THOUGH DDCMP
					;3.02 WAS IMPLEMENTED
	BEQ	102$
.ENDC	;.IF NE FT.MPT
	MOVB	LB.ROK(J),LB.CTL+3(J)	;PUT LAST MSG # RECEIVED OK IN MSG
	INCB	LB.CTL+3(J)		;START & STACK ARE +1
102$:
.ENDC	;.IF EQ FT3.02
.IF NE FT.RDM!FT.RDP
	JSR	R0,RDESST		;IF A RDE DEV, REPORT THE STATE
	.BYTE	0,220
.ENDC ;.IF NE FT.RDM!FT.RDP
.IF NE,FTDCP1
	BIT	#LS.NSP,@J		;IS IT NSP LINE?
	BEQ	49$			;NO, CONTINUE
	INCB	LB.CTL+3(J)		;MESSAGE TO TO SEND
	INCB	LB.CTL+4(J)		;MESSAGE TO RECEIVE
.ENDC
	BR	49$

;HERE TO SEND ACK, NAK, OR REP
40$:	MOVB	LB.ROK(J),LB.CTL+3(J)	;PUT LAST MSG # RECEIVED OK IN MSG
	BIT	#LS.XNK!LS.XAK,@J	;DO WE NEED TO SEND ACK OR NAK ?
	BEQ	45$			;IF NOT NEEDED TO SEND A REP
	CLRB	LB.CTL+4(J)		;DDCMP REQUIRES ZERO FILL FOR ACK/NAK
	BIT	#LS.XNK,@J		;WAS THAT ACK OR NAK ?
	BNE	42$
	BIC	#LS.XAK!LS.NRP,@J		;CLEAR ACK FLAG
	MOV	#ACK,R0
	BR	49$

42$:	BIC	#LS.XNK!LS.NRP,@J	;HERE TO SEND NAK
	MOVB	#NAK,R0
	MOVB	LB.NCD(J),LB.CTL+2(J)	;PUT REASON FOR NAKING IN MSG
	BR	49$
45$:	MOVB	#REP,R0			;HERE TO SEND REP
	CLRB	LB.CTL+3(J)		;THIS IS A FILL BYTE AND MUST BE 0
	MOVB	LB.HSN(J),LB.CTL+4(J)	;HIGHEST MSG # SENT
	BIC	#LS.XRP!LS.NRP,@J	;CLEAR REP FLAG

49$:	MOV	LB.CTL+2(J),R1
	TRACE	DD			;PUT MESSAGE TYPE IN TRACE
	MOVB	R0,LB.CTL+1(J)		;PUT MESSAGE TYPE IN MESSAGE
.IF NE FT.MPT
	BIT	#LS.MPT,(J)		;IF MULTIPOINT, CAN'T HAVE BOOT MODE
	BNE	98$
.ENDC ; .IF NE FT.MPT
	TSTB	LB.BNN+1(J)		;FOR BOOT MODE,
					;SEND MAINTAINANCE MODE MSGS ONLY
	BEQ	98$
	BIT	#LS..ST,(J)		;IF IN BOOT MODE, LINE BETTER BE DOWN
	BNE	DDCX50
	JSR	PC,L.DOWN		;IF IT ISN'T, IT WILL BE NOW
	BR	DDCX50

98$:	MOV	#LB.CTL,R0		;RELATIVE ADDRESS OF CONTROL BUFFER
	ADD	J,R0			;MAKE ABSOLUTE
	JSR	PC,DDCBCC		;PUT BCC ON MSG
	JSR	PC,DDXCTL		;GO TRANSMIT CONTROL MESSAGE
DDCX49:	JMP	LOOP			;RETURN TO LOOP LEVEL
DDCX50:	MOV	LB.BOO(J),R0		;GET ADR OF BOOTSTRAP MSG IF ANY
	BEQ	10$
	CMP	R0,LB.COB(J)		;ALREADY SENT MSG ?
	BNE	52$			;IF NOT SEND IT NOW
	JSR	PC,FRECKS		;RELEASE MSG CHUNKS
	CLR	LB.BOO(J)		;MSG HAS BEEN SENT
	MOV	LB.OBF+2(J),LB.COB(J)	;DON'T RETRANSMIT MSGS
10$:	BIT	#LS..ST!LS.STK,@J	;IS DDCMP STARTED ?
	BNE	DDCX49			;IF NOT DONE
	MOV	LB.OBF+2(J),R0		;CHECK FOR ANY MESSAGES IN QUEUE
	BEQ	60$
	CMP	R0,LB.COB(J)		;WAS LAST MESSAGE SENT LAST IN QUEUE ?
	BEQ	60$			;IF SO DONE
	MOV	LB.COB(J),R0		;GET ADR OF LAST MSG SENT
	BNE	50$
	MOV	LB.OBF(J),R0		;SEND FIRST MSG IN QUEUE
	BR	52$
50$:	MOV	CN.MLK(R0),R0		;GET ADR OF NEXT MSG
52$:	ASSERT CHUNK R0			;BE SURE MSG ADR IS LEGAL
	MOV	R0,LB.COB(J)		;SAVE ADR OF CURRENT MSG
	ADD	#CN.DDC,R0		;POINT TO SOH
	CMPB	#SOH,(R0)		;IF NUMBERED MSG, UNQUEUE WAITING ACK
	BNE	.+6
	BIC	#LS.XAK!LS.NRP,(J)
	MOVB	LB.ROK(J),3(R0)		;PUT RESPONSE INTO MESSAGE
	TRACE	DD
	JSR	PC,DDXBCC		;PUT CRC ON HEADER
	JSR	PC,DDXDAT		;TRANSMIT DATA
	BR	DDCX49

60$:	BIT	#LS.XAK!LS.XRP,@J	;DO WE NEED TO XMT AN ACK/REP ?
	BEQ	DDCX49			;NO, RETURN TO LOOP LEVEL
	JMP	DDCX15
DDXBCC:	BICB	#SELBIT!QSYNC,2(R0)	;LETS NOT BE STUCK WITH A LEFTOVER FLAG
DDCBCC:
.IIF NE FT.QSB,	BISB	#QSYNC,2(R0)	;BETTER LET HIM EXPECT SYNCHS
.IF NE FT.MPT
	BIT	#LS.MPT,(J)		;IF A MULTIPOINT LINE
	BEQ	11$
	CMP	(R0),#STRT*400+ENQ
	BEQ	14$
	CMP	(R0),#STACK*400+ENQ
	BEQ	14$		;MUST ALWAYS SEND SELECT WITH START AND STACK
.IF NE FTTRIB
	TST	LB.MPL(J)		;IF TRIBUTARY
	BNE	13$
	CMPB	LB.LAR(J),LB.HSN(J)	;AND IF DATA TO SEND,WE'RE OK
	BNE	13$
	BIT	#LS.XNK!LS.XRP,(J)	;OR IF MORE CONTROL TO SEND
					;WE'RE OK
	BEQ	14$			;ELSE SEND THE SELECT NOW
;					;NOTE ACKS DON'T COUNT
13$:
.ENDC
	BITB	#MP.SNM,LB.MPS(J)	;AND IF THE  NEXT MSG REQUIRES A SELECT
	BEQ	11$
14$:	BISB	#SELBIT,2(R0)		; SET THE SELECT BIT IN THE MSG
	BICB	#MP.SNM,LB.MPS(J)	;AND CLEAR THE NEXT SELECT FLAG
.IF NE FTTRIB
	TST	LB.MPL(J)
	BNE	12$
	BICB	#MP.SEL,LB.MPS(J)	;IF TRIBUTARY,MUST NOT STAY SELECTED
12$:
.ENDC
.ENDC
11$:
.IF NE FT873				;IF BM873 SUPPORT MOD REQUIRED
	CMPB	(R0),#DLE		;IF BOOTSTRAP MESSAGE
	BNE	20$
	CMPB	LB.DVS(J),#LS..AL	;AND IF THIS IS FOR A SYNC LINE
	BHIS	20$
	BICB	#SELBIT!QSYNC,2(R0)	;BM873 REQUIRES THESE BITS CLEARED
20$:
.ENDC	;.IF NE FT873
	JSR	PC,DDBBCC		;CALCULATE THE  FOR THE HEADER
	MOV	(P)+,(R0)+
	HDRDMP	O,DMPHLN,DMPHOU
	RTS	PC
;
;
DDBBCC:
	MOV	(P),-(P)		;MAKE ROOM ON STACK FOR BCC
.IF NE FTDUP11
	CMPB	#LS..UP,LB.DVS(J)	;IF THIS IS NOT A DUP-11 LINE
	BNE	5$			; THEN DO NORMAL CHECK-SUM
	CLR	2(P)			;OTHERWISE DUMMY CRC TO 0
	ADD	#6,R0			;BUMP PTR TO CRC LOC IN MESSAGE
	RTS	PC			;AND LET THE DUP-11 DO THE REST
5$:
.ENDC ;IF NE FTDUP11
	SAVE <R1>
	PIOFF
	KGLOAD	#0,WORD			;INITIALIZE THE KG11
;	KG IS NOT REENTRANT
	MOV	#KGDATA,R1		;POINTER TO KG11 REGISTER
	MOV	(R0)+,@R1		;CALCULATE THE BCC
	MOV	(R0)+,@R1
	MOV	(R0)+,@R1
	MOV	-(R1),6(P)		;SAVE THE BCC
	PION
	RESTORE	<R1>
	RTS	PC
.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC ; .IF NE NTLINE
.SBTTL		XMITTER INTERRUPT
.IF NE NTLINE
.IF NE,<NTLINE-DMCN>
;HERE TO SEND A CONTROL MESSAGE
; CALL	<PUT 8 CHAR CONTROL MESSAGE IN LB.CTL>
;	JSR	PC,DDXCTL
DDXCTL:	PIOFF				;DISABLE INTERRUPTS FOR A WHILE
	MOV	#10$,LB.XDN(J)		;PLAN TO SEND SOME SYNCHS
	BIS	#LS.XCT,@J		;SENDING CONTROL MESSAGE
	BIT	#LS..XG,@J		;IS SYNCHRONOUS LINE RUNNING ?
	BNE	7$			;BRANCH IF ALREADY TRANSMITTING
	MOV	#20$,LB.XDN(J)		;SETUP CUZ SLXBEG WILL CALL RITE AWAY
	JSR	PC,SLXBEG		;POKE TRANSMITTER
7$:	PION				;REENABLE INTERRUPTS
	RTS	PC			;COME BACK LATER
;HERE VIA LB.XDN WHEN READY TO SEND SOME SYNCHS AHEAD OF CONTROL MESSAGE
10$:
.IF NE NALINE!NMPTL!NTRIB
	CMPB	#LS..AL,LB.DVS(J)	;IF THE DEVICE IS AN ASYNC LINE
	BLOS	20$			;DON'T SEND SYNCS
.ENDC
	MOV	#20$,LB.XDN(J)		;GO HERE WHEN SYNCS HAVE BEEN SENT
.IF NE FTDUP11				;HANDLE DUP SPECIAL TO DISABLE CRC
	CMPB	#LS..UP,LB.DVS(J)	;FIRST MAKE SURE THAT THIS IS A DUP-11
	BNE	15$			; IF IT ISN'T, THEN DO THE USUAL
	MOV	SYNCHS,R0		;OTHERWISE GIVE THE DUP THE SYNC CHAR
					; AS IMMEDIATE DATA
	MOV	#<37777&<-FTNSYN>>,R1	;AND FLAG THE DATA AS 'SYNC' (WITH OUT
					; BCC PROCESSING!)
	RTS	PC			;GIVE THIS BACK TO INTERRUPT LEVEL
15$:					;HERE IF NOT A DUP11
.ENDC; NE DUP11
	MOV	#SYNCHS,R0		;ADDRESS OF SYNCH BUFFER
	MOV	#-FTNSYN,R1		;SEND SEVERAL SYNCHS BEFORE CONTROL
					;MSGS TO BE SURE THEY DON'T GET LOST
	RTS	PC
;HERE VIA LB.XDN WHEN READY TO TAKE CONTROL MESSAGE ADR
20$:	QUEPUT	QO 25$			;SO WE SEND SOMETHING ELSE
	MOV	#30$,LB.XDN(J)		;WHERE TO GO WHEN CONTROL MSG IS STARTED
	MOV	#LB.CTL,R0		;RELATIVE ADR OF MESSAGE
	ADD	J,R0			;MAKE ADR ABSOLUTE
.IF NE FTDUP11
	CMPB	#LS..UP,LB.DVS(J)	;IF THIS ISN'T A DUP, THEN
	BNE	22$			;SHIP INTERRUPT LEVEL A NORMAL MSG
	MOV	#<-6&77777>,R1		;IF IT IS A DUP, THEN ONLY SEND 6
					; CHARS (IT PROVIDES THE BCC) AND
					; SET THE FLAG THAT SAYS SEND BCC
	RTS	PC			;GIVE IT BACK TO INTERRUPT LEVEL
22$:					;HERE IF WE HAVE CALCULATED THE BCC
.ENDC ;.IF NE FTDUP11
	MOV	#-10,R1			;COUNT FOR MESSAGE
	RTS	PC			;BACK TO SYNCHRONOUS LINE
;HERE WHEN SL IS ACTUALLY SENDING CONTROL MESSAGE
30$:
;			;FOR AN ASYNC LINE THIS SUPPLIES THE FIRST NULL BUFFER
;			;FOR SHUTTING THE XMITTER OFF - IT IS REQUIRED TO
;			;PREVENT CLEARING LS.XCT TOO SOON.
;
;			;FOR A SYNCHRONOUS LINE IT SENDS SOME SYNC CHARS
DDCIDL:	MOV	#40$,LB.XDN(J)		;WHERE TO GO WHEN DONE
	BR	DDCI19			;SEND SYNCHS NEXT
;HERE WHEN HAVE FINISHED SENDING CONTROL MESSAGE
40$:					;NO LONGER SENDING CONTROL

;HERE VIA LB.XDN AT INTERRUPT LEVEL WHEN THERE IS NOTHING LEFT TO DO
DDCI18:	MOV	#DDCI20,LB.XDN(J)
	BIC	#LS.XDT!LS.XCT,@J
	QUEPUT	QO 19$
DDCI19:	MOV	#-FTQSYN,R1		;MAKE THAT A SHORT SYNC SEQUENCE
	BR	DDCI21
DDCI20:	MOV	#-FTNSYN,R1		;MAKE THAT AN ORDINARY SYNC SEQUENCE
DDCI21:	MOV	#SYNCHS,R0
.IF NE NALINE!NMPTL!NTRIB
	CMPB	#LS..AL,LB.DVS(J)	;IF THE DEVICE IS AN ASYNC
	BHI	100$
	CLR	R0			; NO SYNCS ARE REQUIRED
	CLR	R1
100$:					;IF NOT ASYNC, JOIN CODE HERE
.ENDC
.IF NE FTDUP11				;DON'T SEND SYNCH'S FOR A DUP
	CMPB	#LS..UP,LB.DVS(J)	;IS THIS A DUP?
	BNE	10$			;IF NOT, DON'T DO IT SPECIAL
	MOV	#DDCI22,LB.XDN(J)	;ONLY SEND 1 SYNCH SEQUENCE IF IT'S
					; A DUP.  THIS IS BECAUSE OF A BUG
					; IN THE DUP DRIVER WHERE IT CLOBBERS
					; THE CHECKSUM OF THE LAST MESSAGE
					; THE PROBLEM SHOULD BE FIXED THERE,
					; BUT THIS IS EASIER

10$:
.ENDC
	RTS	PC

.IF NE FTDUP11
;HERE TO SHUT DOWN A DUP-11 AFTER IT HAS SENT A CLOSING SYNCH SEQUENCE
; (WHICH GIVES IT TIME TO GET THE CHECKSUM OF THE LAST MESSAGE OUT
DDCI22:	CLR	R0			;IF IT'S A DUP, THEN
	CLR	R1			; DON'T IDLE (INTS TAKE TIME)
	RTS	PC			;SHUT HIM DOWN
.ENDC
;HERE TO SEND A DATA MESSAGE
; CALL	<PUT ADR OF 1ST CHUNK IN LB.COB(J)
;	JSR	PC,DDXDAT
DDXDAT:	BIS	#LS.XDT,@J		;WE ARE SENDING DATA !
	PIOFF
	BIC	#LS.XCT,@J		;WHEN THIS IS DONE CAN SEND CONTROL
	MOV	#20$,LB.XDN(J)		;WHERE TO GO ON NEXT XMT INT
	BIT	#LS..XG,@J		;IS TRANSMITTER STILL RUNNING
	BNE	10$			;IF SO DON'T NEED TO SYNCH FIRST
	JSR	PC,SLXBEG		;START TRANSMITTER
10$:	PION
	RTS	PC

;HERE AT INTERRUPT LEVEL TO GET ADR & COUNT FOR 1ST PART OF A MESSAGE
20$:	MOV	#30$,LB.XDN(J)		;WHERE TO GO ON NEXT INTERRUPT
	MOV	LB.COB(J),R0		;GET ADDRESS OF 1ST CHUNK
	BEQ	DDCI18			;CAN HAPPEN IF LINE RESET
	ASSERT CHUNK R0			;BE SURE CHUNK ADR IS KOSHER
	MOV	R0,LB.COB+2(J)		;WHICH IS ALSO CURRENT CHUNK
24$:	MOV	CN.LEN(R0),R1		;GET SIZE OF MESSAGE SANS HEADER
.IF EQ DGUTS
	ASSERT	NE
.IFF
	BNE	25$
	INC	CN.LEN(R0)		;TURN ZERO LENTGH MSG TO SINGLE NUL
	CLR	CN.NCT(R0)
	MOV	#SYN*400,CN.NCT+2(R0)
	BR	24$
25$:
.ENDC ; .IF EQ DGUTS
	ADD	#CN.DDC,R0		;POINT TO DDCMP HEADER

;DATA MESSAGES FOR THE DUP-11 MUST BE HANDLED IN AT LEAST TWO SECTIONS.
; THIS IS NECESSARY TO GIVE THE DUP A CHANCE TO PUT THE BCC ON THE
; HEADER SECTION OF THE MESSAGE.

.IF NE FTDUP11				;TELL DUP TO PUT BCC ON MSG
	CMPB	#LS..UP,LB.DVS(J)	;IS THIS A DUP-11
	BNE	3$			;IF NOT, SEND NORMAL HEADER
	MOV	#23$,LB.XDN(J)		;RETURN HERE AFTER HEADER SENT
	MOV	#<-6&77777>,R1		;HEADER (LESS BCC) WITH BCC
	RTS	PC			;SEND THE HEADER PORTION FIRST.
3$:
.ENDC	;IF NE FTDUP11
	ADD	#12,R1			;ADD SIZE OF HEADER + BCC
	MOV	R1,LB.COB+4(J)		;REMEMBER SIZE OF MESSAGE
	MOV	#CN.DDC-CNKSIZ,R1	;SIZE OF 1ST CHUNK
	BR	35$

;
.IF NE FTDUP11	;HERE AFTER DUP11 SENT HEADER PART OF MESSAGE
;
23$:	MOV	#30$,LB.XDN(J)		;RETURN HERE FOR NEXT MESSAGE
	MOV	LB.COB(J),R0		;GET CURRENT CHUNK
	BNE	21$			;??? I DON'T KNOW HOW, BUT     ???
	JMP	DDCI18			;??? COB SOMETIMES GETS ZAPPED ???
21$:	MOV	CN.LEN(R0),LB.COB+4(J)	;REMEMBER SIZE OF DATA MESSAGE
	ADD	#10+CN.DDC,R0		;POINT TO DATA MESSAGE
	MOV	#CN.DDC+10-CNKSIZ,R1	;COUNT OF DATA LEFT IN THIS CHUNK
	ADD	R1,LB.COB+4(J)		;ADJUST REMAINING COUNT
	BLE	26$			;SKIP IF ONLY 1 CHUNK IN MSG
	RTS	PC			;IF LONG MSG, SEND THIS PART NOW.
26$:	SUB	LB.COB+4(J),R1		;IF SHORT MSG, GET BACK LENGTH
	BIC	#100000,R1		;TELL THE DUP TO SEND THE BCC
	BR	36$			;RE-QUEUE THE LINE AND RETURN
.ENDC ;IF NE FTDUP11

;HERE AT INTERRUPT LEVEL TO GET ADDITIONAL CHUNKS OF A MESSAGE
30$:	MOV	@LB.COB+2(J),R0		;GET ADDRESS OF NEW CHUNK
	ASSERT	NE			;BE SURE WE HAVE A NEXT CHUNK
	ASSERT CHUNK R0			;BE SURE KOSHER ADR
	MOV	R0,LB.COB+2(J)		;SAVE NEW CURRENT CHUNK
	TST	(R0)+			;ADVANCE TO DATA ADR
	MOV	#2-CNKSIZ,R1		;NUMBER OF BYTES TO TRANSMIT
35$:	ADD	R1,LB.COB+4(J)		;ADJUST COUNT LEFT
	BGT	99$			;IF ANOTHER CHUNK DONE
	SUB	LB.COB+4(J),R1		;ADJUST COUNT
.IF NE FTDUP11
	CMPB	#LS..UP,LB.DVS(J)	;IS THIS A DUP.
	BNE	36$			;IF NOT, HANDLE NORMALLY.
	BIC	#100000,R1		;OTHERWISE TELL THE DUP TO SEND THE BCC
36$:
.ENDC ;IF NE FTDUP11
	MOV	#DDCIDL,LB.XDN(J)	;WHERE TO GO WHEN DONE WITH THIS
	QUEPUT	QO 98$
99$:	RTS	PC
.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC ; .IF NE NTLINE
.SBTTL		MULTIPOINT SELECTION

.IF NE NTLINE
.IF NE,<NTLINE-DMCN>
.IF NE FT.MPT

DESLCT:				;DESELECT THIS STATION AND SELECT NEXT ONE
	SAVE	<R0>
	BR	DSLCT1

SELNXT:				;SELECT THE NEXT STATION IF POSSIBLE
.IF NE FTTRIB
	TST	LB.MPL(J)		;IF TRIBUTARY
	BNE	SLCT.3
	BITB	#MP.OFF,LB.MPS(J)	;IF ONLINE
	BEQ	SLCT.4			;FORCE A SELECT
	RTS	PC			;ELSE DO NOTHING
SLCT.3:
.ENDC
	SAVE	<R0>
	BITB	#MP.SEL,LB.MPS(J)	;IF NOT SELECTED DO NOTHING
	BEQ	SLCT.X
	MOV	LB.LCB(J),R0		;GET LINE CONTROL BLOCK
	CLR	LC.INS(R0)		;AND INHIBIT FURTHER INPUT
	BICB	#MP.SEL,LB.MPS(J)	;STATION IS NO LONGER IT
	BIT	#LS..XG,(J)		;IF XMITTER IS RUNNING,WAIT FOR IT TO STOP
	BNE	SLCT.X
DSLCT1:				;DESELECT THIS STATION AND SELECT NEXT ONE
	BIC	#LS..XG,(J)		;XMITTER IS STOPPED
	BICB	#MP.SEL,LB.MPS(J)	;STATION IS NO LONGER IT
	CLRB	LB.MPT(J)		;CLEAR THE SELECT TIMER
	BITB	#MP.SOL,LB.MPS(J)	;IF STATION IS TO BE SET OFF LINE
	BEQ	SLCT.2
	BITB	#MP.OFF,LB.MPS(J)	;AND IF STATION IS  NOT YET OFF LINE
	BNE	SLCT.2
	SAVE	<R1,R2,R3,R4,J>		;SAVE REGISTERS TO BE SAFE
	JSR	PC,L.DOWN		;DECLARE IT DOWN
	RESTORE	<J,R4,R3,R2,R1>		;RESTORE REGISTERS
SLCT.2:
.IF NE FTTRIB
	TST	LB.MPL(J)		;IF NOT TRIBUTARY
	BEQ	SLCT.X
.ENDC
	MOV	LB.LCB(J),R0		;GET THE LINE CONTROL BLOCK
	CLR	LC.BLK(R0)		;AND UNDEFINE THE SELECTED STATION
	CLR	LC.INS(R0)
	CLR	LC.OUS(R0)
	MOV	J,-(P)			;SAVE J
.IIF EQ FTSLCT-1,	MOVB	LB.MPN+1(J),LB.MPN(J)	;RESET THE SELECTION WEIGTH
.IIF EQ FTSLCT-2,	JSR	PC,SLCTDN	;ADJUST AND RESET THE SELECTION WEIGHT
	JSR	PC,SLCTIT		;DETERMINE THE NEXT STATION
	TST	J			;IF THERE IS ONE
	BEQ	SLCT.5
.IF NE NDHMPT!NDHTRB
	.IF NE NDZMPT!NDZTRB
		CMPB	LS..AL,LB.DVS(J)
		BLOS	SLEC2H
	.ENDC
.IFF
	MOV	#DDDZOU,LC.OUS(R0)
	MOV	#DDDZIN,LC.INS(R0)
.IFT
	.IIF NE NDZMPT!NDZTRB, BR SLEC2Z
SLEC2H:
	MOV	#DDDHOU,LC.OUS(R0)	;RESTORE THE LINE CONTROL
	MOV	#DDDHIN,LC.INS(R0)
SLEC2Z:
.ENDC
	MOV	J,LC.BLK(R0)
	JSR	PC,SLCT.4		;SELECT THE STATION
SLCT.5:	MOV	(P)+,J			;RESTORE J
SLCT.X:	RESTORE	<R0>
	RTS	PC
;
;
SLCT.4:
	SAVE	<R0>
	BITB	#MP.OFF,LB.MPS(J)	;IF STATION IS OFFLINE
	BEQ	.+6
	BIS	#LS..ST!LS.XRP,(J)	;WE MUST SEND START
	BICB	#^C<MP.SFF>,LB.MPS(J)
	BISB	#MP.SEL!MP.SNM,LB.MPS(J)	;FLAG IT NEWLY SELECTED
	MOVB	#MP.STV,LB.MPT(J)		;RESET SELECTION TIMER
.IF NE FTTRIB
	TST	LB.MPL(J)		;IF TRIBUTARY
	BNE	10$
	MOVB	#MP.TTV,LB.MPT(J)	;USE CORRECT VALUE
	BICB	#MP.SNM,LB.MPS(J)	;RESET THE SELECT IN NEXT MSG FLAG
10$:
.ENDC
	BIT	#LS..ST!LS.STK!LS.XNK!LS.XAK,(J)
;					;IF NO MESSAGE QUEUED
	BNE	20$
	TST	LB.MPL(J)		;IF MASTER DELAY,THEN SEND REP
	BEQ	20$
	MOVB	#SDLAY,LB.REP(J)	;RESET REP TIMER TO DELAY SELECT
	BNE	21$
20$:	BIS	#LS.XAK,(J)		;SEND ACK
	QUEPUT	QO 11$
21$:	BIT	#LS..RG,(J)		;IF THE RECEIVER IS STOPPED
	BNE	SLCT.X
.IF NE NDHMPT!NDHTRB
	.IF NE NDZMPT!NDZTRB
		CMPB	LS..DH,LB.DVS(J)
		BEQ	22$
	.ENDC
.IFTF
	.IF NE NDZMPT!NDZTRB
		JSR	PC,DZRBEG		;START IT
		BR	SLCT.X
	.ENDC
.IFT
22$:	JSR	PC,DHRBEG		;START IT
	BR	SLCT.X			;RESTORE R0 AND EXIT
.ENDC
;
;
SLCT.1:			;SELECT THIS ONE AS THE FIRST IF ALL OTHERS ARE OFFLINE
	SAVE	<R0,J>
	CLR	R0		;ASSUME THIS IS THE ONLY ACTIVE DROP
10$:	MOV	LB.MPL(J),J	;SEARCH RING
	BEQ	15$
	CMP	J,(P)		;DONE IF BACK WHERE WE STARTED,
	BEQ	15$
	BITB	#MP.OFF,LB.MPS(J)	;LOOP IF DROP IS OFFLINE
	BNE	10$
	MOV	J,R0		;NOTE THE ONLINE DROP
15$:	MOV	(P)+,J			;RESTORE J
	MOVB	#MP.SFC,LB.MPS(J) ;SET THE SELECT FAILURE COUNTER
	CLRB	LB.MPT(J)
	BIS	#LS..ST!LS.XRP,(J)	;BE SURE TO SEND START
	TST	R0			;IF NO STATION ONLINE
	BEQ	SLCT.2			;ACTIVATE THE STATION
					;STATION WILL BE SELECTED IN COURSE
	BR	SLCT.X			;RESTORE R0, AND EXIT
;
;
;
;
;FTSLCT IS -	;SET TO ZERO FOR ROUND ROBIN SELECTION
		;SET TO ONE FOR WEIGHTED ROUND ROBIN
		;SET TO TWO FOR DYNAMIC WEIGHTED ROUND ROBIN
		;SET TO THREE FOR POLLING TABLE SELECTION
;
;
SLCTIT:				;SELECT THE NEXT STATION
				;THIS MUST PRESERVE R0
.IF EQ FTSLCT
;				;FOR TIME BEING SIMPLE ROUND ROBIN OF ON LINE ONES
	MOV	LB.MPL(J),J		;GET NEXT STATION
	BITB	#MP.OFF!MP.SOL,LB.MPS(J) ;IF OFF LINE
	BEQ	30$
	CMP	2(P),J			;CHECK IF ITS THE ONE WE STARTED WITH
	BNE	SLCTIT			;IF NOT TRY NEXT ONE
	CLR	J			;NONE ARE ON LINE
30$:	RTS	PC
;
.ENDC
;
;
.IF GT FTSLCT
.IF LE FTSLCT-2
;					;WEIGHTED ROUND ROBIN SELECTION
	CLR	-(P)			;SET FLAG CLEAR
32$:	MOV	LB.MPL(J),J		;GET NEXT STATION
	BITB	#MP.OFF!MP.SOL,LB.MPS(J) ;IF ONLINE
	BNE	30$
	INC	(P)			;SET ONLINE STATION FLAG
	TSTB	LB.MPN(J)		;IF PASS NOT SET
	BEQ	31$			;THIS IS IT
	DECB	LB.MPN(J)		;ELSE SKIP IT
30$:	CMP	4(P),J			;CHECK IF ITS THE ONE WE STARTED WITH
	BNE	32$			;IF NOT TRY NEXT ONE
	TST	(P)+			;POP THE FLAG
	BNE	SLCTIT			;IF SOME ONLINE TRY NEXT ONE
	CLR	J			;NONE ARE ON LINE
	RTS	PC
31$:	TST	(P)+			;POP THE FLAG
	RTS	PC

.ENDC
.ENDC
;
;
.IF NE FT.RDM
.IF EQ FTSLCT-3
	BIT	#LS.MPT,(J)		;IS THIS A MULTI-POINT LINE?
	BEQ	36$			;NO, NO "SELECTION" THEN
	JSR	PC,RDEGTB		;GET THE DDB FOR THE RDE DEV
	BEQ	36$
	SAVE	<R0,R1>			;IF THERE IS ONE CONTINUE
	MOV	#POLLSZ,R1		;SET THE SEARCH LIMIT
	MOV	J,-(P)			;SET UP THE LIST END ADR
	ADD	#DB.POL+POLLSZ,(P)
30$:	MOVB	@DB..PX(J),R0		;GET THE NEXT STATION FROM THE LIST
	INC	DB..PX(J)		;MOVE TO THE NEXT LIST ENTRY
	CMP	(P),DB..PX(J)		;CHECK WRAP ARROUND
	BLO	31$
	SUB	#POLLSZ,DB..PX(J)
31$:	TST	R0
	BEQ	33$			;BRANCH IF BAD ENTRY
	MOV	DB.HDW(J),J		;IF THERE IS ONE,LETS FIND IT
	JSR	PC,GETMPA
	BEQ	32$
	BITB	#MP.OFF!MP.SOL,LB.MPS(J) ;ITS OFFLINE, WASTED EFFORT
	BEQ	34$
32$:	MOV	(P),J			;RESTORE THE DDB ADR
	SUB	#DB.POL+POLLSZ,J
33$:	SOB	R1,30$			;AND LOOP
	CLR	J			;COULDN'T FIND ONE
34$:	TST	(P)+			;GOT THE RESULT SO EXIT
	RESTORE	<R1,R0>
36$:	RTS	PC
.ENDC ;.IF EQ FTSLCT-3
.ENDC ;.IF NE FT.RDM
;
;
;
.IF EQ FTSLCT-2
SLCTDN:			;ADJUST AND RESET THE WEIGHTING FOR SELECTION
	SAVE	<R0>
	MOVB	LB.MPN+1(J),R0		;FETCH THE WEIGHT SELECTION STATE
	BIT	#^C17,R0
	BEQ	45$			;BRANCH IF STATE NOT ADJUSTED
	BPL	46$			;BRANCH IF HOLDING WEIGHT
	BIC	#^C177,R0		;IF ALREADY ADJUSTED CLEAR THE FLAG
	BR	SLCTDX
45$:	INC	R0			;GET THE NEW ADJUSTED  VALUE
	MOVB	SLCT.N(R0),R0
	BR	SLCTDX
46$:	SUB	#20,R0			;DECREMENT THE HOLDING COUNT
SLCTDX:	MOVB	R0,LB.MPN+1(R0)		;SAVE THE ADJUSTED SELECTION STATE
	BIC	#^C17,R0		;AND THE NEW WEIGHT
	MOVB	R0,LB.MPN(J)
	RESTORE	<R0>
	RTS	PC
;
;
SLCTDO:				;ADJUST THE WEIGHT WHEN DATA GOES OUT
	MOV	R0,-(P)
	MOV	#SLCT.O,-(P)
	BR	SLCTDB
;
SLCTDI:					;ADJUST THE WEIGHT WHEN DATA GOES IN
	MOV	R0,-(P)
	MOV	#SLCT.I,-(P)
SLCTDB:	MOVB	LB.MPN+1(J),R0		;GET THE OLD WEIGTH
	BIC	#^C17,R0		;BE SURE ITS CLEAN
	ADD	(P)+,R0			;AND FETCH THE NEW VALUE
	MOVB	(R0),R0
	BR	SLCTDX			; EXIT
;
;
SLCT.I:
	.BYTE	200,200,200,200,201,201,201,201
	.BYTE	203,203,203,203,203,203,203,203
	.EVEN
;
SLCT.O:
	.BYTE	200,200,200,200,200,200,201,201
	.BYTE	201,201,201,201,202,202,202,202
	.EVEN
;
SLCT.N:
	.BYTE	020,041,062,103,124,145,166,167
	.BYTE	170,171,172,173,174,175,176,177,177
	.EVEN
;
.ENDC
;
;
;
;
LOCMPA:			;LOCATE LINE BLOCK FOR STATION ADDRESSED
			;ASSIGNING ONE IF NECESSARY
	JSR	PC,GT0MPA
	RTS	PC			;RETURN IF IT WAS FOUND
;					;ELSE ASSIGN IT
	MOV	(P),J			;GET THE REUSABLE LINE BLOCK
	BEQ	39$			;BRANCH IF NOTHING TO ASSIGN
	MOVB	R0,LB.MPA(J)		;SET THE STATION ADDRESS
	MOVB	#MP.OFF,LB.MPS(J)	;MAKING SURE THE STATE IS REASONABLE
	CLRB	LB.MPT(J)
39$:	BR	GTXMPA			;CLEAN THE STACK AND EXIT
;
;
;
;			;GET THE LINE BLOCK ASSIGNED TO THE STATION ADDRESS IN R0
GETMPA:	JSR	PC,GT0MPA		;GET THE LINE BLOCK
	RTS	PC			;RETURN IF ITS WAS FOUND
	BR	GTXMPA			;AND ALSO IF NONE FOUND
;
;
;			;GET THE LINE BLOCK ASSIGNED TO THE STATION ADDRESS IN R0
GT0MPA:	MOV	J,-(P)			;SAVE ADDRESS OF FIRST LINE BLOCK TO SEARCH
	CLR	-(P)			;DON'T KNOW IF WE CAN REUSE SOME BLOCK
40$:	CMPB	R0,LB.MPA(J)		;IF THIS IS IT
	BEQ	GTXMPA			;WE'RE DONE
	BITB	#MP.OFF,LB.MPS(J)	;IF STATION IS OFFLINE
	BEQ	45$
	MOV	J,(P)			;SAVE IT FOR REUSE
45$:	MOV	LB.MPL(J),J		;GET THE NEXT LINE BLOCK
	CMP	J,2(P)			;IF NO MORE
	BNE	40$
	MOV	(P),2(P)		;SHIFT THE STACK TOP DOWN ONE
	MOV	4(P),(P)		;AND GET THE RETURN ADDRESS
	ADD	#2,(P)			;MUST SKIP IN  THE FAILURE CASE
	CMP	-(P),-(P)		;PUT TWO DUMMIES ON THE STACK
GTXMPA:	CMP	(P)+,(P)+		;POP THE STACK
	TST	J			;SET THE Z BIT FROM VALUE OF J
	RTS	PC
;
;
;
;			;GET THE LINE BLOCK ASSIGNED TO THE STATION ADDRESS IN R0
;			;LOCATING AN UNUSED BLOCK IF NONE ASSIGNED
GT1MPA:	JSR	PC,GT0MPA
	RTS	PC			;RETURN IF IT WAS FOUND
	BR	GTXMPA			;ELSE RETURN THE FREE ONE
	MOV	(P),J			;ELSE GET THE REUSEABLE ONE
	BR	GTXMPA			;AND EXIT
;

;
.ENDC ; .IF NE FT.MPT
.ENDC;.IF NE,<NTLINE-DMCN>
.ENDC ; .IF NE NTLINE
.SBTTL		SELECTING THE DDCMP LINE SERVICE PROCS

.IF NE NTLINE
.IF NE,<NTLINE-DMCN>

.MACRO	X	QTAG
.MACRO	XX	QDV,QQTAG,QCN
	.IF NE QCN
	    .IF NE FT'QDV'11
		.WORD	QDV'QQTAG
	    .IFF
		.WORD	-1
	    .ENDC
	.IFF
		.WORD	-1
	.ENDC
.ENDM	XX
	XX	DP,QTAG,1
	XX	DS,QTAG,1
	XX	DU,QTAG,1
	XX	DV,QTAG,1
	XX	DQ,QTAG,1
	XX	DUP,QTAG,1
	XX	DMC,QTAG,1
	XX	DH,QTAG,NADHLN!NDHMPT!NDHTRB!RDPDHN
	XX	DZ,QTAG,NADZLN!NDZMPT!NDZTRB!RDPDZN
.ENDM	X

;
;
;HERE TO SELECT THE LINE INITIALIZATION PROC
SL.INI:	MOVB	LB.DVS(J),R2
	JMP	@.+4(R2)
	X	DINI
;
;
;HERE TO SELECT THE RECEIVER START UP PROC
SLRBEG:	MOVB	LB.DVS(J),R3
	JMP	@.+4(R3)
	X	RBEG
;
;
;
;HERE TO SELECT THE XMITTER START UP PROC
SLXBEG:	MOVB	LB.DVS(J),R3
	JMP	@.+4(R3)
	X	XBEG
;
;
;SL.SEC:	RTS			;NO DRIVER TIMER FUNCTIONS NOW;
;
.ENDC;IF NE,<NTLINE-DMCN>
.ENDC	;.IF NE NTLINE