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