1/*	$NetBSD: vga.c,v 1.5 2006/04/10 18:40:06 garbled Exp $	*/
2
3/*-
4 * Copyright (C) 1995-1997 Gary Thomas (gdt@linuxppc.org)
5 * All rights reserved.
6 *
7 * VGA 'glass TTY' emulator
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *      This product includes software developed by Gary Thomas.
20 * 4. The name of the author may not be used to endorse or promote products
21 *    derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#ifdef CONS_VGA
36#include <lib/libsa/stand.h>
37#include <lib/libkern/libkern.h>
38#include "boot.h"
39
40#define	COL		80
41#define	ROW		25
42#define	CHR		2
43#define MONO_BASE	0x3B4
44#define MONO_BUF	0xB0000
45#define CGA_BASE	0x3D4
46#define CGA_BUF		0xB8000
47
48u_char background = 0;  /* Black */
49u_char foreground = 7;  /* White */
50
51u_int addr_6845;
52u_short *Crtat;
53int lastpos;
54int scroll;
55
56/*
57 * The current state of virtual displays
58 */
59struct screen {
60	u_short *cp;		/* the current character address */
61	enum state {
62		NORMAL,			/* no pending escape */
63		ESC,			/* saw ESC */
64		EBRAC,			/* saw ESC[ */
65		EBRACEQ			/* saw ESC[= */
66	} state;		/* command parser state */
67	int	cx;		/* the first escape seq argument */
68	int	cy;		/* the second escap seq argument */
69	int	*accp;		/* pointer to the current processed argument */
70	int	row;		/* current column */
71	int	so;		/* standout mode */
72	u_short color;		/* normal character color */
73	u_short color_so;	/* standout color */
74	u_short save_color;	/* saved normal color */
75	u_short save_color_so;	/* saved standout color */
76} screen;
77
78/*
79 * Color and attributes for normal, standout and kernel output
80 * are stored in the least-significant byte of a u_short
81 * so they don't have to be shifted for use.
82 * This is all byte-order dependent.
83 */
84#define	CATTR(x) (x)		/* store color/attributes un-shifted */
85#define	ATTR_ADDR(which) (((u_char *)&(which))+1) /* address of attributes */
86
87u_short	pccolor;		/* color/attributes for tty output */
88u_short	pccolor_so;		/* color/attributes, standout mode */
89
90static void cursor(void);
91static void initscreen(void);
92void fillw(u_short, u_short *, int);
93void video_on(void);
94void video_off(void);
95
96/*
97 * cursor() sets an offset (0-1999) into the 80x25 text area
98 */
99static void
100cursor(void)
101{
102 	int pos = screen.cp - Crtat;
103
104	if (lastpos != pos) {
105		outb(addr_6845, 14);
106		outb(addr_6845+1, pos >> 8);
107		outb(addr_6845, 15);
108		outb(addr_6845+1, pos);
109		lastpos = pos;
110	}
111}
112
113static void
114initscreen(void)
115{
116	struct screen *d = &screen;
117
118	pccolor = CATTR((background<<4)|foreground);
119	pccolor_so = CATTR((foreground<<4)|background);
120	d->color = pccolor;
121	d->save_color = pccolor;
122	d->color_so = pccolor_so;
123	d->save_color_so = pccolor_so;
124}
125
126
127#define	wrtchar(c, d) { \
128	*(d->cp) = c; \
129	d->cp++; \
130	d->row++; \
131}
132
133void
134fillw(u_short val, u_short *buf, int num)
135{
136	/* Need to byte swap value */
137	u_short tmp;
138
139	tmp = val;
140	while (num-- > 0)
141		*buf++ = tmp;
142}
143
144/*
145 * vga_putc (nee sput) has support for emulation of the 'ibmpc' termcap entry.
146 * This is a bare-bones implementation of a bare-bones entry
147 * One modification: Change li#24 to li#25 to reflect 25 lines
148 * "ca" is the color/attributes value (left-shifted by 8)
149 * or 0 if the current regular color for that screen is to be used.
150 */
151void
152vga_putc(int c)
153{
154	struct screen *d = &screen;
155	u_short *base;
156	int i, j;
157	u_short *pp;
158
159	base = Crtat;
160
161	switch (d->state) {
162	case NORMAL:
163		switch (c) {
164		case 0x0:		/* Ignore pad characters */
165			return;
166
167		case 0x1B:
168			d->state = ESC;
169			break;
170
171		case '\t':
172			do {
173				wrtchar(d->color | ' ', d);
174			} while (d->row % 8);
175			break;
176
177		case '\b':  /* non-destructive backspace */
178			if (d->cp > base) {
179				d->cp--;
180				d->row--;
181				if (d->row < 0)
182					d->row += COL;	/* prev column */
183			}
184			break;
185
186		case '\n':
187			d->cp += COL;
188		case '\r':
189			d->cp -= d->row;
190			d->row = 0;
191			break;
192
193		case '\007':
194			break;
195
196		default:
197			if (d->so) {
198				wrtchar(d->color_so|(c<<8), d);
199			} else {
200				wrtchar(d->color | (c<<8), d);
201			}
202			if (d->row >= COL)
203				d->row = 0;
204			break;
205		}
206		break;
207
208	case EBRAC:
209		/*
210		 * In this state, the action at the end of the switch
211		 * on the character type is to go to NORMAL state,
212		 * and intermediate states do a return rather than break.
213		 */
214		switch (c) {
215		case 'm':
216			d->so = d->cx;
217			break;
218
219		case 'A': /* back one row */
220			if (d->cp >= base + COL)
221				d->cp -= COL;
222			break;
223
224		case 'B': /* down one row */
225			d->cp += COL;
226			break;
227
228		case 'C': /* right cursor */
229			d->cp++;
230			d->row++;
231			break;
232
233		case 'D': /* left cursor */
234			if (d->cp > base) {
235				d->cp--;
236				d->row--;
237				if (d->row < 0)
238					d->row += COL;	/* prev column ??? */
239			}
240			break;
241
242		case 'J': /* Clear to end of display */
243			fillw(d->color|(' '<<8), d->cp, base + COL * ROW - d->cp);
244			break;
245
246		case 'K': /* Clear to EOL */
247			fillw(d->color|(' '<<8), d->cp, COL - (d->cp - base) % COL);
248			break;
249
250		case 'H': /* Cursor move */
251			if (d->cx > ROW)
252				d->cx = ROW;
253			if (d->cy > COL)
254				d->cy = COL;
255			if (d->cx == 0 || d->cy == 0) {
256				d->cp = base;
257				d->row = 0;
258			} else {
259				d->cp = base + (d->cx - 1) * COL + d->cy - 1;
260				d->row = d->cy - 1;
261			}
262			break;
263
264		case '_': /* set cursor */
265			if (d->cx)
266				d->cx = 1;		/* block */
267			else
268				d->cx = 12;	/* underline */
269			outb(addr_6845, 10);
270			outb(addr_6845+1, d->cx);
271			outb(addr_6845, 11);
272			outb(addr_6845+1, 13);
273			break;
274
275		case ';': /* Switch params in cursor def */
276			d->accp = &d->cy;
277			return;
278
279		case '=': /* ESC[= color change */
280			d->state = EBRACEQ;
281			return;
282
283		case 'L':	/* Insert line */
284			i = (d->cp - base) / COL;
285			/* avoid deficiency of bcopy implementation */
286			/* XXX: comment and hack relevant? */
287			pp = base + COL * (ROW-2);
288			for (j = ROW - 1 - i; j--; pp -= COL)
289				memmove(pp + COL, pp, COL * CHR);
290			fillw(d->color|(' '<<8), base + i * COL, COL);
291			break;
292
293		case 'M':	/* Delete line */
294			i = (d->cp - base) / COL;
295			pp = base + i * COL;
296			memmove(pp, pp + COL, (ROW-1 - i)*COL*CHR);
297			fillw(d->color|(' '<<8), base + COL * (ROW - 1), COL);
298			break;
299
300		default: /* Only numbers valid here */
301			if ((c >= '0') && (c <= '9')) {
302				*(d->accp) *= 10;
303				*(d->accp) += c - '0';
304				return;
305			} else
306				break;
307		}
308		d->state = NORMAL;
309		break;
310
311	case EBRACEQ: {
312		/*
313		 * In this state, the action at the end of the switch
314		 * on the character type is to go to NORMAL state,
315		 * and intermediate states do a return rather than break.
316		 */
317		u_char *colp;
318
319		/*
320		 * Set foreground/background color
321		 * for normal mode, standout mode
322		 * or kernel output.
323		 * Based on code from kentp@svmp03.
324		 */
325		switch (c) {
326		case 'F':
327			colp = ATTR_ADDR(d->color);
328	do_fg:
329			*colp = (*colp & 0xf0) | (d->cx);
330			break;
331
332		case 'G':
333			colp = ATTR_ADDR(d->color);
334	do_bg:
335			*colp = (*colp & 0xf) | (d->cx << 4);
336			break;
337
338		case 'H':
339			colp = ATTR_ADDR(d->color_so);
340			goto do_fg;
341
342		case 'I':
343			colp = ATTR_ADDR(d->color_so);
344			goto do_bg;
345
346		case 'S':
347			d->save_color = d->color;
348			d->save_color_so = d->color_so;
349			break;
350
351		case 'R':
352			d->color = d->save_color;
353			d->color_so = d->save_color_so;
354			break;
355
356		default: /* Only numbers valid here */
357			if ((c >= '0') && (c <= '9')) {
358				d->cx *= 10;
359				d->cx += c - '0';
360				return;
361			} else
362				break;
363		}
364		d->state = NORMAL;
365	    }
366	    break;
367
368	case ESC:
369		switch (c) {
370		case 'c':	/* Clear screen & home */
371			fillw(d->color|(' '<<8), base, COL * ROW);
372			d->cp = base;
373			d->row = 0;
374			d->state = NORMAL;
375			break;
376		case '[':	/* Start ESC [ sequence */
377			d->state = EBRAC;
378			d->cx = 0;
379			d->cy = 0;
380			d->accp = &d->cx;
381			break;
382		default: /* Invalid, clear state */
383			d->state = NORMAL;
384			break;
385		}
386		break;
387	}
388	if (d->cp >= base + (COL * ROW)) { /* scroll check */
389		memmove(base, base + COL, COL * (ROW - 1) * CHR);
390		fillw(d->color|(' '<<8), base + COL * (ROW - 1), COL);
391		d->cp -= COL;
392	}
393	cursor();
394}
395
396void
397vga_puts(char *s)
398{
399	char c;
400	while ((c = *s++)) {
401		vga_putc(c);
402	}
403}
404
405void
406video_on(void)
407{
408
409	/* Enable video */
410	outb(0x3C4, 0x01);
411	outb(0x3C5, inb(0x3C5) & ~0x20);
412}
413
414void
415video_off(void)
416{
417
418	/* Disable video */
419	outb(0x3C4, 0x01);
420	outb(0x3C5, inb(0x3C5) | 0x20);
421}
422
423void
424vga_init(u_char *ISA_mem)
425{
426	struct screen *d = &screen;
427
428	memset(d, 0, sizeof (screen));
429	video_on();
430
431	d->cp = Crtat = (u_short *)&ISA_mem[0x0B8000];
432	addr_6845 = CGA_BASE;
433	initscreen();
434	fillw(pccolor|(' '<<8), d->cp, COL * ROW);
435}
436#endif /* CONS_VGA */
437