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