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