Google
 

Trailing-Edge - PDP-10 Archives - tops10_703a_sys_ap115_bb-ju01b-bb - dncdmc.x14
There are 3 other files named dncdmc.x14 in the archive. Click here to see a list.
.SBTTL	DMCDMC - DMC-11 SYNCH LINE INTERFACE  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) 1979,1980,1981,1984 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.

VRCDMC=035				;FILE EDIT NUMBER


.REPT 0

				LOOSE ENDS

.ENDR

.IF NE DMCN

ND	DMCIBF,4			;MAX # OF INPUT BUFFERS TO QUEUE
ND	DMCOBF,4			;MAX # OF OUTPUT BUFFERS.
					;DMC DOESN'T WORK IF YOU GIVE
					; IT ANY MORE...

	SEL0=0				;FIRST REGISTER
	SEL2=2				;SECOND
	SEL4=4				;THIRD
	SEL6=6				; AND FOURTH
.SBTTL	CB - DMC-11 MESSAGE BUFFERS.

.REPT 0

     The DMC-11 requires that the messages it transmits and
receives be in contiguous memory.  This driver accomplishes
that by copying the data back and forth between chunks and
DMC-11 buffers (CB.???).  There is a fixed pool of buffers
that is shared between all DMC-11's on the system.

.ENDR

	BLOCK	CB			;DEFINE THE "X, XX" MACROS
	X	LNK,1			;LINK TO THE NEXT BUFFER
	X	CNT,1			;NUMBER OF BYTES IN BUFFER
	XX	DAT,MSGMAX		;DATA AREA
	X	LEN,0			;LENGTH OF A CB BLOCK



;NOW DEFINE THE NUMBER OF BUFFERS FOR THE SYSTEM.

	CB.NUM=DMCN*<DMCIBF+DMCOBF>	;TOTAL NUMBER OF BUFFERS

;THE BUFFER FREE LIST CONSISTS OF A COUNT AND POINTER TO FIRST
;  LIST AND FREECOUNT ARE SET UP DURING ONCE/ONLY

CBFREC:	.BLKW	1			;COUNT OF FREE DMC-11 BUFFERS
CBFSTF:	.BLKW	1			;FIRST FREE BUFFER


;NOW RESERVE STORAGE FOR THE BUFFERS
.MACRO	X	Q			;ALSO NAME THE BUFFERS.
CB..'Q:	.BLKB	CB.LEN			;RESERVE STORAGE FOR BUFFER
.ENDM

CBFRST:					;ADDRESS OF THE FIRST BUFFER
Z=0					;INIT THE LOOP COUNTER
.REPT	CB.NUM				;FOR ALL BUFFERS
	X	\Z			;RESERVE THE MEMORY
	Z=Z+1				; AND INCREMENT THE COUNTER
.ENDR					;BUFFERS ALL LAYED OUT.
.SBTTL	DMCBAS - BASE TABLES FOR THE DMC-11

.REPT 0

     Each DMC-11 requires a 256. word base table in which
it stores various line counts.  The DMC-11 must be given the
address of this table in a BASE-IN transaction before it will
do anything else (except clobber low memory...)

.ENDR

	BLOCK	MB			;DMC BASE
	XX	,5			;NOTHING OF INTREST HERE
	XX	RNK			;RECEIVED NAKS
	XX	XNB			;SENT NAKS - NO BUFFER
	XX	XNH			;SENT NAKS - BAD HEADER
	XX	XND			;SENT NAKS - DATA BCC
	XX	XRP			;SENT REPS
	XX	RRP			;RECEIVED REPS
	XX	XX,255.-MB.RRP		;FILL OUT TO THE END
	XX	LEN,0			;LENGTH OF THE BASE TABLE (256)


;NOW A MACRO TO LAY OUT EACH BASE TABLE

.MACRO	X	Q			;ARG IS DMC-11 NUMBER
DMBAS'Q: .BLKB	MB.LEN			;LAY OUT THE BASE
.ENDM

Z=0					;INIT LOOP COUNTER
.REPT	DMCN				;FOR ALL DMC-11'S
	X	\Z			;  LAY OUT THEIR BASE TABLE
	Z=Z+1				;INCREMENT THE DMC NUMBER
.ENDR
.SBTTL	QUEUES - DMC-11 MESSAGE BUFFER QUEUES (IN LINE BLOCK)

.REPT 0

     Each line-block for a DMC-11 has 3 queues of DMC buffers.
There are:
     1)	LB.INQ	Empty input buffers waiting to be filled
     2)	LB.IND	Full input buffers ready to be emptied at loop.
     3)	LB.OUT	Full output buffers waiting to be sent by the DMC.

The general flow of control for input is:
     1)	DMCQRB queues input buffers on LB.INQ and gives them to
	the DMC (via BA/CC in transaction) to fill.
     2)	The DMC-11 interrupts when the buffer is filled, the
	interrupt code (DMCBIN) transfers the buffer from the
	LB.INQ to the LB.IND queue and wakes low level.
     3)	Loop level removes the buffer from the LB.IND queue, copies
	the data to chunks (to form a message), frees the DMC
	buffer, and gives the message to the appropriate higher
	level protocol (NCL or NSP).

The general flow of control for output is:
     1)	DNDCMP queues a buffer on LB.OBF and calls the DMC-11
	driver at DMCXMT.
     2)	The driver allocates a DMC-11 buffer, dequeues the
	message from LB.OBF and copies it to the DMC-11 buffer.
	The DMC-11 buffer is given to the DMC-11 to send and
	queued on the LB.OUT queue.  The message is returned
	to the appropriate higher level protocol as "sent".
     3)	The DMC-11 will interrupt after the message has been
	sent and ACKed by the other side.  At this point the
	DMC-11 buffer is dequeued from LB.OBF and freed.

.ENDR

;FORMATS FOR THE QUEUES ARE:

	BLOCK	CBQ			;DEFINE THE DMC-11 QUEUES
	X	CT			;FIRST IS THE QUEUE LENGTH
	X	FS			;NEXT IF THE FIRST BUFFER
	X	LS			;  THEN THE LAST BUFFER
	X	LN,0			;QUEUES ARE THIS LONG.
.SBTTL	LBLK - SPECIAL DMC-11 LINE BLOCK OFFSETS

	BLOCK	LB			;DEFINE THE X, XX MACROS
	XX	,LB.IBF			;GET TO OUR OFFSET AREA
	X	INQ,CBQ.LN		;BUFFERS GIVEN TO DMC TO FILL
	X	IND,CBQ.LN		;FILLED BUFFERS RETURNED BY DMC
	X	OUT,CBQ.LN		;BUFFERS QUEUED TO BE SENT
	X	TMO,1			;TIMER TO TIME OUT DMC-11


;DMC-11 DEPENDENT BITS IN LB.STX

	LSXMAI=B0			;WE ARE IN MAINT MODE
	LSXWAI=B1			;WAITING FOR MEMORY OR BUFFERS
	LSXRUN=B2			;WE BELIEVE DMC IS RUNNING
	LSXKRK=B3			;DMC IS KROAKED
	LSXDMC=LSXMAI!LSXWAI!LSXRUN!LSXKRK	;CLEAR ON RESTART


DMCLB0:	.WORD	0			;ADDRESS OF FIRST DMC-11 LINE
					;FILLED IN BY CKADMC
.SBTTL	VECTOR - INTERRUPT VECTOR DEFINITIONS

.MACRO	X	N			;N := DMC-11 NUMBER
	.NLIST				;NO ONE IS INTERESTED
MCVA'N:	   SAVE	<J,#DMCAIN>		;SAVE POINTER & SERVICE ROUTINE
	   BR	MCVC'N			;BRANCH TO COMMON CODE
MCVB'N:    SAVE	<J,#DMCBIN>		;SAVE POINTER & SERVICE ROUTINE
MCVC'N:	   MOV	DMCLB0,J		;GET ADDR OF FIRST DMC-11 LB
.IIF NE N, ADD	#<LB.SIZ*N>,J		;GET ADDRESS OF N'TH
	   RTS	PC			;"CALL" THE SERVICE ROUTINE
	.LIST
.ENDM

	Z=0				;LOOP COUNTER STARTS AT ZERO
.REPT	DMCN				;FOR ALL DMC-11'S
	X	\Z			;DEFINE THEIR VECTORS
	Z=Z+1				;ADVANCE TO THE NEXT DMC
.ENDR					;VECTORS ALL DEFINED.
.SBTTL	DMCONC - ONCE/ONLY CODE TO SET UP THE BUFFER FREE POOL

;CALLED FROM START UP CODE (JUST AFTER CHUNKS ARE LAYED OUT).
;  FIRST INIT THE FREE LIST COUNT AND ADDRESS OF FIRST FREE BUFFER,
;  THEN LOOP OVER ALL THE BUFFERS CHAINING THE FREE LIST.

DMCONC:	MOV	#CB.NUM,R0		;GET THE NUMBER OF BUFFERS
	MOV	#CBFRST,R1		;GET THE ADDRESS OF THE FIRST
	MOV	R0,CBFREC		;INIT THE FREE COUNT
	MOV	R1,CBFSTF		;INIT THE FIRST FREE POINTER
10$:	MOV	R1,R2			;GET ADDR OF THIS ONE
	ADD	#CB.LEN,R1		;GET ADDR OF NEXT ONE
	MOV	R1,CB.LNK(R2)		;MAKE "THIS" POINT TO "NEXT"
	SOB	R0,10$			;LOOP OVER ALL BUFFERS
	CLR	CB.LNK(R2)		;MAKE LAST POINTER NULL
	RTS	PC			;FREE LIST INITIALIZED.
.SBTTL	DMCINI - ONCE/ONLY CODE TO INIT THE LINE BLOCKS

;CALLED BY ONCE/ONLY AS WHEN IT LOOPS OVER ALL THE LINE BLOCKS
;	J := LINE BLOCK ADDRESS

DMCINI:	BIC	#LSXDMC,LB.STX(J)	;RESET ALL THE DMC-11 BITS
	JSR	PC,DMCHLT		;STOP THE DMC (CLEAR LSXRUN)
	 BCS	99$			;SKIP IF THE DMC IS BUSTED
	QUEPUT	QI			;TELL LOOP LEVEL TO RESTART IT
99$:	RTS	PC			;ALL DONE.


;DUMMY ADDRESSES FOR DISPATCH TABLE ENTRIES (ONLY REQUIRED WHEN
;  THERE ARE OTHER TYPES OF LINES ON THE SYSTEM)
.IF NE,<NTLINE-DMCN>			;IF MORE THAN JUST DMC LINES
DMCDINI:				;INITIALIZE
DMCRBEG:				;RECEIVER BEGIN
DMCXBEG:				;XMIT BEGIN
	TRAP				;SHOULD NEVER GET HERE
.ENDC
.SBTTL	DMCTMR - CLOCK SERVICE FOR THE DMC-11

;DMCTMR	ROUTINE TO PROCESS THE TIMER FUNCTIONS FOR THE DMC-11
;GENERAL ORDER OF BUSINESS IS:
;    1)	SEE IF WE ARE IN MAINT MODE. IF WE ARE, WE'VE BEEN THERE TOO
;	LONG.  CLEAR MAINT, RESTART THE LINE AND EXIT.
;    2)	IF WE'RE WAITING, START LOOP LEVEL AND EXIT. (AFTER ALL, IT
;	ISN'T FAIR TO TIME LINES THAT CAN'T GET MEMORY)
;    3)	IF NO OUTPUT MESSAGES QUEUES, QUEUE AN NCL ACK (NECESSARY
;	BECAUSE STUPID DMC-11 WON'T NOTICE A LINE DOWN BY ITSELF)
;    4)	SEE IF THE LINE HAS BEEN OUTPUT ACTIVE TOO LONG.  IF SO,
;	RESTART IT.

DMCTMR:	BIT	#LSXRUN,LB.STX(J)	;IF WE'RE NOT RUNNING
	BEQ	97$			;LET'S GET STARTED.
	BIT	#LSXMAI,LB.STX(J)	;ARE WE IN MAINT MODE
	BEQ	10$			; IF NOT MAINT, SKIP ON
	INC	LB.TMO(J)		;INCREMENT THE TIMER
	CMP	LB.TMO(J),#^D120./NRPSEC ;BEEN WAITING LONG ENOUGH?
	BLT	99$			;IF NOT, KEEP WAITING
	BIC	#LSXMAI,LB.STX(J)	;OTHERWISW CLEAR MAINT
	BR	97$			; AND RESTART THE LINE

10$:	BIT	#LSXWAI,LB.STX(J)	;WHERE WE WAITING?
	BEQ	20$			; IF NOT, GO TIME THE LINE
	BIC	#LSXWAI,LB.STX(J)	;IF SO, CLEAR THE FLAG
	TWIDDLE				;COUNT NUMBER OF TIMES WE WAIT
	BR	98$			; AND QUEUE THE LINE FOR SERVICE

20$:	TST	LB.OUT+CBQ.CT(J)	;IS THERE AN OUTPUT MSG QUEUED
	BNE	40$			; IF SO, GO TIME IT
	SAVE	SB			;WE CLOBBER THIS
	MOV	LB.SCB(J),SB		;GET OUR NEIGHBOR'S SCB ADDR
	BEQ	30$			;IF NO ONE HOME...
	BIT	#SBF.IC,@SB		;SEE IF WE ARE ON SPEAKING TERMS
	BEQ	30$			;IF WE DON'T KNOW HIM
	INC	LB.TMO(J)		;IF IDLE, WAIT A TIME-OUT PERIOD
	CMP	LB.TMO(J),#REPDWN	; BEFORE WE QUEUE AN ACK
	BLE	30$			;GIVE NCL A CHANCE TO DO OTHER WORK
	BIS	#SF.XAK,@SB		;LET'S SAY HELLO
;	MOVB	#1,SB.TIM(SB)		; AND DO IT SOON
	JSR	PC,NCLQRQ		;PUT A REQUEST IN THE QUEUE
30$:	RESTORE	SB			;PUT CONTENTS OF R4 BACK
	BR	99$			;RESET REP TIMER AND LEAVE

40$:	INC	LB.TMO(J)		;IF OUTPUT ACTIVE, COUNT UP
	CMP	LB.TMO(J),#REPDWN	;HAS IT BEEN TOO LONG?
	BLT	99$			;IF NOT, JUST KEEP GOINT

97$:	JSR	PC,DMCRST		;RESTART THE DMC
	 BCS	99$			;DMC IS ILL
98$:	QUEPUT	QI			;QUEUE IT FOR SERVICE
99$:	MOVB	#REPSEC,LB.REP(J)	;RESET THE REP TIMER
	RTS	PC			; AND WE'RE DONE
.SBTTL	DMCHLT - STOP THE DMC-11 IN IT'S TRACKS.

;DMCHLT	ROUTINE TO HALT THE DMC-11 AND DEQUEUE ALL DMC-11
; MESSAGE BUFFERS IT MAY OWN
;THIS ROUTINE MAY BE CALLED FROM ANY INTERRUPT LEVEL.
;
;ON RETURN, IF CARRY SET THE DMC IS KROAKED, DON'T USE IT.

DMCHLT:	SAVE	<R0,R1>			;WE CLOBBER NO REGISTERS
	BIC	#LSXKRK,LB.STX(J)	;ASSUME THE DMC WILL COOPERATE
	MOV	LB.SLA(J),R0		;GET THE DMC-11 ADDRESS
	BIS	#MC.MCL,@R0		;MASTER CLEAR THE DMC-11

;NOW FREE ALL MESSAGE BUFFERS
10$:	JSR	R0,DMCDEQ		;DEQUEUE A BUFFER
	.WORD	LB.INQ			; FROM THE INPUT WAIT QUEUE
	BCC	20$			;IF WE GOT ONE, GO FREE IT
	JSR	R0,DMCDEQ		;DEQUEUE A BUFFER
	.WORD	LB.IND			; FROM THE INPUT DONE QUEUE
	BCC	20$			;IF WE GOT ONE, GO FREE IT
	JSR	R0,DMCDEQ		;DEQUEUE A BUFFER
	.WORD	LB.OUT			; FROM THE OUTPUT QUEUE
	BCS	30$			;IF NONE, WE'RE DONE
20$:	JSR	PC,DMCGVB		;RETURN THE BUFFER
	BR	10$			; AND GO GET THE REST

30$:	BIC	#LSXRUN,LB.STX(J)	;SAY THE DMC IS NOT RUNNING
	MOV	#2000,R1	       	;INIT A LOOP COUNTER
40$:	BIT	#MC.MCL,@R0		;DID MASTER-CLEAR CLEAR YET
	BEQ	50$			;IF SO, WE'RE DONE
	SOB	R1,40$			;WAIT TILL IT DOES
	BIS	#LSXKRK,LB.STX(J)	;FLAG THIS DMC KROAKED OFF
	TWIDDLE	@LB.SLA(J)		;SAVE DMC CONTROL/STATUS REGISTER
	RESTORE	<R1,R0>			;PUT THE REGISTERS BACK
	SEC				;TAKE ERROR RETURN
	RTS	PC			; AND WE'RE DONE.

50$:	RESTORE	<R1,R0>			;PUT THE REGISTERS BACK
	CLC				;SUCCESSFUL RETURN
	RTS	PC			; AND WE'RE DONE.
.SBTTL	DMCRST - RESTART THE DMC-11

;ROUTINE TO INITIALIZE THE DMC-11, AND PERFORM THE BASE/CONTROL
;  IN.  IF IN MAINT STATE, DMC IS PUT IN MAINT MODE, AND THE TIMER
;  IS SET FOR "LONG TIME", IF NOT MAINT, DMC IS RESTARTED NORMALLY
;  AND THE HIGHER LEVEL PROTOCOL (NCL ! NSP) IS TOLD ABOUT THE LINE.
;
;ON RETURN, IF CARRY SET THEN THE DMC IS ILL, DON'T USE IT.

DMCRST:	SAVE	<R0,R1,R2,R4>		;WE CLOBBER NO REGISTERS
	JSR	PC,DMCHLT		;MAKE SURE THE DMC ISN'T RUNNING
	JSR	PC,L.DOWN		;TELL THE WORLD THE LINE IS DOWN
	CLR	LB.TMO(J)		;CLEAR THE OUTPUT TIMER
	BIT	#LSXKRK,LB.STX(J)	;IS THE DMC OK?
	BEQ	5$			;YES, THEN SET IT UPT
	SEC				;NO, FLAG ERROR
	BR	99$			;AND GET OUT OF THIS MESS

;DMC IS OK (DMCHLT SAYS SO!), INITIALIZE IT

5$:	MOV	LB.SLA(J),R4		;SET R4 := DMC-11 ADDRESS
	BIS	#MC.IEO,MC.CSO(R4)	;ENABLE OUTPUT INTERRUPTS.

;NOW DO THE BASE IN

	MOV	#MC.BSI,R0		;BASE IN CODE
	MOV	LB.BAS(J),R1		;ADDRESS OF THE BASE AREA
	CLR	R2			;RESUME BIT CLEAR
	JSR	PC,DMCIN		;DO THE INPUT TRANSACTION

;THE CONTROL IN

	MOV	#MC.CTI,R0		;CONTROL IN TRANSACTION CODE
	CLR	R1			;NO BUS ADDRESS
	CLR	R2			;FULL DUPLEX, NORMAL MODE
	BIT	#LSXMAI,LB.STX(J)	;IF MAINT MODE,
	BNE	10$			; THE REST IS DONE DIFFERENTLY.
	JSR	PC,DMCIN		;DO THE CONTROL IN
	MOVB	#REPSEC,LB.REP(J)	;SET THE REP TIMER
	JSR	PC,DDDRSS		;TELL THE WORLD WE RESTARTED
	BR	95$			;RESTORE THE REGS AND RETURN

;HERE IF IN MAINT MODE

10$:	BIS	#MC.MAI,R2		;SET MAINT MODE
	JSR	PC,DMCIN		;DO THE CONTROL IN TRANSACTION
	MOVB	#REPSEC*60.,LB.REP(J)	;START A MUCH LONGER TIMER

95$:	BIS	#LSXRUN,LB.STX(J)	;TELL EVERYONE THE DMC IS OK
	BIC	#LS..ST!LS.STK!LS.XRP,@J;FAKE OUT THAT DDCMP IS NOW RUNNING
	JSR	PC,DMCQRB		;GIVE THE DMC-11 RECEIVE BUFFERS
	CLC				;ALL IS WELL HERE
99$:	RESTORE	<R4,R2,R1,R0>		;PUT THE REGS BACK
	RTS	PC			; AND WE'RE DONE
.SBTTL	DMCINP - SEE WHAT THE DMC-11 HAS FOR US TO DO.

;DMCINP	 ROUTINE TO PROCESS DMC-11 QUEUE REQUESTS
; CALLED FROM LOOP WHEN THERE IS A REQUEST IN THE QUEUE.
;GENERAL ORDER OF BUSINESS:
;    1)	SEE IF THE DMC-11 IS RUNNING, IF NOT, KICK IT TILL IT IS.
;    2)	CHECK FOR ANY INCOMING MESSAGES. COPY THEM TO CHUNKS AND
;	GIVE THEM TO THE NEXT LEVEL (NCL ! NSP)
;    3)	CALL DMCXMT TO TRY TO SEND MESSAGES

DMCINP:	BIT	#LSXRUN,LB.STX(J)	;IS THE DMC-11 UP
	BNE	10$			;IF SO, LEAVE WELL ENOUGH ALONE
	JSR	PC,DMCRST		; OTHERWISE WAKE IT UP.
	 BCS	DMCJML			;THE DMC IS DEAD, LONG LIVE THE DMC!
10$:	MOV	LB.SLA(J),R4		;GET THE DMC-11 HDW ADDRESS
	ASSERT	#MC.IEO SET IN 2(R4)	;MAKE SURE INTERRUPT ENABLE SET

;HERE TO PROCESS INPUT MESSAGES

	JSR	PC,DMCBTC		;TRY TO COPY A MESSAGE
	BCS	DMCXMT			;IF CAN'T, GO CHECK XMIT
	SAVE	<J>			;WORRY ABOUT "J"
	BIT	#LSXMAI,LB.STX(J)	;IF WE'RE IN MAINT MODE
	BNE	20$			;USE DIFFERENT SERVICE ROUTINE
	JSR	PC,DDDGIV		;GIVE THE MSG TO (NSP ! NCL)
	RESTORE	<J>			;GET LINE BLOCK ADDR BACK
	BR	10$			; AND GO LOOK FOR MORE WORK.

;HERE IF THIS IS A MAINT MSG

20$:	SAVE	<R0>			;REMEMBER THE MESSAGE ADDR
	JSR	PC,DRBOOT		;GIVE IT TO NCL
	RESTORE	<R0,J>			;GET MSG & LB ADDR BACK
	JSR	PC,FRECKS		;FREE THE MESSAGE.
	JSR	PC,DMCRST		;RESTART THE DMC (RESET TIMER)
	 BCS	DMCJML			;IF DMC IS KROAKED, LEAVE IT ALONE
;	BR	DMCXMT			;SEE IF ANYTHING TO SEND
.SBTTL	DMCXMT - GIVE THE DMC-11 SOMETHING TO DO.

;DMCXMT	ROUTINE TO GIVE THE DMC-11 FULL BUFFERS TO EMPTY OR EMPTY
; ONES TO FILL.  CALLED FROM DMCINP OR DDCMP WHENEVER IT THINKS
; WE HAVE A MESSAGE TO SEND
;GENERAL ORDER OF BUSINESS IS:
;    1)	IF IN BOOT MODE, GO TO DMCXMB - OTHERWISE CONTINUE
;    2)	SEE IF WE'VE QUEUED TOO MANY BUFFERS.  IF SO, EXIT.
;    3)	GET A DMC-11 MESSAGE BUFFER, FILL IT, QUEUE IT ON THE
;	OUTPUT QUEUE, AND GIVE THE "NOT QUITE SENT" MESSAGE
;	BACK TO HIGHER LEVEL (THIS IS OK SINCE WE NOW HAVE OUR
;	OWN COPY)

DMCXMT:	MOV	LB.BOO(J),R0		;IN BOOT MODE?
	BNE	DMCXMB			;IF SO, DO IT ALL DIFFERENT.

	CMP	#DMCOBF,LB.OUT+CBQ.CT(J);HAVE WE SENT OUR LIMIT
	ASSERT	GE			;JUST CHECKING
	BEQ	40$			;WE HAVE.  TRY TO QUEUE INPUT

	JSR	PC,DMCGTB		;GET A DMC-11 MESSAGE BUFFER
	BCS	DMCWAI			;IF NONE, SET WAIT & EXIT
	PIOFF				;NO INTS WHILE LOOKING AT Q
	MOV	LB.OBF(J),R0		;GET NEXT MESSAGE TO GO
	BEQ	20$			;GET OUT IF NONE
	MOV	CN.MLK(R0),LB.OBF(J)	;MAKE THE SECOND THE NEW FIRST
	BNE	10$			;IF THERE IS NO SECOND, THEN
	CLR	LB.OBF+2(J)		; CLEAR THE "LAST"
10$:	DEC	LB.OBF+4(J)		;DEC THE COUNT OF MESSAGES
	CLR	CN.MLK(R0)		;IT'S NOT POLITE TO POINT
20$:	PION				;CONSISTANCY...
	TST	R0			;DID WE WIN A MESSAGE?
	BEQ	30$			;IF NOT, RETURN BUFFER & LEAVE
	JSR	PC,DMCCTB		;COPY TO MESSAGE BUFFER & SEND
	JSR	PC,COMODN		;RETURN MESSAGE TO SENDER.
	BR	DMCXMT			; AND TRY FOR ANOTHER

30$:	JSR	PC,DMCGVB		;RETURN THE MESSAGE BUFFER
40$:	JSR	PC,DMCQRB		;QUEUE RECEIVE BUFFERS
	BCC	DMCJML			;ALL DONE UNLESS NO BUFFERS

;HERE TO SET "WAIT" AND RETURN

DMCWAI:	BIS	#LSXWAI,LB.STX(J)	;INDICATE WE WANT TO RUN
DMCJML:	JMP	LOOP			; AND WE'RE DONE
;HERE TO TRY TO SEND BOOTSTRAP MESSAGES
;FLOW IS:
;    1)	SET MAINT MODE, CLEAN OUT & RESTART THE DMC-11
;    2)	MAKE SURE WE HAVE A RESPONSE BUFFER
;    3)	COPY & QUEUE THE MAINT MESSAGE.
;
;ENTER WITH MESSAGE IN R0

DMCXMB:	CLR	LB.BOO(J)		;INDICATE THE MESSAGE HAS GONE
	BIS	#LSXMAI,LB.STX(J)	;SET MAINT MODE
	JSR	PC,DMCRST		; AND MAKE SURE THE DMC-11 KNOWS
;	 BCS	10$			;DMC IS DEAD, FORGET IT
;	JSR	PC,DMCQRB		;QUEUE A RESPONSE BUFFER
	BCS	10$			; SET "WAIT" IF NONE AVAILABLE
	JSR	PC,DMCGTB		;GET A DMC-11 MESSAGE BUFFER
	BCS	10$			; SET "WAIT" IF NONE
	JSR	PC,DMCCTB		;COPY & QUEUE THE BUFFER
	JSR	PC,FRECKS		;FREE THE BOOT MESSAGE
	BR	DMCJML			; ALL DONE

;HERE TO TOSS THE BOOT REQUEST IF NO MEMORY... (OR IF THE DMC IS DEAD)

10$:	JSR	PC,FRECKS		;FREE THE MESSAGE
	BR	DMCWAI			; AND SET WAIT
.SBTTL	DMCAIN - DMC-11 "A" VECTOR INTERRUPT

;WE DON'T USE THEM

DMCAIN:	TRAP				;WASTE A WORD...
.SBTTL	DMCBIN - DMC-11 "B" VECTOR INTERRUPT

;DMCBIN	ROUTINE TO PROCESS "B" VECTOR INTERRUPTS.
;GENERAL ORDER OF BUSINESS:
;    1)	READ THE REGISTERS AND SEE WHAT TYPE OF INTERRUPT IT IS:
;	DISPATCH TO
;	   BUFFER INPUT
;	   BUFFER OUTPUT
;	   CONTROL OUT
;    2)	PROCESS MESSAGE, IF NECESSARY RESTART THE LINE & EXIT.

	.ENABL	LSB
DMCBIN:	SAVE	<R0,R1,R2,R4>		;WE DARE NOT CLOBBER ANYTHING
	MOV	LB.SLA(J),R4		;GET THE DMC-11 BASE ADDRESS
	MOV	SEL2(R4),R0		;COPY THE DMC REGISTERS
	MOV	SEL4(R4),R1		; INTO OUR
	MOV	SEL6(R4),R2		; REGISTERS

	ASSERT	#MC.RDO SET IN R0	;THIS IS AN OUTPUT TRANSACTION
	ASSERT	#2 CLEAR IN R0		;JUST RANDOM CHECKING...
	BIC	#MC.RDO,SEL2(R4)	;CLEAR REQUEST (WE COPYED INFO)
	BIT	#1,R0			;IS THIS A CONTROL OUT?
	BNE	DMCCOO			; IF SO,GO PROCESS IT
	SUB	#CB.DAT,R1		;RELOCATE TO BUFFER BASE
	BIT	#MC.IOI,R0		;IS IT INPUT OR OUTPUT
	BNE	DMCBOI			;BRANCH IF IT'S AN INPUT BFR

DMCBOO:	ASSERT	R1 EQ LB.OUT+CBQ.FS(J)	;MAKE SURE IT'S THE RIGHT ONE.
	JSR	R0,DMCDEQ		;DEQUEUE THE OUTPUT BUFFER
	.WORD	LB.OUT			; FROM THE "OUTPUT" QUEUE
	JSR	PC,DMCGVB		;RETURN THE BUFFER TO FREE POOL
	CLR	LB.TMO(J)		;INDICATE THAT OUTPUT IS WORKING
	INC	LB.OCN(J)		;COUNT UP MESSAGES FOR DDT-11 TO SEE
	TST	LB.OBF(J)		;IF THERE ARE ANY MORE MSGS
	BNE	98$			; THEN WAKE UP LOW LEVEL
	BR	99$			; OTHERWISE JUST RETURN

;HERE FOR BUFFER IN-INPUT
DMCBOI:	ASSERT	R1 EQ LB.INQ+CBQ.FS(J)	;MAKE SURE IT'S THE RIGHT ONE
	JSR	R0,DMCDEQ		;DEQUEUE THE BUFFER FROM THE
	.WORD	LB.INQ			; "INPUT WAIT" QUEUE
	BIC	#140000,R2		;"FIXUP" THE COUNT
	MOV	R2,CB.CNT(R1)		; AND STORE IT IN THE BUFFER
	JSR	R0,DMCENQ		;REQUEUE THE BUFFER ON THE
	.WORD	LB.IND			; "INPUT DONE" QUEUE
	INC	LB.ICN(J)		;COUNT UP MESSAGES FOR DDT-11 TO SEE
	BR	98$			;WAKE UP LOW LEVEL AND RETURN
;HERE ON A CONTROL OUT TRANSACTION

DMCCOO:	BIT	#MC.LOS!MC.NXM!MC.ERR,R2;ANY "FATAL" ERROR
;RDH	ASSERT	EQ			;DIE IF DEBUGGING
;RDH	BNE	97$			;RESTART THE LINE
	BEQ	3$			;HAPPY IF NO ERROR
	TWIDDLE				;NOTE ERROR OCCURRED
	BR	97$			;RESTART THE LINE

3$:	BIT	#MC.RMA,R2		;IF NOT MAINT RECEIVED
	BEQ	10$			; THEN KEEP CHECKING
	TWIDDLE				;COUNT THE MAINT MSGS
	BIS	#LSXMAI,LB.STX(J)	;ENTER MAINT MODE
	BR	97$			;RESET THE LINE AND EXIT

10$:	BIT	#MC.RST!MC.DSC,R2	;IF SOME ONE JUST CAME UP
	BEQ	20$			; (IF NOT SKIP ON)
	TWIDDLE				;COUNT THE RESTARTS
;	BIT	#LSXMAI,LB.STX(J)	;IF WE'RE NOT IN MAINT MODE.
;	BEQ	97$			;JUST QUEUE THE LINE AND EXIT
	BIC	#LSXMAI,LB.STX(J)	;CLEAR MAINT MODE
	BR	97$			; AND RESET THE LINE

20$:	BIT	#MC.DCK!MC.TMO!MC.OVR,R2;MAKE SURE A BIT IS SET
	ASSERT	NE			;OTHERWISE WHAT HAPPENED?
	TWIDDLE				;COUNT THE TIMES THIS HAPPENS
	BR	99$			; BUT JUST IGNORE IT.

97$:	JSR	PC,DMCHLT		;HALT THE DMC (LOOP RESTARTS IT)
	 BCS	99$			;IGNORE THE DMC IF IT'S BUSTED
98$:	QUEPUT	QI			;TELL LOOP TO LOOK AT THIS LINE
99$:	RESTORE	<R4,R2,R1,R0,J>		;RESTORE THE REGS
	RTI				; AND WE'RE DONE
	.DSABL	LSB
.SBTTL	DMCIN - ROUTINE TO PERFORM DMC-11 INPUT TRANSACTIONS

;DMCIN	ROUTINE TO REQUEST THE DMC-11 INPUT PORT AND STUFF IT.
;CALL	R0 := REQUEST TYPE (LOW 3 BITS)
;	R1 := SEL4 DATA (BUFFER ADDRESS)
;	R2 := SEL6 DATA (COUNT)

DMCIN:	SAVE	<R4>			;CLOBBER NO MORE THAN NECESSARY
	MOV	LB.SLA(J),R4		;GET THE DMC-11 BASE ADDR
	SAVE	<R0>			;SAVE THE TRANSACTION FOR A BIT
	MOV	#2000,R0		;FIRST WAIT FOR THE LAST TRANSACTION
10$:	BIT	#MC.RDI,@R4		; TO BE ACCEPTED (IE. RDYI CLEAR)
	BEQ	20$			;WHEN IT IS, WE CAN PROCEED
	SOB	R0,10$			;LOOP TILL IT CLEARS (OR WE GET BORED)
	TRAP				;DMC DIDN'T CLEAR RDYI.  MUST BE BROKEN

20$:	BIS	#MC.RQI,@R4		;REQUEST THE INPUT PORT
	MOV	#2000,R0		;GET A NICE ROUND NUMBER
30$:	BIT	#MC.RDI,@R4		;INPUT READY YET?
	BNE	40$			; IF SO, GO STUFF IT
	SOB	R0,30$			;LOOP FOR A BIT
	TRAP				;DMC-11 IS BAD

40$:	MOV	R1,SEL4(R4)		;STORE THE VARIOUS
	MOV	R2,SEL6(R4)		; REGISTERS AWAY
	RESTORE	<R0>			;GET REQUEST BACK
	BIC	#^C7,R0			;GET JUST THE TRANSACTION CODE
	BIS	#MC.RDI,R0		;DON'T CLEAR RDYI
	MOVB	R0,SEL0(R4)		;DISMISS THE DMC-11
	RESTORE	<R4>			;GET REG 4 BACK
	RTS	PC			; AND WE'RE DONE
.SBTTL	DMCQRB - QUEUE RECEIVE BUFFERS FOR THE DMC-11

;DMCQRB	ROUTINE TO QUEUE RECEIVE BUFFERS
;CALL	JSR	PC,DMCQRB
;RETURN	CARRY-SET			;NOT ENOUGH FREE BUFFERS
;	CARRY-CLEAR			;DMC-11 HAS BEEN FED ADEQUATELY
;IF IN MAINT MODE, ONLY ONE RECEIVE BUFFER WILL BE QUEUED.
;IF IN "NORMAL" MODE, "DMCIBF" BUFFERS WILL BE QUEUED.

DMCQRB:	SAVE	<R0,R1,R2>		;WE CLOBBER NO REGISTERS
10$:	BIT	#LSXMAI,LB.STX(J)	;ARE WE IN MAINT MODE?
	BEQ	20$			;IF NOT, GO SEE CHECK COUNT
	TST	LB.INQ+CBQ.CT(J)	;IF MAINT, SEE IF ANY QUEUED
	BNE	98$			;ONE BUFFER IS ENOUGH IN MAINT.

20$:	CMP	#DMCIBF,LB.INQ+CBQ.CT(J);SEE HOW MANY BUFFERS QUEUED
	ASSERT	GE			;MAKE SURE NOT TOO MANY
	BEQ	98$			;IF JUST ENOUGH, WE'RE DONE
	JSR	PC,DMCGTB		;GET A FREE BUFFER
	BCS	99$			;IF NONE, GO GIVE ERROR RETURN
	JSR	R0,DMCENQ		;QUEUE THE BUFFER ON THE
	.WORD	LB.INQ			;  THE INPUT WAIT QUEUE
	MOV	#MC.IOI!MC.BCI,R0	;DO A BUFFER IN (INPUT)
	ADD	#CB.DAT,R1		;ADDRESS OF DATA AREA OF MSG
	MOV	#MSGMAX,R2		;AND THIS IS HOW LONG IT IS
	JSR	PC,DMCIN		;DO THE INPUT TRANSACTION
	BR	10$			;GO TRY ANOTHER

98$:	CLC				;INDICATE SUCCESS
99$:	RESTORE	<R2,R1,R0>		;CLEAN UP
	RTS	PC			;  AND RETURN
.SBTTL	DMCCTB - ROUTINE TO COPY FROM CHUNK-MESSAGE TO MESSAGE BUFFER

;DMCCTB	- COPY FROM CHUNK-STREAM TO DMC-11 BUFFER
;CALL	R0 := POINTER TO THE CHUNKS
;	R1 := POINTER TO THE DMC-11 MESSAGE BUFFER
;	JSR	PC,DMCCTB		;COPY FROM CHUNKS TO BUFFER
;RETURN	WITH MESSAGE BUFFER QUEUED ON LB.OUT

DMCCTB:	SAVE	<R2,R3>			;WE USE THESE IN A BIT
	MOV	CN.LEN(R0),R2		;MESSAGE LENGTH (BYTES)
	MOV	R2,CB.CNT(R1)		;REMEMBER IT IN THE BUFFER

	INC	R2			;ROUND UP WHEN CONVERTING
	ASR	R2			; BYTE COUNT TO WORDS.

	SAVE	<R0,R1>			;SAVE THE TWO POINTERS
	ADD	#CN.NCT,R0		;MAKE THEM BOTH POINT TO THEIR
	ADD	#CB.DAT,R1		; RESPECTIVE DATA AREAS

	MOV	#CNKLN1/2,R3		;GET LENGTH OF FIRST CHUNK
	BR	20$			; AND SKIP INTO THE LOOP
10$:	MOV	#CNKLN2/2,R3		;GET LENGTH OF SUBSEQUENT CHUNKS
20$:	SUB	R3,R2			;ACCOUNT FOR THIS CHUNK'S WORTH
	BLE	40$			;EXIT IF THIS IS LAST CHUNK
30$:	MOV	(R0)+,(R1)+		;COPY ONE WORDS WORTH
	SOB	R3,30$			;LOOP OVER ENTIRE CHUNK
	MOV	-CNKSIZ(R0),R0		;FOLLOW CHUNK LIST
	ASSERT	CHUNK R0		;MAKE SURE IT'S REASONABLE
	TST	(R0)+			;ADVANCE TO THE DATA PORTION
	BR	10$			; AND GO DO NEXT CHUNK

40$:	ADD	R2,R3			;R3 := LENGTH OF LAST FRAGMENT
50$:	MOV	(R0)+,(R1)+		;COPY NEXT WORD
	SOB	R3,50$			;LOOP OVER ALL WORDS

	RESTORE	<R1>			;GET OUR MSG POINTER BACK
	TST	LB.OUT+CBQ.CT(J)	;IF WE ALREADY HAVE OUTPUT BUFFERS
	BNE	60$			; QUEUED, DON'T TOUCH THE REP TIMER
	CLR	LB.TMO(J)		;OTHERWISE, START TIMER FRESH
60$:	JSR	R0,DMCENQ		;QUEUE THE BUFFER ON
	.WORD	LB.OUT			; THE OUTPUT QUEUE

	MOV	#MC.BCI,R0		;BA/CC IN -- OUTPUT TRANSACTION
	MOV	CB.CNT(R1),R2		;LENGTH OF THE BUFFER
	ADD	#CB.DAT,R1		;ADDRESS OF THE BUFFER
	JSR	PC,DMCIN		;DO THE BUFFER IN TRANSACTION

	RESTORE	<R0,R3,R2>		;FIXUP THE STACK
	RTS	PC			; AND WE'RE DONE.
.SBTTL	DMCBTC - COPY DMC-11 BUFFER DATA TO CHUNKS.

;DMCBTC	ROUTINE TO DEQUEUE INPUT MESSAGES AND COPY THEM TO CHUNKS.
;CALL	JSR	PC,DMCBTC
;RETURN	CARRY-SET			;NO MESSAGE AVAILABLE
;	CARRY-CLEAR			;R0 := CHUNK-STREAM
;GENERAL LOGIC:
;   1)	FIRST MAKE SURE THERE IS A MESSAGE. RETURN QUICK IF NOT.
;   2)	NEXT TRY TO ALLOCATE ENOUGH CHUNKS FOR THE MESSAGE. IF
;	NOT AVAILABLE, SET "LSXWAI" AND GIVE ERROR RETURN
;   3)	NOW DEQUEUE THE MESSAGE BUFFER (WE DAREN'T DO IT TILL WE
;	HAVE THE CHUNKS.  NOTE THAT THERE IS THE POSSIBILITY OF
;	INTERRUPT LEVEL CLOBBERING THE OUTPUT QUEUE.  WE HAVE TO
;	CHECK FOR THIS CASE BY COMPARING LENGTH BEFORE AND AFTER)
;   4)	COPY THE DATA AND RETURN THE DMC-11 MESSAGE BUFFER.

DMCBTC:	TST	LB.IND+CBQ.CT(J)	;ANYTHING ON THE OUTPUT QUEUE
	BNE	10$			;SKIP ON IF SO
	SEC				;IF NO MESSAGE, SET ERROR FLAG
	RTS	PC			; AND RETURN

10$:	SAVE	<R1,R2,R3>		;SAVE THE REGS WE CLOBBER
	MOV	LB.IND+CBQ.FS(J),R1	;GET POINTER TO OUTPUT MSG
	BEQ	98$			;RACE WITH INTERRUPT LEVEL
	MOV	CB.CNT(R1),R1		;GET THE LENGTH OF THE MESSAGE
	MOV	R1,R2			; AND A COPY FOR LATER
	ASSERT	GT			;MAKE SURE IT'S REASONABLE

	JSR	PC,GETCNK		;GET THE FIRST CHUNK
	BEQ	98$			;IF NONE, SET "LSXWAI" & RETURN
	SUB	#CNKLN1,R1		;DECREMENT REMAINING COUNT
	BLE	30$			;BRANCH IF 1 WAS ENOUGH

20$:	MOV	R0,R3			;STORE THIS CHUNK IN R3
	JSR	PC,GETCNK		;GET THE NEXT CHUNK
	BEQ	97$			;IF NONE, FREE R3'S LIST & EXIT
	MOV	R3,@R0			;MAKE R0 POINT TO R3'S LIST
	SUB	#CNKLN2,R1		;ACCOUNT FOR 1 MORE CHUNK
	BGT	20$			;LOOP TILL WE'VE GOT EHOUGH

30$:	MOV	R0,R3			;SO WE CAN USE "97$" TO "FAIL"
	PIOFF				;NOW WE REALLY DEQUEUE THE MSG.
	MOV	LB.IND+CBQ.FS(J),R1	;R1 -> DMC-11 BUFFER
	BEQ	96$			;IF CLOBBERED, GET OUT NOW
	CMP	R2,CB.CNT(R1)		;SEE IF LENGTH IS STILL THE SAME
	BNE	96$			;IF NOT, INT LEVEL WON RACE...
	JSR	R0,DMCDEQ		;FINALLY DEQUEUE THE MESSAGE
	.WORD	LB.IND			;FROM THE "INPUT DONE" QUEUE
	ASSERT	CC			;WE KNOW THAT THERE IS ONE THERE
	PION				;ALL'S CLEAR NOW.
;DMCBTC CONTINUED

;AT THIS POINT WE HAVE:
;	R0 := POINTER TO THE EMPTY CHUNK STREAM
;	R1 := POINTER TO THE NOW DEQUEUED MESSAGE BUFFER
;	R2 := THE BYTE COUNT FOR THE TRANSFER.

	CLR	CN.MLK(R0)		;DON'T POINT...
	MOV	R2,CN.LEN(R0)		;SET THE MESSAGE LENGTH UP
	ASSERT	GT			;JUST CHECKING...

	INC	R2			;ROUND BYTES UP
	ASR	R2			; WHEN CONVERTING TO WORD COUNT

	SAVE	<R0,R1>			;SAVE THE MESSAGE POINTERS
	ADD	#CN.NCT,R0		; AND MAKE THEM BOTH POINT TO
	ADD	#CB.DAT,R1		; THEIR RESPECTIVE DATA ARESA

	MOV	#CNKLN1/2,R3		;GET # WORDS IN 1ST CHUNK
	BR	50$			; AND SKIP INTO THE CHUNK LOOP
40$:	MOV	#CNKLN2/2,R3		;# WORDS IN SUBSEQUENT CHUNKS
50$:	SUB	R3,R2			;ACCOUNT FOR THIS CHUNK FULL
	BLE	70$			;SKIP OUT IF THIS IS LAST CHUNK
60$:	MOV	(R1)+,(R0)+		;COPY NEXT WORDS WORTH
	SOB	R3,60$			;LOOP OVER ENTIRE CHUNK
	MOV	-CNKSIZ(R0),R0		;FOLLOW MESSAGE POINTER
	ASSERT	CHUNK R0		;MAKE SURE IT'S A CHUNK POINTER
	TST	(R0)+			;SKIP OVER THE LINK WORD.
	BR	40$			; AND GO FILL THIS CHUNK

70$:	ADD	R2,R3			;R3 := LENGTH OF LAST FRAGMENT
80$:	MOV	(R1)+,(R0)+		;MOVE THE NEXT WORD
	SOB	R3,80$			;LOOP OVER ALL OF LAST SEGMENT

	RESTORE	<R1,R0>			;GET OUR POINTERS BACK
	JSR	PC,DMCGVB		;RETURN THE MESSAGE BUFFER
	CLC				;INDICATE SUCCESS
	BR	99$			; AND RETURN

96$:	PION				;FIXUP THE STACK
97$:	MOV	R3,R0			;GET ADDR OF CHUNKS TO FREE
	JSR	PC,FRECKS		; AND FREE THE LIST OF THEM
98$:	BIS	#LSXWAI,LB.STX(J)	;SET "WAITING FOR MEMORY"
	SEC				;SAY WE FAILED
99$:	RESTORE	<R3,R2,R1>		;PUT BACK THE REGS
	RTS	PC			; AND WE'RE DONE
.SBTTL	ENQDEQ - ROUTINES TO QUEUE AND DEQUEUE MESSAGE BUFFERS
	.ENABL	LSB
;DMCENQ	ROUTINE TO ENQUEUE A BUFFER ON A LINE-BLOCK QUEUE
;CALL	R1 := BUFFER TO QUEUE
;	 J := LINE BLOCK ADDRESS
;	JSR	R0,DMCENQ		;CALL THIS ROUTINE
;	.WORD	LB.QUE			;  NEXT WORD IS QUEUE OFFSET

DMCENQ:	SAVE	<R2>			;WE CLOBBER NO REGS
	PIOFF				;NO RACES WITH INT LEVEL
	MOV	(R0)+,R2		;GET OFFSET (FIXUP RET ADDR)
	ADD	J,R2			;MAKE RELATIVE OFFSET ABSOLUTE
	CLR	(R1)			;MAKE SURE MSG DOESN'T POINT
	TST	CBQ.CT(R2)		;IF THE QUEUE ISN'T EMPTY
	BNE	10$			;JUST PUT THIS MSG ON FRONT
	MOV	R1,CBQ.FS(R2)		;  OTHERWISE MAKE THIS LAST
	MOV	R1,CBQ.LS(R2)		;  AND LAST MSG IN THE LIST
	BR	20$			;GO FIXUP THE COUNT & EXIT

10$:	MOV	R1,@CBQ.LS(R2)		;MAKE THE LAST ENTRY POINT TO US
	MOV	R1,CBQ.LS(R2)		;  AND MAKE US THE NEW LAST
20$:	INC	CBQ.CT(R2)		;FIXUP THE COUNT
	BR	98$			;RESTORE R2 AND RETURN
;DMCDEQ	ROUTINE TO DEQUEUE A BUFFER FROM A LINE BLOCK QUEUE
;CALL	J := LINE BLOCK ADDRESS
;	JSR	R0,DMCDEQ		;CALL THIS ROUTINE
;	.WORD	LB.QUE			;RELATIVE QUEUE OFFSET
;RETURN	CARRY SET			;QUEUE WAS EMPTY
;	CARRY CLEAR			; R1 := MESSAGE BUFFER

DMCDEQ:	SAVE	R2			;WE ONLY CLOBBER R1
	PIOFF				;NO RACES WITH INTERRUPT LEVELS
	MOV	(R0)+,R2		;GET RELATIVE QUEUE OFFSET
	ADD	J,R2			;  MAKE IT ABSOLUTE
	TST	CBQ.CT(R2)		;IS THERE ANYTHING IN THE QUEUE
	ASSERT	GE			;BETTER NOT BE NEGATIVE
	BEQ	97$			;IF EMPTY, GIVE ERROR RETURN
	MOV	CBQ.FS(R2),R1		;R1 := ADDR OF THE FIRST MSG
	MOV	@R1,CBQ.FS(R2)		;MAKE OLD 2ND THE NEW 1ST.
	DEC	CBQ.CT(R2)		;DECREMENT THE COUNT
	BNE	98$			;IF QUEUE NOT EMPTY, WE'RE DONE
	CLR	CBQ.FS(R2)		;IF EMPTY, CLEAR THE FIRST
	CLR	CBQ.LS(R2)		;  AND LAST POINTERS
	BR	98$			;  AND RETURN

97$:	PION				;RESTORE PI'S
	SEC				;INDICATE ERROR (NO BUFFERP
	BR	99$			;RESTORE R2 & RETURN
98$:	PION				;ENABLE INTERRUPTS
	CLC				;INDICATE SUCCESS
99$:	RESTORE	<R2>			;RESTORE THE REG
	RTS	R0			;  AND WE'RE DONE
	.DSABL	LSB
.SBTTL	BFRMGT - DMC-11 MESSAGE BUFFER MANAGEMENT.

;DMCGTB	ROUTINE TO ALLOCATE A DMC-11 MESSAGE BUFFER
;CALL	JSR	PC,DMCGTB		;TRY TO GET A BUFFER
;RETURN	CARRY-SET			;NO BUFFER
;	CARRY-CLEAR			;R1 := BUFFER

DMCGTB:	PIOFF				;NO INTS PLEASE
	TST	CBFREC			;SEE IF WE HAVE ANY
	ASSERT	GE			;NEGATIVE COUNT ILLEGAL
	BEQ	10$			;FAIL IF NO FREE BUFFERS
	MOV	CBFSTF,R1		;GET ADDR OF FIRST
	ASSERT	NE			;BETTER BE ONE.
	MOV	@R1,CBFSTF		;MAKE THE OLD SECOND NEW FIRST
	DEC	CBFREC			;COUNT OFF THE BUFFER
	PION				;INTS OK NOW
	CLC				;SAY WE WON
	RTS	PC			; AND RETURN

10$:	PION				;INTS OK
	SEC				;SAY WE FAILED
	RTS	PC			; AND RETURN



;DMCGVB	ROUTINE TO RETURN DMC-11 MESSAGE BUFFERS
;CALL	R1 := ADDR OF BUFFER
;	JSR	PC,DMCGVB		;RETURN THE BUFFER

DMCGVB:	PIOFF				;NO INTERRUPTS
	MOV	CBFSTF,@R1		;MAKE THIS POINT TO OLD HEAD
	MOV	R1,CBFSTF		;MAKE THIS THE NEW HEAD
	INC	CBFREC			;INCREMENT THE FREE COUNT
	PION				;INTS OK NOW
	RTS	PC			; AND WE'RE DONE
.ENDC; IF NE DMCN (AND OF DNCDMC.P11)