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