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