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 <console.h>
14 #include <debug.h>
15 #include <arch/debug_console.h>
16 #include <safemode.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 = NULL;
66
67
68 static inline void
hide_cursor(void)69 hide_cursor(void)
70 {
71 sModule->move_cursor(-1, -1);
72 }
73
74
75 static inline void
update_cursor(int32 x,int32 y)76 update_cursor(int32 x, int32 y)
77 {
78 sModule->move_cursor(x, y);
79 }
80
81
82 static inline void
move_cursor(int32 x,int32 y)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
scroll_up(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
next_line(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
erase_line(erase_line_mode mode)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
back_space(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
put_character(char c)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
set_vt100_attributes(int32 * args,int32 argCount)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
process_vt100_command(const char c,bool seenBracket,int32 * args,int32 argCount)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
parse_character(char c)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
set_paging(int argc,char ** argv)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
blue_screen_init_early()596 blue_screen_init_early()
597 {
598 // we can't use get_module() here, since it's too early in the boot process
599 extern console_module_info gFrameBufferConsoleModule;
600 sModule = &gFrameBufferConsoleModule;
601
602 if (sModule->info.std_ops(B_MODULE_INIT) != B_OK) {
603 sModule = NULL;
604 return B_ERROR;
605 }
606
607 sScreen.paging = sScreen.paging_timeout = false;
608 return B_OK;
609 }
610
611
612 status_t
blue_screen_init()613 blue_screen_init()
614 {
615 if (sModule == NULL) {
616 // Early initialization must have previously failed, or never run.
617 if (blue_screen_init_early() != B_OK)
618 return B_ERROR;
619 }
620
621 sScreen.paging = !get_safemode_boolean(
622 "disable_onscreen_paging", false);
623 sScreen.paging_timeout = false;
624
625 add_debugger_command("paging", set_paging, "Enable or disable paging");
626 return B_OK;
627 }
628
629
630 status_t
blue_screen_enter(bool debugOutput)631 blue_screen_enter(bool debugOutput)
632 {
633 sScreen.attr = debugOutput ? 0xf0 : 0x0f;
634 // black on white for KDL, white on black for debug output
635 sScreen.boot_debug_output = debugOutput;
636 sScreen.ignore_output = false;
637
638 sScreen.x = sScreen.y = 0;
639 sScreen.state = CONSOLE_STATE_NORMAL;
640
641 if (sModule == NULL)
642 return B_NO_INIT;
643
644 sModule->get_size(&sScreen.columns, &sScreen.rows);
645 #if !NO_CLEAR
646 sModule->clear(sScreen.attr);
647 #else
648 sModule->fill_glyph(0, sScreen.y, sScreen.columns, 3, ' ', sScreen.attr);
649 #endif
650 return B_OK;
651 }
652
653
654 bool
blue_screen_paging_enabled(void)655 blue_screen_paging_enabled(void)
656 {
657 return sScreen.paging;
658 }
659
660
661 void
blue_screen_set_paging(bool enabled)662 blue_screen_set_paging(bool enabled)
663 {
664 sScreen.paging = enabled;
665 }
666
667
668 void
blue_screen_clear_screen(void)669 blue_screen_clear_screen(void)
670 {
671 sModule->clear(sScreen.attr);
672 move_cursor(0, 0);
673 }
674
675
676 int
blue_screen_try_getchar(void)677 blue_screen_try_getchar(void)
678 {
679 return arch_debug_blue_screen_try_getchar();
680 }
681
682
683 char
blue_screen_getchar(void)684 blue_screen_getchar(void)
685 {
686 return arch_debug_blue_screen_getchar();
687 }
688
689
690 void
blue_screen_putchar(char c)691 blue_screen_putchar(char c)
692 {
693 if (sScreen.ignore_output
694 && (in_command_invocation() || sScreen.boot_debug_output))
695 return;
696
697 sScreen.ignore_output = false;
698 hide_cursor();
699
700 parse_character(c);
701
702 update_cursor(sScreen.x, sScreen.y);
703 }
704
705
706 void
blue_screen_puts(const char * text)707 blue_screen_puts(const char *text)
708 {
709 if (sScreen.ignore_output
710 && (in_command_invocation() || sScreen.boot_debug_output))
711 return;
712
713 sScreen.ignore_output = false;
714 hide_cursor();
715
716 while (text[0] != '\0') {
717 parse_character(text[0]);
718 text++;
719 }
720
721 update_cursor(sScreen.x, sScreen.y);
722 }
723