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