from DEC
authorMike Karels <karels@ucbvax.Berkeley.EDU>
Thu, 15 Jan 1987 00:56:35 +0000 (16:56 -0800)
committerMike Karels <karels@ucbvax.Berkeley.EDU>
Thu, 15 Jan 1987 00:56:35 +0000 (16:56 -0800)
SCCS-vsn: sys/vax/uba/qd.c 1.1

usr/src/sys/vax/uba/qd.c [new file with mode: 0644]

diff --git a/usr/src/sys/vax/uba/qd.c b/usr/src/sys/vax/uba/qd.c
new file mode 100644 (file)
index 0000000..90f3df6
--- /dev/null
@@ -0,0 +1,3932 @@
+
+#ifndef lint
+static char *sccsid = "@(#)qd.c        1.40    ULTRIX  10/2/86";
+#endif lint
+
+/************************************************************************
+ *                                                                     *
+ *                     Copyright (c) 1985, 1986 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
+ *
+ * Modification history
+ *
+ * QDSS workstation driver
+ *
+ * 26-Aug-86 - rsp (Ricky Palmer)
+ *
+ *     Cleaned up devioctl code to (1) zero out devget structure
+ *     upon entry and (2) use strlen instead of fixed storage
+ *     for bcopy's.
+ *
+ * 21-Jul-86 - Ram Rao
+ *     allowed cursor rectangle to hang (partially) off the
+ *     top and left of screen
+ *
+ * 11-Jul-86 - ricky palmer
+ *
+ *     Added adpt and nexus fields to DEVIOCGET code.
+ *
+ * 02-July-86 - Brian Stevens
+ *
+ *     added support for console writing to second QDSS display
+ *
+ * 20-May-86 - ricky palmer
+ *
+ *     Added new DEVIOCGET ioctl request code. V2.0
+ *
+ * 16-Apr-86 -- darrell
+ *      badaddr is now called via the macro BADADDR
+ *
+ * 14-Apr-86 -- afd
+ *      Changed UMEMmap to QMEMmap and umem to qmem.
+ *
+ *      v_console() is now refered to as v_consputc, and there is a
+ *      corresponding v_consgetc() (defined in /sys/vax/conf.c).
+ *
+ *      Added "qdgetc()" routine for console read.  Needed to read
+ *      user's answer to the "root device?" prompt with a generic kernel.
+ *
+ * 19-Mar-86 -- pmk
+ *      Change DELAY to 20000, because microdelay now in real microsec.
+ *
+ * 18-mar-86  -- jaw    br/cvec changed to NOT use registers.
+ *
+ * 11 mar 86  darrell  replaced percpu with cpusw, and removed all but
+ *                      one global reference
+ * 19 feb 86  bstevens no report of motion event on puck/stylus button action
+ * 18 feb 86  bstevens put in cursor box support for tablets
+ * 18-Mar-86 -- jaw  add routines to cpu switch for nexus/unibus addreses
+ *                   also got rid of some globals like nexnum.
+ *                   ka8800 cleanup.
+ * 06 dec 85  longo  added LK-201 error reporting for graphics device ops
+ * 03 dec 85  longo  made qddint() clear active bit on error
+ * 02 dec 85  longo  fixed up some crocks in the error messages
+ * 25 nov 85  longo  added error handling to DMA ISR and single user locking
+ * 19 nov 85  longo  eliminated "set_defaults()" by breaking out sub-calls.
+ *                  Changed init_shared to do real init of scroll struct
+ * 12 nov 85  longo  fixed bug in open that broke alternate console re-direct
+ * 11 nov 85  longo  changed "_vs_eventqueue" references to "qdinput"
+ * 08 nov 85  longo  improved select service for read/write select wakeup.
+ *                  Also set ISR's to ipl4 to allow the interval timer in.
+ * 04 nov 85  longo  fixed bugs in mouse button reporting and dma request stuff
+ * 30 oct 85  longo  DMA to/from user space is in place
+ * 14 oct 85  longo  added kernel msg redirect and QD_RDCURSOR ioctl
+ * 03 oct 85  longo  added support for multiple QDSS's
+ * 02 oct 85  longo  added color map loading services in qdioctl() & qdaint()
+ * 30 sep 85  longo  added DMA interrupt services
+ * 18 sep 85  longo  added scroll services to "qdaint()" adder intrpt service
+ *                  and put in supporting ioctl's
+ * 04 sep 85  longo  initial implementation of DMA is working
+ * 17 aug 85  longo  added support for the QDSS to be system console
+ * 05 aug 85  longo  now using qfont (QVSS & QDSS) as linked object
+ * 12 jun 85  longo  added mouse event loading to "qdiint()"
+ * 31 may 85  longo  put live interrupts into the probe() routine
+ * 30 may 85  longo  event queue shared memory implementation is now alive
+ * 29 may 85  longo  LK-201 input is now interrupt driven
+ * 25 apr 85  longo  MAPDEVICE works
+ * 14 mar 85  longo  created
+ *
+ *      todo:   fix rlogin bug in console stuff
+ *              cat -u console redirection
+ *              check error return from strategy routine
+ *              verify TOY time stuff (what format?)
+ *              look at system based macro implementation of VTOP
+ *
+ */
+
+#include "../data/qd_data.c"   /* include external references to data file */
+
+/*---------------------------------------------------------------------
+* macro to get system time.  Used to time stamp event queue entries */
+
+#define TOY ((time.tv_sec * 100) + (time.tv_usec / 10000))
+
+/*--------------------------------------------------------------------------
+* the "ioconf.c" program, built and used by auto config, externally refers
+* to definitions below.  */
+
+       int qdprobe();
+       int qdattach();
+       int qddint();                   /* DMA gate array intrpt service */
+       int qdaint();                   /* Dragon ADDER intrpt service */
+       int qdiint();
+
+       u_short qdstd[] = { 0 };
+
+       struct uba_driver qddriver = {  /* externally referenced: ioconf.c */
+
+           qdprobe,                    /* device probe entry */
+           0,                          /* no slave device */
+           qdattach,                   /* device attach entry */
+           0,                          /* no "fill csr/ba to start" */
+           qdstd,                      /* device addresses */
+           "qd",                       /* device name string */
+           qdinfo                      /* ptr to QDSS's uba_device struct */
+       };
+
+/*-------------------
+* general defines */
+
+#define QDPRIOR (PZERO-1)              /* must be negative */
+
+#define FALSE  0
+#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) >> PGSHIFT) /* convert qmem adrs */
+                                                    /* to system page # */
+
+/*------------------------------------------------------------------
+* 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)
+
+/*---------------------------------------------------------------
+* values used in mapping QDSS hardware into the Q memory space */
+
+#define CHUNK    (64 * 1024)
+#define QMEMSIZE  (1024 * 1024 * 4)    /* 4 meg */
+
+/*----------------------------------------------------------------------
+* 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 QDSSMAJOR      42              /* QDSS major device number */
+
+#define CONS           0
+#define ALTCONS        1
+#define GRAPHIC        2
+
+/*----------------------------------------------
+* console cursor bitmap (block cursor type)  */
+
+       short cons_cursor[32] = {      /* white block cursor */
+
+ /* 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          95                      /* # 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)
+/*
+#define FONT_Y         200
+*/
+
+       extern char q_font[];           /* reference font object code */
+
+       extern  char q_key[];           /* reference key xlation tables */
+       extern  char q_shift_key[];
+       extern  char *q_special[];
+
+/*--------------------------------------------------
+* definitions for cursor acceleration reporting  */
+
+#define ACC_OFF        0x01            /* acceleration is inactive */
+
+/*--------------------------------------------------------------------------
+* v_consputc is the switch that is used to redirect the console cnputc() to
+* the virtual console qdputc().
+* v_consgetc is the switch that is used to redirect the console getchar() to
+* the virtual console qdgetc().
+*/
+
+       extern (*v_consputc)();
+       int qdputc();           /* used to direct kernel console output */
+       extern (*v_consgetc)();
+       int qdgetc();           /* used to read kernel console input */
+
+       int qdstart();          /* used to direct /dev/console output */
+
+/*------------------------------------------------------------------------
+* 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;
+
+
+/*****************************************************************
+******************************************************************
+******************************************************************
+*
+*      DRIVER FUNCTIONS START HERE:
+*
+******************************************************************
+******************************************************************
+*****************************************************************/
+
+/*********************************************************************
+*
+*      qdcons_init()... init QDSS as console (before probe routine)
+*
+*********************************************************************/
+
+qdcons_init()
+{
+       register u_int unit;
+
+       int *ptep;                      /* page table entry pointer */
+       caddr_t phys_adr;               /* physical QDSS base adrs */
+       u_int mapix;                    /* index into QMEMmap[] array */
+
+       struct cpusw *cpup;             /* pointer to cpusw structure  */
+       u_short *qdaddr;                /* address of QDSS IO page CSR */
+       u_short *devptr;                /* vitual device space */
+
+#define QDSSCSR 0x1F00
+
+       unit = 0;
+
+/*----------------------------------------------------
+* find the cpusw entry that matches this machine. */
+
+       cpup = &cpusw[cpu];
+                               ;
+       if( cpup == NULL ) {
+               return(0);
+       }
+
+/*------------------------------------------------------
+* Map the Q-bus memory space into the system memory. */
+
+       ubaaccess(((*cpup->v_umaddr)(0)), QMEMmap[0],
+                       cpup->pc_umsize, PG_V | PG_KW);
+
+       ubaaccess(((*cpup->v_udevaddr)(0)), QMEMmap[0]+btop(cpup->pc_umsize),
+                       DEVSPACESIZE ,PG_V|PG_KW);
+
+/*---------------------------------------------------------------------
+* map the QDSS into the Qbus memory (which is now in system space)  */
+
+       devptr = (u_short *)((char *)qmem[0]+cpup->pc_umsize);
+       qdaddr = (u_short *)((u_int)devptr + ubdevreg(QDSSCSR));
+
+       if (BADADDR(qdaddr, sizeof(short)))
+               return(0);
+
+       /*---------------------------------------------------
+       * tell QDSS which Q memory address base to decode */
+
+       mapix = (int) VTOP(QMEMSIZE - CHUNK);
+       ptep = (int *) QMEMmap[0] + mapix;
+       phys_adr = (caddr_t) (((int)*ptep & 0x001FFFFF) << PGSHIFT);
+       *qdaddr = (u_short) ((int)phys_adr >> 16);
+
+       qdflags[unit].config = *(u_short *)qdaddr;
+
+/*----------------------------------------------------------------------
+* load qdmap struct with the virtual addresses of the QDSS elements */
+
+       qdbase[unit] = (caddr_t) (qmem[0] + QMEMSIZE - CHUNK);
+
+       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 */
+
+/*----------------------------------------------------
+* smash the system's virtual console address table */
+
+       v_consputc = qdputc;
+       v_consgetc = qdgetc;
+       cdevsw[0] = cdevsw[QDSSMAJOR];
+       return(1);
+
+} /* qdcons_init */
+
+/*********************************************************************
+*
+*      qdprobe()... configure QDSS into Q memory and make it intrpt
+*
+**********************************************************************
+*
+*  calling convention:
+*                      qdprobe(reg, ctlr);
+*                      caddr_t reg;
+*                      int ctlr;
+*
+*      where: reg - a character pointer to the QDSS I/O page register
+*             ctlr - controller number (?)
+*
+*  side effects: QDSS gets mapped into Qbus memory space at the first
+*               vacant 64kb boundary counting back from the top of
+*               Qbus memory space (qmem+4mb)
+*
+*  return: QDSS bus request level and vector address returned in
+*         registers by UNIX convention.
+*
+*****************/
+
+qdprobe(reg)
+caddr_t reg;
+{
+       /* the variables MUST reside in the first two register declarations
+       * by UNIX convention in order that they be loaded and returned
+       * properly by the interrupt catching mechanism.  */
+
+       register int unit;
+
+       struct dga *dga;                /* pointer to gate array structure */
+       struct cpusw *cpup;             /* pointer to the cpusw structure */
+
+       int *ptep;                      /* page table entry pointer */
+       int vector;
+
+       caddr_t phys_adr;               /* physical QDSS base adrs */
+       u_int mapix;
+
+/*---------------------------------------------------------------
+* calculate board unit number from I/O page register address  */
+
+       unit = (int) (((int)reg >> 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, "qmem[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 "QMEMmap[]" (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.  */
+
+       /* if this QDSS is NOT the console, then do init here.. */
+
+       if (v_consputc != qdputc  ||  unit != 0) {
+
+           /*-------------------------
+           * read QDSS config info */
+
+           qdflags[unit].config = *(u_short *)reg;
+
+           /*------------------------------------
+           * find an empty 64kb adrs boundary */
+
+           qdbase[unit] = (caddr_t) (qmem[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(qmem[0]));
+           ptep = (int *) QMEMmap[0] + mapix;
+           phys_adr = (caddr_t) (((int)*ptep & 0x001FFFFF) << 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;
+       }
+
+/*--------------------------------------------------------------------------
+* 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 = (uba_hd[0].uh_lastiv -= 4*3);  /* take three vectors */
+
+       while (vector & 0x0F) {                    /* if lo nibble != 0.. */
+           vector = (uba_hd[0].uh_lastiv -= 4);  /* ..take another vector */
+       }
+
+       /*---------------------------------------------------------
+       * 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 */
+
+/*----------
+* exits  */
+
+       if (cvec != vector)             /* if vector != base vector.. */
+           return(0);                  /* ..return = 'no device' */
+
+       return(sizeof(short));      /* return size of QDSS I/O page reg */
+
+} /* qdprobe */
+
+/*****************************************************************
+*
+*      qdattach()... do the one-time initialization
+*
+******************************************************************
+*
+*  calling convention:
+*                      qdattach(ui);
+*                      struct uba_device *ui;
+*
+*              where: ui - pointer to the QDSS's uba_device structure
+*
+*  side effects: none
+*       return: none
+*
+*************************/
+
+qdattach(ui)
+struct uba_device *ui;
+{
+       register u_int unit;            /* QDSS module # for this call */
+
+       unit = ui->ui_unit;             /* get QDSS number */
+
+/*----------------------------------
+* init "qdflags[]" for this QDSS */
+
+       qdflags[unit].inuse = 0;        /* init inuse variable EARLY! */
+       qdflags[unit].mapped = 0;
+       qdflags[unit].kernel_loop = 0;
+       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;
+
+/*------------------------------------------
+* init single process access lock switch */
+
+       one_only[unit] = 0;
+
+} /* qdattach */
+
+/***************************************************************
+*
+*      qdopen()... open a minor device
+*
+****************************************************************
+*
+*  calling convention: qdopen(dev, flag);
+*                     dev_t dev;
+*                     int flag;
+*
+*  side effects: none
+*
+*********************/
+
+qdopen(dev, flag)
+dev_t dev;
+int flag;
+{
+       register struct uba_device *ui; /* ptr to uba structures */
+       register struct dga *dga;       /* ptr to gate array struct */
+       register struct tty *tp;
+
+       struct adder *adder;
+       struct duart *duart;
+
+       u_int unit;
+       u_int minor_dev;
+       int s;
+
+       minor_dev = minor(dev); /* get QDSS minor device number */
+       unit = minor_dev >> 2;
+
+/*---------------------------------
+* check for illegal conditions */
+
+       ui = qdinfo[unit];              /* get ptr to QDSS device struct */
+
+       if (ui == 0  || ui->ui_alive == 0)
+           return(ENXIO);              /* no such device or address */
+
+/*--------------
+* init stuff */
+
+       adder = (struct adder *) qdmap[unit].adder;
+       duart = (struct duart *) qdmap[unit].duart;
+       dga = (struct dga *) qdmap[unit].dga;
+
+/*------------------------------------
+* if this is the graphic device... */
+
+       if ((minor_dev & 0x03) == 2) {
+
+           if (one_only[unit] != 0)
+               return(EBUSY);
+           else
+               one_only[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;
+
+/*------------------------------------------------------------------
+* if the open call is to the console or the alternate console... */
+
+       } else if ((minor_dev & 0x03) != 2) {
+
+           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;
+
+           if ((tp->t_state & TS_ISOPEN) == 0) {
+
+               ttychars(tp);
+               tp->t_state = TS_ISOPEN | TS_CARR_ON;
+               tp->t_ispeed = B9600;
+               tp->t_ospeed = B9600;
+
+               if( (minor_dev & 0x03) == 0 )
+                   tp->t_flags = XTABS|EVENP|ECHO|CRMOD;
+               else
+                   tp->t_flags = RAW;
+           }
+
+           /*----------------------------------------
+           * enable intrpts, open line discipline */
+
+           dga->csr |= GLOBAL_IE;      /* turn on the interrupts */
+           return ((*linesw[tp->t_line].l_open)(dev, tp));
+       }
+
+       dga->csr |= GLOBAL_IE;  /* turn on the interrupts */
+       return(0);
+
+} /* qdopen */
+
+/***************************************************************
+*
+*      qdclose()... clean up on the way out
+*
+****************************************************************
+*
+*  calling convention: qdclose();
+*
+*  side effects: none
+*
+*  return: none
+*
+*********************/
+
+qdclose(dev, flag)
+dev_t dev;
+int flag;
+{
+       register struct tty *tp;
+       register struct qdmap *qd;
+       register int *ptep;
+       int i;                          /* SIGNED index */
+
+       struct dga *dga;                /* gate array register map pointer */
+       struct duart *duart;
+       struct adder *adder;
+
+       u_int unit;
+       u_int minor_dev;
+       u_int mapix;
+
+       minor_dev = minor(dev);         /* get minor device number */
+       unit = minor_dev >> 2;          /* get QDSS number */
+       qd = &qdmap[unit];
+
+/*------------------------------------
+* if this is the graphic device... */
+
+       if ((minor_dev & 0x03) == 2) {
+
+           /*-----------------
+           * unlock driver */
+
+           if (one_only[unit] != 1)
+               return(EBUSY);
+           else
+               one_only[unit] = 0;
+
+           /*----------------------------
+           * re-protect device memory */
+
+           if (qdflags[unit].mapped & MAPDEV) {
+
+               /*----------------
+               * TEMPLATE RAM */
+
+               mapix = VTOP((int)qd->template) - VTOP(qmem[0]);
+               ptep = (int *)(QMEMmap[0] + mapix);
+
+               for (i = VTOP(TMPSIZE); i > 0; --i)
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_V | PG_KW;
+
+               /*---------
+               * ADDER */
+
+               mapix = VTOP((int)qd->adder) - VTOP(qmem[0]);
+               ptep = (int *)(QMEMmap[0] + mapix);
+
+               for (i = VTOP(REGSIZE); i > 0; --i)
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_V | PG_KW;
+
+               /*--------------
+               * COLOR MAPS */
+
+               mapix = VTOP((int)qd->red) - VTOP(qmem[0]);
+               ptep = (int *)(QMEMmap[0] + mapix);
+
+               for (i = VTOP(CLRSIZE); i > 0; --i)
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_V | PG_KW;
+           }
+
+           /*----------------------------------------------------
+           * re-protect DMA buffer and free the map registers */
+
+           if (qdflags[unit].mapped & MAPDMA) {
+
+               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(SBR)|0x80000000));
+
+               for (i = (DMAbuf_size >> PGSHIFT); i > 0; --i)
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_V | PG_KW;
+
+               ubarelse(0, &Qbus_unmap[unit]);
+           }
+
+           /*---------------------------------------
+           * re-protect 1K (2 pages) event queue */
+
+           if (qdflags[unit].mapped & MAPEQ) {
+
+               ptep = (int *)
+                       ((VTOP(eq_header[unit])*4) + (mfpr(SBR)|0x80000000));
+
+               *ptep++ = (*ptep & ~PG_PROT) | PG_KW | PG_V;
+               *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(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(SBR) | 0x80000000));
+
+               *ptep++ = (*ptep & ~PG_PROT) | PG_KW | PG_V;
+               *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;
+           }
+
+           /*-----------------------------------
+           * flag that everthing is unmapped */
+
+           mtpr(TBIA, 0);              /* smash CPU's translation buf */
+           qdflags[unit].mapped = 0;   /* flag everything now unmapped */
+           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 | ALTCONS_DEV))) {
+
+               dga = (struct dga *) qdmap[unit].dga;
+               dga->csr &= ~(GLOBAL_IE | DMA_IE);
+           }
+       }
+
+/*----------------------------------------------------
+* if this is the console or the alternate console  */
+
+       else {
+
+           tp = &qd_tty[minor_dev];
+
+           (*linesw[tp->t_line].l_close)(tp);
+           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);
+           }
+       }
+
+/*--------
+* exit */
+
+       return(0);
+
+} /* qdclose */
+
+/***************************************************************
+*
+*      qdioctl()... provide QDSS control services
+*
+****************************************************************
+*
+*  calling convention: qdioctl(dev, cmd, datap, flags);
+*
+*              where:  dev - the major/minor device number
+*                      cmd - the user-passed command argument
+*                      datap - ptr to user input buff (128 bytes max)
+*                      flags - "f_flags" from "struct file" in file.h
+*
+*
+*      - here is the format for the input "cmd" argument
+*
+*      31     29 28    23 22         16 15             8 7              0
+*      +----------------------------------------------------------------+
+*      |I/O type|        | buff length | device ID char |  user command |
+*      +----------------------------------------------------------------+
+*
+*  Return data is in the data buffer pointed to by "datap" input spec
+*
+*********************/
+
+qdioctl(dev, cmd, datap, flags)
+dev_t dev;
+int cmd;
+caddr_t datap;
+int flags;
+{
+       register int *ptep;             /* page table entry pointer */
+       register int mapix;             /* QMEMmap[] page table index */
+       register struct _vs_event *event;
+       register struct tty *tp;
+
+       struct qdmap *qd;               /* pointer to device map struct */
+       struct dga *dga;                /* Gate Array reg structure pntr */
+       struct duart *duart;            /* DUART reg structure pointer */
+       struct adder *adder;            /* ADDER reg structure pointer */
+
+       struct prgkbd *cmdbuf;
+       struct prg_cursor *curs;
+       struct _vs_cursor *pos;
+
+       u_int unit = minor(dev) >> 2;   /* number of caller's QDSS */
+       u_int minor_dev = minor(dev);
+       struct uba_device *ui = qdinfo[unit];
+       struct qd_softc *sc = &qd_softc[ui->ui_unit];
+       struct devget *devget;
+
+       int error;
+       int s;
+
+       int i;                          /* SIGNED index */
+       int sbr;                        /* SBR variable (you silly boy) */
+       u_int ix;
+
+       short status;
+       short *shortp;                  /* generic pointer to a short */
+       char *chrp;                     /* generic character pointer */
+
+       short *temp;                    /* a pointer to template RAM */
+
+/*-----------------------------------------
+* service graphic device ioctl commands */
+
+       switch (cmd) {
+
+           /*-------------------------------------------------
+           * extract the oldest event from the event queue */
+
+           case QD_GETEVENT:
+
+               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);
+               bcopy(event, datap, sizeof(struct _vs_event));
+               break;
+
+           /*-------------------------------------------------------
+           * init the dragon stuff, DUART, and driver variables  */
+
+           case QD_RESET:
+
+               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;
+
+           /*----------------------------------------
+           * init the DUART and driver variables  */
+
+           case QD_SET:
+
+               init_shared(unit);
+               setup_input(unit);
+               break;
+
+           /*---------------------------------------------------------------
+           * clear the QDSS screen.  (NOTE that this reinits the dragon) */
+
+           case QD_CLRSCRN:
+
+               setup_dragon(unit);
+               clear_qd_screen(unit);
+               break;
+
+           /*------------------------------------
+           * load a cursor into template RAM  */
+
+           case QD_WTCURSOR:
+
+               ldcursor(unit, 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;
+
+           /*------------------------------
+           * position the mouse cursor  */
+
+           case QD_POSCURSOR:
+
+               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;
+
+           /*--------------------------------------
+           * set the cursor acceleration factor */
+
+           case QD_PRGCURSOR:
+
+               curs = (struct prg_cursor *) datap;
+               s = spl5();
+               qdflags[unit].curs_acc = curs->acc_factor;
+               qdflags[unit].curs_thr = curs->threshold;
+               splx(s);
+               break;
+
+           /*---------------------------------------
+           * enable 'user write' to device pages */
+
+           case QD_MAPDEVICE:
+
+               /*--------------
+               * init stuff */
+
+               qdflags[unit].mapped |= MAPDEV;
+               qd = (struct qdmap *) &qdmap[unit];
+
+               /*-------------------------------------
+               * enable user write to template RAM */
+
+               mapix = VTOP((int)qd->template) - VTOP(qmem[0]);
+               ptep = (int *)(QMEMmap[0] + mapix);
+
+               for (i = VTOP(TMPSIZE); i > 0; --i)
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+
+               /*----------------------------------
+               * enable user write to registers */
+
+               mapix = VTOP((int)qd->adder) - VTOP(qmem[0]);
+               ptep = (int *)(QMEMmap[0] + mapix);
+
+               for (i = VTOP(REGSIZE); i > 0; --i)
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+
+               /*-----------------------------------
+               * enable user write to color maps */
+
+               mapix = VTOP((int)qd->red) - VTOP(qmem[0]);
+               ptep = (int *)(QMEMmap[0] + mapix);
+
+               for (i = VTOP(CLRSIZE); i > 0; --i)
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+
+               /*------------------------------
+               * enable user write to DUART */
+
+               mapix = VTOP((int)qd->duart) - VTOP(qmem[0]);
+               ptep = (int *)(QMEMmap[0] + mapix);
+               *ptep = (*ptep & ~PG_PROT) | PG_UW | PG_V; /* duart page */
+
+               mtpr(TBIA, 0);          /* smash CPU's translation buffer */
+
+               /*------------------------------------------
+               * stuff qdmap structure in return buffer */
+
+               bcopy(qd, datap, sizeof(struct qdmap));
+               break;
+
+           /*-------------------------------------
+           * do setup for DMA by user process  */
+
+           case QD_MAPIOBUF:
+
+               /*------------------------------------------------
+               * set 'user write enable' bits for DMA buffer  */
+
+               qdflags[unit].mapped |= MAPDMA;
+
+               ptep = (int *) ((VTOP(DMAheader[unit]) * 4)
+                               + (mfpr(SBR) | 0x80000000));
+
+               for (i = (DMAbuf_size >> PGSHIFT); i > 0; --i)
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+
+               mtpr(TBIA, 0);                  /* clr CPU translation buf */
+
+               /*-------------------------------------
+               * set up QBUS map registers for DMA */
+
+               DMAheader[unit]->QBAreg =
+                               uballoc(0, DMAheader[unit], DMAbuf_size, 0);
+
+               if (DMAheader[unit]->QBAreg == 0)
+                   mprintf("\nqd%d: qdioctl: QBA setup error", unit);
+
+               Qbus_unmap[unit] = DMAheader[unit]->QBAreg;
+               DMAheader[unit]->QBAreg &= 0x3FFFF;
+
+               /*----------------------
+               * return I/O buf adr */
+
+               *(int *)datap = (int) DMAheader[unit];
+               break;
+
+           /*----------------------------------------------------------------
+           * map the shared scroll param area and enable scroll interpts  */
+
+           case QD_MAPSCROLL:
+
+               qdflags[unit].mapped |= MAPSCR;
+
+               ptep = (int *) ((VTOP(scroll[unit]) * 4)
+                               + (mfpr(SBR) | 0x80000000));
+
+               /* allow user write to scroll area */
+
+               *ptep = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+
+               mtpr(TBIA, 0);                  /* clr CPU 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;
+
+               /* return scroll area address */
+
+               *(int *)datap = (int) scroll[unit];
+               break;
+
+           /*-------------------------------------------------------------
+           * unmap shared scroll param area and disable scroll intrpts */
+
+           case QD_UNMAPSCROLL:
+
+               if (qdflags[unit].mapped & MAPSCR) {
+
+                   qdflags[unit].mapped &= ~MAPSCR;
+
+                   ptep = (int *) ((VTOP(scroll[unit]) * 4)
+                                   + (mfpr(SBR) | 0x80000000));
+
+                   /* re-protect 512 scroll param area */
+
+                   *ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V;
+
+                   mtpr(TBIA, 0);      /* 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;
+
+           /*-----------------------------------------------------------
+           * map shared color map write buf and turn on vsync intrpt */
+
+           case QD_MAPCOLOR:
+
+               qdflags[unit].mapped |= MAPCOLOR;
+
+               ptep = (int *) ((VTOP(color_buf[unit]) * 4)
+                               + (mfpr(SBR) | 0x80000000));
+
+               /* allow user write to color map write buffer */
+
+               *ptep++ = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+               *ptep = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+
+               mtpr(TBIA, 0);                  /* clr CPU translation buf */
+
+               adder = (struct adder *) qdmap[unit].adder;
+
+               qdflags[unit].adder_ie |= VSYNC;
+               adder->interrupt_enable = qdflags[unit].adder_ie;
+
+               /* return scroll area address */
+
+               *(int *)datap = (int) color_buf[unit];
+               break;
+
+           /*--------------------------------------------------------------
+           * unmap shared color map write buffer and kill VSYNC intrpts */
+
+           case QD_UNMAPCOLOR:
+
+               if (qdflags[unit].mapped & MAPCOLOR) {
+
+                   qdflags[unit].mapped &= ~MAPCOLOR;
+
+                   ptep = (int *) ((VTOP(color_buf[unit]) * 4)
+                                   + (mfpr(SBR) | 0x80000000));
+
+                   /* re-protect color map write buffer */
+
+                   *ptep++ = (*ptep & ~PG_PROT) | PG_KW | PG_V;
+                   *ptep = (*ptep & ~PG_PROT) | PG_KW | PG_V;
+
+                   mtpr(TBIA, 0);      /* smash CPU's translation buf */
+
+                   adder = (struct adder *) qdmap[unit].adder;
+
+                   qdflags[unit].adder_ie &= ~VSYNC;
+                   adder->interrupt_enable = qdflags[unit].adder_ie;
+               }
+               break;
+
+           /*---------------------------------------------
+           * give user write access to the event queue */
+
+           case QD_MAPEVENT:
+
+               qdflags[unit].mapped |= MAPEQ;
+
+               ptep = (int *) ((VTOP(eq_header[unit]) * 4)
+                               + (mfpr(SBR) | 0x80000000));
+
+               /* allow user write to 1K event queue */
+
+               *ptep++ = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+               *ptep = (*ptep & ~PG_PROT) | PG_UW | PG_V;
+
+               mtpr(TBIA, 0);                  /* clr CPU translation buf */
+
+               /* return event queue address */
+
+               *(int *)datap = (int) eq_header[unit];
+               break;
+
+           /*-----------------------------------------------
+           * pass caller's programming commands to LK201 */
+
+           case QD_PRGKBD:
+
+               duart = (struct duart *) qdmap[unit].duart;
+               cmdbuf = (struct prgkbd *) datap;    /* pnt to kbd cmd buf */
+
+               /*----------------
+               * send command */
+
+               for (i = 1000; i > 0; --i) {
+                   if ((status = duart->statusA) & XMT_RDY) {
+                       duart->dataA = cmdbuf->cmd;
+                       break;
+                   }
+               }
+
+               if (i == 0) {
+                   mprintf("\nqd%d: qdioctl: timeout on XMT_RDY [1]", unit);
+                   break;
+               }
+
+               /*----------------
+               * send param1? */
+
+               if (cmdbuf->cmd & LAST_PARAM)
+                   break;
+
+               for (i = 1000; i > 0; --i) {
+                   if ((status = duart->statusA) & XMT_RDY) {
+                       duart->dataA = cmdbuf->param1;
+                       break;
+                   }
+               }
+
+               if (i == 0) {
+                   mprintf("\nqd%d: qdioctl: timeout on XMT_RDY [2]", unit);
+                   break;
+               }
+
+               /*----------------
+               * send param2? */
+
+               if (cmdbuf->param1 & LAST_PARAM)
+                   break;
+
+               for (i = 1000; i > 0; --i) {
+                   if ((status = duart->statusA) & XMT_RDY) {
+                       duart->dataA = cmdbuf->param2;
+                       break;
+                   }
+               }
+
+               if (i == 0) {
+                   mprintf("\nqd%d: qdioctl: timeout on XMT_RDY [3]", unit);
+                   break;
+               }
+
+               break;
+
+           /*----------------------------------------------------
+           * pass caller's programming commands to the mouse  */
+
+           case QD_PRGMOUSE:
+
+               duart = (struct duart *) qdmap[unit].duart;
+
+               for (i = 1000; i > 0; --i) {
+                   if ((status = duart->statusB) & XMT_RDY) {
+                       duart->dataB = *datap;
+                       break;
+                   }
+               }
+
+               if (i == 0) {
+                   mprintf("\nqd%d: qdioctl: timeout on XMT_RDY [4]", unit);
+               }
+
+               break;
+
+           /*----------------------------------------------
+           * get QDSS configuration word and return it  */
+
+           case QD_RDCONFIG:
+
+               *(short *)datap = qdflags[unit].config;
+               break;
+
+           /*--------------------------------------------------------------
+           * re-route kernel console messages to the alternate console  */
+
+           case QD_KERN_LOOP:
+
+               qdflags[unit].kernel_loop = -1;
+               break;
+
+           case QD_KERN_UNLOOP:
+
+               qdflags[unit].kernel_loop = 0;
+               break;
+
+           /*----------------------
+           * program the tablet */
+
+           case QD_PRGTABLET:
+
+               duart = (struct duart *) qdmap[unit].duart;
+
+               for (i = 1000; i > 0; --i) {
+                   if ((status = duart->statusB) & XMT_RDY) {
+                       duart->dataB = *datap;
+                       break;
+                   }
+               }
+
+               if (i == 0) {
+                   mprintf("\nqd%d: qdioctl: timeout on XMT_RDY [5]", unit);
+               }
+
+               break;
+
+           /*-----------------------------------------------
+           * program the tablet report resolution factor */
+
+           case QD_PRGTABRES:
+
+               qdflags[unit].tab_res = *(short *)datap;
+               break;
+
+           case DEVIOCGET:                         /* device status */
+                   devget = (struct devget *)datap;
+                   bzero(devget,sizeof(struct devget));
+                   devget->category = DEV_TERMINAL;
+                   devget->bus = DEV_QB;
+                   bcopy(DEV_VCB02,devget->interface,
+                         strlen(DEV_VCB02));
+                   bcopy(DEV_VR290,devget->device,
+                         strlen(DEV_VR290));               /* terminal */
+                   devget->adpt_num = ui->ui_adpt;         /* which adapter*/
+                   devget->nexus_num = ui->ui_nexus;       /* which nexus  */
+                   devget->bus_num = ui->ui_ubanum;        /* which QB     */
+                   devget->ctlr_num = unit;                /* which interf.*/
+                   devget->slave_num = unit;               /* which line   */
+                   bcopy(ui->ui_driver->ud_dname,
+                         devget->dev_name,
+                         strlen(ui->ui_driver->ud_dname)); /* Ultrix "qd"  */
+                   devget->unit_num = unit;                /* qd line?     */
+                   devget->soft_count =
+                         sc->sc_softcnt;                   /* soft er. cnt.*/
+                   devget->hard_count =
+                         sc->sc_hardcnt;                   /* hard er cnt. */
+                   devget->stat = sc->sc_flags;            /* status       */
+                   devget->category_stat =
+                         sc->sc_category_flags;            /* cat. stat.   */
+                   break;
+
+           default:
+               /*-----------------------------
+               * service tty type ioctl's  */
+
+                   if (!(minor_dev & 0x02)) {
+
+                       tp = &qd_tty[minor_dev];
+
+                       error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, datap, flags);
+                       if (error >= 0) {
+                           return(error);
+                       }
+
+                       error = ttioctl(tp, cmd, datap, flags);
+                       if (error >= 0) {
+                           return(error);
+                       }
+                   }
+                   break;
+       }
+
+/*--------------------------------
+* clean up and get outta here  */
+
+       return(0);
+
+} /* qdioctl */
+
+/**********************************************************************
+*
+*      qdselect()... service select call for event queue input
+*
+**********************************************************************/
+
+qdselect(dev, rw)
+dev_t dev;
+int rw;
+{
+       register int s;
+       register int unit;
+
+       s = spl5();
+       unit = minor(dev) >> 2;
+
+       switch (rw) {
+
+           case FREAD:                 /* event available? */
+
+               if(!(ISEMPTY(eq_header[unit]))) {
+                   splx(s);
+                   return(1);          /* return "1" if event exists */
+               }
+               rsel[unit] = u.u_procp;
+               qdflags[unit].selmask |= SEL_READ;
+               splx(s);
+               return(0);
+
+           case FWRITE:                /* DMA done? */
+
+               if (DMA_ISEMPTY(DMAheader[unit])) {
+                   splx(s);
+                   return(1);          /* return "1" if DMA is done */
+               }
+               rsel[unit] = u.u_procp;
+               qdflags[unit].selmask |= SEL_WRITE;
+               splx(s);
+               return(0);
+       }
+
+} /* qdselect() */
+
+/***************************************************************
+*
+*      qdwrite()... output to the QDSS screen as a TTY
+*
+***************************************************************/
+
+extern qd_strategy();
+
+qdwrite(dev, uio)
+dev_t dev;
+struct uio *uio;
+{
+       register struct tty *tp;
+       register int minor_dev;
+       register int unit;
+
+       minor_dev = minor(dev);
+       unit = (minor_dev >> 2) & 0x07;
+
+       /*------------------------------
+       * if this is the console...  */
+
+       if ((minor_dev & 0x03) != 0x02  &&
+            qdflags[unit].inuse & CONS_DEV) {
+           tp = &qd_tty[minor_dev];
+           return ((*linesw[tp->t_line].l_write)(tp, uio));
+       }
+
+       /*------------------------------------------------
+       * else this must be a DMA xfer from user space */
+
+       else if (qdflags[unit].inuse & GRAPHIC_DEV) {
+           return (physio(qd_strategy, &qdbuf[unit],
+                          dev, B_WRITE, minphys, uio));
+       }
+}
+
+/***************************************************************
+*
+*      qdread()... read from QDSS keyboard as a TTY
+*
+***************************************************************/
+
+qdread(dev, uio)
+dev_t dev;
+struct uio *uio;
+{
+       register struct tty *tp;
+       register int minor_dev;
+       register int unit;
+
+       minor_dev = minor(dev);
+       unit = (minor_dev >> 2) & 0x07;
+
+       /*------------------------------
+       * if this is the console...  */
+
+       if ((minor_dev & 0x03) != 0x02  &&
+            qdflags[unit].inuse & CONS_DEV) {
+           tp = &qd_tty[minor_dev];
+           return ((*linesw[tp->t_line].l_read)(tp, uio));
+       }
+
+       /*------------------------------------------------
+       * else this must be a bitmap-to-processor xfer */
+
+       else if (qdflags[unit].inuse & GRAPHIC_DEV) {
+           return (physio(qd_strategy, &qdbuf[unit],
+                          dev, B_READ, minphys, uio));
+       }
+}
+
+/***************************************************************
+*
+*      qd_strategy()... strategy routine to do DMA
+*
+***************************************************************/
+
+qd_strategy(bp)
+register struct buf *bp;
+{
+       register struct dga *dga;
+       register struct adder *adder;
+
+       char *DMAbufp;
+
+       int QBAreg;
+       int bytcnt;
+       int s;
+       int unit;
+       int cookie;
+
+       int i,j,k;
+
+       unit = (minor(bp->b_dev) >> 2) & 0x07;
+
+/*-----------------
+* init pointers */
+
+       if ((QBAreg = ubasetup(0, bp, 0)) == 0) {
+           mprintf("\nqd%d: qd_strategy: QBA setup error", unit);
+           goto STRAT_ERR;
+       }
+
+       dga = (struct dga *) qdmap[unit].dga;
+
+       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) {
+           sleep((caddr_t)&qdflags[unit].user_dma, QDPRIOR);
+       }
+
+       splx(s);
+       ubarelse(0, &QBAreg);
+
+       if (!(dga->csr & DMA_ERR)) {
+           iodone(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_flags |= B_ERROR;         /* 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;
+       }
+
+       iodone(bp);
+
+} /* qd_strategy */
+
+/*******************************************************************
+*
+*      qdstart()... startup output to the console screen
+*
+********************************************************************
+*
+*      calling convention:
+*
+*              qdstart(tp);
+*              struct tty *tp;         ;pointer to tty structure
+*
+********/
+
+qdstart(tp)
+register struct tty *tp;
+{
+       register int which_unit, unit, c;
+       register struct tty *tp0;
+       int s;
+
+       int curs_on;
+       struct dga *dga;
+
+       unit = minor(tp->t_dev);
+
+       tp0 = &qd_tty[(unit & 0x0FC)+1];
+       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, if the alternate console device
+* is open direct chars there.  Drop input from anything but the console
+* device on the floor. */
+
+       while (tp->t_outq.c_cc) {
+           c = getc(&tp->t_outq);
+           if (unit == 0) {
+               if (tp0->t_state & TS_ISOPEN)
+                   (*linesw[tp0->t_line].l_rint)(c, tp0);
+               else
+                   blitc(which_unit, c & 0xFF);
+           }
+       }
+
+/*--------------------------------------------------------
+* If there are sleepers, and output has drained below low
+* water mark, wake up the sleepers. */
+
+       if ( tp->t_outq.c_cc <= TTLOWAT(tp) ) {
+               if (tp->t_state & TS_ASLEEP){
+                       tp->t_state &= ~TS_ASLEEP;
+                       wakeup((caddr_t) &tp->t_outq);
+               }
+       }
+
+       tp->t_state &= ~TS_BUSY;
+
+out:
+       splx(s);
+
+} /* qdstart */
+
+
+/*******************************************************************
+*
+*      qdstop()... stop the tty
+*
+*******************************************************************/
+
+qdstop(tp, flag)
+register struct tty *tp;
+int flag;
+{
+       register 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);
+}
+
+/*******************************************************************
+*
+*      blitc()... output a character to the QDSS screen
+*
+********************************************************************
+*
+*      calling convention:
+*
+*              blitc(chr);
+*              char chr;               ;character to be displayed
+*
+********/
+
+blitc(unit, chr)
+int unit;
+char chr;
+{
+       register struct adder *adder;
+       register struct dga *dga;
+       register int i;
+
+       short x;
+
+/*---------------
+* init stuff  */
+
+       adder = (struct adder *) qdmap[unit].adder;
+       dga = (struct dga *) qdmap[unit].dga;
+
+/*---------------------------
+* non display character?  */
+
+       chr &= 0x7F;
+
+       switch (chr) {
+
+           case '\r':                  /* return char */
+               cursor[unit].x = 0;
+               dga->x_cursor = TRANX(cursor[unit].x);
+               return(0);
+
+           case '\t':                  /* tab char */
+
+               for (i = 8 - ((cursor[unit].x >> 3) & 0x07); i > 0; --i) {
+                   blitc(unit, ' ');
+               }
+               return(0);
+
+           case '\n':                  /* line feed char */
+
+               if ((cursor[unit].y += CHAR_HEIGHT) > (863 - CHAR_HEIGHT)) {
+                   if (qdflags[unit].inuse & GRAPHIC_DEV) {
+                       cursor[unit].y = 0;
+                   } else {
+                       cursor[unit].y -= CHAR_HEIGHT;
+                       scroll_up(adder);
+                   }
+               }
+               dga->y_cursor = TRANY(cursor[unit].y);
+               return(0);
+
+           case '\b':                  /* backspace char */
+               if (cursor[unit].x > 0) {
+                   cursor[unit].x -= CHAR_WIDTH;
+                   blitc(unit, ' ');
+                   cursor[unit].x -= CHAR_WIDTH;
+                   dga->x_cursor = TRANX(cursor[unit].x);
+               }
+               return(0);
+
+           default:
+               if (chr < ' ' || chr > '~')
+                   return(0);
+       }
+
+/*------------------------------------------
+* 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);
+
+/*----------------------------------------
+* 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;
+
+       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  */
+
+       adder->source_1_x = FONT_X + ((chr - ' ') * CHAR_WIDTH);
+       adder->source_1_y = FONT_Y;
+
+       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;
+       dga->x_cursor = TRANX(cursor[unit].x);
+
+       if (cursor[unit].x > (1024 - CHAR_WIDTH)) {
+           blitc(unit, '\r');
+           blitc(unit, '\n');
+       }
+
+} /* blitc */
+
+qdreset(){}
+qd_init(){}
+
+/******************************************************************
+*******************************************************************
+*******************************************************************
+*
+*      INTERRUPT SERVICE ROUTINES START HERE:
+*
+*******************************************************************
+*******************************************************************
+******************************************************************/
+
+/*****************************************************************
+*
+*      qddint()... service "DMA DONE" interrupt condition
+*
+*****************************************************************/
+
+qddint(qd)
+int qd;
+{
+       register struct DMAreq_header *header;
+       register struct DMAreq *request;
+       register struct dga *dga;
+       struct adder *adder;
+
+       int cookie;                     /* DMA adrs for QDSS */
+       int i;
+
+       spl4();                         /* allow interval timer in */
+
+/*-----------------
+* init pointers */
+
+       header = DMAheader[qd];             /* register for optimization */
+       dga = (struct dga *) qdmap[qd].dga;
+       adder = (struct adder *) qdmap[qd].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)
+               mprintf("\nqd%d: qddint: DMA hardware parity fault.", qd);
+
+           if (dga->csr & BUS_ERR)
+               mprintf("\nqd%d: qddint: DMA hardware bus error.", qd);
+       }
+
+/*----------------------------------------
+* if this was a DMA from user space... */
+
+       if (qdflags[qd].user_dma) {
+           qdflags[qd].user_dma = 0;
+           wakeup((caddr_t)&qdflags[qd].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;
+
+           if (rsel[qd] && qdflags[qd].selmask & SEL_WRITE) {
+               selwakeup(rsel[qd], 0);
+               rsel[qd] = 0;
+               qdflags[qd].selmask &= ~SEL_WRITE;
+           }
+
+           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)) {
+
+           if (rsel[qd] && qdflags[qd].selmask & SEL_WRITE) {
+               selwakeup(rsel[qd], 0);
+               rsel[qd] = 0;
+               qdflags[qd].selmask &= ~SEL_WRITE;
+           }
+       }
+
+       header->DMAreq[header->oldest].DMAdone |= REQUEST_DONE;
+
+       if (DMA_ISEMPTY(header)) {
+           mprintf("\nqd%d: qddint: unexpected interrupt", qd);
+           return;
+       }
+
+       DMA_GETEND(header);     /* update request queue indices */
+
+/*------------------------------------------------------------
+* if no more DMA pending, wake up "select" client and exit */
+
+       if (DMA_ISEMPTY(header)) {
+
+           if (rsel[qd] && qdflags[qd].selmask & SEL_WRITE) {
+               selwakeup(rsel[qd], 0);
+               rsel[qd] = 0;
+               qdflags[qd].selmask &= ~SEL_WRITE;
+           }
+
+           DMA_CLRACTIVE(header);  /* flag DMA done */
+           return;
+       }
+
+/*---------------------------
+* initiate next DMA xfer  */
+
+       request = DMA_GETBEGIN(header);
+
+       switch (request->DMAtype) {
+
+           case DISPLIST:
+               dga->csr |= DL_ENB;
+               break;
+
+           case PTOB:
+               dga->csr |= PTOB_ENB;
+               break;
+
+           case BTOP:
+               dga->csr |= BTOP_ENB;
+               break;
+
+           default:
+               mprintf("\nqd%d: qddint: illegal DMAtype parameter.", qd);
+               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;
+
+       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;
+}
+
+/*****************************************************************
+*
+*      qdaint()... ADDER interrupt service
+*
+*****************************************************************/
+
+qdaint(qd)
+register int qd;
+{
+       register struct adder *adder;
+       struct color_buf *cbuf;
+
+       short stat;
+       int i;
+       register struct rgb *rgbp;
+       register short *red;
+       register short *green;
+       register short *blue;
+
+       spl4();                         /* allow interval timer in */
+
+       adder = (struct adder *) qdmap[qd].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[qd];
+           if (cbuf->status & LOAD_COLOR_MAP) {
+
+               red = (short *) qdmap[qd].red;
+               green = (short *) qdmap[qd].green;
+               blue = (short *) qdmap[qd].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[qd]->status & LOAD_REGS) {
+
+               for ( i = 1000, adder->status = 0
+                   ; i > 0  &&  !((stat = adder->status) & ID_SCROLL_READY)
+                   ; --i);
+
+               if (i == 0) {
+                   mprintf("\nqd%d: qdaint: timeout on ID_SCROLL_READY", qd);
+                   return;
+               }
+
+               adder->ID_scroll_data = scroll[qd]->viper_constant;
+               adder->ID_scroll_command = ID_LOAD | SCROLL_CONSTANT;
+
+               adder->y_scroll_constant = scroll[qd]->y_scroll_constant;
+               adder->y_offset_pending = scroll[qd]->y_offset;
+
+               if (scroll[qd]->status & LOAD_INDEX) {
+
+                   adder->x_index_pending = scroll[qd]->x_index_pending;
+                   adder->y_index_pending = scroll[qd]->y_index_pending;
+               }
+
+           scroll[qd]->status = 0x00;
+           }
+       }
+}
+
+/*****************************************************************
+*
+*      qdiint()... DUART input interrupt service routine
+*
+*****************************************************************/
+
+qdiint(qd)
+register int qd;
+{
+       register struct _vs_event *event;
+       register struct qdinput *eqh;
+
+       struct dga *dga;
+       struct duart *duart;
+       struct mouse_report *new_rep;
+
+       struct uba_device *ui;
+       struct tty *tp;
+
+       char chr;
+       int i,j;
+       int k,l;
+
+       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 */
+
+       spl4();                         /* allow interval timer in */
+
+       eqh = eq_header[qd];            /* optimized as a register */
+       new_rep = &current_rep[qd];
+       duart = (struct duart *) qdmap[qd].duart;
+
+/*-----------------------------------------
+* if the graphic device is turned on.. */
+
+       if (qdflags[qd].inuse & GRAPHIC_DEV) {
+
+           /*---------------
+           * empty DUART */
+
+           while ((status = duart->statusA) & RCV_RDY  ||
+                  (status = duart->statusB) & RCV_RDY) {
+
+               /*---------------------------------
+               * pick up LK-201 input (if any) */
+
+               if ((status = duart->statusA) & RCV_RDY) {
+
+                   /* if error condition, then reset it */
+
+                   if ((status = duart->statusA) & 0x70) {
+                       duart->cmdA = 0x40;
+                       continue;
+                   }
+
+                   /* event queue full now? (overflow condition) */
+
+                   if (ISFULL(eqh) == TRUE) {
+                       mprintf("\nqd%d: qdiint: event queue overflow", 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) {
+                       mprintf("\nqd%d: qdiint: keyboard error, code = %x",qd,key);
+                       return(0);
+                   }
+
+                   if (key < LK_LOWEST)
+                       return(0);
+
+                   ++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[qd].pntr_id == MOUSE_ID) {
+
+                   if (status & 0x70) {
+                       duart->cmdB = 0x40;
+                       continue;
+                   }
+
+                   /* event queue full now? (overflow condition) */
+
+                   if (ISFULL(eqh) == TRUE) {
+                       mprintf("\nqd%d: qdiint: event queue overflow", 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 & 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[qd].curs_acc > ACC_OFF) {
+
+                               if (qdflags[qd].curs_thr <= new_rep->dx)
+                                   new_rep->dx +=
+                                       (new_rep->dx - qdflags[qd].curs_thr)
+                                        * qdflags[qd].curs_acc;
+
+                               if (qdflags[qd].curs_thr <= new_rep->dy)
+                                   new_rep->dy +=
+                                       (new_rep->dy - qdflags[qd].curs_thr)
+                                        * qdflags[qd].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[qd].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[qd].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) {
+                                   mprintf("\nqd%d: qdiint: event queue overflow", 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[qd] = current_rep[qd];
+
+                   }  /* get last byte of report */
+               } /* pickup mouse input */
+
+               /*--------------------------------
+               * pickup tablet input, if any  */
+
+               else if ((status = duart->statusB) & RCV_RDY  &&
+                        qdflags[qd].pntr_id == TABLET_ID) {
+
+                   if (status & 0x70) {
+                       duart->cmdB = 0x40;
+                       continue;
+                   }
+
+                   /* event queue full now? (overflow condition) */
+
+                   if (ISFULL(eqh) == TRUE) {
+                       mprintf("\nqd%d: qdiint: event queue overflow", 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[qd].tab_res;
+                       new_rep->dy = (2200 - new_rep->dy)
+                                     / qdflags[qd].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[qd].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[qd].state & 0x1E;
+
+                       if (a ^ b) {
+
+                           /* event queue full now? (overflow condition) */
+
+                           if (ISFULL(eqh) == TRUE) {
+                               mprintf("\nqd%d: qdiint: event queue overflow",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[qd] = current_rep[qd];
+
+                   } /* get last byte of report */
+               } /* pick up tablet input */
+
+           } /* while input available.. */
+
+           /*---------------------
+           * do select wakeup  */
+
+           if (rsel[qd] && do_wakeup && qdflags[qd].selmask & SEL_READ) {
+               selwakeup(rsel[qd], 0);
+               rsel[qd] = 0;
+               qdflags[qd].selmask &= ~SEL_READ;
+               do_wakeup = 0;
+           }
+       }
+
+/*-----------------------------------------------------------------
+* if the graphic device is not turned on, this is console input */
+
+       else {
+
+           ui = qdinfo[qd];
+           if (ui == 0 || ui->ui_alive == 0)
+               return(0);
+
+           tp = &qd_tty[qd << 2];
+
+           /*--------------------------------------
+           * Get a character from the keyboard. */
+
+           while ((status = 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) {
+                       mprintf("\nqd%d: qdiint: Keyboard error, code = %x",qd,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(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( 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 & 0x80) {
+                       char *string;
+                       string = q_special[chr & 0x7F];
+                       while(*string)
+                           (*linesw[tp->t_line].l_rint)(*string++, tp);
+               }
+               else {
+                       (*linesw[tp->t_line].l_rint)(chr, tp);
+               }
+           }
+       }
+
+/*----------------------
+* cleanup and exit  */
+
+       return(0);
+
+} /* qdiint */
+
+/******************************************************************
+*******************************************************************
+*******************************************************************
+*
+*      THE SUBROUTINES START HERE:
+*
+******************************************************************/
+
+/*****************************************************************
+*
+*      clear_qd_screen()... 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
+*
+****************/
+
+clear_qd_screen(unit)
+int unit;
+{
+       register struct adder *adder;
+       adder = (struct adder *) qdmap[unit].adder;
+
+       adder->x_limit = 1024;
+       adder->y_limit = 2048 - CHAR_HEIGHT;
+       adder->y_offset_pending = 0;
+
+       wait_status(adder, VSYNC);      /* wait at LEAST 1 full frame */
+       wait_status(adder, VSYNC);
+
+       adder->y_scroll_constant = SCROLL_ERASE;
+
+       wait_status(adder, VSYNC);
+       wait_status(adder, VSYNC);
+
+       adder->y_offset_pending = 864;
+
+       wait_status(adder, VSYNC);
+       wait_status(adder, VSYNC);
+
+       adder->y_scroll_constant = SCROLL_ERASE;
+
+       wait_status(adder, VSYNC);
+       wait_status(adder, VSYNC);
+
+       adder->y_offset_pending = 1728;
+
+       wait_status(adder, VSYNC);
+       wait_status(adder, VSYNC);
+
+       adder->y_scroll_constant = SCROLL_ERASE;
+
+       wait_status(adder, VSYNC);
+       wait_status(adder, VSYNC);
+
+       adder->y_offset_pending = 0;     /* back to normal */
+
+       wait_status(adder, VSYNC);
+       wait_status(adder, VSYNC);
+
+       adder->x_limit = MAX_SCREEN_X;
+       adder->y_limit = MAX_SCREEN_Y + FONT_HEIGHT;
+
+} /* clear_qd_screen */
+
+/**********************************************************************
+*
+*      qdputc()... route kernel console output to display destination
+*
+***********************************************************************
+*
+*      calling convention:
+*
+*              qdputc(chr);
+*
+*      where:  char chr;        ;character for output
+*
+****************/
+
+qdputc(chr)
+register char chr;
+{
+       register struct tty *tp0;
+
+/*---------------------------------------------------------
+* if system is now physical, forget it (ie: crash DUMP) */
+
+       if ( (mfpr(MAPEN) & 1) == 0 )
+           return;
+
+/*--------------------------------------------------
+* direct kernel output char to the proper place  */
+
+       tp0 = &qd_tty[1];
+
+       if (qdflags[0].kernel_loop != 0  &&  tp0->t_state & TS_ISOPEN) {
+           (*linesw[tp0->t_line].l_rint)(chr, tp0);
+       } else {
+           blitc(0, chr & 0xff);
+       }
+
+} /* qdputc */
+
+/*******************************************************************
+*
+*      qdgetc()... get a character from the LK201
+*
+*******************************************************************
+*
+*      calling convention:
+*
+*              qdgetc();
+*
+*      returns:  the character read.
+*
+****************/
+
+qdgetc()
+{
+       register short key;
+       register char chr;
+       register struct duart *duart;
+
+       u_int status;
+
+       duart = (struct duart *) qdmap[0].duart;
+
+       /*--------------------------------------
+       * Get a character from the keyboard. */
+
+LOOP:
+       while (!((status = 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(LK_LED_ENABLE, LK_LED_LOCK);
+               else
+                   led_control(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 */
+
+/**********************************************************************
+*
+*      ldcursor()... load the mouse cursor's template RAM bitmap
+*
+*********************************************************************
+*
+*      calling convention:
+*
+*              ldcursor(unit, bitmap);
+*              u_int unit;
+*              short *bitmap;
+*
+****************/
+
+ldcursor(unit, bitmap)
+u_int unit;
+short *bitmap;
+{
+       register struct dga *dga;
+       register short *temp;
+       register int i;
+
+       int cursor;
+
+       dga = (struct dga *) qdmap[unit].dga;
+       temp = (short *) qdmap[unit].template;
+
+       if (dga->csr & CURS_ENB) {      /* if the cursor is enabled.. */
+           cursor = -1;                /* ..note that.. */
+           dga->csr &= ~CURS_ENB;      /* ..and shut it off */
+       }
+       else {
+           cursor = 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 (cursor) {                   /* if cursor was enabled.. */
+           dga->csr |= CURS_ENB;       /* ..turn it back on */
+       }
+
+       return(0);
+
+} /* ldcursor */
+
+/**********************************************************************
+*
+*      ldfont()... put the console font in the QDSS off-screen memory
+*
+***********************************************************************
+*
+*      calling convention:
+*
+*              ldfont(unit);
+*              u_int unit;     ;QDSS unit number
+*
+****************/
+
+ldfont(unit)
+u_int unit;
+{
+       register struct adder *adder;
+
+       int i;          /* scratch variables */
+       int j;
+       int k;
+       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  */
+
+       wait_status(adder, RASTEROP_COMPLETE);
+
+       adder->destination_x = FONT_X;
+       adder->destination_y = FONT_Y;
+       adder->fast_dest_dx = FONT_WIDTH;
+       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 < 48; ++j) {
+
+               /* PTOB one scan of a char cell */
+
+               packed = q_font[k];
+               k += ROWS;
+               packed |= ((short)q_font[k] << 8);
+               k += ROWS;
+
+               wait_status(adder, TX_READY);
+               adder->id_data = packed;
+           }
+       }
+
+}  /* ldfont */
+
+/*********************************************************************
+*
+*      led_control()... twiddle LK-201 LED's
+*
+**********************************************************************
+*
+*      led_control(unit, cmd, led_mask);
+*      u_int unit;     QDSS number
+*      int cmd;        LED enable/disable command
+*      int led_mask;   which LED(s) to twiddle
+*
+*************/
+
+led_control(unit, cmd, led_mask)
+u_int unit;
+int cmd;
+int led_mask;
+{
+       register int i;
+       register int status;
+       register struct duart *duart;
+
+       duart = (struct duart *) qdmap[unit].duart;
+
+       for (i = 1000; i > 0; --i) {
+           if ((status = duart->statusA) & XMT_RDY) {
+               duart->dataA = cmd;
+               break;
+           }
+       }
+
+       for (i = 1000; i > 0; --i) {
+           if ((status = duart->statusA) & XMT_RDY) {
+               duart->dataA = led_mask;
+               break;
+           }
+       }
+
+       if (i == 0)
+           return(BAD);
+
+       return(GOOD);
+
+} /* led_control */
+
+/*******************************************************************
+*
+*      scroll_up()... move the screen up one character height
+*
+********************************************************************
+*
+*      calling convention:
+*
+*              scroll_up(adder);
+*              struct adder *adder;    ;address of adder
+*
+********/
+
+scroll_up(adder)
+register struct adder *adder;
+{
+
+/*------------------------------------------
+* setup VIPER operand control registers  */
+
+       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) ;
+       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()... init shared memory pointers and structures
+*
+*********************************************************************
+*
+*      calling convention:
+*
+*              init_shared(unit);
+*              u_int unit;
+*
+****************/
+
+init_shared(unit)
+register u_int unit;
+{
+       register 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 */
+
+/*********************************************************************
+*
+*      setup_dragon()... init the ADDER, VIPER, bitmaps, & color map
+*
+**********************************************************************
+*
+*      calling convention:
+*
+*              setup_dragon();
+*
+*      return: NONE
+*
+************************/
+
+setup_dragon(unit)
+u_int unit;
+{
+
+       register struct adder *adder;
+       register struct dga *dga;
+       short *memcsr;
+
+       int i;                  /* general purpose variables */
+       int status;
+
+       short top;              /* clipping/scrolling boundaries */
+       short bottom;
+       short right;
+       short left;
+
+       short *red;             /* color map pointers */
+       short *green;
+       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;
+
+           wait_status(adder, VSYNC);  /* delay at least 1 full frame time */
+           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  &&  !((status = adder->status) & ADDRESS_COMPLETE)
+           ; --i);
+
+       if (i == 0)
+           mprintf("\nqd%d: setup_dragon: timeout on ADDRESS_COMPLETE",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;
+
+       wait_status(adder, VSYNC);      /* wait at LEAST 1 full frame */
+       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  &&  !((status = adder->status) & ADDRESS_COMPLETE)
+           ; --i);
+
+       if (i == 0)
+           mprintf("\nqd%d: setup_dragon: timeout on ADDRESS_COMPLETE",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) */
+
+       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  &&  !((status = adder->status) & VSYNC)
+           ; ++i);
+
+       if (i == 0)
+           mprintf("\nqd%d: setup_dragon: timeout on VSYNC", 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;
+
+       return(0);
+
+} /* setup_dragon */
+
+/******************************************************************
+*
+*      setup_input()... init the DUART and set defaults in input
+*                       devices
+*
+*******************************************************************
+*
+*      calling convention:
+*
+*              setup_input(unit);
+*
+*      where: unit - is the QDSS unit number to be setup
+*
+*********/
+
+setup_input(unit)
+u_int unit;
+{
+       register struct duart *duart;   /* DUART register structure pointer */
+       register int i;                 /* scratch variable */
+       register int bits;
+
+       char id_byte;
+       short status;
+
+/*---------------
+* init stuff */
+
+       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 ((status = duart->statusA) & XMT_RDY) {
+               duart->dataA = LK_DEFAULTS;
+               break;
+           }
+       }
+
+       for (i = 100000; i > 0; --i) {
+           if ((status = duart->statusA) & RCV_RDY) {
+               break;
+           }
+       }
+
+       status = duart->dataA;          /* flush the ACK */
+
+/*--------------------------------
+* identify the pointing device */
+
+       for (i = 500; i > 0; --i) {
+           if ((status = duart->statusB) & XMT_RDY) {
+               duart->dataB = SELF_TEST;
+               break;
+           }
+       }
+
+       /*-----------------------------------------
+       * wait for 1st byte of self test report */
+
+       for (i = 100000; i > 0; --i) {
+           if ((status = duart->statusB) & RCV_RDY) {
+               break;
+           }
+       }
+
+       if (i == 0) {
+           mprintf("\nqd[%d]: setup_input: timeout on 1st byte of self test",unit);
+           goto OUT;
+       }
+
+       status = duart->dataB;
+
+       /*-----------------------------------------
+       * wait for ID byte of self test report  */
+
+       for (i = 100000; i > 0; --i) {
+           if ((status = duart->statusB) & RCV_RDY) {
+               break;
+           }
+       }
+
+       if (i == 0) {
+           mprintf("\nqd[%d]: setup_input: timeout on 2nd byte of self test", unit);
+           goto OUT;
+       }
+
+       id_byte = duart->dataB;
+
+       /*------------------------------------
+       * wait for other bytes to come in  */
+
+       for (i = 100000; i > 0; --i) {
+           if ((status = duart->statusB) & RCV_RDY) {
+               status = duart->dataB;
+               break;
+           }
+       }
+
+       if (i == 0) {
+           mprintf("\nqd[%d]: setup_input: timeout on 3rd byte of self test", unit);
+           goto OUT;
+       }
+
+       for (i = 100000; i > 0; --i) {
+           if ((status = duart->statusB) & RCV_RDY) {
+               status = duart->dataB;
+               break;
+           }
+       }
+
+       if (i == 0) {
+           mprintf("\nqd[%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);
+
+       if ((id_byte & 0x0F) != TABLET_ID) {
+
+           qdflags[unit].pntr_id = MOUSE_ID;
+
+           for (i = 500; i > 0; --i) {
+               if ((status = duart->statusB) & XMT_RDY) {
+                   duart->dataB = INC_STREAM_MODE;
+                   break;
+               }
+           }
+       } else {
+
+           qdflags[unit].pntr_id = TABLET_ID;
+
+           for (i = 500; i > 0; --i) {
+               if ((status = duart->statusB) & XMT_RDY) {
+                   duart->dataB = T_STREAM;
+                   break;
+               }
+           }
+       }
+
+/*--------
+* exit */
+
+OUT:
+       duart->imask = qdflags[unit].duart_imask;
+       return(0);
+
+} /* setup_input */
+
+/**********************************************************************
+*
+*      wait_status()... delay for at least one display frame time
+*
+***********************************************************************
+*
+*      calling convention:
+*
+*              wait_status(adder, mask);
+*              struct *adder adder;
+*              int mask;
+*
+*      return: BAD means that we timed out without ever seeing the
+*                    vertical sync status bit
+*              GOOD otherwise
+*
+**************/
+
+wait_status(adder, mask)
+register struct adder *adder;
+register int mask;
+{
+       register short status;
+       int i;
+
+       for ( i = 10000, adder->status = 0
+           ; i > 0  &&  !((status = adder->status) & mask)
+           ; --i);
+
+       if (i == 0) {
+           mprintf("\nwait_status: timeout polling for 0x%x in adder->status", mask);
+           return(BAD);
+       }
+
+       return(GOOD);
+
+} /* wait_status */
+
+/**********************************************************************
+*
+*      write_ID()... write out onto the ID bus
+*
+***********************************************************************
+*
+*      calling convention:
+*
+*              struct *adder adder;    ;pntr to ADDER structure
+*              short adrs;             ;VIPER address
+*              short data;             ;data to be written
+*              write_ID(adder);
+*
+*      return: BAD means that we timed out waiting for status bits
+*                    VIPER-access-specific status bits
+*              GOOD otherwise
+*
+**************/
+
+write_ID(adder, adrs, data)
+register struct adder *adder;
+register short adrs;
+register short data;
+{
+       int i;
+       short status;
+
+       for ( i = 100000, adder->status = 0
+           ; i > 0  &&  !((status = adder->status) & ADDRESS_COMPLETE)
+           ; --i);
+
+       if (i == 0)
+           goto ERR;
+
+       for ( i = 100000, adder->status = 0
+           ; i > 0  &&  !((status = adder->status) & TX_READY)
+           ; --i);
+
+       if (i > 0) {
+           adder->id_data = data;
+           adder->command = ID_LOAD | adrs;
+           return(GOOD);
+       }
+
+ERR:
+       mprintf("\nwrite_ID: timeout trying to write to VIPER");
+       return(BAD);
+
+} /* write_ID */