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