1/*	$NetBSD: zx.c,v 1.47 2021/08/07 16:19:15 thorpej Exp $	*/
2
3/*
4 *  Copyright (c) 2002 The NetBSD Foundation, Inc.
5 *  All rights reserved.
6 *
7 *  This code is derived from software contributed to The NetBSD Foundation
8 *  by Andrew Doran.
9 *
10 *  Redistribution and use in source and binary forms, with or without
11 *  modification, are permitted provided that the following conditions
12 *  are met:
13 *  1. Redistributions of source code must retain the above copyright
14 *     notice, this list of conditions and the following disclaimer.
15 *  2. Redistributions in binary form must reproduce the above copyright
16 *     notice, this list of conditions and the following disclaimer in the
17 *     documentation and/or other materials provided with the distribution.
18 *
19 *  THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 *  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 *  POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * Driver for the Sun ZX display adapter.  This would be called 'leo', but
34 * NetBSD/amiga already has a driver by that name.  The XFree86 and Linux
35 * drivers were used as "living documentation" when writing this; thanks
36 * to the authors.
37 *
38 * Issues (which can be solved with wscons, happily enough):
39 *
40 * o There is lots of unnecessary mucking about rasops in here, primarily
41 *   to appease the sparc fb code.
42 */
43
44#include <sys/cdefs.h>
45__KERNEL_RCSID(0, "$NetBSD: zx.c,v 1.47 2021/08/07 16:19:15 thorpej Exp $");
46
47#include <sys/param.h>
48#include <sys/systm.h>
49#include <sys/device.h>
50#include <sys/ioctl.h>
51#include <sys/malloc.h>
52#include <sys/mman.h>
53#include <sys/tty.h>
54#include <sys/conf.h>
55#include <sys/syslog.h>
56#include <sys/buf.h>
57#ifdef DEBUG
58/* for log(9) in zxioctl() */
59#include <sys/lwp.h>
60#include <sys/proc.h>
61#endif
62
63#include <sys/bus.h>
64#include <machine/autoconf.h>
65
66#include <dev/sun/fbio.h>
67#include <dev/sun/fbvar.h>
68
69#include "wsdisplay.h"
70#include <dev/wscons/wsconsio.h>
71#include <dev/wsfont/wsfont.h>
72#include <dev/rasops/rasops.h>
73#include <dev/wscons/wsdisplay_vconsvar.h>
74
75#include "opt_wsemul.h"
76
77#include <dev/sbus/zxreg.h>
78#include <dev/sbus/zxvar.h>
79#include <dev/sbus/sbusvar.h>
80
81#include <dev/wscons/wsconsio.h>
82
83#include "ioconf.h"
84
85#define	ZX_STD_ROP	(ZX_ROP_NEW | ZX_ATTR_WE_ENABLE | \
86    ZX_ATTR_OE_ENABLE | ZX_ATTR_FORCE_WID)
87
88static void	zx_attach(device_t, device_t, void *);
89static int	zx_match(device_t, cfdata_t, void *);
90
91static void	zx_blank(device_t);
92static int	zx_cmap_put(struct zx_softc *);
93static void	zx_copyrect(struct zx_softc *, int, int, int, int, int, int);
94static int	zx_cross_loadwid(struct zx_softc *, u_int, u_int, u_int);
95static int	zx_cross_wait(struct zx_softc *);
96static void	zx_fillrect(struct zx_softc *, int, int, int, int, uint32_t, int);
97static int	zx_intr(void *);
98static void	zx_reset(struct zx_softc *);
99static void	zx_unblank(device_t);
100
101static void	zx_cursor_blank(struct zx_softc *);
102static void	zx_cursor_color(struct zx_softc *);
103static void	zx_cursor_move(struct zx_softc *);
104static void	zx_cursor_set(struct zx_softc *);
105static void	zx_cursor_unblank(struct zx_softc *);
106
107static void	zx_copycols(void *, int, int, int, int);
108static void	zx_copyrows(void *, int, int, int);
109static void	zx_do_cursor(void *, int, int, int);
110static void	zx_erasecols(void *, int, int, int, long);
111static void	zx_eraserows(void *, int, int, long);
112static void	zx_putchar(void *, int, int, u_int, long);
113
114struct zx_mmo {
115	off_t	mo_va;
116	off_t	mo_pa;
117	off_t	mo_size;
118} static const zx_mmo[] = {
119	{ ZX_FB0_VOFF,		ZX_OFF_SS0,		0x00800000 },
120	{ ZX_LC0_VOFF,		ZX_OFF_LC_SS0_USR,	0x00001000 },
121	{ ZX_LD0_VOFF,		ZX_OFF_LD_SS0,		0x00001000 },
122	{ ZX_LX0_CURSOR_VOFF,	ZX_OFF_LX_CURSOR,	0x00001000 },
123	{ ZX_FB1_VOFF,		ZX_OFF_SS1,		0x00800000 },
124	{ ZX_LC1_VOFF,		ZX_OFF_LC_SS1_USR,	0x00001000 },
125	{ ZX_LD1_VOFF,		ZX_OFF_LD_SS1,		0x00001000 },
126	{ ZX_LX_KRN_VOFF,	ZX_OFF_LX_CROSS,	0x00001000 },
127	{ ZX_LC0_KRN_VOFF,	ZX_OFF_LC_SS0_KRN,	0x00001000 },
128	{ ZX_LC1_KRN_VOFF,	ZX_OFF_LC_SS1_KRN,	0x00001000 },
129	{ ZX_LD_GBL_VOFF,	ZX_OFF_LD_GBL,		0x00001000 },
130};
131
132CFATTACH_DECL_NEW(zx, sizeof(struct zx_softc),
133    zx_match, zx_attach, NULL, NULL);
134
135static dev_type_open(zxopen);
136static dev_type_close(zxclose);
137static dev_type_ioctl(zxioctl);
138static dev_type_mmap(zxmmap);
139
140static struct fbdriver zx_fbdriver = {
141	zx_unblank, zxopen, zxclose, zxioctl, nopoll, zxmmap
142};
143
144struct wsscreen_descr zx_defaultscreen = {
145	"std",
146	0, 0,	/* will be filled in -- XXX shouldn't, it's global */
147		/* doesn't matter - you can't really have more than one leo */
148	NULL,		/* textops */
149	8, 16,	/* font width/height */
150	WSSCREEN_WSCOLORS,	/* capabilities */
151	NULL	/* modecookie */
152};
153
154static int 	zx_ioctl(void *, void *, u_long, void *, int, struct lwp *);
155static paddr_t	zx_mmap(void *, void *, off_t, int);
156static void	zx_init_screen(void *, struct vcons_screen *, int, long *);
157
158static int	zx_putcmap(struct zx_softc *, struct wsdisplay_cmap *);
159static int	zx_getcmap(struct zx_softc *, struct wsdisplay_cmap *);
160
161struct wsdisplay_accessops zx_accessops = {
162	zx_ioctl,
163	zx_mmap,
164	NULL,	/* alloc_screen */
165	NULL,	/* free_screen */
166	NULL,	/* show_screen */
167	NULL, 	/* load_font */
168	NULL,	/* pollc */
169	NULL	/* scroll */
170};
171
172const struct wsscreen_descr *_zx_scrlist[] = {
173	&zx_defaultscreen
174};
175
176struct wsscreen_list zx_screenlist = {
177	sizeof(_zx_scrlist) / sizeof(struct wsscreen_descr *),
178	_zx_scrlist
179};
180
181
182extern const u_char rasops_cmap[768];
183
184static struct vcons_screen zx_console_screen;
185
186static int
187zx_match(device_t parent, cfdata_t cf, void *aux)
188{
189	struct sbus_attach_args *sa;
190
191	sa = (struct sbus_attach_args *)aux;
192
193	return (strcmp(sa->sa_name, "SUNW,leo") == 0);
194}
195
196static void
197zx_attach(device_t parent, device_t self, void *args)
198{
199	struct zx_softc *sc;
200	struct sbus_attach_args *sa;
201	bus_space_handle_t bh;
202	bus_space_tag_t bt;
203	struct fbdevice *fb;
204	struct wsemuldisplaydev_attach_args aa;
205	struct rasops_info *ri = &zx_console_screen.scr_ri;
206	unsigned long defattr;
207	int isconsole, width, height;
208
209	sc = device_private(self);
210	sc->sc_dv = self;
211
212	sa = args;
213	fb = &sc->sc_fb;
214	bt = sa->sa_bustag;
215	sc->sc_bt = bt;
216	sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset);
217
218	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_SS0,
219	    0x800000, BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_LARGE, &bh) != 0) {
220		aprint_error_dev(self, "can't map bits\n");
221		return;
222	}
223	fb->fb_pixels = (void *)bus_space_vaddr(bt, bh);
224	sc->sc_pixels = (uint32_t *)fb->fb_pixels;
225
226	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LC_SS0_USR,
227	    PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) {
228		aprint_error_dev(self, "can't map zc\n");
229		return;
230	}
231
232	sc->sc_bhzc = bh;
233
234	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LD_SS0,
235	    PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) {
236		aprint_error_dev(self, "can't map ld/ss0\n");
237		return;
238	}
239	sc->sc_bhzdss0 = bh;
240
241	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LD_SS1,
242	    PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) {
243		aprint_error_dev(self, "can't map ld/ss1\n");
244		return;
245	}
246	sc->sc_bhzdss1 = bh;
247
248	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LX_CROSS,
249	    PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) {
250		aprint_error_dev(self, "can't map zx\n");
251		return;
252	}
253	sc->sc_bhzx = bh;
254
255	if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LX_CURSOR,
256	    PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) {
257		aprint_error_dev(self, "can't map zcu\n");
258		return;
259	}
260	sc->sc_bhzcu = bh;
261
262	fb->fb_driver = &zx_fbdriver;
263	fb->fb_device = sc->sc_dv;
264	fb->fb_flags = device_cfdata(sc->sc_dv)->cf_flags & FB_USERMASK;
265	fb->fb_pfour = NULL;
266	fb->fb_linebytes = prom_getpropint(sa->sa_node, "linebytes", 8192);
267
268	width = prom_getpropint(sa->sa_node, "width", 1280);
269	height = prom_getpropint(sa->sa_node, "height", 1024);
270	fb_setsize_obp(fb, 32, width, height, sa->sa_node);
271
272	fb->fb_type.fb_cmsize = 256;
273	fb->fb_type.fb_depth = 32;
274	fb->fb_type.fb_size = fb->fb_type.fb_height * fb->fb_linebytes;
275	fb->fb_type.fb_type = FBTYPE_SUNLEO;
276
277	printf(": %d x %d", fb->fb_type.fb_width, fb->fb_type.fb_height);
278	isconsole = fb_is_console(sa->sa_node);
279	if (isconsole)
280		printf(" (console)");
281	printf("\n");
282
283	if (sa->sa_nintr != 0)
284		bus_intr_establish(bt, sa->sa_pri, IPL_NONE, zx_intr, sc);
285
286	sc->sc_cmap = malloc(768, M_DEVBUF, M_WAITOK);
287	zx_reset(sc);
288
289	sc->sc_width = fb->fb_type.fb_width;
290	sc->sc_stride = 8192; /* 32 bit */
291	sc->sc_height = fb->fb_type.fb_height;
292
293	/* setup rasops and so on for wsdisplay */
294	wsfont_init();
295	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
296	sc->sc_bg = WS_DEFAULT_BG;
297
298	vcons_init(&sc->vd, sc, &zx_defaultscreen, &zx_accessops);
299	sc->vd.init_screen = zx_init_screen;
300
301	if (isconsole) {
302		/* we mess with zx_console_screen only once */
303		vcons_init_screen(&sc->vd, &zx_console_screen, 1,
304		    &defattr);
305		zx_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
306
307		zx_defaultscreen.textops = &ri->ri_ops;
308		zx_defaultscreen.capabilities = WSSCREEN_WSCOLORS;
309		zx_defaultscreen.nrows = ri->ri_rows;
310		zx_defaultscreen.ncols = ri->ri_cols;
311		zx_fillrect(sc, 0, 0, width, height,
312		     ri->ri_devcmap[defattr >> 16], ZX_STD_ROP);
313		wsdisplay_cnattach(&zx_defaultscreen, ri, 0, 0, defattr);
314		vcons_replay_msgbuf(&zx_console_screen);
315	} else {
316		/*
317		 * we're not the console so we just clear the screen and don't
318		 * set up any sort of text display
319		 */
320		if (zx_defaultscreen.textops == NULL) {
321			/*
322			 * ugly, but...
323			 * we want the console settings to win, so we only
324			 * touch anything when we find an untouched screen
325			 * definition. In this case we fill it from fb to
326			 * avoid problems in case no zx is the console
327			 */
328			zx_defaultscreen.textops = &ri->ri_ops;
329			zx_defaultscreen.capabilities = ri->ri_caps;
330			zx_defaultscreen.nrows = ri->ri_rows;
331			zx_defaultscreen.ncols = ri->ri_cols;
332		}
333	}
334
335	aa.scrdata = &zx_screenlist;
336	aa.console = isconsole;
337	aa.accessops = &zx_accessops;
338	aa.accesscookie = &sc->vd;
339	config_found(sc->sc_dv, &aa, wsemuldisplaydevprint, CFARGS_NONE);
340	fb_attach(&sc->sc_fb, isconsole);
341}
342
343static int
344zxopen(dev_t dev, int flags, int mode, struct lwp *l)
345{
346
347	if (device_lookup(&zx_cd, minor(dev)) == NULL)
348		return (ENXIO);
349	return (0);
350}
351
352static int
353zxclose(dev_t dev, int flags, int mode, struct lwp *l)
354{
355	struct zx_softc *sc;
356
357	sc = device_lookup_private(&zx_cd, minor(dev));
358
359	zx_reset(sc);
360	zx_cursor_blank(sc);
361	return (0);
362}
363
364static int
365zxioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
366{
367	struct zx_softc *sc;
368	struct fbcmap *cm;
369	struct fbcursor *cu;
370	uint32_t curbits[2][32];
371	int rv, v, count, i, error;
372
373	sc = device_lookup_private(&zx_cd, minor(dev));
374
375	switch (cmd) {
376	case FBIOGTYPE:
377		*(struct fbtype *)data = sc->sc_fb.fb_type;
378		break;
379
380	case FBIOGATTR:
381#define fba ((struct fbgattr *)data)
382		fba->real_type = sc->sc_fb.fb_type.fb_type;
383		fba->owner = 0;		/* XXX ??? */
384		fba->fbtype = sc->sc_fb.fb_type;
385		fba->sattr.flags = 0;
386		fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type;
387		fba->sattr.dev_specific[0] = -1;
388		fba->emu_types[0] = sc->sc_fb.fb_type.fb_type;
389		fba->emu_types[1] = -1;
390		fba->emu_types[2] = -1;
391#undef fba
392		break;
393
394	case FBIOGVIDEO:
395		*(int *)data = ((sc->sc_flags & ZX_BLANKED) != 0);
396		break;
397
398	case FBIOSVIDEO:
399		if (*(int *)data)
400			zx_unblank(sc->sc_dv);
401		else
402			zx_blank(sc->sc_dv);
403		break;
404
405	case FBIOGETCMAP:
406		cm = (struct fbcmap *)data;
407		if (cm->index > 256 || cm->count > 256 - cm->index)
408			return (EINVAL);
409		rv = copyout(sc->sc_cmap + cm->index, cm->red, cm->count);
410		if (rv == 0)
411			rv = copyout(sc->sc_cmap + 256 + cm->index, cm->green,
412			    cm->count);
413		if (rv == 0)
414			rv = copyout(sc->sc_cmap + 512 + cm->index, cm->blue,
415			    cm->count);
416		return (rv);
417
418	case FBIOPUTCMAP:
419		cm = (struct fbcmap *)data;
420		if (cm->index > 256 || cm->count > 256 - cm->index)
421			return (EINVAL);
422		rv = copyin(cm->red, sc->sc_cmap + cm->index, cm->count);
423		if (rv == 0)
424			rv = copyin(cm->green, sc->sc_cmap + 256 + cm->index,
425			    cm->count);
426		if (rv == 0)
427			rv = copyin(cm->blue, sc->sc_cmap + 512 + cm->index,
428			    cm->count);
429		zx_cmap_put(sc);
430		return (rv);
431
432	case FBIOGCURPOS:
433		*(struct fbcurpos *)data = sc->sc_curpos;
434		break;
435
436	case FBIOSCURPOS:
437		sc->sc_curpos = *(struct fbcurpos *)data;
438		zx_cursor_move(sc);
439		break;
440
441	case FBIOGCURMAX:
442		((struct fbcurpos *)data)->x = 32;
443		((struct fbcurpos *)data)->y = 32;
444		break;
445
446	case FBIOSCURSOR:
447		cu = (struct fbcursor *)data;
448		v = cu->set;
449
450		if ((v & FB_CUR_SETSHAPE) != 0) {
451			if ((u_int)cu->size.x > 32 || (u_int)cu->size.y > 32)
452				return (EINVAL);
453			count = cu->size.y * 4;
454			rv = copyin(cu->mask, curbits[0], count);
455			if (rv)
456				return rv;
457			rv = copyin(cu->image, curbits[1], count);
458			if (rv)
459				return rv;
460		}
461		if ((v & FB_CUR_SETCUR) != 0) {
462			if (cu->enable)
463				zx_cursor_unblank(sc);
464			else
465				zx_cursor_blank(sc);
466		}
467		if ((v & (FB_CUR_SETPOS | FB_CUR_SETHOT)) != 0) {
468			if ((v & FB_CUR_SETPOS) != 0)
469				sc->sc_curpos = cu->pos;
470			if ((v & FB_CUR_SETHOT) != 0)
471				sc->sc_curhot = cu->hot;
472			zx_cursor_move(sc);
473		}
474		if ((v & FB_CUR_SETCMAP) != 0) {
475			if (cu->cmap.index > 2 ||
476			    cu->cmap.count > 2 - cu->cmap.index)
477				return (EINVAL);
478
479			uint8_t red[2], green[2], blue[2];
480			const u_int cnt = cu->cmap.count;
481
482			if (cnt &&
483			    ((error = copyin(cu->cmap.red,   red,   cnt)) ||
484			     (error = copyin(cu->cmap.green, green, cnt)) ||
485			     (error = copyin(cu->cmap.blue,  blue,  cnt)))) {
486				return error;
487			}
488
489			for (i = 0; i < cnt; i++) {
490				sc->sc_curcmap[i + cu->cmap.index + 0] =
491				    red[i];
492				sc->sc_curcmap[i + cu->cmap.index + 2] =
493				    green[i];
494				sc->sc_curcmap[i + cu->cmap.index + 4] =
495				    blue[i];
496			}
497			zx_cursor_color(sc);
498		}
499		if ((v & FB_CUR_SETSHAPE) != 0) {
500			sc->sc_cursize = cu->size;
501			count = cu->size.y * 4;
502			memset(sc->sc_curbits, 0, sizeof(sc->sc_curbits));
503			memcpy(sc->sc_curbits[0], curbits[0], count);
504			memcpy(sc->sc_curbits[1], curbits[1], count);
505			zx_cursor_set(sc);
506		}
507		break;
508
509	case FBIOGCURSOR:
510		cu = (struct fbcursor *)data;
511
512		cu->set = FB_CUR_SETALL;
513		cu->enable = ((sc->sc_flags & ZX_CURSOR) != 0);
514		cu->pos = sc->sc_curpos;
515		cu->hot = sc->sc_curhot;
516		cu->size = sc->sc_cursize;
517
518		if (cu->image != NULL) {
519			count = sc->sc_cursize.y * 4;
520			rv = copyout(sc->sc_curbits[1], cu->image, count);
521			if (rv)
522				return (rv);
523			rv = copyout(sc->sc_curbits[0], cu->mask, count);
524			if (rv)
525				return (rv);
526		}
527		if (cu->cmap.red != NULL) {
528			uint8_t red[2], green[2], blue[2];
529			const uint8_t *ccm = sc->sc_curcmap;
530			cm = &cu->cmap;
531
532			if (cm->index > 2 || cm->count > 2 - cm->index)
533				return EINVAL;
534
535			for (i = 0; i < cm->count; i++) {
536				red[i] = ccm[i + cm->index + 0];
537				green[i] = ccm[i + cm->index + 2];
538				blue[i] = ccm[i + cm->index + 4];
539			}
540
541			if ((error = copyout(red, cm->red, cm->count)) ||
542			    (error = copyout(green, cm->green, cm->count)) ||
543			    (error = copyout(blue, cm->blue, cm->count)))
544				return error;
545		} else {
546			cu->cmap.index = 0;
547			cu->cmap.count = 2;
548		}
549		break;
550
551	default:
552#ifdef DEBUG
553		log(LOG_NOTICE, "zxioctl(0x%lx) (%s[%d])\n", cmd,
554		    l->l_proc->p_comm, l->l_proc->p_pid);
555#endif
556		return (ENOTTY);
557	}
558
559	return (0);
560}
561
562static int
563zx_intr(void *cookie)
564{
565
566	return (1);
567}
568
569static void
570zx_reset(struct zx_softc *sc)
571{
572	struct fbtype *fbt;
573	u_int i;
574
575	fbt = &sc->sc_fb.fb_type;
576
577	zx_cross_loadwid(sc, ZX_WID_DBL_8, 0, 0x2c0);
578	zx_cross_loadwid(sc, ZX_WID_DBL_8, 1, 0x30);
579	zx_cross_loadwid(sc, ZX_WID_DBL_8, 2, 0x20);
580	zx_cross_loadwid(sc, ZX_WID_DBL_24, 1, 0x30);
581
582	i = bus_space_read_4(sc->sc_bt, sc->sc_bhzdss1, zd_misc);
583	i |= ZX_SS1_MISC_ENABLE;
584	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss1, zd_misc, i);
585
586	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_wid, 1);
587	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_widclip, 0);
588	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_wmask, 0xffff);
589	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_vclipmin, 0);
590	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_vclipmax,
591	    (fbt->fb_width - 1) | ((fbt->fb_height - 1) << 16));
592	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_fg, 0);
593	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_planemask, 0xffffffff);
594	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_rop, ZX_STD_ROP);
595
596	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_extent,
597	    (fbt->fb_width - 1) | ((fbt->fb_height - 1) << 11));
598	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_addrspace,
599	    ZX_ADDRSPC_FONT_OBGR);
600	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_fontt, 0);
601
602	for (i = 0; i < 256; i++) {
603		sc->sc_cmap[i] = rasops_cmap[i * 3];
604		sc->sc_cmap[i + 256] = rasops_cmap[i * 3 + 1];
605		sc->sc_cmap[i + 512] = rasops_cmap[i * 3 + 2];
606	}
607
608	zx_cmap_put(sc);
609}
610
611static int
612zx_cross_wait(struct zx_softc *sc)
613{
614	int i;
615
616	for (i = 300000; i != 0; i--) {
617		if ((bus_space_read_4(sc->sc_bt, sc->sc_bhzx, zx_csr) &
618		    ZX_CROSS_CSR_PROGRESS) == 0)
619			break;
620		DELAY(1);
621	}
622
623	if (i == 0)
624		printf("zx_cross_wait: timed out\n");
625
626	return (i);
627}
628
629static int
630zx_cross_loadwid(struct zx_softc *sc, u_int type, u_int index, u_int value)
631{
632	u_int tmp = 0;
633
634	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_WID);
635
636	if (zx_cross_wait(sc))
637		return (1);
638
639	if (type == ZX_WID_DBL_8)
640		tmp = (index & 0x0f) + 0x40;
641	else if (type == ZX_WID_DBL_24)
642		tmp = index & 0x3f;
643
644	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, 0x5800 + tmp);
645	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_value, value);
646	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_WID);
647	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_csr,
648	    ZX_CROSS_CSR_UNK | ZX_CROSS_CSR_UNK2);
649
650	return (0);
651}
652
653static int
654zx_cmap_put(struct zx_softc *sc)
655{
656	const u_char *b;
657	u_int i, t;
658
659	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_CLUT0);
660
661	zx_cross_wait(sc);
662
663	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type,
664	    ZX_CROSS_TYPE_CLUTDATA);
665
666	for (i = 0, b = sc->sc_cmap; i < 256; i++) {
667		t = b[i];
668		t |= b[i + 256] << 8;
669		t |= b[i + 512] << 16;
670		bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_value, t);
671	}
672
673	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_CLUT0);
674	i = bus_space_read_4(sc->sc_bt, sc->sc_bhzx, zx_csr);
675	i = i | ZX_CROSS_CSR_UNK | ZX_CROSS_CSR_UNK2;
676	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_csr, i);
677	return (0);
678}
679
680static void
681zx_cursor_move(struct zx_softc *sc)
682{
683	int sx, sy, x, y;
684
685	x = sc->sc_curpos.x - sc->sc_curhot.x;
686	y = sc->sc_curpos.y - sc->sc_curhot.y;
687
688	if (x < 0) {
689		sx = uimin(-x, 32);
690		x = 0;
691	} else
692		sx = 0;
693
694	if (y < 0) {
695		sy = uimin(-y, 32);
696		y = 0;
697	} else
698		sy = 0;
699
700	if (sx != sc->sc_shiftx || sy != sc->sc_shifty) {
701		sc->sc_shiftx = sx;
702		sc->sc_shifty = sy;
703		zx_cursor_set(sc);
704	}
705
706	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_sxy,
707	    ((y & 0x7ff) << 11) | (x & 0x7ff));
708	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc,
709	    bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x30);
710
711	/* XXX Necessary? */
712	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc,
713	    bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x80);
714}
715
716static void
717zx_cursor_set(struct zx_softc *sc)
718{
719	int i, j, data;
720
721	if ((sc->sc_flags & ZX_CURSOR) != 0)
722		bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc,
723		    bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) &
724		    ~0x80);
725
726	for (j = 0; j < 2; j++) {
727		bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_type, 0x20 << j);
728
729		for (i = sc->sc_shifty; i < 32; i++) {
730			data = sc->sc_curbits[j][i];
731			bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_data,
732			    data >> sc->sc_shiftx);
733		}
734		for (i = sc->sc_shifty; i != 0; i--)
735			bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_data, 0);
736	}
737
738	if ((sc->sc_flags & ZX_CURSOR) != 0)
739		bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc,
740		    bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x80);
741}
742
743static void
744zx_cursor_blank(struct zx_softc *sc)
745{
746
747	sc->sc_flags &= ~ZX_CURSOR;
748	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc,
749	    bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) & ~0x80);
750}
751
752static void
753zx_cursor_unblank(struct zx_softc *sc)
754{
755
756	sc->sc_flags |= ZX_CURSOR;
757	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc,
758	    bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x80);
759}
760
761static void
762zx_cursor_color(struct zx_softc *sc)
763{
764	uint8_t tmp;
765
766	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_type, 0x50);
767
768	tmp = sc->sc_curcmap[0] | (sc->sc_curcmap[2] << 8) |
769	    (sc->sc_curcmap[4] << 16);
770	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_data, tmp);
771
772	tmp = sc->sc_curcmap[1] | (sc->sc_curcmap[3] << 8) |
773	    (sc->sc_curcmap[5] << 16);
774	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_data, tmp);
775
776	bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc,
777	    bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x03);
778}
779
780static void
781zx_blank(device_t dv)
782{
783	struct zx_softc *sc;
784
785	sc = device_private(dv);
786
787	if ((sc->sc_flags & ZX_BLANKED) != 0)
788		return;
789	sc->sc_flags |= ZX_BLANKED;
790
791	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_VIDEO);
792	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_csr,
793	    bus_space_read_4(sc->sc_bt, sc->sc_bhzx, zx_csr) &
794	    ~ZX_CROSS_CSR_ENABLE);
795}
796
797static void
798zx_unblank(device_t dv)
799{
800	struct zx_softc *sc;
801
802	sc = device_private(dv);
803
804	if ((sc->sc_flags & ZX_BLANKED) == 0)
805		return;
806	sc->sc_flags &= ~ZX_BLANKED;
807
808	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_VIDEO);
809	bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_csr,
810	    bus_space_read_4(sc->sc_bt, sc->sc_bhzx, zx_csr) |
811	    ZX_CROSS_CSR_ENABLE);
812}
813
814static paddr_t
815zxmmap(dev_t dev, off_t off, int prot)
816{
817	struct zx_softc *sc;
818	const struct zx_mmo *mm, *mmmax;
819
820	sc = device_lookup_private(&zx_cd, minor(dev));
821	off = trunc_page(off);
822	mm = zx_mmo;
823	mmmax = mm + sizeof(zx_mmo) / sizeof(zx_mmo[0]);
824
825	for (; mm < mmmax; mm++)
826		if (off >= mm->mo_va && off < mm->mo_va + mm->mo_size) {
827			off = off - mm->mo_va + mm->mo_pa;
828			return (bus_space_mmap(sc->sc_bt, sc->sc_paddr,
829			    off, prot, BUS_SPACE_MAP_LINEAR));
830		}
831
832	return (-1);
833}
834
835static void
836zx_fillrect(struct zx_softc *sc, int x, int y, int w, int h, uint32_t bg,
837	    int rop)
838{
839
840
841	while ((bus_space_read_4(sc->sc_bt, sc->sc_bhzc, zc_csr) &
842	    ZX_CSR_BLT_BUSY) != 0)
843		;
844
845	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_rop, rop);
846	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_fg, bg);
847	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_extent,
848	    (w - 1) | ((h - 1) << 11));
849	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_fill,
850	    x | (y << 11) | 0x80000000);
851}
852
853static void
854zx_copyrect(struct zx_softc *sc, int sx, int sy, int dx, int dy, int w,
855	    int h)
856{
857	uint32_t dir;
858
859	w -= 1;
860	h -= 1;
861
862	if (sy < dy || sx < dx) {
863		dir = 0x80000000;
864		sx += w;
865		sy += h;
866		dx += w;
867		dy += h;
868	} else
869		dir = 0;
870
871	while ((bus_space_read_4(sc->sc_bt, sc->sc_bhzc, zc_csr) &
872	    ZX_CSR_BLT_BUSY) != 0)
873		;
874
875	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_rop, ZX_STD_ROP);
876	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_extent,
877	    w | (h << 11) | dir);
878	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_src, sx | (sy << 11));
879	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_copy, dx | (dy << 11));
880}
881
882static void
883zx_do_cursor(void *cookie, int on, int row, int col)
884{
885	struct rasops_info *ri = cookie;
886	struct vcons_screen *scr = ri->ri_hw;
887	struct zx_softc *sc = scr->scr_cookie;
888	int x, y, wi, he;
889
890	wi = ri->ri_font->fontwidth;
891	he = ri->ri_font->fontheight;
892
893	if (ri->ri_flg & RI_CURSOR) {
894		x = ri->ri_ccol * wi + ri->ri_xorigin;
895		y = ri->ri_crow * he + ri->ri_yorigin;
896		zx_fillrect(sc, x, y, wi, he, 0xff000000,
897		  ZX_ROP_NEW_XOR_OLD | ZX_ATTR_WE_ENABLE | ZX_ATTR_OE_ENABLE |
898		  ZX_ATTR_FORCE_WID);
899		ri->ri_flg &= ~RI_CURSOR;
900	}
901
902	ri->ri_crow = row;
903	ri->ri_ccol = col;
904
905	if (on)
906	{
907		x = ri->ri_ccol * wi + ri->ri_xorigin;
908		y = ri->ri_crow * he + ri->ri_yorigin;
909		zx_fillrect(sc, x, y, wi, he, 0xff000000,
910		  ZX_ROP_NEW_XOR_OLD | ZX_ATTR_WE_ENABLE | ZX_ATTR_OE_ENABLE |
911		  ZX_ATTR_FORCE_WID);
912		ri->ri_flg |= RI_CURSOR;
913	}
914}
915
916static void
917zx_erasecols(void *cookie, int row, int startcol, int ncols, long attr)
918{
919	struct rasops_info *ri = cookie;
920	struct vcons_screen *scr = ri->ri_hw;
921	struct zx_softc *sc = scr->scr_cookie;
922	int32_t x, y, width, height, bg;
923
924	x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
925	y = ri->ri_yorigin + ri->ri_font->fontheight * row;
926	width = ri->ri_font->fontwidth * ncols;
927	height = ri->ri_font->fontheight;
928	bg = ((uint32_t)ri->ri_devcmap[(attr >> 16) & 0xff]) << 24;
929	zx_fillrect(sc, x, y, width, height, bg, ZX_STD_ROP);
930}
931
932static void
933zx_eraserows(void *cookie, int row, int nrows, long attr)
934{
935	struct rasops_info *ri = cookie;
936	struct vcons_screen *scr = ri->ri_hw;
937	struct zx_softc *sc = scr->scr_cookie;
938	int32_t x, y, width, height, bg;
939
940	if ((row == 0) && (nrows == ri->ri_rows)) {
941		x = y = 0;
942		width = ri->ri_width;
943		height = ri->ri_height;
944	} else {
945		x = ri->ri_xorigin;
946		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
947		width = ri->ri_emuwidth;
948		height = ri->ri_font->fontheight * nrows;
949	}
950	bg = ((uint32_t)ri->ri_devcmap[(attr >> 16) & 0xff]) << 24;
951	zx_fillrect(sc, x, y, width, height, bg, ZX_STD_ROP);
952}
953
954static void
955zx_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
956{
957	struct rasops_info *ri = cookie;
958	struct vcons_screen *scr = ri->ri_hw;
959	struct zx_softc *sc = scr->scr_cookie;
960	int32_t x, ys, yd, width, height;
961
962	x = ri->ri_xorigin;
963	ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
964	yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
965	width = ri->ri_emuwidth;
966	height = ri->ri_font->fontheight * nrows;
967	zx_copyrect(sc, x, ys, x, yd, width, height);
968}
969
970static void
971zx_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
972{
973	struct rasops_info *ri = cookie;
974	struct vcons_screen *scr = ri->ri_hw;
975	struct zx_softc *sc = scr->scr_cookie;
976	int32_t xs, xd, y, width, height;
977
978	xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
979	xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
980	y = ri->ri_yorigin + ri->ri_font->fontheight * row;
981	width = ri->ri_font->fontwidth * ncols;
982	height = ri->ri_font->fontheight;
983	zx_copyrect(sc, xs, y, xd, y, width, height);
984}
985
986static void
987zx_putchar(void *cookie, int row, int col, u_int uc, long attr)
988{
989	struct rasops_info *ri = cookie;
990	struct wsdisplay_font *font = PICK_FONT(ri, uc);
991	struct vcons_screen *scr = ri->ri_hw;
992	struct zx_softc *sc = scr->scr_cookie;
993	volatile uint32_t *dp;
994	uint8_t *fb;
995	int fs, i, ul;
996	uint32_t fg, bg;
997
998	rasops_unpack_attr(attr, &fg, &bg, &ul);
999	bg = ((uint32_t)ri->ri_devcmap[bg]) << 24;
1000	fg = ((uint32_t)ri->ri_devcmap[fg]) << 24;
1001	if (uc == ' ') {
1002		int x, y;
1003
1004		x = ri->ri_xorigin + font->fontwidth * col;
1005		y = ri->ri_yorigin + font->fontheight * row;
1006		zx_fillrect(sc, x, y, font->fontwidth,
1007			    font->fontheight, bg, ZX_STD_ROP);
1008		return;
1009	}
1010
1011	dp = (volatile uint32_t *)sc->sc_pixels +
1012	    ((row * font->fontheight + ri->ri_yorigin) << 11) +
1013	    (col * font->fontwidth + ri->ri_xorigin);
1014	fb = (uint8_t *)font->data + (uc - font->firstchar) *
1015	    ri->ri_fontscale;
1016	fs = font->stride;
1017
1018	while ((bus_space_read_4(sc->sc_bt, sc->sc_bhzc, zc_csr) &
1019	    ZX_CSR_BLT_BUSY) != 0)
1020		;
1021
1022	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_rop, ZX_STD_ROP);
1023	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_fg, fg);
1024	bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_bg, bg);
1025	bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_fontmsk,
1026	    0xffffffff << (32 - font->fontwidth));
1027
1028	if (font->fontwidth <= 8) {
1029		for (i = font->fontheight; i != 0; i--, dp += 2048) {
1030			*dp = *fb << 24;
1031			fb += fs;
1032		}
1033	} else {
1034		for (i = font->fontheight; i != 0; i--, dp += 2048) {
1035			*dp = *((uint16_t *)fb) << 16;
1036			fb += fs;
1037		}
1038	}
1039
1040	if (ul) {
1041		dp -= 4096;
1042		*dp = 0xffffffff;
1043	}
1044}
1045
1046static int
1047zx_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
1048	struct lwp *l)
1049{
1050	/* we'll probably need to add more stuff here */
1051	struct vcons_data *vd = v;
1052	struct zx_softc *sc = vd->cookie;
1053	struct wsdisplay_fbinfo *wdf;
1054	struct vcons_screen *ms = sc->vd.active;
1055	struct rasops_info *ri = &ms->scr_ri;
1056	switch (cmd) {
1057		case WSDISPLAYIO_GTYPE:
1058			*(u_int *)data = WSDISPLAY_TYPE_SUNTCX;
1059			return 0;
1060		case WSDISPLAYIO_GINFO:
1061			wdf = (void *)data;
1062			wdf->height = ri->ri_height;
1063			wdf->width = ri->ri_width;
1064			wdf->depth = ri->ri_depth;
1065			wdf->cmsize = 256;
1066			return 0;
1067
1068		case WSDISPLAYIO_GETCMAP:
1069			return zx_getcmap(sc,
1070			    (struct wsdisplay_cmap *)data);
1071		case WSDISPLAYIO_PUTCMAP:
1072			return zx_putcmap(sc,
1073			    (struct wsdisplay_cmap *)data);
1074
1075		case WSDISPLAYIO_SMODE:
1076			{
1077				int new_mode = *(int*)data;
1078				if (new_mode != sc->sc_mode)
1079				{
1080					sc->sc_mode = new_mode;
1081					if(new_mode == WSDISPLAYIO_MODE_EMUL)
1082					{
1083						zx_reset(sc);
1084						vcons_redraw_screen(ms);
1085					}
1086				}
1087			}
1088			return 0;
1089	}
1090	return EPASSTHROUGH;
1091}
1092
1093static paddr_t
1094zx_mmap(void *v, void *vs, off_t offset, int prot)
1095{
1096	/* I'm not at all sure this is the right thing to do */
1097	return zxmmap(0, offset, prot); /* assume minor dev 0 for now */
1098}
1099
1100static int
1101zx_putcmap(struct zx_softc *sc, struct wsdisplay_cmap *cm)
1102{
1103	u_int index = cm->index;
1104	u_int count = cm->count;
1105	int error,i;
1106	if (index >= 256 || count > 256 || index + count > 256)
1107		return EINVAL;
1108
1109	for (i = 0; i < count; i++)
1110	{
1111		error = copyin(&cm->red[i],
1112		    &sc->sc_cmap[index + i], 1);
1113		if (error)
1114			return error;
1115		error = copyin(&cm->green[i],
1116		    &sc->sc_cmap[index + i + 256], 1);
1117		if (error)
1118			return error;
1119		error = copyin(&cm->blue[i],
1120		    &sc->sc_cmap[index + i + 512], 1);
1121		if (error)
1122			return error;
1123	}
1124	zx_cmap_put(sc);
1125
1126	return 0;
1127}
1128
1129static int
1130zx_getcmap(struct zx_softc *sc, struct wsdisplay_cmap *cm)
1131{
1132	u_int index = cm->index;
1133	u_int count = cm->count;
1134	int error,i;
1135
1136	if (index >= 256 || count > 256 || index + count > 256)
1137		return EINVAL;
1138
1139	for (i = 0; i < count; i++)
1140	{
1141		error = copyout(&sc->sc_cmap[index + i],
1142		    &cm->red[i], 1);
1143		if (error)
1144			return error;
1145		error = copyout(&sc->sc_cmap[index + i + 256],
1146		    &cm->green[i], 1);
1147		if (error)
1148			return error;
1149		error = copyout(&sc->sc_cmap[index + i + 256],
1150		    &cm->blue[i], 1);
1151		if (error)
1152			return error;
1153	}
1154
1155	return 0;
1156}
1157
1158static void
1159zx_init_screen(void *cookie, struct vcons_screen *scr,
1160    int existing, long *defattr)
1161{
1162	struct zx_softc *sc = cookie;
1163	struct rasops_info *ri = &scr->scr_ri;
1164
1165	ri->ri_depth = 8; /*sc->sc_fb.fb_type.fb_depth = 32;*/
1166	ri->ri_width = sc->sc_width;
1167	ri->ri_height = sc->sc_height;
1168	ri->ri_stride = sc->sc_stride;
1169	ri->ri_flg = RI_CENTER;
1170
1171	ri->ri_bits = (void *)sc->sc_pixels;
1172
1173	rasops_init(ri, 0, 0);
1174	ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_REVERSE;
1175	rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
1176		    sc->sc_width / ri->ri_font->fontwidth);
1177
1178	ri->ri_hw = scr;
1179
1180	ri->ri_ops.cursor = zx_do_cursor;
1181	ri->ri_ops.copycols = zx_copycols;
1182	ri->ri_ops.copyrows = zx_copyrows;
1183	ri->ri_ops.erasecols = zx_erasecols;
1184	ri->ri_ops.eraserows = zx_eraserows;
1185	ri->ri_ops.putchar = zx_putchar;
1186}
1187