Home | History | Annotate | Line # | Download | only in vsa
      1 /*	$NetBSD: gpx.c,v 1.4 2024/09/30 00:34:04 rin Exp $ */
      2 /*	$OpenBSD: gpx.c,v 1.25 2014/12/23 21:39:12 miod Exp $	*/
      3 /*
      4  * Copyright (c) 2006 Miodrag Vallat.
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice, this permission notice, and the disclaimer below
      9  * appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 /*-
     20  * Copyright (c) 1988 Regents of the University of California.
     21  * All rights reserved.
     22  *
     23  * Redistribution and use in source and binary forms, with or without
     24  * modification, are permitted provided that the following conditions
     25  * are met:
     26  * 1. Redistributions of source code must retain the above copyright
     27  *    notice, this list of conditions and the following disclaimer.
     28  * 2. Redistributions in binary form must reproduce the above copyright
     29  *    notice, this list of conditions and the following disclaimer in the
     30  *    documentation and/or other materials provided with the distribution.
     31  * 3. Neither the name of the University nor the names of its contributors
     32  *    may be used to endorse or promote products derived from this software
     33  *    without specific prior written permission.
     34  *
     35  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     36  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     37  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     38  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     39  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     40  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     41  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     42  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     43  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     44  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     45  * SUCH DAMAGE.
     46  *
     47  *	@(#)qd.c	7.1 (Berkeley) 6/28/91
     48  */
     49 
     50 /************************************************************************
     51 *									*
     52 *			Copyright (c) 1985-1988 by			*
     53 *		Digital Equipment Corporation, Maynard, MA		*
     54 *			All rights reserved.				*
     55 *									*
     56 *   This software is furnished under a license and may be used and	*
     57 *   copied  only  in accordance with the terms of such license and	*
     58 *   with the  inclusion  of  the  above  copyright  notice.   This	*
     59 *   software  or  any  other copies thereof may not be provided or	*
     60 *   otherwise made available to any other person.  No title to and	*
     61 *   ownership of the software is hereby transferred.			*
     62 *									*
     63 *   The information in this software is subject to change  without	*
     64 *   notice  and should not be construed as a commitment by Digital	*
     65 *   Equipment Corporation.						*
     66 *									*
     67 *   Digital assumes no responsibility for the use  or  reliability	*
     68 *   of its software on equipment which is not supplied by Digital.	*
     69 *									*
     70 *************************************************************************/
     71 
     72 /*
     73  * Driver for the GPX color option on VAXstation 3100, based on the
     74  * MicroVAX II qdss driver.
     75  *
     76  * The frame buffer memory itself is not directly accessible (unlike
     77  * the on-board monochrome smg frame buffer), and writes through the
     78  * Dragon chip can only happen in multiples of 16 pixels, horizontally.
     79  *
     80  * Because of this limitation, the font image is copied to offscreen
     81  * memory (which there is plenty of), and screen to screen blt operations
     82  * are done for everything.
     83  */
     84 
     85 #include "dzkbd.h"
     86 #include "wsdisplay.h"
     87 
     88 #include <sys/param.h>
     89 #include <sys/device.h>
     90 #include <sys/systm.h>
     91 #include <sys/kmem.h>
     92 #include <sys/conf.h>
     93 
     94 #include <machine/sid.h>
     95 #include <machine/cpu.h>
     96 #include <machine/ka420.h>
     97 #include <machine/scb.h>
     98 #include <machine/vsbus.h>
     99 #include <machine/qdreg.h>
    100 
    101 #include <dev/cons.h>
    102 
    103 #include <dev/dec/dzreg.h>
    104 #include <dev/dec/dzvar.h>
    105 #include <dev/dec/dzkbdvar.h>
    106 
    107 #include <dev/wscons/wsconsio.h>
    108 #include <dev/wscons/wscons_callbacks.h>
    109 #include <dev/wscons/wsdisplayvar.h>
    110 #include <dev/rasops/rasops.h>
    111 #include <dev/wsfont/wsfont.h>
    112 
    113 #if 0
    114 #include <dev/ic/bt458reg.h>
    115 #include <dev/ic/dc503reg.h>
    116 #endif
    117 
    118 #define	GPXADDR		0x3c000000	/* base address on VAXstation 3100 */
    119 
    120 #define	GPX_ADDER_OFFSET	0x0000
    121 #define	GPX_VDAC_OFFSET		0x0300
    122 #define	GPX_CURSOR_OFFSET	0x0400	/* DC503 */
    123 #define	GPX_READBACK_OFFSET	0x0500
    124 
    125 #define	GPX_WIDTH	1024
    126 #define	GPX_VISHEIGHT	864
    127 #define	GPX_HEIGHT	2048
    128 
    129 /* XXX these should be in <dev/ic/bt458reg.h> */
    130 /*
    131  * Brooktree Bt451, Bt457, Bt458 register definitions
    132  */
    133 #define BT_OV0	0x00		/* overlay 0 */
    134 #define BT_OV1	0x01		/* overlay 1 */
    135 #define BT_OV2	0x02		/* overlay 2 */
    136 #define BT_OV3	0x03		/* overlay 3 */
    137 #define BT_RMR	0x04		/* read mask */
    138 #define BT_BMR	0x05		/* blink mask */
    139 #define BT_CR	0x06		/* control */
    140 #define BT_CTR	0x07		/* control/test */
    141 
    142 #define BTCR_MPLX_5		0x80	/* multiplex select, 5:1 */
    143 #define BTCR_MPLX_4		0x00	/* multiplex select, 4:1 */
    144 #define BTCR_RAMENA		0x40	/* use color palette RAM */
    145 #define BTCR_BLINK_M		0x30	/* blink mask */
    146 #define BTCR_BLINK_1648		0x00	/*  16 on, 48 off */
    147 #define BTCR_BLINK_1616		0x10	/*  16 on, 16 off */
    148 #define BTCR_BLINK_3232		0x20	/*  32 on, 32 off */
    149 #define BTCR_BLINK_6464		0x30	/*  64 on, 64 off */
    150 #define BTCR_BLINKENA_OV1	0x08	/* OV1 blink enable */
    151 #define BTCR_BLINKENA_OV0	0x04	/* OV0 blink enable */
    152 #define BTCR_DISPENA_OV1	0x02	/* OV1 display enable */
    153 #define BTCR_DISPENA_OV0	0x01	/* OV0 display enable */
    154 
    155 #define BTCTR_R_ENA		0x01	/* red channel enable */
    156 #define BTCTR_G_ENA		0x02	/* green channel enable */
    157 #define BTCTR_B_ENA		0x04	/* blue channel enable */
    158 #define BTCTR_NIB_M		0x08	/* nibble mask: */
    159 #define BTCTR_NIB_LOW		0x08	/*  low */
    160 #define BTCTR_NIB_HIGH		0x00	/*  high */
    161 
    162 /* 4 plane option RAMDAC */
    163 struct	ramdac4 {
    164 	uint16_t	colormap[16];
    165 	uint8_t		unknown[0x20];
    166 	uint16_t	cursormap[4];
    167 	uint8_t		unknown2[0x18];
    168 	uint16_t	control;
    169 #define	RAMDAC4_INIT	0x0047
    170 #define	RAMDAC4_ENABLE	0x0002
    171 };
    172 
    173 /* 8 plane option RAMDAC - Bt458 or compatible */
    174 struct	ramdac8 {
    175 	uint16_t	address;
    176 	uint16_t	cmapdata;
    177 	uint16_t	control;
    178 	uint16_t	omapdata;
    179 };
    180 
    181 struct	gpx_screen {
    182 	struct rasops_info ss_ri;
    183 	int		ss_console;
    184 	u_int		ss_depth;
    185 	u_int		ss_gpr;		/* font glyphs per row */
    186 	struct adder	*ss_adder;
    187 	void		*ss_vdac;
    188 	uint8_t		ss_cmap[256 * 3];
    189 #if 0
    190 	struct dc503reg	*ss_cursor;
    191 	uint16_t	ss_curcmd;
    192 #endif
    193 };
    194 
    195 struct	gpx_softc {
    196 	device_t sc_dev;
    197 	struct gpx_screen *sc_scr;
    198 	int	sc_nscreens;
    199 };
    200 
    201 static int gpx_match(device_t, cfdata_t, void *);
    202 static void gpx_attach(device_t, device_t, void *);
    203 
    204 static int gpx_ioctl(void *, void *, u_long, void *, int, struct lwp *);
    205 static paddr_t gpx_mmap(void *, void *, off_t, int);
    206 static int gpx_alloc_screen(void *, const struct wsscreen_descr *,
    207     void **, int *, int *, long *);
    208 static void gpx_free_screen(void *, void *);
    209 static int gpx_show_screen(void *, void *, int,
    210     void (*) (void *, int, int), void *);
    211 
    212 static void gpx_putchar(void *, int, int, u_int, long);
    213 static void gpx_copycols(void *, int, int, int, int);
    214 static void gpx_erasecols(void *, int, int, int, long);
    215 static void gpx_copyrows(void *, int, int, int);
    216 static void gpx_eraserows(void *, int, int, long);
    217 static void gpx_do_cursor(struct rasops_info *);
    218 
    219 static int gpx_wait(struct gpx_screen *, int);
    220 static int gpx_viper_write(struct gpx_screen *, u_int, uint16_t);
    221 static void gpx_reset_viper(struct gpx_screen *);
    222 static void gpx_clear_screen(struct gpx_screen *);
    223 static int gpx_setup_screen(struct gpx_screen *);
    224 static void gpx_upload_font(struct gpx_screen *);
    225 static void gpx_copyrect(struct gpx_screen *, int, int, int, int, int, int);
    226 static void gpx_fillrect(struct gpx_screen *, int, int, int, int, long, u_int);
    227 static int gpx_getcmap(struct gpx_screen *, struct wsdisplay_cmap *);
    228 static int gpx_putcmap(struct gpx_screen *, struct wsdisplay_cmap *);
    229 static void gpx_loadcmap(struct gpx_screen *, int, int);
    230 static void gpx_resetcmap(struct gpx_screen *);
    231 
    232 /* for console */
    233 static struct gpx_screen gpx_consscr;
    234 
    235 CFATTACH_DECL_NEW(gpx, sizeof(struct gpx_softc),
    236     gpx_match, gpx_attach, NULL, NULL);
    237 
    238 static struct wsscreen_descr gpx_stdscreen = {
    239 	"std",
    240 };
    241 
    242 static const struct wsscreen_descr *_gpx_scrlist[] = {
    243 	&gpx_stdscreen,
    244 };
    245 
    246 static const struct wsscreen_list gpx_screenlist = {
    247 	sizeof(_gpx_scrlist) / sizeof(struct wsscreen_descr *),
    248 	_gpx_scrlist,
    249 };
    250 
    251 static const struct wsdisplay_accessops gpx_accessops = {
    252 	.ioctl = gpx_ioctl,
    253 	.mmap = gpx_mmap,
    254 	.alloc_screen = gpx_alloc_screen,
    255 	.free_screen = gpx_free_screen,
    256 	.show_screen = gpx_show_screen,
    257 	.load_font = NULL
    258 };
    259 
    260 /*
    261  * Autoconf glue
    262  */
    263 
    264 static int
    265 gpx_match(device_t parent, cfdata_t match, void *aux)
    266 {
    267 	struct vsbus_attach_args *va = aux;
    268 	volatile struct adder *adder;
    269 	vaddr_t tmp;
    270 	u_int depth;
    271 	u_short status;
    272 
    273 	switch (vax_boardtype) {
    274 	default:
    275 		return 0;
    276 
    277 	case VAX_BTYP_410:
    278 	case VAX_BTYP_420:
    279 	case VAX_BTYP_43:
    280 		if (va->va_paddr != GPXADDR)
    281 			return 0;
    282 
    283 		/* not present on microvaxes */
    284 		if ((vax_confdata & KA420_CFG_MULTU) != 0)
    285 			return 0;
    286 
    287 		if ((vax_confdata & KA420_CFG_VIDOPT) == 0)
    288 			return 0;
    289 		break;
    290 	}
    291 
    292 	/* Check for hardware */
    293 	adder = (volatile struct adder *)
    294 	    vax_map_physmem(va->va_paddr + GPX_ADDER_OFFSET, 1);
    295 	if (adder == NULL)
    296 		return 0;
    297 	adder->status = 0;
    298 	status = adder->status;
    299 	vax_unmap_physmem((vaddr_t)adder, 1);
    300 	if (status == offsetof(struct adder, status))
    301 		return 0;
    302 
    303 	/* Check for a recognized color depth */
    304 	tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1);
    305 	if (tmp == 0L)
    306 		return 0;
    307 	depth = (*(volatile uint16_t *)tmp) & 0x00f0;
    308 	vax_unmap_physmem(tmp, 1);
    309 	if (depth != 0x00f0 && depth != 0x0080)
    310 		return 0;
    311 
    312 	/* when already running as console, always fake things */
    313 	if ((vax_confdata & KA420_CFG_L3CON) == 0
    314 #if NWSDISPLAY > 0
    315 	    && cn_tab->cn_putc == wsdisplay_cnputc
    316 #endif
    317 	) {
    318 		struct vsbus_softc *sc = device_private(parent);
    319 		sc->sc_mask = 0x08;
    320 		scb_fake(0x44, 0x15);
    321 	} else {
    322 		adder = (struct adder *)vax_map_physmem(va->va_paddr +
    323 		    GPX_ADDER_OFFSET, 1);
    324 		if (adder == NULL)
    325 			return 0;
    326 		adder->interrupt_enable = FRAME_SYNC;
    327 		DELAY(100000);	/* enough to get a retrace interrupt */
    328 		adder->interrupt_enable = 0;
    329 		vax_unmap_physmem((vaddr_t)adder, 1);
    330 	}
    331 	return 20;
    332 }
    333 
    334 static void
    335 gpx_attach(device_t parent, device_t self, void *aux)
    336 {
    337 	struct gpx_softc *sc = device_private(self);
    338 	struct vsbus_attach_args *va = aux;
    339 	struct gpx_screen *scr;
    340 	struct wsemuldisplaydev_attach_args aa;
    341 	int console;
    342 	vaddr_t tmp;
    343 
    344 	sc->sc_dev = self;
    345 	console =
    346 #if NWSDISPLAY > 0
    347 	    (vax_confdata & KA420_CFG_L3CON) == 0 &&
    348 	    cn_tab->cn_putc == wsdisplay_cnputc;
    349 #else
    350 	    (vax_confdata & KA420_CFG_L3CON) == 0;
    351 #endif
    352 	if (console) {
    353 		scr = &gpx_consscr;
    354 		sc->sc_nscreens = 1;
    355 	} else {
    356 		scr = kmem_zalloc(sizeof(*scr), KM_SLEEP);
    357 
    358 		tmp = vax_map_physmem(va->va_paddr + GPX_READBACK_OFFSET, 1);
    359 		if (tmp == 0L) {
    360 			printf(": can not probe depth\n");
    361 			goto bad1;
    362 		}
    363 		scr->ss_depth = (*(uint16_t *)tmp & 0x00f0) == 0x00f0 ? 4 : 8;
    364 		vax_unmap_physmem(tmp, 1);
    365 
    366 		scr->ss_adder = (struct adder *)vax_map_physmem(va->va_paddr +
    367 		    GPX_ADDER_OFFSET, 1);
    368 		if (scr->ss_adder == NULL) {
    369 			aprint_error(": can not map frame buffer registers\n");
    370 			goto bad1;
    371 		}
    372 
    373 		scr->ss_vdac = (void *)vax_map_physmem(va->va_paddr +
    374 		    GPX_VDAC_OFFSET, 1);
    375 		if (scr->ss_vdac == NULL) {
    376 			aprint_error(": can not map RAMDAC\n");
    377 			goto bad2;
    378 		}
    379 
    380 #if 0
    381 		scr->ss_cursor =
    382 		    (struct dc503reg *)vax_map_physmem(va->va_paddr +
    383 		    GPX_CURSOR_OFFSET, 1);
    384 		if (scr->ss_cursor == NULL) {
    385 			aprint_error(": can not map cursor chip\n");
    386 			goto bad3;
    387 		}
    388 #endif
    389 
    390 		if (gpx_setup_screen(scr) != 0) {
    391 			aprint_error(": initialization failed\n");
    392 			goto bad4;
    393 		}
    394 	}
    395 	sc->sc_scr = scr;
    396 
    397 	aprint_normal("\n");
    398 	aprint_normal_dev(self, "%dx%d %d plane color framebuffer\n",
    399 	    GPX_WIDTH, GPX_VISHEIGHT, scr->ss_depth);
    400 
    401 	aa.console = console;
    402 	aa.scrdata = &gpx_screenlist;
    403 	aa.accessops = &gpx_accessops;
    404 	aa.accesscookie = sc;
    405 
    406 	config_found(self, &aa, wsemuldisplaydevprint, CFARGS_NONE);
    407 
    408 	return;
    409 
    410  bad4:
    411 #if 0
    412 	vax_unmap_physmem((vaddr_t)scr->ss_cursor, 1);
    413  bad3:
    414 #endif
    415 	vax_unmap_physmem((vaddr_t)scr->ss_vdac, 1);
    416  bad2:
    417 	vax_unmap_physmem((vaddr_t)scr->ss_adder, 1);
    418  bad1:
    419 	kmem_free(scr, sizeof(*scr));
    420 }
    421 
    422 /*
    423  * wsdisplay accessops
    424  */
    425 
    426 static int
    427 gpx_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
    428 {
    429 	struct gpx_softc *sc = v;
    430 	struct gpx_screen *ss = sc->sc_scr;
    431 	struct wsdisplay_fbinfo *wdf;
    432 	struct wsdisplay_cmap *cm;
    433 	int error;
    434 
    435 	switch (cmd) {
    436 	case WSDISPLAYIO_GTYPE:
    437 		*(u_int *)data = WSDISPLAY_TYPE_GPX;
    438 		break;
    439 
    440 	case WSDISPLAYIO_GINFO:
    441 		wdf = (struct wsdisplay_fbinfo *)data;
    442 		wdf->height = ss->ss_ri.ri_height;
    443 		wdf->width = ss->ss_ri.ri_width;
    444 		wdf->depth = ss->ss_depth;
    445 		wdf->cmsize = 1 << ss->ss_depth;
    446 		break;
    447 
    448 	case WSDISPLAYIO_GETCMAP:
    449 		cm = (struct wsdisplay_cmap *)data;
    450 		error = gpx_getcmap(ss, cm);
    451 		if (error != 0)
    452 			return error;
    453 		break;
    454 	case WSDISPLAYIO_PUTCMAP:
    455 		cm = (struct wsdisplay_cmap *)data;
    456 		error = gpx_putcmap(ss, cm);
    457 		if (error != 0)
    458 			return error;
    459 		gpx_loadcmap(ss, cm->index, cm->count);
    460 		break;
    461 
    462 	case WSDISPLAYIO_GVIDEO:
    463 	case WSDISPLAYIO_SVIDEO:
    464 		break;
    465 
    466 	case WSDISPLAYIO_LINEBYTES:	/* no linear mapping */
    467 		return -1;
    468 
    469 	default:
    470 		return EPASSTHROUGH;
    471 	}
    472 	return 0;
    473 }
    474 
    475 static paddr_t
    476 gpx_mmap(void *v, void *vs, off_t offset, int prot)
    477 {
    478 
    479 	return -1;
    480 }
    481 
    482 static int
    483 gpx_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
    484     int *curxp, int *curyp, long *defattrp)
    485 {
    486 	struct gpx_softc *sc = v;
    487 	struct gpx_screen *ss = sc->sc_scr;
    488 	struct rasops_info *ri = &ss->ss_ri;
    489 
    490 	if (sc->sc_nscreens > 0)
    491 		return ENOMEM;
    492 
    493 	*cookiep = ri;
    494 	*curxp = *curyp = 0;
    495 	ri->ri_ops.allocattr(ri, 0, 0, 0, defattrp);
    496 	sc->sc_nscreens++;
    497 
    498 	return 0;
    499 }
    500 
    501 static void
    502 gpx_free_screen(void *v, void *cookie)
    503 {
    504 	struct gpx_softc *sc = v;
    505 
    506 	sc->sc_nscreens--;
    507 }
    508 
    509 static int
    510 gpx_show_screen(void *v, void *cookie, int waitok,
    511     void (*cb)(void *, int, int), void *cbarg)
    512 {
    513 
    514 	return 0;
    515 }
    516 
    517 /*
    518  * wsdisplay emulops
    519  */
    520 
    521 static void
    522 gpx_putchar(void *v, int row, int col, u_int uc, long attr)
    523 {
    524 	struct rasops_info *ri = v;
    525 	struct gpx_screen *ss = ri->ri_hw;
    526 	struct wsdisplay_font *font = ri->ri_font;
    527 	int dx, dy, sx, sy, fg, bg, ul;
    528 
    529 	rasops_unpack_attr(attr, &fg, &bg, &ul);
    530 
    531 	/* find where to output the glyph... */
    532 	dx = col * font->fontwidth + ri->ri_xorigin;
    533 	dy = row * font->fontheight + ri->ri_yorigin;
    534 	/* ... and where to pick it from */
    535 	uc -= font->firstchar;
    536 	sx = (uc % ss->ss_gpr) * font->stride * NBBY;
    537 	sy = GPX_HEIGHT - (1 + uc / ss->ss_gpr) * font->fontheight;
    538 
    539 	/* setup VIPER operand control registers */
    540 	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
    541 		continue;
    542 	gpx_viper_write(ss, SRC1_OCR_B,
    543 	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
    544 	gpx_viper_write(ss, DST_OCR_B,
    545 	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
    546 	gpx_viper_write(ss, MASK_1, 0xffff);
    547 	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg);
    548 	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg);
    549 	ss->ss_adder->x_clip_min = 0;
    550 	ss->ss_adder->x_clip_max = GPX_WIDTH;
    551 	ss->ss_adder->y_clip_min = 0;
    552 	ss->ss_adder->y_clip_max = GPX_VISHEIGHT;
    553 	/* load DESTINATION origin and vectors */
    554 	ss->ss_adder->fast_dest_dy = 0;
    555 	ss->ss_adder->slow_dest_dx = 0;
    556 	ss->ss_adder->error_1 = 0;
    557 	ss->ss_adder->error_2 = 0;
    558 	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
    559 	gpx_wait(ss, RASTEROP_COMPLETE);
    560 	ss->ss_adder->destination_x = dx;
    561 	ss->ss_adder->fast_dest_dx = font->fontwidth;
    562 	ss->ss_adder->destination_y = dy;
    563 	ss->ss_adder->slow_dest_dy = font->fontheight;
    564 	/* load SOURCE origin and vectors */
    565 	ss->ss_adder->source_1_x = sx;
    566 	ss->ss_adder->source_1_y = sy;
    567 	ss->ss_adder->source_1_dx = font->fontwidth;
    568 	ss->ss_adder->source_1_dy = font->fontheight;
    569 	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1;
    570 
    571 	if (ul != 0) {
    572 		gpx_fillrect(ss, dx, dy + font->fontheight - 2, font->fontwidth,
    573 		    1, attr, LF_R3);	/* fg fill */
    574 	}
    575 }
    576 
    577 static void
    578 gpx_copycols(void *v, int row, int src, int dst, int cnt)
    579 {
    580 	struct rasops_info *ri = v;
    581 	struct gpx_screen *ss = ri->ri_hw;
    582 	struct wsdisplay_font *font = ri->ri_font;
    583 	int sx, y, dx, w, h;
    584 
    585 	sx = ri->ri_xorigin + src * font->fontwidth;
    586 	dx = ri->ri_xorigin + dst * font->fontwidth;
    587 	w = cnt * font->fontwidth;
    588 	y = ri->ri_yorigin + row * font->fontheight;
    589 	h = font->fontheight;
    590 
    591 	gpx_copyrect(ss, sx, y, dx, y, w, h);
    592 }
    593 
    594 static void
    595 gpx_erasecols(void *v, int row, int col, int cnt, long attr)
    596 {
    597 	struct rasops_info *ri = v;
    598 	struct gpx_screen *ss = ri->ri_hw;
    599 	struct wsdisplay_font *font = ri->ri_font;
    600 	int x, y, dx, dy;
    601 
    602 	x = ri->ri_xorigin + col * font->fontwidth;
    603 	dx = cnt * font->fontwidth;
    604 	y = ri->ri_yorigin + row * font->fontheight;
    605 	dy = font->fontheight;
    606 
    607 	gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */
    608 }
    609 
    610 static void
    611 gpx_copyrows(void *v, int src, int dst, int cnt)
    612 {
    613 	struct rasops_info *ri = v;
    614 	struct gpx_screen *ss = ri->ri_hw;
    615 	struct wsdisplay_font *font = ri->ri_font;
    616 	int x, sy, dy, w, h;
    617 
    618 	x = ri->ri_xorigin;
    619 	w = ri->ri_emustride;
    620 	sy = ri->ri_yorigin + src * font->fontheight;
    621 	dy = ri->ri_yorigin + dst * font->fontheight;
    622 	h = cnt * font->fontheight;
    623 
    624 	gpx_copyrect(ss, x, sy, x, dy, w, h);
    625 }
    626 
    627 static void
    628 gpx_eraserows(void *v, int row, int cnt, long attr)
    629 {
    630 	struct rasops_info *ri = v;
    631 	struct gpx_screen *ss = ri->ri_hw;
    632 	struct wsdisplay_font *font = ri->ri_font;
    633 	int x, y, dx, dy;
    634 
    635 	x = ri->ri_xorigin;
    636 	dx = ri->ri_emustride;
    637 	y = ri->ri_yorigin + row * font->fontheight;
    638 	dy = cnt * font->fontheight;
    639 
    640 	gpx_fillrect(ss, x, y, dx, dy, attr, LF_R2); /* bg fill */
    641 }
    642 
    643 static void
    644 gpx_do_cursor(struct rasops_info *ri)
    645 {
    646 	struct gpx_screen *ss = ri->ri_hw;
    647 	int x, y, w, h;
    648 
    649 	x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin;
    650 	y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin;
    651 	w = ri->ri_font->fontwidth;
    652 	h = ri->ri_font->fontheight;
    653 
    654 	gpx_fillrect(ss, x, y, w, h, WSCOL_WHITE << 24, LF_R4);	/* invert */
    655 }
    656 
    657 /*
    658  * low-level programming routines
    659  */
    660 
    661 static int
    662 gpx_wait(struct gpx_screen *ss, int bits)
    663 {
    664 	int i;
    665 
    666 	ss->ss_adder->status = 0;
    667 	for (i = 100000; i != 0; i--) {
    668 		if ((ss->ss_adder->status & bits) == bits)
    669 			break;
    670 		DELAY(1);
    671 	}
    672 
    673 	return i == 0;
    674 }
    675 
    676 static int
    677 gpx_viper_write(struct gpx_screen *ss, u_int reg, uint16_t val)
    678 {
    679 	if (gpx_wait(ss, ADDRESS_COMPLETE) == 0 &&
    680 	    gpx_wait(ss, TX_READY) == 0) {
    681 		ss->ss_adder->id_data = val;
    682 		ss->ss_adder->command = ID_LOAD | reg;
    683 		return 0;
    684 	}
    685 #ifdef DEBUG
    686 	if (ss->ss_console == 0)	/* don't make things worse! */
    687 		printf("gpx_viper_write failure, reg %x val %x\n", reg, val);
    688 #endif
    689 	return 1;
    690 }
    691 
    692 /* Initialize the damned beast. Straight from qdss. */
    693 static void
    694 gpx_reset_viper(struct gpx_screen *ss)
    695 {
    696 	int i;
    697 
    698 	ss->ss_adder->interrupt_enable = 0;
    699 	ss->ss_adder->command = CANCEL;
    700 	/* set monitor timing */
    701 	ss->ss_adder->x_scan_count_0 = 0x2800;
    702 	ss->ss_adder->x_scan_count_1 = 0x1020;
    703 	ss->ss_adder->x_scan_count_2 = 0x003a;
    704 	ss->ss_adder->x_scan_count_3 = 0x38f0;
    705 	ss->ss_adder->x_scan_count_4 = 0x6128;
    706 	ss->ss_adder->x_scan_count_5 = 0x093a;
    707 	ss->ss_adder->x_scan_count_6 = 0x313c;
    708 	ss->ss_adder->sync_phase_adj = 0x0100;
    709 	ss->ss_adder->x_scan_conf = 0x00c8;
    710 	/*
    711 	 * got a bug in second pass ADDER! lets take care of it...
    712 	 *
    713 	 * normally, just use the code in the following bug fix code, but to
    714 	 * make repeated demos look pretty, load the registers as if there was
    715 	 * no bug and then test to see if we are getting sync
    716 	 */
    717 	ss->ss_adder->y_scan_count_0 = 0x135f;
    718 	ss->ss_adder->y_scan_count_1 = 0x3363;
    719 	ss->ss_adder->y_scan_count_2 = 0x2366;
    720 	ss->ss_adder->y_scan_count_3 = 0x0388;
    721 	/*
    722 	 * if no sync, do the bug fix code
    723 	 */
    724 	if (gpx_wait(ss, FRAME_SYNC) != 0) {
    725 		/*
    726 		 * First load all Y scan registers with very short frame and
    727 		 * wait for scroll service.  This guarantees at least one SYNC
    728 		 * to fix the pass 2 Adder initialization bug (synchronizes
    729 		 * XCINCH with DMSEEDH)
    730 		 */
    731 		ss->ss_adder->y_scan_count_0 = 0x01;
    732 		ss->ss_adder->y_scan_count_1 = 0x01;
    733 		ss->ss_adder->y_scan_count_2 = 0x01;
    734 		ss->ss_adder->y_scan_count_3 = 0x01;
    735 		/* delay at least 1 full frame time */
    736 		gpx_wait(ss, FRAME_SYNC);
    737 		gpx_wait(ss, FRAME_SYNC);
    738 		/*
    739 		 * now load the REAL sync values (in reverse order just to
    740 		 * be safe).
    741 		 */
    742 		ss->ss_adder->y_scan_count_3 = 0x0388;
    743 		ss->ss_adder->y_scan_count_2 = 0x2366;
    744 		ss->ss_adder->y_scan_count_1 = 0x3363;
    745 		ss->ss_adder->y_scan_count_0 = 0x135f;
    746 	}
    747 	/* zero the index registers */
    748 	ss->ss_adder->x_index_pending = 0;
    749 	ss->ss_adder->y_index_pending = 0;
    750 	ss->ss_adder->x_index_new = 0;
    751 	ss->ss_adder->y_index_new = 0;
    752 	ss->ss_adder->x_index_old = 0;
    753 	ss->ss_adder->y_index_old = 0;
    754 	ss->ss_adder->pause = 0;
    755 	/* set rasterop mode to normal pen down */
    756 	ss->ss_adder->rasterop_mode =
    757 	    DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
    758 	/* set the rasterop registers to default values */
    759 	ss->ss_adder->source_1_dx = 1;
    760 	ss->ss_adder->source_1_dy = 1;
    761 	ss->ss_adder->source_1_x = 0;
    762 	ss->ss_adder->source_1_y = 0;
    763 	ss->ss_adder->destination_x = 0;
    764 	ss->ss_adder->destination_y = 0;
    765 	ss->ss_adder->fast_dest_dx = 1;
    766 	ss->ss_adder->fast_dest_dy = 0;
    767 	ss->ss_adder->slow_dest_dx = 0;
    768 	ss->ss_adder->slow_dest_dy = 1;
    769 	ss->ss_adder->error_1 = 0;
    770 	ss->ss_adder->error_2 = 0;
    771 	/* scale factor = UNITY */
    772 	ss->ss_adder->fast_scale = UNITY;
    773 	ss->ss_adder->slow_scale = UNITY;
    774 	/* set the source 2 parameters */
    775 	ss->ss_adder->source_2_x = 0;
    776 	ss->ss_adder->source_2_y = 0;
    777 	ss->ss_adder->source_2_size = 0x0022;
    778 	/* initialize plane addresses for eight vipers */
    779 	for (i = 0; i < 8; i++) {
    780 		gpx_viper_write(ss, CS_UPDATE_MASK, 1 << i);
    781 		gpx_viper_write(ss, PLANE_ADDRESS, i);
    782 	}
    783 	/* initialize the external registers. */
    784 	gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff);
    785 	gpx_viper_write(ss, CS_SCROLL_MASK, 0x00ff);
    786 	/* initialize resolution mode */
    787 	gpx_viper_write(ss, MEMORY_BUS_WIDTH, 0x000c);	/* bus width = 16 */
    788 	gpx_viper_write(ss, RESOLUTION_MODE, 0x0000);	/* one bit/pixel */
    789 	/* initialize viper registers */
    790 	gpx_viper_write(ss, SCROLL_CONSTANT,
    791 	    SCROLL_ENABLE | VIPER_LEFT | VIPER_UP);
    792 	gpx_viper_write(ss, SCROLL_FILL, 0x0000);
    793 	/* set clipping and scrolling limits to full screen */
    794 	gpx_wait(ss, ADDRESS_COMPLETE);
    795 	ss->ss_adder->x_clip_min = 0;
    796 	ss->ss_adder->x_clip_max = GPX_WIDTH;
    797 	ss->ss_adder->y_clip_min = 0;
    798 	ss->ss_adder->y_clip_max = GPX_HEIGHT;
    799 	ss->ss_adder->scroll_x_min = 0;
    800 	ss->ss_adder->scroll_x_max = GPX_WIDTH;
    801 	ss->ss_adder->scroll_y_min = 0;
    802 	ss->ss_adder->scroll_y_max = GPX_HEIGHT;
    803 	gpx_wait(ss, FRAME_SYNC);	/* wait at LEAST 1 full frame */
    804 	gpx_wait(ss, FRAME_SYNC);
    805 	ss->ss_adder->x_index_pending = 0;
    806 	ss->ss_adder->y_index_pending = 0;
    807 	ss->ss_adder->x_index_new = 0;
    808 	ss->ss_adder->y_index_new = 0;
    809 	ss->ss_adder->x_index_old = 0;
    810 	ss->ss_adder->y_index_old = 0;
    811 	gpx_wait(ss, ADDRESS_COMPLETE);
    812 	gpx_viper_write(ss, LEFT_SCROLL_MASK, 0x0000);
    813 	gpx_viper_write(ss, RIGHT_SCROLL_MASK, 0x0000);
    814 	/* set source and the mask register to all ones */
    815 	gpx_viper_write(ss, SOURCE, 0xffff);
    816 	gpx_viper_write(ss, MASK_1, 0xffff);
    817 	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
    818 	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
    819 	/* initialize Operand Control Register banks for fill command */
    820 	gpx_viper_write(ss, SRC1_OCR_A,
    821 	    EXT_NONE | INT_M1_M2  | NO_ID | WAIT);
    822 	gpx_viper_write(ss, SRC2_OCR_A,
    823 	    EXT_NONE | INT_SOURCE | NO_ID | NO_WAIT);
    824 	gpx_viper_write(ss, DST_OCR_A,
    825 	    EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
    826 	gpx_viper_write(ss, SRC1_OCR_B,
    827 	    EXT_NONE | INT_SOURCE | NO_ID | WAIT);
    828 	gpx_viper_write(ss, SRC2_OCR_B,
    829 	    EXT_NONE | INT_M1_M2  | NO_ID | NO_WAIT);
    830 	gpx_viper_write(ss, DST_OCR_B,
    831 	    EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
    832 
    833 	/*
    834 	 * Init Logic Unit Function registers.
    835 	 */
    836 	/* putchar */
    837 	gpx_viper_write(ss, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
    838 	/* erase{cols,rows} */
    839 	gpx_viper_write(ss, LU_FUNCTION_R2, FULL_SRC_RESOLUTION | LF_ZEROS);
    840 	/* underline */
    841 	gpx_viper_write(ss, LU_FUNCTION_R3, FULL_SRC_RESOLUTION | LF_ONES);
    842 	/* cursor */
    843 	gpx_viper_write(ss, LU_FUNCTION_R4, FULL_SRC_RESOLUTION | LF_NOT_D);
    844 }
    845 
    846 /* Clear the whole screen. Straight from qdss. */
    847 static void
    848 gpx_clear_screen(struct gpx_screen *ss)
    849 {
    850 
    851 	ss->ss_adder->x_limit = GPX_WIDTH;
    852 	ss->ss_adder->y_limit = GPX_HEIGHT;
    853 	ss->ss_adder->y_offset_pending = 0;
    854 	gpx_wait(ss, FRAME_SYNC);	/* wait at LEAST 1 full frame */
    855 	gpx_wait(ss, FRAME_SYNC);
    856 	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
    857 	gpx_wait(ss, FRAME_SYNC);
    858 	gpx_wait(ss, FRAME_SYNC);
    859 	ss->ss_adder->y_offset_pending = GPX_VISHEIGHT;
    860 	gpx_wait(ss, FRAME_SYNC);
    861 	gpx_wait(ss, FRAME_SYNC);
    862 	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
    863 	gpx_wait(ss, FRAME_SYNC);
    864 	gpx_wait(ss, FRAME_SYNC);
    865 	ss->ss_adder->y_offset_pending = 2 * GPX_VISHEIGHT;
    866 	gpx_wait(ss, FRAME_SYNC);
    867 	gpx_wait(ss, FRAME_SYNC);
    868 	ss->ss_adder->y_scroll_constant = SCROLL_ERASE;
    869 	gpx_wait(ss, FRAME_SYNC);
    870 	gpx_wait(ss, FRAME_SYNC);
    871 	ss->ss_adder->y_offset_pending = 0;	 /* back to normal */
    872 	gpx_wait(ss, FRAME_SYNC);
    873 	gpx_wait(ss, FRAME_SYNC);
    874 	ss->ss_adder->x_limit = GPX_WIDTH;
    875 	ss->ss_adder->y_limit = GPX_VISHEIGHT;
    876 }
    877 
    878 static int
    879 gpx_setup_screen(struct gpx_screen *ss)
    880 {
    881 	struct rasops_info *ri = &ss->ss_ri;
    882 	int cookie;
    883 
    884 	memset(ri, 0, sizeof(*ri));
    885 	ri->ri_depth = 8;	/* masquerade as a 8 bit device for rasops */
    886 	ri->ri_width = GPX_WIDTH;
    887 	ri->ri_height = GPX_VISHEIGHT;
    888 	ri->ri_stride = GPX_WIDTH;
    889 	ri->ri_flg = RI_CENTER;		/* no RI_CLEAR as ri_bits is NULL! */
    890 	ri->ri_hw = ss;
    891 	if (ss == &gpx_consscr)
    892 		ri->ri_flg |= RI_NO_AUTO;
    893 
    894 	/*
    895 	 * We can not let rasops select our font, because we need to use
    896 	 * a font with right-to-left bit order on this frame buffer.
    897 	 */
    898 	wsfont_init();
    899 	cookie = wsfont_find(NULL, 12, 0, 0, WSDISPLAY_FONTORDER_R2L,
    900 	    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
    901 	if (cookie < 0)
    902 		cookie = wsfont_find(NULL, 8, 0, 0, WSDISPLAY_FONTORDER_R2L,
    903 		    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
    904 	if (cookie < 0)
    905 		cookie = wsfont_find(NULL, 0, 0, 0, WSDISPLAY_FONTORDER_R2L,
    906 		    WSDISPLAY_FONTORDER_L2R, WSFONT_FIND_BITMAP);
    907 	if (cookie < 0)
    908 		return -1;
    909 	if (wsfont_lock(cookie, &ri->ri_font) != 0)
    910 		return -1;
    911 	ri->ri_wsfcookie = cookie;
    912 
    913 	/*
    914 	 * Ask for an unholy big display, rasops will trim this to more
    915 	 * reasonable values.
    916 	 */
    917 	if (rasops_init(ri, 160, 160) != 0)
    918 		return -1;
    919 
    920 	/*
    921 	 * Override the rasops emulops.
    922 	 */
    923 	ri->ri_ops.copyrows = gpx_copyrows;
    924 	ri->ri_ops.copycols = gpx_copycols;
    925 	ri->ri_ops.eraserows = gpx_eraserows;
    926 	ri->ri_ops.erasecols = gpx_erasecols;
    927 	ri->ri_ops.putchar = gpx_putchar;
    928 	ri->ri_do_cursor = gpx_do_cursor;
    929 
    930 	gpx_stdscreen.ncols = ri->ri_cols;
    931 	gpx_stdscreen.nrows = ri->ri_rows;
    932 	gpx_stdscreen.textops = &ri->ri_ops;
    933 	gpx_stdscreen.fontwidth = ri->ri_font->fontwidth;
    934 	gpx_stdscreen.fontheight = ri->ri_font->fontheight;
    935 	gpx_stdscreen.capabilities = ri->ri_caps;
    936 
    937 	/*
    938 	 * Initialize RAMDAC.
    939 	 */
    940 	if (ss->ss_depth == 8) {
    941 		struct ramdac8 *rd = ss->ss_vdac;
    942 		rd->address = BT_CR;
    943 		rd->control = BTCR_RAMENA | BTCR_BLINK_1648 | BTCR_MPLX_4;
    944 	} else {
    945 		struct ramdac4 *rd = ss->ss_vdac;
    946 		rd->control = RAMDAC4_INIT;
    947 	}
    948 
    949 	/*
    950 	 * Put the ADDER and VIPER in a good state.
    951 	 */
    952 	gpx_reset_viper(ss);
    953 
    954 	/*
    955 	 * Initialize colormap.
    956 	 */
    957 	gpx_resetcmap(ss);
    958 
    959 	/*
    960 	 * Clear display (including non-visible area), in 864 lines chunks.
    961 	 */
    962 	gpx_clear_screen(ss);
    963 
    964 	/*
    965 	 * Copy our font to the offscreen area.
    966 	 */
    967 	gpx_upload_font(ss);
    968 
    969 #if 0
    970 	ss->ss_cursor->cmdr = ss->ss_curcmd = PCCCMD_HSHI;
    971 #endif
    972 
    973 	return 0;
    974 }
    975 
    976 /*
    977  * Copy the selected wsfont to non-visible frame buffer area.
    978  * This is necessary since the only way to send data to the frame buffer
    979  * is through the ID interface, which is slow and needs 16 bit wide data.
    980  * Adapted from qdss.
    981  */
    982 static void
    983 gpx_upload_font(struct gpx_screen *ss)
    984 {
    985 	struct rasops_info *ri = &ss->ss_ri;
    986 	struct wsdisplay_font *font = ri->ri_font;
    987 	uint8_t *fontbits, *fb;
    988 	u_int remaining, nchars, row;
    989 	u_int i, j;
    990 	uint16_t data;
    991 
    992 	/* setup VIPER operand control registers */
    993 
    994 	gpx_viper_write(ss, MASK_1, 0xffff);
    995 	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
    996 	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
    997 
    998 	gpx_viper_write(ss, SRC1_OCR_B,
    999 	    EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
   1000 	gpx_viper_write(ss, SRC2_OCR_B,
   1001 	    EXT_NONE | INT_NONE | ID | BAR_SHIFT_DELAY);
   1002 	gpx_viper_write(ss, DST_OCR_B,
   1003 	    EXT_SOURCE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
   1004 
   1005 	ss->ss_adder->rasterop_mode =
   1006 	    DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
   1007 	gpx_wait(ss, RASTEROP_COMPLETE);
   1008 
   1009 	/*
   1010 	 * Load font data. The font is uploaded in 8 or 16 bit wide cells, on
   1011 	 * as many ``lines'' as necessary at the end of the display.
   1012 	 */
   1013 	ss->ss_gpr = MIN(GPX_WIDTH / (NBBY * font->stride), font->numchars);
   1014 	if ((ss->ss_gpr & 1) != 0)
   1015 		ss->ss_gpr--;
   1016 	fontbits = font->data;
   1017 	for (row = 1, remaining = font->numchars; remaining != 0;
   1018 	    row++, remaining -= nchars) {
   1019 		nchars = MIN(ss->ss_gpr, remaining);
   1020 
   1021 		ss->ss_adder->destination_x = 0;
   1022 		ss->ss_adder->destination_y =
   1023 		    GPX_HEIGHT - row * font->fontheight;
   1024 		ss->ss_adder->fast_dest_dx = nchars * 16;
   1025 		ss->ss_adder->slow_dest_dy = font->fontheight;
   1026 
   1027 		/* setup for processor to bitmap xfer */
   1028 		gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff);
   1029 		ss->ss_adder->cmd = PBT | OCRB | DTE | LF_R1 | 2; /*XXX why 2?*/
   1030 
   1031 		/* iteratively do the processor to bitmap xfer */
   1032 		for (i = font->fontheight; i != 0; i--) {
   1033 			fb = fontbits;
   1034 			fontbits += font->stride;
   1035 			/* PTOB a scan line */
   1036 			for (j = nchars; j != 0; j--) {
   1037 				/* PTOB one scan of a char cell */
   1038 				if (font->stride == 1) {
   1039 					data = *fb;
   1040 					fb += font->fontheight;
   1041 					/*
   1042 					 * Do not access past font memory if
   1043 					 * it has an odd number of characters
   1044 					 * and this is the last pair.
   1045 					 */
   1046 					if (j != 1 || (nchars & 1) == 0 ||
   1047 					    remaining != nchars) {
   1048 						data |= ((uint16_t)*fb) << 8;
   1049 						fb += font->fontheight;
   1050 					}
   1051 				} else {
   1052 					data =
   1053 					    fb[0] | (((uint16_t)fb[1]) << 8);
   1054 					fb += font->fontheight * font->stride;
   1055 				}
   1056 
   1057 				gpx_wait(ss, TX_READY);
   1058 				ss->ss_adder->id_data = data;
   1059 			}
   1060 		}
   1061 		fontbits += (nchars - 1) * font->stride * font->fontheight;
   1062 	}
   1063 }
   1064 
   1065 static void
   1066 gpx_copyrect(struct gpx_screen *ss,
   1067     int sx, int sy, int dx, int dy, int w, int h)
   1068 {
   1069 
   1070 	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
   1071 		continue;
   1072 	gpx_viper_write(ss, MASK_1, 0xffff);
   1073 	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
   1074 	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
   1075 	gpx_viper_write(ss, SRC1_OCR_B,
   1076 	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
   1077 	gpx_viper_write(ss, DST_OCR_B,
   1078 	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
   1079 	ss->ss_adder->fast_dest_dy = 0;
   1080 	ss->ss_adder->slow_dest_dx = 0;
   1081 	ss->ss_adder->error_1 = 0;
   1082 	ss->ss_adder->error_2 = 0;
   1083 	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
   1084 	gpx_wait(ss, RASTEROP_COMPLETE);
   1085 	ss->ss_adder->destination_x = dx;
   1086 	ss->ss_adder->fast_dest_dx = w;
   1087 	ss->ss_adder->destination_y = dy;
   1088 	ss->ss_adder->slow_dest_dy = h;
   1089 	ss->ss_adder->source_1_x = sx;
   1090 	ss->ss_adder->source_1_dx = w;
   1091 	ss->ss_adder->source_1_y = sy;
   1092 	ss->ss_adder->source_1_dy = h;
   1093 	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | LF_R1;
   1094 }
   1095 
   1096 /*
   1097  * Fill a rectangle with the given attribute and function (i.e. rop).
   1098  */
   1099 static void
   1100 gpx_fillrect(struct gpx_screen *ss, int x, int y, int dx, int dy, long attr,
   1101     u_int function)
   1102 {
   1103 	int fg, bg;
   1104 
   1105 	rasops_unpack_attr(attr, &fg, &bg, NULL);
   1106 
   1107 	while (gpx_viper_write(ss, CS_UPDATE_MASK, 0x00ff))
   1108 		continue;
   1109 	gpx_viper_write(ss, MASK_1, 0xffff);
   1110 	gpx_viper_write(ss, SOURCE, 0xffff);
   1111 	gpx_viper_write(ss, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, fg);
   1112 	gpx_viper_write(ss, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, bg);
   1113 	gpx_viper_write(ss, SRC1_OCR_B,
   1114 	    EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
   1115 	gpx_viper_write(ss, DST_OCR_B,
   1116 	    EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
   1117 	ss->ss_adder->fast_dest_dx = 0;
   1118 	ss->ss_adder->fast_dest_dy = 0;
   1119 	ss->ss_adder->slow_dest_dx = 0;
   1120 	ss->ss_adder->error_1 = 0;
   1121 	ss->ss_adder->error_2 = 0;
   1122 	ss->ss_adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
   1123 	gpx_wait(ss, RASTEROP_COMPLETE);
   1124 	ss->ss_adder->destination_x = x;
   1125 	ss->ss_adder->fast_dest_dx = dx;
   1126 	ss->ss_adder->destination_y = y;
   1127 	ss->ss_adder->slow_dest_dy = dy;
   1128 	ss->ss_adder->source_1_x = x;
   1129 	ss->ss_adder->source_1_dx = dx;
   1130 	ss->ss_adder->source_1_y = y;
   1131 	ss->ss_adder->source_1_dy = dy;
   1132 	ss->ss_adder->cmd = RASTEROP | OCRB | S1E | DTE | function;
   1133 }
   1134 
   1135 /*
   1136  * Colormap handling routines
   1137  */
   1138 
   1139 static int
   1140 gpx_getcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm)
   1141 {
   1142 	u_int index = cm->index, count = cm->count, i;
   1143 	u_int colcount = 1 << ss->ss_depth;
   1144 	int error;
   1145 	uint8_t ramp[256], *c, *r;
   1146 
   1147 	if (index >= colcount || count > colcount - index)
   1148 		return EINVAL;
   1149 
   1150 	if (count == 0)
   1151 		return 0;
   1152 
   1153 	/* extract reds */
   1154 	c = ss->ss_cmap + 0 + index * 3;
   1155 	for (i = count, r = ramp; i != 0; i--)
   1156 		*r++ = *c << (8 - ss->ss_depth), c += 3;
   1157 	if ((error = copyout(ramp, cm->red, count)) != 0)
   1158 		return error;
   1159 
   1160 	/* extract greens */
   1161 	c = ss->ss_cmap + 1 + index * 3;
   1162 	for (i = count, r = ramp; i != 0; i--)
   1163 		*r++ = *c << (8 - ss->ss_depth), c += 3;
   1164 	if ((error = copyout(ramp, cm->green, count)) != 0)
   1165 		return error;
   1166 
   1167 	/* extract blues */
   1168 	c = ss->ss_cmap + 2 + index * 3;
   1169 	for (i = count, r = ramp; i != 0; i--)
   1170 		*r++ = *c << (8 - ss->ss_depth), c += 3;
   1171 	if ((error = copyout(ramp, cm->blue, count)) != 0)
   1172 		return error;
   1173 
   1174 	return 0;
   1175 }
   1176 
   1177 static int
   1178 gpx_putcmap(struct gpx_screen *ss, struct wsdisplay_cmap *cm)
   1179 {
   1180 	u_int index = cm->index, count = cm->count;
   1181 	u_int colcount = 1 << ss->ss_depth;
   1182 	int i, error;
   1183 	uint8_t r[256], g[256], b[256], *nr, *ng, *nb, *c;
   1184 
   1185 	if (index >= colcount || count > colcount - index)
   1186 		return EINVAL;
   1187 
   1188 	if ((error = copyin(cm->red, r, count)) != 0)
   1189 		return error;
   1190 	if ((error = copyin(cm->green, g, count)) != 0)
   1191 		return error;
   1192 	if ((error = copyin(cm->blue, b, count)) != 0)
   1193 		return error;
   1194 
   1195 	nr = r, ng = g, nb = b;
   1196 	c = ss->ss_cmap + index * 3;
   1197 	for (i = count; i != 0; i--) {
   1198 		*c++ = *nr++ >> (8 - ss->ss_depth);
   1199 		*c++ = *ng++ >> (8 - ss->ss_depth);
   1200 		*c++ = *nb++ >> (8 - ss->ss_depth);
   1201 	}
   1202 
   1203 	return 0;
   1204 }
   1205 
   1206 static void
   1207 gpx_loadcmap(struct gpx_screen *ss, int from, int count)
   1208 {
   1209 	uint8_t *cmap = ss->ss_cmap;
   1210 	int i, color12;
   1211 
   1212 	gpx_wait(ss, FRAME_SYNC);
   1213 	if (ss->ss_depth == 8) {
   1214 		struct ramdac8 *rd = ss->ss_vdac;
   1215 
   1216 		cmap += from * 3;
   1217 		rd->address = from;
   1218 		for (i = 0; i < count * 3; i++)
   1219 			rd->cmapdata = *cmap++;
   1220 	} else {
   1221 		struct ramdac4 *rd = ss->ss_vdac;
   1222 
   1223 		cmap = ss->ss_cmap + from;
   1224 		for (i = from; i < from + count; i++) {
   1225 			color12  = (*cmap++ >> 4) << 0;
   1226 			color12 |= (*cmap++ >> 4) << 8;
   1227 			color12 |= (*cmap++ >> 4) << 4;
   1228 			rd->colormap[i] = color12;
   1229 		}
   1230 	}
   1231 }
   1232 
   1233 static void
   1234 gpx_resetcmap(struct gpx_screen *ss)
   1235 {
   1236 
   1237 	if (ss->ss_depth == 8)
   1238 		memcpy(ss->ss_cmap, rasops_cmap, sizeof(ss->ss_cmap));
   1239 	else {
   1240 		memcpy(ss->ss_cmap, rasops_cmap, 8 * 3);
   1241 		memcpy(ss->ss_cmap + 8 * 3, rasops_cmap + 0xf8 * 3, 8 * 3);
   1242 	}
   1243 	gpx_loadcmap(ss, 0, 1 << ss->ss_depth);
   1244 
   1245 	/*
   1246 	 * On the 4bit RAMDAC, make the hardware cursor black on black
   1247 	 */
   1248 	if (ss->ss_depth != 8) {
   1249 		struct ramdac4 *rd = ss->ss_vdac;
   1250 
   1251 		rd->cursormap[0] = rd->cursormap[1] =
   1252 		    rd->cursormap[2] = rd->cursormap[3] = 0x0000;
   1253 	}
   1254 }
   1255 
   1256 /*
   1257  * Console support code
   1258  */
   1259 
   1260 cons_decl(gpx);
   1261 
   1262 /*
   1263  * Called very early to setup the glass tty as console.
   1264  * Because it's called before the VM system is initialized, virtual memory
   1265  * for the framebuffer can be stolen directly without disturbing anything.
   1266  */
   1267 void
   1268 gpxcnprobe(struct consdev *cndev)
   1269 {
   1270 	extern vaddr_t virtual_avail;
   1271 	extern const struct cdevsw wsdisplay_cdevsw;
   1272 	volatile struct adder *adder;
   1273 	vaddr_t tmp;
   1274 	int depth;
   1275 	u_short status;
   1276 
   1277 	switch (vax_boardtype) {
   1278 	case VAX_BTYP_410:
   1279 	case VAX_BTYP_420:
   1280 	case VAX_BTYP_43:
   1281 		if ((vax_confdata & (KA420_CFG_L3CON | KA420_CFG_MULTU)) != 0)
   1282 			break; /* doesn't use graphics console */
   1283 
   1284 		if ((vax_confdata & KA420_CFG_VIDOPT) == 0)
   1285 			break; /* no color option */
   1286 
   1287 		/* Check for hardware */
   1288 		tmp = virtual_avail;
   1289 		ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_ADDER_OFFSET), 1);
   1290 		adder = (struct adder *)tmp;
   1291 		adder->status = 0;
   1292 		status = adder->status;
   1293 		iounaccess(tmp, 1);
   1294 		if (status == offsetof(struct adder, status))
   1295 			return;
   1296 
   1297 		/* Check for a recognized color depth */
   1298 		tmp = virtual_avail;
   1299 		ioaccess(tmp, vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1);
   1300 		depth = *(uint16_t *)
   1301 		    (tmp + (GPX_READBACK_OFFSET & VAX_PGOFSET)) & 0x00f0;
   1302 		iounaccess(tmp, 1);
   1303 		if (depth != 0x00f0 && depth != 0x0080)
   1304 			return;
   1305 
   1306 		cndev->cn_pri = CN_INTERNAL;
   1307 		cndev->cn_dev =
   1308 		    makedev(cdevsw_lookup_major(&wsdisplay_cdevsw), 0);
   1309 		break;
   1310 
   1311 	default:
   1312 		break;
   1313 	}
   1314 }
   1315 
   1316 /*
   1317  * Called very early to setup the glass tty as console.
   1318  * Because it's called before the VM system is initialized, virtual memory
   1319  * for the framebuffer can be stolen directly without disturbing anything.
   1320  */
   1321 void
   1322 gpxcninit(struct consdev *cndev)
   1323 {
   1324 	struct gpx_screen *ss = &gpx_consscr;
   1325 	extern vaddr_t virtual_avail;
   1326 	vaddr_t ova;
   1327 	long defattr;
   1328 	struct rasops_info *ri;
   1329 
   1330 	ova = virtual_avail;
   1331 
   1332 	ioaccess(virtual_avail,
   1333 	    vax_trunc_page(GPXADDR + GPX_READBACK_OFFSET), 1);
   1334 	ss->ss_depth = (0x00f0 & *(uint16_t *)(virtual_avail +
   1335 	    (GPX_READBACK_OFFSET & VAX_PGOFSET))) == 0x00f0 ? 4 : 8;
   1336 
   1337 	ioaccess(virtual_avail, GPXADDR + GPX_ADDER_OFFSET, 1);
   1338 	ss->ss_adder = (struct adder *)virtual_avail;
   1339 	virtual_avail += VAX_NBPG;
   1340 
   1341 	ioaccess(virtual_avail, vax_trunc_page(GPXADDR + GPX_VDAC_OFFSET), 1);
   1342 	ss->ss_vdac = (void *)(virtual_avail + (GPX_VDAC_OFFSET & VAX_PGOFSET));
   1343 	virtual_avail += VAX_NBPG;
   1344 
   1345 #if 0
   1346 	ioaccess(virtual_avail, GPXADDR + GPX_CURSOR_OFFSET, 1);
   1347 	ss->ss_cursor = (struct dc503reg *)virtual_avail;
   1348 	virtual_avail += VAX_NBPG;
   1349 #endif
   1350 
   1351 	virtual_avail = round_page(virtual_avail);
   1352 
   1353 	/* this had better not fail */
   1354 	if (gpx_setup_screen(ss) != 0) {
   1355 #if 0
   1356 		iounaccess((vaddr_t)ss->ss_cursor, 1);
   1357 #endif
   1358 		iounaccess((vaddr_t)ss->ss_vdac, 1);
   1359 		iounaccess((vaddr_t)ss->ss_adder, 1);
   1360 		virtual_avail = ova;
   1361 		return;
   1362 	}
   1363 
   1364 	ri = &ss->ss_ri;
   1365 	ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr);
   1366 	wsdisplay_cnattach(&gpx_stdscreen, ri, 0, 0, defattr);
   1367 	cn_tab->cn_pri = CN_INTERNAL;
   1368 
   1369 #if NDZKBD > 0
   1370 	dzkbd_cnattach(0); /* Connect keyboard and screen together */
   1371 #endif
   1372 }
   1373