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