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