1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Polled I/O safe ANSI terminal emulator module; 31 * Supporting TERM types 'sun' and 'sun-color, parsing 32 * ANSI x3.64 escape sequences, and the like. (See wscons(7d) 33 * for more information). 34 * 35 * IMPORTANT: 36 * 37 * The functions in this file *must* be able to function in 38 * standalone mode, ie. on a quiesced system. In that state, 39 * access is single threaded, only one CPU is running. 40 * System services are NOT available. 41 * 42 * The following restrictions pertain to every function 43 * in this file: 44 * 45 * - CANNOT use the DDI or LDI interfaces 46 * - CANNOT call system services 47 * - CANNOT use mutexes 48 * - CANNOT wait for interrupts 49 * - CANNOT allocate memory 50 * 51 */ 52 53 #include <sys/types.h> 54 #include <sys/ascii.h> 55 #include <sys/visual_io.h> 56 #include <sys/font.h> 57 #include <sys/tem.h> 58 #include <sys/tem_impl.h> 59 #include <sys/ksynch.h> 60 #include <sys/sysmacros.h> 61 #include <sys/mutex.h> 62 63 static void tem_display(struct tem *, 64 struct vis_consdisplay *, 65 cred_t *, enum called_from); 66 static void tem_cursor(struct tem *, 67 struct vis_conscursor *, 68 cred_t *, enum called_from); 69 static void tem_control(struct tem *, uchar_t, 70 cred_t *, enum called_from); 71 static void tem_setparam(struct tem *, int, int); 72 static void tem_selgraph(struct tem *); 73 static void tem_chkparam(struct tem *, uchar_t, 74 cred_t *, enum called_from); 75 static void tem_getparams(struct tem *, uchar_t, 76 cred_t *, enum called_from); 77 static void tem_outch(struct tem *, uchar_t, 78 cred_t *, enum called_from); 79 static void tem_parse(struct tem *, uchar_t, 80 cred_t *, enum called_from); 81 82 static void tem_new_line(struct tem *, 83 cred_t *, enum called_from); 84 static void tem_cr(struct tem *); 85 static void tem_lf(struct tem *, 86 cred_t *, enum called_from); 87 static void tem_send_data(struct tem *, cred_t *, 88 enum called_from); 89 static void tem_cls(struct tem *, 90 cred_t *, enum called_from); 91 static void tem_tab(struct tem *, 92 cred_t *, enum called_from); 93 static void tem_back_tab(struct tem *, 94 cred_t *, enum called_from); 95 static void tem_clear_tabs(struct tem *, int); 96 static void tem_set_tab(struct tem *); 97 static void tem_mv_cursor(struct tem *, int, int, 98 cred_t *, enum called_from); 99 static void tem_shift(struct tem *, int, int, 100 cred_t *, enum called_from); 101 static void tem_scroll(struct tem *, int, int, 102 int, int, cred_t *, enum called_from); 103 static void tem_clear_chars(struct tem *tem, 104 int count, screen_pos_t row, screen_pos_t col, 105 cred_t *credp, enum called_from called_from); 106 static void tem_copy_area(struct tem *tem, 107 screen_pos_t s_col, screen_pos_t s_row, 108 screen_pos_t e_col, screen_pos_t e_row, 109 screen_pos_t t_col, screen_pos_t t_row, 110 cred_t *credp, enum called_from called_from); 111 static void tem_image_display(struct tem *, uchar_t *, 112 int, int, screen_pos_t, screen_pos_t, 113 cred_t *, enum called_from); 114 static void tem_bell(struct tem *tem, 115 enum called_from called_from); 116 static void tem_get_color(struct tem *tem, 117 text_color_t *fg, text_color_t *bg); 118 static void tem_pix_clear_prom_output(struct tem *tem, 119 cred_t *credp, enum called_from called_from); 120 121 #ifdef _HAVE_TEM_FIRMWARE 122 #define DEFAULT_ANSI_FOREGROUND ANSI_COLOR_BLACK 123 #define DEFAULT_ANSI_BACKGROUND ANSI_COLOR_WHITE 124 #else /* _HAVE_TEM_FIRMWARE */ 125 #define DEFAULT_ANSI_FOREGROUND ANSI_COLOR_WHITE 126 #define DEFAULT_ANSI_BACKGROUND ANSI_COLOR_BLACK 127 #endif 128 129 130 /* BEGIN CSTYLED */ 131 /* Bk Rd Gr Br Bl Mg Cy Wh */ 132 static text_color_t fg_dim_xlate[] = { 1, 5, 3, 7, 2, 6, 4, 8 }; 133 static text_color_t fg_brt_xlate[] = { 9, 13, 11, 15, 10, 14, 12, 0 }; 134 static text_color_t bg_xlate[] = { 1, 5, 3, 7, 2, 6, 4, 0 }; 135 /* END CSTYLED */ 136 137 138 text_cmap_t cmap4_to_24 = { 139 /* BEGIN CSTYLED */ 140 /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 141 Wh+ Bk Bl Gr Cy Rd Mg Br Wh Bk+ Bl+ Gr+ Cy+ Rd+ Mg+ Yw */ 142 0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff, 143 0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff, 144 0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00 145 /* END CSTYLED */ 146 }; 147 148 #define PIX4TO32(pix4) (pixel32_t)( \ 149 cmap4_to_24.red[pix4] << 16 | \ 150 cmap4_to_24.green[pix4] << 8 | \ 151 cmap4_to_24.blue[pix4]) 152 153 /* 154 * Fonts are statically linked with this module. At some point an 155 * RFE might be desireable to allow dynamic font loading. The 156 * original intention to facilitate dynamic fonts can be seen 157 * by examining the data structures and set_font(). As much of 158 * the original code is retained but modified to be suited to 159 * traversing a list of static fonts. 160 */ 161 extern struct fontlist fonts[]; 162 163 #define DEFAULT_FONT_DATA font_data_12x22 164 165 extern bitmap_data_t font_data_12x22; 166 extern bitmap_data_t font_data_7x14; 167 extern bitmap_data_t font_data_6x10; 168 /* 169 * Must be sorted by font size in descending order 170 */ 171 struct fontlist fonts[] = { 172 { &font_data_12x22, NULL }, 173 { &font_data_7x14, NULL }, 174 { &font_data_6x10, NULL }, 175 { NULL, NULL } 176 }; 177 178 #define INVERSE(ch) (ch ^ 0xff) 179 180 #define BIT_TO_PIX(tem, c, fg, bg) { \ 181 ASSERT((tem)->state->in_fp.f_bit2pix != NULL); \ 182 (void) (*(tem)->state->in_fp.f_bit2pix)((tem), (c), (fg), (bg));\ 183 } 184 185 void 186 tem_check_first_time( 187 struct tem *tem, 188 cred_t *credp, 189 enum called_from called_from) 190 { 191 static int first_time = 1; 192 193 /* 194 * Realign the console cursor. We did this in tem_init(). 195 * However, drivers in the console stream may emit additional 196 * messages before we are ready. This causes text overwrite 197 * on the screen. This is a workaround. 198 */ 199 if (first_time && tem->state->display_mode == VIS_TEXT) { 200 tem_text_cursor(tem, VIS_GET_CURSOR, credp, called_from); 201 tem_align_cursor(tem); 202 } 203 first_time = 0; 204 205 } 206 207 /* 208 * This entry point handles output requests from restricted contexts like 209 * kmdb, where services like mutexes are not available. This function 210 * is entered when OBP or when a kernel debugger (such as kmdb) 211 * are generating console output. In those cases, power management 212 * concerns are handled by the abort sequence initiation (ie. when 213 * the user hits L1+A or the equivalent to enter OBP or the debugger.). 214 * It is also entered when the kernel is panicing. 215 */ 216 void 217 tem_polled_write( 218 struct tem *tem, 219 uchar_t *buf, 220 int len) 221 { 222 223 ASSERT(tem->hdl != NULL); 224 225 tem_check_first_time(tem, kcred, CALLED_FROM_STANDALONE); 226 tem_terminal_emulate(tem, buf, len, NULL, CALLED_FROM_STANDALONE); 227 } 228 229 230 /* 231 * This is the main entry point into the terminal emulator. 232 * 233 * For each data message coming downstream, ANSI assumes that it is composed 234 * of ASCII characters, which are treated as a byte-stream input to the 235 * parsing state machine. All data is parsed immediately -- there is 236 * no enqueing. 237 */ 238 void 239 tem_terminal_emulate( 240 struct tem *tem, 241 uchar_t *buf, 242 int len, 243 cred_t *credp, 244 enum called_from called_from) 245 { 246 struct tem_state *tems = tem->state; 247 248 ASSERT((called_from == CALLED_FROM_STANDALONE) || 249 MUTEX_HELD(&tem->lock)); 250 251 (*tems->in_fp.f_cursor) 252 (tem, VIS_HIDE_CURSOR, credp, called_from); 253 254 for (; len > 0; len--, buf++) { 255 tem_parse(tem, *buf, credp, called_from); 256 } 257 258 /* 259 * Send the data we just got to the framebuffer. 260 */ 261 tem_send_data(tem, credp, called_from); 262 263 (*tems->in_fp.f_cursor) 264 (tem, VIS_DISPLAY_CURSOR, credp, called_from); 265 } 266 267 /* 268 * Display an rectangular image on the frame buffer using the 269 * mechanism appropriate for the system state being called 270 * from quiesced or normal (ie. use polled I/O vs. layered ioctls) 271 */ 272 static void 273 tem_display( 274 struct tem *tem, 275 struct vis_consdisplay *pda, 276 cred_t *credp, 277 enum called_from called_from) 278 { 279 if (called_from == CALLED_FROM_STANDALONE) 280 tem->fb_polledio->display(tem->fb_polledio->arg, pda); 281 else 282 tem_display_layered(tem, pda, credp); 283 } 284 285 /* 286 * Copy a rectangle from one location to another on the frame buffer 287 * using the mechanism appropriate for the system state being called 288 * from, quiesced or normal (ie. use polled I/O vs. layered ioctls) 289 */ 290 void 291 tem_copy( 292 struct tem *tem, 293 struct vis_conscopy *pca, 294 cred_t *credp, 295 enum called_from called_from) 296 { 297 if (called_from == CALLED_FROM_STANDALONE) 298 tem->fb_polledio->copy(tem->fb_polledio->arg, pca); 299 else 300 tem_copy_layered(tem, pca, credp); 301 } 302 303 /* 304 * Display or hide a rectangular block text cursor of a specificsize 305 * at a specific location on frame buffer* using the mechanism 306 * appropriate for the system state being called from, quisced or 307 * normal (ie. use polled I/O vs. layered ioctls). 308 */ 309 static void 310 tem_cursor( 311 struct tem *tem, 312 struct vis_conscursor *pca, 313 cred_t *credp, 314 enum called_from called_from) 315 { 316 if (called_from == CALLED_FROM_STANDALONE) 317 tem->fb_polledio->cursor(tem->fb_polledio->arg, pca); 318 else 319 tem_cursor_layered(tem, pca, credp); 320 } 321 322 /* 323 * send the appropriate control message or set state based on the 324 * value of the control character ch 325 */ 326 327 static void 328 tem_control( 329 struct tem *tem, 330 uchar_t ch, 331 cred_t *credp, 332 enum called_from called_from) 333 { 334 struct tem_state *tems = tem->state; 335 336 ASSERT((called_from == CALLED_FROM_STANDALONE) || 337 MUTEX_HELD(&tem->lock)); 338 339 tems->a_state = A_STATE_START; 340 switch (ch) { 341 case A_BEL: 342 tem_bell(tem, called_from); 343 break; 344 345 case A_BS: 346 tem_mv_cursor(tem, 347 tems->a_c_cursor.row, 348 tems->a_c_cursor.col - 1, 349 credp, called_from); 350 break; 351 352 case A_HT: 353 tem_tab(tem, credp, called_from); 354 break; 355 356 case A_NL: 357 /* 358 * tem_send_data(tem, credp, called_from); 359 * tem_new_line(tem, credp, called_from); 360 * break; 361 */ 362 363 case A_VT: 364 tem_send_data(tem, credp, called_from); 365 tem_lf(tem, credp, called_from); 366 break; 367 368 case A_FF: 369 tem_send_data(tem, credp, called_from); 370 tem_cls(tem, credp, called_from); 371 break; 372 373 case A_CR: 374 tem_send_data(tem, credp, called_from); 375 tem_cr(tem); 376 break; 377 378 case A_ESC: 379 tems->a_state = A_STATE_ESC; 380 break; 381 382 case A_CSI: 383 { 384 int i; 385 tems->a_curparam = 0; 386 tems->a_paramval = 0; 387 tems->a_gotparam = B_FALSE; 388 /* clear the parameters */ 389 for (i = 0; i < TEM_MAXPARAMS; i++) 390 tems->a_params[i] = -1; 391 tems->a_state = A_STATE_CSI; 392 } 393 break; 394 395 case A_GS: 396 tem_back_tab(tem, credp, called_from); 397 break; 398 399 default: 400 break; 401 } 402 } 403 404 405 /* 406 * if parameters [0..count - 1] are not set, set them to the value 407 * of newparam. 408 */ 409 410 static void 411 tem_setparam(struct tem *tem, int count, int newparam) 412 { 413 int i; 414 415 for (i = 0; i < count; i++) { 416 if (tem->state->a_params[i] == -1) 417 tem->state->a_params[i] = newparam; 418 } 419 } 420 421 422 /* 423 * select graphics mode based on the param vals stored in a_params 424 */ 425 static void 426 tem_selgraph(struct tem *tem) 427 { 428 struct tem_state *tems = tem->state; 429 int curparam; 430 int count = 0; 431 int param; 432 433 curparam = tems->a_curparam; 434 do { 435 param = tems->a_params[count]; 436 437 switch (param) { 438 case -1: 439 case 0: 440 if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { 441 tems->a_flags |= TEM_ATTR_REVERSE; 442 } else { 443 tems->a_flags &= ~TEM_ATTR_REVERSE; 444 } 445 tems->a_flags &= ~TEM_ATTR_BOLD; 446 tems->a_flags &= ~TEM_ATTR_BLINK; 447 tems->fg_color = DEFAULT_ANSI_FOREGROUND; 448 tems->bg_color = DEFAULT_ANSI_BACKGROUND; 449 break; 450 451 case 1: /* Bold Intense */ 452 tems->a_flags |= TEM_ATTR_BOLD; 453 break; 454 455 case 5: /* Blink */ 456 tems->a_flags |= TEM_ATTR_BLINK; 457 break; 458 459 case 7: /* Reverse video */ 460 if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { 461 tems->a_flags &= ~TEM_ATTR_REVERSE; 462 } else { 463 tems->a_flags |= TEM_ATTR_REVERSE; 464 } 465 break; 466 467 case 30: /* black (grey) foreground */ 468 case 31: /* red (light red) foreground */ 469 case 32: /* green (light green) foreground */ 470 case 33: /* brown (yellow) foreground */ 471 case 34: /* blue (light blue) foreground */ 472 case 35: /* magenta (light magenta) foreground */ 473 case 36: /* cyan (light cyan) foreground */ 474 case 37: /* white (bright white) foreground */ 475 tems->fg_color = param - 30; 476 break; 477 478 case 40: /* black (grey) background */ 479 case 41: /* red (light red) background */ 480 case 42: /* green (light green) background */ 481 case 43: /* brown (yellow) background */ 482 case 44: /* blue (light blue) background */ 483 case 45: /* magenta (light magenta) background */ 484 case 46: /* cyan (light cyan) background */ 485 case 47: /* white (bright white) background */ 486 tems->bg_color = param - 40; 487 break; 488 489 default: 490 break; 491 } 492 count++; 493 curparam--; 494 495 } while (curparam > 0); 496 497 498 tems->a_state = A_STATE_START; 499 } 500 501 /* 502 * perform the appropriate action for the escape sequence 503 * 504 * General rule: This code does not validate the arguments passed. 505 * It assumes that the next lower level will do so. 506 */ 507 static void 508 tem_chkparam( 509 struct tem *tem, 510 uchar_t ch, 511 cred_t *credp, 512 enum called_from called_from) 513 { 514 struct tem_state *tems = tem->state; 515 int i; 516 int row; 517 int col; 518 519 ASSERT((called_from == CALLED_FROM_STANDALONE) || 520 MUTEX_HELD(&tem->lock)); 521 522 row = tems->a_c_cursor.row; 523 col = tems->a_c_cursor.col; 524 525 switch (ch) { 526 527 case 'm': /* select terminal graphics mode */ 528 tem_send_data(tem, credp, called_from); 529 tem_selgraph(tem); 530 break; 531 532 case '@': /* insert char */ 533 tem_setparam(tem, 1, 1); 534 tem_shift(tem, tems->a_params[0], TEM_SHIFT_RIGHT, 535 credp, called_from); 536 break; 537 538 case 'A': /* cursor up */ 539 tem_setparam(tem, 1, 1); 540 tem_mv_cursor(tem, row - tems->a_params[0], col, 541 credp, called_from); 542 break; 543 544 case 'd': /* VPA - vertical position absolute */ 545 tem_setparam(tem, 1, 1); 546 tem_mv_cursor(tem, tems->a_params[0] - 1, col, 547 credp, called_from); 548 break; 549 550 case 'e': /* VPR - vertical position relative */ 551 case 'B': /* cursor down */ 552 tem_setparam(tem, 1, 1); 553 tem_mv_cursor(tem, row + tems->a_params[0], col, 554 credp, called_from); 555 break; 556 557 case 'a': /* HPR - horizontal position relative */ 558 case 'C': /* cursor right */ 559 tem_setparam(tem, 1, 1); 560 tem_mv_cursor(tem, row, col + tems->a_params[0], 561 credp, called_from); 562 break; 563 564 case '`': /* HPA - horizontal position absolute */ 565 tem_setparam(tem, 1, 1); 566 tem_mv_cursor(tem, row, tems->a_params[0] - 1, 567 credp, called_from); 568 break; 569 570 case 'D': /* cursor left */ 571 tem_setparam(tem, 1, 1); 572 tem_mv_cursor(tem, row, col - tems->a_params[0], 573 credp, called_from); 574 break; 575 576 case 'E': /* CNL cursor next line */ 577 tem_setparam(tem, 1, 1); 578 tem_mv_cursor(tem, row + tems->a_params[0], 0, 579 credp, called_from); 580 break; 581 582 case 'F': /* CPL cursor previous line */ 583 tem_setparam(tem, 1, 1); 584 tem_mv_cursor(tem, row - tems->a_params[0], 0, 585 credp, called_from); 586 break; 587 588 case 'G': /* cursor horizontal position */ 589 tem_setparam(tem, 1, 1); 590 tem_mv_cursor(tem, row, tems->a_params[0] - 1, 591 credp, called_from); 592 break; 593 594 case 'g': /* clear tabs */ 595 tem_setparam(tem, 1, 0); 596 tem_clear_tabs(tem, tems->a_params[0]); 597 break; 598 599 case 'f': /* HVP Horizontal and Vertical Position */ 600 case 'H': /* CUP position cursor */ 601 tem_setparam(tem, 2, 1); 602 tem_mv_cursor(tem, 603 tems->a_params[0] - 1, 604 tems->a_params[1] - 1, 605 credp, called_from); 606 break; 607 608 case 'I': /* CHT - Cursor Horizontal Tab */ 609 /* Not implemented */ 610 break; 611 612 case 'J': /* ED - Erase in Display */ 613 tem_send_data(tem, credp, called_from); 614 tem_setparam(tem, 1, 0); 615 switch (tems->a_params[0]) { 616 case 0: 617 /* erase cursor to end of screen */ 618 /* FIRST erase cursor to end of line */ 619 tem_clear_chars(tem, 620 tems->a_c_dimension.width - 621 tems->a_c_cursor.col, 622 tems->a_c_cursor.row, 623 tems->a_c_cursor.col, credp, called_from); 624 625 /* THEN erase lines below the cursor */ 626 for (row = tems->a_c_cursor.row + 1; 627 row < tems->a_c_dimension.height; 628 row++) { 629 tem_clear_chars(tem, 630 tems->a_c_dimension.width, 631 row, 0, credp, called_from); 632 } 633 break; 634 635 case 1: 636 /* erase beginning of screen to cursor */ 637 /* FIRST erase lines above the cursor */ 638 for (row = 0; 639 row < tems->a_c_cursor.row; 640 row++) { 641 tem_clear_chars(tem, 642 tems->a_c_dimension.width, 643 row, 0, credp, called_from); 644 } 645 /* THEN erase beginning of line to cursor */ 646 tem_clear_chars(tem, 647 tems->a_c_cursor.col + 1, 648 tems->a_c_cursor.row, 649 0, credp, called_from); 650 break; 651 652 case 2: 653 /* erase whole screen */ 654 for (row = 0; 655 row < tems->a_c_dimension.height; 656 row++) { 657 tem_clear_chars(tem, 658 tems->a_c_dimension.width, 659 row, 0, credp, called_from); 660 } 661 break; 662 } 663 break; 664 665 case 'K': /* EL - Erase in Line */ 666 tem_send_data(tem, credp, called_from); 667 tem_setparam(tem, 1, 0); 668 switch (tems->a_params[0]) { 669 case 0: 670 /* erase cursor to end of line */ 671 tem_clear_chars(tem, 672 (tems->a_c_dimension.width - 673 tems->a_c_cursor.col), 674 tems->a_c_cursor.row, 675 tems->a_c_cursor.col, 676 credp, called_from); 677 break; 678 679 case 1: 680 /* erase beginning of line to cursor */ 681 tem_clear_chars(tem, 682 tems->a_c_cursor.col + 1, 683 tems->a_c_cursor.row, 684 0, credp, called_from); 685 break; 686 687 case 2: 688 /* erase whole line */ 689 tem_clear_chars(tem, 690 tems->a_c_dimension.width, 691 tems->a_c_cursor.row, 692 0, credp, called_from); 693 break; 694 } 695 break; 696 697 case 'L': /* insert line */ 698 tem_send_data(tem, credp, called_from); 699 tem_setparam(tem, 1, 1); 700 tem_scroll(tem, 701 tems->a_c_cursor.row, 702 tems->a_c_dimension.height - 1, 703 tems->a_params[0], TEM_SCROLL_DOWN, 704 credp, called_from); 705 break; 706 707 case 'M': /* delete line */ 708 tem_send_data(tem, credp, called_from); 709 tem_setparam(tem, 1, 1); 710 tem_scroll(tem, 711 tems->a_c_cursor.row, 712 tems->a_c_dimension.height - 1, 713 tems->a_params[0], TEM_SCROLL_UP, 714 credp, called_from); 715 break; 716 717 case 'P': /* DCH - delete char */ 718 tem_setparam(tem, 1, 1); 719 tem_shift(tem, tems->a_params[0], TEM_SHIFT_LEFT, 720 credp, called_from); 721 break; 722 723 case 'S': /* scroll up */ 724 tem_send_data(tem, credp, called_from); 725 tem_setparam(tem, 1, 1); 726 tem_scroll(tem, 0, 727 tems->a_c_dimension.height - 1, 728 tems->a_params[0], TEM_SCROLL_UP, 729 credp, called_from); 730 break; 731 732 case 'T': /* scroll down */ 733 tem_send_data(tem, credp, called_from); 734 tem_setparam(tem, 1, 1); 735 tem_scroll(tem, 0, 736 tems->a_c_dimension.height - 1, 737 tems->a_params[0], TEM_SCROLL_DOWN, 738 credp, called_from); 739 break; 740 741 case 'X': /* erase char */ 742 tem_setparam(tem, 1, 1); 743 tem_clear_chars(tem, 744 tems->a_params[0], 745 tems->a_c_cursor.row, 746 tems->a_c_cursor.col, 747 credp, called_from); 748 break; 749 750 case 'Z': /* cursor backward tabulation */ 751 tem_setparam(tem, 1, 1); 752 753 /* 754 * Rule exception - We do sanity checking here. 755 * 756 * Restrict the count to a sane value to keep from 757 * looping for a long time. There can't be more than one 758 * tab stop per column, so use that as a limit. 759 */ 760 if (tems->a_params[0] > tems->a_c_dimension.width) 761 tems->a_params[0] = tems->a_c_dimension.width; 762 763 for (i = 0; i < tems->a_params[0]; i++) 764 tem_back_tab(tem, credp, called_from); 765 break; 766 } 767 tems->a_state = A_STATE_START; 768 } 769 770 771 /* 772 * Gather the parameters of an ANSI escape sequence 773 */ 774 static void 775 tem_getparams(struct tem *tem, uchar_t ch, 776 cred_t *credp, enum called_from called_from) 777 { 778 struct tem_state *tems = tem->state; 779 780 ASSERT((called_from == CALLED_FROM_STANDALONE) || 781 MUTEX_HELD(&tem->lock)); 782 783 if (ch >= '0' && ch <= '9') { 784 tems->a_paramval = ((tems->a_paramval * 10) + (ch - '0')); 785 tems->a_gotparam = B_TRUE; /* Remember got parameter */ 786 return; /* Return immediately */ 787 } else if (tems->a_state == A_STATE_CSI_EQUAL || 788 tems->a_state == A_STATE_CSI_QMARK) { 789 tems->a_state = A_STATE_START; 790 } else { 791 if (tems->a_curparam < TEM_MAXPARAMS) { 792 if (tems->a_gotparam) { 793 /* get the parameter value */ 794 tems->a_params[tems->a_curparam] = 795 tems->a_paramval; 796 } 797 tems->a_curparam++; 798 } 799 800 if (ch == ';') { 801 /* Restart parameter search */ 802 tems->a_gotparam = B_FALSE; 803 tems->a_paramval = 0; /* No parame value yet */ 804 } else { 805 /* Handle escape sequence */ 806 tem_chkparam(tem, ch, credp, called_from); 807 } 808 } 809 } 810 811 /* 812 * Add character to internal buffer. 813 * When its full, send it to the next layer. 814 */ 815 816 static void 817 tem_outch(struct tem *tem, uchar_t ch, 818 cred_t *credp, enum called_from called_from) 819 { 820 821 ASSERT((called_from == CALLED_FROM_STANDALONE) || 822 MUTEX_HELD(&tem->lock)); 823 824 /* buffer up the character until later */ 825 826 tem->state->a_outbuf[tem->state->a_outindex++] = ch; 827 tem->state->a_c_cursor.col++; 828 if (tem->state->a_c_cursor.col >= tem->state->a_c_dimension.width) { 829 tem_send_data(tem, credp, called_from); 830 tem_new_line(tem, credp, called_from); 831 } 832 } 833 834 static void 835 tem_new_line(struct tem *tem, 836 cred_t *credp, enum called_from called_from) 837 { 838 tem_cr(tem); 839 tem_lf(tem, credp, called_from); 840 } 841 842 static void 843 tem_cr(struct tem *tem) 844 { 845 tem->state->a_c_cursor.col = 0; 846 tem_align_cursor(tem); 847 } 848 849 static void 850 tem_lf(struct tem *tem, 851 cred_t *credp, enum called_from called_from) 852 { 853 struct tem_state *tems = tem->state; 854 int row; 855 856 ASSERT((called_from == CALLED_FROM_STANDALONE) || 857 MUTEX_HELD(&tem->lock)); 858 859 /* 860 * Sanity checking notes: 861 * . a_nscroll was validated when it was set. 862 * . Regardless of that, tem_scroll and tem_mv_cursor will prevent 863 * anything bad from happening. 864 */ 865 row = tems->a_c_cursor.row + 1; 866 867 if (row >= tems->a_c_dimension.height) { 868 if (tems->a_nscroll != 0) { 869 tem_scroll(tem, 0, 870 tems->a_c_dimension.height - 1, 871 tems->a_nscroll, TEM_SCROLL_UP, 872 credp, called_from); 873 row = tems->a_c_dimension.height - 874 tems->a_nscroll; 875 } else { /* no scroll */ 876 /* 877 * implement Esc[#r when # is zero. This means no 878 * scroll but just return cursor to top of screen, 879 * do not clear screen. 880 */ 881 row = 0; 882 } 883 } 884 885 tem_mv_cursor(tem, row, tems->a_c_cursor.col, 886 credp, called_from); 887 888 if (tems->a_nscroll == 0) { 889 /* erase rest of cursor line */ 890 tem_clear_chars(tem, 891 tems->a_c_dimension.width - 892 tems->a_c_cursor.col, 893 tems->a_c_cursor.row, 894 tems->a_c_cursor.col, 895 credp, called_from); 896 897 } 898 899 tem_align_cursor(tem); 900 } 901 902 static void 903 tem_send_data(struct tem *tem, cred_t *credp, 904 enum called_from called_from) 905 { 906 struct tem_state *tems = tem->state; 907 text_color_t fg_color; 908 text_color_t bg_color; 909 910 ASSERT((called_from == CALLED_FROM_STANDALONE) || 911 MUTEX_HELD(&tem->lock)); 912 913 if (tems->a_outindex != 0) { 914 915 if (tems->a_flags & TEM_ATTR_REVERSE) { 916 fg_color = ansi_fg_to_solaris(tem, 917 tems->bg_color); 918 bg_color = ansi_bg_to_solaris(tem, 919 tems->fg_color); 920 } else { 921 fg_color = ansi_fg_to_solaris(tem, 922 tems->fg_color); 923 bg_color = ansi_bg_to_solaris(tem, 924 tems->bg_color); 925 } 926 927 /* 928 * Call the primitive to render this data. 929 */ 930 (*tems->in_fp.f_display)(tem, 931 tems->a_outbuf, 932 tems->a_outindex, 933 tems->a_s_cursor.row, 934 tems->a_s_cursor.col, 935 fg_color, bg_color, 936 credp, called_from); 937 tems->a_outindex = 0; 938 } 939 tem_align_cursor(tem); 940 } 941 942 943 /* 944 * We have just done something to the current output point. Reset the start 945 * point for the buffered data in a_outbuf. There shouldn't be any data 946 * buffered yet. 947 */ 948 void 949 tem_align_cursor(struct tem *tem) 950 { 951 tem->state->a_s_cursor.row = tem->state->a_c_cursor.row; 952 tem->state->a_s_cursor.col = tem->state->a_c_cursor.col; 953 } 954 955 956 957 /* 958 * State machine parser based on the current state and character input 959 * major terminations are to control character or normal character 960 */ 961 962 static void 963 tem_parse(struct tem *tem, uchar_t ch, 964 cred_t *credp, enum called_from called_from) 965 { 966 struct tem_state *tems = tem->state; 967 int i; 968 969 ASSERT((called_from == CALLED_FROM_STANDALONE) || 970 MUTEX_HELD(&tem->lock)); 971 972 if (tems->a_state == A_STATE_START) { /* Normal state? */ 973 if (ch == A_CSI || ch == A_ESC || ch < ' ') /* Control? */ 974 tem_control(tem, ch, credp, called_from); 975 else 976 /* Display */ 977 tem_outch(tem, ch, credp, called_from); 978 return; 979 } 980 981 /* In <ESC> sequence */ 982 if (tems->a_state != A_STATE_ESC) { /* Need to get parameters? */ 983 if (tems->a_state != A_STATE_CSI) { 984 tem_getparams(tem, ch, credp, called_from); 985 return; 986 } 987 988 switch (ch) { 989 case '?': 990 tems->a_state = A_STATE_CSI_QMARK; 991 return; 992 case '=': 993 tems->a_state = A_STATE_CSI_EQUAL; 994 return; 995 case 's': 996 /* 997 * As defined below, this sequence 998 * saves the cursor. However, Sun 999 * defines ESC[s as reset. We resolved 1000 * the conflict by selecting reset as it 1001 * is exported in the termcap file for 1002 * sun-mon, while the "save cursor" 1003 * definition does not exist anywhere in 1004 * /etc/termcap. 1005 * However, having no coherent 1006 * definition of reset, we have not 1007 * implemented it. 1008 */ 1009 1010 /* 1011 * Original code 1012 * tems->a_r_cursor.row = tems->a_c_cursor.row; 1013 * tems->a_r_cursor.col = tems->a_c_cursor.col; 1014 * tems->a_state = A_STATE_START; 1015 */ 1016 1017 tems->a_state = A_STATE_START; 1018 return; 1019 case 'u': 1020 tem_mv_cursor(tem, tems->a_r_cursor.row, 1021 tems->a_r_cursor.col, credp, called_from); 1022 tems->a_state = A_STATE_START; 1023 return; 1024 case 'p': /* sunbow */ 1025 tem_send_data(tem, credp, called_from); 1026 /* 1027 * Don't set anything if we are 1028 * already as we want to be. 1029 */ 1030 if (tems->a_flags & TEM_ATTR_SCREEN_REVERSE) { 1031 tems->a_flags &= ~TEM_ATTR_SCREEN_REVERSE; 1032 /* 1033 * If we have switched the characters to be the 1034 * inverse from the screen, then switch them as 1035 * well to keep them the inverse of the screen. 1036 */ 1037 if (tems->a_flags & TEM_ATTR_REVERSE) { 1038 tems->a_flags &= ~TEM_ATTR_REVERSE; 1039 } else { 1040 tems->a_flags |= TEM_ATTR_REVERSE; 1041 } 1042 } 1043 tem_cls(tem, credp, called_from); 1044 tems->a_state = A_STATE_START; 1045 return; 1046 case 'q': /* sunwob */ 1047 tem_send_data(tem, credp, called_from); 1048 /* 1049 * Don't set anything if we are 1050 * already where as we want to be. 1051 */ 1052 if (!(tems->a_flags & TEM_ATTR_SCREEN_REVERSE)) { 1053 tems->a_flags |= TEM_ATTR_SCREEN_REVERSE; 1054 /* 1055 * If we have switched the characters to be the 1056 * inverse from the screen, then switch them as 1057 * well to keep them the inverse of the screen. 1058 */ 1059 if (!(tems->a_flags & TEM_ATTR_REVERSE)) { 1060 tems->a_flags |= TEM_ATTR_REVERSE; 1061 } else { 1062 tems->a_flags &= ~TEM_ATTR_REVERSE; 1063 } 1064 } 1065 1066 tem_cls(tem, credp, called_from); 1067 tems->a_state = A_STATE_START; 1068 return; 1069 case 'r': /* sunscrl */ 1070 /* 1071 * Rule exception: check for validity here. 1072 */ 1073 tems->a_nscroll = tems->a_paramval; 1074 if (tems->a_nscroll > tems->a_c_dimension.height) 1075 tems->a_nscroll = tems->a_c_dimension.height; 1076 if (tems->a_nscroll < 0) 1077 tems->a_nscroll = 1; 1078 tems->a_state = A_STATE_START; 1079 return; 1080 default: 1081 tem_getparams(tem, ch, credp, called_from); 1082 return; 1083 } 1084 } 1085 1086 /* Previous char was <ESC> */ 1087 if (ch == '[') { 1088 tems->a_curparam = 0; 1089 tems->a_paramval = 0; 1090 tems->a_gotparam = B_FALSE; 1091 /* clear the parameters */ 1092 for (i = 0; i < TEM_MAXPARAMS; i++) 1093 tems->a_params[i] = -1; 1094 tems->a_state = A_STATE_CSI; 1095 } else if (ch == 'Q') { /* <ESC>Q ? */ 1096 tems->a_state = A_STATE_START; 1097 } else if (ch == 'C') { /* <ESC>C ? */ 1098 tems->a_state = A_STATE_START; 1099 } else { 1100 tems->a_state = A_STATE_START; 1101 if (ch == 'c') 1102 /* ESC c resets display */ 1103 tem_reset_display(tem, credp, called_from, 1); 1104 else if (ch == 'H') 1105 /* ESC H sets a tab */ 1106 tem_set_tab(tem); 1107 else if (ch == '7') { 1108 /* ESC 7 Save Cursor position */ 1109 tems->a_r_cursor.row = tems->a_c_cursor.row; 1110 tems->a_r_cursor.col = tems->a_c_cursor.col; 1111 } else if (ch == '8') 1112 /* ESC 8 Restore Cursor position */ 1113 tem_mv_cursor(tem, tems->a_r_cursor.row, 1114 tems->a_r_cursor.col, credp, called_from); 1115 /* check for control chars */ 1116 else if (ch < ' ') 1117 tem_control(tem, ch, credp, called_from); 1118 else 1119 tem_outch(tem, ch, credp, called_from); 1120 } 1121 } 1122 1123 /* ARGSUSED */ 1124 static void 1125 tem_bell(struct tem *tem, enum called_from called_from) 1126 { 1127 if (called_from == CALLED_FROM_STANDALONE) 1128 beep_polled(BEEP_CONSOLE); 1129 else 1130 beep(BEEP_CONSOLE); 1131 } 1132 1133 1134 static void 1135 tem_scroll(tem_t *tem, int start, int end, int count, int direction, 1136 cred_t *credp, enum called_from called_from) 1137 { 1138 struct tem_state *tems = tem->state; 1139 int row; 1140 int lines_affected; 1141 1142 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1143 MUTEX_HELD(&tem->lock)); 1144 1145 lines_affected = end - start + 1; 1146 if (count > lines_affected) 1147 count = lines_affected; 1148 if (count <= 0) 1149 return; 1150 1151 switch (direction) { 1152 case TEM_SCROLL_UP: 1153 if (count < lines_affected) { 1154 tem_copy_area(tem, 0, start + count, 1155 tems->a_c_dimension.width - 1, end, 1156 0, start, credp, called_from); 1157 } 1158 for (row = (end - count) + 1; row <= end; row++) { 1159 tem_clear_chars(tem, tems->a_c_dimension.width, 1160 row, 0, credp, called_from); 1161 } 1162 break; 1163 1164 case TEM_SCROLL_DOWN: 1165 if (count < lines_affected) { 1166 tem_copy_area(tem, 0, start, 1167 tems->a_c_dimension.width - 1, 1168 end - count, 0, start + count, 1169 credp, called_from); 1170 } 1171 for (row = start; row < start + count; row++) { 1172 tem_clear_chars(tem, tems->a_c_dimension.width, 1173 row, 0, credp, called_from); 1174 } 1175 break; 1176 } 1177 } 1178 1179 static void 1180 tem_copy_area(struct tem *tem, 1181 screen_pos_t s_col, screen_pos_t s_row, 1182 screen_pos_t e_col, screen_pos_t e_row, 1183 screen_pos_t t_col, screen_pos_t t_row, 1184 cred_t *credp, enum called_from called_from) 1185 { 1186 struct tem_state *tems = tem->state; 1187 int rows; 1188 int cols; 1189 1190 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1191 MUTEX_HELD(&tem->lock)); 1192 1193 if (s_col < 0 || s_row < 0 || 1194 e_col < 0 || e_row < 0 || 1195 t_col < 0 || t_row < 0 || 1196 s_col >= tems->a_c_dimension.width || 1197 e_col >= tems->a_c_dimension.width || 1198 t_col >= tems->a_c_dimension.width || 1199 s_row >= tems->a_c_dimension.height || 1200 e_row >= tems->a_c_dimension.height || 1201 t_row >= tems->a_c_dimension.height) 1202 return; 1203 1204 if (s_row > e_row || s_col > e_col) 1205 return; 1206 1207 rows = e_row - s_row + 1; 1208 cols = e_col - s_col + 1; 1209 if (t_row + rows > tems->a_c_dimension.height || 1210 t_col + cols > tems->a_c_dimension.width) 1211 return; 1212 1213 (*tems->in_fp.f_copy)(tem, s_col, s_row, 1214 e_col, e_row, t_col, t_row, credp, called_from); 1215 } 1216 1217 static void 1218 tem_clear_chars(struct tem *tem, int count, screen_pos_t row, 1219 screen_pos_t col, cred_t *credp, enum called_from called_from) 1220 { 1221 struct tem_state *tems = tem->state; 1222 1223 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1224 MUTEX_HELD(&tem->lock)); 1225 1226 if (row < 0 || row >= tems->a_c_dimension.height || 1227 col < 0 || col >= tems->a_c_dimension.width || 1228 count < 0) 1229 return; 1230 1231 /* 1232 * Note that very large values of "count" could cause col+count 1233 * to overflow, so we check "count" independently. 1234 */ 1235 if (count > tems->a_c_dimension.width || 1236 col + count > tems->a_c_dimension.width) 1237 count = tems->a_c_dimension.width - col; 1238 1239 (*tems->in_fp.f_cls)(tem, count, row, col, credp, called_from); 1240 } 1241 1242 void 1243 tem_text_display(struct tem *tem, uchar_t *string, 1244 int count, screen_pos_t row, screen_pos_t col, 1245 text_color_t fg_color, text_color_t bg_color, 1246 cred_t *credp, enum called_from called_from) 1247 { 1248 struct vis_consdisplay da; 1249 1250 da.data = string; 1251 da.width = count; 1252 da.row = row; 1253 da.col = col; 1254 1255 da.fg_color = fg_color; 1256 da.bg_color = bg_color; 1257 1258 tem_display(tem, &da, credp, called_from); 1259 } 1260 1261 /* 1262 * This function is used to blit a rectangular color image, 1263 * unperturbed on the underlying framebuffer, to render 1264 * icons and pictures. The data is a pixel pattern that 1265 * fills a rectangle bounded to the width and height parameters. 1266 * The color pixel data must to be pre-adjusted by the caller 1267 * for the current video depth. 1268 * 1269 * This function is unused now. 1270 */ 1271 void 1272 tem_image_display(struct tem *tem, uchar_t *image, 1273 int height, int width, screen_pos_t row, screen_pos_t col, 1274 cred_t *credp, enum called_from called_from) 1275 { 1276 struct vis_consdisplay da; 1277 1278 da.data = image; 1279 da.width = width; 1280 da.height = height; 1281 da.row = row; 1282 da.col = col; 1283 1284 tem_display(tem, &da, credp, called_from); 1285 } 1286 1287 1288 void 1289 tem_text_copy(struct tem *tem, 1290 screen_pos_t s_col, screen_pos_t s_row, 1291 screen_pos_t e_col, screen_pos_t e_row, 1292 screen_pos_t t_col, screen_pos_t t_row, 1293 cred_t *credp, enum called_from called_from) 1294 { 1295 struct vis_conscopy da; 1296 1297 da.s_row = s_row; 1298 da.s_col = s_col; 1299 da.e_row = e_row; 1300 da.e_col = e_col; 1301 da.t_row = t_row; 1302 da.t_col = t_col; 1303 1304 tem_copy(tem, &da, credp, called_from); 1305 } 1306 1307 void 1308 tem_text_cls(struct tem *tem, 1309 int count, screen_pos_t row, screen_pos_t col, cred_t *credp, 1310 enum called_from called_from) 1311 { 1312 struct vis_consdisplay da; 1313 1314 da.data = tem->state->a_blank_line; 1315 da.width = count; 1316 da.row = row; 1317 da.col = col; 1318 1319 tem_get_color(tem, &da.fg_color, &da.bg_color); 1320 tem_display(tem, &da, credp, called_from); 1321 } 1322 1323 1324 1325 void 1326 tem_pix_display(struct tem *tem, 1327 uchar_t *string, int count, 1328 screen_pos_t row, screen_pos_t col, 1329 text_color_t fg_color, text_color_t bg_color, 1330 cred_t *credp, enum called_from called_from) 1331 { 1332 struct tem_state *tems = tem->state; 1333 struct vis_consdisplay da; 1334 int i; 1335 da.data = (uchar_t *)tems->a_pix_data; 1336 da.width = tems->a_font.width; 1337 da.height = tems->a_font.height; 1338 da.row = (row * da.height) + tems->a_p_offset.y; 1339 da.col = (col * da.width) + tems->a_p_offset.x; 1340 1341 for (i = 0; i < count; i++) { 1342 BIT_TO_PIX(tem, string[i], fg_color, bg_color); 1343 tem_display(tem, &da, credp, called_from); 1344 da.col += da.width; 1345 } 1346 } 1347 1348 void 1349 tem_pix_copy(struct tem *tem, 1350 screen_pos_t s_col, screen_pos_t s_row, 1351 screen_pos_t e_col, screen_pos_t e_row, 1352 screen_pos_t t_col, screen_pos_t t_row, 1353 cred_t *credp, 1354 enum called_from called_from) 1355 { 1356 struct tem_state *tems = tem->state; 1357 struct vis_conscopy ma; 1358 static boolean_t need_clear = B_TRUE; 1359 1360 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1361 MUTEX_HELD(&tem->lock)); 1362 1363 if (need_clear && tems->first_line > 0) { 1364 /* 1365 * Clear OBP output above our kernel console term 1366 * when our kernel console term begins to scroll up, 1367 * we hope it is user friendly. 1368 * (Also see comments on tem_pix_clear_prom_output) 1369 * 1370 * This is only one time call. 1371 */ 1372 tem_pix_clear_prom_output(tem, credp, called_from); 1373 } 1374 need_clear = B_FALSE; 1375 1376 ma.s_row = s_row * tems->a_font.height + tems->a_p_offset.y; 1377 ma.e_row = (e_row + 1) * tems->a_font.height + tems->a_p_offset.y - 1; 1378 ma.t_row = t_row * tems->a_font.height + tems->a_p_offset.y; 1379 1380 /* 1381 * Check if we're in process of clearing OBP's columns area, 1382 * which only happens when term scrolls up a whole line. 1383 */ 1384 if (tems->first_line > 0 && t_row < s_row && t_col == 0 && 1385 e_col == tems->a_c_dimension.width - 1) { 1386 /* 1387 * We need to clear OBP's columns area outside our kernel 1388 * console term. So that we set ma.e_col to entire row here. 1389 */ 1390 ma.s_col = s_col * tems->a_font.width; 1391 ma.e_col = tems->a_p_dimension.width - 1; 1392 1393 ma.t_col = t_col * tems->a_font.width; 1394 } else { 1395 ma.s_col = s_col * tems->a_font.width + tems->a_p_offset.x; 1396 ma.e_col = (e_col + 1) * tems->a_font.width + 1397 tems->a_p_offset.x - 1; 1398 ma.t_col = t_col * tems->a_font.width + tems->a_p_offset.x; 1399 } 1400 1401 tem_copy(tem, &ma, credp, called_from); 1402 1403 if (tems->first_line > 0 && t_row < s_row) { 1404 /* We have scrolled up (s_row - t_row) rows. */ 1405 tems->first_line -= (s_row - t_row); 1406 if (tems->first_line <= 0) { 1407 /* All OBP rows have been cleared. */ 1408 tems->first_line = 0; 1409 } 1410 } 1411 1412 } 1413 1414 /* 1415 * This function only clears count of columns in one row 1416 */ 1417 void 1418 tem_pix_cls(struct tem *tem, int count, 1419 screen_pos_t row, screen_pos_t col, cred_t *credp, 1420 enum called_from called_from) 1421 { 1422 struct tem_state *tems = tem->state; 1423 1424 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1425 MUTEX_HELD(&tem->lock)); 1426 1427 tem_pix_cls_range(tem, row, 1, tems->a_p_offset.y, 1428 col, count, tems->a_p_offset.x, B_FALSE, credp, called_from); 1429 } 1430 1431 /* 1432 * This function clears OBP output above our kernel console term area 1433 * because OBP's term may have a bigger terminal window than that of 1434 * our kernel console term. So we need to clear OBP output garbage outside 1435 * of our kernel console term at a proper time, which is when the first 1436 * row output of our kernel console term scrolls at the first screen line. 1437 * 1438 * _________________________________ 1439 * | _____________________ | ---> OBP's bigger term window 1440 * | | | | 1441 * |___| | | 1442 * | | | | | 1443 * | | | | | 1444 * |_|_|___________________|_______| 1445 * | | | ---> first line 1446 * | |___________________|---> our kernel console term window 1447 * | 1448 * |---> columns area to be cleared 1449 * 1450 * This function only takes care of the output above our kernel console term, 1451 * and tem_prom_scroll_up takes care of columns area outside of our kernel 1452 * console term. 1453 */ 1454 static void 1455 tem_pix_clear_prom_output(struct tem *tem, cred_t *credp, 1456 enum called_from called_from) 1457 { 1458 struct tem_state *tems = tem->state; 1459 int nrows, ncols, width, height; 1460 1461 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1462 MUTEX_HELD(&tem->lock)); 1463 1464 width = tems->a_font.width; 1465 height = tems->a_font.height; 1466 1467 nrows = (tems->a_p_offset.y + (height - 1))/ height; 1468 ncols = (tems->a_p_dimension.width + (width - 1))/ width; 1469 1470 tem_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0, 1471 B_FALSE, credp, called_from); 1472 } 1473 1474 /* 1475 * clear the whole screen for pixel mode 1476 */ 1477 static void 1478 tem_pix_clear_entire_screen(struct tem *tem, cred_t *credp, 1479 enum called_from called_from) 1480 { 1481 struct tem_state *tems = tem->state; 1482 int nrows, ncols, width, height; 1483 1484 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1485 MUTEX_HELD(&tem->lock)); 1486 1487 width = tems->a_font.width; 1488 height = tems->a_font.height; 1489 1490 nrows = (tems->a_p_dimension.height + (height - 1))/ height; 1491 ncols = (tems->a_p_dimension.width + (width - 1))/ width; 1492 1493 tem_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0, 1494 B_FALSE, credp, called_from); 1495 1496 tems->a_c_cursor.row = 0; 1497 tems->a_c_cursor.col = 0; 1498 tem_align_cursor(tem); 1499 1500 /* 1501 * Since the whole screen is cleared, we don't need 1502 * to clear OBP output later. 1503 */ 1504 if (tems->first_line > 0) { 1505 tems->first_line = 0; 1506 } 1507 } 1508 1509 /* 1510 * clear the whole screen 1511 */ 1512 static void 1513 tem_cls(struct tem *tem, 1514 cred_t *credp, enum called_from called_from) 1515 { 1516 struct tem_state *tems = tem->state; 1517 int row; 1518 1519 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1520 MUTEX_HELD(&tem->lock)); 1521 1522 if (tems->display_mode == VIS_TEXT) { 1523 for (row = 0; row < tems->a_c_dimension.height; row++) { 1524 tem_clear_chars(tem, tems->a_c_dimension.width, 1525 row, 0, credp, called_from); 1526 } 1527 tems->a_c_cursor.row = 0; 1528 tems->a_c_cursor.col = 0; 1529 tem_align_cursor(tem); 1530 return; 1531 } 1532 1533 ASSERT(tems->display_mode == VIS_PIXEL); 1534 1535 tem_pix_clear_entire_screen(tem, credp, called_from); 1536 } 1537 1538 static void 1539 tem_back_tab(struct tem *tem, 1540 cred_t *credp, enum called_from called_from) 1541 { 1542 struct tem_state *tems = tem->state; 1543 int i; 1544 screen_pos_t tabstop; 1545 1546 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1547 MUTEX_HELD(&tem->lock)); 1548 1549 tabstop = 0; 1550 1551 for (i = tems->a_ntabs - 1; i >= 0; i--) { 1552 if (tems->a_tabs[i] < tems->a_c_cursor.col) { 1553 tabstop = tems->a_tabs[i]; 1554 break; 1555 } 1556 } 1557 1558 tem_mv_cursor(tem, tems->a_c_cursor.row, 1559 tabstop, credp, called_from); 1560 } 1561 1562 static void 1563 tem_tab(struct tem *tem, 1564 cred_t *credp, enum called_from called_from) 1565 { 1566 struct tem_state *tems = tem->state; 1567 int i; 1568 screen_pos_t tabstop; 1569 1570 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1571 MUTEX_HELD(&tem->lock)); 1572 1573 tabstop = tems->a_c_dimension.width - 1; 1574 1575 for (i = 0; i < tems->a_ntabs; i++) { 1576 if (tems->a_tabs[i] > tems->a_c_cursor.col) { 1577 tabstop = tems->a_tabs[i]; 1578 break; 1579 } 1580 } 1581 1582 tem_mv_cursor(tem, tems->a_c_cursor.row, 1583 tabstop, credp, called_from); 1584 } 1585 1586 static void 1587 tem_set_tab(struct tem *tem) 1588 { 1589 struct tem_state *tems = tem->state; 1590 int i; 1591 int j; 1592 1593 if (tems->a_ntabs == TEM_MAXTAB) 1594 return; 1595 if (tems->a_ntabs == 0 || 1596 tems->a_tabs[tems->a_ntabs] < tems->a_c_cursor.col) { 1597 tems->a_tabs[tems->a_ntabs++] = tems->a_c_cursor.col; 1598 return; 1599 } 1600 for (i = 0; i < tems->a_ntabs; i++) { 1601 if (tems->a_tabs[i] == tems->a_c_cursor.col) 1602 return; 1603 if (tems->a_tabs[i] > tems->a_c_cursor.col) { 1604 for (j = tems->a_ntabs - 1; j >= i; j--) 1605 tems->a_tabs[j+ 1] = tems->a_tabs[j]; 1606 tems->a_tabs[i] = tems->a_c_cursor.col; 1607 tems->a_ntabs++; 1608 return; 1609 } 1610 } 1611 } 1612 1613 static void 1614 tem_clear_tabs(struct tem *tem, int action) 1615 { 1616 struct tem_state *tems = tem->state; 1617 int i; 1618 int j; 1619 1620 switch (action) { 1621 case 3: /* clear all tabs */ 1622 tems->a_ntabs = 0; 1623 break; 1624 case 0: /* clr tab at cursor */ 1625 1626 for (i = 0; i < tems->a_ntabs; i++) { 1627 if (tems->a_tabs[i] == tems->a_c_cursor.col) { 1628 tems->a_ntabs--; 1629 for (j = i; j < tems->a_ntabs; j++) 1630 tems->a_tabs[j] = tems->a_tabs[j + 1]; 1631 return; 1632 } 1633 } 1634 break; 1635 } 1636 } 1637 1638 static void 1639 tem_mv_cursor(struct tem *tem, int row, int col, 1640 cred_t *credp, enum called_from called_from) 1641 { 1642 struct tem_state *tems = tem->state; 1643 1644 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1645 MUTEX_HELD(&tem->lock)); 1646 1647 /* 1648 * Sanity check and bounds enforcement. Out of bounds requests are 1649 * clipped to the screen boundaries. This seems to be what SPARC 1650 * does. 1651 */ 1652 if (row < 0) 1653 row = 0; 1654 if (row >= tems->a_c_dimension.height) 1655 row = tems->a_c_dimension.height - 1; 1656 if (col < 0) 1657 col = 0; 1658 if (col >= tems->a_c_dimension.width) 1659 col = tems->a_c_dimension.width - 1; 1660 1661 tem_send_data(tem, credp, called_from); 1662 tems->a_c_cursor.row = row; 1663 tems->a_c_cursor.col = col; 1664 tem_align_cursor(tem); 1665 } 1666 1667 /* ARGSUSED */ 1668 void 1669 tem_reset_emulator(struct tem *tem, 1670 cred_t *credp, enum called_from called_from) 1671 { 1672 struct tem_state *tems = tem->state; 1673 int j; 1674 1675 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1676 MUTEX_HELD(&tem->lock)); 1677 1678 tems->a_c_cursor.row = 0; 1679 tems->a_c_cursor.col = 0; 1680 tems->a_r_cursor.row = 0; 1681 tems->a_r_cursor.col = 0; 1682 tems->a_s_cursor.row = 0; 1683 tems->a_s_cursor.col = 0; 1684 tems->a_outindex = 0; 1685 tems->a_state = A_STATE_START; 1686 tems->a_gotparam = B_FALSE; 1687 tems->a_curparam = 0; 1688 tems->a_paramval = 0; 1689 tems->a_flags = 0; 1690 tems->a_nscroll = 1; 1691 tems->fg_color = DEFAULT_ANSI_FOREGROUND; 1692 tems->bg_color = DEFAULT_ANSI_BACKGROUND; 1693 1694 /* 1695 * set up the initial tab stops 1696 */ 1697 tems->a_ntabs = 0; 1698 for (j = 8; j < tems->a_c_dimension.width; j += 8) 1699 tems->a_tabs[tems->a_ntabs++] = (screen_pos_t)j; 1700 1701 for (j = 0; j < TEM_MAXPARAMS; j++) 1702 tems->a_params[j] = 0; 1703 } 1704 1705 void 1706 tem_reset_display(struct tem *tem, 1707 cred_t *credp, enum called_from called_from, int clear_txt) 1708 { 1709 struct tem_state *tems = tem->state; 1710 1711 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1712 MUTEX_HELD(&tem->lock)); 1713 1714 tem_reset_emulator(tem, credp, called_from); 1715 tem_reset_colormap(tem, credp, called_from); 1716 1717 if (clear_txt) { 1718 (*tems->in_fp.f_cursor)(tem, 1719 VIS_HIDE_CURSOR, credp, called_from); 1720 1721 tem_cls(tem, credp, called_from); 1722 1723 (*tems->in_fp.f_cursor)(tem, 1724 VIS_DISPLAY_CURSOR, credp, called_from); 1725 } 1726 1727 tems->a_initialized = 1; 1728 } 1729 1730 1731 static void 1732 tem_shift( 1733 struct tem *tem, 1734 int count, 1735 int direction, 1736 cred_t *credp, 1737 enum called_from called_from) 1738 { 1739 struct tem_state *tems = tem->state; 1740 int rest_of_line; 1741 1742 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1743 MUTEX_HELD(&tem->lock)); 1744 1745 rest_of_line = tems->a_c_dimension.width - tems->a_c_cursor.col; 1746 if (count > rest_of_line) 1747 count = rest_of_line; 1748 1749 if (count <= 0) 1750 return; 1751 1752 switch (direction) { 1753 case TEM_SHIFT_LEFT: 1754 if (count < rest_of_line) { 1755 tem_copy_area(tem, 1756 tems->a_c_cursor.col + count, 1757 tems->a_c_cursor.row, 1758 tems->a_c_dimension.width - 1, 1759 tems->a_c_cursor.row, 1760 tems->a_c_cursor.col, 1761 tems->a_c_cursor.row, 1762 credp, called_from); 1763 } 1764 1765 tem_clear_chars(tem, count, tems->a_c_cursor.row, 1766 (tems->a_c_dimension.width - count), credp, 1767 called_from); 1768 break; 1769 case TEM_SHIFT_RIGHT: 1770 if (count < rest_of_line) { 1771 tem_copy_area(tem, 1772 tems->a_c_cursor.col, 1773 tems->a_c_cursor.row, 1774 tems->a_c_dimension.width - count - 1, 1775 tems->a_c_cursor.row, 1776 tems->a_c_cursor.col + count, 1777 tems->a_c_cursor.row, 1778 credp, called_from); 1779 } 1780 1781 tem_clear_chars(tem, count, tems->a_c_cursor.row, 1782 tems->a_c_cursor.col, credp, called_from); 1783 break; 1784 } 1785 } 1786 1787 void 1788 tem_text_cursor(struct tem *tem, short action, 1789 cred_t *credp, enum called_from called_from) 1790 { 1791 struct tem_state *tems = tem->state; 1792 struct vis_conscursor ca; 1793 1794 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1795 MUTEX_HELD(&tem->lock)); 1796 1797 ca.row = tems->a_c_cursor.row; 1798 ca.col = tems->a_c_cursor.col; 1799 ca.action = action; 1800 1801 tem_cursor(tem, &ca, credp, called_from); 1802 1803 if (action == VIS_GET_CURSOR) { 1804 tems->a_c_cursor.row = ca.row; 1805 tems->a_c_cursor.col = ca.col; 1806 } 1807 } 1808 1809 1810 void 1811 tem_pix_cursor(struct tem *tem, short action, 1812 cred_t *credp, enum called_from called_from) 1813 { 1814 struct tem_state *tems = tem->state; 1815 struct vis_conscursor ca; 1816 1817 ASSERT((called_from == CALLED_FROM_STANDALONE) || 1818 MUTEX_HELD(&tem->lock)); 1819 1820 ca.row = tems->a_c_cursor.row * tems->a_font.height + 1821 tems->a_p_offset.y; 1822 ca.col = tems->a_c_cursor.col * tems->a_font.width + 1823 tems->a_p_offset.x; 1824 ca.width = tems->a_font.width; 1825 ca.height = tems->a_font.height; 1826 if (tems->a_pdepth == 8 || tems->a_pdepth == 4) { 1827 if (tems->a_flags & TEM_ATTR_REVERSE) { 1828 ca.fg_color.mono = TEM_TEXT_WHITE; 1829 ca.bg_color.mono = TEM_TEXT_BLACK; 1830 } else { 1831 ca.fg_color.mono = TEM_TEXT_BLACK; 1832 ca.bg_color.mono = TEM_TEXT_WHITE; 1833 } 1834 } else if (tems->a_pdepth == 24 || tems->a_pdepth == 32) { 1835 if (tems->a_flags & TEM_ATTR_REVERSE) { 1836 ca.fg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED; 1837 ca.fg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN; 1838 ca.fg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE; 1839 1840 ca.bg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED; 1841 ca.bg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN; 1842 ca.bg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE; 1843 } else { 1844 ca.fg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED; 1845 ca.fg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN; 1846 ca.fg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE; 1847 1848 ca.bg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED; 1849 ca.bg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN; 1850 ca.bg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE; 1851 } 1852 } 1853 1854 ca.action = action; 1855 1856 tem_cursor(tem, &ca, credp, called_from); 1857 } 1858 1859 #define BORDER_PIXELS 10 1860 void 1861 set_font(struct font *f, short *rows, short *cols, short height, short width) 1862 { 1863 bitmap_data_t *font_selected = NULL; 1864 struct fontlist *fl; 1865 1866 /* 1867 * Find best font for these dimensions, or use default 1868 * 1869 * A 1 pixel border is the absolute minimum we could have 1870 * as a border around the text window (BORDER_PIXELS = 2), 1871 * however a slightly larger border not only looks better 1872 * but for the fonts currently statically built into the 1873 * emulator causes much better font selection for the 1874 * normal range of screen resolutions. 1875 */ 1876 for (fl = fonts; fl->data; fl++) { 1877 if ((((*rows * fl->data->height) + BORDER_PIXELS) <= height) && 1878 (((*cols * fl->data->width) + BORDER_PIXELS) <= width)) { 1879 font_selected = fl->data; 1880 break; 1881 } 1882 } 1883 /* 1884 * The minus 2 is to make sure we have at least a 1 pixel 1885 * boarder around the entire screen. 1886 */ 1887 if (font_selected == NULL) { 1888 if (((*rows * DEFAULT_FONT_DATA.height) > height) || 1889 ((*cols * DEFAULT_FONT_DATA.width) > width)) { 1890 *rows = (height - 2) / DEFAULT_FONT_DATA.height; 1891 *cols = (width - 2) / DEFAULT_FONT_DATA.width; 1892 } 1893 font_selected = &DEFAULT_FONT_DATA; 1894 } 1895 1896 f->width = font_selected->width; 1897 f->height = font_selected->height; 1898 bcopy((caddr_t)font_selected->encoding, (caddr_t)f->char_ptr, 1899 sizeof (f->char_ptr)); 1900 f->image_data = font_selected->image; 1901 1902 } 1903 1904 /* 1905 * bit_to_pix4 is for 4-bit frame buffers. It will write one output byte 1906 * for each 2 bits of input bitmap. It inverts the input bits before 1907 * doing the output translation, for reverse video. 1908 * 1909 * Assuming foreground is 0001 and background is 0000... 1910 * An input data byte of 0x53 will output the bit pattern 1911 * 00000001 00000001 00000000 00010001. 1912 */ 1913 1914 void 1915 bit_to_pix4( 1916 struct tem *tem, 1917 uchar_t c, 1918 text_color_t fg_color, 1919 text_color_t bg_color) 1920 { 1921 struct tem_state *tems = tem->state; 1922 int row; 1923 int byte; 1924 int i; 1925 uint8_t *cp; 1926 uint8_t data; 1927 uint8_t nibblett; 1928 int bytes_wide; 1929 uint8_t *dest; 1930 1931 dest = (uint8_t *)tems->a_pix_data; 1932 1933 cp = tems->a_font.char_ptr[c]; 1934 bytes_wide = (tems->a_font.width + 7) / 8; 1935 1936 for (row = 0; row < tems->a_font.height; row++) { 1937 for (byte = 0; byte < bytes_wide; byte++) { 1938 data = *cp++; 1939 for (i = 0; i < 4; i++) { 1940 nibblett = (data >> ((3-i) * 2)) & 0x3; 1941 switch (nibblett) { 1942 case 0x0: 1943 *dest++ = bg_color << 4 | bg_color; 1944 break; 1945 case 0x1: 1946 *dest++ = bg_color << 4 | fg_color; 1947 break; 1948 case 0x2: 1949 *dest++ = fg_color << 4 | bg_color; 1950 break; 1951 case 0x3: 1952 *dest++ = fg_color << 4 | fg_color; 1953 break; 1954 } 1955 } 1956 } 1957 } 1958 } 1959 1960 /* 1961 * bit_to_pix8 is for 8-bit frame buffers. It will write one output byte 1962 * for each bit of input bitmap. It inverts the input bits before 1963 * doing the output translation, for reverse video. 1964 * 1965 * Assuming foreground is 00000001 and background is 00000000... 1966 * An input data byte of 0x53 will output the bit pattern 1967 * 0000000 000000001 00000000 00000001 00000000 00000000 00000001 00000001. 1968 */ 1969 1970 void 1971 bit_to_pix8( 1972 struct tem *tem, 1973 uchar_t c, 1974 text_color_t fg_color, 1975 text_color_t bg_color) 1976 { 1977 struct tem_state *tems = tem->state; 1978 int row; 1979 int byte; 1980 int i; 1981 uint8_t *cp; 1982 uint8_t data; 1983 int bytes_wide; 1984 uint8_t mask; 1985 int bitsleft, nbits; 1986 uint8_t *dest; 1987 1988 dest = (uint8_t *)tems->a_pix_data; 1989 1990 cp = tems->a_font.char_ptr[c]; 1991 bytes_wide = (tems->a_font.width + 7) / 8; 1992 1993 for (row = 0; row < tems->a_font.height; row++) { 1994 bitsleft = tems->a_font.width; 1995 for (byte = 0; byte < bytes_wide; byte++) { 1996 data = *cp++; 1997 mask = 0x80; 1998 nbits = MIN(8, bitsleft); 1999 bitsleft -= nbits; 2000 for (i = 0; i < nbits; i++) { 2001 *dest++ = (data & mask ? fg_color: bg_color); 2002 mask = mask >> 1; 2003 } 2004 } 2005 } 2006 } 2007 2008 /* 2009 * bit_to_pix24 is for 24-bit frame buffers. It will write four output bytes 2010 * for each bit of input bitmap. It inverts the input bits before 2011 * doing the output translation, for reverse video. Note that each 2012 * 24-bit RGB value is finally stored in a 32-bit unsigned int, with the 2013 * high-order byte set to zero. 2014 * 2015 * Assuming foreground is 00000000 11111111 11111111 11111111 2016 * and background is 00000000 00000000 00000000 00000000 2017 * An input data byte of 0x53 will output the bit pattern 2018 * 2019 * 00000000 00000000 00000000 00000000 2020 * 00000000 11111111 11111111 11111111 2021 * 00000000 00000000 00000000 00000000 2022 * 00000000 11111111 11111111 11111111 2023 * 00000000 00000000 00000000 00000000 2024 * 00000000 00000000 00000000 00000000 2025 * 00000000 11111111 11111111 11111111 2026 * 00000000 11111111 11111111 11111111 2027 * 2028 */ 2029 typedef uint32_t pixel32_t; 2030 2031 void 2032 bit_to_pix24( 2033 struct tem *tem, 2034 uchar_t c, 2035 text_color_t fg_color4, 2036 text_color_t bg_color4) 2037 { 2038 struct tem_state *tems = tem->state; 2039 int row; 2040 int byte; 2041 int i; 2042 uint8_t *cp; 2043 uint8_t data; 2044 int bytes_wide; 2045 int bitsleft, nbits; 2046 2047 pixel32_t fg_color32, bg_color32, *destp; 2048 2049 ASSERT(fg_color4 < 16 && bg_color4 < 16); 2050 2051 fg_color32 = PIX4TO32(fg_color4); 2052 bg_color32 = PIX4TO32(bg_color4); 2053 2054 destp = (pixel32_t *)tems->a_pix_data; 2055 cp = tems->a_font.char_ptr[c]; 2056 bytes_wide = (tems->a_font.width + 7) / 8; 2057 2058 for (row = 0; row < tems->a_font.height; row++) { 2059 bitsleft = tems->a_font.width; 2060 for (byte = 0; byte < bytes_wide; byte++) { 2061 data = *cp++; 2062 nbits = MIN(8, bitsleft); 2063 bitsleft -= nbits; 2064 for (i = 0; i < nbits; i++) { 2065 *destp++ = ((data << i) & 0x80 ? 2066 fg_color32 : bg_color32); 2067 } 2068 } 2069 } 2070 } 2071 2072 /* ARGSUSED */ 2073 text_color_t 2074 ansi_bg_to_solaris(struct tem *tem, int ansi) 2075 { 2076 return (bg_xlate[ansi]); 2077 } 2078 2079 text_color_t 2080 ansi_fg_to_solaris(struct tem *tem, int ansi) 2081 { 2082 if (tem->state->a_flags & TEM_ATTR_BOLD) 2083 return (fg_brt_xlate[ansi]); 2084 else 2085 return (fg_dim_xlate[ansi]); 2086 } 2087 2088 static void 2089 tem_get_color(struct tem *tem, text_color_t *fg, text_color_t *bg) 2090 { 2091 if (tem->state->a_flags & TEM_ATTR_SCREEN_REVERSE) { 2092 *fg = ansi_fg_to_solaris(tem, 2093 DEFAULT_ANSI_BACKGROUND); 2094 *bg = ansi_bg_to_solaris(tem, 2095 DEFAULT_ANSI_FOREGROUND); 2096 } else { 2097 *fg = ansi_fg_to_solaris(tem, 2098 DEFAULT_ANSI_FOREGROUND); 2099 *bg = ansi_bg_to_solaris(tem, 2100 DEFAULT_ANSI_BACKGROUND); 2101 } 2102 } 2103 2104 /* 2105 * Clear a rectangle of screen for pixel mode. 2106 * 2107 * arguments: 2108 * row: start row# 2109 * nrows: the number of rows to clear 2110 * offset_y: the offset of height in pixels to begin clear 2111 * col: start col# 2112 * ncols: the number of cols to clear 2113 * offset_x: the offset of width in pixels to begin clear 2114 * scroll_up: whether this function is called during sroll up, 2115 * which is called only once. 2116 */ 2117 void 2118 tem_pix_cls_range(tem_t *tem, 2119 screen_pos_t row, int nrows, int offset_y, 2120 screen_pos_t col, int ncols, int offset_x, 2121 boolean_t sroll_up, cred_t *credp, 2122 enum called_from called_from) 2123 { 2124 struct tem_state *tems = tem->state; 2125 struct vis_consdisplay da; 2126 int i, j; 2127 int row_add = 0; 2128 text_color_t fg_color; 2129 text_color_t bg_color; 2130 2131 ASSERT((called_from == CALLED_FROM_STANDALONE) || 2132 MUTEX_HELD(&tem->lock)); 2133 2134 if (sroll_up) 2135 row_add = tems->a_c_dimension.height - 1; 2136 2137 da.width = tems->a_font.width; 2138 da.height = tems->a_font.height; 2139 2140 tem_get_color(tem, &fg_color, &bg_color); 2141 2142 BIT_TO_PIX(tem, ' ', fg_color, bg_color); 2143 da.data = (uchar_t *)tems->a_pix_data; 2144 2145 for (i = 0; i < nrows; i++, row++) { 2146 da.row = (row + row_add) * da.height + offset_y; 2147 da.col = col * da.width + offset_x; 2148 for (j = 0; j < ncols; j++) { 2149 tem_display(tem, &da, credp, called_from); 2150 da.col += da.width; 2151 } 2152 } 2153 } 2154