1 /* 2 * Copyright 2005-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "blue_screen.h" 8 9 #include <KernelExport.h> 10 #include <frame_buffer_console.h> 11 #include <console.h> 12 #include <arch/debug_console.h> 13 14 #include <string.h> 15 #include <stdio.h> 16 17 #include "debug_commands.h" 18 19 20 #define USE_SCROLLING 0 21 #define NO_CLEAR 1 22 23 #define MAX_ARGS 8 24 25 #define FMASK 0x0f 26 #define BMASK 0x70 27 28 typedef enum { 29 CONSOLE_STATE_NORMAL = 0, 30 CONSOLE_STATE_GOT_ESCAPE, 31 CONSOLE_STATE_SEEN_BRACKET, 32 CONSOLE_STATE_NEW_ARG, 33 CONSOLE_STATE_PARSING_ARG, 34 } console_state; 35 36 typedef enum { 37 LINE_ERASE_WHOLE, 38 LINE_ERASE_LEFT, 39 LINE_ERASE_RIGHT 40 } erase_line_mode; 41 42 struct screen_info { 43 int32 columns; 44 int32 rows; 45 int32 x, y; 46 uint8 attr; 47 bool bright_attr; 48 bool reverse_attr; 49 int32 in_command_rows; 50 bool paging; 51 bool boot_debug_output; 52 bool ignore_output; 53 54 // state machine 55 console_state state; 56 int32 arg_count; 57 int32 args[MAX_ARGS]; 58 } sScreen; 59 60 console_module_info *sModule; 61 62 63 static inline void 64 hide_cursor(void) 65 { 66 sModule->move_cursor(-1, -1); 67 } 68 69 70 static inline void 71 update_cursor(int32 x, int32 y) 72 { 73 sModule->move_cursor(x, y); 74 } 75 76 77 static inline void 78 move_cursor(int32 x, int32 y) 79 { 80 sScreen.x = x; 81 sScreen.y = y; 82 update_cursor(x, y); 83 } 84 85 86 #if USE_SCROLLING 87 88 /*! Scroll from the cursor line up to the top of the scroll region up one 89 line. 90 */ 91 static void 92 scroll_up(void) 93 { 94 // move the screen up one 95 sModule->blit(0, 1, sScreen.columns, sScreen.rows - 1, 0, 0); 96 97 // clear the bottom line 98 sModule->fill_glyph(0, 0, sScreen.columns, 1, ' ', sScreen.attr); 99 } 100 #endif 101 102 103 static void 104 next_line(void) 105 { 106 #if USE_SCROLLING 107 // TODO: scrolling is usually too slow; we could probably just remove it 108 if (sScreen.y == sScreen.rows - 1) 109 scroll_up(); 110 else 111 sScreen.y++; 112 #else 113 if (in_command_invocation()) 114 sScreen.in_command_rows++; 115 else 116 sScreen.in_command_rows = 0; 117 118 if (sScreen.paging && ((sScreen.in_command_rows > 0 119 && ((sScreen.in_command_rows + 3) % sScreen.rows) == 0) 120 || (sScreen.boot_debug_output && sScreen.y == sScreen.rows - 1))) { 121 // Use the paging mechanism: either, we're in the debugger, and a 122 // command is being executed, or we're currently showing boot debug 123 // output 124 const char *text = "Press key to continue, Q to quit"; 125 int32 length = strlen(text); 126 if (sScreen.x + length > sScreen.columns) { 127 // make sure we don't overwrite too much 128 text = "P"; 129 length = 1; 130 } 131 132 for (int32 i = 0; i < length; i++) { 133 // yellow on black (or reverse, during boot) 134 sModule->put_glyph(sScreen.columns - length + i, sScreen.y, 135 text[i], sScreen.boot_debug_output ? 0x6f : 0xf6); 136 } 137 138 char c = blue_screen_getchar(); 139 if (c == 'q') 140 sScreen.ignore_output = true; 141 142 // remove on screen text again 143 sModule->fill_glyph(sScreen.columns - length, sScreen.y, length, 144 1, ' ', sScreen.attr); 145 } 146 if (sScreen.y == sScreen.rows - 1) { 147 sScreen.y = 0; 148 sModule->fill_glyph(0, 0, sScreen.columns, 2, ' ', sScreen.attr); 149 } else 150 sScreen.y++; 151 #endif 152 153 #if NO_CLEAR 154 if (sScreen.y + 2 < sScreen.rows) { 155 sModule->fill_glyph(0, (sScreen.y + 2) % sScreen.rows, sScreen.columns, 156 1, ' ', sScreen.attr); 157 } 158 #endif 159 sScreen.x = 0; 160 } 161 162 163 static void 164 erase_line(erase_line_mode mode) 165 { 166 switch (mode) { 167 case LINE_ERASE_WHOLE: 168 sModule->fill_glyph(0, sScreen.y, sScreen.columns, 1, ' ', 169 sScreen.attr); 170 break; 171 case LINE_ERASE_LEFT: 172 sModule->fill_glyph(0, sScreen.y, sScreen.x + 1, 1, ' ', 173 sScreen.attr); 174 break; 175 case LINE_ERASE_RIGHT: 176 sModule->fill_glyph(sScreen.x, sScreen.y, sScreen.columns 177 - sScreen.x, 1, ' ', sScreen.attr); 178 break; 179 } 180 } 181 182 183 static void 184 back_space(void) 185 { 186 if (sScreen.x <= 0) 187 return; 188 189 sScreen.x--; 190 sModule->put_glyph(sScreen.x, sScreen.y, ' ', sScreen.attr); 191 } 192 193 194 static void 195 put_character(char c) 196 { 197 if (++sScreen.x >= sScreen.columns) { 198 next_line(); 199 sScreen.x++; 200 } 201 202 sModule->put_glyph(sScreen.x - 1, sScreen.y, c, sScreen.attr); 203 } 204 205 206 static void 207 set_vt100_attributes(int32 *args, int32 argCount) 208 { 209 if (argCount == 0) { 210 // that's the default (attributes off) 211 argCount++; 212 args[0] = 0; 213 } 214 215 for (int32 i = 0; i < argCount; i++) { 216 switch (args[i]) { 217 case 0: // reset 218 sScreen.attr = sScreen.boot_debug_output ? 0xf0 : 0x0f; 219 sScreen.bright_attr = true; 220 sScreen.reverse_attr = false; 221 break; 222 case 1: // bright 223 sScreen.bright_attr = true; 224 sScreen.attr |= 0x08; // set the bright bit 225 break; 226 case 2: // dim 227 sScreen.bright_attr = false; 228 sScreen.attr &= ~0x08; // unset the bright bit 229 break; 230 case 4: // underscore we can't do 231 break; 232 case 5: // blink 233 sScreen.attr |= 0x80; // set the blink bit 234 break; 235 case 7: // reverse 236 sScreen.reverse_attr = true; 237 sScreen.attr = ((sScreen.attr & BMASK) >> 4) 238 | ((sScreen.attr & FMASK) << 4); 239 if (sScreen.bright_attr) 240 sScreen.attr |= 0x08; 241 break; 242 case 8: // hidden? 243 break; 244 245 /* foreground colors */ 246 case 30: // black 247 case 31: // red 248 case 32: // green 249 case 33: // yellow 250 case 34: // blue 251 case 35: // magenta 252 case 36: // cyan 253 case 37: // white 254 { 255 const uint8 colors[] = {0, 4, 2, 6, 1, 5, 3, 7}; 256 sScreen.attr = (sScreen.attr & ~FMASK) | colors[args[i] - 30] 257 | (sScreen.bright_attr ? 0x08 : 0); 258 break; 259 } 260 261 /* background colors */ 262 case 40: sScreen.attr = (sScreen.attr & ~BMASK) | (0 << 4); break; // black 263 case 41: sScreen.attr = (sScreen.attr & ~BMASK) | (4 << 4); break; // red 264 case 42: sScreen.attr = (sScreen.attr & ~BMASK) | (2 << 4); break; // green 265 case 43: sScreen.attr = (sScreen.attr & ~BMASK) | (6 << 4); break; // yellow 266 case 44: sScreen.attr = (sScreen.attr & ~BMASK) | (1 << 4); break; // blue 267 case 45: sScreen.attr = (sScreen.attr & ~BMASK) | (5 << 4); break; // magenta 268 case 46: sScreen.attr = (sScreen.attr & ~BMASK) | (3 << 4); break; // cyan 269 case 47: sScreen.attr = (sScreen.attr & ~BMASK) | (7 << 4); break; // white 270 } 271 } 272 } 273 274 275 static bool 276 process_vt100_command(const char c, bool seenBracket, int32 *args, int32 argCount) 277 { 278 bool ret = true; 279 280 // kprintf("process_vt100_command: c '%c', argCount %ld, arg[0] %ld, arg[1] %ld, seenBracket %d\n", 281 // c, argCount, args[0], args[1], seenBracket); 282 283 if (seenBracket) { 284 switch (c) { 285 case 'H': /* set cursor position */ 286 case 'f': { 287 int32 row = argCount > 0 ? args[0] : 1; 288 int32 col = argCount > 1 ? args[1] : 1; 289 if (row > 0) 290 row--; 291 if (col > 0) 292 col--; 293 move_cursor(col, row); 294 break; 295 } 296 case 'A': { /* move up */ 297 int32 deltay = argCount > 0 ? -args[0] : -1; 298 if (deltay == 0) 299 deltay = -1; 300 move_cursor(sScreen.x, sScreen.y + deltay); 301 break; 302 } 303 case 'e': 304 case 'B': { /* move down */ 305 int32 deltay = argCount > 0 ? args[0] : 1; 306 if (deltay == 0) 307 deltay = 1; 308 move_cursor(sScreen.x, sScreen.y + deltay); 309 break; 310 } 311 case 'D': { /* move left */ 312 int32 deltax = argCount > 0 ? -args[0] : -1; 313 if (deltax == 0) 314 deltax = -1; 315 move_cursor(sScreen.x + deltax, sScreen.y); 316 break; 317 } 318 case 'a': 319 case 'C': { /* move right */ 320 int32 deltax = argCount > 0 ? args[0] : 1; 321 if (deltax == 0) 322 deltax = 1; 323 move_cursor(sScreen.x + deltax, sScreen.y); 324 break; 325 } 326 case '`': 327 case 'G': { /* set X position */ 328 int32 newx = argCount > 0 ? args[0] : 1; 329 if (newx > 0) 330 newx--; 331 move_cursor(newx, sScreen.y); 332 break; 333 } 334 case 'd': { /* set y position */ 335 int32 newy = argCount > 0 ? args[0] : 1; 336 if (newy > 0) 337 newy--; 338 move_cursor(sScreen.x, newy); 339 break; 340 } 341 #if 0 342 case 's': /* save current cursor */ 343 save_cur(console, false); 344 break; 345 case 'u': /* restore cursor */ 346 restore_cur(console, false); 347 break; 348 case 'r': { /* set scroll region */ 349 int32 low = argCount > 0 ? args[0] : 1; 350 int32 high = argCount > 1 ? args[1] : sScreen.lines; 351 if (low <= high) 352 set_scroll_region(console, low - 1, high - 1); 353 break; 354 } 355 case 'L': { /* scroll virtual down at cursor */ 356 int32 lines = argCount > 0 ? args[0] : 1; 357 while (lines > 0) { 358 scrdown(console); 359 lines--; 360 } 361 break; 362 } 363 case 'M': { /* scroll virtual up at cursor */ 364 int32 lines = argCount > 0 ? args[0] : 1; 365 while (lines > 0) { 366 scrup(console); 367 lines--; 368 } 369 break; 370 } 371 #endif 372 case 'K': 373 if (argCount == 0 || args[0] == 0) { 374 // erase to end of line 375 erase_line(LINE_ERASE_RIGHT); 376 } else if (argCount > 0) { 377 if (args[0] == 1) 378 erase_line(LINE_ERASE_LEFT); 379 else if (args[0] == 2) 380 erase_line(LINE_ERASE_WHOLE); 381 } 382 break; 383 #if 0 384 case 'J': 385 if (argCount == 0 || args[0] == 0) { 386 // erase to end of screen 387 erase_screen(console, SCREEN_ERASE_DOWN); 388 } else { 389 if (args[0] == 1) 390 erase_screen(console, SCREEN_ERASE_UP); 391 else if (args[0] == 2) 392 erase_screen(console, SCREEN_ERASE_WHOLE); 393 } 394 break; 395 #endif 396 case 'm': 397 if (argCount >= 0) 398 set_vt100_attributes(args, argCount); 399 break; 400 default: 401 ret = false; 402 } 403 } else { 404 switch (c) { 405 #if 0 406 case 'c': 407 reset_console(console); 408 break; 409 case 'D': 410 rlf(console); 411 break; 412 case 'M': 413 lf(console); 414 break; 415 case '7': 416 save_cur(console, true); 417 break; 418 case '8': 419 restore_cur(console, true); 420 break; 421 #endif 422 default: 423 ret = false; 424 } 425 } 426 427 return ret; 428 } 429 430 431 static void 432 parse_character(char c) 433 { 434 switch (sScreen.state) { 435 case CONSOLE_STATE_NORMAL: 436 // just output the stuff 437 switch (c) { 438 case '\n': 439 next_line(); 440 break; 441 case 0x8: 442 back_space(); 443 break; 444 case '\t': 445 // ToDo: real tab... 446 sScreen.x = (sScreen.x + 8) & ~7; 447 if (sScreen.x >= sScreen.columns) 448 next_line(); 449 break; 450 451 case '\r': 452 case '\0': 453 case '\a': // beep 454 break; 455 456 case 0x1b: 457 // escape character 458 sScreen.arg_count = -1; 459 sScreen.state = CONSOLE_STATE_GOT_ESCAPE; 460 break; 461 default: 462 put_character(c); 463 } 464 break; 465 case CONSOLE_STATE_GOT_ESCAPE: 466 // look for either commands with no argument, or the '[' character 467 switch (c) { 468 case '[': 469 sScreen.state = CONSOLE_STATE_SEEN_BRACKET; 470 break; 471 default: 472 sScreen.args[sScreen.arg_count] = 0; 473 process_vt100_command(c, false, sScreen.args, 474 sScreen.arg_count + 1); 475 sScreen.state = CONSOLE_STATE_NORMAL; 476 } 477 break; 478 case CONSOLE_STATE_SEEN_BRACKET: 479 switch (c) { 480 case '0'...'9': 481 sScreen.arg_count = 0; 482 sScreen.args[sScreen.arg_count] = c - '0'; 483 sScreen.state = CONSOLE_STATE_PARSING_ARG; 484 break; 485 case '?': 486 // private DEC mode parameter follows - we ignore those 487 // anyway 488 break; 489 default: 490 process_vt100_command(c, true, sScreen.args, 491 sScreen.arg_count + 1); 492 sScreen.state = CONSOLE_STATE_NORMAL; 493 } 494 break; 495 case CONSOLE_STATE_NEW_ARG: 496 switch (c) { 497 case '0'...'9': 498 sScreen.arg_count++; 499 if (sScreen.arg_count == MAX_ARGS) { 500 sScreen.state = CONSOLE_STATE_NORMAL; 501 break; 502 } 503 sScreen.args[sScreen.arg_count] = c - '0'; 504 sScreen.state = CONSOLE_STATE_PARSING_ARG; 505 break; 506 default: 507 process_vt100_command(c, true, sScreen.args, 508 sScreen.arg_count + 1); 509 sScreen.state = CONSOLE_STATE_NORMAL; 510 } 511 break; 512 case CONSOLE_STATE_PARSING_ARG: 513 // parse args 514 switch (c) { 515 case '0'...'9': 516 sScreen.args[sScreen.arg_count] *= 10; 517 sScreen.args[sScreen.arg_count] += c - '0'; 518 break; 519 case ';': 520 sScreen.state = CONSOLE_STATE_NEW_ARG; 521 break; 522 default: 523 process_vt100_command(c, true, sScreen.args, 524 sScreen.arg_count + 1); 525 sScreen.state = CONSOLE_STATE_NORMAL; 526 } 527 } 528 } 529 530 531 static int 532 set_paging(int argc, char **argv) 533 { 534 if (argc > 1 && !strcmp(argv[1], "--help")) { 535 kprintf("usage: %s [on|off]\n", argv[0]); 536 return 0; 537 } 538 539 if (argc == 1) 540 sScreen.paging = !sScreen.paging; 541 else if (!strcmp(argv[1], "on")) 542 sScreen.paging = true; 543 else if (!strcmp(argv[1], "off")) 544 sScreen.paging = false; 545 else 546 sScreen.paging = parse_expression(argv[1]) != 0; 547 548 kprintf("paging is turned %s now.\n", sScreen.paging ? "on" : "off"); 549 return 0; 550 } 551 552 553 // #pragma mark - 554 555 556 status_t 557 blue_screen_init(void) 558 { 559 extern console_module_info gFrameBufferConsoleModule; 560 561 // we can't use get_module() here, since it's too early in the boot process 562 563 if (!frame_buffer_console_available()) 564 return B_ERROR; 565 566 sModule = &gFrameBufferConsoleModule; 567 sScreen.paging = true; 568 569 add_debugger_command("paging", set_paging, "Enable or disable paging"); 570 return B_OK; 571 } 572 573 574 status_t 575 blue_screen_enter(bool debugOutput) 576 { 577 sScreen.attr = debugOutput ? 0xf0 : 0x0f; 578 // black on white for KDL, white on black for debug output 579 sScreen.boot_debug_output = debugOutput; 580 sScreen.ignore_output = false; 581 582 sScreen.x = sScreen.y = 0; 583 sScreen.state = CONSOLE_STATE_NORMAL; 584 585 if (sModule == NULL) 586 return B_NO_INIT; 587 588 sModule->get_size(&sScreen.columns, &sScreen.rows); 589 #if !NO_CLEAR 590 sModule->clear(sScreen.attr); 591 #else 592 sModule->fill_glyph(0, sScreen.y, sScreen.columns, 3, ' ', sScreen.attr); 593 #endif 594 return B_OK; 595 } 596 597 598 void 599 blue_screen_clear_screen(void) 600 { 601 sModule->clear(sScreen.attr); 602 move_cursor(0, 0); 603 } 604 605 606 char 607 blue_screen_getchar(void) 608 { 609 return arch_debug_blue_screen_getchar(); 610 } 611 612 613 void 614 blue_screen_putchar(char c) 615 { 616 if (sScreen.ignore_output 617 && (in_command_invocation() || sScreen.boot_debug_output)) 618 return; 619 620 sScreen.ignore_output = false; 621 hide_cursor(); 622 623 parse_character(c); 624 625 update_cursor(sScreen.x, sScreen.y); 626 } 627 628 629 void 630 blue_screen_puts(const char *text) 631 { 632 if (sScreen.ignore_output 633 && (in_command_invocation() || sScreen.boot_debug_output)) 634 return; 635 636 sScreen.ignore_output = false; 637 hide_cursor(); 638 639 while (text[0] != '\0') { 640 parse_character(text[0]); 641 text++; 642 } 643 644 update_cursor(sScreen.x, sScreen.y); 645 } 646