V8/usr/sys/kdi/main.s

Compare this file to the similar file:
Show the results in this format:

/*
 * This version of kmc-cpu interface uses two circular buffers.
 * Cmdbuf is used to pass command to kmc;
 * statbuf is used to report status.
 * Csr4~5 are the head/tail of the cmdbuf, and csr6~7 for statbuf.
 *
 * When csr0 is 0, kmc is idle and does nothing.
 * The cpu will set csr to 1 and pass cmdbuf/statbuf addresses
 * in csr4~7.
 * Then the kmc will set csr0 to 2 and stay at this state forever.
 *
 *
 * Permanently assigned registers:
 *
 * Channel dependent assigned registers:
 *
 *	r14 - free kmc queue pointer
 *	r9 - channel #
 *	r8 - group number currently being serviced
 *	r10 - address of current line-table entry
 */

#define	C_RLEN		0	/* read length */
#define	C_RADDR		2	/* read address */
#define	C_RULEN		5	/* uncheck input in RADDR */
#define	C_XLEN		7	/* write length */
#define	C_XADDR		9	/* write address */
#define	C_RQ		12	/* un-ack input: in host memory */
#define	C_RBLEN		13	/* length */
#define	C_RB		15	/* un-check input: waiting for trailer */
#define	C_S		16	/* next seq# to be sent */
#define	C_R		17	/* last seq# echoed */
#define	C_A		18	/* last seq# acked */
#define	C_C		19	/* last seq# received */
#define	C_TA0		20	/* trailer: BOT/BOTM/BOTS/SOI */
#define	C_TA1		21	/* len of trailer data */
#define	C_TA2		22	/* 1st byte of trailer/SOI data */
#define	C_TA3		23	/* 2nd byte of trailer/SOI data */
#define	C_RSEQ		24	/* seq# for this block */
#define	C_XCNTL		25	/* cntl char following the data */
#define	C_X		26	/* state of the chan */
#define	C_X1		27	/* state of the chan (ovf from C_X) */
#define	C_Y		28	/* input state */
#define	C_ITIME		29	/* timer for last char input */
#define	C_INITIM	30	/* initial value for ITIME */
 
/*
 * define bits in C_X
 * bits 0-2 are exclusive
 */
#define	XIDLE	(1<<0)	/* chan is uninitialized */
#define	XACT	(1<<1)	/* chan is initialized */
#define	XINIT	(1<<2)	/* chan is in the init stage */
/*
 * define bits in C_X1 (overflowed from C_X)
 */
#define	XNIL	(1<<0)	/* send trailer only */
#define	XREJ	(1<<3)	/* hasn't rcv ECHO since last REJ */
#define	XBLK	(1<<4)	/* set if block mode, else char mode */
#define	XENQ	(1<<5)	/* has sent ENQ and not rcv ECHO yet */
#define	XACK	(1<<6)	/* C_S has been incremented since ENQ/CHECK */
#define	XBOTM	(1<<7)	/* send BOTM (instead of BOT) */
/*
 * define bits in C_Y
 */
#define	YTIMACT	(1<<0)	/* timer active */
#define	YEXPIRE	(1<<1)	/* timer expires */
#define	YBLOCK	(1<<5)	/* rcv return on block boundary */
#define	YTIME	(1<<6)	/* rcv return on time expires */
/*
 * Define useful constants
 */
#define	NIL	0377
#define	NULL	0377
#define	NUL	0377
#define	ZERO	0
/*
 * Datakit and protocol related constant
 */
#define	DKCHUNK		16	/* packet size - don't change */
#define	CHANMODS	96	/* # of channels per module */
#define	NGRP		CHANMODS/8-1 /* # of bytes for bit map */
#define	BUFSIZ		10	/* size of cmdbuf, statbuf, etc */
#define	MAXBADPK	64	/* bad packet received before complaining */
#define	DKBMASK		03	/* seq # is  0 to 3 */
#define	WINDOW		07	/* max window size */
#define	DKBLOCK		28	/* block size before trailer */
#define	BSIZE		64	/* size (9-bits) of each host buffer */
#define	BSIZE2		BSIZE*2	/* size in bytes */
/*
 * protocol cntl patterns
 */
#define	I_SEQ		0010	/* 8 sequence numbers to end trailers */
#define	I_ECHO		0020	/* 8 echoes, data given to host */
#define	I_REJ		0030	/* 8 rejections, transmission error */
#define	I_ACK		0040	/* first of 8 acks, correct reception */
#define	I_BOT		0050	/* normal beginning of trailer */
#define	I_BOTM		0051	/* trailer with more data to follow */
#define	I_BOTS		0052	/* seq update algorithm on this trailer */
#define	I_SOI		0053	/* start of interrupt trailer */
#define	I_EOI		0054	/* end of interrupt trailer */
#define	I_ENQ		0055	/* xmitter request flow/error status */
#define	I_CHECK		0056	/* xmitter request error status */
#define	I_INITREQ	0057	/* request initialization */
#define	I_INIT0		0060	/* disable trailer processing */
#define	I_INIT1		0061	/* enable trailer processing */
#define	I_AINIT		0062	/* response to INIT0/INIT1 */
/*
 * command type
 */
#define	KC_INIT		1	/* init: 0,0,0,0 */
#define	KC_SEND		2	/* send: len, cntl, mode, addr */
#define	KC_RCVB		3	/* rcv: len, time, mode, addr */
#define	KC_CLOSE	4	/* close: 0, 0, 0, 0 */
#define	KC_XINIT	5	/* re-init xmitter: 0, 0, 0, 0 */
#define	KC_CMD		6	/* cmd to kmc: cmd, 0, 0, 0 */
#define	KC_FLAG		7	/* i/oflag: iflag, oflaghi, oflaglo, 0 */
#define	KC_SOI		8	/* send express: (byte2<<8)|byte1, 0, 0, 0 */
#define	KC_TDK		9	/* send to tdk: ctl, 0, 0, 0 */
/*
 * report type
 */
#define	KS_SEND		20	/* send: 0, 0, 0, 0 */
#define	KS_RDB		21	/* rcv: residue len, cntl, mode, 0 */
#define	KS_EOI		22	/* rcv express: (byte2<<8)|byte1, 0, 0, 0 */
#define	KS_CNTL		23	/* rcv tdk cntl: 0, cntl, 0, 0 */
#define	KS_ERR		24	/* error: code, 0, 0, 0 */
/*
 * sub command bits for KC_CMD
 */
#define	OFLUSH	(1<<1)	/* Flush output */
#define	OSPND	(1<<2)	/* Suspend output */
#define	ORSME	(1<<3)	/* Resume output */
/*
 * KC_RCVB mode
 */
#define	CBLOCK	(1<<5)	/* return on block boundary */
#define	CTIME	(1<<6)	/* return on time expires */
/*
 * KS_RDB mode
 */
#define	SFULL	(1<<0)	/* buffer full */
#define	SCNTL	(1<<1)	/* cntl char rcv */
#define	SABORT	(1<<3)	/* rcv aborted */
#define	SBLOCK	(1<<5)	/* block boundary */
#define	STIME	(1<<6)	/* timer expires */
/* 
 * define bit pattern 
 */
#define	BIT0	(1<<0)
#define	BIT1	(1<<1)
#define	BIT2	(1<<2)
#define	BIT3	(1<<3)
#define	BIT4	(1<<4)
#define	BIT5	(1<<5)
#define	BIT6	(1<<6)
#define	BIT7	(1<<7)

#define	CLT0		0
#define	CLT1		010
#define	CLK1MS		16
#define	R_TIMER		6	/* multiple of 50 us (300us here) */
/*
 * head/tail of circular buffers
 */
#define	HC		csr4	/* head of cmdbuf */
#define	TC		csr5	/* tail of cmdbuf */
#define	HS		csr6	/* head of statbuf */
#undef	TS
#define	TS		csr7	/* tail of statbuf */
/*
 * Define bits in npr
 */
#define NRQ (1<<0)
#define OUT (1<<4)
#define BYTE (1<<7)
/*
 * Define bits in nprx
 */
#define NEM (1<<0)
#define ACLO (1<<1)
#define PCLK (1<<4)
#define VEC4 (1<<6)
#define BRQ (1<<7)
/*
 * Define check point code
 */
#define	B0_KCSO	(1<<0)	/* enter KC_SOI cmd */
#define	B0_KCCM	(1<<1)	/* enter KC_CMD cmd */
#define	B0_KCFG	(1<<2)	/* enter KC_FLAG cmd */
#define	B0_KC1	(1<<3)	/* enter KC_ONE cmd */
#define	B0_KCSD	(1<<4)	/* enter KC_SEND cmd */
#define	B0_KCRB	(1<<5)	/* enter KC_RCVB cmd */
#define	B0_KCIT	(1<<6)	/* enter KC_INIT cmd */
#define	B0_KCCS	(1<<7)	/* enter KC_CLOSE cmd */

#define	B1_ENT	(1<<0)	/* enter output seg */
#define	B1_CNTL	(1<<1)	/* send XCNTL */
#define	B1_ERO	(1<<2)	/* enter robin */
#define	B1_SPK	(1<<4)	/* issue D_XPACK */
#define	B1_ENRO	(1<<5)	/* endrobin */
#define	B1_EXIT	(1<<7)	/* exit output */

#define	B2_ENT	(1<<0)	/* enter input seq */
#define	B2_CNTL	(1<<1)	/* control packet */
#define	B2_SUP	(1<<2)	/* rcv sup cntl char */
#define	B2_NWWT	(1<<3)	/* rcv not well form trailer */
#define	B2_REP	(1<<4)	/* KS_RDB interrupt */
#define	B2_DATA	(1<<6)	/* data packet */
#define	B2_EXIT	(1<<7)	/* exit input seg */

#define	B3_SEQ	(1<<0)	/* rcv SEQ */
#define	B3_ECHO	(1<<1)	/* rcv ECHO */
#define	B3_ACK	(1<<2)	/* rcv ACK */
#define	B3_REJ	(1<<3)	/* rcv REJ */
#define	B3_CHK	(1<<4)	/* rcv CHK */
#define	B3_ENQ	(1<<5)	/* rcv ENQ */

#define	B4_EOI	(1<<0)	/* rcv EOI */
#define	B4_BOT	(1<<1)	/* rcv BOT */
#define	B4_IN1	(1<<2)	/* rcv INIT1 */
#define	B4_IN0	(1<<3)	/* rcv INIT0 */
#define	B4_AIN	(1<<4)	/* rcv AINIT*/
#define	B4_IREQ	(1<<5)	/* rcv INITREQ */

#define	B5_ENT	(1<<0)	/* enter rcv */
#define	B5_PLEN	(1<<1)	/* plen >= RLEN */
#define	B5_ODD	(1<<2)	/* r_odd */
#define	B5_REP	(1<<3)	/* report READ comp */
#define	B5_MOVE	(1<<4)	/* partial buffer */
#define	B5_AJAX	(1<<5)	/* enter clean() */
#define	B5_EXIT	(1<<6)	/* exit rcv */
#define	B5_TIME	(1<<7)	/* timer expires */

#define	B6_POST	(1<<0)	/* output postprocessing */
#define	B6_NL	(1<<1)	/* NL */
#define	B6_CR	(1<<2)	/* CR */
#define	B6_TAB	(1<<3)	/* TAB */
#define	B6_BS	(1<<4)	/* back space */
#define	B6_VT	(1<<5)	/* VT */
#define	B6_FF	(1<<6)	/* FF */

#define	B7_IFL	(1<<0)	/* flush input */
#define	B7_OFL	(1<<1)	/* flush output */
#define	B7_OSP	(1<<2)	/* suspend output */
#define	B7_ORE	(1<<3)	/* resume output */
#define	B7_IUB	(1<<4)	/* unblock input */
#define	B7_IBL	(1<<5)	/* block input */
#define	B7_BRK	(1<<6)	/* send break */

/*
 * Define error codes
 */
#define	E_SW		00	/* dispatcher switch */
#define	E_BUS		01	/* Unibus error */
#define	E_IPANIC	02	/* input routine panic */
#define	E_CMD		03	/* command unknown */
#define	E_NOQB		04	/* run out of queue or buffer */
#define	E_DUP		05	/* duplicate SEND */
#define	E_ODKOVF	06	/* output routine panic */
#define	E_UMETA		07	/* un-recognized cntl char */
#define	E_SYS1		041	/* system error 1 */
#define	E_SYS2		042	/* system error 2 */
 
#ifdef	DR11C
/*
 * specific declarations for dr11-c
 */
#define	DKRDONE	(1<<7)	/* 15-th bit */
#define	DKTDONE	(1<<7)	/* 7-th bit */
#define	DKCOM	(3<<0)	/* 01 bits */
#define	DKTENAB	(1<<6)	/* 6-th bit */
#define	DKRENAB	(1<<5)	/* 5-th bit */
#define DKDATA	(1<<0)	/* 8-th bit */
#define	DKMARK	(1<<1)	/* 9-th bit */
#define	ILO	idl
#define	IHI	idh
#define	OLO	odl
#define	OHI	odh
#define	D_OSEQ	0
#define	D_READ	1
#define	D_WRITE	2
#define	D_XPACK	3
#define	D_WHEAD	(1<<1)		/* 9-th bit */
#define	D_WDATA	(1<<0)		/* data byte */
#define	D_WCNTL	0		/* control byte */
#endif
#ifdef	KDI
/*
 * specific declarations for dki board
 */
#define	ILO	lur0
#define	IHI	lur5
#define	OLO	lur0
#define	OHI	lur1
#define	DKFRDY	(1<<0)	/* data from dk ready */
#define	TRQRDY	(1<<1)	/* seq# rdy: xmit done */
#define	D_OSEQ	(0<<6)
#define	D_RESET	(0<<6)
#define	D_READ	(1<<6)
#define	D_WHEAD	0202
#define	D_WDATA	0201
#define	D_WCNTL	0200
#define	D_XPACK	0301
#endif

/*
 * MACRO definition
 */
 
/*
 * Define the "BUSWAIT" macro
 * not test NMEM is dangereous if memory does not exist, but fast
 */
#define	BUSWAIT\
5:	mov	npr,brg;\
	br0	5b		/* loop until ~NRQ */

/*
 * Macros to get and release queue entries
 * r14 points to the list of available queue entries
 */
#define	FREEQ(X)\
	mov	03,brg;\
	and	brg,X,%mar;\
	mov	~03,brg;\
	and	brg,X,mar;\
	mov	r14,mem;\
	mov	X,brg;\
	mov	brg,r14

#define	GETQ(X,Y)\
	mov	r14,brg;\
	brz	Y;\
	mov	brg,X;\
	mov	03,brg;\
	and	brg,X,%mar;\
	mov	~03,brg;\
	and	brg,X,mar;\
	mov	mem,r14
 
/*
 * QA(REG) : QA converts a queue pointer to mar & %mar form.
 *	REG is a scratch pad register and has the format:
 *	bits 7-2 from REG + 00 forms the offset within a page
 *	bits 1-0 is the page number
 *	Queues are 4-byte long, so it always fall in 4-byte boundary
 *	Queues can only be located in the first 4 pages
 */
#define	QA(REG)\
	mov	~03,brg;\
	and	brg,REG,mar;\
	mov	03,brg;\
	and	brg,REG,%mar

/*
 * GEBI(X,ADDR) : get a free buffer index
 *	'bmap' to 'bmap'+16 indicate 128 buffers, each 128 bytes.
 *	A '1' in i-th bit postion indicates i-th buffer is buzy
 *	GEBI finds the first free buffer and returns its number
 *	in register X. If no buffer is free, jump to ADDR
 *	Turn on i-th bit
 *	X can't be r0
 *	using r0, X, brg, mar, %mar
 */
#define GEBI(X,ADDR)\
	mov	0,brg;\
	mov	brg,X;\
	mov	1,brg;\
	mov	brg,r0;\
	mov	bmap,mar;\
	mov	%bmap,%mar;\
5:	mov	mem,brg;\
	brz	8f;\
6:	br0	7f;\
	br	9f;\
7:	inc	X;\
	asl	r0;\
	mov	0,brg>>;\
	br	6b;\
8:	mov	8,brg|mar++;\
	add	brg,X,X|brg;\
	br7	ADDR;\
	br	5b;\
9:	or	mem,r0,mem

/*
 * IBEG(X) : return buffer index X to pool (i.e. turn off bit in 'bmap')
 *	X can't be r0
 *	using r0, X, brg, mar, %mar
 */
#define	IBEG(X)\
	mov	X,brg;\
	brz	9f;\
	mov	bmap0,mar;\
	mov	%bmap0,%mar;\
6:	mov	8,brg|mar++;\
	sub	brg,X;\
	brc	6b;\
	add	brg,X;\
	mov	1,brg;\
	mov	brg,r0;\
7:	dec	X;\
	brz	8f;\
	asl	r0,r0|brg;\
	br	7b;\
8:	mov	0,r0;\
	addn	brg,r0;\
	and	mem,r0,mem;\
9:

/*
 * BITA(I,X,Y,Z): buffer index (in I) to address (X:LSB, Y, Z:MSB)
 */
#define	BITA(I,X,Y,Z)\
	mov	baddr,mar;\
	mov	%baddr,%mar;\
	mov	mem,X|mar++;\
	mov	mem,Y|mar++;\
	mov	mem,Z;\
8:	dec	I;\
	brz	9f;\
	mov	BSIZE2,brg;\
	add	brg,X;\
	adc	Y;\
	adc	Z;\
	br	8b;\
9:

/*
 * Subroutine CALL and RETURN macros
 */
#define	CALL(X,SAVE)\
	mov	%SAVE,%mar;\
	mov	SAVE,mar;\
	mov	%9f,mem|mar++;\
	mov	9f,mem;\
	mov	%X,brg;\
	mov	brg,pcr;\
	jmp	X;\
9:

#define	RETURN(SAVE)	\
	mov	%SAVE,%mar;\
	mov	SAVE,mar;\
	mov	mem,pcr|mar++;\
	jmp	(mem)

/*
 * ERROR(X) --- store error code in brg, jump to errlog
 */
#define	ERROR(X) \
	mov	%errlog,brg;\
	mov	brg,pcr;\
	mov	X,brg;\
	jmp	errlog
 
/*
 * Macro BRB(X,Y,Z): if bit Y is on in X, then branch to Z
 *	X is a scratch pat register
 * Macro BRP(X,Y,Z): if bit Y is on in X, then branch to Z
 *	X may not be a scratch pat register
 */
#define	BRB(X,Y,Z)	\
	mov	~Y,brg;\
	or	brg,X,-;\
	brz	Z
#define	BRP(X,Y,Z)	\
	mov	X,r0;\
	mov	~Y,brg;\
	or	brg,r0,-;\
	brz	Z
 
/*
 * CHK(X,Y): set flag X on Y-th check word
 */
#ifdef CHECK
#define	CHK(X,Y)\
	mov	Y,brg;\
	mov	brg,r0;\
	mov	check,brg;\
	add	brg,r0,mar;\
	mov	%check,%mar;\
	mov	X,brg;\
	mov	brg,r0;\
	or	mem,r0,mem
#else
#define	CHK(X,Y)
#endif
 
/*
 * CHK1(X,Y): set flag X on Y-th check word
 * same as CHK except restore page register (r8)
 */
#ifdef CHECK
#define	CHK1(X,Y)\
	CHK(X,Y);\
	mov	r8,%mar
#else
#define	CHK1(X,Y)
#endif

/*
 * GLTE(CHAN) : CHAN is the channel #
 *	LTE size is 32 bytes for each channel
 *	When exit, r8(%mar), r10(mar) = addr of LTE
 *	Using r8, r10, brg, mar, mem
 */
#define	GLTE(CHAN)\
	mov	CHAN,brg;\
	mov	brg,brg>>;\
	mov	brg,brg>>;\
	mov	brg,brg>>;\
	mov	brg,r8;\
	mov	0340,brg;\
	and	brg,r8,brg;\
	mov	brg,r10|mar;\
	mov	037,brg;\
	and	brg,r8;\
	mov	%dzst,brg;\
	add	brg,r8,r8|%mar
 
/*
 * INCA(X,Y,Z,AMOUNT) - inc the content of registers X, Y, Z
 *	(Z most significant) by AMOUNT
 *	X, Y, Z are scratch pad registers
 *	Using X, Y, Z, brg
 */
#define	INCA(X,Y,Z,AMOUNT)\
	mov	AMOUNT,brg;\
	add	brg,X;\
	adc	Y;\
	adc	Z

/*
 * SPUTTWO(Z,ERR) - put two bytes from odl & odh to cpu
 *	cpu addresses are in oal (bits 7-0), oah (bits 15-8),
 *	and Z (bits 17-16)
 *	If error, go to ERR.
 *	When exit, cpu addresses are NOT incremented by 2
 *	Does NOT wait for UNIBUS to complete
 *	Using r0, r1, brg, npr, oal, oah, odl, odh, mar, mem, X, Y, Z.
 */
#define	SPUTTWO(Z,ERR)\
	asl	Z,brg;\
	mov	brg,r0;\
	asl	r0;\
	mov	nprx,r1;\
	mov	~(BRQ|ACLO|(3<<2)),brg;\
	and	brg,r1,brg;\
	or	brg,r0,nprx	/* set up bits 17-16 */;\
	mov	NRQ|OUT,brg;\
	mov	brg,npr		/* issue unibus request */;\
	BUSWAIT
 
/*
 * PUTTWO(X,Y,Z,ERR) - put two bytes from odl & odh to cpu
 *	cpu addresses are stored in X (bits 7-0)
 *		Y (bits 15-8) and Z (bits 17-16)
 *	If error, go to ERR.
 *	When exit, cpu addresses are incremented by 2
 *	Using r0, r1, brg, npr, oal, oah, odl, odh, mar, mem, X, Y, Z.
 */
#define	PUTTWO(X,Y,Z,ERR)\
	mov	X,brg;\
	mov	brg,oal;\
	mov	Y,brg;\
	mov	brg,oah;\
	SPUTTWO(Z,ERR);\
	INCA(X,Y,Z,2);\
	BUSWAIT
 
/*
 * SPUTONE(Z,ERR) - put one bytes from odl/odh to cpu
 *	cpu addresses are in oal (bits 7-0), oah (bits 15-8),
 *	and Z (bits 17-16)
 *	If error, go to ERR.
 *	When exit, cpu addresses are NOT incremented by 1
 *	Does NOT wait for UNIBUS to complete
 *	Using r0, r1, brg, npr, oal, oah, odl, odh, mar, mem, Z.
 */
#define	SPUTONE(Z,ERR)\
	asl	Z,brg;\
	mov	brg,r0;\
	asl	r0;\
	mov	nprx,r1;\
	mov	~(BRQ|ACLO|(3<<2)),brg;\
	and	brg,r1,brg;\
	or	brg,r0,nprx	/* set up bits 17-16 */;\
	mov	NRQ|OUT|BYTE,brg;\
	mov	brg,npr		/* issue unibus request */;\
	BUSWAIT
 
/*
 * PUTONE(X,Y,Z,ERR) - put one bytes from odl/odh to cpu
 *	cpu addresses are stored in X (bits 7-0)
 *		Y (bits 15-8) and Z (bits 17-16)
 *	If error, go to ERR.
 *	When exit, cpu addresses are incremented by 1
 *	Using r0, r1, brg, npr, oal, oah, odl, odh, mar, mem, X, Y, Z.
 */
#define	PUTONE(X,Y,Z,ERR)\
	mov	X,brg;\
	mov	brg,oal;\
	mov	Y,brg;\
	mov	brg,oah;\
	SPUTONE(Z,ERR);\
	INCA(X,Y,Z,1);\
	BUSWAIT
 
/*
 * SGETTWO(X,Y,Z,ERR) - get two bytes into idl & idh from cpu
 *	cpu addresses are stored in X (bits 7-0)
 *		Y (bits 15-8) and Z (bits 17-16)
 *	If error, go to ERR.
 *	Does NOT wait for UNIBUS to complete
 *	When exit, cpu addresses are incremented by 2
 *	Using r0, brg, npr, ial, iah, idl, idh, mar, mem, X, Y, Z.
 */
#define	SGETTWO(X,Y,Z,ERR)\
	mov	X,brg;\
	mov	brg,ial;\
	mov	Y,brg;\
	mov	brg,iah;\
	asl	Z,brg;\
	mov	brg,r0;\
	asl	r0;\
	mov	NRQ,brg;\
	or	brg,r0,npr;\
	INCA(X,Y,Z,2);\
	BUSWAIT
 
/*
 * GETTWO(X,Y,Z,ERR) - get two bytes into idl & idh from cpu
 *	cpu addresses are stored in X (bits 7-0)
 *		Y (bits 15-8) and Z (bits 17-16)
 *	If error, go to ERR.
 *	When exit, cpu addresses are incremented by 2
 *	Using r0, brg, npr, ial, iah, idl, idh, mar, mem, X, Y, Z.
 */
#define	GETTWO(X,Y,Z,ERR)\
	SGETTWO(X,Y,Z,ERR);\
	BUSWAIT
 
/*
 * MOD1(X,Y,Z) : Z = (X==Y)? 0 : X
 * X, Y, Z are scratch pad registers
 * X, Y won't be changed when returned
 * Using brg
 */
#define	MOD1(X,Y,Z)\
	mov	X,brg;\
	addn	brg,Y,-;\
	brz	8f;\
	br	9f;\
8:	mov	0,brg;\
9:	mov	brg,Z

/*
 * MOD0(X,Y,Z) - Z = (X<0)? (X+Y) : (X)
 * X, Y, Z are scratch pad registers
 * X, Y won't be changed when returned
 * Using brg
 */
#define	MOD0(X,Y,Z)\
	mov	X,brg;\
	br7	8f;\
	br	9f;\
8:	add	brg,Y,brg;\
9:	mov	brg,Z

/*
 * TIMES(W,X,Y,Z) - Y(hibyte)Z(low) = W * X
 * W, X, Y, Z are scratch pad registers
 * W, X, Y, Z can't be the same
 * X, Y, Z can't be r0
 * X, W won't be changed when returned
 * Using brg, r0
 */
#define	TIMES(W,X,Y,Z)\
	mov	0,brg;\
	mov	brg,Y;\
	mov	brg,Z;\
	mov	W,brg;\
	mov	brg,r0;\
	mov	X,brg;\
8:	dec	r0;\
	brz	9f;\
	add	brg,Z;\
	adc	Y;\
	br	8b;\
9:

/*
 * BIT(X,Y) - bit pattern - Y = (1 << (x))
 * X, Y are scratch pat registers and can't be the same
 * usually 0 <= X <= 7
 */
#define	BIT(X,Y)\
	mov	1,brg;\
	mov	brg,Y;\
8:	dec	X;\
	brz	9f;\
	asl	Y;\
	br	8b;\
9:

/*
 * SENDDATA(HI,LO) : send HI to lur1 and LO to lur0
 *	using brg, lur0, lur1, lur2 (if KDI)
 *	using brg, odl, odh, oal, oah, mem, mar, npr, nprx, r0 (DR11C)
 */
#define	SENDDATA(HI,LO)	\
	mov	HI,brg;\
	mov	brg,OHI;\
	mov	LO,brg;\
	mov	brg,OLO;\
	SENDD

#ifdef	KDI
#ifdef CPM422
/*
 *	cpm422 can't send an envelop within 2us
 *	using brg, r0
 */
#define D422(X)	DELAY(X)
#else
#define	D422(X)
#endif
/*
 * DELAY(X):  some time
 *	delay 400*(X+1) ns
 *	using r0, brg
 */
#define	DELAY(X) \
	mov	X,brg;\
	mov	brg,r0;\
9:	dec	r0;\
	brc	9b

/*
 * READDATA(LOC) : read data in lur0 (lo byte) and lur5 (hi byte)
 *	if data is not ready (i.e. DKFRDY (bit 0) is not set), goto LOC.
 *	using lur0, lur1, lur5, lur6, brg
 */
#define	READDATA(LOC)	\
	mov	lur6,brg;\
	br0	8f;\
	br	LOC;\
8:

/*
 * SENDD : OLO and OHI have been loaded, just trigger lur2
 */
#define	SENDD\
	mov	0,brg;\
	mov	brg,lur2	/* strobe */

/*
 * SENDHEAD(CHAN): send DKMARK with channel #
 *	using brg, lur0, lur1, lur2
 */
#define	SENDHEAD(CHAN)\
	SENDDATA(D_WHEAD,CHAN)

/*
 * SENDPACK(SEQ) : send seq# to seq fifo
 *	SEQ is in a scratch pad register
 *	using brg, lur1, lur2, SEQ
* using also lur6 to check that the transmitter becomes
* ready again.  We busy wait until it does.
* We the read the sequence number so that the busy
* wait loop will be valid the next time
 */
#define	SENDPACK(SEQ)	\
	mov	D_XPACK,brg;\
	mov	brg,lur1;\
	SENDD;\
1:	mov	lur6,brg;\
	br1	1f;\
	br	1b;\
1:	mov	D_OSEQ,brg;\
	mov	brg,lur1;\
	mov	lur1,brg	/*read strobe*/

/*
 * SENDCMD(CMD)
 */
#define	SENDCMD(CMD)\
	mov	CMD,brg;\
	mov	brg,lur1;\
	D422(3)

#else
/*
 * DR11-C specific macros
 */
/*
 * DELAY(X): DR11C is slow enough and don't need delay
 */
#define DELAY(X)

/*
 * READCMD : read csr word in the DR-11C
 * Input: csraddr is the csr unibus address
 * Output: idl (low-byte) and idh (hi-byte) of the csr
 * Using ial, iah, mar, mem, r0, brg, npr
 */
#define	READCMD\
	mov	%csraddr,%mar;\
	mov	csraddr,mar;\
	READDR
 
/*
 * READDATA : read dki word of the DR-11C
 * Input: dkiaddr is the dki unibus address
 * Output: idl (low-byte) and idh (hi-byte) of the dki
 * Using ial, iah, mar, mem, r0, brg, npr
 */
#define	READDATA(LOC)\
	READCMD;\
	mov	idh,brg;\
	br7	9f;\
	br	LOC;\
9:;\
	mov	%dkiaddr,%mar;\
	mov	dkiaddr,mar;\
	READDR
 
/*
 * READDR : read a word whose unibus addr is pointed by mar
 */
#define	READDR\
	mov	mem,ial|mar++;\
	mov	mem,iah|mar++;\
	mov	mem,r0;\
	asl	r0;\
	asl	r0;\
	mov	NRQ,brg;\
	or	brg,r0,npr;\
	BUSWAIT
 
/*
/*
 * WRITEDR : write odl and odh to unibus addr pointed by mar
 */
#define	WRITEDR\
	mov	nprx,r0;\
	mov	~(BRQ|ACLO|(3<<2)),brg;\
	and	brg,r0,brg;\
	mov	mem,oal|mar++;\
	mov	mem,oah|mar++;\
	mov	mem,r0;\
	asl	r0;\
	asl	r0;\
	or	brg,r0,nprx;\
	mov	OUT|NRQ,brg;\
	mov	brg,npr;\
	BUSWAIT
 
/*
 * SENDCMD : Issue a DR-11C command by outputing csr
 * Using odl, odh, oal, oah, mar, mem, brg, npr, nprx, r0
 */
#define	SENDCMD(CMD)\
	mov	CMD,brg;\
	mov	brg,odl;\
	mov	0,brg;\
	mov	brg,odh;\
	mov	%csraddr,%mar;\
	mov	csraddr,mar;\
	WRITEDR

/*
 * SENDD: OHI, OLO have been loaded
 * Using odl, odh, oal, oah, mar, mem, brg, npr, nprx, r0
 */
#define	SENDD\
	mov	%dkoaddr,%mar;\
	mov	dkoaddr,mar;\
	WRITEDR

/*
 * SENDHEAD(CHAN) : send DKMARK with channel #
 *	channel # can't be in r0
 *	using odl, odh, oal, oah, mar, %mar, mem, brg, npr, nprx, r0
 */
#define	SENDHEAD(CHAN) \
	SENDCMD(D_WRITE);\
	SENDDATA(D_WHEAD,CHAN)

/*
 * SENDPACK(SEQ) : send seq# to seq fifo
 *	SEQ can't be in r0
 *	using odl, odh, oal, oah, mar, %mar, mem, brg, npr, nprx, r0
 */
#define	SENDPACK(SEQ) \
	SENDCMD(D_XPACK);\
	SENDDATA(SEQ,ZERO)

#endif

/*
 * Data definitions
 */
	.data
/*
 * Global data structures
 */
	.org	0
/*
 * check point code
 */
check:	.byte	0,0,0,0,0,0,0
/*
 * 1 ms timer
 * 16 times 50-us timer expires ==> set this timer
 */
clk1ms:		.byte	0
/*
 * 5 sec timer : 4096 * 1ms
 * Every time clk1ms expires (1ms gone by), clk5s gets decremented
 */
clk5s:		.byte	0,0
/*
 * temp storage
 */
tmp:	.byte	0,0,0,0,0,0,0,0,0,0,0,0
/*
 * return code: code, lenlo, lenhi, cntl, mode
 */
repinfo:	.byte	0
replen:		.byte	0,0
repctl:		.byte	0
repmode:	.byte	0
/*
 * report seq#
 */
kseq:		.byte	0
/*
 * # of bad packets received so far
 */
badpack:	.byte	0
/*
 * temp storage for output routine
 */
blklen:		.byte	0
/*
 * a flag indicates a cntl char was rcv (used in subr.s)
 */
icntl:		.byte	0
/*
 * input state: bit7 - RBLEN>0; bit0 - 1st byte of a pair is now in odl
 */
iflag:		.byte	0
/*
 * chan#
 */
chan:		.byte	0
/*
 * seq#
 */
pseq:		.byte	0
/*
 * save r13/r12/r1/... when calling 'rcv'
 */
rcvsave:	.byte	0,0,0,0
/*
 * save area for r12 in report
 */
repsave:	.byte	0,0,0,0
/*
 * buffer map: 16 locations indicate 16*8 (128) buffers, each 128 bytes
 */
bmap0:		.byte	0
bmap:		.org	.+16
/*
 * timer map: for max 96 channels
 */
timemap:	.org	.+12
/*
 * bit pattern
 */
bitpatt:	.byte	BIT0,BIT1,BIT2,BIT3,BIT4,BIT5,BIT6,BIT7
/*
 * starting addresses of host buffer
 */
baddr:		.byte	0,0,0
/*
 * Addresses for cmdbuf, tail of cmdbuf, statbuf, head of statbuf,
 */
cbaddr:		.byte	0,0,0
cbtail:		.byte	0,0,0
sbaddr:		.byte	0,0,0
sbhead:		.byte	0,0,0
/*
 * CALL/RETURN return address
 */
s.send:		.byte	0,0
s.rcv:		.byte	0,0
s.report:	.byte	0,0
s.clean:	.byte	0,0
s.copy1:	.byte	0,0
s.cktimer:	.byte	0,0
s.strailer:	.byte	0,0
/*
 * dr11c specific
 */
#ifdef DR11C
/*
 * every time PCLK expires, r_timer gets decremented.
 * if r_timer reaches -1, then read CSR to check R/TDONE
 */
r_timer:	.byte	0
/*
 * UNIBUS addresses for the DR11C
 * Bits 7-0 in 1st byte, 15-8 in 2nd, 17-16 in 3rd (right-adjust)
 */
csraddr:	.byte	0,0,0
dkoaddr:	.byte	0,0,0
dkiaddr:	.byte	0,0,0
#endif
/*
 * output active queue
 */
outq:		.byte	NIL
outq1:		.byte	NIL		/* work area */
 
/*
 * Queue entries
 */
inqueue:
	.org	4*256
/*
 * LTE tables from page 4 to 15, each entry is 32 bytes
 */
dzst:
/*
 * Instruction text starts here
 */
	.text
/*
 * Segment 0
 *
 * This is the main segment
 */
	.org	0
seg0:
init:
/*
 * Set csr0 to idle state (i.e. 0)
 */
	mov	0,brg
	mov	brg,csr0
/*
 * initialize all global variables
 */
	mov	brg,mar
	mov	brg,%mar
	mov	inqueue-1,brg
	mov	brg,r0
init1:
	mov	0,mem|mar++
	dec	r0
	brc	init1
/*
 * initialize special variables (~0)
 */
	mov	outq,mar
	mov	%outq,%mar
	mov	NIL,mem|mar++
	mov	NIL,mem|mar++
	mov	%bitpatt,%mar
	mov	bitpatt,mar
	mov	7,r7
	mov	1,r1
1:
	mov	r1,mem|mar++
	dec	r7
	brz	2f
	asl	r1
	br	1b
2:
/*
 * Initialize the free-buffer list
 * page 0-7 is for global data and queues
 * page 8-15 for LTE entries
 */
#define	NLTE	(CHANMODS+7)/8
#define	NPQ	4
#define	NQE	((NPQ*256-((inqueue+3)&~3))/4)
/*
 * Initialize the list of available queue entries
 */
	mov	NIL,brg
	mov	brg,r14
	mov	(inqueue+3)&~3,brg
	mov	brg,r0
	mov	NQE-2,brg
	mov	brg,r1
init4:
	FREEQ(r0)
	mov	4,brg		/* queue is 4-byte long */
	add	brg,r0
	adc	r0
	dec	r1
	brc	init4
/*
 * Initialize LTE queue pointers
 */
	mov	CHANMODS-1,brg
	mov	brg,r9
init6:
/*
 * reset all LTE values: (can be done globally)
 */
	GLTE(r9)
	mov	31,brg			/* 32: size of LTE */
	mov	brg,r0
1:
	mov	0,mem|mar++
	dec	r0
	brc	1b
/*
 * initialized C_X to idle and RQ/RB to nil
 * initailize whatever else is needed
 */
	mov	r8,%mar
	mov	C_X,brg
	add	brg,r10,mar
	mov	XIDLE,mem
	mov	C_RQ,brg
	add	brg,r10,mar
	mov	NIL,mem
	mov	C_RB,brg
	add	brg,r10,mar
	mov	NIL,mem
	dec	r9
	brc	init6
/*
 * Go to disp
 */
	br	disp
/*
 * Dispatcher loop--keep looking for something to do
 */
disp:
/*
 * If csr0 == 0, does nothing and loop.
 * If csr0 == 1, read addresses from csr2~7.
 * If csr0 == 2, normal operation (i.e. take command from
 *	cmdbuff and put report to statbuf).
 */
	mov	csr0,brg
	mov	3,r3
	and	brg,r3,brg
	br1	disp2		/* normal operation */
	br0	disp1		/* direct command */
	mov	brg,r0
	dec	r0,-
	brz	disp		/* idle */
	ERROR(E_SW)
disp1:
/*
 * Get init information from structure pointed by csr2~4
 * struct init {
 *	paddr_t	*cmdaddr;
 *	paddr_t	*stataddr;
 *	paddr_t	*bufaddr
 *	paddr_t	*csraddr
 * };
 */
	mov	csr2,r13
	mov	csr3,r12
	mov	csr4,r11
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r5
	GETTWO(r13,r12,r11,err_bus)
	mov	cbaddr,mar
	mov	%cbaddr,%mar
	mov	idl,mem|mar++		/* cbaddr */
	mov	idh,mem|mar++
	mov	r5,mem|mar++
	mov	idl,mem|mar++		/* cbtail */
	mov	idh,mem|mar++
	mov	r5,mem|mar++
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r5
	GETTWO(r13,r12,r11,err_bus)
	mov	sbaddr,mar
	mov	%sbaddr,%mar
	mov	idl,mem|mar++		/* sbaddr */
	mov	idh,mem|mar++
	mov	r5,mem|mar++
	mov	idl,mem|mar++		/* sbhead */
	mov	idh,mem|mar++
	mov	r5,mem|mar++
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r5
	GETTWO(r13,r12,r11,err_bus)
	mov	baddr,mar
	mov	%baddr,%mar
	mov	idl,mem|mar++		/* bufaddr */
	mov	idh,mem|mar++
	mov	r5,mem|mar++
#ifdef KDI
/*
 * Issue D_RESET command and set dko to 0 to clear all fifo's
 */
	mov	D_RESET,brg
	mov	brg,lur1
	mov	brg,lur2
#else
/*
 * Record the unibus address (csr) for this DR-11
 * addr(dko) = addr(csr) + 2
 * addr(dki) = addr(csr) + 4
 */
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r5
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r7
	mov	idh,r6
	mov	3,brg
	mov	brg,r5		/* io page */
	mov	%csraddr,%mar
	mov	csraddr,mar
	mov	r7,mem|mar++
	mov	r6,mem|mar++
	mov	r5,mem|mar++
/*
 * Now mar points to dkoaddr
 */
	mov	2,brg
	add	brg,r7,mem|mar++
	adc	r6,mem|mar++
	adc	r5,mem|mar++
/*
 * Now mar points to dkiaddr
 */
	mov	4,brg
	add	brg,r7,mem|mar++
	adc	r6,mem|mar++
	adc	r5,mem
/*
 * Issue D_OSEQ command and set dko to 0 to clear all fifo's
 */
	mov	D_OSEQ,brg
	mov	brg,odl
	mov	0,brg
	mov	brg,odh
	SENDCMD(D_OSEQ)
	SENDDATA(ZERO,ZERO)
#endif
/*
 * Now set csr0 to 2 (i.e. normal operation)
 * Set head/tail of all circular buffers to 0
 */
	mov	0,brg
	mov	brg,csr2
	mov	brg,csr3
	mov	brg,csr4
	mov	brg,csr5
	mov	brg,csr6
	mov	brg,csr7
	mov	2,brg
	mov	brg,csr0
	br	disp

disp2:
/*
 * This is normal operation loop
 */
	mov	csr0,brg
	br0	disp2
/*
 * assume kmc is fast enough so that cmdbuf never overflows
 * If HC==TC, then no command available and loop
 */
	mov	HC,brg		/* head of cmdbuf */
	mov	TC,r0		/* tail of cmdbuf */
	addn	brg,r0,-
	brz	disp3
/*
 * Read the command pointed by TC
 * Put k_type in r1, k_chan in r9, k_len in r7-r6,
 * k_ctl/time in r5, k_mode in r4, k_addr in r3-r2-idl-idh
 */
	mov	cbtail,mar
	mov	%cbtail,%mar
	mov	mem,r13|mar++
	mov	mem,r12|mar++
	mov	mem,r11|mar++
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r1
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r9
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r7
	mov	idh,r6
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r5
	mov	idh,r4
	GETTWO(r13,r12,r11,err_bus)
	mov	idl,r3
	mov	idh,r2
	GETTWO(r13,r12,r11,err_bus)
/*
 * Now modify TC and cbtail.
 * If TC==BUFSIZ-1, then TC=0 and cbtail=cbaddr
 */
	mov	TC,r0
	mov	BUFSIZ-1,brg
	sub	brg,r0,-	/* TC-(BUFSIZ-1) */
	brc	1f
	inc	r0,TC
	br	2f
1:	mov	0,brg		/* reset TC */
	mov	brg,TC
	mov	cbaddr,mar
	mov	%cbaddr,%mar
	mov	mem,r13|mar++
	mov	mem,r12|mar++
	mov	mem,r11
2:
/*
 * Restore cbtail
 */
	mov	cbtail,mar
	mov	%cbtail,%mar
	mov	r13,mem|mar++
	mov	r12,mem|mar++
	mov	r11,mem|mar++
/*
 * Decode the command
 * switch (k_type) {
 * case	KC_SEND:	goto kc_send;
 * case	KC_RCVB:	goto kc_rcvb;
 * case	KC_SOI:		goto kc_soi;
 * case KC_CMD:		goto kc_cmd;
 * case KC_FLAG:	goto kc_flag;
 * case KC_TDK:		goto kc_tdk;
 * case KC_XINIT:	goto kc_xinit;
 * case KC_CLOSE:	goto kc_close;
 * case KC_INIT:	goto kc_init;
 * }
 */
	GLTE(r9)		/* set up r8/r10 */
	mov	KC_SEND,brg
	addn	brg,r1,-
	brz	kc_send
	mov	KC_RCVB,brg
	addn	brg,r1,-
	brz	kc_rcvb
	mov	KC_SOI,brg
	addn	brg,r1,-
	brz	kc_soi
	mov	KC_CMD,brg
	addn	brg,r1,-
	brz	kc_cmd
	mov	KC_FLAG,brg
	addn	brg,r1,-
	brz	kc_flag
	mov	KC_TDK,brg
	addn	brg,r1,-
	brz	kc_tdk
	mov	KC_XINIT,brg
	addn	brg,r1,-
	brz	kc_xinit
	mov	KC_CLOSE,brg
	addn	brg,r1,-
	brz	kc_close
	mov	KC_INIT,brg
	addn	brg,r1,-
	brz	kc_init
	ERROR(E_CMD)
	br	disp3

kc_init:
/*
 * Process a KC_INIT command
 *	r5 is either D_CALL or D_IDL1
 *	C_X = XINIT; goto kc_reset
 */
	CHK1(B0_KCIT,0)
	mov	C_X,brg
	add	brg,r10,mar
	mov	XINIT,mem
	br	kc_reset		/* expect to rcv D_TALK from ty-4 */

kc_xinit:
/*
 * Process a KC_XINIT command
 *	C_X = XINIT; C_XLEN = 0; send(INIT1);
 */
	CHK1(B0_KCIT,0)
	mov	C_X,brg
	add	brg,r10,mar
	mov	XINIT,mem|mar++
/*
	mov	0,mem
 */
	mov	C_XLEN,brg
	add	brg,r10,mar
	mov	0,mem|mar++
	mov	0,mem
	mov	C_XCNTL,brg
	add	brg,r10,mar
	mov	0,mem
	mov	I_INIT1,brg
	mov	brg,r1
	CALL(send,s.send)		/* can simply goto 'time' */
	br	disp3

kc_close:
/*
 * process KC_CLOSE command
 *	k_chan is the channel #
 *	if (C_RLEN) report(KS_RDB, chan);
 *	goto kc_reset
 */
	CHK1(B0_KCCS,0)
	mov	C_X,brg
	add	brg,r10,mar
	mov	XIDLE,mem|mar++
	mov	0,mem		/* C_X1 = 0 */
	mov	C_RLEN,brg
	add	brg,r10,mar
	mov	mem,r7|brg
	mov	brg,r0|mar++
	mov	mem,r6
	or	mem,r0
	dec	r0,-
	brz	kc_reset
/*
 * report (KS_RDB,chan) with mode SABORT
 */
	mov	repinfo,mar
	mov	%repinfo,%mar
	mov	KS_RDB,mem|mar++
	mov	r7,mem|mar++		/* residue length */
	mov	r6,mem|mar++
	mov	0,mem|mar++		/* cntl char */
	mov	SABORT,mem		/* RCV mode */
	CALL(report,s.report)
kc_reset:
/*
 * Reset RLEN, XLEN.
 * while (RQ!=NIL) free the queues and buffers
 */
	mov	r8,%mar
	mov	C_RLEN,brg
	add	brg,r10,mar
	mov	0,mem|mar++
	mov	0,mem
	mov	C_RBLEN,brg
	add	brg,r10,mar
	mov	0,mem|mar++
	mov	0,mem
	mov	C_TA0,brg
	add	brg,r10,mar
	mov	0,mem|mar++
	mov	0,mem
	mov	C_C,brg
	add	brg,r10,mar
	mov	0,mem
	mov	C_RQ,brg
	add	brg,r10,mar
	mov	mem,r2
	brz	kc_rs2
	mov	NIL,mem
	CALL(clean,s.clean)
	mov	r8,%mar
kc_rs2:
	mov	C_RB,brg
	add	brg,r10,mar
	mov	mem,r2
	brz	kc_rs4
	mov	NIL,mem
	CALL(clean,s.clean)
kc_rs4:
	mov	r8,%mar
	mov	C_XLEN,brg
	add	brg,r10,mar
	mov	0,mem|mar++
	mov	0,mem
	mov	C_XCNTL,brg
	add	brg,r10,mar
	mov	0,mem
/*
 * remove chan# from 'outq'
 * r2 is running pointer for outq (currently looked at)
 * r3 is the next outq entry
 * r4 is the running pointer for outq1
 */
	mov	outq,mar
	mov	%outq,%mar
	mov	mem,r2
kc_rs6:
	brz	kc_rs14
	QA(r2)
	mov	mem,r3
	mov	NIL,mem|mar++
	addn	mem,r9,-
	brz	kc_rs12			/* delete this chan# */
	mov	outq1,mar
	mov	%outq1,%mar
kc_rs8:
	mov	mem,r4
	brz	kc_rs10
	QA(r4)
	br	kc_rs8			/* try to find the end of outq1 */
kc_rs10:
/*
 * found the end of outq1, append r2 here
 */
	mov	r2,mem
kc_rs11:
/*
 * put r3 in r2 position
 */
	mov	r3,brg
	mov	brg,r2
	br	kc_rs6
kc_rs12:
	FREEQ(r2)
	br	kc_rs11
kc_rs14:
/*
 * restore outq from outq1
 */
	mov	outq1,mar
	mov	%outq1,%mar
	mov	mem,brg
	mov	outq,mar
	mov	%outq,%mar
	mov	brg,mem|mar++
	mov	NIL,mem
	br	disp3
kc_soi:
/*
 * Process a KC_SOI command
 *	send 2 bytes in r7 (lo) and r6 (hi) to chan r9
 *	bypass error/flow control mechanism
 */
	CHK1(B0_KCSO,0)
	mov	tmp,mar
	mov	%tmp,%mar
	mov	r9,mem|mar++
	mov	D_WHEAD,mem|mar++
	mov	I_SOI,mem|mar++
	mov	D_WCNTL,mem|mar++
	mov	r7,mem|mar++
	mov	D_WDATA,mem|mar++
	mov	r6,mem|mar++
	mov	D_WDATA,mem|mar++
	mov	I_EOI,mem|mar++
	mov	D_WCNTL,mem|mar++
	mov	ZERO,mem|mar++		/* seq# */
	mov	D_XPACK,mem|mar++
#ifdef DR11C
	SENDCMD(D_WRITE)		/* for dr11-c */
#endif
	mov	0,brg
	mov	brg,r5
kc_so1:
	mov	%tmp,%mar
	mov	tmp,brg
	add	brg,r5,mar
	mov	mem,OLO|mar++
	mov	mem,OHI|mar++
	SENDD
	inc	r5
	inc	r5
	mov	10,brg
	addn	brg,r5,-
	brz	kc_so3		/* issue XPACK */
	brc	disp3
	br	kc_so1
kc_so3:
	SENDCMD(D_XPACK)	/* dor dr11-c */
	br	kc_so1

kc_send:
/*
 * Process a KC_SEND command
 * The format is:
 *	k_chan is the chan #
 *	r7-r6: XLEN; r5: XCNTL; r4: mode; idl-idh-r3: XADDR;
 */
	CHK1(B0_KCSD,0)
/*
 * If this chan already in output waiting (C_XLEN!=0), error
 */
	mov	C_XLEN,brg
	add	brg,r10,mar
	mov	mem,r0|mar++
	or	mem,r0
	mov	C_XCNTL,brg
	add	brg,r10,mar
	or	mem,r0
	dec	r0,-
	brz	1f
	br	err_dup
1:
/*
 * Fill LTE info
 */
	mov	C_XLEN,brg
	add	brg,r10,mar
	mov	r7,mem|mar++	/* C_XLEN */
	mov	r6,mem|mar++
	mov	idl,mem|mar++	/* C_XADDR */
	mov	idh,mem|mar++
	mov	r3,mem|mar++
	mov	C_XCNTL,brg
	add	brg,r10,mar
	mov	r5,mem
/*
 * if mode = XBOTM (i.e. 1<<7), set this bit in C_X1;
 * otherwise reset this bit
 */
	mov	C_X1,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	~XBOTM,brg
	or	brg,r4,-
	brz	1f
	and	brg,r0,mem
	br	2f
1:
	orn	brg,r0,mem
2:
/*
 * if xlen (r7,r6) and xcntl (r5) are all 0, set XNIL for sending
 * trailer only
 */
	mov	r7,brg
	or	brg,r6,brg
	or	brg,r5
	dec	r5,-
	brz	kc_sn6
/*
 * if the channel is not active (i.e. hasn't rcv ainit)
 */
	mov	C_X,brg
	add	brg,r10,mar
	mov	mem,brg
	br1	kc_sn2		/* XACT is set */
	br	disp3
kc_sn2:
/*
 * this channel is output active: put it in 'outq'
 */
	GETQ(r1,noqb)
	mov	NIL,mem|mar++
	mov	r9,mem
	mov	outq,mar
	mov	%outq,%mar
kc_sn3:
	mov	mem,r2
	brz	kc_sn5
	QA(r2)
	br	kc_sn3
kc_sn5:
	mov	r1,mem
	br	disp3
kc_sn6:
/*
 * send trailer mode is on (i.e. 0 data byte, no cntl byte)
 */
	mov	C_X,brg
	add	brg,r10,mar
	mov	mem,brg|mar++
	mov	mem,r7
	mov	XNIL,mem
	or	mem,r7,mem
	br1	kc_sn7
	br	disp3		/* hasn't rcv ainit */
kc_sn7:
	CALL(strailer,s.strailer)
	br	disp3

kc_rcvb:
/*
 * process a receive-buffer command
 * The format is:
 *	k_chan is the chan# to be input
 *	r7-r6: RLEN; r5: INITIM; r4: mode; idl-idh-r3: RADDR;
 */
	CHK1(B0_KCRB,0)
	mov	C_RLEN,brg
	add	brg,r10,mar
	mov	mem,r13
	mov	r7,mem|mar++
	mov	mem,r12
	mov	r6,mem|mar++
	mov	idl,mem|mar++		/* RADDR */
	mov	idh,mem|mar++
	mov	r3,mem
	mov	C_INITIM,brg
	add	brg,r10,mar
	mov	r5,mem
	mov	C_Y,brg
	add	brg,r10,mar
	mov	r4,mem
/*
 * If there was an unfinished read-buf, report it
 * i.e. if C_RLEN > 0
 */
	mov	r13,brg
	or	brg,r12,brg
	mov	brg,r0
	dec	r0,-
	brz	kc_rc4
/*
 * abort the old pending read
 * report whatever having been received
 */
	mov	%repinfo,%mar
	mov	repinfo,mar
	mov	KS_RDB,mem|mar++
	mov	r13,mem|mar++
	mov	r12,mem|mar++		/* residue length */
	mov	0,mem|mar++		/* cntl */
	mov	SABORT,mem		/* mode */
	CALL(report,s.report)
kc_rc4:
/*
 * if (char mode && YTIME && (RB != NULL))  set YEXPIRE
 */
	mov	r8,%mar
	mov	C_X1,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	XBLK,brg
	orn	brg,r0,-
	brz	kc_rc7
	mov	C_Y,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	YTIME,brg
	orn	brg,r0,-
	brz	1f
	mov	C_RB,brg
	br	kc_rc8
1:
	mov	C_RB,brg
	add	brg,r10,mar
	mov	mem,brg
	brz	disp3
/*
 * set YEXPIRE
 */
	mov	C_Y,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	YEXPIRE,brg
	or	brg,r0,mem
kc_rc6:
/*
 * call rcv to copy RQ/RB to RADDR
 */
	CALL(rcv,s.rcv)			/* copy C_RQ to RADDR */
	br	disp3
kc_rc7:	mov	C_RQ,brg
kc_rc8:	add	brg,r10,mar
	mov	mem,brg
	brz	disp3
	br	kc_rc6

kc_cmd:
/*
 * process KC_CMD command
 *	k_chan is the chan #
 *	r7 is the subcommand
 *	only OFLUSH is implemented, others will be added later
 */
	CHK1(B0_KCCM,0)
	br	kc_rs4

kc_flag:
/*
 * process KC_FLAG command
 *	k_chan is the chan #
 *	1st two bytes (r7-r6) are iflag, next two (r5-r4) are oflag
 * TO BE DONE LATER
	CHK1(B0_KCFG,0)
 */
br disp3

kc_one:
/*
 * process KC_ONE command
 *	k_chan is the chan #
 *	send one byte (control byte) in r7 to ty-4
 * TO BE ADDED LATER
	CHK1(B0_KC1,0)
 */
br disp3

kc_tdk:
/*
 * process KC_TDK command
 *	k_chan is the chan #
 * TO BE ADDED LATER
 */
br disp3

kc_stime:
/*
 * process KC_STIME command
 *	k_chan is the chan #
 *	r7 is the timer value
 * TO BE ADDED LATER
 */
br disp3

disp3:
#ifdef KDI
/*
 * check input data ready and output completion
 * input ready is indicated by DKFRDY (bit 0)
 * output ready is indicated by TRQRDY (bit 1)
 */
	mov	lur6,brg
	br0	csrck2
/*
 * since we are not reading seq fifo, bit 1 is always set
	br1	csrck4
 */
#endif
	BRP(nprx,PCLK,tick)
	br	disp2

tick:
/*
 * 50-us timer expires, restart the timer
 */
	mov	nprx,r0
	mov	~(BRQ|ACLO),brg
	and	brg,r0
	mov	PCLK,brg
	or	brg,r0,nprx
#ifdef DR11C
/*
 * Dec the r_timer, and goto r_expire if r_timer reaches -1
 */
	mov	r_timer,mar
	mov	%r_timer,%mar
	mov	mem,r0
	dec	r0,mem
	brz	r_expire
	br	tick1

r_expire:
/*
 * reset r_timer
 */
	mov	R_TIMER,mem
/*
 * check input data ready, then output completion
 */
	READCMD			/* get csr in idl & idh */
/*
 * If there is an input character available then go to csrck2
 * DKRDONE is 1<<15 in dkcsr
 */
	mov	idh,brg
	br7	csrck2
/*
 * If there is an output data request then go to csrck4
 * DKTDONE is 1<<7 in dkcsr
	mov	idl,brg
	br7	csrck4
 */
#endif
tick1:
/*
 * dec the 1ms timer and return if the result is non-negative
 */
	mov	clk1ms,mar
	mov	%clk1ms,%mar
	mov	mem,r0
	dec	r0,mem
	brc	tick10
	mov	CLK1MS,mem	/* restore timer */
	mov	NGRP,brg
	mov	brg,r6
tick2:
/*
 * 1ms timer expires: dec ITIME for each input chan which on timemap
 * r6: row, r7: column, r5: map itself
 */
	mov	%timemap,%mar
	mov	timemap,brg
	add	brg,r6,mar
	mov	mem,r5|brg|mar++
	mov	7,r7
tick3:
	dec	r5,-
	brz	tick4
tick3.2:
	br7	tick5
	asl	r5,r5|brg
	dec	r7
	br	tick3.2
tick3.5:
	asl	r5,r5|brg
	dec	r7
	brc	tick3
tick4:
	dec	r6
	brc	tick2
	br	tick9
tick5:
/*
 * found this channel (r6<<2 + r7) is timer active (i.e. YTIMACT)
 */
	mov	r6,brg
	mov	brg,r9
	asl	r9
	asl	r9
	asl	r9
	mov	r7,brg
	add	brg,r9		/* r9 is the chan # */
	GLTE(r9)
	mov	C_Y,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	YTIME,brg
	orn	brg,r0,-
	brz	tick7
	br	timeerr
tick7:
/*
 * timer processing is enabled for this chan
 */
	mov	YEXPIRE,brg
	orn	brg,r0,-
	brz	timeerr
	mov	YTIMACT,brg
	orn	brg,r0,-
	brz	1f
	br	timeerr			/* timer not active */
1:
/*
 * timer is active, dec ITIME
 */
	mov	C_ITIME,brg
	add	brg,r10,mar
	mov	mem,r0
	dec	r0,mem
	brc	tick3.5		/* not reach zero yet */
/*
 * timer expires: set YEXPIRE
 */
	mov	C_Y,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	YEXPIRE,brg
	or	brg,r0,mem
/*
 * YEXPIRE is set: call 'rcv' 
 */
	mov	%rcvsave,%mar
	mov	rcvsave,mar
	mov	r5,mem|mar++
	mov	r6,mem|mar++
	mov	r7,mem|mar++
	CALL(rcv,s.rcv)
	mov	%rcvsave,%mar
	mov	rcvsave,mar
	mov	mem,r5|mar++
	mov	mem,r6|mar++
	mov	mem,r7|mar++
	CHK(B5_TIME,5)
tick8:
/*
 * reset timemap for this chan
 * the following was done in 'rcv'
	mov	%bitpatt,%mar
	mov	bitpatt,brg
	add	brg,r7,mar
	mov	mem,r0
	mov	%timemap,%mar
	mov	timemap,brg
	add	brg,r6,mar
	xor	mem,r0,mem
 */
	br	tick3.5
timeerr:
/*
 * should never happen: will be removed after being debugged
 */
mov 0222,brg
mov brg,r15
br .

tick9:
/*
 * dec 5 sec timer, goto timeout if expires
 * steals this 50-us cycle and 'time' no returns
 */
	mov	clk5s,mar
	mov	%clk5s,%mar
	mov	mem,r0
	dec	r0
	mov	r0,mem|mar++
	mov	mem,r1
	mov	0,brg
	subc	brg,r1,mem
	and	mem,r0,-
	brz	time
tick10:
/*
 * if (outq) goto csrck4;
 * This is the replacement of sending a dummy packet to chan 511
 */
	mov	outq,mar
	mov	%outq,%mar
	mov	mem,r0
	brz	disp2
csrck4:
/*
 * Output data request--jump to output segment
 */
	mov	%out,brg
	mov	brg,pcr
	jmp	out
csrck2:
/*
 * Input data available--jump to input segment
 */
	mov	%in,brg
	mov	brg,pcr
	jmp	in

time:
/*
 * timeout routine
 * for each active chan, if (S != R+1) send ENQ
 */
/*
 * reinitialize the clk5s
 */
	mov	clk5s,mar
	mov	%clk5s,%mar
	mov	CLT0,mem|mar++
	mov	CLT1,mem
	mov	CHANMODS,brg
	mov	brg,r9		/* channel # */
time1:
	dec	r9
	brz	disp2
	GLTE(r9)			/* setup r8/r10 for LTE */
	mov	C_X,brg
	add	brg,r10,mar
	mov	mem,brg
	br0	time1		/* bit 0 indicates un-initialized */
	br1	time2		/* bit 1 indicates initialized */
/*
 * INIT1 was sent and no reply; keep sending INIT1
 */
	mov	I_INIT1,brg
	br	time3
time2:
/*
 * chan is active, if (S!=R+1) send ENQ
 */
	mov	C_S,brg
	add	brg,r10,mar
	mov	mem,r1|mar++
	mov	mem,r0
	inc	r0
	mov	WINDOW,brg
	and	brg,r0,brg
	addn	brg,r1,-
	brz	time1
	mov	C_X1,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	~(XACK|XREJ),brg
	and	brg,r0			/* reset XACK and XREJ */
	mov	XENQ,brg
	or	brg,r0,mem		/* set XENQ */
	mov	I_ENQ,brg
time3:
	mov	brg,r1
	CALL(send,s.send)		/* argument in r9, r1 */
	br	time1
 
err_bus:
/*
 * The Unibus transfer fails to complete within 20 usec
 * Clear NEM bit, queue the error, and go to disp
 */
	mov	nprx,r0
	mov	~(BRQ|ACLO|NEM),brg
	and	brg,r0,nprx
	ERROR(E_BUS)
	br	disp
 
err_dup:
/*
 * Two SEND command
 */
	ERROR(E_DUP)
	br	disp

noqb:
/*
 * no queue or buffer
 */
	ERROR(E_NOQB)
	br	disp

errlog:
/*
 * r9 - chan#
 * brg - error code
 * after interrupt cpu, go to init and wait for reinitialization
 */
	mov	repinfo,mar
	mov	%repinfo,%mar
	mov	KS_ERR,mem|mar++
	mov	brg,mem
	CALL(report,s.report)
	br	disp2
 
/*
 * End of segment 0
 */
endseg0:
/*
 * Pick up code from other segments
 */
#include	"output.s"
#include	"input.s"
#include	"subr.s"