1 /* 2 * Copyright 2005, 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 put_character(' '); 392 break; 393 394 case '\r': 395 case '\0': 396 case '\a': // beep 397 break; 398 399 case 0x1b: 400 // escape character 401 sScreen.arg_count = -1; 402 sScreen.state = CONSOLE_STATE_GOT_ESCAPE; 403 break; 404 default: 405 put_character(c); 406 } 407 break; 408 case CONSOLE_STATE_GOT_ESCAPE: 409 // look for either commands with no argument, or the '[' character 410 switch (c) { 411 case '[': 412 sScreen.state = CONSOLE_STATE_SEEN_BRACKET; 413 break; 414 default: 415 sScreen.args[sScreen.arg_count] = 0; 416 process_vt100_command(c, false, sScreen.args, sScreen.arg_count + 1); 417 sScreen.state = CONSOLE_STATE_NORMAL; 418 } 419 break; 420 case CONSOLE_STATE_SEEN_BRACKET: 421 switch (c) { 422 case '0'...'9': 423 sScreen.arg_count = 0; 424 sScreen.args[sScreen.arg_count] = c - '0'; 425 sScreen.state = CONSOLE_STATE_PARSING_ARG; 426 break; 427 case '?': 428 // private DEC mode parameter follows - we ignore those anyway 429 break; 430 default: 431 process_vt100_command(c, true, sScreen.args, sScreen.arg_count + 1); 432 sScreen.state = CONSOLE_STATE_NORMAL; 433 } 434 break; 435 case CONSOLE_STATE_NEW_ARG: 436 switch (c) { 437 case '0'...'9': 438 sScreen.arg_count++; 439 if (sScreen.arg_count == MAX_ARGS) { 440 sScreen.state = CONSOLE_STATE_NORMAL; 441 break; 442 } 443 sScreen.args[sScreen.arg_count] = c - '0'; 444 sScreen.state = CONSOLE_STATE_PARSING_ARG; 445 break; 446 default: 447 process_vt100_command(c, true, sScreen.args, sScreen.arg_count + 1); 448 sScreen.state = CONSOLE_STATE_NORMAL; 449 } 450 break; 451 case CONSOLE_STATE_PARSING_ARG: 452 // parse args 453 switch (c) { 454 case '0'...'9': 455 sScreen.args[sScreen.arg_count] *= 10; 456 sScreen.args[sScreen.arg_count] += c - '0'; 457 break; 458 case ';': 459 sScreen.state = CONSOLE_STATE_NEW_ARG; 460 break; 461 default: 462 process_vt100_command(c, true, sScreen.args, sScreen.arg_count + 1); 463 sScreen.state = CONSOLE_STATE_NORMAL; 464 } 465 } 466 } 467 468 469 // #pragma mark - 470 471 472 status_t 473 blue_screen_init(void) 474 { 475 extern console_module_info gFrameBufferConsoleModule; 476 477 // we can't use get_module() here, since it's too early in the boot process 478 479 if (!frame_buffer_console_available()) 480 return B_ERROR; 481 482 sModule = &gFrameBufferConsoleModule; 483 return B_OK; 484 } 485 486 487 void 488 blue_screen_enter(void) 489 { 490 sScreen.attr = 0x0f; // black on white 491 sScreen.x = sScreen.y = 0; 492 sScreen.state = CONSOLE_STATE_NORMAL; 493 494 sModule->get_size(&sScreen.columns, &sScreen.rows); 495 #if !NO_CLEAR 496 sModule->clear(sScreen.attr); 497 #else 498 sModule->fill_glyph(0, sScreen.y, sScreen.columns, 3, ' ', sScreen.attr); 499 #endif 500 } 501 502 503 char 504 blue_screen_getchar(void) 505 { 506 return arch_debug_blue_screen_getchar(); 507 } 508 509 510 void 511 blue_screen_putchar(char c) 512 { 513 hide_cursor(); 514 515 parse_character(c); 516 517 update_cursor(sScreen.x, sScreen.y); 518 } 519 520 521 void 522 blue_screen_puts(const char *text) 523 { 524 hide_cursor(); 525 526 while (text[0] != '\0') { 527 parse_character(text[0]); 528 text++; 529 } 530 531 update_cursor(sScreen.x, sScreen.y); 532 } 533