SysIII/usr/src/uts/vax/up/dza/dza.s

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

/*
 * This is the KMC-11 portion of the UTS driver for a DZ-11
 * with KMC assist.
 *
 * The following functions are performed in the KMC:
 *
 *	1. On command from the driver, transmit multi-character
 *	   buffers to output lines.
 *
 *	2. Notify the driver of output buffer completions.
 *
 *	3. Optionally insert delays for carriage-control characters
 *	   (CR, LF, BS, HT, VT, and FF).
 *
 *	4. Optionally replace HT by an appropriate number of blanks.
 *
 *	5. Optionally replace LF by CR-LF.
 *
 *	6. Optionally generate escape sequences for upper-case-only
 *	   terminals.
 *
 * Permanently assigned registers:
 *
 *	r15 - 100-microsecond timer/DZ-11 commutator
 *	r14 - pointer to head of report-needed queue
 *	r13 - pointer to end of report-needed queue
 *	r12 - pointer to head of csr-check-needed queue
 *	r11 - pointer to end of csr-check-needed queue
 *	r10 - address of current line-table entry
 *	r9 - current state of the dialog with the PDP-11
 *	r8 - DZ number currently being serviced
 *	r7 - entry number of current line-table entry
 *
 * The structure of a line-table entry is as follows:
 *
 *  Byte 	Contents
 *
 *   0 - current state of this line
 *
 *   1 - number of characters remaining in the current output buffer
 *
 *   2 - high-order byte of the current output word
 *
 *   3 - saved character to be sent after the current character
 *
 *   4 - low-order byte of the address of the current output word
 *
 *   5 - high-order byte of the address of the current output word
 *
 *   6 - bits 17-16 of the address of the current output word.
 *
 *   7 - flags
 *
 *   8 - count for time delay, etc.
 *
 *   9 - icol
 *
 *   10 - character count for new output buffer
 *
 *   11 - sflags (single-bit stty options)
 *
 *   12 - NL delay algorithm
 *
 *   13 - HT delay algorithm
 *
 *   14 - CR delay algorithm
 *
 *   15 - (bsdelay<<1)|(vtdelay)
 *
 * The flag bits in byte 7 are used as follows:
 *
 *  Bit		Meaning
 *
 * 1<<1 - The high-order data byte has not been sent
 * 1<<4 - There is a byte in SAVECHAR to be sent
 * 1<<0 - The current newline character has already been expanded
 *
 * Define useful constants
 */
#define	NIL	0377
#define	EOT	004
#define	NUL	0
#define	CEOT	004
#define	NL	012
#define	CR	015
#define	CSTART	021
#define	CSTOP	023
#define	SP	040
#define BS	010
#define HT	011
#define	ESCAPE	0134
#define	DEL	0177
#define	CQUIT	034
#define	CINTR	0177
/*
 * Define report types
 */
#define	XBUFOUT	0
#define	RBUFOUT	1
#define	RBUF1OT	2
#define	RBRK	3
#define	COCHG	4
#define	ERROUT	5
/*
 * Define the byte offset for the fields of a line-table entry
 *
 * Note:  Must have STATE == 0
 *
 * Also, the fields beginning with OFLAG must remain in 
 * the order given
 */
#define	STATE	0
#define	NCH	1
#define	HIBYTE	2
#define	SAVECHAR	3
#define	BUFAD	4
#define	FLAGS	7
#define	FLAGS1	8
#define	COUNT	9
#define	ICOL	10
#define	NCHNEW	11
#define	OFLAG	12
#define DELAYS	13
#define EXPCHR	14
#define UNUSED	15
/*
 * STATE == 0	no output in progress
 * STATE == 1	sending normal data
 * STATE == 2	sending character that needs time fill
 * STATE == 3	expanding horizontal tabs
 * STATE == 4	sending fill characters
 * STATE == 5	twait state (Timer running)
 */
/*
 * Define bits in FLAGS 
 */
#define	NLEXP	(1<<0)
#define	SENDHIGH	(1<<1)
#define	EMITESC	(1<<2)
#define	SENDSAVE	(1<<4)
#define	IPREVESC	(1<<5)
#define	CARR_ON	(1<<6)
/*
 * Define bits in FLAGS1 (overflow from FLAGS)
 */
#define	TTSTOP	(1<<0)
#define DELIM (1<<1)
#define TIMACT	(1<<2)
#define LTEINQ (1<<4)
#define PREVNB	(1<<5)
#define SNDEXP	(1<<6)
#define OUTBRK	(1<<7)
/*
 * Define the bits in OFLAGS
 */
#define	OPOST	(1<<0)
#define	OLCUC	(1<<1)
#define	ONLCR	(1<<2)
#define	OCRNL	(1<<3)
#define	ONOCR	(1<<4)
#define	ONLRET	(1<<5)
#define	OFILL	(1<<6)
#define	OFDEL	(1<<7)
/*
 * Define the bits in DELAYS
 */
#define	NLDELAY	(1<<0)
#define	CRDELAY	(3<<1)
#define	HTDELAY	(3<<3)
#define	BSDELAY	(1<<5)
#define	VTDELAY	(1<<6)
#define	FFDELAY	(1<<7)
/*
 * Define the "buswait" macro
 */
#define buswait 1:mov npr,brg;br0 1b;mov nprx,brg;br0 buserr;
/*
 * Subroutine CALL and RETURN macros
 *
 * Nested calls are not supported
 * R0 is destroyed by teh return instructions
 */
#define	CALL(X)\
	mov	%return,%mar;\
	mov	return,mar;\
	mov	%0f,mem|mar++;\
	mov	0f,mem;\
	br	X;\
0:

#define	RETURN		br	ret;

/*
 * 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 bits in csr0
 */
#define RQI (1<<7)
#define IEI (1<<0)
#define IEO (1<<4)
/*
 * Define bits in csr2
 */
#define RDYO (1<<7)
#define RDYI (1<<4)
/*
 * Data definitions
 */
	.data
/*
 * Save room for the line-table entry address table
 * and the enable bits
 */
	.org	.+16
/*
 * Define the macro for generating line-table entries
 */
#define LTENT  .byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
/*
 * Now generate the actual line-table entries
 */
lte0:	LTENT
lte1:	LTENT
lte2:	LTENT
lte3:	LTENT
lte4:	LTENT
lte5:	LTENT
lte6:	LTENT
lte7:	LTENT
/*
 * temp for debugging
 *
 * last command received from the host computer for this DZ
 */
cmdsave:  .byte	0,0,0,0,0,0,0,0
rptsave:  .byte	0,0,0,0,0,0,0,0
/*
 * Byte strings for setting up unibus transfers
 *
 * Each string contains one unibus address
 *
 * Bits 7-0 are in the first byte, bits 15-8 are in the second byte,
 * and bits 17-16 are in the third byte
 *
 * String used to set up fetch for CSR
 */
csrget:	.byte	0,0,0
/*
 * String used to put one byte into TBUF
 */
tdrput:	.byte	0,0,0,0
/*
 * String used to set or clear the transmit-enable bits
 */
tcrput:	.byte	0,0,0,0
/*
 * Cell used to save status info for each active DZ-11
 * (There is a different cell for each active DZ-11)
 */
status:	.byte	0
/*
 * The bits in the status cell are used as follows:
 *
 *	1<<0 - CSR check requested
 *	1<<1 - driver notification requested
 *	1<<5 - unibus error has occurred
 *	1<<7 - base-in command received
 */
/*
 * Cell to record the DZ number for this page
 */
dznr:	.byte	0
/*
 * Cell to record output-buffer completions (one bit for each line)
 *
 * The bit is set when the last word of an output buffer is fetched
 * from the host computer.  The entire cell is cleared when the bits have
 * been passed to the driver
 */
buffcomp:	.byte	0
/*
 * KMC copy of the transmit-enable bits in the DZ-11
 */
actline:	.byte	0
/*
 * Current output character (for debugging)
 */
charsave:  .byte	0
/*
 * 16.7 msec software timer
 */
clock1:	.byte	0
/*
 * Link to next DZ in report-needed queue
 */
rptlink:	.byte	0
/*
 * Link to next DZ in csr-check-needed queue
 */
csrlink:	.byte	0
/*
 * The following variables are only used in page zero
 */

/*
 * Return address of the CALL/RETURN marcors
 */
return:	.byte	0,0
/*
 * Set up the table of addresses of the line-table entries
 *
 * Each byte holds the address of one line-table entry
 *
 * This table must be at location zero of each page
 */
	.org	0
	.byte	lte0
	.byte	lte1
	.byte	lte2
	.byte	lte3
	.byte	lte4
	.byte	lte5
	.byte	lte6
	.byte	lte7
/*
 * Masks to set or clear the transmit-enable bit for each line
 *
 * Note:  The enable bits must immediately follow the lte address list
 */
enblbits:	.byte	1<<0,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7
/*
 * Ditto for page 1
 */
	.org	256
	.byte	lte0
	.byte	lte1
	.byte	lte2
	.byte	lte3
	.byte	lte4
	.byte	lte5
	.byte	lte6
	.byte	lte7
	.byte	1<<0,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7
/*
 * Ditto for page 2
 */
	.org	512
	.byte	lte0
	.byte	lte1
	.byte	lte2
	.byte	lte3
	.byte	lte4
	.byte	lte5
	.byte	lte6
	.byte	lte7
	.byte	1<<0,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7
/*
 * Ditto for page 3
 */
	.org	768
	.byte	lte0
	.byte	lte1
	.byte	lte2
	.byte	lte3
	.byte	lte4
	.byte	lte5
	.byte	lte6
	.byte	lte7
	.byte	1<<0,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7
/*
 * Instruction text starts here
 */
	.text
/*
 * KMC initialization sequence--keep this at location zero
 */
	.org	0
	br	init	/* keep this instruction at location 0 */
/*
 * Vectors of jump instructions
 *
 * The reason for putting the vectors here is to avoid
 * splitting a vector across a page boundary.
 */
dispsw:
	br	disp0
	br	disp1
	br	disp2
	br	disp3
	br	disp4
	br	e1
statesw:
	br	disable
	br	odrst1
	br	odrst2
	br	odrst3
	br	odrst4
	br	odrst5
	br	e1
/*
 * Code to do a subroutine return
 */
ret:
	mov	%return,%mar
	mov	return,mar
	mov	mem,r0|mar++
	mov	0f,brg
	br	(add, brg, r0), %0f
0:
	br	(mem),p0
	br	(mem),p1
	br	(mem),p2
	br	(mem),p3

/*
 * The following table of jump instructions is accessed by a
 * calculated branch instruction using the ASCII character
 * as index.  It provides a fast implementation of tests
 * for specific control characters and for characters which
 * require special handling for upper-case only terminals.
 */
charsw:
	br	sendit		/* 0 - NUL */
	br	sendit		/* 1 - SOH */
	br	sendit		/* 2 - STX */
	br	sendit		/* 3 - ETX */
	br	odrst1		/* 4 - EOT */
	br	sendit		/* 5 - ENQ */
	br	sendit		/* 6 - ACK */
	br	sendit		/* 7 - BEL */
	br	isbs		/* 010 - BS */
	br	isht		/* 011 - HT */
	br	isnl		/* 012 - NL */
	br	isvt		/* 013 - VT */
	br	isff		/* 014 - FF */
	br	iscr		/* 015 - CR */
	br	sendit		/* 016 - SO */
	br	sendit		/* 017 - SI */
	br	sendit		/* 020 - DLE */
	br	sendit		/* 021 - DC1 */
	br	sendit		/* 022 - DC2 */
	br	sendit		/* 023 - DC3 */
	br	sendit		/* 024 - DC4 */
	br	sendit		/* 025 - NAK */
	br	sendit		/* 026 - SYN */
	br	sendit		/* 027 - ETB */
	br	sendit		/* 030 - CAN */
	br	sendit		/* 031 - EM */
	br	sendit		/* 032 - SUB */
	br	sendit		/* 033 - ESC */
	br	sendit		/* 034 - FS */
	br	sendit		/* 035 - GS */
	br	sendit		/* 036 - RS */
	br	sendit		/* 037 - US */
/*
 * Initialize the KMC
 */
init:
	mov	0,brg
	mov	brg,r9
	mov	brg,csr2
	mov	IEO,brg
	mov	brg,csr0
	mov	NIL,brg
	mov	brg,r14
	mov	brg,r13
	mov	brg,r12
	mov	brg,r11
	br	disp
/*
 * Dispatcher loop--keep looking for something to do
 */
disp:
/*
 * If the 50-microsecond timer has expired then go to tick
 */
	mov	nprx,brg
	br4	tick
/*
 * Get OFLAG into r6
 */
	mov	OFLAG,brg
	add	brg,r10,mar
	mov	mem,r6
/*
 * Go to disp0, disp1, disp2, disp3, or disp4 depending on the
 * current state of the dialog with the PDP-11
 */
	mov	dispsw,brg
	br	(add,brg,r9),%dispsw
/*
 * Case 0 (interface inactive):
 *
 * If the driver has set RQI then go to rqiset
 */
disp0:
	mov	csr0,brg
	br7	rqiset
/*
 * If there is something to report to the driver then go to report
 */
	mov	r14,-
	brz	1f
	br	report
1:
/*
 * Go to dispb
 */
	br	dispb
/*
 * The driver has requested the interface (by setting RQI)
 * in order to pass a command to the KMC.
 */
rqiset:
/*
 * Set RDYI
 */
	mov	csr2,r0
	mov	RDYI,brg
	or	brg,r0,csr2
/*
 * Set dialog state = 1
 */
	mov	1,brg
	mov	brg,r9
/*
 * Go to disp
 */
	br	disp
/*
 * Case 1 (RQI  received, RDYI sent):
 */
disp1:
/*
 * If the driver has cleared RDYI then go to command
 */
	mov	csr2,brg
	br4	1f
	br	command
1:
/*
 * If the driver has set IEI then go to ieiset
 */
	mov	csr0,brg
	br0	ieiset
/*
 * Go to dispb
 */
	br	dispb
/*
 * The driver has requested an interrupt (by setting IEI).
 * The driver has already set RQI and the KMC has responded
 * by setting RDYI.
 */
ieiset:
/*
 * Send interrupt 1 to the PDP-11
 */
	mov	BRQ,brg
	mov	brg,nprx
/*
 * Set dialog state = 2
 */
	mov	2,brg
	mov	brg,r9
/*
 * Go to disp
 */
	br	disp
/*
 * Case 2 (RQI and IEI received, RDYI and BRQ sent):
 */
disp2:
/*
 * If the driver has cleared RDYI and BRQ then go to command,
 * otherwise go to dispb
 */
	mov	csr2,brg
	br4	dispb
	mov	nprx,brg
	br7	dispb
	br	command
/*
 * Case 3 (RDYO has been set)
 */
disp3:
/*
 * If the driver has set IEO then go to report2, else if the
 * driver has cleared RDYO then set dialog state = 0 and
 * go to disp, otherwise go to dispb
 */
	mov	csr0,brg
	br4	report2
	mov	csr2,brg
	br7	dispb
	mov	0,brg
	mov	brg,r9
	br	disp
/*
 * Case 4 (RDYO, BRQ, and VEC4 have been set):
 */
disp4:
/*
 * If the driver has cleared RDYO and BRQ then set dialog state = 0
 * and go to disp, otherwise go to dispb
 */
	mov	csr2,brg
	br7	dispb
	mov	nprx,brg
	br7	dispb
	mov	0,brg
	mov	brg,r9
	br	disp
/*
 * If a CSR check is needed then go to csrcheck
 * else go to disp
 */
dispb:
	mov	r12,-
	brz	disp
	br	csrcheck
/*
 * The KMC has something to report to the driver
 *
 * (We get here from state 0 when the report-needed queue
 * is non-empty.)
 */
report:
/*
 * Set page register = DZ number.
 */
	mov	r14,brg
	mov	brg,r8|%mar
/*
 * Remove this DZ page from the head of the report-needed queue.
 */
	mov	rptlink,mar
	mov	mem,r14
	brz	1f
	br	2f
1:
	mov	mem,r13
2:
/*
 * Copy the buffer-completion bits to the interface
 * and clear the bits in the KMC.
 */
	mov	buffcomp,mar
	mov	mem,csr4
	mov	0,mem
/*
 * Put the DZ number in csr3
 */
	mov	dznr,mar
	mov	mem,csr3
/*
 * Copy the status bits to csr7 and clear the report-needed
 * bit and the unibus-error bit in the KMC
 */
	mov	status,mar
	mov	mem,csr7
	mov	~((1<<1)|(1<<5)),brg
	mov	brg,r0
	and	mem,r0,mem
/*
 * Save the report
 */
	mov	rptsave,mar
	mov	csr0,mem|mar++
	mov	csr1,mem|mar++
	mov	csr2,mem|mar++
	mov	csr3,mem|mar++
	mov	csr4,mem|mar++
	mov	csr5,mem|mar++
	mov	csr6,mem|mar++
	mov	csr7,mem
/*
 * Set RDYO
 */
	mov	csr2,r0
	mov	RDYO,brg
	or	brg,r0,csr2
/*
 * If IEO is set then go to report2
 */
	mov	csr0,brg
	br4	report2
/*
 * Set dialog state = 3
 */
	mov	3,brg
	mov	brg,r9
/*
 * Go to disp
 */
	br	disp
/*
 * Continuation of driver-report processing
 *
 * We come here when the driver sets IEO (in response to
 * our setting RDYO)
 */
report2:
/*
 * Send interrupt 2 to the PDP-11
 */
	mov	BRQ|VEC4,brg
	mov	brg,nprx
/*
 * Set dialog state = 4
 */
	mov	4,brg
	mov	brg,r9
/*
 * Go to disp
 */
	br	disp
/*
 * Process a command from the driver
 */
command:
/*
 * Set dialog state = 0
 */
	mov	0,brg
	mov	brg,r9
/*
 * Set page register = low-order two bits of DZ number
 *
 *
 */
	mov	csr3,r0
	mov	3<<3,brg
	and	brg,r0,brg
	mov	brg,brg>>
	mov	brg,brg>>
	mov	brg,brg>>
	mov	brg,r8|%mar
/*
 * Save command info for debugging
 */
	mov	cmdsave,mar
	mov	csr0,mem|mar++
	mov	csr1,mem|mar++
	mov	csr2,mem|mar++
	mov	csr3,mem|mar++
	mov	csr4,mem|mar++
	mov	csr5,mem|mar++
	mov	csr6,mem|mar++
	mov	csr7,mem|mar++
/*
 * Get the command type from csr2
 */
	mov	csr2,r0
	mov	7,brg
	and	brg,r0
/*
 * If command type == 0 then go to buffsend
 * else if command type == 1 then go to ioctl
 * else if command type == 2 then go to basein
 * esle if command type == 3 then go to cmdin
 * else go to disp
 */
	dec	r0
	brz	buffsend
	dec	r0
	brz	ioctl
	dec	r0
	brz	basein
	dec	r0
	brz	cmdin
	br	disp
/*
 * Process a BASEIN command
 *
 * The format of a BASEIN command is:
 *
 *	csr3 - DZ number<<3
 *	csr4 - low byte of DZ-11 unibus address
 *	csr5 - high byte of DZ-11 unibus address
 *
 * Note: Eventually we may need to pass bits 17-16 of the
 * unibus address.  For now we assume they are always ones.
 */
basein:
/*
 * If this DZ is already active then ignore the base-in
 * command; otherwise mark this DZ as active.
 */
	mov	status,mar
	mov	mem,brg
	br7	disp
	mov	1<<7,mem
/*
 * Record the DZ number
 */
	mov	csr3,r0
	mov	7<<3,brg
	and	brg,r0,brg
	mov	brg,brg>>
	mov	brg,brg>>
	mov	brg,brg>>
	mov	dznr,mar
	mov	brg,mem
/*
 * Record the unibus address for this DZ-11
 */
	mov	csrget,mar
	mov	csr4,mem
	mov	mem,r0|mar++
	mov	csr5,mem
	mov	mem,r1|mar++
	mov	(3<<2)|NRQ,mem
/*
 * Calculate and save the TDR address
 */
	mov	tdrput,mar
	mov	6,brg
	add	brg,r0,mem|mar++
	adc	r1,mem|mar++
	mov	3<<2,mem|mar++
	mov	BYTE|OUT|NRQ,mem
/*
/*
 * Calculate and save the TCR address
 */
	mov	tcrput,mar
	mov	4,brg
	add	brg,r0,mem|mar++
	adc	r1,mem|mar++
	mov	3<<2,mem|mar++
	mov	BYTE|OUT|NRQ,mem
/*
 * Go to disp
 */
	br	disp
/*
 * Process a buffer-send command
 *
 * The format of a buffer-send command is:
 *
 *	csr3 - ((DZ number)<<3)|(line number)
 *	csr4 - low byte of buffer address
 *	csr5 - high byte of buffer address
 *	csr6 - character count
 *	csr7 - bits 17-16 of buffer address (left-adjusted)
 */
buffsend:
/*
 * If a base-in command has not been received yet for this DZ
 * then go to disp
 */
	mov	status,mar
	mov	mem,brg
	br7	1f
	br	disp
1:
/*
 * Extract the line number and look up the address of the
 * line-table entry
 */
	mov	7,r7
	mov	csr3,brg
	and	brg,r7,r7|mar
	mov	mem,r10|mar
/*
 * Move the character count to the line-table entry
 */
	mov	NCHNEW,brg
	add	brg,r10,mar
	mov	csr6,mem|mar++
/*
 * Copy the buffer address to the line-table entry
 */
	mov	BUFAD,brg
	add	brg,r10,mar
	mov	csr4,mem|mar++
	mov	csr5,mem|mar++
	mov	csr7,brg
	mov	0,brg>>
	mov	0,brg>>
	mov	0,brg>>
	mov	0,brg>>
	mov	0,brg>>
	mov	0,brg>>
	mov	brg,mem
/*
 * If the line is already active (state != 0) then go to disp
 */
	mov	STATE,brg
	add	brg,r10,mar
	mov	mem,r0
	dec	r0,-
	brc	disp
/*
 * Set state = 1
 */
	mov	1,mem
/*
 * Enable the line
 */
	CALL(enable)
	mov	r8,%mar
/*
 * Go to disp
 */
	br	disp
/*
 * Process an ioctl command
 *
 * The format of an ioctl command is:
 *
 *	csr3 - ((DZ number)<<3)|(line number)
 *	csr4 - new value for OFLAG
 *	csr5 - new values for DELAYS
 *
 */
ioctl:
/*
 * If a base-in command has not been received yet for this DZ
 * then go to disp
 */
	mov	status,mar
	mov	mem,brg
	br7	1f
	br	disp
1:
/*
 * Extract the line number and look up the address of the
 * line-table entry
 */
	mov	7,r7
	mov	csr3,brg
	and	brg,r7,r7|mar
	mov	mem,r10|mar
/*
 * Copy the stty flags to the lte (two bytes)
 */
	mov	OFLAG,brg
	add	brg,r10,mar
	mov	csr4,mem|mar++
	mov	csr5,mem
/*
 * Go to disp
 */
	br	disp
/*
 * Process an cmdin command
 *
 * The format of an cmdin command is:
 *
 *	csr3 - ((DZ number)<<3)|(line number)
 *	csr4 - command byte as described below
 *	csr5 - unused
 *	csr6 - character to be transmitted
 *	csr7 - unused
 *
 */

/*
 * The following bits describe the commands
 */
#define OFLUSH	(1<<1)	/* Flush output */
#define OSPND	(1<<2)	/* Suspend output */
#define ORSME	(1<<3)	/* Resume output */
#define SCHAR	(1<<4)	/* Send character in csr6 */

cmdin:
/*
 * If a base-in command has not been received yet for this DZ
 * then go to disp
 */
	mov	status,mar
	mov	mem,brg
	br7	1f
	br	disp
1:
/*
 * Extract the line number and look up the address of the
 * line-table entry
 */
	mov	7,r7
	mov	csr3,brg
	and	brg,r7,r7|mar
	mov	mem,r10|mar
	mov	csr4,r4
/*
 * Process commands
 */
	mov	OFLUSH,brg
	orn	brg,r4,-
	brz	1f
	br	2f
1:
	CALL(oflush)
	CALL(oqueue)
	mov	r8,%mar
	mov	csr4,r4
2:
/*
 * Setup for next two commands
 */
	mov	FLAGS1,brg
	add	brg,r10,mar
	mov	mem,r0
/*
 * See if suspend
 */
	mov	OSPND,brg
	orn	brg,r4,-
	brz	1f
	br	2f
1:
	mov	TTSTOP,brg
	or	brg,r0,mem
2:
	mov	ORSME,brg
	orn	brg,r4,-
	brz	1f
	br	2f
1:
/*
 * See if line was stopped
 */
	mov	~TTSTOP,brg
	or	brg,r0,-
	brz	1f
	br	2f
/*
 * Restart line
 */
1:
	and	brg,r0,mem
	CALL(enable)
	mov	r8,%mar
	mov	csr4,r4
2:
/*
 * See if character is to be sent
 */
	mov	SCHAR,brg
	orn	brg,r4,-
	brz	1f
	br	2f
1:
/*
 * Put character into LTE and set SNDEXP
 */
	mov	EXPCHR,brg
	add	brg,r10,mar
	mov	csr6,mem
	mov	FLAGS1,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	SNDEXP,brg
	or	brg,r0,mem
	CALL(enable)
	mov	r8,%mar
	mov	csr4,r4
2:
/*
 * Return to dispatcher
 */
	br	disp
/*
 * The 50-microsecond hardware timer has expired
 */
tick:
/*
 * Reset the 50-microsecond timer
 */
	mov	nprx,r0
	mov	~(BRQ|ACLO),brg
	and	brg,r0
	mov	PCLK,brg
	or	brg,r0,nprx
/*
 * The effect of the following code is to schedule a
 * csr check for a different DZ every 100 microseconds.
 * Since there are four possible DZ's, each DZ gets scheduled
 * for a csr check every 400 microseconds.  This is approximately
 * a factor of two better than we need for 9600 baud (one character
 * every 1042 microseconds).
 */
/*
 * Increment r15.  If the result is odd then go to disp.
 */
	inc	r15,r15|brg
	br0	disp
/*
 * Set the page register from bits 2-1 of r15.
 */
	mov	3<<1,brg
	and	brg,r15,brg
	mov	brg,brg>>
	mov	brg,r8|%mar
/*
 * If a base-in command has not been received yet for this DZ
 * then go to disp
 */
	mov	status,mar
	mov	mem,brg
	br7	1f
	br	disp
1:
/*
 * If a csr check has already been requested for this DZ
 * then go to delaycheck
 */
	br0	delaycheck
/*
 * Set a flag to indicate that a csr check has been requested
 */
	mov	1<<0,r1
	or	mem,r1,mem
/*
 * Insert the page for the current DZ at the end of the
 * csr-check-needed queue
 */
	mov	csrlink,mar
	mov	NIL,mem
	mov	r11,brg
	brz	1f
	mov	brg,%mar
	mov	r8,mem
	mov	r8,brg|%mar
	mov	brg,r11
	br	2f
1:
	mov	r8,brg
	mov	brg,r11
	mov	brg,r12
2:
delaycheck:
/*
 * Decrement the 16.7 msec timer.  If the result is 
 * non-negative then go to disp
 */
	mov	r8,%mar
	mov	clock1,mar
	mov	mem,r0
	dec	r0,mem
	brc	disp
/*
 * Reset the 16.7 msec timer
 */
	mov	35,mem
/*
 * The 16.7 msec timer has expired.  Therefore we need to decrement
 * the count field for each lte which is currently in the twait
 * state (delaying)
 */
/*
 * Initialize registers for loop which follows
 */
	mov	5,r5
	mov	7,r7|mar
/*
 * The following loop is executed once for each lte
 * in the page for the selected DZ.
 *
 * If we find a line which is in the twait state
 * then we decrement the count field of the lte.
 * If the result is negative then we put the line
 * back into state 1 and enable the line.
 */
delaych1:
/*
 * Get the address of the lte
 *
 * If state == 5 (twait) then go to delaych3
 *
 * Note that the following code assumes that STATE is the
 * first cell in the lte and that the memory-address
 * register (mar) has already been loaded from r7
 */
	mov	mem,r10|mar
	addn	mem,r5,-
	brz	delaych3
/*
 * Test for end of loop
 */
delaych2:
/*
 * Decrement the current entry number.  If the result is
 * non-negative then go to delaych1
 */
	dec	r7,r7|mar
	brc	delaych1
/*
 * Go to disp
 */
	br	disp
/*
 * We get here when we find a line with STATE == 5 (twait)
 */
delaych3:
/*
 * Decrement the count field of the lte.  If the result is
 * non-negative then go to delaych2
 */
	mov	COUNT,brg
	add	brg,r10,mar
	mov	mem,r0
	dec	r0,mem
	brc	delaych2
/*
 * The lte pointed to by r7 and r10 has completed the specified
 * delay.  Therefore we must remove it from the twait state and
 * put it back into state 1 (sending normal data).
 */
/*
 * Set state = 1 (sending normal data)
 */
	mov	STATE,brg
	add	brg,r10,mar
	mov	1,mem
/*
 * Enable the line
 */
	CALL(enable)
	mov	r8,%mar
/*
 * Go to delaych2
 */
	br	delaych2
/*
 * The following code checks for output character completions
 */
csrcheck:
/*
 * Set page register = page number of DZ at top of queue
 */
	mov	r12,brg
	mov	brg,r8|%mar
/*
 * Set up a unibus request for the current value of CSR
 */
	mov	csrget,mar
	mov	mem,ial|mar++
	mov	mem,iah|mar++
	mov	mem,npr
/*
 * Wait for the unibus request to complete.
 * If an error occurs then go to buserr.
 */
	buswait
/*
 * If there is an output data request then go to csrchk2
 */
	mov	idh,brg
	br7	csrchk2
/*
 * Clear the flag which indicates that a CSR check is needed
 */
	mov	status,mar
	mov	~(1<<0),brg
	mov	brg,r0
	and	mem,r0,mem
/*
 * Remove the current DZ from the csr-check-needed queue
 */
	mov	csrlink,mar
	mov	mem,r12
	brz	1f
	br	2f
1:
	mov	mem,r11
2:
/*
 * Go to disp
 */
	br	disp
/*
 * The DZ-11 has requested an output character.  Get the line
 * number and look up the address of the line-table entry.
 * Save the address of the line-table entry in register r10.
 */
csrchk2:
	mov	idh,brg
	mov	7,r7
	and	brg,r7,r7|mar
	mov	mem,r10|mar
/*
 * Get flags
 */
	mov	FLAGS1,brg
	add	brg,r10,mar
	mov	mem,r0
/*
 * See if there is an express character
 */
	mov	~SNDEXP,brg
	or	brg,r0,-
	brz	1f
	br	2f
/*
 * Send express character reguardless of state of line
 */
1:
	and	brg,r0,mem
	mov	EXPCHR,brg
	add	brg,r10,mar
	mov	mem,odl
	br	sendit
2:
/*
 * If TTSTOP is set then go to disable
 */
	mov	TTSTOP,brg
	orn	brg,r0,-
	brz	disable
/*
 * Get OFLAG into r6
 */
	mov	OFLAG,brg
	add	brg,r10,mar
	mov	mem,r6
/*
 * Get the current state of this line
 */
	mov	STATE,brg
	add	brg,r10,mar
	mov	mem,r0
/*
 * If state == 0 then go to disable
 * else if state == 1 then go to odrst1
 * else if state == 2 then go to odrst2
 * else if state == 3 then go to odrst3
 * else if state == 4 then go to odrst4
 * else if state == 5 then go to odrst5
 */
	mov	statesw,brg
	br	(add,brg,r0),%statesw
/*
 * Output data request with state == 1 (sending normal data)
 */
odrst1:
/*
 * If there is a saved byte to be sent then go to sendsave
 */
	mov	FLAGS,brg
	add	brg,r10,mar
	mov	mem,brg
	br4	sendsave
/*
 * If there are no characters remaining in the current
 * buffer for this line then go to odrst1a
 */
	mov	NCH,brg
	add	brg,r10,mar
	mov	mem,r0
	dec	r0,-
	brz	odrst1a
/*
 * Decrement the number of characters remaining in the current
 * buffer for this line
 */
	dec	r0
	mov	r0,mem
/*
 * If this was the last character queue the line
 */
	dec	r0,-
	brc	odrst1b
	CALL(oqueue)
	mov	r8,%mar
	br	odrst1b
/*
 * There are no more characters remaining in the current
 * buffer for this line
 */
odrst1a:
/*
 * If the host computer has not yet presented a new buffer then
 * go to nomore
 */
	mov	NCHNEW,brg
	add	brg,r10,mar
	mov	mem,r0
	dec	r0,-
	brz	nomore
	mov	0,mem
/*
 * Decrement the character count for the new output
 * buffer and recore the result as the character
 * count for the current buffer.
 */
	mov	NCH,brg
	add	brg,r10,mar
	dec	r0
	mov	r0,mem
/*
 * If there was only one character in the buffer queue the line
 */
	dec	r0,-
	brc	1f
	CALL(oqueue)
	mov	r8,%mar
1:
/*
 * If the buffer address is even then go to odrst1c
 */
	mov	BUFAD,brg
	add	brg,r10,mar
	mov	mem,brg
	br0	1f
	br	odrst1c
1:
/*
 * Clear the low-order bit of the buffer address
 */
	mov	1,r1
	xor	mem,r1,mem
/*
 * Fetch two bytes
 */
	mov	mem,ial|mar++
	mov	mem,iah|mar++
	mov	mem,r0
	mov	3,brg
	and	brg,r0
	asl	r0
	asl	r0
	mov	NRQ,brg
	or	brg,r0,npr
	buswait;
/*
 * Save the high-order byte as the current output character
 */
	mov	idh,odl
/*
 * Set buffer address += 2
 */
	mov	BUFAD,brg
	add	brg,r10,mar
	mov	2,r2
	add	mem,r2,mem|mar++
	mov	mem,r0
	adc	r0,mem|mar++
	mov	mem,r0
	adc	r0,mem
/*
 * Go to sendtest
 */
	br	sendtest
/*
 * The DZ has just started sending the last byte of the
 * current output buffer.  Call oflush which just
 * bookkeeps the lte and dispable the line.
 * The next buffer-send command will reactivate the line.
 */

nomore:
	CALL(oflush)
	mov	r8,%mar
	br	disable

/*
 * Output data request with non-empty buffer
 */
odrst1b:
/*
 * If there is a high-order byte to send then go to sendhigh
 */
	mov	FLAGS,brg
	add	brg,r10,mar
	mov	mem,brg
	br1	sendhigh
/*
 * We need to get another word of data for this line.
 */
odrst1c:
/*
 * Get the buffer address field of the line-table entry,
 * then set up a unibus request for the next two bytes.
 */
	mov	BUFAD,brg
	add	brg,r10,mar
	mov	mem,ial|mar++
	mov	mem,iah|mar++
	mov	mem,r0
	asl	r0
	asl	r0
	mov	NRQ,brg
	or	brg,r0,npr
/*
 * Wait for the unibus transfer to complete.
 * If an error occurs then go to buserr.
 */
	buswait
/*
 * Set the bit that says to send the high-order byte
 */
	mov	FLAGS,brg
	add	brg,r10,mar
	mov	SENDHIGH,brg
	mov	mem,r0
	or	brg,r0,mem|mar++
/*
 * Save the high-order data byte in the line-table entry
 */
	mov	HIBYTE,brg
	add	brg,r10,mar
	mov	idh,mem
/*
 * Move the low-order data byte to the output data register
 */
	mov	idl,odl
/*
 * Increment the buffer address
 */
	mov	BUFAD,brg
	add	brg,r10,mar
	mov	2,r2
	add	mem,r2,mem|mar++
	mov	mem,r2
	adc	r2,mem|mar++
	mov	mem,r2
	adc	r2,mem
/*
 * Go to sendtest
 */
	br	sendtest
/*
 * Prepare to send the high-order byte.
 */
sendhigh:
/*
 * Move the high-order data byte from the line-table entry 
 * to the output data register
 */
	mov	HIBYTE,brg
	add	brg,r10,mar
	mov	mem,odl
/*
 * Clear the flag that says to send the high-order byte
 */
	mov	FLAGS,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	~SENDHIGH,brg
	and	brg,r0,mem
/*
 * Go to sendtest
 */
	br	sendtest
/*
 * Prepare to send the byte in SAVECHAR
 */
sendsave:
/*
 * Clear the flag that says there is a saved byte to be sent
 */
	mov	~SENDSAVE,brg
	mov	mem,r0
	and	brg,r0,mem
/*
 * Move the saved byte into odl
 */
	mov	SAVECHAR,brg
	add	brg,r10,mar
	mov	mem,odl
/*
 * Go to sendtest
 */
	br	sendtest
/*
 * The following code acts as a filter for outgoing characters
 */
sendtest:
/*
 * Get output flags
 */
	mov	OFLAG,brg
	add	brg,r10,mar
	mov	mem,r6
/*
 * If output post-processing is not enabled then go to sendit
 */
	mov	OPOST,brg
	orn	brg,r6,-
	brz	1f
	br	sendit
1:
/*
 * Get the delays
 */
	mov	DELAYS,brg
	add	brg,r10,mar
	mov	mem,r5
/*
 * Get the output character
 */
	mov	odl,r0
/*
 * If the character is a control character then go to iscc
 */
	mov	040,brg
	sub	brg,r0,-
	brc	1f
	br	iscc
1:
/*
 * If the character is a DEL then go to sendit
 */
	mov	0177,brg
	addn	brg,r0,-
	brz	sendit
/*
 * Increment the column pointer
 */
	mov	ICOL,brg
	add	brg,r10,mar
	mov	mem,r1
	inc	r1,mem
/*
 * If OLCUC is set then translate lower case alpha to upper case
 */
	mov	OLCUC,brg
	orn	brg,r6,-
	brz	1f
	br	2f
1:
	mov	'a',brg
	sub	brg,r0,-
	brc	1f
	br	2f
1:
	mov	'z',brg
	addn	brg,r0,-
	brc	2f
	mov	~040,brg
	and	brg,r0
	mov	r0,odl
2:
/*
 * Go to sendit
 */
	br	sendit
/*
 * Process an ASCII control character
 */
iscc:
/*
 * If the character is a backspace then go to isbs;
 * else if the character is a horizontal tab then go to isht;
 * else if the character is a newline then go to isnl;
 * else if the character is a vertical tab then go to isvt;
 * else if the character is a form-feed then go to isff;
 * else if the character is a carriage return then go to iscr;
 * else go to sendit
 */
	mov	charsw,brg
	br	(add,brg,r0),%charsw
/*
 * Process a newline character
 */
isnl:
/*
 * If ONLRET is set then go to iscr2
 */
	mov	ONLRET,brg
	orn	brg,r6,-
	brz	iscr2
/*
 * If we are not expanding NL for this line then go to nlpart3
 */
	mov	ONLCR,brg
	orn	brg,r6,-
	brz	1f
	br	nlpart3
1:
/*
 * If column pointer == 0 and ONOCR is set then go to iscr2.
 * (We omit the carriage return in this case.)
 */
	mov	ICOL,brg
	add	brg,r10,mar
	mov	mem,r0
	dec	r0,-
	brc	1f
	mov	ONOCR,brg
	orn	brg,r6,-
	brz	iscr2
1:
/*
 * If this NL has already been expanded then go to nlpart2
 */
	mov	FLAGS,brg
	add	brg,r10,mar
	mov	mem,brg
	br0	nlpart2
/*
 * Set flags to indicate that there is a saved byte in SAVECHAR
 * and that the NL character has already been expanded
 */
	mov	SENDSAVE|NLEXP,brg
	mov	mem,r0
	or	brg,r0,mem
/*
 * Put a new-line character into SAVECHAR
 */
	mov	SAVECHAR,brg
	add	brg,r10,mar
	mov	NL,mem
/*
 * Put a carriage-return character into odl
 */
	mov	CR,brg
	mov	brg,odl
/*
 * Go to sendit
 */
	br	sendit
/*
 * Part two of newline character processing
 *
 * The generated carriage return has already been sent.
 *
 * At this point we just have to send the saved newline character
 * and generate the appropriate time delay, if any.
 */
nlpart2:
/*
 * Clear the flag which indicates that this particular newline
 * has already been expanded
 */
	mov	FLAGS,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	~NLEXP,brg
	and	brg,r0,mem
/*
 * Go to iscr2
 */
	br	iscr2
/*
 * Process a carriage-return character
 */
iscr:
/*
 * If translate-CR-to-NL is set then go to xmtnl
 */
	mov	OCRNL,brg
	orn	brg,r6,-
	brz	xmtnl
/*
 * If at col 0 and ONOCR is set do not send out carriage return
 */
	mov	ICOL,brg
	add	brg,r10,mar
	mov	mem,r2
	dec	r2
	brc	iscr2
	mov	ONOCR,brg
	orn	brg,r6,-
	brz	odrst1
/*
 * Entered here from isnl
 */
iscr2:
/*
 * Save column pointer
 */
	mov	ICOL,brg
	add	brg,r10,mar
	mov	mem,r2
/*
 * Set column pointer = 0
 */
	mov	0,mem
/*
 * Get delay type for carriage-return.
 * If delay type == 0 then go to sendit
 * else if delay type == 1 then go to cr1
 * else if delay type == 2 then go to cr2
 * else go to cr3
 */
	mov	CRDELAY,brg
	and	brg,r5,brg
	mov	0,brg>>
	br1	1f
	br0	cr1
	br	sendit
1:
	br0	cr3
	br	cr2
/*
 * Carriage-return delay type 1
 */
cr1:
/*
 * If OFILL is set then go to filltwo
 */
	mov	OFILL,brg
	orn	brg,r6,-
	brz	filltwo
/*
 * Set delay ticks = (icol>>4)+3
 */
	mov	017<<4,brg
	and	brg,r2,brg
	mov	brg,brg>>
	mov	brg,brg>>
	mov	brg,brg>>
	mov	brg,brg>>
	mov	3,r3
	add	brg,r3
/*
 * If delay > 6 then set delay = 6
 */
	mov	6,brg
	sub	brg,r3,-
	brc	1f
	mov	r3,brg
1:
/*
 * Go to delay
 */
	br	delay
/*
 * Carriage-return delay type 2
 */
cr2:
/*
 * If OFILL is set then go to fillfour
 */
	mov	OFILL,brg
	orn	brg,r6,-
	brz	fillfour
/*
 * Schedule a delay
 */
	mov	5,brg
/*
 * Go to delay
 */
	br	delay
/*
 * Carriage-return delay type 3
 */
cr3:
/*
 * If OFILL is set then go to fill40
 */
	mov	OFILL,brg
	orn	brg,r6,-
	brz	fill40
/*
 * Schedule a .15 second delay
 */
	mov	9,brg
/*
 * Go to delay
 */
	br	delay
/*
 * Translate carriage return to newline
 */
xmtnl:
	mov	NL,brg
	mov	brg,odl
	br	nlpart3
/*
 * Entered here from isnl when neither ONLRET nor ONLCR is set
 */
nlpart3:
/*
 * If newline delay is not enabled then go to sendit
 */
	mov	NLDELAY,brg
	orn	brg,r5,-
	brz	1f
	br	sendit
1:
/*
 * Schedule a .083 second delay
 */
	mov	5,brg
/*
 * Go to delay
 */
	br	delay
/*
 * Process a horizontal tab control character.
 */
isht:
/*
 * If we are expanding tabs into spaces then go to ht3
 */
	mov	HTDELAY,brg
	orn	brg,r5,-
	brz	ht3
/*
 * Save the column pointer
 */
	mov	ICOL,brg
	add	brg,r10,mar
	mov	mem,r2
/*
 * Update the column pointer
 */
	mov	mem,r1
	mov	7,brg
	or	brg,r1
	inc	r1,mem
/*
 * Get the delay type for horizontal tabs.  If the delay type is
 * one then go to ht1; else if the delay type is two then go to ht2;
 * else go to sendit
 */
	mov	HTDELAY,brg
	and	brg,r5,brg
	mov	0,brg>>
	mov	0,brg>>
	mov	0,brg>>
	br0	ht1
	br1	ht2
	br	sendit
/*
 * HT delay type 1
 */
ht1:
/*
 * If OFILL is set then go to filltwo
 */
	mov	OFILL,brg
	orn	brg,r6,-
	brz	filltwo
/*
 * Calculate delay ticks = 1 - (icol|~7)
 */
	mov	7,brg
	orn	brg,r2,brg
	mov	1,r1
	sub	brg,r1
/*
 * If delay < 5 then go to sendit
 */
	mov	5,brg
	sub	brg,r1,-
	brc	sendit
	mov	r1,brg
/*
 * Go to delay
 */
	br	delay
/*
 * HT delay type 2
 */
ht2:
/*
 * If OFILL is set then go to filltwo
 */
	mov	OFILL,brg
	orn	brg,r6,-
	brz	filltwo
/*
 * Schedule a .1 second delay
 */
	mov	6,brg
/*
 * Go to delay
 */
	br	delay
/*
 * HT delay type 3:  Expand horizontal tab into spaces
 */
ht3:
/*
 * Calculate the number of spaces needed
 *
 * The number is calculated by evaluating 7 & ~icol
 */
	mov	ICOL,brg
	add	brg,r10,mar
	mov	0,r0
	orn	mem,r0
	mov	7,brg
	and	brg,r0
/*
 * Update the column pointer
 */
	mov	mem,r1
	or	brg,r1
	inc	r1,mem
/*
 * Save the count of the number of spaces needed
 */
	mov	COUNT,brg
	add	brg,r10,mar
	mov	r0,mem
/*
 * Put a space into odl
 */
	mov	SP,brg
	mov	brg,odl
/*
 * Set state = 3 (expanding horizontal tab)
 */
	mov	STATE,brg
	add	brg,r10,mar
	mov	3,mem
/*
 * Go to sendit
 */
	br	sendit
/*
 * Process a backspace character
 */
isbs:
/*
 * If the column pointer is not already zero then decrement it
 */
	mov	ICOL,brg
	add	brg,r10,mar
	mov	mem,r1
	dec	r1
	brz	1f
	mov	r1,mem
1:
/*
 * If backspace delays have not been requested then go to sendit
 */
	mov	BSDELAY,brg
	orn	brg,r5,-
	brz	1f
	br	sendit
1:
/*
 * If OFILL is set then go to fillone
 */
	mov	OFILL,brg
	orn	brg,r6,-
	brz	fillone
/*
 * Schedule a .05 second delay
 */
	mov	3,brg
/*
 * Go to delay
 */
	br	delay
/*
 * Process a vertical-tab character
 */
isvt:
/*
 * If form-feed delays have not been requested then go to sendit
 */
	mov	VTDELAY,brg
	orn	brg,r5,-
	brz	1f
	br	sendit
1:
/*
 * Schedule a two-second delay
 */
	mov	127,brg
/*
 * Go to delay
 */
	br	delay
/*
 * Process a form-feed character
 */
isff:
/*
 * If form-feed delays have not been requested then go to sendit
 */
	mov	FFDELAY,brg
	orn	brg,r5,-
	brz	1f
	br	sendit
1:
/*
 * Schedule a two-second delay
 */
	mov	127,brg
/*
 * Go to delay
 */
	br	delay
/*
 * Schedule one fill character
 */
fillone:
	mov	1,brg
	br	fdelay
/*
 * Schedule two fill characters
 */
filltwo:
	mov	2,brg
	br	fdelay
/*
 * Schedule four fill characters
 */
fillfour:
	mov	4,brg
	br	fdelay
/*
 * Schedule the appropriate number of fill characters for a
 * Teletype 40 printer or a GE Terminet 1200
 */
fill40:
	mov	36,brg
	sub	brg,r2,brg
	brc	sendit
	mov	0,r0
	sub	brg,r0,brg
	br	fdelay
/*
 * Schedule a time delay on a given line
 *
 * The amount of delay needed has already been calculated.
 * Register brg contains the number of 16.7 msec ticks needed
 * for the delay.  The character which requires time fill has not
 * yet been passed to the DZ11.
 */
delay:
/*
 * If delay == 0 then go to sendit
 */
	mov	brg,r1
	dec	r1
	brz	sendit
/*
 * Add 6 to the delay count and save the result in the LTE
 *
 * The reason for adding 6 to the delay count is roughly as follows:
 *
 * 6/60 = .1 second, which is one character time at 110 baud;
 * this compensates for the double-buffering in the DZ11;
 * at speeds greater than 110 baud it overcompensates, but this is
 * traditional
 */
	mov	COUNT,brg
	add	brg,r10,mar
	mov	6,brg
	add	brg,r1,mem
/*
 * Set STATE = 2
 */
	mov	STATE,brg
	add	brg,r10,mar
	mov	2,mem
/*
 * Go to sendit
 */
	br	sendit
/*
 * Schedule a time delay on a given line
 *
 * The amount of delay needed has already been calculated.
 * Register brg contains the number of character times needed
 * for the delay.  The character which requires time fill has not
 * yet been passed to the DZ11.
 *
 * This code differs from the delay code above by using fill
 * characters rather than timed pauses
 */
fdelay:
/*
 * If delay == 0 then go to sendit
 */
	mov	brg,r1
	dec	r1,-
	brz	sendit
/*
 * Put the delay count in the lte
 */
	mov	COUNT,brg
	add	brg,r10,mar
	mov	r1,mem
/*
 * Set STATE = 4  (Sending fill characters)
 */
	mov	STATE,brg
	add	brg,r10,mar
	mov	4,mem
/*
 * Go to sendit
 */
	br	sendit
/*
 * Output data request with state == 2 (sending a character that needs 
 * time fill)
 *
 * Note:  The line was put into state 2 when we passed the
 * character that needs time fill to the DZ11.  Since the DZ11
 * is double-buffered, we get another output data request when
 * the DZ11 actually starts sending the character that needs
 * time fill.
 * At this point we turn off the enable bit and put the lte into the
 * twait state.
 *
 * Note that, because of double-buffering in the DZ11, the wait
 * time is overlapped with the time required to send the character
 * that needs time fill.
 */
odrst2:
/*
 * Put this lte into the twait state
 */
	mov	r10,mar
	mov	5,mem
/*
 * Go to disable
 */
	br	disable
/*
 * Output data request with state == 3 (expanding horizontal tab)
 */
odrst3:
/*
 * Decrement the count field of the lte.  If the result is
 * negative then go to resume
 */
	mov	COUNT,brg
	add	brg,r10,mar
	mov	mem,r0
	dec	r0,mem
	brz	resume
/*
 * Put a space character into odl
 */
	mov	SP,brg
	mov	brg,odl
/*
 * Go to sendit
 */
	br	sendit
/*
 * Output data request with state == 4 (sending fill characters)
 */
odrst4:
/*
 * Decrement the count field of the lte.  If the result is
 * negative then go to resume
 */
	mov	COUNT,brg
	add	brg,r10,mar
	mov	mem,r0
	dec	r0,mem
	brz	resume
/*
 * Put a fill character into odl
 */
	mov	DEL,brg
	mov	brg,odl
	mov	OFDEL,brg
	orn	brg,r6,-
	brz	1f
	mov	NUL,brg
	mov	brg,odl
1:
/*
 * Go to sendit
 */
	br	sendit
/*
 * Output data request with state == 5 (twait)
 *
 * This may happen if an express character has been sent while in this state
 */
odrst5:
	br	disable
/*
 * The following code is used to restart a line which has been
 * delaying or expanding a horizontal tab character
 */
resume:
/*
 * Set state = 1 (sending normal data)
 */
	mov	STATE,brg
	add	brg,r10,mar
	mov	1,mem
/*
 * Go to odrst1
 */
	br	odrst1
/*
 * Send one character on the specified line.  The character to be
 * sent has already been placed in odl
 */
sendit:
/*
 * Set up a unibus request to put one byte into TBUF
 */
	mov	tdrput,mar
	mov	mem,oal|mar++
	mov	mem,oah|mar++
	mov	nprx,r0
	mov	~(BRQ|ACLO),brg
	and	brg,r0
	or	mem,r0,nprx|mar++
	mov	mem,npr
/*
 * Wait for the unibus transfer to complete.
 * If an error occurs then go to buserr.
 */
	buswait
/*
 * Go to disp
 */
	br	disp

/*
 * Subroutine to enable a line
 */
enable:
/*
 * Set up a unibus request to enable the line
 */
	mov	r8,%mar
	mov	enblbits,brg
	add	brg,r7,mar
	mov	mem,r0
	mov	actline,mar
	or	mem,r0,mem
	mov	mem,odl
	mov	tcrput,mar
	mov	mem,oal|mar++
	mov	mem,oah|mar++
	mov	nprx,r0
	mov	~(BRQ|ACLO),brg
	and	brg,r0
	or	mem,r0,nprx|mar++
	mov	mem,npr
/*
 * Wait for the unibus transfer to complete.
 */
	buswait
/*
 * Return to caller
 */
	RETURN
/*
 * The following code is used to turn off the transmit-enable bit 
 * at the beginning of a timed delay period or when there are no
 * characters to transmit
 */
disable:
/*
 * Clear the KMC copy of the transmit-enable bit for this line
 * and set up a unibus request to clear the corresponding bit
 * in the DZ11
 */
	mov	r8,%mar
	mov	enblbits,brg
	add	brg,r7,mar
	mov	mem,r0
	mov	actline,mar
	and	mem,r0
	xor	mem,r0,mem
	mov	mem,odl
	mov	tcrput,mar
	mov	mem,oal|mar++
	mov	mem,oah|mar++
	mov	nprx,r0
	mov	~(BRQ|ACLO),brg
	and	brg,r0
	or	mem,r0,nprx|mar++
	mov	mem,npr
/*
 * Wait for the unibus transfer to complete.
 */
	buswait
/*
 * Go to disp
 */
	br	disp

/*
 * This two entry point subroutine is called at oflush to
 * flush any current output regardless of the current state
 * of the line.  Oflush is also called when the last character
 * of normal output has been sent out and the dz is requesting
 * another character.  This simply bookkeeps the LTE.  The
 * entry point oqueue is called when the last character in
 * the current output buffer is used so the host may reload
 * another buffer before the dz requests another character.
 */
oflush:
	mov	r8,%mar
/*
 * Clear output flags
 */
	mov	FLAGS,brg
	add	brg,r10,mar
	mov	~(SENDSAVE|SENDHIGH|NLEXP),brg
	mov	brg,r0
	and	mem,r0,mem
	mov	FLAGS1,brg
	add	brg,r10,mar
	mov	mem,r0
	mov	~(TTSTOP|PREVNB),brg
	and	brg,r0,mem
	mov	NCH,brg
	add	brg,r10,mar
	mov	0,mem
	mov	NCHNEW,brg
	add	brg,r10,mar
	mov	0,mem
/*
 * Set state = 0
 */
	mov	STATE,brg
	add	brg,r10,mar
	mov	0,mem
	RETURN
/*
 * Queue the output buffer
 */
oqueue:
	mov	r8,%mar
/*
 * Set the buffer-completion bit for this line
 */
	mov	enblbits,brg
	add	brg,r7,mar
	mov	mem,r0
	mov	buffcomp,mar
	or	mem,r0,mem
/*
 * If a report to the driver has already been requested for this
 * DZ then return
 */
	mov	status,mar
	mov	mem,brg
	br1	2f
/*
 * Set a flag to indicate that a report to the driver has been
 * requested
 */
	mov	1<<1,r2
	or	mem,r2,mem
/*
 * Insert the page for the current DZ at the end of the
 * driver-report-needed queue
 */
	mov	rptlink,mar
	mov	NIL,mem
	mov	r13,brg
	brz	1f
	mov	brg,%mar
	mov	r8,mem
	mov	r8,brg|%mar
	mov	brg,r13
	br	2f
1:
	mov	r8,brg
	mov	brg,r13
	mov	brg,r14
2:
	RETURN

/*
 * The unibus transfer failed to complete within a reasonable time
 */
buserr:
/*
 * Clear the non-existent memory bit
 */
	mov	nprx,r0
	mov	~(BRQ|ACLO|NEM),brg
	and	brg,r0,nprx
/*
 * Set a flag to indicate that a bus error has occurred
 */
	mov	status,mar
	mov	mem,brg
	mov	1<<5,brg
	mov	brg,r0
	or	mem,r0,mem
/*
 * If a report to the driver has already been requested for this
 * DZ then go to notlast
 */
	mov	status,mar
	mov	mem,brg
	br1	disp
/*
 * Set a flag to indicate that a report to the driver has been
 * requested
 */
	mov	1<<1,r2
	or	mem,r2,mem
/*
 * Insert the page for the current DZ at the end of the
 * driver-report-needed queue
 */
	mov	rptlink,mar
	mov	NIL,mem
	mov	r13,brg
	brz	1f
	mov	brg,%mar
	mov	r8,mem
	mov	r8,brg|%mar
	mov	brg,r13
	br	2f
1:
	mov	r8,brg
	mov	brg,r13
	mov	brg,r14
2:
/*
 * Go to disp
 */
	br	disp
/*
 * Error loops for debugging
 */
e1:
	mov	NIL,brg
	mov	brg,r15
	br	.

endseg: