/*	$NetBSD: qd.c,v 1.53 2012/10/27 17:18:37 chs Exp $	*/

/*-
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)qd.c	7.1 (Berkeley) 6/28/91
 */

/************************************************************************
*									*
*			Copyright (c) 1985-1988 by			*
*		Digital Equipment Corporation, Maynard, MA		*
*			All rights reserved.				*
*									*
*   This software is furnished under a license and may be used and	*
*   copied  only  in accordance with the terms of such license and	*
*   with the  inclusion  of  the  above  copyright  notice.   This	*
*   software  or  any  other copies thereof may not be provided or	*
*   otherwise made available to any other person.  No title to and	*
*   ownership of the software is hereby transferred.			*
*									*
*   The information in this software is subject to change  without	*
*   notice  and should not be construed as a commitment by Digital	*
*   Equipment Corporation.						*
*									*
*   Digital assumes no responsibility for the use  or  reliability	*
*   of its software on equipment which is not supplied by Digital.	*
*									*
*************************************************************************/

/*
 * qd.c - QDSS display driver for VAXSTATION-II GPX workstation
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: qd.c,v 1.53 2012/10/27 17:18:37 chs Exp $");

#include "opt_ddb.h"

#include "qd.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/tty.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/poll.h>
#include <sys/buf.h>

#include <dev/cons.h>

#include <sys/bus.h>
#include <machine/scb.h>

#ifdef __vax__
#include <machine/sid.h>
#include <sys/cpu.h>
#include <machine/pte.h>
#endif

#include <dev/qbus/ubavar.h>

#include <dev/qbus/qduser.h>
#include <dev/qbus/qdreg.h>
#include <dev/qbus/qdioctl.h>

#include "ioconf.h"

/*
 * QDSS driver status flags for tracking operational state
 */
struct qdflags {
	u_int inuse;		/* which minor dev's are in use now */
	u_int config;		/* I/O page register content */
	u_int mapped;		/* user mapping status word */
	u_int kernel_loop;	/* if kernel console is redirected */
	u_int user_dma;		/* DMA from user space in progress */
	u_short pntr_id;	/* type code of pointing device */
	u_short duart_imask;	/* shadowing for duart intrpt mask reg */
	u_short adder_ie;	/* shadowing for adder intrpt enbl reg */
	u_short curs_acc;	/* cursor acceleration factor */
	u_short curs_thr;	/* cursor acceleration threshold level */
	u_short tab_res;	/* tablet resolution factor */
	u_short selmask;	/* mask for active qd select entries */
};

/*
 * Softc struct to keep track of all states in this driver.
 */
struct	qd_softc {
	bus_space_tag_t	sc_iot;
	bus_space_handle_t sc_ioh;
	bus_dma_tag_t	sc_dmat;
};

/*
 * bit definitions for 'inuse' entry
 */
#define CONS_DEV	0x01
#define GRAPHIC_DEV	0x04

/*
 * bit definitions for 'mapped' member of flag structure
 */
#define MAPDEV		0x01		/* hardware is mapped */
#define MAPDMA		0x02		/* DMA buffer mapped */
#define MAPEQ		0x04		/* event queue buffer mapped */
#define MAPSCR		0x08		/* scroll param area mapped */
#define MAPCOLOR	0x10		/* color map writing buffer mapped */

/*
 * constants used in shared memory operations
 */
#define EVENT_BUFSIZE  1024	/* # of bytes per device's event buffer */
#define MAXEVENTS  ( (EVENT_BUFSIZE - sizeof(struct qdinput))	 \
	/ sizeof(struct _vs_event) )
#define DMA_BUFSIZ	(1024 * 10)
#define COLOR_BUFSIZ  ((sizeof(struct color_buf) + 512) & ~0x01FF)

/*
 * reference to an array of "uba_device" structures built by the auto
 * configuration program.  The uba_device structure decribes the device
 * sufficiently for the driver to talk to it.  The auto configuration code
 * fills in the uba_device structures (located in ioconf.c) from user
 * maintained info.
 */
struct uba_device *qdinfo[NQD];  /* array of pntrs to each QDSS's */
struct tty *qd_tty[NQD*4];	/* teletype structures for each.. */
volatile char *qvmem[NQD];
volatile struct pte *QVmap[NQD];
#define CHUNK	  (64 * 1024)
#define QMEMSIZE  (1024 * 1024 * 4)	/* 4 meg */

/*
 * static storage used by multiple functions in this code
 */
int Qbus_unmap[NQD];		/* Qbus mapper release code */
struct qdmap qdmap[NQD];	/* QDSS register map structure */
struct qdflags qdflags[NQD];	/* QDSS register map structure */
void *qdbase[NQD];		/* base address of each QDSS unit */
short qdopened[NQD];		/* graphics device is open exclusive use */

/*
 * the array "event_shared[]" is made up of a number of event queue buffers
 * equal to the number of QDSS's configured into the running kernel (NQD).
 * Each event queue buffer begins with an event queue header (struct qdinput)
 * followed by a group of event queue entries (struct _vs_event).  The array
 * "*eq_header[]" is an array of pointers to the start of each event queue
 * buffer in "event_shared[]".
 */
#define EQSIZE ((EVENT_BUFSIZE * NQD) + 512)

char event_shared[EQSIZE];	    /* reserve space for event bufs */
struct qdinput *eq_header[NQD];     /* event queue header pntrs */

/*
 * This allocation method reserves enough memory pages for NQD shared DMA I/O
 * buffers.  Each buffer must consume an integral number of memory pages to
 * guarantee that a following buffer will begin on a page boundary.  Also,
 * enough space is allocated so that the FIRST I/O buffer can start at the
 * 1st page boundary after "&DMA_shared".  Page boundaries are used so that
 * memory protections can be turned on/off for individual buffers.
 */
#define IOBUFSIZE  ((DMA_BUFSIZ * NQD) + 512)

char DMA_shared[IOBUFSIZE];	    /* reserve I/O buffer space */
struct DMAreq_header *DMAheader[NQD];  /* DMA buffer header pntrs */

/*
 * The driver assists a client in scroll operations by loading dragon
 * registers from an interrupt service routine.	The loading is done using
 * parameters found in memory shrade between the driver and it's client.
 * The scroll parameter structures are ALL loacted in the same memory page
 * for reasons of memory economy.
 */
char scroll_shared[2 * 512];	/* reserve space for scroll structs */
struct scroll *scroll[NQD];	/* pointers to scroll structures */

/*
 * the driver is programmable to provide the user with color map write
 * services at VSYNC interrupt time.  At interrupt time the driver loads
 * the color map with any user-requested load data found in shared memory
 */
#define COLOR_SHARED  ((COLOR_BUFSIZ * NQD) + 512)

char color_shared[COLOR_SHARED];      /* reserve space: color bufs */
struct color_buf *color_buf[NQD];     /* pointers to color bufs */

/*
 * mouse input event structures
 */
struct mouse_report last_rep[NQD];
struct mouse_report current_rep[NQD];

struct selinfo qdrsel[NQD];	/* process waiting for select */
struct _vs_cursor cursor[NQD];	/* console cursor */
int qdcount = 0;		/* count of successfully probed qd's */
int nNQD = NQD;
int DMAbuf_size = DMA_BUFSIZ;
int QDlast_DMAtype;		/* type of the last DMA operation */

/*
 * macro to get system time.  Used to time stamp event queue entries
 */
#define TOY ((time.tv_sec * 100) + (time.tv_usec / 10000))

void qd_attach(device_t, device_t, void *);
static int qd_match(device_t, cfdata_t, void *);

static void qddint(void *);	/* DMA gate array intrpt service */
static void qdaint(void *);	/* Dragon ADDER intrpt service */
static void qdiint(void *);

#define QDPRIOR (PZERO-1)		/* must be negative */
#define FALSE	0
#ifdef TRUE
#undef TRUE
#endif
#define TRUE	~FALSE
#define BAD	-1
#define GOOD	0

/*
 * macro to create a system virtual page number from system virtual adrs
 */
#define VTOP(x)  (((int)x & ~0xC0000000) >> VAX_PGSHIFT)

/*
 * QDSS register address offsets from start of QDSS address space
 */
#define QDSIZE	 (52 * 1024)	/* size of entire QDSS foot print */
#define TMPSIZE  (16 * 1024)	/* template RAM is 8k SHORT WORDS */
#define TMPSTART 0x8000		/* offset of template RAM from base adrs */
#define REGSIZE  (5 * 512)	/* regs touch 2.5k (5 pages) of addr space */
#define REGSTART 0xC000		/* offset of reg pages from base adrs */
#define ADDER	(REGSTART+0x000)
#define DGA	(REGSTART+0x200)
#define DUART	(REGSTART+0x400)
#define MEMCSR	(REGSTART+0x800)
#define CLRSIZE  (3 * 512)		/* color map size */
#define CLRSTART (REGSTART+0xA00)	/* color map start offset from base */
/*  0x0C00 really */
#define RED	(CLRSTART+0x000)
#define BLUE	(CLRSTART+0x200)
#define GREEN	(CLRSTART+0x400)


/*
 * QDSS minor device numbers.  The *real* minor device numbers are in
 * the bottom two bits of the major/minor device spec.  Bits 2 and up are
 * used to specify the QDSS device number (ie: which one?)
 */

#define CONS		0
#define GRAPHIC		2

/*
 * console cursor bitmap (white block cursor)
 */
short cons_cursor[32] = {
	/* A */ 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF,
	0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF,
	/* B */ 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF,
	0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF
};

/*
 * constants used in font operations
 */
#define CHARS		190			/* # of chars in the font */
#define CHAR_HEIGHT	15			/* char height in pixels */
#define CHAR_WIDTH	8			/* char width in pixels*/
#define FONT_WIDTH	(CHAR_WIDTH * CHARS)	/* font width in pixels */
#define ROWS		CHAR_HEIGHT
#define FONT_X		0			/* font's off screen adrs */
#define FONT_Y		(2048 - CHAR_HEIGHT)

/* Offset to second row characters (XXX - should remove) */
#define FONT_OFFSET	((MAX_SCREEN_X/CHAR_WIDTH)*CHAR_HEIGHT)

extern char q_font[];		/* reference font object code */
extern	u_short q_key[];	/* reference key xlation tables */
extern	u_short q_shift_key[];
extern	char *q_special[];

/*
 * definitions for cursor acceleration reporting
 */
#define ACC_OFF		0x01		/* acceleration is inactive */

/*
 * virtual console support.
 */
extern struct cdevsw *consops;
cons_decl(qd);
void setup_dragon(int);
void init_shared(int);
void clear_qd_screen(int);
void ldfont(int);
void ldcursor(int, short *);
void setup_input(int);
void blitc(int, u_char);
void scroll_up(volatile struct adder *);
void write_ID(volatile struct adder *, short, short);
int wait_status(volatile struct adder *, int);
void led_control(int, int, int);
void qdstart(struct tty *);
void qdearly(void);
int qdpolling = 0;

dev_type_open(qdopen);
dev_type_close(qdclose);
dev_type_read(qdread);
dev_type_write(qdwrite);
dev_type_ioctl(qdioctl);
dev_type_stop(qdstop);
dev_type_poll(qdpoll);
dev_type_kqfilter(qdkqfilter);

const struct cdevsw qd_cdevsw = {
	qdopen, qdclose, qdread, qdwrite, qdioctl,
	qdstop, notty, qdpoll, nommap, qdkqfilter,
};

/*
 * LK-201 state storage for input console keyboard conversion to ASCII
 */
struct q_keyboard {
	int shift;			/* state variables	*/
	int cntrl;
	int lock;
	int lastcode;			/* last keycode typed	*/
	unsigned kup[8];		/* bits for each keycode*/
	unsigned dkeys[8];		/* down/up mode keys	*/
	char last;			/* last character	*/
} q_keyboard;

/*
 * tty settings on first open
 */
#define IFLAG (BRKINT|ISTRIP|IXON|IXANY|ICRNL|IMAXBEL)
#define OFLAG (OPOST|OXTABS|ONLCR)
#define LFLAG (ISIG|ICANON|ECHO|IEXTEN)
#define CFLAG (PARENB|CREAD|CS7|CLOCAL)

/*
 * Kernel virtual addresses where we can map in the QBUS io page and the
 * QDSS memory during qdcninit.  pmap_bootstrap fills this in.
 */
void *qd_ubaio;

/* This is the QDSS unit 0 CSR.  It is hard-coded in here so that the
 * QDSS can be used as the console.  The console routines don't get
 * any config info.  The ROM also autodetects at this address, so
 * the console QDSS should be at this address.  Furthermore, nothing
 * else shuld be at this address instead because that would confuse the
 * ROM and this driver.
 */
#define QDSSCSR 0x1F00

volatile u_short *qdaddr;	/* Virtual address for QDSS CSR */

/*
 * This flag is set to 1 if the console initialization (qdcninit)
 * has been performed on qd0.  That initialization is required and must
 * be done before the device probe routine.
 */
int qd0cninited = 0, qd0iscons = 0;

/*
 * Do early check if the qdss is console. If not; don't allocate
 * any memory for it in bootstrap.
 */
void
qdearly(void)
{
	extern vaddr_t virtual_avail;
	int tmp;

	/* Make sure we're running on a system that can have a QDSS */
	if (vax_boardtype == VAX_BTYP_630)  {
		/* Now check some undocumented flag */
		if ((*(int *)(0x200B801E) & 0x60) == 0)
			/* The KA630 isn't using a QDSS as the console,
			 * so we won't either */
			return;
	} else if (vax_boardtype != VAX_BTYP_650)
		return;

	/* How to check for console on KA650? We assume that if there is a
	 * QDSS, it is console.
	 */
#define	QIOPAGE	0x20000000	/* XXX */
#define	UBAIOPAGES 16
	tmp = QIOPAGE + ubdevreg(QDSSCSR);
	if (badaddr((void *)tmp, sizeof(short)))
		return;

	MAPVIRT(qvmem[0], 64 * 1024 * NQD / VAX_NBPG);
	MAPVIRT(qd_ubaio, 16);
	pmap_map((int)qd_ubaio, QIOPAGE, QIOPAGE + UBAIOPAGES * VAX_NBPG,
	    VM_PROT_READ|VM_PROT_WRITE);
	qdaddr = (u_short *)((u_int)qd_ubaio + ubdevreg(QDSSCSR));
	qd0iscons = 1;
}

void
qdcnprobe(struct consdev *cndev)
{
	int i;

	cndev->cn_pri = CN_DEAD;

	if (mfpr(PR_MAPEN) == 0)
		return; /* Cannot use qd if vm system is OFF */

	if (!qd0iscons)
		return;

	/* Find the console device corresponding to the console QDSS */
	cndev->cn_dev = makedev(cdevsw_lookup_major(&qd_cdevsw), 0);
	cndev->cn_pri = CN_INTERNAL;
	return;
}


/*
 * Init QDSS as console (before probe routine)
 */
void
qdcninit(struct consdev *cndev)
{
	void *phys_adr;		/* physical QDSS base adrs */
	u_int mapix;			/* index into QVmap[] array */
	int unit;

	/* qdaddr must point to CSR for this unit! */

	/* The console QDSS is QDSS unit 0 */
	unit = 0;

	/*
	 * Map q-bus memory used by qdss. (separate map)
	 */
	mapix = QMEMSIZE - (CHUNK * (unit + 1));
#define	QMEM 0x30000000
	(int)phys_adr = QMEM + mapix;
	pmap_map((int)(qvmem[0]), (int)phys_adr, (int)(phys_adr + (CHUNK*NQD)),
				    VM_PROT_READ|VM_PROT_WRITE);

	/*
	 * Set QVmap to point to page table entries for what we just
	 * mapped.
	 */
	QVmap[0] = (struct pte *)kvtopte(qvmem[0]);

	/*
	 * tell QDSS which Q memory address base to decode
	 * (shifted right 16 bits - its in 64K units)
	 */
	*qdaddr = (u_short)((int)mapix >> 16);
	qdflags[unit].config = *(u_short *)qdaddr;

	/*
	 * load qdmap struct with the virtual addresses of the QDSS elements
	 */
	qdbase[unit] = (void *) (qvmem[0]);
	qdmap[unit].template = qdbase[unit] + TMPSTART;
	qdmap[unit].adder = qdbase[unit] + ADDER;
	qdmap[unit].dga = qdbase[unit] + DGA;
	qdmap[unit].duart = qdbase[unit] + DUART;
	qdmap[unit].memcsr = qdbase[unit] + MEMCSR;
	qdmap[unit].red = qdbase[unit] + RED;
	qdmap[unit].blue = qdbase[unit] + BLUE;
	qdmap[unit].green = qdbase[unit] + GREEN;

	qdflags[unit].duart_imask = 0;	/* init shadow variables */

	/*
	 * init the QDSS
	 */

	*(short *)qdmap[unit].memcsr |= SYNC_ON; /* once only: turn on sync */

	cursor[unit].x = 0;
	cursor[unit].y = 0;
	init_shared(unit);		/* init shared memory */
	setup_dragon(unit);		/* init the ADDER/VIPER stuff */
	clear_qd_screen(unit);		/* clear the screen */
	ldfont(unit);			/* load the console font */
	ldcursor(unit, cons_cursor);	/* load default cursor map */
	setup_input(unit);		/* init the DUART */
	selinit(&qdrsel[unit]);

	/* Set flag so probe knows */
	qd0cninited = 1;
} /* qdcninit */

/* see <sys/device.h> */
CFATTACH_DECL_NEW(qd, sizeof(struct qd_softc),
    qd_match, qd_attach, NULL, NULL);

#define	QD_RCSR(reg) \
	bus_space_read_2(sc->sc_iot, sc->sc_ioh, reg)
#define	QD_WCSR(reg, val) \
	bus_space_write_2(sc->sc_iot, sc->sc_ioh, reg, val)

/*
 *  Configure QDSS into Q memory and make it intrpt.
 *
 *  side effects: QDSS gets mapped into Qbus memory space at the first
 *		 vacant 64kb boundary counting back from the top of
 *		 Qbus memory space (qvmem+4mb)
 *
 *  return: QDSS bus request level and vector address returned in
 *	   registers by UNIX convention.
 *
 */
static int
qd_match(device_t parent, cfdata_t match, void *aux)
{
	struct qd_softc ssc;
	struct qd_softc *sc = &ssc;
	struct uba_attach_args *ua = aux;
	struct uba_softc *uh = device_private(parent);
	int unit;
	volatile struct dga *dga;	/* pointer to gate array structure */
	int vector;
#ifdef notdef
	int *ptep;			/* page table entry pointer */
	void *phys_adr;		/* physical QDSS base adrs */
	u_int mapix;
#endif

	/* Create a "fake" softc with only a few fields used. */
	sc->sc_iot = ua->ua_iot;
	sc->sc_ioh = ua->ua_ioh;
	sc->sc_dmat = ua->ua_dmat;
	/*
	 * calculate board unit number from I/O page register address
	 */
	unit = (int) (((int)sc->sc_ioh >> 1) & 0x0007);

	/*
	 * QDSS regs must be mapped to Qbus memory space at a 64kb
	 * physical boundary.  The Qbus memory space is mapped into
	 * the system memory space at config time.  After config
	 * runs, "qvmem[0]" (ubavar.h) holds the system virtual adrs
	 * of the start of Qbus memory.   The Qbus memory page table
	 * is found via an array of pte ptrs called "QVmap[]" (ubavar.h)
	 * which is also loaded at config time.   These are the
	 * variables used below to find a vacant 64kb boundary in
	 * Qbus memory, and load it's corresponding physical adrs
	 * into the QDSS's I/O page CSR.
	 */

	/*
	 * Only if QD is the graphics device.
	 */

	/* if this QDSS is NOT the console, then do init here.. */

	if (unit != 0) {
		printf("qd: can't support two qdss's (yet)\n");
#ifdef notdef	/* can't test */
		if (v_consputc != qdputc  ||  unit != 0) {

			/*
			* read QDSS config info
			*/
			qdflags[unit].config = *(u_short *)reg;

			/*
			* find an empty 64kb adrs boundary
			*/

			qdbase[unit] = (void *) (qvmem[0] + QMEMSIZE - CHUNK);

			/*
			* find the cpusw entry that matches this machine.
			*/
			cpup = &cpusw[cpu];
			while (!(BADADDR(qdbase[unit], sizeof(short))))
				qdbase[unit] -= CHUNK;

			/*
			* tell QDSS which Q memory address base to decode
			*/
			mapix = (int) (VTOP(qdbase[unit]) - VTOP(qvmem[0]));
			ptep = (int *) QVmap[0] + mapix;
			phys_adr = (void *)(((int)*ptep&0x001FFFFF)<<VAX_PGSHIFT);
			*(u_short *)reg = (u_short) ((int)phys_adr >> 16);

			/*
			* load QDSS adrs map with system addresses
			* of device regs
			*/
			qdmap[unit].template = qdbase[unit] + TMPSTART;
			qdmap[unit].adder = qdbase[unit] + ADDER;
			qdmap[unit].dga = qdbase[unit] + DGA;
			qdmap[unit].duart = qdbase[unit] + DUART;
			qdmap[unit].memcsr = qdbase[unit] + MEMCSR;
			qdmap[unit].red = qdbase[unit] + RED;
			qdmap[unit].blue = qdbase[unit] + BLUE;
			qdmap[unit].green = qdbase[unit] + GREEN;

			/* device init */

			cursor[unit].x = 0;
			cursor[unit].y = 0;
			init_shared(unit);		/* init shared memory */
			setup_dragon(unit);	/* init the ADDER/VIPER stuff */
			ldcursor(unit, cons_cursor);	/* load default cursor map */
			setup_input(unit);		/* init the DUART */
			clear_qd_screen(unit);
			ldfont(unit);			/* load the console font */

			/* once only: turn on sync */

			*(short *)qdmap[unit].memcsr |= SYNC_ON;
		}
#endif /*notdef*/
	} else {
		/* We are dealing with qd0 */

		if (!qd0cninited) {
			/*
			 * qd0 has not been initiallized as the console.
			 * We need to do some initialization now
			 *
			 * XXX
			 * However, if the QDSS is not the console then
			 * that stupid undocumented bit (see qdcnprobe)
			 * is cleared.  Then the QDSS refuses to work.
			 * (What did the ROM do to it!?)
			 * XXX
			 */
			 return 0;

#if 0
			 qdaddr = (void *)reg;

			 /* Lame probe for QDSS.  Should be ok for qd0 */
			 if (badaddr((void *)qdaddr, sizeof(short)))
				 return 0;

			 qdcninit(NULL);
#endif
		}
	}


	/*
	* The QDSS interrupts at HEX vectors xx0 (DMA) xx4
	* (ADDER) and xx8 (DUART).  Therefore, we take three
	* vectors from the vector pool, and then continue
	* to take them until we get a xx0 HEX vector.  The
	* pool provides vectors in contiguous decending
	* order.
	*/

	vector = (uh->uh_lastiv -= 4*3);	/* take three vectors */

	while (vector & 0x0F) {		   /* if lo nibble != 0.. */
		/* ..take another vector */
		vector = (uh->uh_lastiv -= 4);
	}

	/*
	* setup DGA to do a DMA interrupt (transfer count = 0)
	*/
	dga = (struct dga *) qdmap[unit].dga;
	dga->csr = (short) HALT;	/* disable everything */
	dga->ivr = (short) vector;	/* load intrpt base vector */
	dga->bytcnt_lo = (short) 0;	/* DMA xfer count = 0 */
	dga->bytcnt_hi = (short) 0;

	/*
	* turn on DMA interrupts
	*/
	dga->csr &= ~SET_DONE_FIFO;
	dga->csr |= DMA_IE | DL_ENB;

	DELAY(20000);			/* wait for the intrpt */
	dga->csr = HALT;		/* stop the wheels */

	/*
	* score this as an existing qdss
	*/
	qdcount++;

	return 1;
} /* qdprobe */


void
qd_attach(device_t parent, device_t self, void *aux)
{
	struct uba_attach_args *ua = aux;
	int unit;	/* QDSS module # for this call */

	printf("\n");

	unit = device_unit(self);		/* get QDSS number */

	/* Set interrupt vectors for interrupt handlers */

	uba_intr_establish(ua->ua_icookie, ua->ua_cvec    , qddint, self);
	uba_intr_establish(ua->ua_icookie, ua->ua_cvec + 4, qdaint, self);
	uba_intr_establish(ua->ua_icookie, ua->ua_cvec + 8, qdiint, self);

	/*
	* init "qdflags[]" for this QDSS
	*/
	qdflags[unit].inuse = 0;	/* init inuse variable EARLY! */
	qdflags[unit].mapped = 0;
	qdflags[unit].kernel_loop = -1;
	qdflags[unit].user_dma = 0;
	qdflags[unit].curs_acc = ACC_OFF;
	qdflags[unit].curs_thr = 128;
	qdflags[unit].tab_res = 2;	/* default tablet resolution factor */
	qdflags[unit].duart_imask = 0;	/* init shadow variables */
	qdflags[unit].adder_ie = 0;

	/*
	* init structures used in kbd/mouse interrupt service.	This code must
	* come after the "init_shared()" routine has run since that routine
	* inits the eq_header[unit] structure used here.
	*/

	/*
	* init the "latest mouse report" structure
	*/
	last_rep[unit].state = 0;
	last_rep[unit].dx = 0;
	last_rep[unit].dy = 0;
	last_rep[unit].bytcnt = 0;

	/*
	* init the event queue (except mouse position)
	*/
	eq_header[unit]->header.events =
	    (struct _vs_event *)((int)eq_header[unit] + sizeof(struct qdinput));

	eq_header[unit]->header.size = MAXEVENTS;
	eq_header[unit]->header.head = 0;
	eq_header[unit]->header.tail = 0;

	/*
	 * open exclusive for graphics device.
	 */
	qdopened[unit] = 0;

} /* qdattach */


/*ARGSUSED*/
int
qdopen(dev_t dev, int flag, int mode, struct proc *p)
{
	volatile struct dga *dga;	/* ptr to gate array struct */
	struct tty *tp;
	volatile struct duart *duart;
	struct uba_softc *sc;
	int unit;
	int minor_dev;

	minor_dev = minor(dev); /* get QDSS minor device number */
	unit = minor_dev >> 2;

	/*
	* check for illegal conditions
	*/
	sc = device_lookup_private(&qd_cd, unit);
	if (sc == NULL)
		return ENXIO;

	duart = (struct duart *) qdmap[unit].duart;
	dga = (struct dga *) qdmap[unit].dga;

	if ((minor_dev & 0x03) == 2) {
		/*
		* this is the graphic device...
		*/
		if (qdopened[unit] != 0)
			return(EBUSY);
		else
			qdopened[unit] = 1;
		qdflags[unit].inuse |= GRAPHIC_DEV;  /* graphics dev is open */
		/*
		 * enble kbd & mouse intrpts in DUART mask reg
		 */
		qdflags[unit].duart_imask |= 0x22;
		duart->imask = qdflags[unit].duart_imask;
	} else {
		/* Only one console */
		if (minor_dev) return ENXIO;

		/* If not done already, allocate tty structure */
		if (qd_tty[minor_dev] == NULL)
			qd_tty[minor_dev] = tty_alloc();

		if (qd_tty[minor_dev] == NULL)
			return ENXIO;

		/*
		* this is the console
		*/
		qdflags[unit].inuse |= CONS_DEV;  /* mark console as open */
		dga->csr |= CURS_ENB;
		qdflags[unit].duart_imask |= 0x02;
		duart->imask = qdflags[unit].duart_imask;
		/*
		* some setup for tty handling
		*/
		tp = qd_tty[minor_dev];
		/* tp->t_addr = ui->ui_addr; */
		tp->t_oproc = qdstart;
		tp->t_dev = dev;
		if ((tp->t_state & TS_ISOPEN) == 0) {
			ttychars(tp);
			tp->t_ispeed = B9600;
			tp->t_ospeed = B9600;
			tp->t_state = TS_ISOPEN | TS_CARR_ON;
			tp->t_iflag = TTYDEF_IFLAG;
			tp->t_oflag = TTYDEF_OFLAG;
			tp->t_lflag = TTYDEF_LFLAG;
			tp->t_cflag = TTYDEF_CFLAG;
			ttsetwater(tp);
		}
		/*
		* enable intrpts, open line discipline
		*/
		dga->csr |= GLOBAL_IE;	/* turn on the interrupts */
		return ((*tp->t_linesw->l_open)(dev, tp));
	}
	dga->csr |= GLOBAL_IE;	/* turn on the interrupts */
	return(0);

} /* qdopen */

/*ARGSUSED*/
int
qdclose(dev_t dev, int flag, int mode, struct proc *p)
{
	struct tty *tp;
	struct qdmap *qd;
	volatile int *ptep;
	volatile struct dga *dga;	/* gate array register map pointer */
	volatile struct duart *duart;
	volatile struct adder *adder;
	int unit;
	int minor_dev;
	u_int mapix;
	int i;				/* SIGNED index */
	struct uba_softc *uh;

	minor_dev = minor(dev);		/* get minor device number */
	unit = minor_dev >> 2;		/* get QDSS number */
	qd = &qdmap[unit];

	uh = device_private(device_parent(device_lookup(&qd_cd, unit)));


	if ((minor_dev & 0x03) == 2) {
		/*
		* this is the graphic device...
		*/
		if (qdopened[unit] != 1)
			return(EBUSY);
		else
			qdopened[unit] = 0;	/* allow it to be re-opened */
		/*
		* re-protect device memory
		*/
		if (qdflags[unit].mapped & MAPDEV) {
			/*
			* TEMPLATE RAM
			*/
			mapix = VTOP((int)qd->template) - VTOP(qvmem[0]);
			ptep = (int *)(QVmap[0] + mapix);
			for (i = 0; i < vax_btop(TMPSIZE); i++, ptep++)
				*ptep = (*ptep & ~PG_PROT) | PG_V | PG_KW;
			/*
			* ADDER
			*/
			mapix = VTOP((int)qd->adder) - VTOP(qvmem[0]);
			ptep = (int *)(QVmap[0] + mapix);
			for (i = 0; i < vax_btop(REGSIZE); i++, ptep++)
				*ptep = (*ptep & ~PG_PROT) | PG_V | PG_KW;
			/*
			* COLOR MAPS
			*/
			mapix = VTOP((int)qd->red) - VTOP(qvmem[0]);
			ptep = (int *)(QVmap[0] + mapix);
			for (i = 0; i < vax_btop(CLRSIZE); i++, ptep++)
				*ptep = (*ptep & ~PG_PROT) | PG_V | PG_KW;
		}

		/*
		* re-protect DMA buffer and free the map registers
		*/
		if (qdflags[unit].mapped & MAPDMA) {
			panic("Unmapping unmapped buffer");
#ifdef notyet
/*
 * Ragge 990620:
 * Can't happen because the buffer can't be mapped.
 */
			dga = (struct dga *) qdmap[unit].dga;
			adder = (struct adder *) qdmap[unit].adder;
			dga->csr &= ~DMA_IE;
			dga->csr &= ~0x0600;	/* kill DMA */
			adder->command = CANCEL;
			/*
			 * if DMA was running, flush spurious intrpt
			 */
			if (dga->bytcnt_lo != 0) {
				dga->bytcnt_lo = 0;
				dga->bytcnt_hi = 0;
				DMA_SETIGNORE(DMAheader[unit]);
				dga->csr |= DMA_IE;
				dga->csr &= ~DMA_IE;
			}
			ptep = (int *)
			   ((VTOP(DMAheader[unit]*4)) + (mfpr(PR_SBR)|0x80000000));
			for (i = 0; i < vax_btop(DMAbuf_size); i++, ptep++)
				*ptep = (*ptep & ~PG_PROT) | PG_V | PG_KW;
			ubarelse(uh, &Qbus_unmap[unit]);
#endif
		}

		/*
		* re-protect 1K (2 pages) event queue
		*/
		if (qdflags[unit].mapped & MAPEQ) {
			ptep = (int *)
			   ((VTOP(eq_header[unit])*4) + (mfpr(PR_SBR)|0x80000000));
			*ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V; ptep++;
			*ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V;
		}
		/*
		* re-protect scroll param area and disable scroll intrpts
		*/
		if (qdflags[unit].mapped & MAPSCR) {
			ptep = (int *) ((VTOP(scroll[unit]) * 4)
				+ (mfpr(PR_SBR) | 0x80000000));
			/*
			 * re-protect 512 scroll param area
			 */
			*ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V;
			adder = (struct adder *) qdmap[unit].adder;
			qdflags[unit].adder_ie &= ~FRAME_SYNC;
			adder->interrupt_enable = qdflags[unit].adder_ie;
		}
		/*
		* re-protect color map write buffer area and kill intrpts
		*/
		if (qdflags[unit].mapped & MAPCOLOR) {
			ptep = (int *) ((VTOP(color_buf[unit]) * 4)
				+ (mfpr(PR_SBR) | 0x80000000));
			*ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V; ptep++;
			*ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V;
			color_buf[unit]->status = 0;
			adder = (struct adder *) qdmap[unit].adder;
			qdflags[unit].adder_ie &= ~VSYNC;
			adder->interrupt_enable = qdflags[unit].adder_ie;
		}
		mtpr(0, PR_TBIA);
		/* flag everything now unmapped */
		qdflags[unit].mapped = 0;
		qdflags[unit].inuse &= ~GRAPHIC_DEV;
		qdflags[unit].curs_acc = ACC_OFF;
		qdflags[unit].curs_thr = 128;
		/*
		* restore the console
		*/
		dga = (struct dga *) qdmap[unit].dga;
		adder = (struct adder *) qdmap[unit].adder;
		dga->csr &= ~DMA_IE;
		dga->csr &= ~0x0600;	/* halt the DMA! (just in case...) */
		dga->csr |= DMA_ERR;	/* clear error condition */
		adder->command = CANCEL;
		/*
		 * if DMA was running, flush spurious intrpt
		 */
		if (dga->bytcnt_lo != 0) {
			dga->bytcnt_lo = 0;
			dga->bytcnt_hi = 0;
			DMA_SETIGNORE(DMAheader[unit]);
			dga->csr |= DMA_IE;
			dga->csr &= ~DMA_IE;
		}
		init_shared(unit);		/* init shared memory */
		setup_dragon(unit);		/* init ADDER/VIPER */
		ldcursor(unit, cons_cursor);	/* load default cursor map */
		setup_input(unit);		/* init the DUART */
		ldfont(unit);
		cursor[unit].x = 0;
		cursor[unit].y = 0;
		/*
		 * shut off the mouse rcv intrpt and turn on kbd intrpts
		 */
		duart = (struct duart *) qdmap[unit].duart;
		qdflags[unit].duart_imask &= ~(0x20);
		qdflags[unit].duart_imask |= 0x02;
		duart->imask = qdflags[unit].duart_imask;
		/*
		* shut off interrupts if all is closed
		*/
		if (!(qdflags[unit].inuse & CONS_DEV)) {
			dga = (struct dga *) qdmap[unit].dga;
			dga->csr &= ~(GLOBAL_IE | DMA_IE);
		}
	} else {
		/*
		* this is the console
		*/
		tp = qd_tty[minor_dev];
		(*tp->t_linesw->l_close)(tp, flag);
		ttyclose(tp);
		tp->t_state = 0;
		qdflags[unit].inuse &= ~CONS_DEV;
		/*
		* if graphics device is closed, kill interrupts
		*/
		if (!(qdflags[unit].inuse & GRAPHIC_DEV)) {
			dga = (struct dga *) qdmap[unit].dga;
			dga->csr &= ~(GLOBAL_IE | DMA_IE);
		}
	}

	return(0);

} /* qdclose */

int
qdioctl(dev_t dev, u_long cmd, void *datap, int flags, struct proc *p)
{
	volatile int *ptep;	/* page table entry pointer */
	int mapix;		/* QVmap[] page table index */
	struct _vs_event *event;
	struct tty *tp;
	int i;
	struct qdmap *qd;		/* pointer to device map struct */
	volatile struct dga *dga;	/* Gate Array reg structure pntr */
	volatile struct duart *duart;	/* DUART reg structure pointer */
	volatile struct adder *adder;	/* ADDER reg structure pointer */
	struct prgkbd *cmdbuf;
	struct prg_cursor *curs;
	struct _vs_cursor *pos;
	int unit = minor(dev) >> 2;	/* number of caller's QDSS */
	u_int minor_dev = minor(dev);
	int error;
	int s;
	short *temp;			/* a pointer to template RAM */
	struct uba_softc *uh;

	uh = device_private(device_parent(device_lookup(&qd_cd, unit)));

	/*
	* service graphic device ioctl commands
	*/
	switch (cmd) {

	case QD_GETEVENT:
		/*
		* extract the oldest event from the event queue
		*/
		if (ISEMPTY(eq_header[unit])) {
			event = (struct _vs_event *) datap;
			event->vse_device = VSE_NULL;
			break;
		}
		event = (struct _vs_event *) GETBEGIN(eq_header[unit]);
		s = spl5();
		GETEND(eq_header[unit]);
		splx(s);
		memcpy(datap, (void *)event, sizeof(struct _vs_event));
		break;

	case QD_RESET:
		/*
		* init the dragon stuff, DUART, and driver variables
		*/
		init_shared(unit);		/* init shared memory */
		setup_dragon(unit);		/* init the ADDER/VIPER stuff */
		clear_qd_screen(unit);
		ldcursor(unit, cons_cursor);	/* load default cursor map */
		ldfont(unit);			/* load the console font */
		setup_input(unit);		/* init the DUART */
		break;

	case QD_SET:
		/*
		* init the DUART and driver variables
		*/
		init_shared(unit);
		setup_input(unit);
		break;

	case QD_CLRSCRN:
		/*
		* clear the QDSS screen.  (NOTE that this reinits the dragon)
		*/
#ifdef notdef	/* has caused problems and isn't necessary */
		setup_dragon(unit);
		clear_qd_screen(unit);
#endif
		break;

	case QD_WTCURSOR:
		/*
		* load a cursor into template RAM
		*/
		ldcursor(unit, (short *)datap);
		break;

	case QD_RDCURSOR:

		temp = (short *) qdmap[unit].template;
		/*
		 * cursor is 32 WORDS from the end of the 8k WORD...
		 *  ...template space
		 */
		temp += (8 * 1024) - 32;
		for (i = 0; i < 32; ++i, datap += sizeof(short))
			*(short *)datap = *temp++;
		break;

	case QD_POSCURSOR:
		/*
		* position the mouse cursor
		*/
		dga = (struct dga *) qdmap[unit].dga;
		pos = (struct _vs_cursor *) datap;
		s = spl5();
		dga->x_cursor = TRANX(pos->x);
		dga->y_cursor = TRANY(pos->y);
		eq_header[unit]->curs_pos.x = pos->x;
		eq_header[unit]->curs_pos.y = pos->y;
		splx(s);
		break;

	case QD_PRGCURSOR:
		/*
		* set the cursor acceleration factor
		*/
		curs = (struct prg_cursor *) datap;
		s = spl5();
		qdflags[unit].curs_acc = curs->acc_factor;
		qdflags[unit].curs_thr = curs->threshold;
		splx(s);
		break;

	case QD_MAPDEVICE:
		/*
		* enable 'user write' to device pages
		*/
		qdflags[unit].mapped |= MAPDEV;
		qd = (struct qdmap *) &qdmap[unit];
		/*
		* enable user write to template RAM
		*/
		mapix = VTOP((int)qd->template) - VTOP(qvmem[0]);
		ptep = (int *)(QVmap[0] + mapix);
		for (i = 0; i < vax_btop(TMPSIZE); i++, ptep++)
			*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;

		/*
		* enable user write to registers
		*/
		mapix = VTOP((int)qd->adder) - VTOP(qvmem[0]);
		ptep = (int *)(QVmap[0] + mapix);
		for (i = 0; i < vax_btop(REGSIZE); i++, ptep++)
			*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;

		/*
		* enable user write to color maps
		*/
		mapix = VTOP((int)qd->red) - VTOP(qvmem[0]);
		ptep = (int *)(QVmap[0] + mapix);
		for (i = 0; i < vax_btop(CLRSIZE); i++, ptep++)
			*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;

		/*
		* enable user write to DUART
		*/
		mapix = VTOP((int)qd->duart) - VTOP(qvmem[0]);
		ptep = (int *)(QVmap[0] + mapix);
		*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V; /* duart page */

		mtpr(0, PR_TBIA);		/* invalidate translation buffer */

		/*
		 * stuff qdmap structure in return buffer
		 */
		memcpy(datap, (void *)qd, sizeof(struct qdmap));

		break;

#ifdef notyet
/*
 * Ragge 999620:
 * Can't map in the graphic buffer into user space for now.
 * The best way to fix this is to convert this driver to wscons.
 */
	case QD_MAPIOBUF:
		/*
		 * do setup for DMA by user process
		 *
		 * set 'user write enable' bits for DMA buffer
		 */
		qdflags[unit].mapped |= MAPDMA;
		ptep = (int *) ((VTOP(DMAheader[unit]) * 4)
			+ (mfpr(PR_SBR) | 0x80000000));
		for (i = 0; i < vax_btop(DMAbuf_size); i++, ptep++)
			*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
		mtpr(0, PR_TBIA);	/* invalidate translation buffer */
		/*
		* set up QBUS map registers for DMA
		*/
		DMAheader[unit]->QBAreg =
		    uballoc(uh, (void *)DMAheader[unit], DMAbuf_size, 0);
		if (DMAheader[unit]->QBAreg == 0)
		    printf("qd%d: qdioctl: QBA setup error\n", unit);
		Qbus_unmap[unit] = DMAheader[unit]->QBAreg;
		DMAheader[unit]->QBAreg &= 0x3FFFF;
		/*
		* return I/O buf adr
		*/
		*(int *)datap = (int) DMAheader[unit];
		break;
#endif

	case QD_MAPSCROLL:
		/*
		* map the shared scroll param area and enable scroll interpts
		*/
		qdflags[unit].mapped |= MAPSCR;
		ptep = (int *) ((VTOP(scroll[unit]) * 4)
			+ (mfpr(PR_SBR) | 0x80000000));
		/*
		 * allow user write to scroll area
		 */
		*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
		mtpr(0, PR_TBIA);			/* invalidate translation buf */
		scroll[unit]->status = 0;
		adder = (struct adder *) qdmap[unit].adder;
		qdflags[unit].adder_ie |= FRAME_SYNC;
		adder->interrupt_enable = qdflags[unit].adder_ie;
		*(int *)datap = (int) scroll[unit]; /* return scroll area */
		break;

	case QD_UNMAPSCROLL:
		/*
		* unmap shared scroll param area and disable scroll intrpts
		*/
		if (qdflags[unit].mapped & MAPSCR) {
			qdflags[unit].mapped &= ~MAPSCR;
			ptep = (int *) ((VTOP(scroll[unit]) * 4)
				+ (mfpr(PR_SBR) | 0x80000000));
			/*
			 * re-protect 512 scroll param area
			 */
			*ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V;
			mtpr(0, PR_TBIA);	/* smash CPU's translation buf */
			adder = (struct adder *) qdmap[unit].adder;
			qdflags[unit].adder_ie &= ~FRAME_SYNC;
			adder->interrupt_enable = qdflags[unit].adder_ie;
		}
		break;

	case QD_MAPCOLOR:
		/*
		* map shared color map write buf and turn on vsync intrpt
		*/
		qdflags[unit].mapped |= MAPCOLOR;
		ptep = (int *) ((VTOP(color_buf[unit]) * 4)
			+ (mfpr(PR_SBR) | 0x80000000));
		/*
		 * allow user write to color map write buffer
		 */
		*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V; ptep++;
		*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
		mtpr(0, PR_TBIA);			/* clr CPU translation buf */
		adder = (struct adder *) qdmap[unit].adder;
		qdflags[unit].adder_ie |= VSYNC;
		adder->interrupt_enable = qdflags[unit].adder_ie;
		/*
		 * return color area address
		 */
		*(int *)datap = (int) color_buf[unit];
		break;

	case QD_UNMAPCOLOR:
		/*
		 * unmap shared color map write buffer and kill VSYNC intrpts
		 */
		if (qdflags[unit].mapped & MAPCOLOR) {
			qdflags[unit].mapped &= ~MAPCOLOR;
			ptep = (int *) ((VTOP(color_buf[unit]) * 4)
				+ (mfpr(PR_SBR) | 0x80000000));
			/*
			 * re-protect color map write buffer
			 */
			*ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V; ptep++;
			*ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V;
			mtpr(0, PR_TBIA);
			adder = (struct adder *) qdmap[unit].adder;
			qdflags[unit].adder_ie &= ~VSYNC;
			adder->interrupt_enable = qdflags[unit].adder_ie;
		}
		break;

	case QD_MAPEVENT:
		/*
		* give user write access to the event queue
		*/
		qdflags[unit].mapped |= MAPEQ;
		ptep = (int *) ((VTOP(eq_header[unit]) * 4)
			+ (mfpr(PR_SBR) | 0x80000000));
		/*
		 * allow user write to 1K event queue
		 */
		*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V; ptep++;
		*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
		mtpr(0, PR_TBIA);			/* clr CPU translation buf */
		/*
		 * return event queue address
		 */
		*(int *)datap = (int)eq_header[unit];
		break;

	case QD_PRGKBD:
		/*
		* pass caller's programming commands to LK201
		*/
		duart = (struct duart *)qdmap[unit].duart;
		cmdbuf = (struct prgkbd *)datap;    /* pnt to kbd cmd buf */
		/*
		* send command
		*/
		for (i = 1000; i > 0; --i) {
			if (duart->statusA&XMT_RDY) {
				duart->dataA = cmdbuf->cmd;
				break;
			}
		}
		if (i == 0) {
			printf("qd%d: qdioctl: timeout on XMT_RDY [1]\n", unit);
			break;
		}
		/*
		* send param1?
		*/
		if (cmdbuf->cmd & LAST_PARAM)
			break;
		for (i = 1000; i > 0; --i) {
			if (duart->statusA&XMT_RDY) {
				duart->dataA = cmdbuf->param1;
				break;
			}
		}
		if (i == 0) {
			printf("qd%d: qdioctl: timeout on XMT_RDY [2]\n", unit);
			break;
		}
		/*
		* send param2?
		*/
		if (cmdbuf->param1 & LAST_PARAM)
		    break;
		for (i = 1000; i > 0; --i) {
			if (duart->statusA&XMT_RDY) {
				duart->dataA = cmdbuf->param2;
				break;
			}
		}
		if (i == 0) {
			printf("qd%d: qdioctl: timeout on XMT_RDY [3]\n", unit);
			break;
		}
		break;

	case QD_PRGMOUSE:
		/*
		* pass caller's programming commands to the mouse
		*/
		duart = (struct duart *) qdmap[unit].duart;
		for (i = 1000; i > 0; --i) {
			if (duart->statusB&XMT_RDY) {
				duart->dataB = *datap;
				break;
			}
		}
		if (i == 0) {
			printf("qd%d: qdioctl: timeout on XMT_RDY [4]\n", unit);
		}
		break;

	case QD_RDCONFIG:
		/*
		* get QDSS configuration word and return it
		*/
		*(short *)datap = qdflags[unit].config;
		break;

	case QD_KERN_LOOP:
	case QD_KERN_UNLOOP:
		/*
		 * vestige from ultrix.  BSD uses TIOCCONS to redirect
		 * kernel console output.
		 */
		break;

	case QD_PRGTABLET:
		/*
		* program the tablet
		*/
		duart = (struct duart *) qdmap[unit].duart;
		for (i = 1000; i > 0; --i) {
			if (duart->statusB&XMT_RDY) {
				duart->dataB = *datap;
				break;
			}
		}
		if (i == 0) {
			printf("qd%d: qdioctl: timeout on XMT_RDY [5]\n", unit);
		}
		break;

	case QD_PRGTABRES:
		/*
		* program the tablet report resolution factor
		*/
		qdflags[unit].tab_res = *(short *)datap;
		break;

	default:
		/*
		* service tty ioctl's
		*/
		if (!(minor_dev & 0x02)) {
			tp = qd_tty[minor_dev];
			error =

		   (*tp->t_linesw->l_ioctl)(tp, cmd, datap, flags, p);
			if (error != EPASSTHROUGH) {
				return(error);
			}
			return ttioctl(tp, cmd, datap, flags, p);
		}
		break;
	}

	return(0);

} /* qdioctl */


int
qdpoll(dev_t dev, int events, struct proc *p)
{
	int s;
	int unit;
	struct tty *tp;
	u_int minor_dev = minor(dev);
	int revents = 0;

	s = spl5();
	unit = minor_dev >> 2;

	if ((minor_dev & 0x03) == 2)  {
		/*
		 * This is a graphics device, so check for events.
		 */

		if (events & (POLLIN | POLLRDNORM))
			if(!(ISEMPTY(eq_header[unit])))
				revents |= events & (POLLIN | POLLRDNORM);

		if (events & (POLLOUT | POLLWRNORM))
			if (DMA_ISEMPTY(DMAheader[unit]))
				revents |= events & (POLLOUT | POLLWRNORM);

		if (revents == 0)  {
			if (events & (POLLIN | POLLRDNORM))
				selrecord(p, &qdrsel[unit]);

			if (events & (POLLOUT | POLLWRNORM))
				selrecord(p, &qdrsel[unit]);
		}
	} else  {
		/*
		* this is a tty device
		*/
		tp = qd_tty[minor_dev];
		revents = (*tp->t_linesw->l_poll)(tp, events, p);
	}

	splx(s);
	return (revents);
} /* qdpoll() */

static void
filt_qdrdetach(struct knote *kn)
{
	dev_t dev = (intptr_t) kn->kn_hook;
	u_int minor_dev = minor(dev);
	int unit = minor_dev >> 2;
	int s;

	s = spl5();
	SLIST_REMOVE(&qdrsel[unit].sel_klist, kn, knote, kn_selnext);
	splx(s);
}

static int
filt_qdread(struct knote *kn, long hint)
{
	dev_t dev = (intptr_t) kn->kn_hook;
	u_int minor_dev = minor(dev);
	int unit = minor_dev >> 2;

	if (ISEMPTY(eq_header[unit]))
		return (0);

	kn->kn_data = 0;	/* XXXLUKEM (thorpej): what to put here? */
	return (1);
}

static int
filt_qdwrite(struct knote *kn, long hint)
{
	dev_t dev = (intptr_t) kn->kn_hook;
	u_int minor_dev = minor(dev);
	int unit = minor_dev >> 2;

	if (! DMA_ISEMPTY(DMAheader[unit]))
		return (0);

	kn->kn_data = 0;	/* XXXLUKEM (thorpej): what to put here? */
	return (1);
}

static const struct filterops qdread_filtops =
	{ 1, NULL, filt_qdrdetach, filt_qdread };

static const struct filterops qdwrite_filtops =
	{ 1, NULL, filt_qdrdetach, filt_qdwrite };

int
qdkqfilter(dev_t dev, struct knote *kn)
{
	struct klist *klist;
	u_int minor_dev = minor(dev);
	int s, unit = minor_dev >> 2;

	if ((minor_dev & 0x03) != 2) {
		/* TTY device. */
		return (ttykqfilter(dev, kn));
	}

	switch (kn->kn_filter) {
	case EVFILT_READ:
		klist = &qdrsel[unit].sel_klist;
		kn->kn_fop = &qdread_filtops;
		break;

	case EVFILT_WRITE:
		klist = &qdrsel[unit].sel_klist;
		kn->kn_fop = &qdwrite_filtops;
		break;

	default:
		return (EINVAL);
	}

	kn->kn_hook = (void *)(intptr_t) dev;

	s = spl5();
	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
	splx(s);

	return (0);
}

void qd_strategy(struct buf *bp);

/*ARGSUSED*/
int
qdwrite(dev_t dev, struct uio *uio, int flag)
{
	struct tty *tp;
	int minor_dev;
	int unit;

	minor_dev = minor(dev);
	unit = (minor_dev >> 2) & 0x07;

	if (((minor_dev&0x03) != 0x02) && (qdflags[unit].inuse&CONS_DEV)) {
		/*
		* this is the console...
		*/
		tp = qd_tty[minor_dev];
		return ((*tp->t_linesw->l_write)(tp, uio, flag));
	} else if (qdflags[unit].inuse & GRAPHIC_DEV) {
		/*
		* this is a DMA xfer from user space
		*/
		return (physio(qd_strategy, NULL, dev, B_WRITE, minphys, uio));
	}
	return (ENXIO);
}

/*ARGSUSED*/
int
qdread(dev_t dev, struct uio *uio, int flag)
{
	struct tty *tp;
	int minor_dev;
	int unit;

	minor_dev = minor(dev);
	unit = (minor_dev >> 2) & 0x07;

	if ((minor_dev & 0x03) != 0x02 && qdflags[unit].inuse & CONS_DEV) {
		/*
		* this is the console
		*/
		tp = qd_tty[minor_dev];
		return ((*tp->t_linesw->l_read)(tp, uio, flag));
	} else if (qdflags[unit].inuse & GRAPHIC_DEV) {
		/*
		* this is a bitmap-to-processor xfer
		*/
		return (physio(qd_strategy, NULL, dev, B_READ, minphys, uio));
	}
	return (ENXIO);
}

/***************************************************************
*
*	qd_strategy()... strategy routine to do DMA
*
***************************************************************/

void
qd_strategy(struct buf *bp)
{
	volatile struct dga *dga;
	volatile struct adder *adder;
	int unit;
	int QBAreg;
	int s;
	int cookie;
	struct uba_softc *uh;

	unit = (minor(bp->b_dev) >> 2) & 0x07;

	uh = device_private(device_parent(device_lookup(&qd_cd, unit)));

	/*
	* init pointers
	*/
	dga = (struct dga *) qdmap[unit].dga;
panic("qd_strategy");
#ifdef notyet
	if ((QBAreg = ubasetup(uh, bp, 0)) == 0) {
		printf("qd%d: qd_strategy: QBA setup error\n", unit);
		goto STRAT_ERR;
	}
#endif
	s = spl5();
	qdflags[unit].user_dma = -1;
	dga->csr |= DMA_IE;
	cookie = QBAreg & 0x3FFFF;
	dga->adrs_lo = (short) cookie;
	dga->adrs_hi = (short) (cookie >> 16);
	dga->bytcnt_lo = (short) bp->b_bcount;
	dga->bytcnt_hi = (short) (bp->b_bcount >> 16);

	while (qdflags[unit].user_dma) {
		(void) tsleep(&qdflags[unit].user_dma, QSPRIOR,
		    "qdstrat", 0);
	}
	splx(s);
#ifdef notyet
	ubarelse(uh, &QBAreg);
#endif
	if (!(dga->csr & DMA_ERR)) {
		biodone(bp);
		return;
	}

/* STRAT_ERR: */
	adder = (struct adder *) qdmap[unit].adder;
	adder->command = CANCEL;	/* cancel adder activity */
	dga->csr &= ~DMA_IE;
	dga->csr &= ~0x0600;		/* halt DMA (reset fifo) */
	dga->csr |= DMA_ERR;		/* clear error condition */
	bp->b_error = EIO;		/* flag an error to physio() */

	/*
	 * if DMA was running, flush spurious intrpt
	 */
	if (dga->bytcnt_lo != 0) {
		dga->bytcnt_lo = 0;
		dga->bytcnt_hi = 0;
		DMA_SETIGNORE(DMAheader[unit]);
		dga->csr |= DMA_IE;
	}
	biodone(bp);
} /* qd_strategy */


/*
 *  Start output to the console screen
 */
void qdstart(tp)
	struct tty *tp;
{
	int which_unit, unit, c;
	int s;

	unit = minor(tp->t_dev);
	which_unit = (unit >> 2) & 0x3;
	unit &= 0x03;

	s = spl5();

	/*
	* If it's currently active, or delaying, no need to do anything.
	*/
	if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
		goto out;

	/*
	* Display chars until the queue is empty.
	* Drop input from anything but the console
	* device on the floor.
	*
	* XXX - this loop is done at spltty.
	*
	*/
	while (tp->t_outq.c_cc) {
		c = getc(&tp->t_outq);
		if (unit == 0)
			blitc(which_unit, (u_char)c);
	}
	ttypull(tp);
	tp->t_state &= ~TS_BUSY;

out:
	splx(s);

} /* qdstart */

/*ARGSUSED*/
void
qdstop(struct tty *tp, int flag)
{
	int s;

	s = spl5();	/* block intrpts during state modification */
	if (tp->t_state & TS_BUSY) {
		if ((tp->t_state & TS_TTSTOP) == 0)
			tp->t_state |= TS_FLUSH;
		else
			tp->t_state &= ~TS_BUSY;
	}
	splx(s);
}

/*
 *  Output a character to the QDSS screen
 */
void
blitc(int unit, u_char chr)
{
	volatile struct adder *adder;
	volatile struct dga *dga;
	int i;
	int nograph = !(qdflags[unit].inuse&GRAPHIC_DEV);
	static short inescape[NQD];

	adder = (struct adder *)qdmap[unit].adder;
	dga = (struct dga *) qdmap[unit].dga;
	/*
	 * BSD comment: this (&=0177) defeats the extended character
	 * set code for the glass tty, but if i had the time i would
	 * spend it ripping out the code completely.  This driver
	 * is too big for its own good.
	 */
	chr &= 0177;
	/*
	 * Cursor addressing (so vi will work).
	 * Decode for "\E=%.%." cursor motion description.
	 * Corresponds to type "qdcons" in /etc/termcap:
	 *
	 *    qd|qdss|qdcons|qdss glass tty (4.4 BSD):\
	 *      :am:do=^J:le=^H:bs:cm=\E=%.%.:cl=1^Z:co#128:li#57::nd=^L:up=^K:
	 *
	 */
	if (inescape[unit] && nograph) {
		switch (inescape[unit]++) {
		case 1:
			if (chr != '=') {
				/* abort escape sequence */
				inescape[unit] = 0;
				blitc(unit, chr);
			}
			return;
		case 2:
			/* position row */
			cursor[unit].y = CHAR_HEIGHT * chr;
			if (cursor[unit].y > 863 - CHAR_HEIGHT)
				cursor[unit].y = 863 - CHAR_HEIGHT;
			dga->y_cursor = TRANY(cursor[unit].y);
			return;
		case 3:
			/* position column */
			cursor[unit].x = CHAR_WIDTH * chr;
			if (cursor[unit].x > 1024 - CHAR_WIDTH)
				cursor[unit].x = 1023 - CHAR_WIDTH;
			dga->x_cursor = TRANX(cursor[unit].x);
			inescape[unit] = 0;
			return;
		default:
			inescape[unit] = 0;
			blitc(unit, chr);
		}
	}

	switch (chr) {
	case '\r':			/* return char */
		cursor[unit].x = 0;
		if (nograph)
			dga->x_cursor = TRANX(cursor[unit].x);
		return;

	case '\t':			/* tab char */
		for (i = 8 - ((cursor[unit].x >> 3) & 0x07); i > 0; --i) {
			blitc(unit, ' ');
		}
		return;

	case '\n':			/* line feed char */
		if ((cursor[unit].y += CHAR_HEIGHT) > (863 - CHAR_HEIGHT)) {
			if (nograph) {
				cursor[unit].y -= CHAR_HEIGHT;
				scroll_up(adder);
			} else
				cursor[unit].y = 0;
		}
		if (nograph)
			dga->y_cursor = TRANY(cursor[unit].y);
		return;

	case '\b':			/* backspace char */
		if (cursor[unit].x > 0) {
			cursor[unit].x -= CHAR_WIDTH;
			if (nograph)
				dga->x_cursor = TRANX(cursor[unit].x);
		}
		return;
	case CTRL('k'):		/* cursor up */
		if (nograph && cursor[unit].y > 0) {
			cursor[unit].y -= CHAR_HEIGHT;
			dga->y_cursor = TRANY(cursor[unit].y);
		}
		return;

	case CTRL('^'):		/* home cursor */
		if (nograph) {
			cursor[unit].x = 0;
			dga->x_cursor = TRANX(cursor[unit].x);
			cursor[unit].y = 0;
			dga->y_cursor = TRANY(cursor[unit].y);
		}
		return;

	case CTRL('l'):		/* cursor right */
		if (nograph && cursor[unit].x < 1023 - CHAR_WIDTH) {
			cursor[unit].x += CHAR_WIDTH;
			dga->x_cursor = TRANX(cursor[unit].x);
		}
		return;

	case CTRL('z'):		/* clear screen */
		if (nograph) {
			setup_dragon(unit);
			clear_qd_screen(unit);
			/* home cursor - termcap seems to assume this */
			cursor[unit].x = 0;
			dga->x_cursor = TRANX(cursor[unit].x);
			cursor[unit].y = 0;
			dga->y_cursor = TRANY(cursor[unit].y);
		}
		return;

	case '\033':		/* start escape sequence */
		if (nograph)
			inescape[unit] = 1;
		return;

	default:
		if ((chr < ' ') || (chr > '~'))
			return;
	}
	/*
	 * setup VIPER operand control registers
	 */
	write_ID(adder, CS_UPDATE_MASK, 0x0001);  /* select plane #0 */
	write_ID(adder, SRC1_OCR_B,
	EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
	write_ID(adder, CS_UPDATE_MASK, 0x00FE);  /* select other planes */
	write_ID(adder, SRC1_OCR_B,
	EXT_SOURCE | INT_NONE | NO_ID | BAR_SHIFT_DELAY);
	write_ID(adder, CS_UPDATE_MASK, 0x00FF);  /* select all planes */
	write_ID(adder, DST_OCR_B,
	EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
	write_ID(adder, MASK_1, 0xFFFF);
	write_ID(adder, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 1);
	write_ID(adder, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
	adder->x_clip_min = 0;
	adder->x_clip_max = 1024;
	adder->y_clip_min = 0;
	adder->y_clip_max = 864;
	/*
	 * load DESTINATION origin and vectors
	 */
	adder->fast_dest_dy = 0;
	adder->slow_dest_dx = 0;
	adder->error_1 = 0;
	adder->error_2 = 0;
	adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
	(void)wait_status(adder, RASTEROP_COMPLETE);
	adder->destination_x = cursor[unit].x;
	adder->fast_dest_dx = CHAR_WIDTH;
	adder->destination_y = cursor[unit].y;
	adder->slow_dest_dy = CHAR_HEIGHT;
	/*
	 * load SOURCE origin and vectors
	 */
	if ((chr - ' ') > (CHARS - 1))  {
		printf("Invalid character (x)%x in blitc\n",chr);
		chr = ' ';
	}
	/*
	 * X position is modulo the number of characters per line
	 */
	adder->source_1_x = FONT_X +
	    (((chr - ' ') % (MAX_SCREEN_X/CHAR_WIDTH)) * CHAR_WIDTH);
	/*
	 * Point to either first or second row
	 */
	adder->source_1_y = 2048 - 15 *
	    (((chr - ' ')/(MAX_SCREEN_X/CHAR_WIDTH)) + 1);
	adder->source_1_dx = CHAR_WIDTH;
	adder->source_1_dy = CHAR_HEIGHT;
	write_ID(adder, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
	adder->cmd = RASTEROP | OCRB | 0 | S1E | DTE;
	/*
	 * update console cursor coordinates
	 */
	cursor[unit].x += CHAR_WIDTH;
	if (nograph)
		dga->x_cursor = TRANX(cursor[unit].x);
	if (cursor[unit].x > (1024 - CHAR_WIDTH)) {
		blitc(unit, '\r');
		blitc(unit, '\n');
	}

} /* blitc */

/*
 *  INTERRUPT SERVICE ROUTINES
 */

/*
 *  Service "DMA DONE" interrupt condition
 */

static void
qddint(void *arg)
{
	device_t dv = arg;
	struct DMAreq_header *header;
	struct DMAreq *request;
	volatile struct dga *dga;
	volatile struct adder *adder;
	int cookie;			/* DMA adrs for QDSS */
	int unit = device_unit(dv);

	(void)spl4();			/* allow interval timer in */

	/*
	* init pointers
	*/
	header = DMAheader[unit];    /* register for optimization */
	dga = (struct dga *) qdmap[unit].dga;
	adder = (struct adder *) qdmap[unit].adder;

	/*
	* if this interrupt flagged as bogus for interrupt flushing purposes..
	*/
	if (DMA_ISIGNORE(header)) {
	   DMA_CLRIGNORE(header);
		return;
	}

	/*
	* dump a DMA hardware error message if appropriate
	*/
	if (dga->csr & DMA_ERR) {

		if (dga->csr & PARITY_ERR)
		    printf("qd%d: qddint: DMA hardware parity fault.\n", unit);

		if (dga->csr & BUS_ERR)
		    printf("qd%d: qddint: DMA hardware bus error.\n", unit);
	}

	/*
	* if this was a DMA from user space...
	*/
	if (qdflags[unit].user_dma) {
		qdflags[unit].user_dma = 0;
		wakeup((void *)&qdflags[unit].user_dma);
		return;
	}

	/*
	* if we're doing DMA request queue services, field the error condition
	*/
	if (dga->csr & DMA_ERR) {

		dga->csr &= ~0x0600;		/* halt DMA (reset fifo) */
		dga->csr |= DMA_ERR;		/* clear error condition */
		adder->command = CANCEL;	/* cancel adder activity */

		DMA_SETERROR(header);	/* flag error in header status word */
		DMA_CLRACTIVE(header);
		header->DMAreq[header->oldest].DMAdone |= HARD_ERROR;
		header->newest = header->oldest;
		header->used = 0;

		selnotify(&qdrsel[unit], 0, 0);

		if (dga->bytcnt_lo != 0) {
			dga->bytcnt_lo = 0;
			dga->bytcnt_hi = 0;
			DMA_SETIGNORE(header);
		}
		return;
	}

	/*
	* if the DMA request queue is now becoming non-full,
	* wakeup "select" client.
	*/
	if (DMA_ISFULL(header)) {
		selnotify(&qdrsel[unit], 0, 0);
	}

	header->DMAreq[header->oldest].DMAdone |= REQUEST_DONE;
	QDlast_DMAtype = header->DMAreq[header->oldest].DMAtype;

	/* check for unexpected interrupt */
	if (DMA_ISEMPTY(header))
	    return;

	DMA_GETEND(header);	/* update request queue indices */

	/*
	* if no more DMA pending, wake up "select" client and exit
	*/
	if (DMA_ISEMPTY(header)) {
		selnotify(&qdrsel[unit], 0, 0);
		DMA_CLRACTIVE(header);  /* flag DMA done */
		return;
	}

	/*
	* initiate next DMA xfer
	*/
	request = DMA_GETBEGIN(header);
	if (request->DMAtype != QDlast_DMAtype) {
		dga->csr &= ~0x0600;	  /* halt DMA (reset fifo) */
		adder->command = CANCEL;  /* cancel adder activity */
	}


	switch (request->DMAtype) {

	case DISPLIST:
		if (request->DMAtype != QDlast_DMAtype) {
			dga->csr |= DL_ENB;
			dga->csr &= ~(BTOP_ENB | BYTE_DMA);
		}
		break;

	case PTOB:
		if (request->DMAtype != QDlast_DMAtype) {
			if (request->DMAdone & BYTE_PACK)
			    dga->csr |= (PTOB_ENB | BYTE_DMA);
			else {
				dga->csr |= PTOB_ENB;
				dga->csr &= ~BYTE_DMA;
			}
		}
		break;

	case BTOP:
		if (request->DMAtype != QDlast_DMAtype) {
			if (request->DMAdone & BYTE_PACK) {
				dga->csr &= ~DL_ENB;
				dga->csr |= (BTOP_ENB | BYTE_DMA);
			}
			else {
				dga->csr |= BTOP_ENB;
				dga->csr &= ~(BYTE_DMA | DL_ENB);
			}
		}
		break;
	default:
		printf("qd%d: qddint: illegal DMAtype parameter.\n", unit);
		DMA_CLRACTIVE(header);	/* flag DMA done */
		return;
	}

	if (request->DMAdone & COUNT_ZERO) {
		dga->csr &= ~SET_DONE_FIFO;
	}
	else if (request->DMAdone & FIFO_EMPTY) {
		dga->csr |= SET_DONE_FIFO;
	}

	if (request->DMAdone & WORD_PACK)
	    dga->csr &= ~BYTE_DMA;
	else if (request->DMAdone & BYTE_PACK)
	    dga->csr |= BYTE_DMA;

	dga->csr |= DMA_IE;
	QDlast_DMAtype = request->DMAtype;

	cookie = ((int)request->bufp - (int)header) + (int)header->QBAreg;

	dga->adrs_lo = (short) cookie;
	dga->adrs_hi = (short) (cookie >> 16);

	dga->bytcnt_lo = (short) request->length;
	dga->bytcnt_hi = (short) (request->length >> 16);

	return;
}

/*
 * ADDER interrupt service routine
 */
static void
qdaint(void *arg)
{
	device_t dv = arg;
	volatile struct adder *adder;
	struct color_buf *cbuf;
	int i;
	struct rgb *rgbp;
	volatile short *red;
	volatile short *green;
	volatile short *blue;
	int unit = device_unit(dv);

	(void)spl4();			/* allow interval timer in */

	adder = (struct adder *) qdmap[unit].adder;

	/*
	* service the vertical blank interrupt (VSYNC bit) by loading
	* any pending color map load request
	*/
	if (adder->status & VSYNC) {
		adder->status &= ~VSYNC;	/* clear the interrupt */
		cbuf = color_buf[unit];
		if (cbuf->status & LOAD_COLOR_MAP) {

			red = (short *) qdmap[unit].red;
			green = (short *) qdmap[unit].green;
			blue = (short *) qdmap[unit].blue;

			for (i = cbuf->count, rgbp = cbuf->rgb;
			     --i >= 0; rgbp++) {
				red[rgbp->offset] = (short) rgbp->red;
				green[rgbp->offset] = (short) rgbp->green;
				blue[rgbp->offset] = (short) rgbp->blue;
			}

			cbuf->status &= ~LOAD_COLOR_MAP;
		}
	}

	/*
	* service the scroll interrupt (FRAME_SYNC bit)
	*/
	if (adder->status & FRAME_SYNC) {
		adder->status &= ~FRAME_SYNC;	/* clear the interrupt */

		if (scroll[unit]->status & LOAD_REGS) {

			for (i = 1000, adder->status = 0; i > 0 &&
			     !(adder->status&ID_SCROLL_READY); --i)
				;

			if (i == 0) {
			    printf("qd%d: qdaint: timeout on ID_SCROLL_READY\n",
				qd);
				return;
			}

			adder->ID_scroll_data = scroll[unit]->viper_constant;
			adder->ID_scroll_command = ID_LOAD | SCROLL_CONSTANT;

			adder->y_scroll_constant =
				scroll[unit]->y_scroll_constant;
			adder->y_offset_pending = scroll[unit]->y_offset;

			if (scroll[unit]->status & LOAD_INDEX) {

				adder->x_index_pending =
					scroll[unit]->x_index_pending;
				adder->y_index_pending =
					scroll[unit]->y_index_pending;
			}

			scroll[unit]->status = 0x00;
		}
	}
}

/*
 *  DUART input interrupt service routine
 *
 *  XXX - this routine should be broken out - it is essentially
 *	      straight line code.
 */

static void
qdiint(void *arg)
{
	device_t dv = arg;
	struct _vs_event *event;
	struct qdinput *eqh;
	volatile struct dga *dga;
	volatile struct duart *duart;
	struct mouse_report *new_rep;
	struct tty *tp;
	u_short chr;
	u_short status;
	u_short data;
	u_short key;
	char do_wakeup = 0;		/* flag to do a select wakeup call */
	char a, b, c;			/* mouse button test variables */
	int unit = device_unit(dv);

	(void)spl4();			/* allow interval timer in */

	eqh = eq_header[unit];		/* optimized as a register */
	new_rep = &current_rep[unit];
	duart = (struct duart *) qdmap[unit].duart;

	/*
	* if the graphic device is turned on..
	*/
	if (qdflags[unit].inuse & GRAPHIC_DEV) {
		/*
		* empty DUART
		*/
		while (duart->statusA&RCV_RDY || duart->statusB&RCV_RDY) {
			/*
			 * pick up LK-201 input (if any)
			 */
			if (duart->statusA&RCV_RDY) {

				/* if error condition, then reset it */

				if (duart->statusA&0x70) {
					duart->cmdA = 0x40;
					continue;
				}

				/* event queue full now? (overflow condition) */

				if (ISFULL(eqh) == TRUE) {
					printf(
					 "qd%d: qdiint: event queue overflow\n",
					   qd);
					break;
				}

				/*
				* Check for various keyboard errors  */

				key = duart->dataA & 0xFF;

				if (key==LK_POWER_ERROR ||
				    key==LK_KDOWN_ERROR ||
				    key == LK_INPUT_ERROR ||
				    key == LK_OUTPUT_ERROR) {
					printf(
				    "qd%d: qdiint: keyboard error, code = %x\n",
					qd,key);
					return;
				}

				if (key < LK_LOWEST)
				    return;

				++do_wakeup;  /* request a select wakeup call */

				event = PUTBEGIN(eqh);
				PUTEND(eqh);

				event->vse_key = key;
				event->vse_key &= 0x00FF;
				event->vse_x = eqh->curs_pos.x;
				event->vse_y = eqh->curs_pos.y;
				event->vse_time = TOY;
				event->vse_type = VSE_BUTTON;
				event->vse_direction = VSE_KBTRAW;
				event->vse_device = VSE_DKB;
			}

			/*
			* pick up the mouse input (if any)  */

			if ((status = duart->statusB) & RCV_RDY  &&
			    qdflags[unit].pntr_id == MOUSE_ID) {

				if (status & 0x70) {
					duart->cmdB = 0x40;
					continue;
				}

				/* event queue full now? (overflow condition) */

				if (ISFULL(eqh) == TRUE) {
					printf(
					"qd%d: qdiint: event queue overflow\n",
					     qd);
					break;
				}

				data = duart->dataB;	/* get report byte */
				++new_rep->bytcnt; /* bump report byte count */

				/*
				* if 1st byte of report.. */

				if ( data & START_FRAME) {
					new_rep->state = data;
					if (new_rep->bytcnt > 1) {
						/* start of new frame */
						new_rep->bytcnt = 1;
						/* ..continue looking */
						continue;
					}
				}

				/*
				* if 2nd byte of report.. */

				else if (new_rep->bytcnt == 2) {
					new_rep->dx = data & 0x00FF;
				}

				/*
				* if 3rd byte of report, load input event queue */

				else if (new_rep->bytcnt == 3) {

					new_rep->dy = data & 0x00FF;
					new_rep->bytcnt = 0;

					/*
					* if mouse position has changed.. */

					if (new_rep->dx != 0  ||  new_rep->dy != 0) {

						/*
						* calculate acceleration factor, if needed	*/

						if (qdflags[unit].curs_acc > ACC_OFF) {

							if (qdflags[unit].curs_thr <= new_rep->dx)
							    new_rep->dx +=
							    (new_rep->dx - qdflags[unit].curs_thr)
							    * qdflags[unit].curs_acc;

							if (qdflags[unit].curs_thr <= new_rep->dy)
							    new_rep->dy +=
							    (new_rep->dy - qdflags[unit].curs_thr)
							    * qdflags[unit].curs_acc;
						}

						/*
						* update cursor position coordinates */

						if (new_rep->state & X_SIGN) {
							eqh->curs_pos.x += new_rep->dx;
							if (eqh->curs_pos.x > 1023)
							    eqh->curs_pos.x = 1023;
						}
						else {
							eqh->curs_pos.x -= new_rep->dx;
							if (eqh->curs_pos.x < -15)
							    eqh->curs_pos.x = -15;
						}

						if (new_rep->state & Y_SIGN) {
							eqh->curs_pos.y -= new_rep->dy;
							if (eqh->curs_pos.y < -15)
							    eqh->curs_pos.y = -15;
						}
						else {
							eqh->curs_pos.y += new_rep->dy;
							if (eqh->curs_pos.y > 863)
							    eqh->curs_pos.y = 863;
						}

						/*
						* update cursor screen position */

						dga = (struct dga *) qdmap[unit].dga;
						dga->x_cursor = TRANX(eqh->curs_pos.x);
						dga->y_cursor = TRANY(eqh->curs_pos.y);

						/*
						* if cursor is in the box, no event report */

						if (eqh->curs_pos.x <= eqh->curs_box.right	&&
						    eqh->curs_pos.x >= eqh->curs_box.left  &&
						    eqh->curs_pos.y >= eqh->curs_box.top  &&
						    eqh->curs_pos.y <= eqh->curs_box.bottom ) {
							goto GET_MBUTTON;
						}

						/*
						* report the mouse motion event */

						event = PUTBEGIN(eqh);
						PUTEND(eqh);

						++do_wakeup;   /* request a select wakeup call */

						event->vse_x = eqh->curs_pos.x;
						event->vse_y = eqh->curs_pos.y;

						event->vse_device = VSE_MOUSE;  /* mouse */
						event->vse_type = VSE_MMOTION;  /* pos changed */
						event->vse_key = 0;
						event->vse_direction = 0;
						event->vse_time = TOY;	/* time stamp */
					}

GET_MBUTTON:
					/*
					* if button state has changed */

					a = new_rep->state & 0x07;    /*mask nonbutton bits */
					b = last_rep[unit].state & 0x07;

					if (a ^ b) {

						for ( c = 1;  c < 8; c <<= 1) {

							if (!( c & (a ^ b))) /* this button change? */
							    continue;

							/* event queue full? (overflow condition) */

							if (ISFULL(eqh) == TRUE) {
								printf("qd%d: qdiint: event queue overflow\n", qd);
								break;
							}

							event = PUTBEGIN(eqh);	/* get new event */
							PUTEND(eqh);

							++do_wakeup;   /* request select wakeup */

							event->vse_x = eqh->curs_pos.x;
							event->vse_y = eqh->curs_pos.y;

							event->vse_device = VSE_MOUSE;	/* mouse */
							event->vse_type = VSE_BUTTON; /* new button */
							event->vse_time = TOY;	      /* time stamp */

							/* flag changed button and if up or down */

							if (c == RIGHT_BUTTON)
							    event->vse_key = VSE_RIGHT_BUTTON;
							else if (c == MIDDLE_BUTTON)
							    event->vse_key = VSE_MIDDLE_BUTTON;
							else if (c == LEFT_BUTTON)
							    event->vse_key = VSE_LEFT_BUTTON;

							/* set bit = button depressed */

							if (c & a)
							    event->vse_direction = VSE_KBTDOWN;
							else
								event->vse_direction = VSE_KBTUP;
						}
					}

					/* refresh last report */

					last_rep[unit] = current_rep[unit];

				}  /* get last byte of report */
			} else if ((status = duart->statusB)&RCV_RDY &&
				   qdflags[unit].pntr_id == TABLET_ID) {
				/*
				* pickup tablet input, if any
				*/
				if (status&0x70) {
					duart->cmdB = 0x40;
					continue;
				}
				/*
				 * event queue full now? (overflow condition)
				 */
				if (ISFULL(eqh) == TRUE) {
					printf("qd%d: qdiint: event queue overflow\n", qd);
					break;
				}

				data = duart->dataB;	/* get report byte */
				++new_rep->bytcnt;	/* bump report byte count */

				/*
				* if 1st byte of report.. */

				if (data & START_FRAME) {
					new_rep->state = data;
					if (new_rep->bytcnt > 1) {
						new_rep->bytcnt = 1;    /* start of new frame */
						continue;		    /* ..continue looking */
					}
				}

				/*
				* if 2nd byte of report.. */

				else if (new_rep->bytcnt == 2) {
					new_rep->dx = data & 0x3F;
				}

				/*
				* if 3rd byte of report.. */

				else if (new_rep->bytcnt == 3) {
					new_rep->dx |= (data & 0x3F) << 6;
				}

				/*
				* if 4th byte of report.. */

				else if (new_rep->bytcnt == 4) {
					new_rep->dy = data & 0x3F;
				}

				/*
				* if 5th byte of report, load input event queue */

				else if (new_rep->bytcnt == 5) {

					new_rep->dy |= (data & 0x3F) << 6;
					new_rep->bytcnt = 0;

					/*
					* update cursor position coordinates */

					new_rep->dx /= qdflags[unit].tab_res;
					new_rep->dy = (2200 - new_rep->dy)
					    / qdflags[unit].tab_res;

					if (new_rep->dx > 1023) {
						new_rep->dx = 1023;
					}
					if (new_rep->dy > 863) {
						new_rep->dy = 863;
					}

					/*
					* report an event if the puck/stylus has moved
					*/

					if (eqh->curs_pos.x != new_rep->dx ||
					    eqh->curs_pos.y != new_rep->dy) {

						eqh->curs_pos.x = new_rep->dx;
						eqh->curs_pos.y = new_rep->dy;

						/*
						* update cursor screen position */

						dga = (struct dga *) qdmap[unit].dga;
						dga->x_cursor = TRANX(eqh->curs_pos.x);
						dga->y_cursor = TRANY(eqh->curs_pos.y);

						/*
						* if cursor is in the box, no event report
						*/

						if (eqh->curs_pos.x <= eqh->curs_box.right	&&
						    eqh->curs_pos.x >= eqh->curs_box.left  &&
						    eqh->curs_pos.y >= eqh->curs_box.top  &&
						    eqh->curs_pos.y <= eqh->curs_box.bottom ) {
							goto GET_TBUTTON;
						}

						/*
						* report the tablet motion event */

						event = PUTBEGIN(eqh);
						PUTEND(eqh);

						++do_wakeup;   /* request a select wakeup call */

						event->vse_x = eqh->curs_pos.x;
						event->vse_y = eqh->curs_pos.y;

						event->vse_device = VSE_TABLET;  /* tablet */
						/*
						* right now, X handles tablet motion the same
						* as mouse motion
						*/
						event->vse_type = VSE_MMOTION;   /* pos changed */
						event->vse_key = 0;
						event->vse_direction = 0;
						event->vse_time = TOY;	/* time stamp */
					}
GET_TBUTTON:
					/*
					* if button state has changed */

					a = new_rep->state & 0x1E;   /* mask nonbutton bits */
					b = last_rep[unit].state & 0x1E;

					if (a ^ b) {

						/* event queue full now? (overflow condition) */

						if (ISFULL(eqh) == TRUE) {
							printf("qd%d: qdiint: event queue overflow\n",qd);
							break;
						}

						event = PUTBEGIN(eqh);  /* get new event */
						PUTEND(eqh);

						++do_wakeup;   /* request a select wakeup call */

						event->vse_x = eqh->curs_pos.x;
						event->vse_y = eqh->curs_pos.y;

						event->vse_device = VSE_TABLET;  /* tablet */
						event->vse_type = VSE_BUTTON; /* button changed */
						event->vse_time = TOY;	   /* time stamp */

						/* define the changed button and if up or down */

						for ( c = 1;  c <= 0x10; c <<= 1) {
							if (c & (a ^ b)) {
								if (c == T_LEFT_BUTTON)
								    event->vse_key = VSE_T_LEFT_BUTTON;
								else if (c == T_FRONT_BUTTON)
								    event->vse_key = VSE_T_FRONT_BUTTON;
								else if (c == T_RIGHT_BUTTON)
								    event->vse_key = VSE_T_RIGHT_BUTTON;
								else if (c == T_BACK_BUTTON)
								    event->vse_key = VSE_T_BACK_BUTTON;
								break;
							}
						}

						/* set bit = button depressed */

						if (c & a)
						    event->vse_direction = VSE_KBTDOWN;
						else
							event->vse_direction = VSE_KBTUP;
					}

					/* refresh last report */

					last_rep[unit] = current_rep[unit];

				} /* get last byte of report */
			} /* pick up tablet input */

		} /* while input available.. */

		/*
		* do select wakeup
		*/
		if (do_wakeup) {
			selnotify(&qdrsel[unit], 0, 0);
			do_wakeup = 0;
		}
	} else {
		/*
		 * if the graphic device is not turned on, this is console input
		 */
		if (qdpolling)
			return;

		if (unit >= qd_cd.cd_ndevs || device_lookup(&qd_cd, unit) == NULL)
			return;		/* no such device or address */

		tp = qd_tty[unit << 2];

		/*
		 * Get a character from the keyboard.
		 */
		while (duart->statusA&RCV_RDY) {
			key = duart->dataA;
			key &= 0xFF;
			/*
			* Check for various keyboard errors
			*/
			if (key == LK_POWER_ERROR || key == LK_KDOWN_ERROR ||
			    key == LK_INPUT_ERROR || key == LK_OUTPUT_ERROR) {
				printf("qd%d: qdiint: Keyboard error, code = %x\n",qd,key);
				return;
			}

			if (key < LK_LOWEST)
			    return;

			/*
			* See if its a state change key */

			switch (key) {

			case LOCK:
				q_keyboard.lock ^= 0xffff;	/* toggle */
				if (q_keyboard.lock)
					led_control(qd, LK_LED_ENABLE,
							  LK_LED_LOCK);
				else
					led_control(qd, LK_LED_DISABLE,
							  LK_LED_LOCK);
				return;

			case SHIFT:
				q_keyboard.shift ^= 0xFFFF;
				return;

			case CNTRL:
				q_keyboard.cntrl ^= 0xFFFF;
				return;

			case ALLUP:
				q_keyboard.cntrl = 0;
				q_keyboard.shift = 0;
				return;

			case REPEAT:
				chr = q_keyboard.last;
				break;

				/*
				* Test for cntrl characters. If set, see if the character
				* is elligible to become a control character. */

			default:

				if (q_keyboard.cntrl) {
					chr = q_key[key];
					if (chr >= ' ' && chr <= '~')
					    chr &= 0x1F;
					else if (chr >= 0xA1 && chr <= 0xFE)
					    chr &= 0x9F;
				}
				else if( q_keyboard.lock || q_keyboard.shift )
				    chr = q_shift_key[key];
				else
					chr = q_key[key];
				break;
			}

			q_keyboard.last = chr;

			/*
			* Check for special function keys */

			if (chr & 0x100) {
				char *string;
				string = q_special[chr & 0x7F];
				while(*string)
				    (*tp->t_linesw->l_rint)(*string++, tp);
			}
			else {
#ifdef DDB
				/* Check for kernel debugger escape here */
				int j;

				j = kdbrint(chr&0177);

				if (j == 1)  /* Escape received, just return */
				    return;

				if (j == 2)  /* Second char wasn't 'D' */
				    (*tp->t_linesw->l_rint)(27, tp);
#endif
				(*tp->t_linesw->l_rint)(chr&0177, tp);
			}
		}
	}
} /* qdiint */

/*
 *
 * Clear the QDSS screen
 *
 *			     >>> NOTE <<<
 *
 *   This code requires that certain adder initialization be valid.  To
 *   assure that this requirement is satisfied, this routine should be
 *   called only after calling the "setup_dragon()" function.
 *
 *   Clear the bitmap a piece at a time. Since the fast scroll clear
 *   only clears the current displayed portion of the bitmap put a
 *   temporary value in the y limit register so we can access whole
 *   bitmap
 *
 */
void
clear_qd_screen(int unit)
{
	volatile struct adder *adder;
	adder = (struct adder *) qdmap[unit].adder;

	adder->x_limit = 1024;
	adder->y_limit = 2048 - CHAR_HEIGHT;
	adder->y_offset_pending = 0;
#define WSV  (void)wait_status(adder, VSYNC); (void)wait_status(adder, VSYNC)
	WSV;
	adder->y_scroll_constant = SCROLL_ERASE;
	WSV;
	adder->y_offset_pending = 864;
	WSV;
	adder->y_scroll_constant = SCROLL_ERASE;
	WSV;
	adder->y_offset_pending = 1728;
	WSV;
	adder->y_scroll_constant = SCROLL_ERASE;
	WSV;
	adder->y_offset_pending = 0;	 /* back to normal */
	WSV;
	adder->x_limit = MAX_SCREEN_X;
	adder->y_limit = MAX_SCREEN_Y + FONT_HEIGHT;
#undef WSV

} /* clear_qd_screen */

/*
 *  kernel console output to the glass tty
 */
void
qdcnputc(dev_t dev, int chr)
{

	/*
	 * if system is now physical, forget it (ie: crash DUMP)
	 */
	if ((mfpr(PR_MAPEN) & 1) == 0)
		return;

	blitc(0, (u_char)(chr & 0xff));
	if ((chr & 0177) == '\n')
		blitc(0, '\r');

} /* qdputc */

/*
 *  load the mouse cursor's template RAM bitmap
 */
void
ldcursor(int unit, short *bitmap)
{
	volatile struct dga *dga;
	volatile short *temp;
	int i;
	int curs;

	dga = (struct dga *) qdmap[unit].dga;
	temp = (short *) qdmap[unit].template;

	if (dga->csr & CURS_ENB) {	/* if the cursor is enabled.. */
		curs = -1;		/* ..note that.. */
		dga->csr &= ~CURS_ENB;	/* ..and shut it off */
	} else
		curs = 0;

	dga->csr &= ~CURS_ENB;		/* shut off the cursor */

	temp += (8 * 1024) - 32;	/* cursor is 32 WORDS from the end */
	/* ..of the 8k WORD template space */
	for (i = 0; i < 32; ++i)
		*temp++ = *bitmap++;

	if (curs) {			/* if cursor was enabled.. */
		dga->csr |= CURS_ENB;	/* ..turn it back on */
	}

} /* ldcursor */

/*
 *  Put the console font in the QDSS off-screen memory
 */
void
ldfont(int unit)
{
	volatile struct adder *adder;

	int i, j, k, max_chars_line;
	short packed;

	adder = (struct adder *) qdmap[unit].adder;

	/*
	* setup VIPER operand control registers
	*/
	write_ID(adder, MASK_1, 0xFFFF);
	write_ID(adder, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
	write_ID(adder, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);

	write_ID(adder, SRC1_OCR_B,
	EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
	write_ID(adder, SRC2_OCR_B,
	EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
	write_ID(adder, DST_OCR_B,
	EXT_SOURCE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);

	adder->rasterop_mode = DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;

	/*
	* load destination data
	*/
	(void)wait_status(adder, RASTEROP_COMPLETE);

	adder->destination_x = FONT_X;
	adder->destination_y = FONT_Y;
#if FONT_WIDTH > MAX_SCREEN_X
	adder->fast_dest_dx = MAX_SCREEN_X;
#else
	adder->fast_dest_dx = FONT_WIDTH;
#endif
	adder->slow_dest_dy = CHAR_HEIGHT;

	/*
	* setup for processor to bitmap xfer  */

	write_ID(adder, CS_UPDATE_MASK, 0x0001);
	adder->cmd = PBT | OCRB | 2 | DTE | 2;

	/*
	* Figure out how many characters can be stored on one "line" of
	* offscreen memory.
	*/
	max_chars_line = MAX_SCREEN_X/(CHAR_WIDTH*2);
	if ((CHARS/2 + CHARS%2) < max_chars_line)
	    max_chars_line = CHARS/2 + CHARS%2;

	/*
	* iteratively do the processor to bitmap xfer */

	for (i = 0; i < ROWS; ++i) {

		/* PTOB a scan line */

		for (j = 0, k = i; j < max_chars_line; ++j) {
			/* PTOB one scan of a char cell */

			packed = q_font[k];
			k += ROWS;
			packed |= ((short)q_font[k] << 8);
			k += ROWS;

			(void)wait_status(adder, TX_READY);
			adder->id_data = packed;
		}
	}

	/*
	 * (XXX XXX XXX - should remove)
	 *
	 * Copy the second row of characters.  Subtract the first
	 * row from the total number.  Divide this quantity by 2
	 * because 2 chars are stored in a short in the PTOB loop
	 * below.  Figure out how many characters can be stored on
	 * one "line" of offscreen memory
	 */

	max_chars_line = MAX_SCREEN_X/(CHAR_WIDTH*2);
	if ((CHARS/2 + CHARS%2) < max_chars_line)
	    return;
	max_chars_line = (CHARS/2 + CHARS%2) - max_chars_line; /* 95 - 64 */
	/* Paranoia check to see if 3rd row may be needed */
	if (max_chars_line > (MAX_SCREEN_X/(CHAR_WIDTH*2)))
	    max_chars_line = MAX_SCREEN_X/(CHAR_WIDTH*2);

	adder->destination_x = FONT_X;
	adder->destination_y = FONT_Y - CHAR_HEIGHT;
	adder->fast_dest_dx = max_chars_line * CHAR_WIDTH * 2;
	adder->slow_dest_dy = CHAR_HEIGHT;

	/*
	* setup for processor to bitmap xfer
	*/
	write_ID(adder, CS_UPDATE_MASK, 0x0001);
	adder->cmd = PBT | OCRB | 2 | DTE | 2;

	/*
	* iteratively do the processor to bitmap xfer
	*/
	for (i = 0; i < ROWS; ++i) {
		/*
		 * PTOB a scan line
		 */
		for (j = 0, k = i; j < max_chars_line; ++j) {
			/*
			 * PTOB one scan of a char cell
			 */
			packed = q_font[k + FONT_OFFSET];
			k += ROWS;
			packed |= ((short)q_font[k + FONT_OFFSET] << 8);
			k += ROWS;
			(void)wait_status(adder, TX_READY);
			adder->id_data = packed;
		}
	}

}  /* ldfont */


/*
 * Disable or enable polling.  This is used when entering or leaving the
 * kernel debugger.
 */
void
qdcnpollc(dev_t dev, int onoff)
{
	qdpolling = onoff;
}


/*
 *  Get a character from the LK201 (polled)
 */
int
qdcngetc(dev_t dev)
{
	short key;
	char chr;
	volatile struct duart *duart;

	duart = (struct duart *) qdmap[0].duart;

	/*
	* Get a character from the keyboard.
	*/
LOOP:
	while (!(duart->statusA&RCV_RDY))
		;

	key = duart->dataA;
	key &= 0xFF;

	/*
	* Check for various keyboard errors  */

	if (key == LK_POWER_ERROR || key == LK_KDOWN_ERROR ||
	    key == LK_INPUT_ERROR || key == LK_OUTPUT_ERROR) {
		printf("Keyboard error, code = %x\n", key);
		return(0);
	}

	if (key < LK_LOWEST)
		return(0);

	/*
	 * See if its a state change key
	 */
	switch (key) {

	case LOCK:
		q_keyboard.lock ^= 0xffff;	/* toggle */
		if (q_keyboard.lock)
			led_control(0, LK_LED_ENABLE, LK_LED_LOCK);
		else
			led_control(0, LK_LED_DISABLE, LK_LED_LOCK);
		goto LOOP;

	case SHIFT:
		q_keyboard.shift ^= 0xFFFF;
		goto LOOP;

	case CNTRL:
		q_keyboard.cntrl ^= 0xFFFF;
		goto LOOP;

	case ALLUP:
		q_keyboard.cntrl = 0;
		q_keyboard.shift = 0;
		goto LOOP;

	case REPEAT:
		chr = q_keyboard.last;
		break;

		/*
		* Test for cntrl characters. If set, see if the character
		* is elligible to become a control character.
		*/
	default:

		if (q_keyboard.cntrl) {
			chr = q_key[key];
			if (chr >= ' ' && chr <= '~')
			    chr &= 0x1F;
		}
		else if ( q_keyboard.lock || q_keyboard.shift )
		    chr = q_shift_key[key];
		else
			chr = q_key[key];
		break;
	}

	if (chr < ' ' && chr > '~')	/* if input is non-displayable */
		return(0);		/* ..then pitch it! */

	q_keyboard.last = chr;

	/*
	* Check for special function keys */

	if (chr & 0x80)			/* pitch the function keys */
		return(0);
	else
		return(chr);

} /* qdgetc */

/*
 *  led_control()... twiddle LK-201 LED's
 */
void
led_control(int unit, int cmd, int led_mask)
{
	int i;
	volatile struct duart *duart;

	duart = (struct duart *)qdmap[unit].duart;

	for (i = 1000; i > 0; --i) {
		if (duart->statusA&XMT_RDY) {
			duart->dataA = cmd;
			break;
		}
	}
	for (i = 1000; i > 0; --i) {
		if (duart->statusA&XMT_RDY) {
			duart->dataA = led_mask;
			break;
		}
	}
	return;

} /* led_control */

/*
 *  scroll_up()... move the screen up one character height
 */
void
scroll_up(volatile struct adder *adder)
{
	/*
	* setup VIPER operand control registers
	*/
	(void)wait_status(adder, ADDRESS_COMPLETE);
	write_ID(adder, CS_UPDATE_MASK, 0x00FF);  /* select all planes */
	write_ID(adder, MASK_1, 0xFFFF);
	write_ID(adder, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
	write_ID(adder, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
	write_ID(adder, SRC1_OCR_B,
	EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
	write_ID(adder, DST_OCR_B,
	EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
	/*
	 * load DESTINATION origin and vectors
	 */
	adder->fast_dest_dy = 0;
	adder->slow_dest_dx = 0;
	adder->error_1 = 0;
	adder->error_2 = 0;
	adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
	adder->destination_x = 0;
	adder->fast_dest_dx = 1024;
	adder->destination_y = 0;
	adder->slow_dest_dy = 864 - CHAR_HEIGHT;
	/*
	 * load SOURCE origin and vectors
	 */
	adder->source_1_x = 0;
	adder->source_1_dx = 1024;
	adder->source_1_y = 0 + CHAR_HEIGHT;
	adder->source_1_dy = 864 - CHAR_HEIGHT;
	write_ID(adder, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
	adder->cmd = RASTEROP | OCRB | 0 | S1E | DTE;
	/*
	 * do a rectangle clear of last screen line
	 */
	write_ID(adder, MASK_1, 0xffff);
	write_ID(adder, SOURCE, 0xffff);
	write_ID(adder,DST_OCR_B,
	(EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY));
	write_ID(adder, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 0);
	adder->error_1 = 0;
	adder->error_2 = 0;
	adder->slow_dest_dx = 0;		/* set up the width of	*/
	adder->slow_dest_dy = CHAR_HEIGHT;	/* rectangle */
	adder->rasterop_mode = (NORMAL | DST_WRITE_ENABLE) ;
	(void)wait_status(adder, RASTEROP_COMPLETE);
	adder->destination_x = 0;
	adder->destination_y = 864 - CHAR_HEIGHT;
	adder->fast_dest_dx = 1024;	/* set up the height	*/
	adder->fast_dest_dy = 0;	/* of rectangle		*/
	write_ID(adder, LU_FUNCTION_R2, (FULL_SRC_RESOLUTION | LF_SOURCE));
	adder->cmd = (RASTEROP | OCRB | LF_R2 | DTE ) ;

} /* scroll_up */

/*
 *  init shared memory pointers and structures
 */
void
init_shared(int unit)
{
	volatile struct dga *dga;

	dga = (struct dga *) qdmap[unit].dga;

	/*
	* initialize the event queue pointers and header */

	eq_header[unit] = (struct qdinput *)
	    ((((int)event_shared & ~(0x01FF)) + 512)
		+ (EVENT_BUFSIZE * unit));
	eq_header[unit]->curs_pos.x = 0;
	eq_header[unit]->curs_pos.y = 0;
	dga->x_cursor = TRANX(eq_header[unit]->curs_pos.x);
	dga->y_cursor = TRANY(eq_header[unit]->curs_pos.y);
	eq_header[unit]->curs_box.left = 0;
	eq_header[unit]->curs_box.right = 0;
	eq_header[unit]->curs_box.top = 0;
	eq_header[unit]->curs_box.bottom = 0;
	/*
	 * assign a pointer to the DMA I/O buffer for this QDSS.
	 */
	DMAheader[unit] = (struct DMAreq_header *)
	    (((int)(&DMA_shared[0] + 512) & ~0x1FF)
		+ (DMAbuf_size * unit));
	DMAheader[unit]->DMAreq = (struct DMAreq *) ((int)DMAheader[unit]
	    + sizeof(struct DMAreq_header));
	DMAheader[unit]->QBAreg = 0;
	DMAheader[unit]->status = 0;
	DMAheader[unit]->shared_size = DMAbuf_size;
	DMAheader[unit]->used = 0;
	DMAheader[unit]->size = 10;	/* default = 10 requests */
	DMAheader[unit]->oldest = 0;
	DMAheader[unit]->newest = 0;
	/*
	* assign a pointer to the scroll structure for this QDSS.
	*/
	scroll[unit] = (struct scroll *)
	    (((int)(&scroll_shared[0] + 512) & ~0x1FF)
		+ (sizeof(struct scroll) * unit));
	scroll[unit]->status = 0;
	scroll[unit]->viper_constant = 0;
	scroll[unit]->y_scroll_constant = 0;
	scroll[unit]->y_offset = 0;
	scroll[unit]->x_index_pending = 0;
	scroll[unit]->y_index_pending = 0;
	/*
	* assign a pointer to the color map write buffer for this QDSS
	*/
	color_buf[unit] = (struct color_buf *)
	    (((int)(&color_shared[0] + 512) & ~0x1FF)
		+ (COLOR_BUFSIZ * unit));
	color_buf[unit]->status = 0;
	color_buf[unit]->count = 0;

} /* init_shared */

/*
 * init the ADDER, VIPER, bitmaps, & color map
 */
void
setup_dragon(int unit)
{

	volatile struct adder *adder;
	volatile struct dga *dga;
	volatile short *memcsr;
	int i;
	short top;		/* clipping/scrolling boundaries */
	short bottom;
	short right;
	short left;
	volatile short *red;		/* color map pointers */
	volatile short *green;
	volatile short *blue;

	/*
	* init for setup
	*/
	adder = (struct adder *) qdmap[unit].adder;
	dga = (struct dga *) qdmap[unit].dga;
	memcsr = (short *) qdmap[unit].memcsr;
	dga->csr &= ~(DMA_IE | 0x700);	/* halt DMA and kill the intrpts */
	*memcsr = SYNC_ON;		/* blank screen and turn off LED's */
	adder->command = CANCEL;
	/*
	* set monitor timing
	*/
	adder->x_scan_count_0 = 0x2800;
	adder->x_scan_count_1 = 0x1020;
	adder->x_scan_count_2 = 0x003A;
	adder->x_scan_count_3 = 0x38F0;
	adder->x_scan_count_4 = 0x6128;
	adder->x_scan_count_5 = 0x093A;
	adder->x_scan_count_6 = 0x313C;
	adder->sync_phase_adj = 0x0100;
	adder->x_scan_conf = 0x00C8;
	/*
	 * got a bug in secound pass ADDER! lets take care of it
	 *
	 * normally, just use the code in the following bug fix code, but to
	 * make repeated demos look pretty, load the registers as if there was
	 * no bug and then test to see if we are getting sync
	 */
	adder->y_scan_count_0 = 0x135F;
	adder->y_scan_count_1 = 0x3363;
	adder->y_scan_count_2 = 0x2366;
	adder->y_scan_count_3 = 0x0388;
	/*
	 * if no sync, do the bug fix code
	 */
	if (wait_status(adder, VSYNC) == BAD) {
		/* first load all Y scan registers with very short frame and
		 * wait for scroll service.  This guarantees at least one SYNC
		 * to fix the pass 2 Adder initialization bug (synchronizes
		 * XCINCH with DMSEEDH)
		 */
		adder->y_scan_count_0 = 0x01;
		adder->y_scan_count_1 = 0x01;
		adder->y_scan_count_2 = 0x01;
		adder->y_scan_count_3 = 0x01;
		/*
		 * delay at least 1 full frame time
		 */
		(void)wait_status(adder, VSYNC);
		(void)wait_status(adder, VSYNC);
		/*
		 * now load the REAL sync values (in reverse order just to
		 * be safe.
		 */
		adder->y_scan_count_3 = 0x0388;
		adder->y_scan_count_2 = 0x2366;
		adder->y_scan_count_1 = 0x3363;
		adder->y_scan_count_0 = 0x135F;
	}
	*memcsr = SYNC_ON | UNBLANK;	/* turn off leds and turn on video */
	/*
	 * zero the index registers
	 */
	adder->x_index_pending = 0;
	adder->y_index_pending = 0;
	adder->x_index_new = 0;
	adder->y_index_new = 0;
	adder->x_index_old = 0;
	adder->y_index_old = 0;
	adder->pause = 0;
	/*
	 * set rasterop mode to normal pen down
	 */
	adder->rasterop_mode = DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
	/*
	 * set the rasterop registers to a default values
	 */
	adder->source_1_dx = 1;
	adder->source_1_dy = 1;
	adder->source_1_x = 0;
	adder->source_1_y = 0;
	adder->destination_x = 0;
	adder->destination_y = 0;
	adder->fast_dest_dx = 1;
	adder->fast_dest_dy = 0;
	adder->slow_dest_dx = 0;
	adder->slow_dest_dy = 1;
	adder->error_1 = 0;
	adder->error_2 = 0;
	/*
	 * scale factor = UNITY
	 */
	adder->fast_scale = UNITY;
	adder->slow_scale = UNITY;
	/*
	 * set the source 2 parameters
	 */
	adder->source_2_x = 0;
	adder->source_2_y = 0;
	adder->source_2_size = 0x0022;
	/*
	* initialize plane addresses for eight vipers
	*/
	write_ID(adder, CS_UPDATE_MASK, 0x0001);
	write_ID(adder, PLANE_ADDRESS, 0x0000);
	write_ID(adder, CS_UPDATE_MASK, 0x0002);
	write_ID(adder, PLANE_ADDRESS, 0x0001);
	write_ID(adder, CS_UPDATE_MASK, 0x0004);
	write_ID(adder, PLANE_ADDRESS, 0x0002);
	write_ID(adder, CS_UPDATE_MASK, 0x0008);
	write_ID(adder, PLANE_ADDRESS, 0x0003);
	write_ID(adder, CS_UPDATE_MASK, 0x0010);
	write_ID(adder, PLANE_ADDRESS, 0x0004);
	write_ID(adder, CS_UPDATE_MASK, 0x0020);
	write_ID(adder, PLANE_ADDRESS, 0x0005);
	write_ID(adder, CS_UPDATE_MASK, 0x0040);
	write_ID(adder, PLANE_ADDRESS, 0x0006);
	write_ID(adder, CS_UPDATE_MASK, 0x0080);
	write_ID(adder, PLANE_ADDRESS, 0x0007);
	/*
	 * initialize the external registers.
	 */
	write_ID(adder, CS_UPDATE_MASK, 0x00FF);
	write_ID(adder, CS_SCROLL_MASK, 0x00FF);
	/*
	 * initialize resolution mode
	 */
	write_ID(adder, MEMORY_BUS_WIDTH, 0x000C);	/* bus width = 16 */
	write_ID(adder, RESOLUTION_MODE, 0x0000);	/* one bit/pixel */
	/*
	 * initialize viper registers
	 */
	write_ID(adder, SCROLL_CONSTANT, SCROLL_ENABLE|VIPER_LEFT|VIPER_UP);
	write_ID(adder, SCROLL_FILL, 0x0000);
	/*
	 * set clipping and scrolling limits to full screen
	 */
	for (i = 1000, adder->status = 0;
	     i > 0 && !(adder->status&ADDRESS_COMPLETE); --i)
		;
	if (i == 0)
	    printf("qd%d: setup_dragon: timeout on ADDRESS_COMPLETE\n",unit);
	top = 0;
	bottom = 2048;
	left = 0;
	right = 1024;
	adder->x_clip_min = left;
	adder->x_clip_max = right;
	adder->y_clip_min = top;
	adder->y_clip_max = bottom;
	adder->scroll_x_min = left;
	adder->scroll_x_max = right;
	adder->scroll_y_min = top;
	adder->scroll_y_max = bottom;
	(void)wait_status(adder, VSYNC);	/* wait at LEAST 1 full frame */
	(void)wait_status(adder, VSYNC);
	adder->x_index_pending = left;
	adder->y_index_pending = top;
	adder->x_index_new = left;
	adder->y_index_new = top;
	adder->x_index_old = left;
	adder->y_index_old = top;

	for (i = 1000, adder->status = 0; i > 0 &&
	     !(adder->status&ADDRESS_COMPLETE) ; --i)
		;
	if (i == 0)
		printf("qd%d: setup_dragon: timeout on ADDRESS_COMPLETE\n",unit);

	write_ID(adder, LEFT_SCROLL_MASK, 0x0000);
	write_ID(adder, RIGHT_SCROLL_MASK, 0x0000);
	/*
	* set source and the mask register to all ones (ie: white) o
	*/
	write_ID(adder, SOURCE, 0xFFFF);
	write_ID(adder, MASK_1, 0xFFFF);
	write_ID(adder, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
	write_ID(adder, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
	/*
	* initialize Operand Control Register banks for fill command
	*/
	write_ID(adder, SRC1_OCR_A, EXT_NONE | INT_M1_M2  | NO_ID | WAIT);
	write_ID(adder, SRC2_OCR_A, EXT_NONE | INT_SOURCE | NO_ID | NO_WAIT);
	write_ID(adder, DST_OCR_A, EXT_NONE | INT_NONE	 | NO_ID | NO_WAIT);
	write_ID(adder, SRC1_OCR_B, EXT_NONE | INT_SOURCE | NO_ID | WAIT);
	write_ID(adder, SRC2_OCR_B, EXT_NONE | INT_M1_M2  | NO_ID | NO_WAIT);
	write_ID(adder, DST_OCR_B, EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
	/*
	* init Logic Unit Function registers, (these are just common values,
	* and may be changed as required).
	*/
	write_ID(adder, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
	write_ID(adder, LU_FUNCTION_R2, FULL_SRC_RESOLUTION | LF_SOURCE |
		 INV_M1_M2);
	write_ID(adder, LU_FUNCTION_R3, FULL_SRC_RESOLUTION | LF_D_OR_S);
	write_ID(adder, LU_FUNCTION_R4, FULL_SRC_RESOLUTION | LF_D_XOR_S);
	/*
	* load the color map for black & white
	*/
	for (i = 0, adder->status = 0; i < 10000 && !(adder->status&VSYNC); ++i)
		;

	if (i == 0)
		printf("qd%d: setup_dragon: timeout on VSYNC\n", unit);

	red = (short *) qdmap[unit].red;
	green = (short *) qdmap[unit].green;
	blue = (short *) qdmap[unit].blue;

	*red++ = 0x00;			/* black */
	*green++ = 0x00;
	*blue++ = 0x00;

	*red-- = 0xFF;			/* white */
	*green-- = 0xFF;
	*blue-- = 0xFF;

	/*
	* set color map for mouse cursor
	*/

	red += 254;
	green += 254;
	blue += 254;

	*red++ = 0x00;			/* black */
	*green++ = 0x00;
	*blue++ = 0x00;

	*red = 0xFF;			/* white */
	*green = 0xFF;
	*blue = 0xFF;

} /* setup_dragon */

/*
 * Init the DUART and set defaults in input
 */
void
setup_input(int unit)
{
	volatile struct duart *duart;	/* DUART register structure pointer */
	int i, bits;
	char id_byte;

	duart = (struct duart *) qdmap[unit].duart;
	duart->imask = 0;

	/*
	* setup the DUART for kbd & pointing device
	*/
	duart->cmdA = RESET_M;	/* reset mode reg ptr for kbd */
	duart->modeA = 0x13;	/* 8 bits, no parity, rcv IE, */
				/* no RTS control,char error mode */
	duart->modeA = 0x07;	/* 1 stop bit,CTS does not IE XMT */
				/* no RTS control,no echo or loop */
	duart->cmdB = RESET_M;	/* reset mode reg pntr for host */
	duart->modeB = 0x07;	/* 8 bits, odd parity, rcv IE.. */
				/* ..no RTS cntrl, char error mode */
	duart->modeB = 0x07;	/* 1 stop bit,CTS does not IE XMT */
				/* no RTS control,no echo or loop */
	duart->auxctl = 0x00;	/* baud rate set 1 */
	duart->clkselA = 0x99;	/* 4800 baud for kbd */
	duart->clkselB = 0x99;	/* 4800 baud for mouse */

	/* reset everything for keyboard */

	for (bits = RESET_M; bits < START_BREAK; bits += 0x10)
		duart->cmdA = bits;

	/* reset everything for host */

	for (bits = RESET_M; bits < START_BREAK; bits += 0x10)
		duart->cmdB = bits;

	duart->cmdA = EN_RCV | EN_XMT; /* enbl xmt & rcv for kbd */
	duart->cmdB = EN_RCV | EN_XMT; /* enbl xmt & rcv for pointer device */

	/*
	* init keyboard defaults (DUART channel A)
	*/
	for (i = 500; i > 0; --i) {
		if (duart->statusA&XMT_RDY) {
			duart->dataA = LK_DEFAULTS;
			break;
		}
	}

	for (i = 100000; i > 0; --i) {
		if (duart->statusA&RCV_RDY) {
			break;
		}
	}

	if (duart->dataA)	/* flush the ACK */
		;

	/*
	* identify the pointing device
	*/
	for (i = 500; i > 0; --i) {
		if (duart->statusB&XMT_RDY) {
			duart->dataB = SELF_TEST;
			break;
		}
	}

	/*
	* wait for 1st byte of self test report */

	for (i = 100000; i > 0; --i) {
		if (duart->statusB&RCV_RDY) {
			break;
		}
	}

	if (i == 0) {
		printf("qd[%d]: setup_input: timeout on 1st byte of self test\n"
		    ,unit);
		goto OUT;
	}

	if (duart->dataB)
		;

	/*
	* wait for ID byte of self test report
	*/
	for (i = 100000; i > 0; --i) {
		if (duart->statusB&RCV_RDY) {
			break;
		}
	}

	if (i == 0) {
		printf("qd[%d]: setup_input: timeout on 2nd byte of self test\n", unit);
		goto OUT;
	}

	id_byte = duart->dataB;

	/*
	* wait for other bytes to come in
	*/
	for (i = 100000; i > 0; --i) {
		if (duart->statusB & RCV_RDY) {
			if (duart->dataB)
				;
			break;
		}
	}
	if (i == 0) {
		printf("qd[%d]: setup_input: timeout on 3rd byte of self test\n", unit);
		goto OUT;
	}
	for (i = 100000; i > 0; --i) {
		if (duart->statusB&RCV_RDY) {
			if (duart->dataB)
				;
			break;
		}
	}
	if (i == 0) {
		printf("qd[%d]: setup_input: timeout on 4th byte of self test\n", unit);
		goto OUT;
	}
	/*
	* flag pointing device type and set defaults
	*/
	for (i=100000; i>0; --i)
		;		/*XXX*/

	if ((id_byte & 0x0F) != TABLET_ID) {
		qdflags[unit].pntr_id = MOUSE_ID;

		for (i = 500; i > 0; --i) {
			if (duart->statusB&XMT_RDY) {
				duart->dataB = INC_STREAM_MODE;
				break;
			}
		}
	}
	else {
		qdflags[unit].pntr_id = TABLET_ID;

		for (i = 500; i > 0; --i) {
			if (duart->statusB&XMT_RDY) {
				duart->dataB = T_STREAM;
				break;
			}
		}
	}
OUT:
	duart->imask = qdflags[unit].duart_imask;

} /* setup_input */

/*
 * delay for at least one display frame time
 *
 *	return: BAD means that we timed out without ever seeing the
 *		      vertical sync status bit
 *		GOOD otherwise
 */
int
wait_status(volatile struct adder *adder, int mask)
{
	int i;

	for (i = 10000, adder->status = 0 ; i > 0  &&
	     !(adder->status&mask) ; --i)
		;

	if (i == 0) {
		printf("wait_status: timeout polling for 0x%x in adder->status\n", mask);
		return(BAD);
	}

	return(GOOD);

} /* wait_status */

/*
 * write out onto the ID bus
 */
void
write_ID(volatile struct adder *adder, short adrs, short data)
{
	int i;

	for (i = 100000, adder->status = 0 ;
	      i > 0  &&  !(adder->status&ADDRESS_COMPLETE) ; --i)
		;

	if (i == 0)
		goto ERR;

	for (i = 100000, adder->status = 0 ;
	      i > 0  &&  !(adder->status&TX_READY) ; --i)
		;

	if (i > 0) {
		adder->id_data = data;
		adder->command = ID_LOAD | adrs;
		return ;
	}

ERR:
	printf("write_ID: timeout trying to write to VIPER\n");
	return ;

} /* write_ID */
