libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / system / kernel / debug / blue_screen.cpp
blobe298cf05e4ef4aaa94f86b055305edbb86739531
1 /*
2 * Copyright 2005-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
5 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
6 * Distributed under the terms of the NewOS License.
7 */
10 #include "blue_screen.h"
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>
19 #include <string.h>
20 #include <stdio.h>
22 #include "debug_commands.h"
25 #define USE_SCROLLING 0
26 #define NO_CLEAR 1
28 #define MAX_ARGS 8
30 #define FMASK 0x0f
31 #define BMASK 0x70
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;
41 typedef enum {
42 LINE_ERASE_WHOLE,
43 LINE_ERASE_LEFT,
44 LINE_ERASE_RIGHT
45 } erase_line_mode;
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;
60 // state machine
61 console_state state;
62 int32 arg_count;
63 int32 args[MAX_ARGS];
64 } sScreen;
66 console_module_info *sModule;
69 static inline void
70 hide_cursor(void)
72 sModule->move_cursor(-1, -1);
76 static inline void
77 update_cursor(int32 x, int32 y)
79 sModule->move_cursor(x, y);
83 static inline void
84 move_cursor(int32 x, int32 y)
86 sScreen.x = x;
87 sScreen.y = y;
88 update_cursor(x, y);
92 #if USE_SCROLLING
94 /*! Scroll from the cursor line up to the top of the scroll region up one
95 line.
97 static void
98 scroll_up(void)
100 // move the screen up one
101 sModule->blit(0, 1, sScreen.columns, sScreen.rows - 1, 0, 0);
103 // clear the bottom line
104 sModule->fill_glyph(0, 0, sScreen.columns, 1, ' ', sScreen.attr);
106 #endif
109 static void
110 next_line(void)
112 bool abortCommand = false;
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;
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;
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);
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;
162 // remove on screen text again
163 sModule->fill_glyph(sScreen.columns - length, sScreen.y, length,
164 1, ' ', sScreen.attr);
167 if (sScreen.in_command_rows > 0)
168 sScreen.in_command_rows += 2;
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
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);
182 #endif
183 sScreen.x = 0;
185 if (abortCommand) {
186 abort_debugger_command();
187 // should not return
192 static void
193 erase_line(erase_line_mode mode)
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;
212 static void
213 back_space(void)
215 if (sScreen.x <= 0)
216 return;
218 sScreen.x--;
219 sModule->put_glyph(sScreen.x, sScreen.y, ' ', sScreen.attr);
223 static void
224 put_character(char c)
226 if (++sScreen.x >= sScreen.columns) {
227 next_line();
228 sScreen.x++;
231 sModule->put_glyph(sScreen.x - 1, sScreen.y, c, sScreen.attr);
235 static void
236 set_vt100_attributes(int32 *args, int32 argCount)
238 if (argCount == 0) {
239 // that's the default (attributes off)
240 argCount++;
241 args[0] = 0;
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;
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
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;
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
304 static bool
305 process_vt100_command(const char c, bool seenBracket, int32 *args,
306 int32 argCount)
308 bool ret = true;
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);
313 if (seenBracket) {
314 switch (c) {
315 case 'H': // set cursor position
316 case 'f':
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;
327 case 'A': // move up
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;
335 case 'e':
336 case 'B': // move down
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;
344 case 'D': // move left
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;
352 case 'a':
353 case 'C': // move right
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;
361 case '`':
362 case 'G': // set X position
364 int32 newX = argCount > 0 ? args[0] : 1;
365 if (newX > 0)
366 newX--;
367 move_cursor(newX, sScreen.y);
368 break;
370 case 'd': // set y position
372 int32 newY = argCount > 0 ? args[0] : 1;
373 if (newY > 0)
374 newY--;
375 move_cursor(sScreen.x, newY);
376 break;
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
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;
393 case 'L': // scroll virtual down at cursor
395 int32 lines = argCount > 0 ? args[0] : 1;
396 while (lines > 0) {
397 scrdown(console);
398 lines--;
400 break;
402 case 'M': // scroll virtual up at cursor
404 int32 lines = argCount > 0 ? args[0] : 1;
405 while (lines > 0) {
406 scrup(console);
407 lines--;
409 break;
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);
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);
434 break;
435 #endif
436 case 'm':
437 if (argCount >= 0)
438 set_vt100_attributes(args, argCount);
439 break;
440 default:
441 ret = false;
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;
467 return ret;
471 static void
472 parse_character(char c)
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;
491 case '\r':
492 case '\0':
493 case '\a': // beep
494 break;
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);
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;
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;
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;
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;
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;
571 #ifndef _BOOT_MODE
572 static int
573 set_paging(int argc, char **argv)
575 if (argc > 1 && !strcmp(argv[1], "--help")) {
576 kprintf("usage: %s [on|off]\n", argv[0]);
577 return 0;
580 if (argc == 1)
581 sScreen.paging = !sScreen.paging;
582 else if (!strcmp(argv[1], "on"))
583 sScreen.paging = true;
584 else if (!strcmp(argv[1], "off"))
585 sScreen.paging = false;
586 else
587 sScreen.paging = parse_expression(argv[1]) != 0;
589 kprintf("paging is turned %s now.\n", sScreen.paging ? "on" : "off");
590 return 0;
592 #endif // !_BOOT_MODE
595 // #pragma mark -
598 status_t
599 blue_screen_init(void)
601 extern console_module_info gFrameBufferConsoleModule;
603 // we can't use get_module() here, since it's too early in the boot process
605 if (!frame_buffer_console_available())
606 return B_ERROR;
608 sModule = &gFrameBufferConsoleModule;
609 #ifdef _BOOT_MODE
610 sScreen.paging = false;
611 #else
612 sScreen.paging = !get_safemode_boolean(
613 "disable_onscreen_paging", false);
614 sScreen.paging_timeout = false;
616 add_debugger_command("paging", set_paging, "Enable or disable paging");
617 #endif
618 return B_OK;
622 status_t
623 blue_screen_enter(bool debugOutput)
625 sScreen.attr = debugOutput ? 0xf0 : 0x0f;
626 // black on white for KDL, white on black for debug output
627 sScreen.boot_debug_output = debugOutput;
628 sScreen.ignore_output = false;
630 sScreen.x = sScreen.y = 0;
631 sScreen.state = CONSOLE_STATE_NORMAL;
633 if (sModule == NULL)
634 return B_NO_INIT;
636 sModule->get_size(&sScreen.columns, &sScreen.rows);
637 #if !NO_CLEAR
638 sModule->clear(sScreen.attr);
639 #else
640 sModule->fill_glyph(0, sScreen.y, sScreen.columns, 3, ' ', sScreen.attr);
641 #endif
642 return B_OK;
646 bool
647 blue_screen_paging_enabled(void)
649 return sScreen.paging;
653 void
654 blue_screen_set_paging(bool enabled)
656 sScreen.paging = enabled;
660 void
661 blue_screen_clear_screen(void)
663 sModule->clear(sScreen.attr);
664 move_cursor(0, 0);
668 #ifndef _BOOT_MODE
670 blue_screen_try_getchar(void)
672 return arch_debug_blue_screen_try_getchar();
676 char
677 blue_screen_getchar(void)
679 return arch_debug_blue_screen_getchar();
681 #endif
684 void
685 blue_screen_putchar(char c)
687 if (sScreen.ignore_output
688 && (in_command_invocation() || sScreen.boot_debug_output))
689 return;
691 sScreen.ignore_output = false;
692 hide_cursor();
694 parse_character(c);
696 update_cursor(sScreen.x, sScreen.y);
700 void
701 blue_screen_puts(const char *text)
703 if (sScreen.ignore_output
704 && (in_command_invocation() || sScreen.boot_debug_output))
705 return;
707 sScreen.ignore_output = false;
708 hide_cursor();
710 while (text[0] != '\0') {
711 parse_character(text[0]);
712 text++;
715 update_cursor(sScreen.x, sScreen.y);