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