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