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