1/*	$NetBSD: ite_tv.c,v 1.15 2007/03/11 06:01:05 isaki Exp $	*/
2
3/*
4 * Copyright (c) 1997 Masaru Oki.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *      This product includes software developed by Masaru Oki.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__KERNEL_RCSID(0, "$NetBSD: ite_tv.c,v 1.15 2007/03/11 06:01:05 isaki Exp $");
35
36#include <sys/param.h>
37#include <sys/device.h>
38#include <sys/proc.h>
39#include <sys/systm.h>
40
41#include <machine/bus.h>
42#include <machine/grfioctl.h>
43
44#include <arch/x68k/x68k/iodevice.h>
45#include <arch/x68k/dev/itevar.h>
46#include <arch/x68k/dev/grfvar.h>
47#include <arch/x68k/dev/mfp.h>
48
49/*
50 * ITE device dependent routine for X680x0 Text-Video framebuffer.
51 * Use X680x0 ROM fixed width font (8x16)
52 */
53
54#define CRTC    (IODEVbase->io_crtc)
55
56/*
57 * font constant
58 */
59#define FONTWIDTH   8
60#define FONTHEIGHT  16
61#define UNDERLINE   14
62
63/*
64 * framebuffer constant
65 */
66#define PLANEWIDTH  1024
67#define PLANEHEIGHT 1024
68#define PLANELINES  (PLANEHEIGHT / FONTHEIGHT)
69#define ROWBYTES    (PLANEWIDTH  / FONTWIDTH)
70#define PLANESIZE   (PLANEHEIGHT * ROWBYTES)
71
72u_int  tv_top;
73u_char *tv_row[PLANELINES];
74char   *tv_font[256];
75volatile char *tv_kfont[0x7f];
76
77u_char kern_font[256 * FONTHEIGHT];
78
79#define PHYSLINE(y)  ((tv_top + (y)) % PLANELINES)
80#define ROWOFFSET(y) ((y) * FONTHEIGHT * ROWBYTES)
81#define CHADDR(y, x) (tv_row[PHYSLINE(y)] + (x))
82
83#define SETGLYPH(to,from) memcpy(&kern_font[(from)*16],&kern_font[(to)*16], 16)
84#define KFONTBASE(left)   ((left) * 32 * 0x5e - 0x21 * 32)
85
86/* prototype */
87void tv_init(struct ite_softc *);
88void tv_deinit(struct ite_softc *);
89void tv_putc(struct ite_softc *, int, int, int, int);
90void tv_cursor(struct ite_softc *, int);
91void tv_clear(struct ite_softc *, int, int, int, int);
92void tv_scroll(struct ite_softc *, int, int, int, int);
93
94inline static int expbits(int);
95inline static void txrascpy(u_char, u_char, short, signed short);
96
97static inline void
98txrascpy(u_char src, u_char dst, short size, short mode)
99{
100	/*int s;*/
101	u_short saved_r21 = CRTC.r21;
102	char d;
103
104	d = (mode < 0) ? -1 : 1;
105	src *= FONTHEIGHT / 4;
106	dst *= FONTHEIGHT / 4;
107	size *= 4;
108	if (d < 0) {
109		src += (FONTHEIGHT / 4) - 1;
110		dst += (FONTHEIGHT / 4) - 1;
111	}
112
113	/* specify same time write mode & page */
114	CRTC.r21 = (mode & 0x0f) | 0x0100;
115	/*mfp.ddr = 0;*/			/* port is input */
116
117	/*s = splhigh();*/
118	while (--size >= 0) {
119		/* wait for hsync */
120		mfp_wait_for_hsync ();
121		CRTC.r22 = (src << 8) | dst;	/* specify raster number */
122		/* start raster copy */
123		CRTC.crtctrl = 8;
124
125		src += d;
126		dst += d;
127	}
128	/*splx(s);*/
129
130	/* wait for hsync */
131	mfp_wait_for_hsync ();
132
133	/* stop raster copy */
134	CRTC.crtctrl = 0;
135
136	CRTC.r21 = saved_r21;
137}
138
139/*
140 * Change glyphs from SRAM switch.
141 */
142void
143ite_set_glyph(void)
144{
145	u_char glyph = IODEVbase->io_sram[0x59];
146
147	if (glyph & 4)
148		SETGLYPH(0x82, '|');
149	if (glyph & 2)
150		SETGLYPH(0x81, '~');
151	if (glyph & 1)
152		SETGLYPH(0x80, '\\');
153}
154
155/*
156 * Initialize
157 */
158void
159tv_init(struct ite_softc *ip)
160{
161	short i;
162
163	/*
164	 * initialize private variables
165	 */
166	tv_top = 0;
167	for (i = 0; i < PLANELINES; i++)
168		tv_row[i] = (void *)__UNVOLATILE(&IODEVbase->tvram[ROWOFFSET(i)]);
169	/* shadow ANK font */
170	memcpy(kern_font, (void *)&IODEVbase->cgrom0_8x16, 256 * FONTHEIGHT);
171	ite_set_glyph();
172	/* set font address cache */
173	for (i = 0; i < 256; i++)
174		tv_font[i] = &kern_font[i * FONTHEIGHT];
175	for (i = 0x21; i < 0x30; i++)
176		tv_kfont[i] = &IODEVbase->cgrom0_16x16[KFONTBASE(i-0x21)];
177	for (; i < 0x50; i++)
178		tv_kfont[i] = &IODEVbase->cgrom1_16x16[KFONTBASE(i-0x30)];
179	for (; i < 0x7f; i++)
180		tv_kfont[i] = &IODEVbase->cgrom2_16x16[KFONTBASE(i-0x50)];
181
182	/*
183	 * initialize part of ip
184	 */
185	ip->cols = ip->grf->g_display.gd_dwidth  / FONTWIDTH;
186	ip->rows = ip->grf->g_display.gd_dheight / FONTHEIGHT;
187	/* set draw routine dynamically */
188	ip->isw->ite_putc   = tv_putc;
189	ip->isw->ite_cursor = tv_cursor;
190	ip->isw->ite_clear  = tv_clear;
191	ip->isw->ite_scroll = tv_scroll;
192
193	/*
194	 * Intialize colormap
195	 */
196#define RED   (0x1f << 6)
197#define BLUE  (0x1f << 1)
198#define GREEN (0x1f << 11)
199	IODEVbase->tpalet[0] = 0;			/* black */
200	IODEVbase->tpalet[1] = 1 | RED;			/* red */
201	IODEVbase->tpalet[2] = 1 | GREEN;		/* green */
202	IODEVbase->tpalet[3] = 1 | RED | GREEN;		/* yellow */
203	IODEVbase->tpalet[4] = 1 | BLUE;		/* blue */
204	IODEVbase->tpalet[5] = 1 | BLUE | RED;		/* magenta */
205	IODEVbase->tpalet[6] = 1 | BLUE | GREEN;	/* cyan */
206	IODEVbase->tpalet[7] = 1 | BLUE | RED | GREEN;	/* white */
207}
208
209/*
210 * Deinitialize
211 */
212void
213tv_deinit(struct ite_softc *ip)
214{
215	ip->flags &= ~ITE_INITED; /* XXX? */
216}
217
218typedef void tv_putcfunc(struct ite_softc *, int, char *);
219static tv_putcfunc tv_putc_nm;
220static tv_putcfunc tv_putc_in;
221static tv_putcfunc tv_putc_ul;
222static tv_putcfunc tv_putc_ul_in;
223static tv_putcfunc tv_putc_bd;
224static tv_putcfunc tv_putc_bd_in;
225static tv_putcfunc tv_putc_bd_ul;
226static tv_putcfunc tv_putc_bd_ul_in;
227
228static tv_putcfunc *putc_func[ATTR_ALL + 1] = {
229	tv_putc_nm,
230	tv_putc_in,
231	tv_putc_ul,
232	tv_putc_ul_in,
233	tv_putc_bd,
234	tv_putc_bd_in,
235	tv_putc_bd_ul,
236	tv_putc_bd_ul_in,
237	/* no support for blink */
238	tv_putc_nm,
239	tv_putc_in,
240	tv_putc_ul,
241	tv_putc_ul_in,
242	tv_putc_bd,
243	tv_putc_bd_in,
244	tv_putc_bd_ul,
245	tv_putc_bd_ul_in,
246};
247
248/*
249 * simple put character function
250 */
251void
252tv_putc(struct ite_softc *ip, int ch, int y, int x, int mode)
253{
254	char *p = CHADDR(y, x);
255	short fh;
256
257	/* multi page write mode */
258	CRTC.r21 = 0x0100 | ip->fgcolor << 4;
259
260	/* draw plane */
261	putc_func[mode](ip, ch, p);
262
263	/* erase plane */
264	CRTC.r21 ^= 0x00f0;
265	if (ip->save_char) {
266		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
267			*(u_short *)p = 0;
268	} else {
269		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
270			*p = 0;
271	}
272
273	/* crtc mode reset */
274	CRTC.r21 = 0;
275}
276
277void
278tv_putc_nm(struct ite_softc *ip, int ch, char *p)
279{
280	short fh, hi;
281	char *f;
282	volatile short *kf;
283
284	hi = ip->save_char & 0x7f;
285
286	if (hi >= 0x21 && hi <= 0x7e && ch >= 0x21 && ch <= 0x7e) {
287		/* multibyte character */
288		kf = (volatile short *)tv_kfont[hi];
289		kf += ch * FONTHEIGHT;
290		/* draw plane */
291		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
292			*(u_short *)p = *kf++;
293		return;
294	}
295
296	/* singlebyte character */
297	if (*ip->GL == CSET_JISKANA)
298		ch |= 0x80;
299	f = tv_font[ch];
300
301	/* draw plane */
302	for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
303		*p = *f++;
304}
305
306void
307tv_putc_in(struct ite_softc *ip, int ch, char *p)
308{
309	short fh, hi;
310	char *f;
311	volatile short *kf;
312
313	hi = ip->save_char & 0x7f;
314
315	if (hi >= 0x21 && hi <= 0x7e && ch >= 0x21 && ch <= 0x7e) {
316		/* multibyte character */
317		kf = (volatile short *)tv_kfont[hi];
318		kf += ch * FONTHEIGHT;
319		/* draw plane */
320		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
321			*(u_short *)p = ~*kf++;
322		return;
323	}
324
325	/* singlebyte character */
326	if (*ip->GL == CSET_JISKANA)
327		ch |= 0x80;
328	f = tv_font[ch];
329
330	/* draw plane */
331	for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
332		*p = ~*f++;
333}
334
335void
336tv_putc_bd(struct ite_softc *ip, int ch, char *p)
337{
338	short fh, hi;
339	char *f;
340	volatile short *kf;
341
342	hi = ip->save_char & 0x7f;
343
344	if (hi >= 0x21 && hi <= 0x7e && ch >= 0x21 && ch <= 0x7e) {
345		/* multibyte character */
346		kf = (volatile short *)tv_kfont[hi];
347		kf += ch * FONTHEIGHT;
348		/* draw plane */
349		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES) {
350			ch = *kf++;
351			*(u_short *)p = ch | (ch >> 1);
352		}
353		return;
354	}
355
356	/* singlebyte character */
357	if (*ip->GL == CSET_JISKANA)
358		ch |= 0x80;
359	f = tv_font[ch];
360
361	/* draw plane */
362	for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES) {
363		ch = *f++;
364		*p = ch | (ch >> 1);
365	}
366}
367
368inline static int
369expbits(int data)
370{
371	int i, nd = 0;
372	if (data & 1)
373		nd |= 0x02;
374	for (i=1; i < 32; i++) {
375		if (data & (1 << i))
376			nd |= 0x5 << (i-1);
377	}
378	nd &= ~data;
379	return (~nd);
380}
381
382void
383tv_putc_ul(struct ite_softc *ip, int ch, char *p)
384{
385	short fh, hi;
386	char *f;
387	volatile short *kf;
388
389	hi = ip->save_char & 0x7f;
390
391	if (hi >= 0x21 && hi <= 0x7e && ch >= 0x21 && ch <= 0x7e) {
392		/* multibyte character */
393		kf = (volatile short *)tv_kfont[hi];
394		kf += ch * FONTHEIGHT;
395		/* draw plane */
396		for (fh = 0; fh < UNDERLINE; fh++, p += ROWBYTES)
397			*(u_short *)p = *kf++;
398		*(u_short *)p = expbits(*kf++);
399		p += ROWBYTES;
400		for (fh++; fh < FONTHEIGHT; fh++, p += ROWBYTES)
401			*(u_short *)p = *kf++;
402		return;
403	}
404
405	/* singlebyte character */
406	if (*ip->GL == CSET_JISKANA)
407		ch |= 0x80;
408	f = tv_font[ch];
409
410	/* draw plane */
411	for (fh = 0; fh < UNDERLINE; fh++, p += ROWBYTES)
412		*p = *f++;
413	*p = expbits(*f++);
414	p += ROWBYTES;
415	for (fh++; fh < FONTHEIGHT; fh++, p += ROWBYTES)
416		*p = *f++;
417}
418
419void
420tv_putc_bd_in(struct ite_softc *ip, int ch, char *p)
421{
422	short fh, hi;
423	char *f;
424	volatile short *kf;
425
426	hi = ip->save_char & 0x7f;
427
428	if (hi >= 0x21 && hi <= 0x7e && ch >= 0x21 && ch <= 0x7e) {
429		/* multibyte character */
430		kf = (volatile short *)tv_kfont[hi];
431		kf += ch * FONTHEIGHT;
432		/* draw plane */
433		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES) {
434			ch = *kf++;
435			*(u_short *)p = ~(ch | (ch >> 1));
436		}
437		return;
438	}
439
440	/* singlebyte character */
441	if (*ip->GL == CSET_JISKANA)
442		ch |= 0x80;
443	f = tv_font[ch];
444
445	/* draw plane */
446	for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES) {
447		ch = *f++;
448		*p = ~(ch | (ch >> 1));
449	}
450}
451
452void
453tv_putc_ul_in(struct ite_softc *ip, int ch, char *p)
454{
455	short fh, hi;
456	char *f;
457	volatile short *kf;
458
459	hi = ip->save_char & 0x7f;
460
461	if (hi >= 0x21 && hi <= 0x7e && ch >= 0x21 && ch <= 0x7e) {
462		/* multibyte character */
463		kf = (volatile short *)tv_kfont[hi];
464		kf += ch * FONTHEIGHT;
465		/* draw plane */
466		for (fh = 0; fh < UNDERLINE; fh++, p += ROWBYTES)
467			*(u_short *)p = ~*kf++;
468		*(u_short *)p = ~expbits(*kf++);
469		p += ROWBYTES;
470		for (fh++; fh < FONTHEIGHT; fh++, p += ROWBYTES)
471			*(u_short *)p = ~*kf++;
472		return;
473	}
474
475	/* singlebyte character */
476	if (*ip->GL == CSET_JISKANA)
477		ch |= 0x80;
478	f = tv_font[ch];
479
480	/* draw plane */
481	for (fh = 0; fh < UNDERLINE; fh++, p += ROWBYTES)
482		*p = ~*f++;
483	*p = ~expbits(*f++);
484	p += ROWBYTES;
485	for (fh++; fh < FONTHEIGHT; fh++, p += ROWBYTES)
486		*p = ~*f++;
487}
488
489void
490tv_putc_bd_ul(struct ite_softc *ip, int ch, char *p)
491{
492	short fh, hi;
493	char *f;
494	volatile short *kf;
495
496	hi = ip->save_char & 0x7f;
497
498	if (hi >= 0x21 && hi <= 0x7e && ch >= 0x21 && ch <= 0x7e) {
499		/* multibyte character */
500		kf = (volatile short *)tv_kfont[hi];
501		kf += ch * FONTHEIGHT;
502		/* draw plane */
503		for (fh = 0; fh < UNDERLINE; fh++, p += ROWBYTES) {
504			ch = *kf++;
505			*(u_short *)p = ch | (ch >> 1);
506		}
507		ch = *kf++;
508		*(u_short *)p = expbits(ch | (ch >> 1));
509		p += ROWBYTES;
510		for (fh++; fh < FONTHEIGHT; fh++, p += ROWBYTES) {
511			ch = *kf++;
512			*(u_short *)p = ch | (ch >> 1);
513		}
514		return;
515	}
516
517	/* singlebyte character */
518	if (*ip->GL == CSET_JISKANA)
519		ch |= 0x80;
520	f = tv_font[ch];
521
522	/* draw plane */
523	for (fh = 0; fh < UNDERLINE; fh++, p += ROWBYTES) {
524		ch = *f++;
525		*p = ch | (ch >> 1);
526	}
527	ch = *f++;
528	*p = expbits(ch | (ch >> 1));
529	p += ROWBYTES;
530	for (fh++; fh < FONTHEIGHT; fh++, p += ROWBYTES) {
531		ch = *f++;
532		*p = ch | (ch >> 1);
533	}
534}
535
536void
537tv_putc_bd_ul_in(struct ite_softc *ip, int ch, char *p)
538{
539	short fh, hi;
540	char *f;
541	volatile short *kf;
542
543	hi = ip->save_char & 0x7f;
544
545	if (hi >= 0x21 && hi <= 0x7e && ch >= 0x21 && ch <= 0x7e) {
546		/* multibyte character */
547		kf = (volatile short *)tv_kfont[hi];
548		kf += ch * FONTHEIGHT;
549		/* draw plane */
550		for (fh = 0; fh < UNDERLINE; fh++, p += ROWBYTES) {
551			ch = *kf++;
552			*(u_short *)p = ~(ch | (ch >> 1));
553		}
554		ch = *kf++;
555		*(u_short *)p = ~expbits(ch | (ch >> 1));
556		p += ROWBYTES;
557		for (fh++; fh < FONTHEIGHT; fh++, p += ROWBYTES) {
558			ch = *kf++;
559			*(u_short *)p = ~(ch | (ch >> 1));
560		}
561		return;
562	}
563
564	/* singlebyte character */
565	if (*ip->GL == CSET_JISKANA)
566		ch |= 0x80;
567	f = tv_font[ch];
568
569	/* draw plane */
570	for (fh = 0; fh < UNDERLINE; fh++, p += ROWBYTES) {
571		ch = *f++;
572		*p = ~(ch | (ch >> 1));
573	}
574	ch = *f++;
575	*p = ~expbits(ch | (ch >> 1));
576	p += ROWBYTES;
577	for (fh++; fh < FONTHEIGHT; fh++, p += ROWBYTES) {
578		ch = *f++;
579		ch |= ch >> 1;
580		*p = ~(ch | (ch >> 1));
581	}
582}
583
584/*
585 * draw/erase/move cursor
586 */
587void
588tv_cursor(struct ite_softc *ip, int flag)
589{
590	u_char *p;
591	short fh;
592
593	/* erase */
594	switch (flag) {
595	/*case DRAW_CURSOR:*/
596	/*case ERASE_CURSOR:*/
597	/*case MOVE_CURSOR:*/
598	case START_CURSOROPT:
599		/*
600		 * old: ip->cursorx, ip->cursory
601		 * new: ip->curx, ip->cury
602		 */
603		p = CHADDR(ip->cursory, ip->cursorx);
604		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
605			*p = ~*p;
606		break;
607	}
608
609	/* draw */
610	switch (flag) {
611	/*case MOVE_CURSOR:*/
612	case END_CURSOROPT:
613		/*
614		 * Use exclusive-or.
615		 */
616		p = CHADDR(ip->cury, ip->curx);
617		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
618			*p = ~*p;
619
620		ip->cursorx = ip->curx;
621		ip->cursory = ip->cury;
622		break;
623	}
624}
625
626/*
627 * clear rectangle
628 */
629void
630tv_clear(struct ite_softc *ip, int y, int x, int height, int width)
631{
632	char *p;
633	short fh;
634
635	/* XXX: reset scroll register on clearing whole screen */
636	if (y == 0 && x == 0 && height == ip->rows && width == ip->cols) {
637		CRTC.r10 = 0;
638		CRTC.r11 = tv_top * FONTHEIGHT;
639	}
640
641	CRTC.r21 = 0x01f0;
642	while (height--) {
643		p = CHADDR(y++, x);
644		for (fh = 0; fh < FONTHEIGHT; fh++, p += ROWBYTES)
645			memset(p, 0, width);
646	}
647	/* crtc mode reset */
648	CRTC.r21 = 0;
649}
650
651/*
652 * scroll lines/columns
653 */
654void
655tv_scroll(struct ite_softc *ip, int srcy, int srcx, int count, int dir)
656{
657	int dst, siz, pl;
658
659	switch (dir) {
660	case SCROLL_UP:
661		/*
662		 * src: srcy
663		 * dst: (srcy - count)
664		 * siz: (ip->bottom_margin - sy + 1)
665		 */
666		dst = srcy - count;
667		siz = ip->bottom_margin - srcy + 1;
668		if (dst == 0 && ip->bottom_margin == ip->rows - 1) {
669			/* special case, hardware scroll */
670			tv_top = (tv_top + count) % PLANELINES;
671			CRTC.r11 = tv_top * FONTHEIGHT;
672		} else {
673			srcy = PHYSLINE(srcy);
674			dst = PHYSLINE(dst);
675			txrascpy(srcy, dst, siz, 0x0f);
676		}
677		break;
678
679	case SCROLL_DOWN:
680		/*
681		 * src: srcy
682		 * dst: (srcy + count)
683		 * siz: (ip->bottom_margin - dy + 1)
684		 */
685		dst = srcy + count;
686		siz = ip->bottom_margin - dst + 1;
687		if (srcy == 0 && ip->bottom_margin == ip->rows - 1) {
688			/* special case, hardware scroll */
689			tv_top = (tv_top + PLANELINES - count) % PLANELINES;
690			CRTC.r11 = tv_top * FONTHEIGHT;
691		} else {
692			srcy = PHYSLINE(srcy) + siz - 1;
693			dst = PHYSLINE(dst) + siz - 1;
694			txrascpy(srcy, dst, siz, 0x0f | 0x8000);
695		}
696		break;
697
698	case SCROLL_LEFT:
699		for (pl = 0; pl < PLANESIZE * 4; pl += PLANESIZE) {
700			short fh;
701			char *src = CHADDR(srcy, srcx) + pl;
702			char *dest = CHADDR(srcy, srcx - count) + pl;
703
704			siz = ip->cols - srcx;
705			for (fh = 0; fh < FONTHEIGHT; fh++) {
706				memcpy(dest, src, siz);
707				src += ROWBYTES;
708				dest += ROWBYTES;
709			}
710		}
711		break;
712
713	case SCROLL_RIGHT:
714		for (pl = 0; pl < PLANESIZE * 4; pl += PLANESIZE) {
715			short fh;
716			char *src = CHADDR(srcy, srcx) + pl;
717			char *dest = CHADDR(srcy, srcx + count) + pl;
718
719			siz = ip->cols - (srcx + count);
720			for (fh = 0; fh < FONTHEIGHT; fh++) {
721				memcpy(dest, src, siz);
722				src += ROWBYTES;
723				dest += ROWBYTES;
724			}
725		}
726		break;
727	}
728}
729