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