Google
 

Trailing-Edge - PDP-10 Archives - BB-J724B-SM_1982 - sources/lindrv.p11
There are 5 other files named lindrv.p11 in the archive. Click here to see a list.
	.SBTTL	LINDRV - line drivers

;line driver support is invoked via conditional assembly switches in the
; configuration file

;supported driver types are:

;	DQ11		- FT.DQ
;	KMC11/DUP11	- FT.KDP
;	DUP11		- FT.DUP
.REPT 0


                          COPYRIGHT (c) 1982,1981,1980, 1979
            DIGITAL EQUIPMENT CORPORATION, maynard, mass.

THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH  LICENSE AND WITH THE
INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE OR  ANY  OTHER
COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
TRANSFERRED.

THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE  WITHOUT  NOTICE
AND  SHOULD  NOT  BE  CONSTRUED  AS  A COMMITMENT BY DIGITAL EQUIPMENT
CORPORATION.

DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR  RELIABILITY  OF  ITS
SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.

.ENDR
	.SBTTL	  DQ11  - DQ11 synchronous line driver

.IF NE,FT.DQ

; this section contains subroutines to drive the DQ11.
;  these are:

DQENT:	DQINI			;initialize the DQ11
	DQRED			;read a message
	DQWRT			;output a data message
	DQCTL			;output a control message
	DQKIL			;cease any DQ11 operations
	DQINQ			;check if any more characters available in current msg
	DQON			;set DTR on
	DQOFF			;set DTR off
	DQSTS			;return DTR,DSR status
	DQTIK			;once per tick routine
	DQTRP			;stop code processor
;		REVISION HISTORY


; 4(001) BS	ADDED VERSION NUMBERS
; 4(002) RLS	CHANGE RCVPRI &RCVSEC TO CALL GETCHK INSTEAD OF RIPPING OFF
;		THEIR OWN CHUNKS
; 4(003) RLS	FIX RCVSEC & RCVPRI TO USE NEW RIPCHK FCN INSTEAD OF GETCHK.
; 4(004) RLS	04-Mar-81
;		Fix XMTPBO to not declare error when it discovers both buffers
;		have overflowed. Condition arises when last buffer is very small
;		(4 is small enough) and other interrupts keep the processor
;		busy til it finishes. Eliminated call to DQERRR at XMTPBO/16$
;		so it stops normally. Let protocol handle any line errors
;		which might result.
; 4(005) RLS	07-APR-81
;		Changes to reflect use of message header for data(DQWRIT,XMTPRI)
; 4(006) RLS	17-APR-81
;		Transform static flow control to static/line control

		

VDQ11=006

VEDIT=VEDIT+VDQ11
	.SBTTL		DQINIT - initialize the DQ11 driver

; r4 = pointer to line control block for dq11 to be initialized

DQINI:	TRACE	TRCDQF,R4	;trace "dq11 initialization"
	MOV	#DQRI,LVECRA(R4) ;set receiver interrupt process address
	MOV	#DQXI,LVECXA(R4) ;set transmitter interrupt process address
	MOV	#5,LB.TRL(R4)	;set number of trailing pads for data message
	MOV	LB.SLA(R4),R3	;get dq11 line hardware address
	DQREGS	MSC,(R3)	;select miscellaneous register
	MOV	#DQ.MC,6(R3)	;master clear the dq11
	DQREGS	MSC,(R3)	;select misc. register again
	MOV	#DQ.MC,6(R3)	;clear it again
	DQREGS	MSC,(R3)	;select misc. register again
	MOV	#10*400,6(R3)	;set '8-bit chars'
	MOV	#DQ.EIE,2(R3)	; and enable error interrupts
	CALL	DQKIL		;kill io to complete database initialization
	RETURN
	.SBTTL		DQREAD	- receive a message

; r4 = pointer to line control block
;
; returns c bit set if error, clear if ok.
;  (only error is chunks depleted)

DQRED:	TRACE	TRCDQF,<(SP),R4,JIFCLK> ;trace entry to "dq read"
	MOV	LB.SLA(R4),R3	;get hardware address
	DQREGS	SYN,(R3)	;select sync register
	MOV	#EBCSYN*401,6(R3) ;store sync value
	CALL	RCVCHK		;get enough chunks to receive a message
	BCS	16$		;can't
15$:	CALL	DQRSTR		;start receiver

; here if out of chunks.

16$:	RETURN			;give error return (c already set)
; subroutine to start the receiver.  the line block
;  points to the first two chunks.

DQRSTR:	DQREGS	PRA,(R3)	;select primary receive address
	CALL	GETRBF		;get primary chunk
	MOV	R0,LB.CH1(R4)
	ADD	#CHDAT,R0	;point to data part of chunk
	MOV	R0,6(R3)	;store in dq11
				;chunk byte count is zero
	DQREGS	PRC,(R3)	;point to primary receive count
	MOV	#-CHDATL,6(R3)	;store data length of chunk
	DQREGS	SRA,(R3)	;select secondary receive address
	CALL	GETRBF		;get secondary chunk
	MOV	R0,LB.CH2(R4)
	ADD	#CHDAT,R0	;point to data part of chunk
	MOV	R0,6(R3)	;store in dq11
	DQREGS	SRC,(R3)	;point to secondary receive count
	MOV	#-CHDATL,6(R3)	;store data length of chunk
	BIS	#LS.RGO,(R4)	;flag receiver to start on carrier

	PIOFF			;disable cpu interrupts
	BIT	#DQ.SEC,(R3)	;secondary buffer next?
	BEQ	11$		;no.
	MOV	LB.CH2(R4),LB.CCH(R4) ;yes, secondary is current
	MOV	#RG.SRC,LB.CCR(R4) ;point to its count field
	CLR	LB.CH2(R4)	;clear this as it will be q'd
	BR	12$		;arrange to start receiver

; primary buffer will be first

11$:	MOV	LB.CH1(R4),LB.CCH(R4) ;primary is current
	MOV	#RG.PRC,LB.CCR(R4) ;point to its count field
	CLR	LB.CH1(R4)	;clear this as it will be q'd

; here when everything is ready.  if carrier is already up,
;  or this is a zero-turn-around-time modem, start the
;  receiver.  otherwise let carrier coming up start it.

12$:	CLR	LB.CRD(R4)	;clear count of characters read
	MOV	LB.TC1(R4),R1	;get tcb of bsc task
	MOV	LB.CCH(R4),R0	;get current chunk
	CALL	QUECHK		;send chunk to driver while we fill it
	BIS	#DQ.DIE,2(R3)	;enable dataset interrupts
	BIC	#DQ.RDP!DQ.RDS,(R3) ;kill any "hanging" interrupts
	TST	LB.CSD(R4)	;is this a zero-turn-around-
				; time modem?
	BEQ	13$		;yes, start the receiver.
	BIT	#DQ.CAR,2(R3)	;no, is carrier already present?
	BEQ	14$		;no.
13$:	BIS	#DQ.RIE!DQ.RGO,(R3) ;yes, start the dq11 now
	BIC	#LS.RGO,(R4)	;not waiting for carrier
	BIS	#LS.RRN,(R4)	;receiver is now running
14$:	PION			;restore cpu priority

	TRACE	TRCDQF,<R4,(R4),(R3),2(R3),LB.CCH(R4),LB.CH1(R4),LB.CH2(R4),JIFCLK>
DQRCST:	CLC			;flag no error
	RETURN
	.SBTTL		DQWRIT - start transmitting a data message from LB.MSG

; r4 = pointer to line control block
;
; returns with c bit set if error, clear if not.

DQWRT:	TRACE	TRCDQF!TRCLIN,<(SP),R4,(R4),LB.MSG(R4),JIFCLK> ;trace entry to "dq write data"

	BIT	#LS.ACT,(R4)	;is dq11 still active?
	BEQ	11$		;no.
	CALL	DQKIL		;yes - kleen up
11$:	BIC	#LS.CTL,(R4)	;this is a data, not a control message
	MOV	LB.SLA(R4),R3	;get dq11 hardware address

	DQREGS	SYN,(R3)	;select sync register
	MOV	#EBCSYN*401,6(R3) ;specify the sync character
	MOV	LB.MSG(R4),R0	;get message ptr
	CALL	FMSGI		;make sure message is inited for output
	MOV	MSGPTR(R0),R1	;get ptr to 1st full byte
	BIT	#DQ.SEC,2(R3)	;check primary/secondary bit
	BEQ	XMTPRI		;primary is next

; here if the secondary registers will go first.  make the
;  first data chunk the secondary and the second the
;  primary.

				;R1/ptr to 1st full byte of message
	DQREGS	STA,(R3)	;select secondary transmitter address
	MOV	R1,6(R3)	;store address in dq11
	DQREGS	STC,(R3)	;point to secondary transmit count
	MOV	CHLEN(R0),R1	;get data length
	NEG	R1		;negate it
	MOV	R1,6(R3)	;give -length to dq11
	MOV	R0,LB.CH4(R4)	;remember secondary xmit chunk
	MOV	(R0),R0		;get next chunk
	BNE	13$		;branch if there is a second chunk
	DQREGS	PTC,(R3)	;no second chunk
	CLR	6(R3)		;so clear primary count reg
	CLR	LB.CH3(R4)	;note in lcb, last chunk.
	CLR	LB.CH4(R4)
	BR	14$		;and we are done.

; here if there is a primary chunk

13$:	DQREGS	PTA,(R3)	;select primary transmit address
	MOV	R0,R1		;compute data address
	ADD	#CHDAT,R1
	MOV	R1,6(R3)	;store address in dq11
	DQREGS	PTC,(R3)	;select primary transmit count
	MOV	CHLEN(R0),R1	;get data length
	NEG	R1		;negate it for dq11
	MOV	R1,6(R3)	;store length in cc register
	MOV	R0,LB.CH3(R4)	;remember primary xmit chunk
14$:	CALL	DQXSTR		;start transmitter
	RETURN
; here if the primary registers will go first.  make the first
;  data chunk the primary and the second the secondary.

				;r1/ptr to 1st full byte of message
XMTPRI:	DQREGS	PTA,(R3)	;select primary transmitter address
	MOV	R1,6(R3)	;store address in dq11
	DQREGS	PTC,(R3)	;select primary transmit count
	MOV	CHLEN(R0),R1	;get data length
	NEG	R1		;negate it
	MOV	R1,6(R3)	;store negated length in dq11
	MOV	R0,LB.CH3(R4)	;remember primary xmit chunk
	MOV	(R0),R0		;get next chunk
	BNE	11$		;branch if there is a second chunk
	DQREGS	STC,(R3)	;no second chunk
	CLR	6(R3)		;so make secondary count zero
	CLR	LB.CH4(R4)	;note in lb, last chunk.
	CLR	LB.CH3(R4)
	BR	12$		; and we are done.

; here if there is another chunk.

11$:	DQREGS	STA,(R3)	;select secondary transmit address
	MOV	R0,R1		;compute data address
	ADD	#CHDAT,R1
	MOV	R1,6(R3)	;store address in dq11
	DQREGS	STC,(R3)	;select secondary transmit count
	MOV	CHLEN(R0),R1	;get data length
	NEG	R1		;negate it
	MOV	R1,6(R3)	;store in cc register
	MOV	R0,LB.CH4(R4)	;remember secondary xmit chunk

; here when all done.

12$:	CALL	DQXSTR		;start transmitter
	RETURN
; subroutine to arrange to start the transmitter

DQXSTR:	TRACE	TRCDQF,<(SP),R4,LB.CH3(R4),LB.CH4(R4),JIFCLK>
	BIS	#LS.XGO,(R4)	;xmitter waiting for cts

; experience with bell 303 modems may lead to inserting code
;  here to avoid setting rts if cts is still up.

	BIS	#DQ.RTS,2(R3)	;raise request to send
	MOV	LB.CSD(R4),LB.DEW(R4) ;wait a while, then check for cts
				;note that modem interrupts are ignored
				; until time is up.
	BEQ	13$		;time is zero, i.e., already up.
	CLC			;time not up, clear 'c' flag
	RETURN

; here if clear-to-send delay specified is zero.
;  cause a modem interrupt in case cts is already up, and
;  enable modem interrupts so that if cts is not up we will
;  get an interrupt when it comes up.  note that
;  a zero clear-to-send delay should be used only with modems
;  that have a clear-to-send delay of less than 16 milliseconds,
;  since a larger delay can be timed without loss of efficiency
;  and too short a delay (less than about 8 milliseconds)
;  does not work with some ibm hosts.

13$:	BIS	#DQ.DSF!DQ.DIE,2(R3) ;enable interrupts and
				; cause one.
	CLC			;clear 'c' flag
	RETURN
	.SBTTL		DQCNTL - transmit a control message

; r4 = pointer to line control block
; r0 = pointer to message to send
; r1 = length of message to send
;
; on return:
;
;	c set if error, clear if not.

DQCTL:	TRACE	TRCLIN!TRCDQF,<(SP),R4,(R4),R0,R1,JIFCLK> ;trace entry to "dq write control"
	MOV	R0,LB.CMA(R4)	;remember control message address
	MOV	R1,LB.CMC(R4)	; and length

	BIT	#LS.ACT,(R4)	;is dq11 still active?
	BEQ	11$		;no.
	CALL	DQKIL		;yes - kleen up
11$:	BIS	#LS.CTL,(R4)	;we are working on a control message
	MOV	LB.SLA(R4),R3	;get dq11 hardware address

	DQREGS	SYN,(R3)	;select sync register
	MOV	#EBCSYN*401,6(R3) ;specify the sync character
	BIT	#DQ.SEC,2(R3)	;check primary/secondary bit
	BEQ	14$		;primary is next

; here if the first register set to go will be the
;  secondary registers.

13$:	DQREGS	STA,(R3)	;select secondary xmit address
	MOV	R0,6(R3)	;store address in dq11
	DQREGS	STC,(R3)	;select secondary xmit count
	NEG	R1		;negate count
	MOV	R1,6(R3)	;store count in dq11
	DQREGS	PTC,(R3)	;select primary xmit count
	CLR	6(R3)		;no subsequent chunk
	BR	15$		;go start dq11

; here if the first register set to go will be the
;  primary registers.

14$:	DQREGS	PTA,(R3)	; select primary xmit address
	MOV	R0,6(R3)	;store address in dq11
	DQREGS	PTC,(R3)	;select primary xmit count
	NEG	R1		;negate count
	MOV	R1,6(R3)	; store count in dq11
	DQREGS	STC,(R3)	;select secondary xmit count
	CLR	6(R3)		; no subsequent data

; here to start the dq11

15$:	CLR	LB.CH3(R4)	;flag no more data to send
	CLR	LB.CH4(R4)	; for interrupt routines
	CALL	DQXSTR		; start the transmitter
	RETURN
	.SBTTL		DQRI -	interrupt routine for DQ11 receivers


DQRI:	RCVADJ			;R4/line block
	TRACE	TRCINT,<2(SP),6(SP),R4,JIFCLK> ;trace DQ11 interrupt
	SAVE	<R3,R1,R0>
	MOV	LB.SLA(R4),R3	;get hardware address of dq11
	INC	LB.RCT(R4)	;one more receiver interrupt

; here to check for (another) dq11 receiver interrupt bit

10$:	MOV	(R3),R0		;get receiver status register
	MOV	R0,LB.RST(R4)	;store last receiver status
				;trace line,line sts,rsts,xsts,errsts,time
	TRACE	TRCDQF,<R4,(R4),R0,2(R3),4(R3),JIFCLK>
	BIT	#DQ.RDP!DQ.RDS!DQ.VCH,R0 ;any interrupt bits?
	BEQ	14$		;no, check receiver running and exit
	BIT	#DQ.RDS,R0	;yes, secondary buffer?
	BEQ	11$		;no.
	CALL	RCVSEC		;yes, process buffer overflow
	BCC	10$		;if all ok, test for more bits
	BR	15$		;otherwise just exit.

11$:	BIT	#DQ.RDP,R0	;primary buffer overflow?
	BEQ	12$		;no.
	CALL	RCVPRI		;yes, process buffer overflow
	BCC	10$		;if all ok, test for more bits
	BR	15$		;otherwise just exit

; here on special character interrupt - only enabled if task waiting

12$:	BIC	#DQ.VCH,(R3)	;clear special character flag
	BIT	#DQ.RAC,(R3)	;is receiver active (i.e., seen sync)?
	BEQ	13$		;no, don't change sync char
	BIT	#DQ.CIE,(R3)	;interrupts still enabled?
	BEQ	14$		;no, dont bother doing this twice
	BIC	#DQ.CIE,(R3)	;yes, disable special char interrupts
	CALL	RCVSCC		;store cc and wake task
	DQREGS	SYN,(R3)	;point to sync register
	MOV	#EBCPAD*401,6(R3) ;get interrupt on trailing pad
13$:	BR	10$		;check for more interrupt bits

; here to verify that the receiver is still running

14$:	BIT	#LS.RRN,(R4)	;should receiver be running?
	BEQ	15$		;no.
	BIT	#DQ.RGO,R0	;yes, is it?
	BNE	15$		;yes, all is ok.
	INC	LB.SE4(R4)	;no, "receiver not fast enough"
	CALL	DQERR		;record dq11 error

; here to restore registers and return.

15$:	RESTOR	<R0,R1,R3,R4>
	JMP	SKDCHK		;exit interrupt
	.SBTTL		DQERR - record a DQ11 error

DQERR:	MOV	(R3),LB.SE2(R4)	;record status reg 1
	MOV	2(R3),LB.SE3(R4) ; and status reg 2
	ATRACE	<(SP),R4,(R4),(R3),2(R3),4(R3),JIFCLK>
	BIT	#LS.ENB,(R4)	;is it because line is disabled
	BEQ	20$
	INC	LB.SE1(R4)	;count master error counter
	BIS	#LS.ERR,(R4)	;flag an error

.IF	NE,DEBUG
	BIT	#TRCLER,TRCHLT	;check for error halts
	BEQ	20$
	STOPCD	DBG
.ENDC				;.if ne,debug

20$:	RETURN
; subroutine to process a secondary receive buffer overflow

RCVSEC:	BIT	#DQ.RDP,R0	;other buffer interrupted yet?
	BNE	12$		;yes, we are too slow.
	BIC	#DQ.RDS,(R3)	;clear the flag bit
	CALL	GETRBF		;get a chunk
	BCS	10$		;ran out of receive buffer allotment
	MOV	R0,LB.CH2(R4)	;save ptr to next secondary buffer

	DQREGS	SRA,(R3)	;select secondary receive address
	MOV	R0,R1		;compute data address
	ADD	#CHDAT,R1
	MOV	R1,6(R3)	;store address in dq11
	DQREGS	SRC,(R3)	;select secondary receive count
	MOV	#-CHDATL,6(R3)	;store count

10$:	MOV	LB.CCH(R4),R0	;get chunk just filled
	MOV	#CHDATL,CHLEN(R0) ;note chunk is full

; processing of filled chunk is complete

	MOV	LB.CH1(R4),R0	;get new current chunk
	BEQ	14$		;max size message
	MOV	R0,LB.CCH(R4)	;make it current
	MOV	LB.TC1(R4),R1	;get tcb of task to process received chunks
	CALL	QUECHK		;send him the chunk
	MOV	#RG.PRC,LB.CCR(R4) ;point to its count reg
	CLR	LB.CH1(R4)	;clear this since it has been q'd
	ADD	#CHDATL,LB.CRD(R4) ;count more data read
	CLC			;no, return flag all ok
	RETURN

; come here if we are too slow.

12$:	INC	LB.SE4(R4)	;count "receiver not fast enough"
	CALL	DQERR		;record dq11 error
	SEC			;flag error
	RETURN

14$:	CALL	DQRSTP		;kill the receiver
	SEC			;flag error
	RETURN
; subroutine to process a primary buffer overflow.
;
; c set on return if out of chunks.

RCVPRI:	BIC	#DQ.RDP,(R3)	;clear primary overflow flag
	CALL	GETRBF		;get a chunk
	BCS	10$		;ran out of receive buffer allotment
	MOV	R0,LB.CH1(R4)	;save ptr to next primary buffer

	DQREGS	PRA,(R3)	;select primary receive address
	MOV	R0,R1		;compute data address
	ADD	#CHDAT,R1
	MOV	R1,6(R3)	;store address in dq11
	DQREGS	PRC,(R3)	;select primary receive count
	MOV	#-CHDATL,6(R3)	;store count

10$:	MOV	LB.CCH(R4),R0	;get chunk just filled
	MOV	#CHDATL,CHLEN(R0) ;mark chunk full

; processing of filled chunk is complete.

	MOV	LB.CH2(R4),R0	;get secondary chunk
	BEQ	13$		;max size message
	MOV	R0,LB.CCH(R4)	;it is now current
	MOV	LB.TC1(R4),R1	;get tcb of receive chunk handler
	CALL	QUECHK		;send him the chunk
	MOV	#RG.SRC,LB.CCR(R4) ;point to its char count reg
	CLR	LB.CH2(R4)	;clear this since it has been q'd
	ADD	#CHDATL,LB.CRD(R4) ;increment count of characters read
	CLC			;no, clear "c" to flag ok
	RETURN

13$:	CALL	DQRSTP		;kill the receiver
	SEC			;flag error
	RETURN
; subroutine to stop the receiver
;
;   r4 points to lcb, r3 to csr.

DQRSTP:				;trace caller,line sts,dq rsts,dq xsts,dq errsts
	TRACE	TRCDQF,<(SP),R4,(R4),(R3),2(R3),4(R3),JIFCLK>
RCVKIL:
	BIC	#DQ.RDP!DQ.RDS!DQ.RGO!DQ.CIE!DQ.RIE,(R3)
				;kill the dq11 receiver
	BIC	#DQ.DIE,2(R3)	;disable dataset interrupts
	CALL	RCVCLR		;flush any extra receive buffers
	SAVE	R5
	MOV	LB.TC1(R4),R5
	MOV	LB.CH1(R4),R0
	CALL	FRECHK		;release leftover primary chunk
	MOV	LB.CH2(R4),R0
	CALL	FRECHK		;release leftover secondary chunk
	RESTOR	R5
	CLR	LB.CCH(R4)	;no current chunk
	BIC	#LS.RGO!LS.RRN,(R4) ;receiver not running
	SIGNAL	LB.TC1(R4),EBINTR ;maybe, unwait it.
	SETSKD	LB.TC1(R4)	;force scheduling pass
	RETURN
; subroutine to store the current character count in the current
;  chunk and restart the driver task if the count has changed.

RCVSCC:	MOVB	LB.CCR(R4),5(R3) ;point to active count reg
	MOV	6(R3),R1	;fetch current count register
	ADD	#CHDATL,R1	;make true count
	MOV	LB.CCH(R4),R0	;get pointer to current chunk
	CMP	R1,CHLEN(R0)	;has count changed?
	BEQ	11$		;no.
	MOV	R1,CHLEN(R0)	;yes, store new count
	BIC	#DQ.CIE,(R3)	;no longer need special char interrupts
	SIGNAL	LB.TC1(R4),EBINTR
	SETSKD	LB.TC1(R4)	;force scheduling pass
	SEC			;indicate that characters were waiting
11$:	RETURN
	.SBTTL		DQXI -  DQ11 transmiter and status interrupts

DQXI:	XMTADJ			;R4/line block
	TRACE	TRCINT,<2(SP),6(SP),R4,JIFCLK> ;trace DQ11 interrupt
	SAVE	<R3,R0,R1>
	MOV	LB.SLA(R4),R3	;get hardware address
	INC	LB.XCT(R4)	;one more transmit interrupt

; here to check for (another) dq11 transmitter or status
;  interrupt bit.

10$:	MOV	2(R3),R1	;get transmitter status bits
	MOV	R1,LB.XST(R4)	;record last transmit status
	TRACE	TRCDQF,<R4,(R4),(R3),R1,4(R3),JIFCLK> ;trace line,line sts,rsts,xsts,errsts
	MOV	4(R3),R0	;any errors?
	BMI	30$		;yes.
	BIT	#DQ.XDP,R1	;no, primary buffer overflow?
	BEQ	11$
	CALL	XMTPBO		;yes.
	BR	10$

11$:	BIT	#DQ.XDS,R1	;no, secondary?
	BEQ	12$
	CALL	XMTSBO		;yes.
	BR	10$

12$:	BIT	#DQ.DSF,R1	;no, dataset flag?
	BEQ	15$		;no.  all done.
	CALL	DQDSF		;yes.
	BR	10$		;check for more bits

; here when no more flags are found.

15$:	BIT	#LS.XRN,(R4)	;should transmitter be running?
	BEQ	20$		;no.
	BIT	#DQ.XGO,R1	;yes, is it?
	BNE	20$		;yes, all ok.
	INC	LB.SE5(R4)	;no, record error
	CALL	DQERR		;update error counters and store status
	CALL	DQXSTP		;stop the transmitter
20$:	RESTOR	<R1,R0,R3,R4>
	JMP	SKDCHK		;exit the interrupt.

30$:	CALL	DQXERR
	BR	10$
; here on error interrupt

DQXERR:	MOV	R0,LB.ERS(R4)	;save error status
	BIS	#LS.ERR,(R4)	;flag an error
	ATRACE	<R4,(R4),R0,JIFCLK> ;trace dq11 status
	BIT	#DQ.XCL,R0	;transmit clock loss?
	BEQ	11$		;no.
	INC	LB.XCL(R4)	;yes, record it.
	BIC	#DQ.XCL,4(R3)	;clear the flag
	CALL	DQXSTP		;stop the transmitter
	RETURN			;look for more bits

; here if not transmitter clock loss

11$:	BIT	#DQ.RCL,R0	;receiver clock loss?
	BEQ	12$		;no.
	INC	LB.RCL(R4)	;yes, record it.
	BIC	#DQ.RCL,4(R3)	;clear the flag
	CALL	DQRSTP		;terminate receiver
	RETURN			;look for more bits

; here if an error other than clock loss happens

12$:	STOPCD	QER		;dq11 error
; here when the primary buffer overflows.

XMTPBO:	BIT	#DQ.XDS,R1	;has other buffer overflowed?
	BNE	16$		;yes, we are too slow.
	MOV	LB.CH4(R4),R0	;get secondary chunk
	BNE	12$		;there is one
	CALL	DQXSTP		;none--end of transmission
	RETURN			;go look for more flags

; here if there is a secondary chunk

12$:	MOV	(R0),R0		;get its successor
	BEQ	15$		;no more.
	MOV	R0,LB.CH3(R4)	;store new primary chunk
	DQREGS	PTA,(R3)	;select primary transmitter address
	MOV	R0,R1		;compute data address
	ADD	#CHDAT,R1
	MOV	R1,6(R3)	;store new data address
	DQREGS	PTC,(R3)	;select primary transmit count
	MOV	CHLEN(R0),R1	;get length of data
	NEG	R1		;negate it
	MOV	R1,6(R3)	;store negative of data length
	BIC	#DQ.XDP,2(R3)	;clear flag
14$:	RETURN			;look for more flags

; here on primary transmit interrupt with no more data

15$:	DQREGS	PTC,(R3)	;select count register
	CLR	6(R3)		;no more data
	CLR	LB.CH3(R4)	;no longer a primary chunk
	CLR	LB.CH4(R4)	; nor a secondary
	BIC	#DQ.XDP,2(R3)	;clear this flag
	RETURN			; and go look for another.

; here if both buffers have overflowed.

16$:	INC	LB.SE5(R4)	;count "transmitter too slow"
	CALL	DQERR		;record error information
	CALL	DQXSTP		;stop the transmitter
	RETURN			;look for more bits
; here if secondary buffer overflows

XMTSBO:	MOV	LB.CH3(R4),R0	;get primary chunk
	BNE	12$		;there is one
	CALL	DQXSTP		;none--stop transmitter
	RETURN			;and look for more bits

; here if there is a primary chunk

12$:	MOV	(R0),R0		;get primary chunk's successor
	BEQ	14$		;end of data
	MOV	R0,LB.CH4(R4)	;store new secondary chunk
	DQREGS	STA,(R3)	;select secondary transmit address
	MOV	R0,R1		;compute data address
	ADD	#CHDAT,R1
	MOV	R1,6(R3)	;store address of new chunk
	DQREGS	STC,(R3)	;select secondary transmit count
	MOV	CHLEN(R0),R1	;get length of data in chunk
	NEG	R1		;negate it for dq11
	MOV	R1,6(R3)	;store in dq11 cc register
	BIC	#DQ.XDS,2(R3)	;clear this flag
	RETURN			;check for more flags

; here on secondary transmit interrupt with no more data

14$:	DQREGS	STC,(R3)	;select count register
	CLR	6(R3)		;no more data
	CLR	LB.CH4(R4)	;no longer a secondary chunk
	CLR	LB.CH3(R4)	; nor a primary
	BIC	#DQ.XDS,2(R3)	;clear this flag
	RETURN			; and go look for another.
	.SBTTL		DQDSF - process a data set flag.

	.ENABL	LSB
DQDSF:	BIC	#DQ.DSF,2(R3)	;clear dataset flag
	INC	LB.DIC(R4)	;count dataset interrupts
	MOV	2(R3),R1	;get dataset bits again
				; in case they changed
	MOV	R1,LB.DIS(R4)	;record last modem interrupt status
	TST	LB.DEW(R4)	;waiting for dataset enable?
	BNE	14$		;yes, ignore modem signal.
	MOV	(R4),R0		;no, get status bits
	BIT	#LS.XGO,R0	;waiting to start xmitter?
	BEQ	11$		;no.
	CALL	15$		;yes, check for clear-to-send.
11$:	BIT	#LS.XRN,R0	;running the transmitter?
	BEQ	12$		;no.
	CALL	17$		;yes, be sure cts still up
12$:	BIT	#LS.RGO,R0	;waiting to start receiver?
	BEQ	13$		;no.
	CALL	19$		;yes, see if carrier up yet
13$:	BIT	#LS.RRN,R0	;running receiver?
	BEQ	14$		;no.
	CALL	21$		;yes, set sync reg to pads
14$:	RETURN

; four subroutines to process data set flags.  they are called
;  based on the current state of the dq11.
;
; r1 = modem status flags, from the dq11.
;
; here if we are waiting to start the transmitter

15$:	BIT	#DQ.CTS,R1	;have we clear-to-send?
	BEQ	16$		;no, wait for it.
	BIC	#LS.XGO,(R4)	;no longer waiting to start transmitter
	BIS	#LS.XRN,(R4)	;now running transmitter
	BIS	#DQ.HD,(R3)	;blind the receiver
	BIS	#DQ.XGO!DQ.XIE,2(R3) ;start the transmitter
16$:	RETURN			;all done.

; here if we are running the transmitter

17$:	BIT	#DQ.CTS,R1	;is clear-to-send still up?
	BNE	18$		;yes, all ok.
	INC	LB.SE6(R4)	;no, note clear-to-send failure
	CALL	DQERR		;note error
	CALL	DQXSTP		;stop the transmitter
18$:	RETURN			;all done.

; here if we are waiting to start the receiver

19$:	BIT	#DQ.CAR,R1	;is carrier up yet?
	BEQ	20$		;no, keep waiting for it.
	BIS	#DQ.RGO!DQ.RIE,(R3) ;yes, start the receiver
	BIC	#LS.RGO,(R4)	;no longer waiting to start receiver

23$:	BIS	#LS.RRN!LS.CAR,(R4) ;now running receiver
	TRACE	TRCDQF,<R4,(R4),R1,JIFCLK>
DQCDT:	RETURN

20$:	BIC	#LS.CAR,(R4)	;haven't seen carrier yet
	RETURN			;all done.

; here if the receiver is running.

21$:	BIT	#DQ.RAC,(R3)	;is receiver active?
	BEQ	22$		;no.  random modem transition.
	DQREGS	SYN,(R3)	;yes, point to sync register
	MOV	#EBCPAD*401,6(R3) ;change sync to trailing pad
	SAVE	R1
	CALL	RCVSCC		;store char count
	RESTOR	R1

22$:	BIT	#DQ.CAR,R1	;check carrier
	BNE	25$		;ok
	BIT	#LS.CAR,(R4)	;no - seen carrier before?
	BEQ	24$		;no - a non-event
	TRACE	TRCDQF,<R4,(R4),R1,JIFCLK> ;lost it
DQCFL:	BIC	#LS.CAR,(R4)

24$:	RETURN

25$:	BIT	#LS.CAR,(R4)	;seen carrier before?
	BEQ	23$		;no - carrier detect event
	RETURN			;all done.
	.DSABL	LSB
; subroutine to stop the transmitter

; r4 points to the lcb
; r3 points to the csr

DQXSTP:				;trace caller,line sts,dq rsts,dq xsts,dq errsts
	TRACE	TRCDQF,<(SP),R4,(R4),(R3),2(R3),4(R3),JIFCLK>
XMTKIL:	BIC	#LS.XGO!LS.XRN,(R4)
				;dq11 transmitter not running
	BIC	#DQ.RTS!DQ.XDP!DQ.XDS!DQ.XGO!DQ.XIE,2(R3)
				;shut down the transmitter
	BIC	#DQ.HD,(R3)	;unblind the receiver -- this is
				; done here because transmit active
				; won't clear for a while and we
				; want to receive right away for the
				; 0 turn-around-time modems.
	CLR	LB.DEW(R4)	;not waiting to enable modem
	MOV	LB.TC1(R4),R1	;get tcb of bsc task
	SIGNAL	R1,EBINTR	;maybe, restart it.
	SETSKD	R1		;force scheduling pass
11$:	CLC			;mark ok
	RETURN
	.SBTTL		DQINQ - check the dq11 for recently received characters

; r5/tcb
;  r4 and r3 are not set up and must be preserved.
;
; on return:
;
;	c set: rcvscc has found characters in the dq11
;	c clear: the dq11 has no characters.  it is now
;		enabled for special character interrupts.

DQINQ:	SAVE	<R3,R4>
	MOV	TCLCB(R5),R4	;point to lcb
	MOV	LB.SLA(R4),R3	;point to dq11 csr
	PIOFF			;disable interrupts
	CALL	RCVSCC		;see if any chars in the dq11
	BCS	10$		;carry set => there characters to process
	BIS	#DQ.CIE,(R3)	;enable special character interrupts
	BIC	#1,(SP)		;set carry clear for return
	BR	15$

10$:	BIS	#1,(SP)		;set carry on for return

15$:	PION			;restore interrupt level
	RESTOR	<R4,R3>
	RETURN			;RCVSCC set/cleared cry if characters/none
				;	also cleared DQ.CIE if characters found
	.SBTTL		DQKIL - kill any DQ11 operations

; r4 = lcb pointer
; r5 = pointer to bsc tcb
;
; destroys r3

DQKIL:	TRACE	TRCDQF,<(SP),R4,(R4),LB.ALR(R4),JIFCLK> ;trace entry to DQKIL
	MOV	LB.SLA(R4),R3	;point to csr
	PIOFF			;disable interrupts
	BIT	#LS.ACT,(R4)	;is the dq11 doing anything?
	BEQ	12$		;no.
	BIT	#LS.XGO!LS.XRN,(R4) ;yes, transmitter running?
	BEQ	11$		;no.
	CALL	DQXSTP		;yes, stop the transmitter
11$:	BIT	#LS.RGO!LS.RRN,(R4) ;is receiver running?
	BEQ	12$		;no.
	CALL	DQRSTP		;yes, stop the receiver
12$:	BIC	#DQ.DIE,2(R3)	;clear dataset interrupt enbale
	PION			;restore interrupts
13$:	CALL	DEQCHK		;any chunks in reveiver queue?
	BCS	14$		;no.
	CALL	FRECHK		; by sending to background task
	BR	13$		;be sure we've got them all.

; here when the dq11 has been killed.

14$:	RETURN
	.SBTTL		DQOFF,DQON,DQSTS - DSR,DTR functions

;  set and clear the modem signals dtr and dsr.
;
; subroutine to clear dtr
;
; r0 = lcb address

DQOFF:	SAVE	R0
	MOV	LB.SLA(R0),R0	;point to csr
	MOV	2(R0),-(SP)	;save current state
	BIC	#DQ.DTR,2(R0)	;clear dtr
	TRACE	TRCDQF,<4(SP),4(SP),2(R0),JIFCLK> ;trace caller,line,dq status
	BIT	#DQ.DTR,(SP)+	;was it on ?
	BEQ	10$		;no - go away
	DSCHED	,#3*JIFSEC	;yes - allow time for slow modems and phones to hangup
10$:	RESTOR	R0
	RETURN

; subroutine to set dtr

DQON:	SAVE	R0
	MOV	LB.SLA(R0),R0	;point to csr
	BIS	#DQ.DTR,2(R0)	;set dtr
	TRACE	TRCDQF,<2(SP),2(SP),2(R0),JIFCLK> ;trace caller,line,dq status
	RESTOR	R0
	RETURN

; subroutine to return values of dtr and dsr.
;
; r0 = lcb address
; r1 bits 1 and 2 are 0.
;
; on return:
;
;	bit 1 of r1 = dtr
;	bit 2 of r1 = dsr
;	 r1 is otherwise undisturbed.

DQSTS:	SAVE	R0
	MOV	LB.SLA(R0),R0	;point to csr
	BIT	#DQ.DTR,2(R0)	;is dtr set?
	BEQ	11$		;no.
	BIS	#B1,R1		;yes, set flag in r1
11$:	BIT	#DQ.DSR,2(R0)	;is dsr set?
	BEQ	12$		;no.
	BIS	#B2,R1		;yes, set flag in r1
12$:	TRACE	TRCDQF,<2(SP),2(SP),2(R0),JIFCLK> ;trace caller,line,dq status
	RESTOR	R0
	RETURN
	.SBTTL		DQTRP -  trap handler stop code processor

;  this subroutine is called to store the status of the dq11
;  in the lcb for post-mortum debugging.
;  r5 does not point to a tcb, but it must not be destroyed.
;
; r4 = lcb address

DQTRP:	MOV	LB.SLA(R4),R3	;get hardware address
	MOV	(R3),LB.RG0(R4)	;store reg 0
	MOV	2(R3),LB.RG1(R4) ;and the other regs
	MOV	4(R3),LB.RG2(R4)
	MOV	6(R3),LB.RG3(R4)
	CLR	R2		;register pointer
	MOV	R4,R1		;build pointer into lcb
	ADD	#LB.RGI,R1
	MOV	#20,R0		;number of registers to store
11$:	MOVB	R2,5(R3)	;point to internal register
	MOV	6(R3),(R1)+	;store register for debugging
	INC	R2		;go on to next register
	SOB	R0,11$		;do all internal registers
	RETURN
	.SBTTL		DQTIK - DQ11 once per tick functions

; R4/line block

DQTIK:	BIT	#LF.DAC,LB.FGS(R4) ;check for shut down
	BNE	14$		;yes - forget it
	MOV	LB.SLA(R4),R3	;point to csr
	TST	LB.DEW(R4)	;waiting to set modem enable?
	BEQ	12$		;no.
	DEC	LB.DEW(R4)	;yes, waited long enough?
	BNE	12$		;no, wait a while longer.
	BIS	#DQ.DSF!DQ.DIE,2(R3) ;yes, 
				; enable interrupt and force one now
				; to catch current state
12$:	BIT	#LS.RRN,(R4)	;is receiver running?
	BEQ	14$		;no, done with this line.
	BIT	#DQ.RAC,(R3)	;is receiver active?
	BEQ	14$		;no, still searching for sync
	PIOFF			;disable interrupts
	BIT	#DQ.RAC,(R3)	;is receiver still active?
	BEQ	13$		;no.
	DQREGS	SYN,(R3)	;yes, point to sync reg
	MOV	#EBCPAD*401,6(R3) ;change sync char to trailing pad
	CALL	RCVSCC		;significant event if count changed
13$:	PION			;enable interrupts
14$:	RETURN


.ENDC	;.IF NE,FT.DQ
	.SBTTL	  KMC11 - KMC11/DUP11 synchronous line driver

.IF NE,FT.KDP


; this section contains subroutines to drive the KMC11/DUP11.

KDPENT:	KDPINI			;initialize the KMC11 and DUP11s
	KDPRED			;read a message
	KDPWRT			;output a data message
	KDPCTL			;output a control message
	KDPKIL			;cease any KMC/DUP11 operations
	KDPINQ			;check if any more characters available in current msg
	KDPON			;set DTR on
	KDPOFF			;set DTR off
	KDPSTS			;return DTR,DSR status
	KDPTIK			;once per tick routine
	KDPTRP			;stop code processor
;		REVISION HISTORY


; 4(001) BS	ADDED EDIT NUMBERS

; 4(002) 25-Feb-81 RLS	add CIEWAK function and its uses - to make sure BSC task
;			is awakened when it needs to be, line faults as well as
;			character arrival.
;			Make XMTSTP and RCVSTP be carefull  and call CIEWAK when done. 
; 4(003) 07-Apr-81 RLS	Changes to reflect use of message header fordata(DQWRIT)
;			DQWRIT has special hack to transform new message format
;			into old for benefit of KMC microcode.
; 4(004) 17-Apr-81 RLS
;		Transform static flow control to static/line control
; 4(005) 09-JUL-81 RLS	Add timer/event counter(LB.DRP) to lcb for kmc's.  When
;			carrier drops while receiver running, timer is set to
;			wait for kmc to possibly finish transferring characters
;			to 11 memory.  Once/tick code counts down timer and declares
;			line error if end of message has not been seen by BSC.
;			This fixes a race between dataset interrupts and kmc
;			observed at low line speeds(2400 and lower).
; 4(006) 22-JAN-82 RLS	Reformat interfaces to driver functions. DQxxxx symbols
;			are mapped to kdpyyy symbols...yyy is mnemonical for xxx.
; 4(007) 18-MAR-82 RLS	GCO 4.2.1274 - explicitly clear UP.XDL and UP.SND when
;			UP.XDL error occurs...KMC cannot always do this for
;			some reason.
; 4(010) 21-MAR-82 RLS	GCO 4.2.1281 - add time stamps to some trace events.

VKMC11=010

VEDIT=VEDIT+VKMC11
	.SBTTL		KDPINI - initialize a KMC11 and its DUPs


; R4 = pointer to line control block for the KMC11/DUP11 to be initialized


KDPINI:	TRACE	TRCDQF,R4	;trace KDP initialization
	MOV	#KDPRI,LVECRA(R4) ;set receiver interrupt process address
	MOV	#KDPXI,LVECXA(R4) ;set transmitter interrupt process address
	MOV	#1,LB.TRL(R4)	;set number of trailing pads for data message
	MOV	LB.SLA(R4),R3	;get dup11 line hardware address

	MOV	#UP.INI,UP.XSR(R3) ;initialize the dup11
1$:	BIT	#UP.INI,UP.XSR(R3) ;has it finished initialization?
	BNE	1$		;no, wait for it.
	MOV	#UP.CCI!UP.DMD!EBCSYN,UP.PAR(R3) ;set up parms for bisync
	MOV	#UP.HDX,UP.XSR(R3) ;blind receiver during transmissions
	MOV	#UP.SSY,UP.RSR(R3) ;strip leading sync from messages
				;find this lines's kmc
	MOV	#LINTYP,R0	;scan LINTYP for 1st kdp driver
5$:	CMP	#KDPDRV,(R0)+
	BNE	5$		;we won't be here unless there is one
	SUB	#LINTYP+2,R0	;gen 1st kdp line number
	ASR	R0
	SUB	LB.LNU(R4),R0	;calc this line relative to 1st kmc line
	NEG	R0
	SAVE	R0		;now find which kmc this line belongs to
	ASR	R0
	ASR	R0		;relative line number/4
	MOV	R0,R2		;save the kmc number
	MOV	#MDTBL-MDTLN,R1	;1st kmc
6$:	ADD	#MDTLN,R1	;next kmc
	DEC	R0
	BGE	6$
	RESTOR	R0		;R1/kmc control block for this line
	BIC	#^C3,R0		;R0/this line number relative to this kmc
	ASL	R0
	ADD	R1,R0
	MOV	R4,MDLCB(R0)	;point this kmc11 to this line
	MOV	R1,LB.MD(R4)	;this lines's kmc11
	MOV	R2,R0		;get the kmc number back
	ASL	R0		; make a word offset

; here to initialize the kmc11 which owns this dup11.
;  if the kmc11 has already been initialized (by starting one
;  of its other lines first, for example) then we need do nothing.
;  otherwise we store the kmc11 control block address into sel 2
;  to signal it to start processing and wait for its "run" bit.
;
; R1 = address of kmc11 control block
; R0 = kmc11 number * 2
; R4 = lcb pointer

13$:	BIT	#MDFER,MDFGE(R1) ;is pdp-11 already running?
	BNE	15$		;yes, don't init again.
	MOV	MDVEC(R0),R2	;no, point to kmc11 vector
	BNE	14$		;there is such a kmc11
	STOPCD	KMM		;kmc11 is missing

14$:	MOV	#MDAINT,(R2)+	;store pointer to int. handler
	MOV	#MD.LVL*40,(R2)+ ;store priority level
	MOV	#MDBINT,(R2)+	;store other int. handler pointer
	MOV	#MD.LVL*40,(R2)+ ;store priority level
	MOV	#MDFER,MDFGE(R1) ;mark pdp-11 running
	MOV	MDCSR(R0),R3	;point to kmc11 csr
	MOV	R1,2(R3)	;point microcode to kmc11 table
15$:

; now wait for the kmc11 to start.  we wait up to three seconds.

	MOV	R4,-(SP)	;save r4
	MOV	#JIFSEC*3,-(SP)	;number of jiffies to wait

; here begins the loop that waits for the kmc11 to begin running.

16$:	MOV	2(SP),R4	;point r4 to lcb
	MOV	LB.MD(R4),R3	;point r3 to kmc11 control block
	BIT	#MDFKR,MDFGK(R3) ;is the kmc11 running now?
	BNE	17$		;yes.
	DSCHED	,#1		;no, wait one jiffy

	DEC	(SP)		;waited long enough?
	BNE	16$		;no, check again.
	STOPCD	KNR		;kmc11 not running (waited 3 sec)

; here when the kmc11 has set its "run" bit.

17$:	MOV	(SP)+,R4	;discard depleted counter
	MOV	(SP)+,R4	;get back lcb pointer

; exercise the kmc11 to be sure it is working

	MOV	#21,R0		;count of times to exercise
18$:	CALL	MDACTV		;pull its chain
	SOB	R0,18$		;do this for a while
	CALL	KDPKIL		;now kill io to complete database initialization
	RETURN
	.SBTTL		KDPRED - start receiver

; R4 = pointer to line control block
;
; returns C bit set if error, clear if ok.
;  (only error is chunks depleted)

KDPRED:	TRACE	TRCDQF,<(SP),R4,JIFCLK>	;trace read entry
	MOV	LB.SLA(R4),R3	;get hardware address
	CALL	GTLCHK
	BCS	15$		;no more chunks
	CLR	LB.CRD(R4)	;clear count of characters read
	MOV	R4,R2		;point r2 to lcb
	ADD	#LB.SO1,R2	;build pointer to first silo
	CALL	MDSLOI		;initialize the silo

; here when everything is ready.
;  unless we are waiting to start a write,
;  we must start the receiver now, since some dup11's
;  don't interrupt on carrier, and if we wait up to
;  16 milliseconds before starting the receiver after
;  getting carrier, we will frequently lose the first
;  character of the message.

	PIOFF			;disable cpu interrupts
	BIS	#UP.DIE,UP.RSR(R3) ;enable modem interrupts
	BIS	#LS.RGO,(R4)	;waiting to start receiver
	BIT	#LS.XGO,(R4)	;is a write pending?
	BNE	14$		;yes, wait for it to start.
	BIC	#LS.RGO!LS.CAR,(R4) ;no, no read pending.
	BIC	#UP.CAR,LB.DIP(R4) ;process carrier next time we see it
	BIS	#LS.RRN,(R4)	;start the read now.
	CALL	MDACTV		;activate the kmc11
	BIS	#UP.REN,UP.RSR(R3) ;enable  the dup11 for reading
14$:	PION			;restore cpu priority
	CLC			;flag no error
	RETURN


; here if we cannot get a chunk to read into.  this is a fatal read error.

15$:	INC	LB.CHD(R4)	;count times chunks depleted
	RETURN			;give error return.
	.SBTTL		KDPWRT - start transmitting a data message from LB.MSG

; R4 = pointer to line control block
;
; returns with C bit set if error, clear if not.

KDPWRT:	TRACE	TRCDQF!TRCLIN,<(SP),R4,(R4),LB.MSG(R4),JIFCLK> ;trace entry to "dq write data"
	BIT	#LS.ACT,(R4)	;is dup11 still active?
	BEQ	11$		;no.
	CALL	KDPKIL		;yes - kleen it up
11$:	BIC	#LS.CTL,(R4)	;this is a data, not a control message
	MOV	LB.SLA(R4),R3	;get dup11 hardware address

	MOV	LB.MSG(R4),R0	;get the message
	CALL	FMSGI		;maske sure it is inited for output
				;LB.MSG points to message header which is already
				; initialized for output, namely:
				;	MSGPTR/ptr to 1st full byte
				;	MSGLCH/ptr to 1st chunk(=message header)
				;	CHLEN of 1st chunk/byte count of 1st chunk
;**************************************************************
;this crockery is installed because the KMC microcode knows all about how
;messages ought to be constructed

	TST	CHLEN(R0)	;check if we have faked up old msg format previously
	BEQ	30$		;yes(or else the header is empty anyhow)
	SAVE	<R0,R1,R2>

15$:	CALL	GTLCHK		;need a place for the data in the header - privileged call
	BCC	17$		;suc'd ?
	SAVE	<LB.ALR(R4),TCTIM(R5)> ;since this a kluge anyhow - protect against alarms
	CLR	LB.ALR(R4)
	CLR	TCTIM(R5)
	DSCHED	#EBINTR,#10.	;dally awhile
	RESTOR	<TCTIM(R5),LB.ALR(R4)>
	BIT	#LS.ENB,(R4)	;check for death
	BEQ	16$		;line disabled
	BIT	#TCOAB,TCFG2(R5) ;check output abort also
	BEQ	15$		;all seems to be ok - try again

16$:	RESTOR	<R2,R1,R0>	;time to toss in the towel
	SEC
	RETURN

17$:	MOV	LB.MSG(R4),R1	;get the header
	MOV	MSGPTR(R1),-(SP);get ptr to beg of data
	MOV	(R1),R2		;link the new chunk in
	MOV	R0,(R1)+
	MOV	R2,(R0)+
	MOV	(R1),R2
	CLR	(R1)		;header is now empty - like so many we know
	MOV	(SP)+,R1	;R1 ->  beg of data
	MOV	R2,(R0)+	;R2/count
				;R0 -> beg of data in new chunk
	INC	R2
	ASR	R2		;xfer words

10$:	MOV	(R1)+,(R0)+
	SOB	R2,10$

20$:	RESTOR	<R2,R1,R0>

;*********************************************************

30$:	CALL	KDXSTR		;arrange to start transmitter
	RETURN
; subroutine to arrange to start the transmitter

KDXSTR:	BIS	#LS.XGO,(R4)	;xmitter waiting for cts
	PIOFF			;shut off the world
	BIS	#UP.RTS!UP.DIE,UP.RSR(R3) ;raise request to send
				; and enable for cts interrupt
	MOV	LB.CSD(R4),LB.DEW(R4) ;wait a while, then check for cts
				; note that modem interrupts are ignored
				; until time is up.
	BNE	11$		;TIME IS NOT YET UP.

; here if clear-to-send delay specified is zero.
;  if cts is up, start the transmitter.  note that
;  a zero clear-to-send delay should be used only with modems
;  that have a clear-to-send delay of less than 16 milliseconds,
;  since a larger delay can be timed without loss of efficiency
;  and too short a delay (less than about 8 milliseconds)
;  does not work with some ibm hosts.

	BIT	#UP.CTS,UP.RSR(R3) ;is cts up?
	BEQ	11$		;no, wait for modem interrupt
	CALL	KDPXST		;start the dup
	BR	12$		;enable interrupts, clear c and return.

; here if cts delay is non-zero, or if it is zero and cts is not up.

11$:	BIC	#UP.CTS,LB.DIP(R4) ;process cts raising next time we see it
12$:	TRACE	TRCDQF,<R4,(R4),UP.RSR(R3),UP.XSR(R3),LB.DEW(R4),JIFCLK>
	PION			;enable interrupts
	CLC			;clear 'c' flag
	RETURN

KDPXST:	BIS	#UP.SND,UP.XSR(R3) ;start dup transmitter
	MOV	#UP.XSM!EBCPAD,UP.XBF(R3) ;start transmission
	BIC	#LS.XGO,(R4)	;not waiting for cts
	BIS	#LS.XRN,(R4)	;now running

10$:	BIT	#UP.XDN,UP.XSR(R3) ;wait for transmitter done - should be soon
	BEQ	10$
	MOV	#EBCPAD,UP.XBF(R3) ;stuff another pad character - clears start bit
	CALL	MDACTV		;activate the kmc11
	RETURN
	.SBTTL		KDPCTL - transmit a control message

; R4 = pointer to line control block
; R0 = pointer to message to send
; R1 = length of message to send
;
; on return:
;
;	C set if error, clear if not.

KDPCTL:	TRACE	TRCDQF!TRCLIN,<(SP),R4,(R4),R0,R1,JIFCLK> ;trace entry to "dq write control"
	MOV	R0,LB.CMA(R4)	;remember control message address
	MOV	R1,LB.CMC(R4)	; and length
	BIT	#LS.ACT,(R4)	;is dup11 still active?
	BEQ	11$		;no.
	CALL	KDPKIL		;yes - kleen it up
11$:	BIS	#LS.CTL,(R4)	;we are working on a control message
	MOV	LB.SLA(R4),R3	;get dup11 hardware address
	CALL	KDXSTR		;arrange to start transmitter
	RETURN
	.SBTTL		KDPRI - KDP receiver interrupt processing

; this is the common interrupt code for dup11 receiver interrupts.
;  it is called with ps containing the line number.
;  modem interrupts also come here.

KDPRI:	RCVADJ
	SAVE	<R3,R1,R0>
	MOV	LB.SLA(R4),R3	;GET HARDWARE ADDRESS OF DUP11

; HERE TO CHECK FOR A DUP11 RECEIVER INTERRUPT BIT

RCVTST:	MOV	UP.RSR(R3),R0	;GET RECEIVER STATUS REGISTER
	MOV	R0,LB.RST(R4)	;STORE LAST RECEIVER STATUS
	TRACE	TRCDQF,<R4,R0,LB.ALR(R4),JIFCLK> ;TRACE INTERRUPT STATUS

; HERE TO CHECK FOR DATASET INTERRUPT FLAGS, SINCE THE KMC11 TAKES
;  CARE OF DATA SIGNALS.

11$:	BIT	#UP.DCA!UP.DCB,R0 ;ANY DATASET FLAGS?
	BEQ	12$		;NO, EXIT THE INTERRUPT.
	CALL	KDPDSF		;YES, PROCESS DATASET FLAGS

; HERE ON NORMAL EXIT.  WAKE THE BSC TASK IF REQUESTED.

12$:	CALL	CIEWAK		; wake BSC on exceptional event

; HERE TO RESTORE REGISTERS AND RETURN.

14$:	RESTOR	<R0,R1,R3,R4>
	RTI			;EXIT INTERRUPT

	.ENABL	LSB
CIEWAK:				; wake BSC if it is waiting for an event
				;R4/line block
	BIT	#LS.CIE,(R4)	;DOES BSC TASK WANT TO BE AWAKENED?
	BEQ	10$		;NO.

KDPBWK:	MOV	LB.TC1(R4),R0	;point to bsc task's tcb
	TRACE	TRCDQF,<(SP),R4,(R4),LB.ALR(R4),JIFCLK>
	SIGNAL	R0,EBINTR	;wake the bsc task for a line event
	BIC	#LS.CIE,(R4)	;flush any cie wake flag
	SETSKD	R0		;force scheduling pass
10$:	RETURN
	.DSABL	LSB
	.SBTTL		KDPERR - record a DUP11 error


KDPERR:	MOV	LB.SLA(R4),R3	;point to dup11 csr
	MOV	UP.RSR(R3),LB.SE2(R4) ;record status reg 1
	MOV	UP.XSR(R3),LB.SE3(R4) ; and status reg 2
	ATRACE	<(SP),R4,(R4),UP.RSR(R3),UP.RBF(R3),UP.XSR(R3),UP.XBF(R3),JIFCLK>
	BIT	#LS.ENB,(R4)	;is this all because line is diabled?
	BEQ	20$
	INC	LB.SE1(R4)	;count master error counter
	BIS	#LS.ERR,(R4)	;flag an error

.IF NE,DEBUG
	BIT	#TRCLER,TRCHLT
	BEQ	20$
	STOPCD	DBG
.ENDC	;.IF NE,DEBUG

20$:	RETURN
	.SBTTL		KDRSTP,KILKMC,KDXSTP - stop io functions

;   R4 points to lcb, R3 to csr.

	.ENABL	LSB
KDRSTP:	TRACE	TRCDQF,<(SP),R4,(R4),LB.DRP(R4),LB.ALR(R4),JIFCLK> ;trace dup11 receiver stop
	CLRB	LB.DRP+1(R4)	;clear carrier drop timer
	BIC	#UP.DIE,UP.RSR(R3) ;disable modem interrupts
	BIT	#LS.RGO!LS.RRN,(R4) ;is receiver running?
	BEQ	15$		;no - don't bother KMC
				;yes - kill the KMC

KILKMC:				;kill the KMC - dead
	BIC	#LS.KLC,(R4)	;make sure this is off so KMC won't ignore
	BIS	#LS.KIL,(R4)	;kill current operation
	CALL	MDACTV		;awaken the kmc11
	MOV	#MDMXTT,R0	;time to wait for kill
10$:	BIT	#LS.KLC,(R4)	;wait for the KMC to declare the kill complete
	BNE	15$
	SOB	R0,10$		;but only for awhile

15$:				;flush any event wakeup flag,dup bits
	CALL	KDPBWK		;wake the bsc
20$:	CLC			;indicate all is hunkydory...if anyone is interrested
	RETURN

; subroutine to stop the transmitter

; R4 points to the lcb
; R3 POINTS TO THE DUP11 CSR

KDXSTP:	TRACE	TRCDQF,<(SP),R4,(R4),LB.ALR(R4),JIFCLK> ;trace dup11 transmitter stop
	BIC	#UP.RTS,UP.RSR(R3) ;drop request to send
	CLR	LB.DEW(R4)	;not waiting to enable modem
	BIT	#LS.XGO!LS.XRN,(R4) ;transmitter running?
	BEQ	15$		;NO - don't bother KMC
	BR	KILKMC		;yes - mung the KMC
	.DSABL	LSB
	.SBTTL		MDAINT - KMC11 silo interrupt

; here on a silo interrupt from the kmc11.
;  drain any non-empty silos which have their "interrupt on
;  next character" flag clear, which means that the kmc11
;  might have interrupted because of them.  the kmc11 will
;  give full and warning interrupts regardless of the
;  state of this flag.

MDAINT:	INTSKD
	SAVE	<R2,R3,R4,R5>

; here to check all lcb's for a non-empty silo

	CLR	R5		;line number * 2

; here to check the next lcb

11$:	MOV	LINTBL(R5),R4	;point to lcb
	BEQ	13$		;no lcb for this line
	BIT	#LS.XGO!LS.XRN!LS.KIL!LS.KLC,(R4)
				;is this line writing or killed?
	BNE	13$		;yes, ignore its silo
	BIT	#LS.RRN,(R4)	;no, is line doing input?
	BEQ	13$		;no, ignore its silo.
	MOV	LB.SLO(R4),R3	;point to silo
	BEQ	13$		;not a KDP line

	TRACE	TRCDQF,<R4,R3,LB.CCD(R4),MDSLPT(R3),LB.ALR(R4),JIFCLK> ;kmc11 input interrupt for this lcb
	BIT	#MDSLFE,MDSLFG(R3) ;will this silo int. on next char?
	BNE	12$		;yes, ignore it until it interrupts.
	MOV	MDSLPT(R3),R2	;no, get current address from silo
	SUB	LB.CCD(R4),R2	;subtract initial address
	CMP	R2,@LB.CCR(R4)	;has count changed?
	BEQ	12$		;no, ignore this empty silo.
	CALL	MDSLEM		;yes, empty the silo
	BIS	#LS.RSE,(R4)	;mark that the receive silo was emptied.
.IF NE,DEBUG
	BR	13$		;done with this lcb
.ENDC ;.if ne,debug (no code between finish and reject unless debug)

; here to reject this lcb (r3 is set up)

12$:
	TRACE	TRCDQF,<R3,(R3),JIFCLK> ;record silo pointer and its status bits

; here when we are done with this lcb

13$:	ADD	#2,R5		;increment line number * 2
	CMP	#<NLINES*2>,R5	;done all the lines?
	BNE	11$		;no, do the rest.

; here when all the silos have been emptied.

	RESTOR	<R5,R4,R3,R2>
	RTI			;exit the interrupt
	.SBTTL		KDPXI - DUP11 transmit interrupts: an error
;  PROCESSES ALL DATA FLAGS.

KDPXI:	XMTADJ
	STOPCD	UQX		;unknown dup11 transmit interrupt
	.SBTTL		MDBINT - interrupt for KMC11 function done and error

MDBINT:	INTSKD
	SAVE	<R0,R1,R2,R3,R4,R5>

; here to check all lcb's for completion or error.

	CLR	R5		;line number * 2

; here to check the next lcb

11$:	MOV	LINTBL(R5),R4	;point to lcb
	BEQ	9$		;no lcb, do nothing.
	MOV	LB.SLA(R4),R3	;point to dup11 csr
	BIT	#LS.CMP!LS.KLC,(R4) ;function complete?
	BNE	10$
9$:	JMP	18$

10$:	TRACE	TRCDQF,<R4,(R4),LB.ALR(R4),JIFCLK> ;record lcb status on interrupt
	BIT	#LS.KLC,(R4)	;completion of kill request?
	BEQ	12$		;no.  check for write and read.
	BIT	#LS.MDE,(R4)	;yes, have we had an error?
	BEQ	16$		;no.  awaken bsc task if req.
	BIC	#LS.MDE,(R4)	;yes, clear kmc11 error flag
	BIS	#LS.ERR,(R4)	;set summary error flag
	BR	16$		;awaken bsc task if req.
; here if not completion of a kill request

12$:	BIT	#LS.XGO!LS.XRN,(R4) ;completion of a write?
	BEQ	14$		;no.
	BIC	#UP.DIE,UP.RSR(R3) ;yes, disable modem interrupts
	BIC	#UP.RTS,UP.RSR(R3) ;drop rts
	CLR	LB.DEW(R4)	;not waiting for modem signals
	BIT	#LS.MDE,(R4)	;have we had an error?
	BEQ	13$		;no.
	INC	LB.SE5(R4)	;yes, "transmitter not fast enough"
	CALL	KDPERR		;record dup11 registers
	CALL	KDXSTP		;stop the transmitter
	CALL	KDRSTP		;stop the receiver
	BIC	#UP.XDL!UP.SND,UP.XSR(R3) ;flush transmitter and error explicitly
	BIC	#LS.ACT!LS.MDE,(R4) ;dont start a read following write
	CLR	LB.CCH(R4)	;there is no current chunk
13$:	BIC	#LS.XGO!LS.XRN!LS.CMP,(R4) ;clear completed write flags
	BIT	#LS.ACT,(R4)	;is dup11 still active?
	BEQ	16$		;no, leave modem ints disabled.
	BIS	#UP.DIE,UP.RSR(R3) ;yes, enable modem ints
	BR	16$		;awaken bsc task if requested.

; here if not completion of kill or write

14$:	BIT	#LS.RGO!LS.RRN,(R4) ;receiver running?
	BEQ	15$		;no.
	BIC	#LS.RGO!LS.RRN!LS.CMP!LS.MDE,(R4) ;yes, clear the flags
	BIC	#UP.DIE,UP.RSR(R3) ;clear modem interrupt enable
	CLR	LB.CCH(R4)	;there is no current chunk
	MOV	LB.SLO(R4),R0	;point to current silo
	BIT	#MDSLFO,MDSLFG(R0) ;has it overflowed?
	BEQ	20$		;no.
	INC	LB.SE7(R4)	;yes, count silo overflows.
20$:	INC	LB.SE4(R4)	;must be an error...
	CALL	KDPERR		; since that is only reason for stopping
	BR	16$		;awaken bsc task if requested

; here if not completion of kill, write or read.

15$:	ATRACE	<R4,(R4),UP.RSR(R3),UP.RBF(R3),UP.XSR(R3),UP.XBF(R3),JIFCLK>
	STOPCD	KCQ		;kmc11 complete on an idle lcb
; here when line has made a transition.

16$:	CALL	KDPBWK		;wake the bsc
17$:

; here when we are done with this lcb.

18$:
.IF NE,DEBUG
	TST	R4		;have we an lcb?
	BEQ	19$		;no, dont trace
	TRACE	TRCDQF,<R4,(R4),LB.ALR(R4),JIFCLK> ;yes, trace new state of lcb
19$:
.ENDC ;.IF NE,DEBUG
	ADD	#2,R5		;increment line number * 2
	CMP	R5,#NLINES*2	;done last line?
	BGE	90$		;no, do the rest.
	JMP	11$
90$:

; here when we have done a pass and all lines have no action.

	RESTOR	<R5,R4,R3,R2,R1,R0>
	RTI			;exit the interrupt.
	.SBTTL		KDPDSF - process data set flags

;  R0 = dataset flags
;  R3 = DUP11 csr

; on return, R3 is destroyed.

	.ENABL	LSB
KDPDSF:	INC	LB.DIC(R4)	;count dataset interrupts
	MOV	R0,LB.DIS(R4)	;record last modem interrupt status
	TRACE	TRCDQF,<(SP),R4,(R4),R0,LB.DEW(R4),LB.ALR(R4),JIFCLK>
	TST	LB.DEW(R4)	;waiting for dataset enable?
	BNE	13$		;yes, ignore modem signal.
	BIT	#LS.ACT,(R4)	;is the dup11 active?
	BEQ	13$		;no, ignore the modem signals
	MOV	R0,LB.DIP(R4)	;yes, record last modem signals processed
	BIT	#LS.XGO,(R4)	;waiting to start xmitter?
	BEQ	11$		;no.
	CALL	14$		;yes, check for clear-to-send.
11$:	BIT	#LS.XRN,(R4)	;running the transmitter?
	BEQ	12$		;no.
	CALL	18$		;yes, be sure cts still up
12$:	BIT	#LS.RRN,(R4)	;running receiver?
	BEQ	13$		;no.
	CALL	20$		;yes, worry about carrier failure.
13$:	RETURN
; three subroutines to process data set flags.  they are called
;  based on the current state of the dup11.

; R0 = modem status flags, from the dup11.
; R3 = pointer to dup11 csr

; HERE if we are waiting to start the transmitter

14$:	BIT	#UP.CTS,R0	;have we clear-to-send?
	BEQ	17$		;no, wait for it.
	BIT	#LS.RGO,(R4)	;waiting to start receiver?
	BEQ	15$		;no.
	BIC	#LS.RGO!LS.CAR,(R4) ;yes, waiting no longer.
	BIC	#UP.CAR,LB.DIP(R4) ;process carrier next time we see it
	BIS	#LS.RRN,(R4)	;start xmit, follow with rcv

; here if the write is not to be followed by a read

15$:	CALL	KDPXST		;start the dup
17$:	RETURN			;all done.

; here if we are running the transmitter

18$:	BIT	#UP.CTS,R0	;is clear-to-send still up?
	BNE	19$		;yes, all ok.
	INC	LB.SE6(R4)	;no, note clear-to-send failure
	CALL	KDPERR		;note error
	CALL	KDXSTP		;stop the transmitter
19$:	RETURN			;all done.

; here if the receiver is running.

20$:	BIT	#UP.CAR,R0	;IS CARRIER UP?
	BNE	24$		;yes
	BIT	#LS.CAR,(R4)	;no - was it up previously ?
	BEQ	22$		;no - worry not
	BIC	#LS.CAR,(R4)	;yes -  transition, flush the flag
	MOVB	#10.,LB.DRP+1(R4);set a timer to distinguish kmc/dataset race
				;from crufty carrier
	INCB	LB.DRP(R4)	;count these events
	BNE	23$
	DECB	LB.DRP(R4)	;hang at max count(255.)
23$:	TRACE	TRCLIN!TRCDQF,<R4,(R4),R0,LB.DRP(R4),LB.ALR(R4),JIFCLK>

KDPCFL:	RETURN

; HERE IF CARRIER IS UP

24$:	TSTB	LB.DRP+1(R4)	;check if carrier previously went off
	BEQ	21$		;no - all is kosher
				;yes - this is a hard error
.IF NE,DEBUG
	CALL	23$		;trace this
	BIT	#TRCLER,TRCHLT	;check if we should halt
	BEQ	25$
	STOPCD	DBG
25$:	CALL	KDRSTP		;definitely a hard error if carrier bobbles
	RETURN
.ENDC	;.IF NE,DEBUG

21$:
.IF NE,DEBUG
	BIT	#LS.CAR,(R4)	;check for 1st detect
	BNE	22$		;no
	TRACE	TRCLIN!TRCDQF,<R4,(R4),R0,LB.DRP(R4),LB.ALR(R4),JIFCLK>
KDPCDT:
.ENDC	;.IF NE,DEBUG

26$:	BIS	#LS.CAR,(R4)	;mark we have seen carrier
22$:	RETURN
	.DSABL	LSB
	.SBTTL		KDPINQ - see if the KMC11 has read any good characters lately

; all characters up to CHLEN have been processed.

; R5 points to the tcb
; R4 points to the current chunk.  

; on return:
;
;	C set: some characters were found in the silo.
;		if R4 undisturbed, they are in current chunk
;		  (the length field has been updated)
;		if R4 clear, get a new chunk to process.
;
;	C clear: the silo is empty.  it is now
;		set to restart the bsc task on the next character.

KDPINQ:	SAVE	<R0,R3,R2>
	MOV	R4,R0		;point r0 to current chunk
	MOV	TCLCB(R5),R4	;point to lcb
11$:	PIOFF
	BIT	#LS.RSE,(R4)	;has the receive silo been emptied since last call?
	BNE	13$		;yes, be sure processing is done.
	CMP	R0,LB.CCH(R4)	;no, is this still the current chunk?
	BNE	12$		;no.  discard it and get another.
	MOV	LB.SLO(R4),R3	;no, point r3 to silo
	MOV	MDSLPT(R3),R2	;get current address from silo
	SUB	LB.CCD(R4),R2	;subtract initial address
	CMP	R2,@LB.CCR(R4)	;has count changed?
	BNE	14$		;yes, process characters
	BR	16$		;no, tell caller to wait

; here to abandon this chunk because it is no longer being filled.

12$:	PION			;enable interrupts
	TRACE	TRCDQF,<R4,R0,JIFCLK>	;trace decision to abandon old chunk
	MOV	R1,-(SP)	;save r1 
	CALL	FRECHK
	MOV	(SP)+,R1	;restore r1
	BR	15$		;tell caller to recycle

; here to tell caller to recycle because the silo has been
;  drained since the last time we came through here.  if we
;  do not do this, the silo might have been drained between
;  the caller's last test and our tests, so make caller
;  do his tests one more time.

13$:	BIC	#LS.RSE,(R4)	;clear interrupt flag
	PION			;restore interrupts
	BR	15$		;tell caller to recycle

; here to empty the silo and tell caller to process chars

14$:	CALL	MDSLEM		;empty the silo
	PION			;enable interrupts

; here to return, telling caller not to wait.

15$:	MOV	R0,R4		;restore r4
	RESTOR	<R2,R3,R0>
	SEC			;flag more processing required
	RETURN

; here if the silo is still empty.  arrange to awaken this task
;  when the next character is stored.

16$:	BIS	#LS.CIE,(R4)	;set wakeup flag
	BIS	#MDSLFE,MDSLFG(R3) ;make this silo interrupt on next character

; come here to wait the task.  from above, we will awaken
;  on the next character.  if the line is running fast,
;  we bypass the above code and just require the bsc task
;  to wait until either the chunk fills or its timer
;  expires.

17$:	TRACE	TRCDQF,<R4,JIFCLK> ;trace decision to wait the task.
	PION			;restore interrupt level
	MOV	R0,R4		;restore r4
	RESTOR	<R2,R3,R0>
	CLC			;flag no characters
	RETURN
	.SBTTL		MDSLEM - empty the KMC11'S silo

; this subroutine is called from input interrupt
;  and if the bsc task needs more characters to
;  avoid going idle.

; interrupts are masked off by the caller of this subroutine.

; R4 = lcb pointer
; R3 = silo pointer (from LB.SLO(R4)).
;  destroys r2

MDSLEM:	TRACE	TRCDQF,<R4,(R4),MDSLFG(R3),LB.ALR(R4),JIFCLK> ;trace silo flag bits
	SAVE	<R0,R1>
	BIT	#MDSLFW,MDSLFG(R3) ;reached warning level yet?
	BEQ	MDSLE1		;no, dont switch silos
	TRACE	TRCDQF,<R4,MDSLCC(R3)> ;yes, trace departure of chunk
	MOV	R4,R2		;build pointer to other silo
	ADD	#LB.SO1,R2
	CMP	R2,R3		;is this silo 1?
	BNE	13$		;no.
	ADD	#LB.SO2-LB.SO1,R2 ;yes, make r2 point to silo 2
13$:

; get a chunk to hold new silo data

	CALL	GTLCHK		;get a chunk
	BCS	14$		;yes, we are out of chunks
	TRACE	TRCDQF,<R4,R0,JIFCLK> ;trace chunk manipulation
	CALL	MDSLOI		;initialize the new silo
	BR	MDSLE1		;process data in old silo

; here if we run out of chunks.  this is not a fatal error if
;  the bsc task sees the end of the bisync message before
;  needing the data that was lost.

14$:	INC	LB.CHD(R4)	;record exhaustion of chunks
	MOV	LB.SLA(R4),R3	;point to dup11 csr
	CALL	KDRSTP		;stop the receiver
	BR	MDSLEX		;exit the subroutine
; here to give the characters in the silo to the task.
;  this is done by updating the length field of the current chunk

; R3 = pointer to silo.  LB.SLO(R4) may point to the other silo.

MDSLE1:	MOV	MDSLCC(R3),R2	;point to this silo's chunk
	MOV	MDSLPT(R3),R0	;compute new fill level
	SUB	R2,R0		;copy pointer
	SUB	#CHDAT,R0	;make pointer to data area
	MOV	R0,CHLEN(R2)	;store new length
	TRACE	TRCDQF,<R4,R0,LB.ALR(R4),JIFCLK> ;trace new length
	BIT	#MDSLFW,MDSLFG(R3) ;are we at warning point?
	BEQ	12$		;no, dont record statistic.
	MOV	MDSLPT(R3),R0	;yes, get current fill point (note that this
				; may still be the current silo, but  this is unlikely)
	SUB	MDSLPW(R3),R0	;subtract warning point
	CMP	R0,LB.MDU(R4)	;greater than max needed before?
	BLO	12$		;no.
	MOV	R0,LB.MDU(R4)	;yes, store new max depth
12$:	CMP	R3,LB.SLO(R4)	;are we changing silos?
	BEQ	MDSLEX		;no.
	ADD	CHLEN(R2),LB.CRD(R4) ;yes, accumulate chars read
	CMP	LB.CRD(R4),#DQRLMT ;too many characters in message?
	BLE	MDSLEX		;no, look for another character.
	MOV	LB.SLA(R4),R3	;yes, point to dup11 csr
	CALL	KDRSTP		;stop the receiver

; here when the silo has been emptied, or on an error.

MDSLEX:	CALL	CIEWAK		; wake the BSC if he is waiting on an event
	RESTOR	<R1,R0>
	RETURN
	.SBTTL		MDSLOI -  intiialize a silo

; R0 = pointer to chunk to hold data
; R2 = pointer to silo.
; R4 = lcb pointer

MDSLOI:	MOV	R0,LB.CCH(R4)	;silo chunk is now current
	MOV	LB.TC1(R4),R1	;get tcb of receive chunk handler
	TRACE	TRCLIN,<R4,R0,R2,LB.ALR(R4),JIFCLK>
	CALL	QUECHK		;send him the chunk
	ADD	#CHLEN,R0	;point to length field
	MOV	R0,LB.CCR(R4)	;store for next input interrupt
	ADD	#CHDAT-CHLEN,R0	;point to data field
	MOV	R0,LB.CCD(R4)	;store for next input interrupt
	MOV	R0,MDSLPT(R2)	;store new initial data pointer
	ADD	#CHDATL,R0	;point to end of data area
	MOV	R0,MDSLPL(R2)	;store for overflow checking
	SUB	LB.MDS(R4),R0	;back up for latency
	MOV	R0,MDSLPW(R2)	;store as warning level
	MOV	LB.CCH(R4),MDSLCC(R2) ;copy pointer to current chunk
	CLR	MDSLFG(R2)	;clear silo flags
	MOV	R2,LB.SLO(R4)	;we have a new silo
	TRACE	TRCDQF,<R4,LB.CCH(R4),LB.ALR(R4),JIFCLK> ;trace new "current chunk"
	RETURN
	.SBTTL		KDPKIL -  kill any KMC11/DUP11 operations  

; R4 = lcb pointer
; R5 = pointer to bsc tcb

; destroys r0, r1 and r3

KDPKIL:	TRACE	TRCDQF,<(SP),R4,(R4),LB.ALR(R4),JIFCLK> ;trace entry to kdpkil
	MOV	LB.SLA(R4),R3	;point to csr

	CALL	KDXSTP		;flush the tranmitter if on
	CALL	KDRSTP		;flush the receiver if on


14$:	CLR	LB.CCH(R4)	;there is no current chunk
.IF NE,DEBUG
	MOV	#-1,LB.SLO(R4)	;the silo is gone
.ENDC ;.IF NE,DEBUG

; here when the line has been killed, or was not running at all.

15$:	PIOFF			;disable interrupts
	BIC	#UP.DIE,UP.RSR(R3) ;disable dataset interrupts
				; (avoid hardware race condition
				; in 11/34 by disabling ints)
	PION			;restore interrupts
16$:	CALL	DEQCHK		;any chunks in receiver queue?
	BCS	17$		;no.
	CALL	FRECHK		;flush it
	BR	16$		;be sure we've got them all.

; here when the kmc11/dup11 has been killed.

17$:	BIT	#LS.ACT!LS.CMP,(R4) ;was the kmc11 active?
	BEQ	19$		;no, this was all for nothing.
	BIT	#LS.KLC,(R4)	;yes, has the kill completed?
	BNE	18$		;yes.
	STOPCD	KKF		;no, kmc11 kill failed.

; here when the kill is complete.  clear all relevent lcb status bits.

18$:	BIC	#LS.ACT!LS.CMP!LS.KIL!LS.KLC,(R4)
				;the kmc11/dup11 is no longer active
19$:	TRACE	TRCDQF,R4	;trace completion of KDPKIL
	RETURN
	.SBTTL		MDCATV - poke the kmc11 so it will check its line blocks

MDACTV:	TRACE	TRCDQF,<R4,(R4),LB.ALR(R4),JIFCLK> ;trace entry to MDACTV
	SAVE	<R3,R0,R1,R2>
	MOV	LB.MD(R4),R3	;point to kmc11 table
	MOV	#MDMXTT,R2	;wait this long for kmc11 to respond
11$:	MOV	MDFGE(R3),R0	;get 11-flags
	MOV	MDFGK(R3),R1	; and kmc flags
	XOR	R1,R0		;xor 11-flags with kmc-flags
	BIT	#MDFEA,R0	;is old active flag still on?
	BEQ	12$		;no.
	SOB	R2,11$		;yes, wait for kmc11 to follow
	STOPCD	KNA		;kmc11 not following active flag

12$:	BIT	#MDFKR,MDFGK(R3) ;is kmc11 still running?
	BNE	14$		;yes.
	STOPCD	KNR		;no, kmc11 not running.

; here when the kmc11 has completed the last operation and is running.

14$:	MOV	#MDFEA,R0	;get bit to xor
	XOR	R0,MDFGE(R3)	;complement active flag
	RESTOR	<R2,R1,R0,R3>
	RETURN			;done.
	.SBTTL		KDPOFF,KDPON,KDPSTS - DTR,DSR functions

;  set and clear the modem signals dtr and dsr.

; subroutine to clear dtr

; R0 = lcb address

KDPOFF:	SAVE	R0
	MOV	LB.SLA(R0),R0	;point to csr
	MOV	UP.RSR(R0),-(SP) ;save current state
	BIC	#UP.DTR,UP.RSR(R0) ;clear dtr
	TRACE	TRCDQF,<4(SP),4(SP),UP.RSR(R0),JIFCLK>;trace caller,line,DUP status
	BIT	#UP.DTR,(SP)+	;was it on ?
	BEQ	10$		;no - go away
	DSCHED	,#3*JIFSEC	;yes - allow time for slow modems and phones to hangup
10$:	RESTOR	R0
	RETURN

; subroutine to set dtr

KDPON:	SAVE	R0
	MOV	LB.SLA(R0),R0	;point to csr
	BIS	#UP.DTR,UP.RSR(R0) ;set dtr
	TRACE	TRCDQF,<2(SP),2(SP),UP.RSR(R0),JIFCLK>;trace caller,line,dq status
	RESTOR	R0
	RETURN

; subroutine to return values of dtr and dsr.

; R0 = lcb address
; R1 bits 1 and 2 are 0.

; on return:
;
;	bit 1 of R1 = DTR
;	bit 2 of R1 = DSR
;	 R1 is otherwise undisturbed.

KDPSTS:	SAVE	R0
	MOV	LB.SLA(R0),R0	;point to csr
	BIT	#UP.DTR,UP.RSR(R0) ;is dtr set?
	BEQ	11$		;no.
	BIS	#B1,R1		;yes, set flag in r1
11$:	BIT	#UP.DSR,UP.RSR(R0) ;is dsr set?
	BEQ	12$		;no.
	BIS	#B2,R1		;yes, set flag in r1
12$:	TRACE	TRCDQF,<2(SP),2(SP),UP.RSR(R0),JIFCLK>;trace caller,line,DUP status
	RESTOR	R0
	RETURN
	.SBTTL		KDPTRP - stop code processor for the KDP driver

;  this subroutine is called to store the status of the dup11
;  in the lcb for post-mortum debugging.

;  R5 does not point to a tcb, but it must not be destroyed.

; R4 = lcb address

KDPTRP:	MOV	LB.SLA(R4),R3	;get hardware address
	MOV	UP.RSR(R3),LB.RG0(R4) ;store receiver status register
	MOV	UP.RBF(R3),LB.RG1(R4) ;store receiver buffer
	MOV	UP.XSR(R3),LB.RG2(R4) ;store transmit status register
	MOV	UP.XBF(R3),LB.RG3(R4) ;store transmit buffer
	RETURN

; subroutine to stop the kmc11s on a fatal error.

TRAPMD:	CLR	R1		;kmc11 number
	MOV	#MDTBL,R3	;pointer to kmc11 table
11$:	BIC	#MDFER,MDFGE(R3) ;clear "11-running"
	ADD	#MDTLN,R3	;point to next kmc11 table
	INC	R1		;increment kmc11 number
	CMP	R1,NKMC11	;done all kmc11s?
	BLT	11$		;no, do the rest.
	RETURN			;return to trap handler.
	.SBTTL		KDPTIK - check on the DUP11S once a jiffie

;  we must count the clear-to-send delay and
;  compensate for improper dup11 jumpers which
;  will prevent the carrier signal from causing
;  an interrupt.
;
;  R4/line block

KDPTIK:
11$:	BIT	#LF.DAC,LB.FGS(R4);check for shut down
	BNE	15$		;yes - forget it
	PIOFF			;disable interrupts
	TST	LB.DEW(R4)	;waiting to set modem enable?
	BEQ	12$		;no.
	DEC	LB.DEW(R4)	;yes, waited long enough?
	BNE	14$		;no, wait until next time
12$:	MOV	LB.SLA(R4),R3	;point to csr
	MOV	UP.RSR(R3),R0	;get modem status bits
	BIT	#UP.DIE,R0	;are modem interrupts enabled?
	BEQ	14$		;no, dont call subroutine.
	BIT	#UP.DCA!UP.DCB,R0 ;are dataset change flags set?
	BNE	13$		;yes, process any possible changes
	MOV	LB.DIP(R4),R2	;get last modem signals processed
	XOR	R0,R2		;check for any changes in signals
	BIT	#UP.CTS!UP.CAR,R2 ;any important modem signals changed?
	BEQ	14$		;no.
13$:	CALL	KDPDSF		;yes, process any modem signal changes
14$:	TSTB	LB.DRP+1(R4)	;check for carrier drop timer
	BEQ	10$		;no
	TRACE	TRCLIN!TRCDQF,<R4,(R4),R0,LB.DRP(R4),LB.ALR(R4),JIFCLK>
	DECB	LB.DRP+1(R4)	;yes - count it down
	BNE	10$
.IF NE,DEBUG
	BIT	#TRCLER,TRCHLT	;check if we should halt
	BEQ	69$
	STOPCD	DBG
69$:
.ENDC	;.IF NE,DEBUG
	CALL	KDRSTP		;timed out before BSC could call
				; KDRSTP(when it sees end of msg), this is
				; a hardware error

10$:	PION			;restore interrupt level
15$:

; now check this line's KMC11

	MOV	LB.MD(R4),R3	;get the kmc control block
	BEQ	18$		;amazing - not controlled by a kmc
	MOV	#4,R2		;find 1st line controlled by this kmc11
	MOV	R3,R0
	ADD	#MDLCB,R0
20$:	TST	(R0)+		;find 1st nonzero entry
	BNE	25$
	SOB	R2,20$
	STOPCD	DBG		;should't be here

25$:	CMP	R4,-(R0)	;check if this line is 1st one in kmc's block
	BNE	18$		;this way we dthe kmc check only once

16$:	BIT	#MDFER,MDFGE(R3) ;is pdp-11 running?
	BEQ	18$		;no, this kmc11 not in use.
	BIT	#MDFKR,MDFGK(R3) ;yes, is kmc11 running?
	BEQ	18$		;no, no timeing yet.

; here when we have a running kmc11.

	INC	MDTIC(R3)	;count towards one second
	CMP	#JIFSEC,MDTIC(R3) ;reached a second yet?
	BNE	18$		;no, wait until next time
	CLR	MDTIC(R3)	;yes, reinitialize tick counter
	INC	MDALE(R3)	;count pdp-11 alive counter
	BIT	#7,MDALE(R3)	;has it been eight seconds? [2(755)]
	BNE	18$		;no, check next kmc11
	MOV	MDALK(R3),R0	;yes, get kmc11 alive counter
	CMP	R0,MDALKS(R3)	;same as last time?
	BNE	17$		;no, kmc11 still alive.
	STOPCD	KKS		;kmc11 keep-alive stopped.

17$:	MOV	R0,MDALKS(R3)	;save old value of keep-alive

; here when we are done with this kmc11

18$:

; here when we are done with this "tick".

	RETURN

.ENDC	;.IF NE,FT.KDP
	.SBTTL	  DUP11 - DUP11 synchronous line driver

.IF NE,FT.DUP


; this section contains subroutines to drive the dup11.
;  these are:

DUPENT:	DUPINI			;initialize the dup11
	DUPRED			;read a message
	DUPWRT			;output a data message
	DUPCTL			;output a control message
	DUPKIL			;cease any dup11 operations
	DUPINQ			;check if any more characters available in current msg
	DUPON			;set DTR on
	DUPOFF			;set DTR off
	DUPSTS			;return DTR,DSR status
	DUPTIK			;once per tick routine
	DUPTRP			;stop code processor
;		REVISION HISTORY


; 4(001) BS	ADDED EDIT NUMBERS
; 4(002)	Remove chunk ownership trace in DUP receiver interrupt code
; 4(003) 07-Apr-81 RLS	Changes to reflect use of message header for data(DQWRIT)
; 4(004) 17-Apr-81 RLS
;		Transform static flow control to static/line control
; 4(005) 28-DEC-81 RLS
;		Rewrite for performance.
; 4(006) 27-AUG-82 RLS GCO 4.2.1504
;		in DUPRED, continuous carrier case, fix wrong sense of branch
;		on transmitter active test.

VDUP11=006

VEDIT=VEDIT+VDUP11
	.SBTTL		DUPINI - initialization

; R4 = pointer to line control block for dup11 to be initialized.

DUPINI:	TRACE	TRCDQF,R4	;trace dup11 initialization
	MOV	#DUPRI,LVECRA(R4) ;set receiver interrupt process address
	MOV	#DUPXI,LVECXA(R4) ;set transmitter interrupt process address
	MOV	#1,LB.TRL(R4)	;set number of trailing pads for data message
	MOV	LB.SLA(R4),R3	;get dup11 line hardware address
	MOV	#UP.INI,UP.XSR(R3) ;initialize the dup11
11$:	BIT	#UP.INI,UP.XSR(R3) ;has it finished initialization?
	BNE	11$		;no, wait for it.
	MOV	#UP.CCI!UP.DMD!EBCSYN,UP.PAR(R3) ;set up parms for bisync
	MOV	#UP.HDX,UP.XSR(R3) ;blind receiver during transmissions
	MOV	#UP.SSY,UP.RSR(R3) ;strip leading sync from messages
	CALL	DUPKIL		;kill io to complete database initialization
	RETURN
	.SBTTL		DUPRED - start receiver

; R4 = pointer to line control block
;
; returns C bit set if error, clear if ok.
;  (only error is chunks depleted)

DUPRED:	TRACE	TRCDQF,<(SP),R4,JIFCLK> ;trace entry to DUPRED
	MOV	LB.SLA(R4),R3	;get hardware address
	CALL	RCVCHK		;get enough chunks to receive a message
	BCS	14$		;no more chunks

; here when everything is ready.
;  we must start the receiver now, since some dup11's
;  don't interrupt on carrier, and if we wait up to
;  16 milliseconds before starting the receiver after
;  getting carrier, we will frequently lose the first
;  character of the message.

	PIOFF			;disable cpu interrupts

	CALL	DNXRCK		;setup to receive
	TRACE	TRCDQF,<R5,(R4),UP.RSR(R3),LB.CCH(R4),LB.RBF(R4),TCCHKQ(R5),TCCHK2(R5)>
	BIS	#LS.RGO,(R4)	;waiting to start receiver
	BIT	#UP.CAR,UP.RSR(R3) ;check for continous carrier
	BNE	10$		;yes - we can't expect a carrier detect interrupt
				;switched carrier
	BIC	#LS.CAR,(R4)	;we have not yet seen carrier
	BIC	#UP.CAR,LB.DIP(R4) ;process carrier next time we see it
	BR	12$

10$:	BIS	#LS.CAR,(R4)	;carrier has been seen
	BIS	#UP.CAR,LB.DIP(R4);so we don't need to process it
	BIT	#LS.XGO!LS.XRN,(R4) ;carrier present - check pending transmit
	BNE	12$		;yes - wait for transmision end
	CALL	DPRSTR		;start the sucker

12$:	BIS	#UP.DIE,UP.RSR(R3) ;enable modem interrupts
	PION			;restore cpu priority
	CLC			;flag no error
14$:	RETURN

DPRSTR:	BIS	#UP.DIE!UP.RIE!UP.REN,UP.RSR(R3) ;start the dup11 now
	BIS	#LS.RRN,(R4)	;receiver is now running
	BIC	#LS.RGO,(R4)	;not waiting to start it anymore
	RETURN
	.SBTTL		DUPWRT - start transmitting a data message from LB.MSG

; R4 = pointer to line control block
;
; returns with C bit set if error, clear if not.

DUPWRT:	TRACE	TRCDQF!TRCLIN,<(SP),R4,(R4),LB.MSG(R4),JIFCLK> ;trace entry to DUPWRT

	BIT	#LS.ACT,(R4)	; is dup11 still active?
	BEQ	11$		;no.
	CALL	DUPKIL		;yes - kleen up
11$:	BIC	#LS.CTL,(R4)	;this is a data, not a control message
	MOV	LB.SLA(R4),R3	;get dup11 hardware address

	MOV	LB.MSG(R4),R0	;get the message
	CALL	FMSGI		;maske sure it is inited for output
				;LB.MSG/ptr to message already inited for output
				;	MSGPTR/ptr to 1st full byte
				;	MSGLCH/ptr to 1st chunk(=message header)
				;	CHLEN of 1st chunk/bytes in 1st chunk
	MOV	MSGPTR(R0),LB.CXD(R4) ;set data ptr
	MOV	(R0)+,LB.CCX(R4) ;save ptr to next chunk in message
	MOV	(R0),LB.CXR(R4) ;set byte count

	CALL	DPXSTR		;arrange to start transmitter
	RETURN
; subroutine to arrange to start the transmitter

DPXSTR:	BIS	#LS.XGO,(R4)	;xmitter waiting for cts
	MFPS	-(SP)		;save interrupt level
	MTPS	#BR7		;disable interrupts
	BIS	#UP.RTS!UP.DIE,UP.RSR(R3) ;raise request to send
				; and enable for cts interrupt
	MOV	LB.CSD(R4),LB.DEW(R4) ;wait a while, then check for cts
				; note that modem interrupts are ignored
				; until time is up.
	BNE	11$		;time is not yet up.

; here if clear-to-send delay specified is zero.
;  if cts is up, start the transmitter.  note that
;  a zero clear-to-send delay should be used only with modems
;  that have a clear-to-send delay of less than 16 milliseconds,
;  since a larger delay can be timed without loss of efficiency
;  and too short a delay (less than about 8 milliseconds)
;  does not work with some ibm hosts.

	BIT	#UP.CTS,UP.RSR(R3) ;is cts up?
	BEQ	11$		;no, wait for modem interrupt
	CALL	DUPXST		;start dup
	BR	12$		;enable interrupts, clear c and return.

; here if cts delay is non-zero, or if it is zero and cts is not up.

11$:	BIC	#UP.CTS,LB.DIP(R4) ;process cts raising next time we see it
12$:	MTPS	(SP)+		;enable interrupts
	CLC
	RETURN

DUPXST:	BIS	#UP.SND,UP.XSR(R3) ;start dup transmitter
	MOV	#UP.XSM!EBCPAD,UP.XBF(R3) ;start transmission
	BIC	#LS.XGO,(R4)	;not waiting for cts
	BIS	#LS.XRN,(R4)	;now running
	CLR	R0
	BISB	@LB.CXD(R4),R0	;get 1ST data char
	INC	LB.CXD(R4)	;advance data byte ptr
				;but don't count it
10$:	BIT	#UP.XDN,UP.XSR(R3) ;wait for transmitter done - should be soon
	BEQ	10$
	MOV	R0,UP.XBF(R3)	;stuff 1st character - clears start bit
	BIS	#UP.XIE,UP.XSR(R3) ;now turn on transmitter interrupts
	RETURN
	.SBTTL		DUPCTL - start transmitting a control message (e.g., ACK)

; R4 = pointer to line control block
; R0 = pointer to message to send
; R1 = length of message to send
;
; on return:
;
;	C set if error, clear if not.

DUPCTL:	TRACE	TRCDQF!TRCLIN,<(SP),R4,(R4),R0,R1,JIFCLK> ;trace entry to DUPCTL
	MOV	R0,LB.CMA(R4)	;remember control message address
	MOV	R1,LB.CMC(R4)	; and length

	BIT	#LS.ACT,(R4)	;is dup11 still active?
	BEQ	11$		;no.
	CALL	DUPKIL		;yes - kleen up
11$:	BIS	#LS.CTL,(R4)	; we are working on a control message
	MOV	LB.SLA(R4),R3	;get dup11 hardware address

	MOV	LB.CMA(R4),LB.CXD(R4) ;set data ptr
	MOV	LB.CMC(R4),LB.CXR(R4) ;set byte count
	CALL	DPXSTR		; arrange to start transmitter
	RETURN
	.SBTTL		DUPRI - receiver interrupt processor

; this is the common interrupt code for dup11 receiver interrupts.
;  it is called with ps containing the line number.
;  modem interrupts also come here.

DUPRI:	RCVADJ			;dup11 receiver interrupt processor
				;R4 -> line block for interrupting dup11
				;old R4 on stack
	SAVE	<R3,R1>
	MOV	LB.SLA(R4),R3	;get device address
	MOV	UP.RSR(R3),LB.RST(R4) ;get receiver status register
	BMI	10$		;summary for various conditions:
				; cts transition
				; ring
				; with optional jumper W5 installed:
				; carrier detect transition
				; dsr transition
				; secondary receive data line transition
	MOV	UP.RBF(R3),R1	;no, get character
	BMI	12$		;error.
	MOVB	R1,@LB.CCD(R4)	;store byte in current chunk
	INC	LB.CCD(R4)	;advance pointer
	DEC	LB.CCR(R4)	;count the character
	BGT	20$		;chunk not full yet

; we have filled the current chunk.  get another one so we can fill it.

	SAVE	R0
	MOV	LB.CCH(R4),R0	;set the byte count
	MOV	#CHDATL,CHLEN(R0)
	CALL	DNXRCK		;advance to next chunk
	BCC	17$
	BR	14$		;msg is already maximum length

10$:	SAVE	R0		;error

11$:	MOV	LB.RST(R4),R0	;get the status flags
	CALL	DUPDSF		;process dataset flags
	BR	17$

; here on error in receiver

12$:	SAVE	R0
	INC	LB.SE4(R4)	;receiver overrun
	CALL	DUPERR		;record an error
				;terminate the interrupt.

; come here if we have too long a message.  if this is actually
;  an error and not just reading pads it will be caught by
;  the bsc task, as above.

14$:	CALL	DPRSTP		;kill the receiver
	RESTOR	R0
	BR	35$		;force scheduler pass

; here to restore registers and return.

17$:	RESTOR	R0
	BR	25$

20$:	CMPB	#EBCPAD,R1	;check for possible end of message
	BEQ	30$		;maybe

25$:	RESTOR	<R1,R3,R4>
	RTI			;exit interrupt

30$:	MOV	#CHDATL,R1	;update current chunk byte count
	SUB	LB.CCR(R4),R1	; minus bytes already counted
	MOV	R1,@LB.CHL(R4)	;stuff in chunk byte count word
	SIGNAL	LB.TC1(R4),EBINTR,NOTRACE
	SETSKD	LB.TC1(R4)

35$:	RESTOR	<R1,R3,R4>
	JMP	SKDCHK		;force scheduler pass

DUPFUL:	BIT	#LS.RRN,(R4)	;update curretn chunk's byte count
	BEQ	10$		;can't safely update count if receiver not running
	SAVE	R0
	MOV	#CHDATL,R0	;max possible
	SUB	LB.CCR(R4),R0	; minus bytes already counted
	MOV	R0,@LB.CHL(R4)	;stuff in chunk byte count word
	RESTOR	R0
10$:	RETURN

DNXRCK:	MOV	LB.RBF(R4),R0	;advance to next chunk to receive more message
	BEQ	10$		;no more - msg a trifle long
	MOV	R0,LB.CCH(R4)	;set as current buffer
	MOV	(R0)+,LB.RBF(R4) ;delink it
	MOV	R0,LB.CHL(R4)	;set ptr to chunk byte count
	CLR	(R0)+		;clear chunk byte count
	MOV	R0,LB.CCD(R4)	;stash the data ptr
	MOV	#CHDATL,LB.CCR(R4) ;init the receive count
	MOV	LB.CCH(R4),R0	;get the current chunk
	MOV	LB.TC1(R4),R1
	JMP	QUEFST		;and queue it to the bsc task
10$:	SEC
	RETURN
; subroutine to record a dup11 error.

DUPERR:	MOV	UP.RSR(R3),LB.SE2(R4) ;record status reg 1
	MOV	UP.XSR(R3),LB.SE3(R4) ; and status reg 2
	ATRACE	<(SP),R4,(R4),LB.SE2(R4),LB.SE3(R4),JIFCLK>
	BIT 	#LS.ENB,(R4)	;is this because line is disabled
	BEQ	20$
	INC	LB.SE1(R4)	;count master error counter
	BIS	#LS.ERR,(R4)	;flag an error

.IF NE,DEBUG
	BIT	#TRCLER,TRCHLT
	BEQ	20$
	STOPCD	BUG
.ENDC	;.IF NE,DEBUG

20$:	RETURN
; subroutine to stop the receiver
;
;   R4 points to lcb, R3 to csr.

DPRSTP:				;stop the receiver
	CALL	DUPFUL		;final update of chunk byte count
	BIC	#UP.REN!UP.RIE!UP.DIE,UP.RSR(R3) ;stop the receiver
	BIC	#LS.RGO!LS.RRN,(R4) ;not running
	CALL	RCVCLR		;flush unused receive buffers
	CALL	DUPBWK		;wake the bsc task
	RETURN

DUPBWK:	TRACE	TRCDQF,<(SP),R4,(R4),JIFCLK> ;wake the bsc task
	BIC	#LS.CIE,(R4)	;yes, clear flag
	SAVE	R0
	MOV	LB.TC1(R4),R0	;point to bsc task's tcb
	SIGNAL	R0,EBINTR
	SETSKD	R0		;force scheduling pass
	RESTOR	R0
	RETURN
	.SBTTL		DUPXI - transmitter interrupt processor

DUPXI:	XMTADJ			;dup11 transmitter interrupt processor
				;R4 -> line block for interrupting dup
				;old R4 on stack
	SAVE	<R1,R3>
	MOV	LB.SLA(R4),R3	;get hardware address

	MOV	UP.XSR(R3),R1	;get transmitter status bits
	BMI	17$		;branch if any errors.

; here when we have a real transmit done interrupt.

11$:	DEC	LB.CXR(R4)	;count the character just completed
	BLE	12$		;check if chunk empty

13$:	MOVB	@LB.CXD(R4),UP.XBF(R3) ;do next character
	INC	LB.CXD(R4)	;advance data ptr

19$:	RESTOR	<R3,R1,R4>
	RTI

12$:	BLT	18$		;this interrupt for last character completing
	BIT	#LS.CTL,(R4)	;are we sending a control message?
	BNE	15$		;yes - end of message by definition
	MOV	LB.CCX(R4),R1	;get next chunk to go
	BEQ	15$		;end of message
	MOV	(R1)+,LB.CCX(R4) ;delink this chunk
	MOV	(R1)+,LB.CXR(R4) ;set number characters in this chunk
	MOV	R1,LB.CXD(R4)	;set data ptr
	BR	13$		;output next character from new chunk

;here on end of message

15$:	BIS	#UP.XEM,UP.XBF(R3) ;tell the dup11 end of message
16$:	BIC	#UP.SND,UP.XSR(R3) ;next interrupt on end of message state
	BR	19$

; here on transmit error interrupt
;  (the only error detected here is transmitter data late.)

17$:	MOV	R1,LB.XST(R4)	;record last transmit status
	MOV	R1,LB.ERS(R4)	;save error status
	TRACE	TRCDQF!TRCLIN,<R4,R1,JIFCLK> ;trace error interrupt
	BIS	#LS.ERR,(R4)	;flag an error
	INC	LB.SE5(R4)	;count transmitter not fast enough
	CALL	DUPERR		;record the error
	BIC	#UP.SND,UP.XSR(R3) ;kill the transmitter

18$:	CALL	DPXST1		;wind up
	BIT	#LS.RGO,(R4)	;check for pending read
	BEQ	25$		;no - go away
	BIT	#UP.CAR,UP.RSR(R3) ;yes - check for continuous carrier
	BEQ	25$		;no - wait for carrier detect interrupt
	CALL	DPRSTR		;yes - start the receiver now

25$:	RESTOR	<R3,R1,R4>
	JMP	SKDCHK		;and force scheduler pass
; subroutine to stop the transmitter
;
; R4 points to the lcb
; R3 points to the csr

DPXSTP:	TRACE	TRCDQF,<(SP),R4,JIFCLK> ;trace dup11 transmitter stop
	BIC	#UP.SND,UP.XSR(R3) ;tell dup11 we have stopped sending
	BIT	#UP.XIE,UP.XSR(R3) ;are transmit interrupts enabled?
	BEQ	DPXST1		;no, do second half right away.
	CLR	LB.CXR(R4)	;indicate that next character done is final
	RETURN

; subroutine called on next transmit done interrupt
;  or branched to from above when transmit interrupts not enabled.
; also called directly from transmit error, since in that case
;  we do not get another done interrupt.

DPXST1:	BIC	#UP.RTS,UP.RSR(R3) ;drop request to send
	BIC	#UP.XIE,UP.XSR(R3) ;disable transmit done interrupts
	BIC	#LS.XGO!LS.XRN,(R4) ;dup11 transmitter not running
	CLR	LB.DEW(R4)	;not waiting to enable modem
	CALL	DUPBWK		;wake the bsc task
	RETURN
; subroutine to process a data set flag.

	.ENABL	LSB
DUPDSF:	INC	LB.DIC(R4)	;count dataset interrupts
	MOV	R0,LB.DIS(R4)	;record last modem interrupt status
	TST	LB.DEW(R4)	;waiting for dataset enable?
	BNE	14$		;yes, ignore modem signal.
	BIT	#LS.ACT,(R4)	;is the dup11 active?
	BEQ	14$		;no, ignore the modem signals
	MOV	R0,LB.DIP(R4)	;yes, record last modem signals processed
	BIT	#LS.XGO,(R4)	;waiting to start xmitter?
	BEQ	11$		;no.
	CALL	15$		;yes, check for clear-to-send.
11$:	BIT	#LS.XRN,(R4)	;running the transmitter?
	BEQ	12$		;no.
	CALL	17$		;yes, be sure cts still up
12$:	BIT	#LS.RGO,(R4)	;waiting to start receiver?
	BEQ	13$		;no.
	CALL	19$		;yes, see if carrier up yet
13$:	BIT	#LS.RRN,(R4)	;running receiver?
	BEQ	14$		;no.
	CALL	21$		;yes, worry about carrier failure.
14$:	RETURN

; four subroutines to process data set flags.  they are called
;  based on the current state of the dup11.
;
; R0 = modem status flags, from the dup11.
;
; here if we are waiting to start the transmitter

15$:	BIT	#UP.CTS,R0	;have we clear-to-send?
	BEQ	16$		;no, wait for it.
	CALL	DUPXST		;yes - start the transmitter
	MOV	LB.DIS(R4),R0	;get the modem status back
16$:	RETURN

; here if we are running the transmitter

17$:	BIT	#UP.CTS,R0	;is clear-to-send still up?
	BNE	18$		;yes, all ok.
	INC	LB.SE6(R4)	;no, note clear-to-send failure
	CALL	DUPERR		;note error
	CALL	DPXSTP		;stop the transmitter
18$:	RETURN

; here if we are waiting to start the receiver

19$:	BIT	#UP.CAR,R0	;is carrier up yet?
	BEQ	20$		;no, keep waiting for it.
	CALL	DPRSTR		;yes - start the receiver now
	TRACE	TRCDQF,<R4,(R4),R0,JIFCLK>
DPRCDT:	BIS	#LS.CAR,(R4)	;mark we have seen carrier

20$:	RETURN

; here if the receiver is running.

21$:	BIT	#UP.CAR,R0	;is carrier up?
	BNE	22$		;yes.
	BIT	#LS.CAR,(R4)	;no, have we seen carrier yet?
	BEQ	23$		;no, dont worry about it.
	TRACE	TRCDQF,<R4,(R4),R0,JIFCLK>
DPRCFL:	CALL	DPRSTP		;yes, stop the receiver
	RETURN			;all done.

; here if carrier is up

22$:

23$:	RETURN
	.DSABL	LSB
	.SBTTL		DUPINQ - check if any more characters available

; R5/tcb
;
; on return:
;
;	C set: DUPFUL has found characters in the DUP11
;		(not possible in this version)
;	C clear: the dup11 has no characters.  it is now
;		set to restart the bsc task on the next character.

DUPINQ:	SAVE	R4
	MOV	TCLCB(R5),R4	;point to lcb
	PIOFF			;disable interrupts
	TRACE	TRCDQD,<R4,(R4),LB.CCH(R4),LB.CCD(R4),LB.CCR(R4),@LB.CHL(R4),JIFCLK>
	MOV	@LB.CHL(R4),-(SP) ;save current chunk count
	CALL	DUPFUL		;update character count in current chunk
	CMP	(SP)+,@LB.CHL(R4) ;check if it changed
	BEQ	20$		;no
	PION			;restore interrupt level
	RESTOR	R4
	SEC			;claim there characters to be found
	RETURN
20$:	PION
	RESTOR	R4
	CLC
	RETURN			;claim there are no characters to be found
	.SBTTL		DUPKIL - kill any dup11 operations.  

; R4 = lcb pointer
; R5 = pointer to bsc tcb

DUPKIL:	TRACE	TRCDQF,<(SP),R4,(R4),LB.ALR(R4),JIFCLK> ;trace entry to dupkil
	SAVE	<R0,R1,R3>
	MOV	LB.SLA(R4),R3	;point to csr
	PIOFF			;disable interrupts
	BIT	#LS.ACT,(R4)	;is the dup11 doing anything?
	BEQ	12$		;no.

; consider stopping the transmitter

	BIT	#LS.XGO!LS.XRN,(R4) ;yes, transmitter running?
	BEQ	11$		;no.
	BIC	#UP.XIE,UP.XSR(R3) ;disable transmit done interrupts
	CALL	DPXSTP		;stop the transmitter

; consider stopping the receiver

11$:	BIT	#LS.RGO!LS.RRN,(R4) ;is receiver running?
	BEQ	12$		;no.
	CALL	DPRSTP		;yes, stop the receiver
12$:	BIC	#UP.DIE,UP.RSR(R3) ;clear dataset interrupt enbale
	PION			;enable interrupts
13$:	CALL	DEQCHK		;any chunks in receiver queue?
	BCS	14$		;no.
	CALL	FRECHK		;flush it
	BR	13$		;be sure we've got them all.

; here when the dup11 has been killed.

14$:	RESTOR	<R3,R1,R0>
	RETURN
	.SBTTL		DUPON,DUPOFF,DUPSTS - set,clear,check DTR,DSR

; three subroutines to interface to the dl10 task.  these sense
;  set and clear the modem signals dtr and dsr.
;
; subroutine to clear DTR
;
; R0 = lcb address

DUPOFF:	SAVE	R0
	MOV	LB.SLA(R0),R0		;point to csr
	MOV	UP.RSR(R0),-(SP)	;save current state
	BIC	#UP.DTR,UP.RSR(R0)	;clear dtr
	TRACE	TRCDQF,<4(SP),4(SP),UP.RSR(R0),JIFCLK> ;trace caller,line,DUP status
	BIT	#UP.DTR,(SP)+		;was it on ?
	BEQ	10$			;no - go away
	DSCHED	,#3*JIFSEC		;yes - allow time for slow modems
					; and phones to hangup
10$:	RESTOR	R0
	RETURN

; subroutine to set DTR

DUPON:	SAVE	R0
	MOV	LB.SLA(R0),R0		;point to csr
	BIS	#UP.DTR,UP.RSR(R0)	;set dtr
	TRACE	TRCDQF,<2(SP),2(SP),UP.RSR(R0),JIFCLK> ;trace caller,line,dq status
	RESTOR	R0
	RETURN

; subroutine to return values of DTR and DSR.
;
; R0 = lcb address
; R1 bits 1 and 2 are 0.
;
; on return:
;
;	bit 1 of R1 = DTR
;	bit 2 of R1 = DSR
;	 R1 is otherwise undisturbed.

DUPSTS:	SAVE	R0
	MOV	LB.SLA(R0),R0		;point to csr
	BIT	#UP.DTR,UP.RSR(R0)	;is dtr set?
	BEQ	11$			;no.
	BIS	#B1,R1			;yes, set flag in r1
11$:	BIT	#UP.DSR,UP.RSR(R0)	;is dsr set?
	BEQ	12$			;no.
	BIS	#B2,R1			;yes, set flag in r1
12$:	TRACE	TRCDQF,<2(SP),2(SP),UP.RSR(R0),JIFCLK> ;trace caller,line,dq status
	RESTOR	R0
	RETURN
	.SBTTL		DUPTRP - stop code processor for dups

; subroutine to interface with the trap handler.  on a stop code
;  this subroutine is called to store the status of the dup11
;  in the lcb for post-mortum debugging.
;  R5 does not point to a tcb, but it must not be destroyed.
;
; R4 = lcb address

DUPTRP:	MOV	LB.SLA(R4),R3	;get hardware address
	MOV	UP.RSR(R3),LB.RG0(R4) ;store receiver status register
	MOV	UP.RBF(R3),LB.RG1(R4) ;store receiver buffer
	MOV	UP.XSR(R3),LB.RG2(R4) ;store transmit status register
	MOV	UP.XBF(R3),LB.RG3(R4) ;store transmit buffer
	RETURN
	.SBTTL		DUPTIK - once per tick routine

; subroutine to check on the dup11s once a jiffie
;  we must count the clear-to-send delay and
;  compensate for improper dup11 jumpers which
;  will prevent the carrier signal from causing
;  an interrupt.
;
;	R4/line block ptr

DUPTIK:	CLR	R1		;r1 is dup11 number
	BIT	#LF.DAC,LB.FGS(R4) ;check for shut down
	BNE	15$		;yes - forget it
	PIOFF			;disable interrupts
	TST	LB.DEW(R4)	;waiting to set modem enable?
	BEQ	12$		;no.
	DEC	LB.DEW(R4)	;yes, waited long enough?
	BNE	14$		;no, wait until next time
12$:	MOV	LB.SLA(R4),R3	;point to csr
	MOV	UP.RSR(R3),R0	;get modem status bits

20$:	BIT	#UP.DIE,R0	;are modem interrupts enabled?
	BEQ	14$		;no, dont call subroutine.
	BIT	#UP.DCA,R0	;check for any summary bits(DCB seems to be on always)
	BNE	13$		;always process these
	MOV	LB.DIP(R4),R2	;get last modem signals processed
	XOR	R0,R2		;check for any changes in signals
	BIT	#UP.DCA!UP.DCB!UP.CTS!UP.CAR,R2 ;any important modem signals changed?
	BEQ	14$		;no.
13$:	CALL	DUPDSF		;yes, process any modem signal changes
14$:	PION			;restore interrupt level
15$:	RETURN

.ENDC	;.IF NE,FT.DUP
	.SBTTL		control messages


; ENQ message, for bidding for the line and asking for a
;  repeat of the last response.

ENQMSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCENQ
ENQLEN=.-ENQMSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

; ACK-0 message, used for positive acknowledgment to bid and
;  for positive acknowledge of even data blocks

AK0MSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCDLE,EBCAK0
AK0LEN=.-AK0MSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

; ACK-1 message, used for positive acknowledgment of odd data
;  blocks

AK1MSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCDLE,EBCAK1
AK1LEN=.-AK1MSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

; TTD message, for soliciting a NAK because data is not
;  yet ready.

TTDMSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSTX,EBCENQ
TTDLEN=.-TTDMSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

;  SOH-ENQ - hasp bid sequence for bidding for a line.

BIDMSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSOH,EBCENQ
BIDLEN=.-BIDMSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

; EOT message, used to terminate a message sequence.

EOTMSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN,EBCEOT
EOTLEN=.-EOTMSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

; DLE-EOT message, used to declare line shutdown for leased lines.

DISMSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN,EBCDLE,EBCEOT
DISLEN=.-DISMSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

; NAK message, used to request the retransmission of the last
;  data message and to refuse a bid.

NAKMSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCNAK
NAKLEN=.-NAKMSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

; WACK message, used to acknowledge a data block but force a wait
;  before the next data block is sent.

WAKMSG:	.BYTE	EBCLPD,EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCSYN
	.BYTE	EBCSYN,EBCDLE,EBCWAK
WAKLEN=.-WAKMSG
	.BYTE	EBCPAD
.IIF NE,FT.DQ,	.BYTE	EBCPAD,EBCPAD,EBCPAD,EBCPAD

	.EVEN			;be sure next section starts on a word boundry