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