xref: /netbsd-src/sys/arch/prep/stand/boot/vga.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: vga.c,v 1.2 2001/07/22 14:57:51 wiz 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 "boot.h"
38 
39 #define	COL		80
40 #define	ROW		25
41 #define	CHR		2
42 #define MONO_BASE	0x3B4
43 #define MONO_BUF	0xB0000
44 #define CGA_BASE	0x3D4
45 #define CGA_BUF		0xB8000
46 
47 u_char background = 0;  /* Black */
48 u_char foreground = 7;  /* White */
49 
50 u_int addr_6845;
51 u_short *Crtat;
52 int lastpos;
53 int scroll;
54 
55 /*
56  * The current state of virtual displays
57  */
58 struct screen {
59 	u_short *cp;		/* the current character address */
60 	enum state {
61 		NORMAL,			/* no pending escape */
62 		ESC,			/* saw ESC */
63 		EBRAC,			/* saw ESC[ */
64 		EBRACEQ			/* saw ESC[= */
65 	} state;		/* command parser state */
66 	int	cx;		/* the first escape seq argument */
67 	int	cy;		/* the second escap seq argument */
68 	int	*accp;		/* pointer to the current processed argument */
69 	int	row;		/* current column */
70 	int	so;		/* standout mode */
71 	u_short color;		/* normal character color */
72 	u_short color_so;	/* standout color */
73 	u_short save_color;	/* saved normal color */
74 	u_short save_color_so;	/* saved standout color */
75 } screen;
76 
77 /*
78  * Color and attributes for normal, standout and kernel output
79  * are stored in the least-significant byte of a u_short
80  * so they don't have to be shifted for use.
81  * This is all byte-order dependent.
82  */
83 #define	CATTR(x) (x)		/* store color/attributes un-shifted */
84 #define	ATTR_ADDR(which) (((u_char *)&(which))+1) /* address of attributes */
85 
86 u_short	pccolor;		/* color/attributes for tty output */
87 u_short	pccolor_so;		/* color/attributes, standout mode */
88 
89 static void cursor __P((void));
90 static void initscreen __P((void));
91 void fillw __P((u_short, u_short *, int));
92 void video_on __P((void));
93 void video_off __P((void));
94 
95 /*
96  * cursor() sets an offset (0-1999) into the 80x25 text area
97  */
98 static void
99 cursor()
100 {
101  	int pos = screen.cp - Crtat;
102 
103 	if (lastpos != pos) {
104 		outb(addr_6845, 14);
105 		outb(addr_6845+1, pos >> 8);
106 		outb(addr_6845, 15);
107 		outb(addr_6845+1, pos);
108 		lastpos = pos;
109 	}
110 }
111 
112 static void
113 initscreen()
114 {
115 	struct screen *d = &screen;
116 
117 	pccolor = CATTR((background<<4)|foreground);
118 	pccolor_so = CATTR((foreground<<4)|background);
119 	d->color = pccolor;
120 	d->save_color = pccolor;
121 	d->color_so = pccolor_so;
122 	d->save_color_so = pccolor_so;
123 }
124 
125 
126 #define	wrtchar(c, d) { \
127 	*(d->cp) = c; \
128 	d->cp++; \
129 	d->row++; \
130 }
131 
132 void
133 fillw(val, buf, num)
134 	u_short val;
135 	u_short *buf;
136 	int num;
137 {
138 	/* Need to byte swap value */
139 	u_short tmp;
140 
141 	tmp = val;
142 	while (num-- > 0)
143 		*buf++ = tmp;
144 }
145 
146 /*
147  * vga_putc (nee sput) has support for emulation of the 'ibmpc' termcap entry.
148  * This is a bare-bones implementation of a bare-bones entry
149  * One modification: Change li#24 to li#25 to reflect 25 lines
150  * "ca" is the color/attributes value (left-shifted by 8)
151  * or 0 if the current regular color for that screen is to be used.
152  */
153 void
154 vga_putc(int c)
155 {
156 	struct screen *d = &screen;
157 	u_short *base;
158 	int i, j;
159 	u_short *pp;
160 
161 	base = Crtat;
162 
163 	switch (d->state) {
164 	case NORMAL:
165 		switch (c) {
166 		case 0x0:		/* Ignore pad characters */
167 			return;
168 
169 		case 0x1B:
170 			d->state = ESC;
171 			break;
172 
173 		case '\t':
174 			do {
175 				wrtchar(d->color | ' ', d);
176 			} while (d->row % 8);
177 			break;
178 
179 		case '\b':  /* non-destructive backspace */
180 			if (d->cp > base) {
181 				d->cp--;
182 				d->row--;
183 				if (d->row < 0)
184 					d->row += COL;	/* prev column */
185 			}
186 			break;
187 
188 		case '\n':
189 			d->cp += COL;
190 		case '\r':
191 			d->cp -= d->row;
192 			d->row = 0;
193 			break;
194 
195 		case '\007':
196 			break;
197 
198 		default:
199 			if (d->so) {
200 				wrtchar(d->color_so|(c<<8), d);
201 			} else {
202 				wrtchar(d->color | (c<<8), d);
203 			}
204 			if (d->row >= COL)
205 				d->row = 0;
206 			break;
207 		}
208 		break;
209 
210 	case EBRAC:
211 		/*
212 		 * In this state, the action at the end of the switch
213 		 * on the character type is to go to NORMAL state,
214 		 * and intermediate states do a return rather than break.
215 		 */
216 		switch (c) {
217 		case 'm':
218 			d->so = d->cx;
219 			break;
220 
221 		case 'A': /* back one row */
222 			if (d->cp >= base + COL)
223 				d->cp -= COL;
224 			break;
225 
226 		case 'B': /* down one row */
227 			d->cp += COL;
228 			break;
229 
230 		case 'C': /* right cursor */
231 			d->cp++;
232 			d->row++;
233 			break;
234 
235 		case 'D': /* left cursor */
236 			if (d->cp > base) {
237 				d->cp--;
238 				d->row--;
239 				if (d->row < 0)
240 					d->row += COL;	/* prev column ??? */
241 			}
242 			break;
243 
244 		case 'J': /* Clear to end of display */
245 			fillw(d->color|(' '<<8), d->cp, base + COL * ROW - d->cp);
246 			break;
247 
248 		case 'K': /* Clear to EOL */
249 			fillw(d->color|(' '<<8), d->cp, COL - (d->cp - base) % COL);
250 			break;
251 
252 		case 'H': /* Cursor move */
253 			if (d->cx > ROW)
254 				d->cx = ROW;
255 			if (d->cy > COL)
256 				d->cy = COL;
257 			if (d->cx == 0 || d->cy == 0) {
258 				d->cp = base;
259 				d->row = 0;
260 			} else {
261 				d->cp = base + (d->cx - 1) * COL + d->cy - 1;
262 				d->row = d->cy - 1;
263 			}
264 			break;
265 
266 		case '_': /* set cursor */
267 			if (d->cx)
268 				d->cx = 1;		/* block */
269 			else
270 				d->cx = 12;	/* underline */
271 			outb(addr_6845, 10);
272 			outb(addr_6845+1, d->cx);
273 			outb(addr_6845, 11);
274 			outb(addr_6845+1, 13);
275 			break;
276 
277 		case ';': /* Switch params in cursor def */
278 			d->accp = &d->cy;
279 			return;
280 
281 		case '=': /* ESC[= color change */
282 			d->state = EBRACEQ;
283 			return;
284 
285 		case 'L':	/* Insert line */
286 			i = (d->cp - base) / COL;
287 			/* avoid deficiency of bcopy implementation */
288 			/* XXX: comment and hack relevant? */
289 			pp = base + COL * (ROW-2);
290 			for (j = ROW - 1 - i; j--; pp -= COL)
291 				memmove(pp + COL, pp, COL * CHR);
292 			fillw(d->color|(' '<<8), base + i * COL, COL);
293 			break;
294 
295 		case 'M':	/* Delete line */
296 			i = (d->cp - base) / COL;
297 			pp = base + i * COL;
298 			memmove(pp, pp + COL, (ROW-1 - i)*COL*CHR);
299 			fillw(d->color|(' '<<8), base + COL * (ROW - 1), COL);
300 			break;
301 
302 		default: /* Only numbers valid here */
303 			if ((c >= '0') && (c <= '9')) {
304 				*(d->accp) *= 10;
305 				*(d->accp) += c - '0';
306 				return;
307 			} else
308 				break;
309 		}
310 		d->state = NORMAL;
311 		break;
312 
313 	case EBRACEQ: {
314 		/*
315 		 * In this state, the action at the end of the switch
316 		 * on the character type is to go to NORMAL state,
317 		 * and intermediate states do a return rather than break.
318 		 */
319 		u_char *colp;
320 
321 		/*
322 		 * Set foreground/background color
323 		 * for normal mode, standout mode
324 		 * or kernel output.
325 		 * Based on code from kentp@svmp03.
326 		 */
327 		switch (c) {
328 		case 'F':
329 			colp = ATTR_ADDR(d->color);
330 	do_fg:
331 			*colp = (*colp & 0xf0) | (d->cx);
332 			break;
333 
334 		case 'G':
335 			colp = ATTR_ADDR(d->color);
336 	do_bg:
337 			*colp = (*colp & 0xf) | (d->cx << 4);
338 			break;
339 
340 		case 'H':
341 			colp = ATTR_ADDR(d->color_so);
342 			goto do_fg;
343 
344 		case 'I':
345 			colp = ATTR_ADDR(d->color_so);
346 			goto do_bg;
347 
348 		case 'S':
349 			d->save_color = d->color;
350 			d->save_color_so = d->color_so;
351 			break;
352 
353 		case 'R':
354 			d->color = d->save_color;
355 			d->color_so = d->save_color_so;
356 			break;
357 
358 		default: /* Only numbers valid here */
359 			if ((c >= '0') && (c <= '9')) {
360 				d->cx *= 10;
361 				d->cx += c - '0';
362 				return;
363 			} else
364 				break;
365 		}
366 		d->state = NORMAL;
367 	    }
368 	    break;
369 
370 	case ESC:
371 		switch (c) {
372 		case 'c':	/* Clear screen & home */
373 			fillw(d->color|(' '<<8), base, COL * ROW);
374 			d->cp = base;
375 			d->row = 0;
376 			d->state = NORMAL;
377 			break;
378 		case '[':	/* Start ESC [ sequence */
379 			d->state = EBRAC;
380 			d->cx = 0;
381 			d->cy = 0;
382 			d->accp = &d->cx;
383 			break;
384 		default: /* Invalid, clear state */
385 			d->state = NORMAL;
386 			break;
387 		}
388 		break;
389 	}
390 	if (d->cp >= base + (COL * ROW)) { /* scroll check */
391 		memmove(base, base + COL, COL * (ROW - 1) * CHR);
392 		fillw(d->color|(' '<<8), base + COL * (ROW - 1), COL);
393 		d->cp -= COL;
394 	}
395 	cursor();
396 }
397 
398 void
399 vga_puts(s)
400 	char *s;
401 {
402 	char c;
403 	while ((c = *s++)) {
404 		vga_putc(c);
405 	}
406 }
407 
408 void
409 video_on()
410 {
411 
412 	/* Enable video */
413 	outb(0x3C4, 0x01);
414 	outb(0x3C5, inb(0x3C5) & ~0x20);
415 }
416 
417 void
418 video_off()
419 {
420 
421 	/* Disable video */
422 	outb(0x3C4, 0x01);
423 	outb(0x3C5, inb(0x3C5) | 0x20);
424 }
425 
426 void
427 vga_init(ISA_mem)
428 	u_char *ISA_mem;
429 {
430 	struct screen *d = &screen;
431 
432 	memset(d, 0, sizeof (screen));
433 	video_on();
434 
435 	d->cp = Crtat = (u_short *)&ISA_mem[0x0B8000];
436 	addr_6845 = CGA_BASE;
437 	initscreen();
438 	fillw(pccolor|(' '<<8), d->cp, COL * ROW);
439 }
440 #endif /* CONS_VGA */
441