xref: /netbsd/sys/arch/prep/stand/boot/vga.c (revision 6550d01e)
1 /*	$NetBSD: vga.c,v 1.6 2009/03/18 10:22:34 cegger 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 
48 u_char background = 0;  /* Black */
49 u_char foreground = 7;  /* White */
50 
51 u_int addr_6845;
52 u_short *Crtat;
53 int lastpos;
54 int scroll;
55 
56 /*
57  * The current state of virtual displays
58  */
59 struct 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 
87 u_short	pccolor;		/* color/attributes for tty output */
88 u_short	pccolor_so;		/* color/attributes, standout mode */
89 
90 static void cursor(void);
91 static void initscreen(void);
92 void fillw(u_short, u_short *, int);
93 void video_on(void);
94 void video_off(void);
95 
96 /*
97  * cursor() sets an offset (0-1999) into the 80x25 text area
98  */
99 static void
100 cursor(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 
113 static void
114 initscreen(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 
133 void
134 fillw(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  */
151 void
152 vga_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 
396 void
397 vga_puts(char *s)
398 {
399 	char c;
400 	while ((c = *s++)) {
401 		vga_putc(c);
402 	}
403 }
404 
405 void
406 video_on(void)
407 {
408 
409 	/* Enable video */
410 	outb(0x3C4, 0x01);
411 	outb(0x3C5, inb(0x3C5) & ~0x20);
412 }
413 
414 void
415 video_off(void)
416 {
417 
418 	/* Disable video */
419 	outb(0x3C4, 0x01);
420 	outb(0x3C5, inb(0x3C5) | 0x20);
421 }
422 
423 void
424 vga_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