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