swapmap[SMAPSIZ];
struct callo callout[NCALL];
struct mount mount[NMOUNT];

struct	cblock	cfree[NCLIST];

#include "../hd/var.h"
struct var v {
	NBUF,
	NCALL,
	NINODE,
	&inode[NINODE],
	NFILE,
	&file[NFILE],
	NMOUNT,
	&mount[NMOUNT],
	NPROC,
	&proc[1],
	NTEXT,
	&text[NTEXT],
	NCLIST,
};

char	pwbname[9] "pwbname";
de "../hd/proc.h"
struct	proc	proc[NPROC];	/* process table */

#include "../hd/text.h"
struct	text text[NTEXT];	/* text table */

#include "../hd/systm.h"
struct map coremap[CMAPSIZ];
struct map /*
 * Random set of variables used by more than one routine.
 */
struct map {
	int	m_size;
	char	*m_addr;
};
struct map coremap[];	/* core allocation map */
struct map swapmap[];	/* swap allocation map */
int	*rootdir;		/* pointer to inode of root directory */
int	*runq;			/* head of linked list of running processes */
int	cputype;		/* type of cpu =40, 45, or 70 */
int	lbolt;			/* time of day in 60th not in time */
long	time;			/* time in sec from 1970 */
long	tout;			/* time of day of next sleep */
/*
 * The callout structure is for a routine arranging to be
 *  called by the clock interrupt (clock.c) with a specified argument,
 * in a specified amount of time.
 * Used, for example, to time tab delays on typewriters.
 */
struct	callo
{
	int	c_time;		/* incremental time */
	int	c_arg;		/* argument to routine */
	int	(*c_func)();	/* routine */
} callout[];
/*
 * Mount structure.
 * One allocated on every mount.
 * Used to find the super block.
 */
struct	mount
{
	int	m_dev;		/* device mounted */
	int	*m_bufp;	/* pointer to superblock */
	int	*m_inodp;	/* pointer to mounted on inode */
} mount[];

int	mpid;			/* generic for unique process id's */
char	runin;			/* scheduling flag */
char	runout;			/* scheduling flag */
char	runrun;			/* scheduling flag */
char	curpri;			/* more scheduling */
int	maxmem;			/* actual max memory per process */
int	*lks;			/* pointer to clock device */
int	rootdev;		/* dev of root see conf.c */
int	swapdev;		/* dev of swap see conf.c */
int	swplo;			/* block number of swap space */
int	nswap;			/* size of swap space */
int	updlock;		/* lock for sync */
int	rablock;		/* block to be read ahead */
char	regloc[];		/* locs. of saved user registers (trap.c) */
char	pwbname[];
/* scheduling flag */
char	runrun;			/* scheduling flag */
char	curpri;			/* more scheduling */
int	maxmem;			/* actual max memory per process */
int	*lks;			/* pointer to clock device */
int	rootdev;		/* dev of root see conf.c */
int	swapdev;		/* dev of swap see conf.c */
int	swplo;			/* block number of swap space */
in/*
 * Text structure.
 * One allocated per pure procedure on swap device.
 * Manipulated by text.c
 */
struct text
{
	int	x_daddr;	/* disk address of segment */
	int	x_caddr;	/* core address, if loaded */
	int	x_size;		/* size (*64) */
	int	*x_iptr;	/* inode of prototype */
	char	x_count;	/* reference count */
	char	x_ccount;	/* number of loaded references */
	char	x_flag;		/* traced, written flags */
};

extern struct text text[];

#define	XWRIT	02		/* Text written into, must swap out */
#define	XLOAD	04		/* Currently being read from file */
#define	XLOCK	010		/* Being swapped in or out */
#define	XWANT	020		/* Wanted for swapping */
/* disk address of segment */
	int	x_caddr;	/* core address, if loaded */
	int	x_size;		/* size (*64) */
	int	*x_iptr;	/* inode of prototype */
	char	x_count;	/* reference count */
	char	x_ccount;	/* number of loaded references */
	char	x_flag;		/* traced, written flags */
};

extern struct text text[];

#define	XWRIT	02		/* Text written into, must swap out */
#define	XLOAD	04		/*
 * A clist structure is the head of a linked list queue of characters.
 * The characters are stored in 4-word
 * blocks containing a link and 6 characters.
 * The routines getc and putc (m45.s or m40.s)
 * manipulate these structures.
 */
struct clist
{
	int	c_cc;		/* character count */
	int	c_cf;		/* pointer to first block */
	int	c_cl;		/* pointer to last block */
};

/*
 * A tty structure is needed for each UNIX character device that
 * is used for normal terminal IO.
 * The routines in tty.c handle the common code associated with
 * these structures.
 * The definition and device dependent code is in each driver.
 */
struct tty
{
	struct	clist t_rawq;	/* input chars right off device */
	struct	clist t_canq;	/* input chars after erase and kill */
	struct	clist t_outq;	/* output list to device */
	int	t_flags;	/* mode, settable by stty call */
	int	*t_addr;	/* device address (register or startup fcn) */
	char	t_delct;	/* number of delimiters in raw q */
	char	t_col;		/* printing column of device */
	char	t_erase;	/* erase character */
	char	t_kill;		/* kill character */
	char	t_state;	/* internal state, not visible externally */
	char	t_char;		/* character temporary */
	int	t_speeds;	/* output+input line speed */
	int	t_pgrp;		/* process group name */
};

/*
 * The actual structure of a clist block manipulated by
 * getc and putc (mch.s)
 */
struct cblock {
	struct cblock *c_next;
	char info[6];
};

struct ttydma {
	char	c[8];
};

#define	TTIPRI	10
#define	TTOPRI	20

#define	CERASE	'#'		/* default special characters */
#define	CEOT	004
#define	CKILL	'@'
#define	CQUIT	034		/* FS, cntl shift L */
#define	CINTR	0177		/* DEL */

/* limits */
#define	TTHIWAT	100
#define	TTLOWAT	50
#define	TTYHOG	256

/* modes */
#define	HUPCL	01
#define	XTABS	02
#define	LCASE	04
#define	ECHO	010
#define	CRMOD	020
#define	RAW	040
#define	ODDP	0100
#define	EVENP	0200
#define	NLDELAY	001400
#define	TBDELAY	006000
#define	CRDELAY	030000
#define	VTDELAY	040000

/* Hardware bits */
#define	DONE	0200
#define	IENABLE	0100

/* Internal state bits */
#define	TIMEOUT	01		/* Delay timeout in progress */
#define	WOPEN	02		/* Waiting for open to complete */
#define	ISOPEN	04		/* Device is open */
#define	SSTART	010		/* Has special start routine at addr */
#define	CARR_ON	020		/* Software copy of carrier-present */
#define	BUSY	040		/* Output in progress */
#define	ASLEEP	0100		/* Wakeup when output done */
fine	TBDELAY	006000
#define	CRDELAY	030000
#define	VTDELAY	040000

/* Hardware bits */
#define	DONE	0200
#define	IENABLE	0100

/* Internal sta/*
 * The user structure.
 * One allocated per process.
 * Contains all per process data that doesn't need to be referenced
 * while the process is swapped.
 * The user block is USIZE*64 bytes long; resides at virtual kernel
 * loc 140000; contains the system stack per user; is cross referenced
 * with the proc structure for the same process.
 */
struct user
{
	int	u_rsav[2];		/* save r5,r6 when exchanging stacks */
	int	u_fsav[25];		/* save fp registers */
					/* rsav and fsav must be first in structure */
	char	u_segflg;		/* IO flag: 0:user D; 1:system; 2:user I */
	char	u_error;		/* return error code */
	char	u_uid;			/* effective user id */
	char	u_gid;			/* effective group id */
	char	u_ruid;			/* real user id */
	char	u_rgid;			/* real group id */
	int	u_procp;		/* pointer to proc structure */
	char	*u_base;		/* base address for IO */
	char	*u_count;		/* bytes remaining for IO */
	char	*u_offset[2];		/* offset in file for IO */
	int	*u_cdir;		/* pointer to inode of current directory */
	char	u_dbuf[DIRSIZ];		/* current pathname component */
	char	*u_dirp;		/* current pointer to inode */
	struct	{			/* current directory entry */
		int	u_ino;
		char	u_name[DIRSIZ];
	} u_dent;
	int	*u_pdir;		/* inode of parent directory of dirp */
	int	u_uisa[16];		/* prototype of segmentation addresses */
	int	u_uisd[16];		/* prototype of segmentation descriptors */
	int	u_ofile[NOFILE];	/* pointers to file structures of open files */
	int	u_arg[5];		/* arguments to current system call */
	int	u_tsize;		/* text size (*64) */
	int	u_dsize;		/* data size (*64) */
	int	u_ssize;		/* stack size (*64) */
	int	u_sep;			/* flag for I and D separation */
	int	u_qsav[2];		/* label variable for quits and interrupts */
	int	u_ssav[2];		/* label variable for swapping */
	int	u_signal[NSIG];		/* disposition of signals */
	long	u_utime;		/* this process user time */
	long	u_stime;		/* this process system time */
	long	u_cutime;		/* sum of childs' utimes */
	long	u_cstime;		/* sum of childs' stimes */
	int	*u_ar0;			/* address of users saved R0 */
	int	u_prof[4];		/* profile arguments */
	char	u_intflg;		/* catch intr from sys */
	int	u_ttyp;			/* controlling tty pointer */
	int	u_ttyd;			/* controlling tty dev */
	struct {			/* header of executable file */
		int	ux_mag;		/* magic number */
		int	ux_tsize;	/* text size */
		int	ux_dsize;	/* data size */
		int	ux_bsize;	/* bss size */
		int	ux_ssize;	/* symbol table size */
		int	ux_entloc;	/* entry location */
	} u_exdata;
	int	u_udata[16];		/* user log data */
					/* kernel stack per user
					 * extends from u + USIZE*64
					 * backward not to reach here
					 */
};
extern u;

/* u_error codes */
#define	EPERM	1
#define	ENOENT	2
#define	ESRCH	3
#define	EINTR	4
#define	EIO	5
#define	ENXIO	6
#define	E2BIG	7
#define	ENOEXEC	8
#define	EBADF	9
#define	ECHILD	10
#define	EAGAIN	11
#define	ENOMEM	12
#define	EACCES	13
#define	EFAULT	14
#define	ENOTBLK	15
#define	EBUSY	16
#define	EEXIST	17
#define	EXDEV	18
#define	ENODEV	19
#define	ENOTDIR	20
#define	EISDIR	21
#define	EINVAL	22
#define	ENFILE	23
#define	EMFILE	24
#define	ENOTTY	25
#define	ETXTBSY	26
#define	EFBIG	27
#define	ENOSPC	28
#define	ESPIPE	29
#define	EROFS	30
#define	EMLINK	31
#define	EPIPE	32
H	3
#define	EINTR	4
#define	EIO	5
#define	ENXIO	6
#define	E2BIG	7
#define	ENOEXEC	8
#define	EBADF	9
#define	ECHILD	10
#define	EAGAIN	11
#define	ENOMEM	12
#define	EACCES	13
#define	EFAULT	14
#define	ENOTBLK	15
#define	EBUSY	16
#define	EEXIST	17
#define	EXDEV	18
#define	ENODEV	19
#define	ENOTDIR	20
#define	EISDIR	21
#define	EINVAL	22
#define	ENFILE	23
#defstruct var {
	int	v_buf;
	int	v_call;
	int	v_inode;
	char *	ve_inode;
	int	v_file;
	char *	ve_file;
	int	v_mount;
	char *	ve_mount;
	int	v_proc;
	char *	ve_proc;
	int	v_text;
	char *	ve_text;
	int	v_clist;
};
extern struct var v;
ENOEXEC	8
#define	EBADF	9
#define	ECHILD	10
#define	EAGAIN	11
#define	ENOMEM	12
#define	EACCES	13
#define	EFAULT	14
#define	ENOTBLK	15
#define	EBUSY	16
#define	EEXIST	17
#define	EXDEV	18
#define	ENODEV	19
#define	ENOTDIR	20
#define	EISDIR	21
#define	EINVAL	22
#define	ENFILE	23
#def#
#include "../hd/param.h"
#include "../hd/user.h"
#include "../hd/buf.h"
#include "../hd/conf.h"
#include "../hd/systm.h"
#include "../hd/var.h"

/*
 * Declarations of the tables for the magtape devices;
 * see bdwrite.
 */
int	tmtab, httab;

/*
 * The following several routines allocate and free
 * buffers with various side effects.  In general the
 * arguments to an allocate routine are a device and
 * a block number, and the value is a pointer to
 * to the buffer header; the buffer is marked "busy"
 * so that no on else can touch it.  If the block was
 * already in core, no I/O need be done; if it is
 * already busy, the process waits until it becomes free.
 * The following routines allocate a buffer:
 *	getblk
 *	bread
 *	breada
 * Eventually the buffer must be released, possibly with the
 * side effect of writing it out, by using one of
 *	bwrite
 *	bdwrite
 *	bawrite
 *	brelse
 */

/*
 * Read in (if necessary) the block and return a buffer pointer.
 */
bread(dev, blkno)
{
	register struct buf *rbp;

	rbp = getblk(dev, blkno);
	if (rbp->b_flags&B_DONE)
		return(rbp);
	rbp->b_flags =| B_READ;
	(*bdevsw[major(dev)].d_strategy)(rbp);
	iowait(rbp);
	return(rbp);
}

/*
 * Read in the block, like bread, but also start I/O on the
 * read-ahead block (which is not allocated to the caller)
 */
breada(adev, blkno, rablkno)
{
	register struct buf *rbp, *rabp;
	register int dev;

	dev = adev;
	rbp = 0;
	if (!incore(dev, blkno)) {
		rbp = getblk(dev, blkno);
		if ((rbp->b_flags&B_DONE) == 0) {
			rbp->b_flags =| B_READ;
			(*bdevsw[major(adev)].d_strategy)(rbp);
		}
	}
	if (rablkno && bfreelist.b_wcount>1 && !incore(dev, rablkno)) {
		rabp = getblk(dev, rablkno);
		if (rabp->b_flags & B_DONE)
			brelse(rabp);
		else {
			rabp->b_flags =| B_READ|B_ASYNC;
			(*bdevsw[major(adev)].d_strategy)(rabp);
		}
	}
	if (rbp==0)
		return(bread(dev, blkno));
	iowait(rbp);
	return(rbp);
}

/*
 * Write the buffer, waiting for completion.
 * Then release the buffer.
 */
bwrite(bp)
struct buf *bp;
{
	register struct buf *rbp;
	register flag;

	rbp = bp;
	flag = rbp->b_flags;
	rbp->b_flags =& ~(B_READ | B_DONE | B_ERROR | B_DELWRI | B_AGE);
	(*bdevsw[major(rbp->b_dev)].d_strategy)(rbp);
	if ((flag&B_ASYNC) == 0) {
		iowait(rbp);
		brelse(rbp);
	} else if ((flag&B_DELWRI)==0)
		geterror(rbp); else
		rbp->b_flags =| B_AGE;
}

/*
 * Release the buffer, marking it so that if it is grabbed
 * for another purpose it will be written out before being
 * given up (e.g. when writing a partial block where it is
 * assumed that another write for the same block will soon follow).
 * This can't be done for magtape, since writes must be done
 * in the same order as requested.
 */
bdwrite(bp)
struct buf *bp;
{
	register struct buf *rbp;
	register struct devtab *dp;

	rbp = bp;
	dp = bdevsw[major(rbp->b_dev)].d_tab;
	if (dp == &tmtab || dp == &httab)
		bawrite(rbp);
	else {
		rbp->b_flags =| B_DELWRI | B_DONE;
		brelse(rbp);
	}
}

/*
 * Release the buffer, start I/O on it, but don't wait for completion.
 */
bawrite(bp)
struct buf *bp;
{
	register struct buf *rbp;

	rbp = bp;
	if(bfreelist.b_wcount>4)
		rbp->b_flags =| B_ASYNC;
	bwrite(rbp);
}

/*
 * release the buffer, with no I/O implied.
 */
brelse(bp)
struct buf *bp;
{
	register struct buf *rbp, **backp;
	register int sps;

	rbp = bp;
	if (rbp->b_flags&B_WANTED)
		wakeup(rbp);
	if (bfreelist.b_flags&B_WANTED) {
		bfreelist.b_flags =& ~B_WANTED;
		wakeup(&bfreelist);
	}
	if (rbp->b_flags&B_ERROR) {
		rbp->b_dev =| 0200;
		rbp->b_flags =& ~(B_ERROR|B_DELWRI);
	}
	sps = PS->integ;
	spl6();
	if(rbp->b_flags & B_AGE) {
		backp = &bfreelist.av_forw;
		(*backp)->av_back = rbp;
		rbp->av_forw = *backp;
		*backp = rbp;
		rbp->av_back = &bfreelist;
	} else {
		backp = &bfreelist.av_back;
		(*backp)->av_forw = rbp;
		rbp->av_back = *backp;
		*backp = rbp;
		rbp->av_forw = &bfreelist;
	}
	rbp->b_flags =& ~(B_WANTED|B_BUSY|B_ASYNC|B_AGE);
	bfreelist.b_wcount++;
	PS->integ = sps;
}

/*
 * See if the block is associated with some buffer
 * (mainly to avoid getting hung up on a wait in breada)
 */
incore(adev, blkno)
{
	register int dev;
	register struct buf *bp;
	register struct devtab *dp;

	dev = adev;
	dp = bdevsw[major(adev)].d_tab;
	for (bp=dp->b_forw; bp != dp; bp = bp->b_forw)
		if (bp->b_blkno==blkno && bp->b_dev==dev)
			return(bp);
	return(0);
}

/*
 * Assign a buffer for the given block.  If the appropriate
 * block is already associated, return it; otherwise search
 * for the oldest non-busy buffer and reassign it.
 * When a 512-byte area is wanted for some random reason
 * (e.g. during exec, for the user arglist) getblk can be called
 * with device NODEV to avoid unwanted associativity.
 */
getblk(dev, blkno)
{
	register struct buf *bp;
	register struct devtab *dp;

    loop:
	if (dev < 0)
		dp = &bfreelist;
	else {
		if (major(dev) >= bdevcnt)
			panic("blkdev");
		dp = bdevsw[major(dev)].d_tab;
		if(dp == NULL)
			panic("devtab");
		for (bp=dp->b_forw; bp != dp; bp = bp->b_forw) {
			if (bp->b_blkno!=blkno || bp->b_dev!=dev)
				continue;
			spl6();
			if (bp->b_flags&B_BUSY) {
				bp->b_flags =| B_WANTED;
				sleep(bp, PRIBIO+1);
				spl0();
				goto loop;
			}
			spl0();
			notavail(bp);
			return(bp);
		}
	}
	spl6();
	if (bfreelist.av_forw == &bfreelist) {
		bfreelist.b_flags =| B_WANTED;
		sleep(&bfreelist, PRIBIO+1);
		spl0();
		goto loop;
	}
	spl0();
	notavail(bp = bfreelist.av_forw);
	if (bp->b_flags & B_DELWRI) {
		bp->b_flags =| B_ASYNC;
		bwrite(bp);
		goto loop;
	}
	bp->b_flags = B_BUSY;
	bp->b_back->b_forw = bp->b_forw;
	bp->b_forw->b_back = bp->b_back;
	bp->b_forw = dp->b_forw;
	bp->b_back = dp;
	dp->b_forw->b_back = bp;
	dp->b_forw = bp;
	bp->b_dev = dev;
	bp->b_blkno = blkno;
	return(bp);
}

/*
 * Wait for I/O completion on the buffer; return errors
 * to the user.
 */
iowait(bp)
struct buf *bp;
{
	register struct buf *rbp;

	rbp = bp;
	spl6();
	while ((rbp->b_flags&B_DONE)==0)
		sleep(rbp, PRIBIO);
	spl0();
	geterror(rbp);
}

/*
 * Unlink a buffer from the available list and mark it busy.
 * (internal interface)
 */
notavail(bp)
struct buf *bp;
{
	register struct buf *rbp;
	register int sps;

	rbp = bp;
	sps = PS->integ;
	spl6();
	rbp->av_back->av_forw = rbp->av_forw;
	rbp->av_forw->av_back = rbp->av_back;
	rbp->b_flags =| B_BUSY;
	bfreelist.b_wcount--;
	PS->integ = sps;
}

/*
 * Mark I/O complete on a buffer, release it if I/O is asynchronous,
 * and wake up anyone waiting for it.
 */
iodone(bp)
struct buf *bp;
{
	register struct buf *rbp;

	rbp = bp;
	rbp->b_flags =| B_DONE;
	if (rbp->b_flags&B_ASYNC)
		brelse(rbp);
	else {
		rbp->b_flags =& ~B_WANTED;
		wakeup(rbp);
	}
}

/*
 * Zero the core associated with a buffer.
 */
clrbuf(bp)
int *bp;
{
	register *p;
	register c;

	p = bp->b_addr;
	c = 256;
	do
		*p++ = 0;
	while (--c);
}

/*
 * Initialize the buffer I/O system by freeing
 * all buffers and setting all device buffer lists to empty.
 */
binit()
{
	register struct buf *bp;
	register struct devtab *dp;
	register int i;
	struct bdevsw *bdp;

	bfreelist.b_forw = bfreelist.b_back =
	    bfreelist.av_forw = bfreelist.av_back = &bfreelist;
	for (i=0; i<v.v_buf; i++) {
		bp = &buf[i];
		bp->b_dev = -1;
		bp->b_addr = buffers[i];
		bp->b_back = &bfreelist;
		bp->b_forw = bfreelist.b_forw;
		bfreelist.b_forw->b_back = bp;
		bfreelist.b_forw = bp;
		bp->b_flags = B_BUSY;
		bp->b_wcount = -256;
		brelse(bp);
	}
	for (i=0, bdp = bdevsw; i<bdevcnt; i++, bdp++) {
		dp = bdp->d_tab;
		if(dp) {
			dp->b_forw = dp;
			dp->b_back = dp;
		}
	}
}

/*
 * make sure all write-behind blocks on dev (or NODEV for all)
 * are flushed out.
 */
bflush(dev)
{
	register struct buf *bp;

loop:
	spl6();
	for (bp = bfreelist.av_forw; bp != &bfreelist; bp = bp->av_forw) {
		if (bp->b_flags&B_DELWRI && (dev == NODEV||dev==bp->b_dev)) {
			bp->b_flags =| B_ASYNC;
			notavail(bp);
			bwrite(bp);
			goto loop;
		}
	}
	spl0();
}

/*
 * Pick up the device's error number and pass it to the user;
 * if there is an error but the number is 0 set a generalized code.
 */
geterror(abp)
struct buf *abp;
{
	register struct buf *bp;

	bp = abp;
	if (bp->b_flags&B_ERROR)
		if ((u.u_error = bp->b_error)==0)
			u.u_error = EIO;
}
 (bp = bfreelist.av_forw; #
/*
 * DR11C driver used for C/A/T
 */

#include "../hd/param.h"
#include "../hd/user.h"
#include "../hd/tty.h"

extern cat_addr[], cat_cnt;

#define	CTPRI	9
#define	CTMAX	4
#define	CTHIWAT	80
#define	CTLOWAT	20

struct {
	struct	clist	cat_outq;
	int	cat_lock;
} cat[CTMAX];

struct {
	int	drcsr, drobuf, dribuf;
};

catopen(dev)
register dev;
{
	if (dev >= cat_cnt || dev >= CTMAX || cat[dev].cat_lock) {
		u.u_error = ENXIO;
		return;
	}
	cat[dev].cat_lock++;
	cat_addr[dev]->drcsr =| IENABLE;
}

catclose(dev)
{
	cat[dev].cat_lock = 0;
	catrint(dev);
}

catwrite(dev)
{
	register c, *cp;
	extern lbolt;

	cp = &cat[dev];
	while ((c=cpass()) >= 0) {
		spl5();
		while (cp->cat_outq.c_cc > CTHIWAT)
			sleep(cp, CTPRI);
		while (putc(c, &cp->cat_outq) < 0)
			sleep(&lbolt, CTPRI);
		catrint(dev);
		spl0();
	}
}

catrint(dev)
{
	register c, *cp, *dp;

	dp = cat_addr[dev];
	if (dp->drcsr&DONE) {
		cp = &cat[dev];
		if ((c = getc(&cp->cat_outq)) >= 0) {
			dp->drobuf = c;
			if (cp->cat_outq.c_cc==0 || cp->cat_outq.c_cc==CTLOWAT)
				wakeup(cp);
		} else if (cp->cat_lock==0)
			dp->drcsr = 0;
	}
}
catxint() {}
;

	cp = &cat[dev];
	while ((c=cpass()) >= 0) {
		spl5();
		while (cp->cat_outq.c_cc > CTHIWAT)
			sleep(cp, CTPRI);
		while (putc(c, &cp->cat_outq) < 0)
			sleep(&lbolt, CTPRI);
		catrint(dev);
		spl0();
	}
}

catrint(dev)
{
	register c, *cp, *dp;

	dp = cat_addr[dev];
	if (dp->drcsr&DONE) {
		cp = &cat[dev];
		if ((c = getc(&cp->cat_outq)) >= 0) {
			dp->drobuf = c;
			if (cp->cat_outq.c_cc==0 || cp->cat_outq.c_cc#
/*
 *	DH11 & DM11-BB driver for multiple units
 */

#include "../hd/param.h"
#include "../hd/user.h"
#include "../hd/tty.h"

extern	dh_addr[], dh_cnt;
struct tty dh_tty[];
struct ttydma dh_ttydma[];
int	dhsar[8];

#define	BITS6	01
#define	BITS7	02
#define	BITS8	03
#define	TWOSB	04
#define	PENABLE	020
#define	OPAR	040
#define	HDUPLX	040000

#define	IENABLE	030100
#define	PERROR	010000
#define	FRERROR	020000
#define	XINT	0100000
#define	SSPEED	7	/* standard speed: 300 baud */

struct {
	int dhcsr, dhnxch, dhlpr, dhcar;
	int dhbcr, dhbar, dhbreak, dhsilo;
};

int	dm_addr[];

#define	DONE	0200
#define	SCENABL	040
#define	BSY	020
#define	TURNON	07	/* RTS, DTR, line enable */
#define	TURNOFF	1	/* line enable only */
#define	CARRIER	0140

struct {
	int	dmcsr, dmlstat;
};

dhopen(dev, flag)
{
	register struct tty *tp;
	extern dhstart();

	if (dev >= dh_cnt) {
		u.u_error = ENXIO;
		return;
	}
	tp = &dh_tty[dev];
	if ((tp->t_state&(ISOPEN|WOPEN)) == 0) {
		tp->t_addr = dhstart;
		tp->t_state = SSTART;
		tp->t_erase = CERASE;
		tp->t_kill = CKILL;
		tp->t_speeds = SSPEED | (SSPEED<<8);
		tp->t_flags = ODDP|EVENP|ECHO|HUPCL;
		dhparam(dev);
	}
	spl4();
	if (dmctl(dev, TURNON)&CARRIER)
		tp->t_state =| CARR_ON;
	while ((tp->t_state&CARR_ON)==0) {
		tp->t_state =| WOPEN;
		sleep(&tp->t_rawq, TTIPRI);
	}
	ttyopen(tp);
	spl0();
}

dhclose(dev)
{
	register struct tty *tp;

	tp = &dh_tty[dev];
	wflushtty(tp);
	if (tp->t_flags&HUPCL)
		dmctl(dev, TURNOFF);
	tp->t_state =& (CARR_ON|SSTART);
}

dhread(dev)
{
	ttread(&dh_tty[dev]);
}

dhwrite(dev)
{
	ttwrite(&dh_tty[dev]);
}

dhrint(dev)
{
	register struct tty *tp;
	register int c, *dhaddr;

	dhaddr = dh_addr[dev];
	while ((c = dhaddr->dhnxch) < 0) {	/* char. present */
		tp = &dh_tty[((c>>8)&017)|(dev<<4)];
		if (tp >= &dh_tty[dh_cnt])
			continue;
		if((tp->t_state&ISOPEN)==0) {
			wakeup(&tp->t_rawq);
			continue;
		}
		if (c&FRERROR)		/* break */
			if (tp->t_flags&RAW)
				c = 0;		/* null (for getty) */
			else
				c = 0177;	/* DEL (intr) */
		if (c&PERROR)
			if ((tp->t_flags&(EVENP|ODDP))==EVENP
			 || (tp->t_flags&(EVENP|ODDP))==ODDP )
				continue;
		ttyinput(c, tp);
	}
}

dhsgtty(dev, av)
int *av;
{
	register struct tty *tp;

	tp = &dh_tty[dev];
	if (ttystty(tp, av))
		return;
	dhparam(dev);
}

/*
 * Set parameters from open or stty into the DH hardware
 * registers.
 */
dhparam(dev)
{
	register struct tty *tp;
	register int lpr, *dhaddr;

	tp = &dh_tty[dev];
	dhaddr = dh_addr[dev>>4];
	spl5();
	dhaddr->dhcsr =& ~017;
	dhaddr->dhcsr =| (dev&017) | IENABLE;
	if (tp->t_speeds.lobyte==0) {	/* Hang up line */
		dhaddr->dhbcr = 0;
		spl0();
		dmctl(dev, TURNOFF);
		return;
	}
	lpr = (tp->t_speeds.hibyte<<10) | (tp->t_speeds.lobyte<<6);
	if (tp->t_speeds.lobyte == 4)		/* 134.5 baud */
		lpr =| BITS6|PENABLE|HDUPLX;
	else if (tp->t_flags&RAW)
		lpr =| BITS8;
	else
		lpr =| BITS7|PENABLE;
	if ((tp->t_flags&EVENP)==0)
		lpr =| OPAR;
	if (tp->t_speeds.lobyte == 3)	/* 110 baud */
		lpr =| TWOSB;
	dhaddr->dhlpr = lpr;
	spl0();
}

/*
 * DH11 transmitter interrupt.
 * Restart each line which used to be active but has
 * terminated transmission since the last interrupt.
 */
dhxint(dev)
{
	register struct tty *tp;
	register ttybit, bar;
	int *dhaddr;

	dhaddr = dh_addr[dev];
	bar = dhsar[dev] & ~dhaddr->dhbar;
	dhaddr->dhcsr =& ~XINT;
	ttybit = 1;
	for (tp = &dh_tty[dev<<4]; bar; tp++) {
		if(bar&ttybit) {
			dhsar[dev] =& ~ttybit;
			bar =& ~ttybit;
			tp->t_state =& ~BUSY;
			dhstart(tp);
		}
		ttybit =<< 1;
	}
}

/*
 * Start (restart) transmission on the given DH11 line.
 */
dhstart(atp)
struct tty *atp;
{
	extern ttrstrt();
	register c, nch;
	register struct tty *tp;
	int sps, dev;
	char *cp;
	int line, *dhaddr;

	sps = PS->integ;
	spl5();
	tp = atp;
	dev = tp-dh_tty;
	/*
	 * If it's currently active, or delaying,
	 * no need to do anything.
	 */
	if (tp->t_state&(TIMEOUT|BUSY))
		goto out;
	/*
	 * t_char is a delay indicator which may have been
	 * left over from the last start.
	 * Arrange for the delay.
	 */
	if (c = tp->t_char) {
		tp->t_char = 0;
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
		goto out;
	}
	cp = &dh_ttydma[dev];
	nch = 0;
	/*
	 * Copy TTYDMA characters, or up to a delay indicator,
	 * to the DMA area.
	 */
	while (nch > -(sizeof (struct ttydma)) && (c = getc(&tp->t_outq))>=0) {
		if (c >= 0200 && (tp->t_flags&RAW)==0) {
			tp->t_char = c;
			break;
		}
		*cp++ = c;
		nch--;
	}
	/*
	 * If the writer was sleeping on output overflow,
	 * wake him when low tide is reached.
	 */
	if (tp->t_outq.c_cc<=TTLOWAT && tp->t_state&ASLEEP) {
		tp->t_state =& ~ASLEEP;
		wakeup(&tp->t_outq);
	}
	/*
	 * If any characters were set up, start transmission;
	 * otherwise, check for possible delay.
	 */
	if (nch) {
		dhaddr = dh_addr[dev>>4];
		line = dev & 017;
		dhaddr->dhcsr.lobyte = line | IENABLE;
		dhaddr->dhcar = cp+nch;
		dhaddr->dhbcr = nch;
		c = 1<<line;
		dhaddr->dhbar =| c;
		dhsar[dev>>4] =| c;
		tp->t_state =| BUSY;
	} else if (c = tp->t_char) {
		tp->t_char = 0;
		timeout(ttrstrt, tp, (c&0177)+6);
		tp->t_state =| TIMEOUT;
	}
    out:
	PS->integ = sps;
}

/*
 * Dump control bits into the DM registers.
 */
dmctl(dev, bits)
register dev;
{
	register *dmaddr;

	dmaddr = dm_addr[dev>>4];
	dmaddr->dmcsr =& ~SCENABL;
	while(dmaddr->dmcsr&BSY);
	dmaddr->dmcsr = dev&017;
	dmaddr->dmlstat = bits;
	dev = dmaddr->dmlstat;
	dmaddr->dmcsr =| IENABLE|SCENABL;
	return(dev);
}

/*
 * DM11 interrupt.
 * Mainly, deal with carrier transitions.
 */
dmintr(dev)
{
	register struct tty *tp;
	register *dmaddr;

	dmaddr = dm_addr[dev];
	if (dmaddr->dmcsr&DONE) {
		tp = &dh_tty[(dmaddr->dmcsr&017)|(dev<<4)];
		if (tp < &dh_tty[dh_cnt]) {
			wakeup(&tp->t_rawq);
			if ((dmaddr->dmlstat&CARRIER)==0) {
				if ((tp->t_state&WOPEN)==0) {
					signal(tp->t_pgrp, SIGHUP);
					dmaddr->dmlstat = 0;
					flushtty(tp);
				}
				tp->t_state =& ~CARR_ON;
			} else
				tp->t_state =| CARR_ON;
		}
		dmaddr->dmcsr =& ~DONE;
	}
}
ainly, deal with carrier transitions.
 */
dmintr(dev)
{
	register struct tty *tp;
	register *dmaddr;

	dmaddr = dm_addr[dev];
	if (dmaddr->dmcsr&DONE) {
		tp = &dh_tty[(dmaddr->dmcs#
/*
 * DN-11 ACU interface
 */

#include "../hd/param.h"
#include "../hd/user.h"

struct {
	int	dn_reg[4];
};

extern	dn_addr[], dn_cnt;

#define	PWI	0100000
#define	ACR	040000
#define	DLO	010000
#define	DONE	0200
#define	IENABLE	0100
#define	DSS	040
#define	PND	020
#define	MENABLE	04
#define	DPR	02
#define	CRQ	01

#define	DNPRI	5

dnopen(dev)
register dev;
{
	register *dp;

	if (dev >= dn_cnt ||
	   (dp = dn_addr[dev>>2])->dn_reg[dev&03]&(PWI|DLO))
		u.u_error = ENXIO;
	else {
		*dp =| MENABLE;
		dp->dn_reg[dev&03] = IENABLE|MENABLE|CRQ;
	}
}

dnclose(dev)
{
	dn_addr[dev>>2]->dn_reg[dev&03] = MENABLE;
}

dnwrite(dev)
register dev;
{
	register c, *dp;
	extern lbolt;

	dp = &(dn_addr[dev>>2]->dn_reg[dev&03]);
	while ((*dp & (PWI|ACR|DSS)) == 0) {
		spl5();
		if ((*dp&DONE) == 0) {
			sleep(dn_addr[dev>>2], DNPRI);
		} else if (u.u_count == 0) {
			*dp = IENABLE|MENABLE|CRQ;
		} else if ((c=cpass()) == '-') {
			sleep(&lbolt, DNPRI);
			sleep(&lbolt, DNPRI);
		} else if (*dp&PND) {
			*dp = (c<<8)|IENABLE|MENABLE|DPR|CRQ;
		}
		spl0();
	}
	if (*dp&(PWI|ACR))
		u.u_error = EIO;
	*dp = MENABLE|CRQ;
}

dnintr(dev)
{
	wakeup(dn_addr[dev]);
}
	register c, *dp;
	extern lbolt;

	dp = &(dn_addr[dev>>2]->dn_reg[dev&03]);
	while ((*dp & (PWI|ACR|DSS)) == 0) {
		spl5();
		if ((*dp&DONE) == 0) {
			sleep(dn_addr[dev>>2], DNPRI);
		} else if (u.u_count == 0) {
			*dp = IENABLE|MENABLE|CRQ;
		} else if ((c=cpass()) == '-') {
			sleep(&lbolt, DNPRI);
			sleep(&lbolt, DNPRI);
		} else if (*dp&PND) {
			*dp = (c<<8)|IENABLE|MENA#
/*
 * DQS11[AB] handler - IBM Bisync
 */

#define	DQS11
#include "../hd/param.h"
#include "../hd/buf.h"
#include "../hd/systm.h"
#include "../hd/user.h"
#include "../hd/peri.h"

int dqstr[61];
extern dqs_addr[], dqs_cnt;

#define PRI 5

struct { int csr, bcr, bar, dbr; };

#define CSR dqs->addr->csr
#define BCR dqs->addr->bcr
#define BAR dqs->addr->bar
#define DBR dqs->addr->dbr

#define GO  01
#define RX  02
#define IE  0100
#define RDY 0200
#define DTR	0400
#define ERR 0177000

#define DLY dqs->dly

#define EOT dqs->eot
#define ENQ dqs->enq
#define NAK dqs->nak
#define ETB dqs->etb
#define ETX dqs->etx

#define TTD  dqs->ttd
#define ACK0 dqs->ack0
#define WACK dqs->wack
#define ACKX dqs->ackx

#define R0 1
#define R1 2
#define R2 3
#define R3 4
#define R4 5
#define R5 6
#define W0 7
#define W1 8
#define W2 9
#define W3 10

#define RT0 11
#define RT1 12
#define RT2 13
#define RT3 14
#define RT4 15
#define RT5 16
#define WT0 17
#define WT1 18
#define WT2 19
#define WT3 20

#define TOUT 10

dqsopen(dev)
register dev;
{
	register struct dqsdat *dqs;
	int dqstout();
	if (dev>=NDQ || dev>=dqs_cnt || (dqs = &dqsx[dev])->open) {
		u.u_error = ENXIO;
		return;
	}
	dqs->addr = dqs_addr[dev];
	dqs->open=1; 
	dqs->time=0;
	timeout(dqstout,dqs,20);
}

dqsclose(dev)
{
	register struct dqsdat *dqs;
	dqs = &dqsx[dev];
	CSR = DTR;
	dqs->qcase=0;
	dqs->open=2;
	dqsfin();
	CSR = 0;
}

dqsread(dev)
{
	register struct dqsdat *dqs;
	register struct dqsbuf *b;
	register int c;
	dqs = &dqsx[dev];
	spl5();
	while (dqs->state<0) sleep(dqs,PRI);
	spl0();
	if (dqs->state==0) {
		dqs->q=0;
		dqs->resp=ACK0; 
		dqs->try=3;
		dqs->state++; 
		dqs->qcase=R0;
		dqsrecv(); 
		dqs->time=20;
		dqs->u=0;
	}
	if (dqs->u==0) {
		dqs->uoff=0;
		spl5();
		while ((c=dqs->state)>0) {
			if ((dqs->u=dqsget(1))!=0) break;
			if (c==2 && dqs->x==0) {
				dqsbeg(); 
				spl5();
				if (dqs->qcase==R4) dqs->time=1;
				continue;
			}
			if (c>=3) {
				spl0(); 
				dqsfin(); 
				return;
			}
			dqsleep();
		}
		spl0();
		if (dqs->state<=0) return;
	}
	b=dqs->u;
	c=min(b->bufc,u.u_count);
	if (c&1 && c<u.u_count) {
		c++; 
		u.u_count++;
	}
	iomove(b->bufa,dqs->uoff,c,B_READ);
	dqs->uoff=+c;
	if ((b->bufc=- c)>0) return;
	b->bufl=0; 
	dqs->u=0;
	spl5();
	if (dqs->qcase==R4) dqs->time=1;
	spl0();
}

dqswrite(dev)
{
	register struct dqsdat *dqs;
	register struct dqsbuf *b;
	register int c;
	dqs = &dqsx[dev];
	spl5();
	while (dqs->state>0) sleep(dqs,PRI);
	if (u.u_count==0) {
		while ((c=dqs->state)<0 && c>(-5)) {
			if (c==(-2)) {
				dqs->state--;
				if (dqs->qcase==W2) dqs->time=1;
			}
			dqsleep();
		}
		goto N;
	}
	spl0();
	if (dqs->state==0) {
		dqs->q=0;
		dqs->resp=ACK0; 
		dqs->try=3;
		dqs->state--; 
		dqs->qcase=W0;
		dqspoke(ENQ);
	}
	spl5();
	while ((c=dqs->state)<0 && c>(-5)) {
		if ((b=dqsget(0))!=0) break;
		if (c==(-2) && dqs->x==0) {
			dqsbeg(); 
			spl5(); 
			continue;
		}
		dqsleep();
	}
N: 
	spl0();
	if ((c=dqs->state)<=(-5)) {
		if (c==(-5)) u.u_error=EIO;
		dqsfin(); 
		return;
	}
	if (dqs->state>=0) return;
	b->bufc=c=min(512,u.u_count);
	if (c&1) {
		c++; 
		u.u_count++;
	}
	iomove(b->bufa,0,c,B_WRITE);
	b->bufl=1;
	spl5();
	if (dqs->qcase==W2) dqs->time=1;
	spl0();
}

dqsgo(reg)
struct dqsreg *reg;
{
	reg->csr=IE|GO;
}

dqsintr(dev)
{
	int erf;
	register struct dqsdat *dqs;
	register struct dqsbuf *b;
	register int tmp;
	struct {
		int *ip;
	};
	struct {
		char *bp;
	};
	dqs = &dqsx[dev];
	tmp = &dqstr[dqstr[0]+1];
	*tmp.bp++=dev;
	*tmp.bp++=time.loword;
	*tmp.bp++=dqs->state;
	*tmp.bp++=dqs->qcase;
	*tmp.ip++=CSR;
	*tmp.ip++=BCR;
	*tmp.ip++=BAR;
	*tmp.ip++=DBR;
	tmp=tmp.ip-(dqstr+1);
	if (tmp>54) tmp=0;
	dqstr[0]=tmp;
	erf=CSR&ERR;
	CSR=0; 
	dqs->time=0;
	switch (dqs->qcase) {
	case R0:
		tmp=dqs->cc;
		if (erf) tmp=0;
		if (tmp.lobyte==EOT) goto reot;
		if (tmp.lobyte==ENQ) {
			if (dqs->state==1) {
				dqs->state++; 
				dqswake(); 
				dqs->try=7;
			}
			goto rack;
		}
		dqs->qcase=R1; 
		goto rnak;
	case R1: 
	case RT1:
		dqs->qcase=R0; 
		dqsrecv(); 
		break;
	case R2: 
	case RT2:
		dqs->qcase=R3;
		tmp=dqs->addr;
		tmp->bcr=(-512);
		tmp->bar=dqs->q->bufb;
		tmp->csr=IE|RX|GO;
		dqs->time=40; 
		break;
	case R3:
		dqs->qcase=R2;
		if (erf) goto rnak;
		b=dqs->q;
		tmp=(*(b->bufb));
		if (tmp.lobyte==EOT) goto reot;
		if (tmp.lobyte==ENQ) goto rack;
		b->bufc=(BAR-(b->bufb));
		b->bufc--; 
		tmp=DBR;
		if (tmp.lobyte==ENQ) goto rnak;
		if (tmp.lobyte!=ETB && tmp.lobyte!=ETX) {
			b->bufc++; 
			tmp=>>8;
			if (tmp.lobyte!=ETB && tmp.lobyte!=ETX) goto rnak;
		}
		b->bufl=1; 
		dqs->q=0;
		dqswake();
		dqs->resp=ACKX-dqs->resp;
		dqs->try=7;
rack: 
		if (--dqs->try<0) goto rabt;
	case R4: 
	case RT4:
		if (dqs->q==0 && (dqs->q=dqsget(0))==0) {
			if (dqs->qcase==RT4) {
				dqs->try++; 
				dqs->qcase=R1; 
				dqspoke(WACK); 
				break;
			}
			dqs->qcase=R4; 
			dqs->time=7; 
			break;
		}
		dqs->qcase=R2; 
		dqspoke(dqs->resp); 
		break;
rnak: 
		if (--dqs->try<=0) goto rabt;
		dqspoke(NAK); 
		break;
	case RT0: 
	case RT3:
rabt: 
		if (dqs->state==1) goto reot;
		dqs->qcase=R5; 
		dqspoke(EOT); 
		break;
	case R5: 
	case RT5:
reot: 
		dqs->state=3; 
		dqswake(); 
		dqs->qcase=0; 
		break;
	case W0: 
	case WT0:
		dqs->qcase=W1; 
		dqsrecv(); 
		break;
	case WT1:
		erf++;
	case W1:
		tmp=dqs->cc;
		if (erf) tmp=0;
		if (tmp!=dqs->resp) goto wnot;
		if (dqs->state==(-1)) dqs->state--;
		if (b=dqs->q) {
			b->bufl=0; 
			dqs->q=0;
		}
		dqswake();
		dqs->resp=ACKX-dqs->resp;
		dqs->try=7;
	case W2: 
	case WT2:
wsnd: 
		if (dqs->q==0 && (dqs->q=dqsget(1))==0) {
			if (dqs->state==(-3)) goto weot;
			if (dqs->qcase==WT2) {
				dqs->try++; 
				dqs->qcase=W0; 
				dqspoke(TTD); 
				break;
			}
			dqs->qcase=W2; 
			dqs->time=7; 
			break;
		}
		b=dqs->q;
		dqs->qcase=W0;
		tmp=dqs->addr;
		tmp->bcr=(-b->bufc);
		tmp->bar=b->bufb;
		dqs->time=7+DLY;
		if (DLY==0) tmp->csr=IE|GO;
		else timeout(dqsgo,tmp,DLY);
		break;
wnot: 
		if (--dqs->try<=0) goto wabt;
		if (tmp.lobyte==ENQ || tmp.lobyte==EOT) goto wabt;
		if (tmp.lobyte==NAK) {
			if (dqs->state==(-1)) goto wabt; 
			goto wsnd;
		}
		dqs->qcase=W0; 
		dqspoke(ENQ); 
		break;
wabt: 
		if (dqs->state!=(-1)) dqs->state=(-4);
		else if (tmp.lobyte==ENQ) goto wend;
weot: 
		dqs->qcase=W3; 
		dqspoke(EOT); 
		break;
	case W3: 
	case WT3:
wend: 
		dqs->state=(dqs->state>(-4)? -6:-5);
		dqswake(); 
		dqs->qcase=0; 
		break;
	}
}

dqstout(ptr)
struct dqsdat *ptr;
{
	register struct dqsdat *dqs;
	dqs=ptr;
	if (dqs->open!=1) {
		dqs->open=0; 
		return;
	}
	if (dqs->time>0 && --dqs->time==0) {
		CSR=0; 
		dqs->qcase=+TOUT; 
		dqsintr(dqs-dqsx);
	}
	timeout(dqstout,dqs,20);
}


/* dqs passed in register beyond this point */

dqsrecv()
{
	register struct dqsdat *dqs;
	register int *reg;
	reg=dqs->addr;
	reg->bcr=(-2);
	reg->bar=(&dqs->cc);
	reg->csr=IE|RX|GO;
	dqs->time=11;
}

dqspoke(code)
{
	register struct dqsdat *dqs;
	register int *reg,tmp;
	reg=dqs->addr;
	tmp=code; 
	dqs->cc=tmp;
	reg->bcr=(tmp==tmp.lobyte? -1:-2);
	reg->bar=(&dqs->cc);
	dqs->time=2+DLY;
	if (DLY==0) reg->csr=IE|GO;
	else timeout(dqsgo,reg,DLY);
}

dqsleep()
{
	register struct dqsdat *dqs;
	dqs->slp=1; 
	sleep(dqs->addr,PRI);
}

dqswake()
{
	register struct dqsdat *dqs;
	if (dqs->slp) wakeup(dqs->addr);
	dqs->slp=0;
}

dqsbeg()
{
	register struct dqsdat *dqs;
	register struct dqsbuf *b;
	register int i;
	dqs->x=dqs;
	b=(&dqs->bf[NBF-1]);
	for (i=0;i<NBF;i++) {
		b=b->bufn=(&dqs->bf[i]); 
		b->bufl=0;
		b->bufb=(b->bufa=getblk(NODEV))->b_addr;
	}
	dqs->x=b;
}

dqsget(flag)
{
	register struct dqsdat *dqs;
	register struct dqsbuf *b;
	if ((b=dqs->x)<=dqs) return (0);
	if (flag!=0) {
		if (b->bufl!=1) return (0);
		dqs->x=b->bufn; 
		goto P;
	}
	while (b->bufl!=0) {
		b=b->bufn; 
		if (b==dqs->x) return (0);
	}
P: 
	b->bufl=2; 
	return (b);
}

dqsfin()
{
	register struct dqsdat *dqs;
	register struct dqsbuf *b;
	if ((b=dqs->x)==dqs) return;
	if (b!=0) {
		do {
			brelse(b->bufa); 
			b=b->bufn;
		} 
		while (b!=dqs->x);
	}
	dqs->x=0;
	dqs->state=0;
	wakeup(dqs);
}

dqsget(flag)
{
	register struct dqsdat *dqs;
	register struct dqsbuf *b;
	if ((b=dqs->x)<=dqs) return (0);
	if (flag!=0) {
		if (b->bufl!=1) return (0);
		dqs->x=b->bufn; 
		goto P#
/*
 * DU-11 Synchronous interface driver
 */

#include "../hd/param.h"
#include "../hd/user.h"
#include "../hd/buf.h"
#include "../hd/peri.h"

/* device registers */
struct {
	int	rxcsr, rxdbuf;
	int	txcsr, txdbuf;
};

extern	du_addr[], du_cnt;

#define	DONE	0200
#define	IE	0100
#define	SIE	040
#define CTS	020000
#define	CARRIER	010000
#define	RCVACT	04000
#define	DSR	01000
#define STRIP	0400
#define SCH	020
#define RTS	04
#define	DTR	02
#define MR	0400
#define SEND	020
#define	HALF	010

#define	READ	0
#define	WRITE	1
#define PWRIT	2

#define	DUPRI	5

duopen(dev)
{
	register struct du *dp;
	register *lp;

	if (dev >= du_cnt ||
	   ((dp = &du_du[dev])->du_proc!=0 && dp->du_proc!=u.u_procp)) {
		u.u_error = ENXIO;
		return;
	}
	dp->du_proc = u.u_procp;
	lp = du_addr[dev];
	lp->txcsr = MR;
	lp->rxdbuf = 035026;
	if (dp->du_buf==0) {
		dp->du_buf = getblk(NODEV);
		dp->du_bufb = dp->du_buf->b_addr;
		dp->du_bufe = dp->du_bufb+512;
		dp->du_state = WRITE;
		duturn(dev);
	}
}

duclose(dev)
{
	register struct du *dp;
	register *lp;

	dp = &du_du[dev];
	lp = du_addr[dev];
	lp->rxcsr = 0;
	lp->txcsr = 0;
	dp->du_proc = 0;
	if (dp->du_buf != 0) {
		brelse(dp->du_buf);
		dp->du_buf = 0;
	}
}

duread(dev)
{
	register char *bp;
	register struct du *dp;
	register n;
	int	flag;

	dp = &du_du[dev];
	bp = dp->du_bufb;
	flag = 0;
	for(;;) {
		if(duwait(dev))
			return;
		spl5();
		if (n = dp->du_nleft) {
			dp->du_nleft = 0;
			spl0();
			flag++;
			while (n--) {
				if (passc(*bp++)<0)
					break;
				if (bp == dp->du_bufe)
					bp = dp->du_bufb;
			}
		} else {
			if ( flag) {
				spl0();
				break;
			}
			sleep(dp, DUPRI);
			spl0();
		}
	}
	dp->du_bufp = dp->du_bufb;
}

duwrite(dev)
{
	register struct du *dp;
	register char *bp,*ep;
	int	n, *lp;

	dp = &du_du[dev];
	lp = du_addr[dev];
	if (u.u_count==0 ||duwait(dev))
		return;
	dp->du_bufp = ep = bp = dp->du_bufb;
	dp->du_state = PWRIT;
	lp->rxcsr =& ~SCH;
	lp->rxcsr = SIE|RTS|DTR;
	n = 0;
	while (u.u_count!=0) {
		*bp++ = cpass();
		n++;
		if (bp==dp->du_bufe)
			bp = dp->du_bufb;
		if (bp==ep) {
			duset(dev, n);
			if (u.u_count!=0) {
				spl5();
				sleep(dp,DUPRI);
				spl0();
			} else
				return;
			ep = dp->du_bufp;
			n = 0;
		}
	}
	duset(dev, n);
}

duwait(dev)
{
	register struct du *dp;
	register *lp;

	dp = &du_du[dev];
	lp = du_addr[dev];
	for(;;) {
		if ((lp->rxcsr&DSR)==0 || dp->du_buf==0) {
			u.u_error = EIO;
			return(1);
		}
		spl5();
		if (dp->du_state==READ &&
			((lp->rxcsr&RCVACT)==0 || dp->du_nleft)) {
			spl0();
			return(0);
		}
		sleep(dp, DUPRI);
		spl0();
	}
}

dustart(dev)
{
	register struct du *dp;
	register *lp

	dp = &du_du[dev];
	lp = du_addr[dev];
	if (dp->du_nleft > 0) {
		if (--dp->du_nleft==200)
			wakeup(dp);
		lp->txdbuf = *dp->du_bufp++;
		if (dp->du_bufp==dp->du_bufe)
			dp->du_bufp = dp->du_bufb;
	} else {
		duturn(dev);
	}
}

duset(dev, an)
{
	register struct du *dp;
	register *lp;

	dp = &du_du[dev];
	lp = du_addr[dev];
	spl5();
	while((lp->rxcsr&CTS)==0)
		sleep(dp,DUPRI);
	dp->du_nleft =+ an;
	if (dp->du_state != WRITE) {
		dp->du_state = WRITE;
		lp->txcsr = IE|SIE|SEND|HALF;
		dustart(dev);
	}
	spl0();
}

durint(dev)
{
	register struct du *dp;
	register c, s;
	int	dustat, *lp;

	dp = &du_du[dev];
	lp = du_addr[dev];
	dustat = lp->rxcsr;
	if(dustat<0) {
		if((dustat&CARRIER)==0 && dp->du_state==READ)
			duturn(dev); else
			wakeup(dp);
	} else
	if(dustat&DONE) {
		lp->rxcsr = IE|SIE|SCH|DTR;
		c = s = lp->rxdbuf;
		c =& 0177;
		if(s<0)
			c =| 0200;
		if (++dp->du_nleft==384)
			wakeup(dp);
		if (dp->du_bufp == dp->du_bufe)
			dp->du_bufp = dp->du_bufb;
		*dp->du_bufp++ = c;
		if (c == 0377) duturn(dev);
	}
}

duxint(dev)
{
	register struct du *dp;
	register *lp;
	register int dustat;

	dp = &du_du[dev];
	lp = du_addr[dev];
	dustat = lp->txcsr;
	if(dustat<0)
		duturn(dev); else
	if(dustat&DONE)
		dustart(dev);
}

duturn(dev)
{
	register struct du *dp;
	register *lp;

	dp = &du_du[dev];
	lp = du_addr[dev];
	if (dp->du_state!=READ) {
		dp->du_state = READ;
		dp->du_bufp = dp->du_bufb;
		dp->du_nleft = 0;
	}
	lp->txcsr = HALF;
	lp->rxcsr =& ~SCH;
	lp->rxcsr = STRIP|IE|SIE|SCH|DTR;
	wakeup(dp);
}
 duturn(dev);
	}
}

duxint(dev)
{
	register struct du *dp;
	register *lp;
	register int dustat;

	dp = &du_du[dev];
	lp = du_addr[dev];
	dustat = lp->txcsr;
	if(dustat<0)
		duturn(dev); else
	if(dustat&DONE)
		dustart(dev);
}

duturn(dev)
{
	register struct du *dp;
	register *lp;

	dp = &du_du[dev];
	lp = du_addr[dev];
	if (dp->du_state!=READ) {
		dp->du_state = READ;
		dp->du_bufp = dp->du_bufb;
		dp->du_nleft = 0;
	}
	lp->txcsr = HALF;
	lp->rxcsr#
/*
 * RJP04, RWP04 driver
 */

#include "../hd/param.h"
#include "../hd/buf.h"
#include "../hd/user.h"

struct {
	int	hpcs1, hpwc, hpba, hpda;	
	int	hpcs2, hpds, hper1, hpas;	
	int	hpla, hpdb, hpmr, hpdt;	
	int	hpsn, hpof, hpdc, hpcc;	
	int	hper2, hper3, hpec1, hpec2;	
	int	hpbae, hpcs3;	
};

int	hp_addr, hp_cnt;

struct {
	unsigned nblocks;
	int	cyloff;
} hp_sizes[8];

struct	devtab	hptab;
struct	devtab	hputab[8];
struct	buf	hpbuf;
char	hpstatus[8];
int	hpsioc[8], hpsios[8];

#define	GO	01
#define	UNLOAD	02
#define RECAL	06
#define DCLR	010
#define	RELEASE	012
#define	PRESET	020
#define	SEARCH	030

#define	ERR	040000	/* hpds - Error */
#define PIP	020000
#define	MOL	010000	/* hpds - Medium online */
#define	DRY	0200	/* hpds - drive ready */
#define VV	0100	/* hpds - volume valid */
#define SC	0100000	/* hpcs1 - Special condition */
#define	TRE	040000	/* hpcs1 - transfer error */
#define DVA	04000	/* hpcs1 - drive available */
#define	IE	0100	/* hpcs1 - interrupt enable */
#define	WLE	04000	/* hper1 - Write lock error */
#define FMT22	010000	/* hpof - 16 bit /word format */
#define	ECI	04000	/* hpof - ecc inhibit */
/*
 * Use av_back to save track+sector,
 * b_resid for cylinder.
 */

#define	trksec	av_back
#define	cylin	b_resid

hpopen(dev)
{
	if (((dev>>3)&037)>hp_cnt)
		u.u_error = ENXIO;
}

hpclose(dev)
{
}

hpstrategy(abp)
struct buf *abp;
{
	register struct buf *bp;
	register char *p1, *p2;
	struct devtab *dp;
	int	unit;

	bp = abp;
	p1 = &hp_sizes[minor(bp->b_dev)&07];
	unit = minor(bp->b_dev)>>3;
	if (hpstatus[unit] || bp->b_blkno >= p1->nblocks) {
		if (bp->b_blkno == p1->nblocks && bp->b_flags&B_READ)
			bp->b_resid = 512;
		else {
			bp->b_flags =| B_ERROR;
			bp->b_error = ENXIO;
		}
		iodone(bp);
		return;
	}
	bp->av_forw = 0;
	bp->cylin = p1->cyloff;
	p1 = bp->b_blkno;
	p2 = lrem(p1, 22);
	p1 = ldiv(p1, 22);
	bp->trksec = (p1%19)<<8 | p2;
	bp->cylin =+ p1/19;
	spl5();
	dp = &hputab[unit];
	hpsioc[unit]++;
	if ((p1 = dp->d_actf)==0)
		dp->d_actf = bp;
	else {
		for (; p2 = p1->av_forw; p1 = p2) {
			if (p1->cylin <= bp->cylin
			 && bp->cylin <  p2->cylin
			 || p1->cylin >= bp->cylin
			 && bp->cylin >  p2->cylin) 
				break;
		}
		bp->av_forw = p2;
		p1->av_forw = bp;
	}
	if (dp->d_active==0) {
		hpustart(unit);
		if (hptab.d_active==0)
			hpstart();
	}
	spl0();
}

hpustart(unit)
{
	register struct buf *bp;
	register struct devtab *dp;
	register *rp;
	int	search;

	dp = &hputab[unit];
	rp = hp_addr;
	rp->hpcs2 = unit;
	rp->hpcs1.lobyte = IE;
	rp->hpas = 1<<unit;
	if ((rp->hpcs1&DVA)==0) {
	/* either NED or dual-port not avail */
		goto abort;
	}
	if (rp->hpds&ERR) {
		printf("RP04 drive %d errors: %o %o %o\n",
			unit, rp->hper1, rp->hper2, rp->hper3);
		rp->hpcs1.lobyte = IE|DCLR|GO;
		if (rp->hpds&ERR || ++dp->d_errcnt > 16)
			goto abort;
	}
	if ((rp->hpds&MOL)==0)
		goto abort;
	hpstatus[unit] = 0;
	if ((bp = dp->d_actf)==0)
		return;
	if ((rp->hpds&VV) == 0) {
		rp->hpcs1.lobyte = IE|PRESET|GO;
		rp->hpof = FMT22|ECI;
	}
	dp->d_active++;
	rp->hpdc = bp->cylin;
	search = bp->trksec.lobyte-(rp->hpla>>6)-1;
	if (search<0) search =+ 22;
	if ((bp->cylin!=rp->hpcc || search>6) &&
	    dp->d_active<3) {
		search = bp->trksec;
		search.lobyte =- 4;
		if (search.lobyte<0) search.lobyte =+ 22;
		hpsios[unit]++;
		rp->hpda = search;
		rp->hpcs1.lobyte = IE|SEARCH|GO;
	} else {
		dp->b_forw = 0;
		if (hptab.d_actf == 0)
			hptab.d_actf = dp; else
			hptab.d_actl->b_forw = dp;
		hptab.d_actl = dp;
	}
	return;
    abort:
	rp->hpas = 1<<unit;
	if (hpstatus[unit])
		return;
	hpstatus[unit] = 1;
	while(bp = dp->d_actf) {
		bp->b_flags =| B_ERROR;
		dp->d_actf = bp->av_forw;
		iodone(bp);
	}
	dp->d_active = 0;
	dp->d_errcnt = 0;
	printf("RP04 drive %d offline\n",unit);
}

hpstart()
{
	register struct buf *bp;
	register struct devtab *dp;
	register *rp;

    loop:
	if ((dp = hptab.d_actf) == 0)
		return;
	if ((bp = dp->d_actf) == 0) {
		hptab.d_actf = dp->b_forw;
		goto loop;
	}
	rp = hp_addr;
	rp->hpcs2 = minor(bp->b_dev) >> 3;
	hptab.d_active++;
	rp->hpdc = bp->cylin;
	rhstart(bp, &rp->hpda, bp->trksec, &rp->hpbae);
}

hpintr()
{
	register struct buf *bp;
	register unit;
	register *rp;
	struct devtab *dp;
	int as;

	rp = hp_addr;
	as = rp->hpas&0377;
	if (hptab.d_active) {	/* data transfer underway */
		dp = hptab.d_actf;
		bp = dp->d_actf;
		unit = minor(bp->b_dev)>>3;
		rp->hpcs2.lobyte = unit;
		if (rp->hpcs1 & TRE) {		/* error bit */
			while ((rp->hpds&DRY)==0);
			if (++hptab.d_errcnt>16 || rp->hper1&WLE)
				bp->b_flags =| B_ERROR; else
				hptab.d_active = 0;
			if (hptab.d_errcnt > 3)
				deverror(bp, rp->hper1, rp->hpcs2);
			rp->hpcs1 = TRE|IE|DCLR|GO;
			if ((hptab.d_errcnt&07)==04) {	/* 4,12 */
				rp->hpcs1 = IE|RECAL|GO;
				while ((rp->hpds&PIP));
			}
		}
		if (hptab.d_active) {
			hptab.d_active = 0;
			hptab.d_errcnt = 0;
			hptab.d_actf = dp->b_forw;
			dp->d_active = 0;
			dp->d_errcnt = 0;
			dp->d_actf = bp->av_forw;
			bp->b_resid = (-rp->hpwc)<<1;
			iodone(bp);
			rp->hpcs1 = IE|RELEASE|GO;
			if (dp->d_actf)
				hpustart(unit);
		}
		as =& ~(1<<unit);
	} else {
		if (as==0)
			rp->hpcs1 = IE;
		rp->hpcs1.hibyte = TRE>>8;
	}
	for (unit=0; as; unit++)
		if (as&(1<<unit)) {
			as =& ~(1<<unit);
			hpustart(unit);
		}
	hpstart();
}

hpread(dev)
{
	if (physck(hp_sizes[dev&07].nblocks, B_READ))
		physio(hpstrategy, &hpbuf, dev, B_READ);
}

hpwrite(dev)
{
	if (physck(hp_sizes[dev&07].nblocks, B_WRITE))
		physio(hpstrategy, &hpbuf, dev, B_WRITE);
}
c)<<1;
			iodone(bp);
			rp->hpcs1 = IE|RELEASE|GO;
			if (dp->d_actf)
				hpustart(unit);
		}
		as =& ~(1<<unit);
	} else {
		if (as==0)
			rp->hpcs1 = IE;
#
/*
 * RS03/04 disk driver
 */

#include "../hd/param.h"
#include "../hd/buf.h"
#include "../hd/user.h"

struct {
	int	hscs1, hswc, hdba, hsda;
	int	hscs2, hsds, hser, hsas;
	int	hsla, hsdb, hsmr, hsdt;
	int	hsbae, hscs3;
};

struct	devtab	hstab;
struct	buf	rhsbuf;

int	hs_addr, hs_cnt;

#define ERR	040000	/* hscs1 - composite error */

#define GO	01
#define RCLR	010
#define	DRY	0200	/* hsds - Drive Ready */

hsopen(dev)
{
}

hsclose(dev)
{
}

hsstrategy(abp)
struct buf *abp;
{
	register struct buf *bp;
	register mblks;

	bp = abp;
	if(bp->b_dev&010)
		mblks = 2048; /* RS04 */
	else	mblks = 1024; /* RS03 */
	if(bp->b_blkno >= mblks) {
		if (bp->b_blkno == mblks && bp->b_flags&B_READ)
			bp->b_resid = 512;
		else {
			bp->b_flags =| B_ERROR;
			bp->b_error = ENXIO;
		}
		iodone(bp);
		return;
	}
	bp->av_forw = 0;
	spl5();
	if (hstab.d_actf==0)
		hstab.d_actf = bp; else
		hstab.d_actl->av_forw = bp;
	hstab.d_actl = bp;
	if (hstab.d_active==0)
		hsstart();
	spl0();
}

hsstart()
{
	register struct buf *bp;
	register *rp;
	register addr;

	if ((bp = hstab.d_actf) == 0)
		return;
	hstab.d_active++;
	rp = hs_addr;
	addr = bp->b_blkno<<1;
	if ((bp->b_dev&010) == 0)
		addr =<< 1;
	rp->hscs2 = bp->b_dev & 07;
	rhstart(bp, &rp->hsda, addr, &rp->hsbae);
}

hsintr()
{
	register struct buf *bp;
	register *rp;

	if (hstab.d_active == 0)
		return;
	bp = hstab.d_actf;
	rp = hs_addr;
	hstab.d_active = 0;
	if(rp->hscs1 & ERR){	/* error bit */
		deverror(bp, rp->hscs2, 0);
		rp->hscs1 = RCLR|GO;
		if (++hstab.d_errcnt <= 10) {
			hsstart();
			return;
		}
		bp->b_flags =| B_ERROR;
	}
	hstab.d_errcnt = 0;
	hstab.d_actf = bp->av_forw;
	bp->b_resid = (-rp->hswc)<<1;
	iodone(bp);
	hsstart();
}

hsread(dev)
{
	if (physck((dev&010) ? 2048 : 1024, B_READ))
		physio(hsstrategy, &rhsbuf, dev, B_READ);
}

hswrite(dev)
{
	if (physck((dev&010) ? 2048 : 1024, B_WRITE))
		physio(hsstrategy, &rhsbuf, dev, B_WRITE);
}
 = 0;
	if(rp->hscs1 & ERR){	/* error bit */
		deverror(bp, rp->hscs2, 0);
		rp->hscs1 = RCLR|GO;
		if (++hstab.d_errcnt <= 10) {
		#
/*
 * TJU16, TWU16 driver
 * Handles one TM02 controller, up to 4 TU16 slave transports
 * minor device classes:
 * bits 0,1: slave select
 * bit 2 off: rewind on close; on: position after first TM
 * bit 3 off: 800 bpi; on: 1600 bpi
 */

#include "../hd/param.h"
#include "../hd/buf.h"
#include "../hd/user.h"

#define NUNIT 4

struct {
	int	htcs1, htwc, htba, htfc;
	int	htcs2, htds, hter, htas;
	int	htck, htdb, htmr, htdt;
	int	htsn, httc, htbae, htcs3;
};

struct	devtab	httab;
struct	buf	rhtbuf, chtbuf;

char	h_openf[NUNIT];
int	h_den[NUNIT];
char	*h_blkno[NUNIT], *h_nxrec[NUNIT];

int	ht_addr;

#define	GO	01
#define	NOP	0
#define	WEOF	026
#define	SFORW	030
#define	SREV	032
#define	ERASE	024
#define	REW	06
#define	DCLR	010
#define P800	01300		/* 800 + pdp11 mode */
#define	P1600	02300		/* 1600 + pdp11 mode */
#define	IENABLE	0100
#define	RDY	0200
#define	TM	04
#define	DRY	0200
#define EOT	02000
#define CS	02000
#define COR	0100000
#define PES	040
#define WRL	04000
#define MOL	010000
#define ERR	040000
#define FCE	01000
#define	TRE	040000
#define HARD	064023	/* UNS|OPI|NEF|FMT|RMR|ILR|ILF */

#define	SIO	1
#define	SSFOR	2
#define SSREV	3
#define SRETRY	4
#define SCOM	5
#define SOK	6

htopen(dev, flag)
{
	register unit, ds;

	unit = dev&03;
	if (unit >= NUNIT || h_openf[unit]) {
		u.u_error = ENXIO;
		return;
	}
	h_den[unit] = (dev&010 ? P1600 : P800)|unit;
	h_blkno[unit] = 0;
	h_nxrec[unit] = -1;
	ds = hcommand(unit, NOP);
	if ((ds&MOL)==0 || (flag && (ds&WRL)))
		u.u_error = ENXIO;
	if (u.u_error==0)
		h_openf[unit]++;
}

htclose(dev, flag)
{
	register unit;
	register struct buf *bp;
	register struct devtab *dp;

	unit = dev&03;
	if (flag) {
		hcommand(unit, WEOF);
		hcommand(unit, WEOF);
	}
	if (dev&04) {
		if (flag)
			hcommand(unit, SREV); else
			{
				hcommand(unit, NOP);
				if (h_blkno[unit] < h_nxrec[unit])
					hcommand(unit, SFORW);
			}
	} else
		hcommand(unit, REW);
	dp = &httab;
	for (bp=dp->b_forw; bp!=dp; bp=bp->b_forw)
		if ((bp->b_dev&03)==unit)
			bp->b_dev =| 0100;
	h_openf[unit] = 0;
}

hcommand(unit, com)
{
	register struct buf *bp;
	register *rp;

	bp = &chtbuf;
	rp = ht_addr;
	spl5();
	while(bp->b_flags&B_BUSY) {
		bp->b_flags =| B_WANTED;
		sleep(bp, PRIBIO);
	}
	spl0();
	bp->b_dev = unit;
	bp->b_resid = com;
	bp->b_blkno = 0;
	bp->b_flags = B_BUSY|B_READ;
	htstrategy(bp);
	iowait(bp);
	if(bp->b_flags&B_WANTED)
		wakeup(bp);
	bp->b_flags = 0;
	return(bp->b_resid);
}

htstrategy(bp)
register struct buf *bp;
{
	register char **p;

	if (bp != &chtbuf) {
		p = &h_nxrec[bp->b_dev&03];
		if (bp->b_blkno > *p) {
			bp->b_flags =| B_ERROR;
			bp->b_error = ENXIO;
			iodone(bp);
			return;
		}
		if (bp->b_blkno == *p && bp->b_flags&B_READ) {
			bp->b_resid = 512;
			clrbuf(bp);
			iodone(bp);
			return;
		}
		if ((bp->b_flags&B_READ)==0)
			*p = bp->b_blkno + 1;
	}
	bp->av_forw = 0;
	spl5();
	if (httab.d_actf==0)
		httab.d_actf = bp;
	else
		httab.d_actl->av_forw = bp;
	httab.d_actl = bp;
	if (httab.d_active==0)
		htstart();
	spl0();
}

htstart()
{
	register struct buf *bp;
	register int unit;
	register *rp;
	char *blkno;

	rp = ht_addr;
    loop:
	if ((bp = httab.d_actf) == 0)
		return;
	unit = bp->b_dev&03;
	rp->htcs2 = 0;
	if((rp->httc&03777)!=h_den[unit])
		rp->httc = h_den[unit];
	blkno = h_blkno[unit];
	if (bp == &chtbuf) {
		if (bp->b_resid==NOP) {
			bp->b_resid = rp->htds;
			goto next;
		}
		httab.d_active = SCOM;
		rp->htfc = 0;
		rp->htcs1 = bp->b_resid|IENABLE|GO;
		return;
	}
	if (h_openf[unit] < 0 || bp->b_blkno > h_nxrec[unit])
		goto abort;
	if (blkno == bp->b_blkno) {
		httab.d_active = SIO;
		rhstart(bp, &rp->htfc, bp->b_wcount<<1, &rp->htbae);
	} else {
		if (blkno < bp->b_blkno) {
			httab.d_active = SSFOR;
			rp->htfc = blkno - bp->b_blkno;
			rp->htcs1 = SFORW|IENABLE|GO;
		} else {
			httab.d_active = SSREV;
			rp->htfc = bp->b_blkno - blkno;
			rp->htcs1 = SREV|IENABLE|GO;
		}
	}
	return;
    abort:
	b