1 /* graphics.c - graphics mode support for GRUB */ 2 /* Implemented as a terminal type by Jeremy Katz <katzj@redhat.com> based 3 * on a patch by Paulo C�sar Pereira de Andrade <pcpa@conectiva.com.br> 4 */ 5 /* 6 * GRUB -- GRand Unified Bootloader 7 * Copyright (C) 2001,2002 Red Hat, Inc. 8 * Portions copyright (C) 2000 Conectiva, Inc. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 */ 24 25 26 27 #ifdef SUPPORT_GRAPHICS 28 29 #include <term.h> 30 #include <shared.h> 31 #include <graphics.h> 32 33 #include <logo.xbm> 34 35 int saved_videomode; 36 unsigned char *font8x16; 37 38 int graphics_inited = 0; 39 static char splashimage[64]; 40 41 #define HPIXELS 640 42 #define VPIXELS 480 43 #define HPIXELSPERBYTE 8 44 45 #define ROWBYTES (HPIXELS / HPIXELSPERBYTE) 46 #define SCREENBYTES (ROWBYTES * VPIXELS) 47 48 #define VSHADOW VSHADOW1 49 unsigned char VSHADOW1[SCREENBYTES]; 50 unsigned char VSHADOW2[SCREENBYTES]; 51 unsigned char VSHADOW4[SCREENBYTES]; 52 unsigned char VSHADOW8[SCREENBYTES]; 53 54 static unsigned char *s1 = (unsigned char*)VSHADOW1; 55 static unsigned char *s2 = (unsigned char*)VSHADOW2; 56 static unsigned char *s4 = (unsigned char*)VSHADOW4; 57 static unsigned char *s8 = (unsigned char*)VSHADOW8; 58 59 /* constants to define the viewable area */ 60 const int x0 = 0; 61 const int x1 = ROWBYTES; 62 const int y0 = 0; 63 const int y1 = 30; 64 65 /* text buffer has to be kept around so that we can write things as we 66 * scroll and the like */ 67 unsigned short text[ROWBYTES * 30]; 68 69 /* why do these have to be kept here? */ 70 int foreground = (63 << 16) | (63 << 8) | (63), background = 0, border = 0; 71 72 73 /* current position */ 74 static int fontx = 0; 75 static int fonty = 0; 76 77 /* global state so that we don't try to recursively scroll or cursor */ 78 static int no_scroll = 0; 79 80 /* color state */ 81 static int graphics_standard_color = A_NORMAL; 82 static int graphics_normal_color = A_NORMAL; 83 static int graphics_highlight_color = A_REVERSE; 84 static int graphics_current_color = A_NORMAL; 85 static color_state graphics_color_state = COLOR_STATE_STANDARD; 86 87 88 /* graphics local functions */ 89 static void graphics_setxy(int col, int row); 90 static void graphics_scroll(); 91 92 /* FIXME: where do these really belong? */ 93 static inline void outb(unsigned short port, unsigned char val) 94 { 95 __asm __volatile ("outb %0,%1"::"a" (val), "d" (port)); 96 } 97 98 static void MapMask(int value) { 99 outb(0x3c4, 2); 100 outb(0x3c5, value); 101 } 102 103 /* bit mask register */ 104 static void BitMask(int value) { 105 outb(0x3ce, 8); 106 outb(0x3cf, value); 107 } 108 109 110 /* Set the splash image */ 111 void graphics_set_splash(char *splashfile) { 112 grub_strcpy(splashimage, splashfile); 113 } 114 115 /* Get the current splash image */ 116 char *graphics_get_splash(void) { 117 return splashimage; 118 } 119 120 /* Initialize a vga16 graphics display with the palette based off of 121 * the image in splashimage. If the image doesn't exist, leave graphics 122 * mode. */ 123 int graphics_init() 124 { 125 if (!graphics_inited) { 126 saved_videomode = set_videomode(0x12); 127 } 128 129 /* 130 * XXX this is known not to reset the image 131 * properly in the case of failure 132 */ 133 if (!read_image(splashimage)) { 134 set_videomode(saved_videomode); 135 grub_printf("failed to read image\n"); 136 return 0; 137 } 138 139 font8x16 = (unsigned char*)graphics_get_font(); 140 141 graphics_inited = 1; 142 143 /* make sure that the highlight color is set correctly */ 144 graphics_highlight_color = ((graphics_normal_color >> 4) | 145 ((graphics_normal_color & 0xf) << 4)); 146 147 return 1; 148 } 149 150 /* Leave graphics mode */ 151 void graphics_end(void) 152 { 153 if (graphics_inited) { 154 set_videomode(saved_videomode); 155 graphics_inited = 0; 156 } 157 } 158 159 /* Print ch on the screen. Handle any needed scrolling or the like */ 160 void graphics_putchar(int ch) { 161 ch &= 0xff; 162 163 graphics_cursor(0); 164 165 if (ch == '\n') { 166 if (fonty + 1 < y1) 167 graphics_setxy(fontx, fonty + 1); 168 else 169 graphics_scroll(); 170 graphics_cursor(1); 171 return; 172 } else if (ch == '\r') { 173 graphics_setxy(x0, fonty); 174 graphics_cursor(1); 175 return; 176 } 177 178 graphics_cursor(0); 179 180 text[fonty * ROWBYTES + fontx] = ch; 181 text[fonty * ROWBYTES + fontx] &= 0x00ff; 182 if (graphics_current_color & 0xf0) 183 text[fonty * ROWBYTES + fontx] |= 0x100; 184 185 graphics_cursor(0); 186 187 if ((fontx + 1) >= x1) { 188 graphics_setxy(x0, fonty); 189 if (fonty + 1 < y1) 190 graphics_setxy(x0, fonty + 1); 191 else 192 graphics_scroll(); 193 } else { 194 graphics_setxy(fontx + 1, fonty); 195 } 196 197 graphics_cursor(1); 198 } 199 200 /* get the current location of the cursor */ 201 int graphics_getxy(void) { 202 return (fontx << 8) | fonty; 203 } 204 205 void graphics_gotoxy(int x, int y) { 206 graphics_cursor(0); 207 208 graphics_setxy(x, y); 209 210 graphics_cursor(1); 211 } 212 213 void graphics_cls(void) { 214 int i; 215 unsigned char *mem; 216 217 graphics_cursor(0); 218 graphics_gotoxy(x0, y0); 219 220 mem = (unsigned char*)VIDEOMEM; 221 222 for (i = 0; i < ROWBYTES * 30; i++) 223 text[i] = ' '; 224 graphics_cursor(1); 225 226 BitMask(0xff); 227 228 /* plane 1 */ 229 MapMask(1); 230 grub_memcpy(mem, s1, SCREENBYTES); 231 232 /* plane 2 */ 233 MapMask(2); 234 grub_memcpy(mem, s2, SCREENBYTES); 235 236 /* plane 3 */ 237 MapMask(4); 238 grub_memcpy(mem, s4, SCREENBYTES); 239 240 /* plane 4 */ 241 MapMask(8); 242 grub_memcpy(mem, s8, SCREENBYTES); 243 244 MapMask(15); 245 } 246 247 void graphics_setcolorstate (color_state state) { 248 switch (state) { 249 case COLOR_STATE_STANDARD: 250 graphics_current_color = graphics_standard_color; 251 break; 252 case COLOR_STATE_NORMAL: 253 graphics_current_color = graphics_normal_color; 254 break; 255 case COLOR_STATE_HIGHLIGHT: 256 graphics_current_color = graphics_highlight_color; 257 break; 258 default: 259 graphics_current_color = graphics_standard_color; 260 break; 261 } 262 263 graphics_color_state = state; 264 } 265 266 void graphics_setcolor (int normal_color, int highlight_color) { 267 graphics_normal_color = normal_color; 268 graphics_highlight_color = highlight_color; 269 270 graphics_setcolorstate (graphics_color_state); 271 } 272 273 int graphics_setcursor (int on) { 274 /* FIXME: we don't have a cursor in graphics */ 275 return 1; 276 } 277 278 void 279 draw_xbmlogo(void) 280 { 281 unsigned char mask; 282 unsigned xbm_index = 0, xbm_incr; 283 unsigned screenx, logox, logoy, fb_offset, fb_index; 284 285 /* 286 * Place the logo such that the right hand side will be four pixels from 287 * the right hand edge of the screen and the bottom will be two pixels 288 * from the bottom edge. 289 */ 290 fb_offset = ((VPIXELS - 1) - logo_height - 2) * ROWBYTES; 291 xbm_incr = (logo_width / 8) + 1; 292 293 for (logoy = 0; logoy < logo_height; logoy++) { 294 for (logox = 0, screenx = (HPIXELS - 1) - logo_width - 4; 295 logox < logo_width; logox++, screenx++) { 296 mask = 0x80 >> (screenx & 7); 297 fb_index = fb_offset + (screenx >> 3); 298 299 /* 300 * If a bit is clear in the bitmap, draw it onto the 301 * framebuffer in the default foreground color. 302 */ 303 if ((logo_bits[xbm_index + (logox >> 3)] & 304 (1 << (logox & 7))) == 0) { 305 /* system default foreground color */ 306 s1[fb_index] |= mask; 307 s2[fb_index] |= mask; 308 s4[fb_index] |= mask; 309 s8[fb_index] |= mask; 310 } 311 } 312 313 xbm_index += xbm_incr; 314 fb_offset += ROWBYTES; 315 } 316 } 317 318 /* 319 * Read in the splashscreen image and set the palette up appropriately. 320 * 321 * Format of splashscreen is an XPM (can be gzipped) with up to 15 colors and 322 * is assumed to be of the proper screen dimensions. 323 */ 324 int read_image(char *s) 325 { 326 char buf[32], pal[16]; 327 unsigned char c, base, mask; 328 unsigned i, len, idx, colors, x, y, width, height; 329 330 if (!grub_open(s)) 331 return 0; 332 333 /* read XPM header - must match memcmp string PRECISELY. */ 334 if (!grub_read((char*)&buf, 10) || grub_memcmp(buf, "/* XPM */\n", 10)) { 335 grub_close(); 336 return 0; 337 } 338 339 /* skip characters until we reach an initial '"' */ 340 while (grub_read(&c, 1)) { 341 if (c == '"') 342 break; 343 } 344 345 /* skip whitespace */ 346 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) 347 ; 348 349 /* 350 * Format here should be four integers: 351 * 352 * Width Height NumberOfColors CharactersPerPixel 353 */ 354 i = 0; 355 width = c - '0'; 356 while (grub_read(&c, 1)) { 357 if (c >= '0' && c <= '9') 358 width = width * 10 + c - '0'; 359 else 360 break; 361 } 362 363 /* skip whitespace to advance to next digit */ 364 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) 365 ; 366 367 height = c - '0'; 368 while (grub_read(&c, 1)) { 369 if (c >= '0' && c <= '9') 370 height = height * 10 + c - '0'; 371 else 372 break; 373 } 374 375 /* skip whitespace to advance to next digit */ 376 while (grub_read(&c, 1) && (c == ' ' || c == '\t')) ; 377 378 colors = c - '0'; 379 while (grub_read(&c, 1)) { 380 if (c >= '0' && c <= '9') 381 colors = colors * 10 + c - '0'; 382 else 383 break; 384 } 385 386 /* 387 * Allow 15 specified palette colors (indices 1 - 15) at most. 388 * 389 * One would expect that this should be 14 allowing for foreground 390 * and background, but there are a number of 15 color graphics in 391 * use that shouldn't break with this check. 392 */ 393 if (colors > 15) { 394 grub_close(); 395 return 0; 396 } 397 398 /* eat rest of line - assumes chars per pixel is one */ 399 while (grub_read(&c, 1) && c != '"') 400 ; 401 402 /* 403 * Parse the XPM palette - the format is: 404 * 405 * identifier colorspace #RRGGBB 406 * 407 * The identifier is simply a single character; the colorspace identifier 408 * is skipped as it's assumed to be "c" denoting RGB color. 409 * 410 * The six digits after the "#" are assumed to be a six digit RGB color 411 * identifier as defined in X11's rgb.txt file. 412 */ 413 for (i = 0, idx = 1; i < colors; i++) { 414 len = 0; 415 416 while (grub_read(&c, 1) && c != '"') 417 ; 418 419 grub_read(&c, 1); /* char */ 420 base = c; 421 grub_read(buf, 4); /* \t c # */ 422 423 while (grub_read(&c, 1) && c != '"') { 424 if (len < sizeof(buf)) 425 buf[len++] = c; 426 } 427 428 if (len == 6) { 429 int r = ((hex(buf[0]) << 4) | hex(buf[1])) >> 2; 430 int g = ((hex(buf[2]) << 4) | hex(buf[3])) >> 2; 431 int b = ((hex(buf[4]) << 4) | hex(buf[5])) >> 2; 432 433 pal[idx] = base; 434 graphics_set_palette(idx, r, g, b); 435 ++idx; 436 } 437 } 438 439 x = y = len = 0; 440 441 /* clear (zero out) all four planes of the framebuffer */ 442 for (i = 0; i < SCREENBYTES; i++) 443 s1[i] = s2[i] = s4[i] = s8[i] = 0; 444 445 /* parse the XPM data */ 446 while (y < height) { 447 /* exit on EOF, otherwise skip characters until an initial '"' */ 448 while (1) { 449 if (!grub_read(&c, 1)) { 450 grub_close(); 451 return 0; 452 } 453 if (c == '"') 454 break; 455 } 456 457 /* read characters until we hit an EOF or a terminating '"' */ 458 while (grub_read(&c, 1) && c != '"') { 459 460 /* look up specified pixel color in palette */ 461 for (i = 1; i < 15; i++) 462 if (pal[i] == c) { 463 c = i; 464 break; 465 } 466 467 /* 468 * A bit is set in each of the "planes" of the frame buffer to 469 * denote a pixel drawn in each color of the palette. 470 * 471 * The planes are a binary representation of the palette, so a 472 * pixel in color "1" of the palette would be denoted by setting a 473 * bit in plane "s1"; a pixel in color "15" of the palette would 474 * set the same bit in each of the four planes. 475 * 476 * Pixels are represented by set bits in a byte, in the order 477 * left-to-right (e.g. pixel 0 is 0x80, pixel 7 is 1.) 478 */ 479 mask = 0x80 >> (x & 7); 480 if (c & 1) 481 s1[len + (x >> 3)] |= mask; 482 if (c & 2) 483 s2[len + (x >> 3)] |= mask; 484 if (c & 4) 485 s4[len + (x >> 3)] |= mask; 486 if (c & 8) 487 s8[len + (x >> 3)] |= mask; 488 489 /* 490 * Increment "x"; if we hit pixel HPIXELS, wrap to the start of the 491 * next horizontal line if we haven't yet reached the bottom of 492 * the screen. 493 */ 494 if (++x >= HPIXELS) { 495 x = 0; 496 497 if (y++ < VPIXELS) 498 len += ROWBYTES; 499 else 500 break; 501 } 502 } 503 } 504 505 grub_close(); 506 507 /* 508 * Set BIOS palette color 0 to be the system background color, 15 to be the 509 * system foreground color, and 17 to be the system border color. 510 */ 511 graphics_set_palette(0, (background >> 16), (background >> 8) & 63, 512 background & 63); 513 graphics_set_palette(15, (foreground >> 16), (foreground >> 8) & 63, 514 foreground & 63); 515 graphics_set_palette(0x11, (border >> 16), (border >> 8) & 63, 516 border & 63); 517 518 draw_xbmlogo(); 519 520 return 1; 521 } 522 523 /* Convert a character which is a hex digit to the appropriate integer */ 524 int hex(int v) 525 { 526 if (v >= 'A' && v <= 'F') 527 return (v - 'A' + 10); 528 if (v >= 'a' && v <= 'f') 529 return (v - 'a' + 10); 530 return (v - '0'); 531 } 532 533 534 /* move the graphics cursor location to col, row */ 535 static void graphics_setxy(int col, int row) { 536 if (col >= x0 && col < x1) { 537 fontx = col; 538 cursorX = col << 3; 539 } 540 if (row >= y0 && row < y1) { 541 fonty = row; 542 cursorY = row << 4; 543 } 544 } 545 546 /* scroll the screen */ 547 static void graphics_scroll() { 548 int i, j; 549 550 /* we don't want to scroll recursively... that would be bad */ 551 if (no_scroll) 552 return; 553 no_scroll = 1; 554 555 /* move everything up a line */ 556 for (j = y0 + 1; j < y1; j++) { 557 graphics_gotoxy(x0, j - 1); 558 for (i = x0; i < x1; i++) { 559 graphics_putchar(text[j * ROWBYTES + i]); 560 } 561 } 562 563 /* last line should be blank */ 564 graphics_gotoxy(x0, y1 - 1); 565 for (i = x0; i < x1; i++) 566 graphics_putchar(' '); 567 graphics_setxy(x0, y1 - 1); 568 569 no_scroll = 0; 570 } 571 572 void graphics_cursor(int set) { 573 unsigned char *pat, *mem, *ptr, chr[16 << 2]; 574 int i, ch, invert, offset; 575 576 if (set && no_scroll) 577 return; 578 579 offset = cursorY * ROWBYTES + fontx; 580 ch = text[fonty * ROWBYTES + fontx] & 0xff; 581 invert = (text[fonty * ROWBYTES + fontx] & 0xff00) != 0; 582 pat = font8x16 + (ch << 4); 583 584 mem = (unsigned char*)VIDEOMEM + offset; 585 586 if (!set) { 587 for (i = 0; i < 16; i++) { 588 unsigned char mask = pat[i]; 589 590 if (!invert) { 591 chr[i ] = ((unsigned char*)VSHADOW1)[offset]; 592 chr[16 + i] = ((unsigned char*)VSHADOW2)[offset]; 593 chr[32 + i] = ((unsigned char*)VSHADOW4)[offset]; 594 chr[48 + i] = ((unsigned char*)VSHADOW8)[offset]; 595 596 /* FIXME: if (shade) */ 597 if (1) { 598 if (ch == DISP_VERT || ch == DISP_LL || 599 ch == DISP_UR || ch == DISP_LR) { 600 unsigned char pmask = ~(pat[i] >> 1); 601 602 chr[i ] &= pmask; 603 chr[16 + i] &= pmask; 604 chr[32 + i] &= pmask; 605 chr[48 + i] &= pmask; 606 } 607 if (i > 0 && ch != DISP_VERT) { 608 unsigned char pmask = ~(pat[i - 1] >> 1); 609 610 chr[i ] &= pmask; 611 chr[16 + i] &= pmask; 612 chr[32 + i] &= pmask; 613 chr[48 + i] &= pmask; 614 if (ch == DISP_HORIZ || ch == DISP_UR || ch == DISP_LR) { 615 pmask = ~pat[i - 1]; 616 617 chr[i ] &= pmask; 618 chr[16 + i] &= pmask; 619 chr[32 + i] &= pmask; 620 chr[48 + i] &= pmask; 621 } 622 } 623 } 624 chr[i ] |= mask; 625 chr[16 + i] |= mask; 626 chr[32 + i] |= mask; 627 chr[48 + i] |= mask; 628 629 offset += ROWBYTES; 630 } 631 else { 632 chr[i ] = mask; 633 chr[16 + i] = mask; 634 chr[32 + i] = mask; 635 chr[48 + i] = mask; 636 } 637 } 638 } 639 else { 640 MapMask(15); 641 ptr = mem; 642 for (i = 0; i < 16; i++, ptr += ROWBYTES) { 643 cursorBuf[i] = pat[i]; 644 *ptr = ~pat[i]; 645 } 646 return; 647 } 648 649 offset = 0; 650 for (i = 1; i < 16; i <<= 1, offset += 16) { 651 int j; 652 653 MapMask(i); 654 ptr = mem; 655 for (j = 0; j < 16; j++, ptr += ROWBYTES) 656 *ptr = chr[j + offset]; 657 } 658 659 MapMask(15); 660 } 661 662 #endif /* SUPPORT_GRAPHICS */ 663