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