xref: /illumos-gate/usr/src/uts/common/io/tem_safe.c (revision 0e3b7565e6fd42aa8b3cf5b0c25be52203b47fb1)
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 *, tem_char_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 *, tem_char_t,
96 			cred_t *, enum called_from);
97 static void	tem_safe_getparams(struct tem_vt_state *, tem_char_t,
98 			cred_t *, enum called_from);
99 static void	tem_safe_outch(struct tem_vt_state *, tem_char_t,
100 			cred_t *, enum called_from);
101 static void	tem_safe_parse(struct tem_vt_state *, tem_char_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 		    tem_char_t *, 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, tem_char_t c,
151 		    text_color_t fg_color, text_color_t bg_color);
152 static void	bit_to_pix8(struct tem_vt_state *tem, tem_char_t c,
153 		    text_color_t fg_color, text_color_t bg_color);
154 static void	bit_to_pix16(struct tem_vt_state *tem, tem_char_t c,
155 		    text_color_t fg_color, text_color_t bg_color);
156 static void	bit_to_pix24(struct tem_vt_state *tem, tem_char_t c,
157 		    text_color_t fg_color, text_color_t bg_color);
158 static void	bit_to_pix32(struct tem_vt_state *tem, tem_char_t c,
159 		    text_color_t fg_color, text_color_t bg_color);
160 
161 /* BEGIN CSTYLED */
162 /*                                  Bk  Rd  Gr  Br  Bl  Mg  Cy  Wh */
163 static text_color_t dim_xlate[] = {  1,  5,  3,  7,  2,  6,  4,  8 };
164 static text_color_t brt_xlate[] = {  9, 13, 11, 15, 10, 14, 12,  0 };
165 /* END CSTYLED */
166 
167 
168 text_cmap_t cmap4_to_24 = {
169 /* BEGIN CSTYLED */
170 /* 0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
171   Wh+  Bk   Bl   Gr   Cy   Rd   Mg   Br   Wh   Bk+  Bl+  Gr+  Cy+  Rd+  Mg+  Yw */
172   0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff,
173   0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff,
174   0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00
175 /* END CSTYLED */
176 };
177 
178 #define	PIX4TO32(pix4) (uint32_t)(  \
179     cmap4_to_24.red[pix4] << 16 |  \
180     cmap4_to_24.green[pix4] << 8 | \
181     cmap4_to_24.blue[pix4])
182 
183 #define	INVERSE(ch) (ch ^ 0xff)
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, fg, bg)	{		\
190 	ASSERT(tems.ts_callbacks->tsc_bit2pix != NULL);			\
191 	(void) (*tems.ts_callbacks->tsc_bit2pix)((tem), (c), (fg), (bg));\
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 	int 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 /*
538  * select graphics mode based on the param vals stored in a_params
539  */
540 static void
541 tem_safe_selgraph(struct tem_vt_state *tem)
542 {
543 	int curparam;
544 	int count = 0;
545 	int param;
546 
547 	tem->tvs_state = A_STATE_START;
548 
549 	curparam = tem->tvs_curparam;
550 	do {
551 		param = tem->tvs_params[count];
552 
553 		switch (param) {
554 		case -1:
555 		case 0:
556 			/* reset to initial normal settings */
557 			tem->tvs_fg_color = tems.ts_init_color.fg_color;
558 			tem->tvs_bg_color = tems.ts_init_color.bg_color;
559 			tem->tvs_flags = tems.ts_init_color.a_flags;
560 			break;
561 
562 		case 1: /* Bold Intense */
563 			tem->tvs_flags |= TEM_ATTR_BOLD;
564 			break;
565 
566 		case 2: /* Faint Intense */
567 			tem->tvs_flags &= ~TEM_ATTR_BOLD;
568 			break;
569 
570 		case 5: /* Blink */
571 			tem->tvs_flags |= TEM_ATTR_BLINK;
572 			break;
573 
574 		case 7: /* Reverse video */
575 			if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
576 				tem->tvs_flags &= ~TEM_ATTR_REVERSE;
577 			} else {
578 				tem->tvs_flags |= TEM_ATTR_REVERSE;
579 			}
580 			break;
581 
582 		case 30: /* black	(grey)		foreground */
583 		case 31: /* red		(light red)	foreground */
584 		case 32: /* green	(light green)	foreground */
585 		case 33: /* brown	(yellow)	foreground */
586 		case 34: /* blue	(light blue)	foreground */
587 		case 35: /* magenta	(light magenta)	foreground */
588 		case 36: /* cyan	(light cyan)	foreground */
589 		case 37: /* white	(bright white)	foreground */
590 			tem->tvs_fg_color = param - 30;
591 			tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
592 			break;
593 
594 		case 39:
595 			/*
596 			 * Reset the foreground colour and brightness.
597 			 */
598 			tem->tvs_fg_color = tems.ts_init_color.fg_color;
599 			if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_FG)
600 				tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
601 			else
602 				tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
603 			break;
604 
605 		case 40: /* black	(grey)		background */
606 		case 41: /* red		(light red)	background */
607 		case 42: /* green	(light green)	background */
608 		case 43: /* brown	(yellow)	background */
609 		case 44: /* blue	(light blue)	background */
610 		case 45: /* magenta	(light magenta)	background */
611 		case 46: /* cyan	(light cyan)	background */
612 		case 47: /* white	(bright white)	background */
613 			tem->tvs_bg_color = param - 40;
614 			tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
615 			break;
616 
617 		case 49:
618 			/*
619 			 * Reset the background colour and brightness.
620 			 */
621 			tem->tvs_bg_color = tems.ts_init_color.bg_color;
622 			if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_BG)
623 				tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
624 			else
625 				tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
626 			break;
627 
628 		case 90: /* black	(grey)		foreground */
629 		case 91: /* red		(light red)	foreground */
630 		case 92: /* green	(light green)	foreground */
631 		case 93: /* brown	(yellow)	foreground */
632 		case 94: /* blue	(light blue)	foreground */
633 		case 95: /* magenta	(light magenta)	foreground */
634 		case 96: /* cyan	(light cyan)	foreground */
635 		case 97: /* white	(bright white)	foreground */
636 			tem->tvs_fg_color = param - 90;
637 			tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
638 			break;
639 
640 		case 100: /* black	(grey)		background */
641 		case 101: /* red	(light red)	background */
642 		case 102: /* green	(light green)	background */
643 		case 103: /* brown	(yellow)	background */
644 		case 104: /* blue	(light blue)	background */
645 		case 105: /* magenta	(light magenta)	background */
646 		case 106: /* cyan	(light cyan)	background */
647 		case 107: /* white	(bright white)	background */
648 			tem->tvs_bg_color = param - 100;
649 			tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
650 			break;
651 
652 		default:
653 			break;
654 		}
655 		count++;
656 		curparam--;
657 
658 	} while (curparam > 0);
659 }
660 
661 /*
662  * perform the appropriate action for the escape sequence
663  *
664  * General rule:  This code does not validate the arguments passed.
665  *                It assumes that the next lower level will do so.
666  */
667 static void
668 tem_safe_chkparam(struct tem_vt_state *tem, tem_char_t ch, cred_t *credp,
669     enum called_from called_from)
670 {
671 	int	i;
672 	int	row;
673 	int	col;
674 
675 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
676 	    MUTEX_HELD(&tem->tvs_lock));
677 
678 	row = tem->tvs_c_cursor.row;
679 	col = tem->tvs_c_cursor.col;
680 
681 	switch (ch) {
682 
683 	case 'm': /* select terminal graphics mode */
684 		tem_safe_send_data(tem, credp, called_from);
685 		tem_safe_selgraph(tem);
686 		break;
687 
688 	case '@':		/* insert char */
689 		tem_safe_setparam(tem, 1, 1);
690 		tem_safe_shift(tem, tem->tvs_params[0], TEM_SHIFT_RIGHT,
691 		    credp, called_from);
692 		break;
693 
694 	case 'A':		/* cursor up */
695 		tem_safe_setparam(tem, 1, 1);
696 		tem_safe_mv_cursor(tem, row - tem->tvs_params[0], col,
697 		    credp, called_from);
698 		break;
699 
700 	case 'd':		/* VPA - vertical position absolute */
701 		tem_safe_setparam(tem, 1, 1);
702 		tem_safe_mv_cursor(tem, tem->tvs_params[0] - 1, col,
703 		    credp, called_from);
704 		break;
705 
706 	case 'e':		/* VPR - vertical position relative */
707 	case 'B':		/* cursor down */
708 		tem_safe_setparam(tem, 1, 1);
709 		tem_safe_mv_cursor(tem, row + tem->tvs_params[0], col,
710 		    credp, called_from);
711 		break;
712 
713 	case 'a':		/* HPR - horizontal position relative */
714 	case 'C':		/* cursor right */
715 		tem_safe_setparam(tem, 1, 1);
716 		tem_safe_mv_cursor(tem, row, col + tem->tvs_params[0],
717 		    credp, called_from);
718 		break;
719 
720 	case '`':		/* HPA - horizontal position absolute */
721 		tem_safe_setparam(tem, 1, 1);
722 		tem_safe_mv_cursor(tem, row, tem->tvs_params[0] - 1,
723 		    credp, called_from);
724 		break;
725 
726 	case 'D':		/* cursor left */
727 		tem_safe_setparam(tem, 1, 1);
728 		tem_safe_mv_cursor(tem, row, col - tem->tvs_params[0],
729 		    credp, called_from);
730 		break;
731 
732 	case 'E':		/* CNL cursor next line */
733 		tem_safe_setparam(tem, 1, 1);
734 		tem_safe_mv_cursor(tem, row + tem->tvs_params[0], 0,
735 		    credp, called_from);
736 		break;
737 
738 	case 'F':		/* CPL cursor previous line */
739 		tem_safe_setparam(tem, 1, 1);
740 		tem_safe_mv_cursor(tem, row - tem->tvs_params[0], 0,
741 		    credp, called_from);
742 		break;
743 
744 	case 'G':		/* cursor horizontal position */
745 		tem_safe_setparam(tem, 1, 1);
746 		tem_safe_mv_cursor(tem, row, tem->tvs_params[0] - 1,
747 		    credp, called_from);
748 		break;
749 
750 	case 'g':		/* clear tabs */
751 		tem_safe_setparam(tem, 1, 0);
752 		tem_safe_clear_tabs(tem, tem->tvs_params[0]);
753 		break;
754 
755 	case 'f':		/* HVP Horizontal and Vertical Position */
756 	case 'H':		/* CUP position cursor */
757 		tem_safe_setparam(tem, 2, 1);
758 		tem_safe_mv_cursor(tem,
759 		    tem->tvs_params[0] - 1,
760 		    tem->tvs_params[1] - 1,
761 		    credp, called_from);
762 		break;
763 
764 	case 'I':		/* CHT - Cursor Horizontal Tab */
765 		/* Not implemented */
766 		break;
767 
768 	case 'J':		/* ED - Erase in Display */
769 		tem_safe_send_data(tem, credp, called_from);
770 		tem_safe_setparam(tem, 1, 0);
771 		switch (tem->tvs_params[0]) {
772 		case 0:
773 			/* erase cursor to end of screen */
774 			/* FIRST erase cursor to end of line */
775 			tem_safe_clear_chars(tem,
776 			    tems.ts_c_dimension.width -
777 			    tem->tvs_c_cursor.col,
778 			    tem->tvs_c_cursor.row,
779 			    tem->tvs_c_cursor.col, credp, called_from);
780 
781 			/* THEN erase lines below the cursor */
782 			for (row = tem->tvs_c_cursor.row + 1;
783 			    row < tems.ts_c_dimension.height;
784 			    row++) {
785 				tem_safe_clear_chars(tem,
786 				    tems.ts_c_dimension.width,
787 				    row, 0, credp, called_from);
788 			}
789 			break;
790 
791 		case 1:
792 			/* erase beginning of screen to cursor */
793 			/* FIRST erase lines above the cursor */
794 			for (row = 0;
795 			    row < tem->tvs_c_cursor.row;
796 			    row++) {
797 				tem_safe_clear_chars(tem,
798 				    tems.ts_c_dimension.width,
799 				    row, 0, credp, called_from);
800 			}
801 			/* THEN erase beginning of line to cursor */
802 			tem_safe_clear_chars(tem,
803 			    tem->tvs_c_cursor.col + 1,
804 			    tem->tvs_c_cursor.row,
805 			    0, credp, called_from);
806 			break;
807 
808 		case 2:
809 			/* erase whole screen */
810 			for (row = 0;
811 			    row < tems.ts_c_dimension.height;
812 			    row++) {
813 				tem_safe_clear_chars(tem,
814 				    tems.ts_c_dimension.width,
815 				    row, 0, credp, called_from);
816 			}
817 			break;
818 		}
819 		break;
820 
821 	case 'K':		/* EL - Erase in Line */
822 		tem_safe_send_data(tem, credp, called_from);
823 		tem_safe_setparam(tem, 1, 0);
824 		switch (tem->tvs_params[0]) {
825 		case 0:
826 			/* erase cursor to end of line */
827 			tem_safe_clear_chars(tem,
828 			    (tems.ts_c_dimension.width -
829 			    tem->tvs_c_cursor.col),
830 			    tem->tvs_c_cursor.row,
831 			    tem->tvs_c_cursor.col,
832 			    credp, called_from);
833 			break;
834 
835 		case 1:
836 			/* erase beginning of line to cursor */
837 			tem_safe_clear_chars(tem,
838 			    tem->tvs_c_cursor.col + 1,
839 			    tem->tvs_c_cursor.row,
840 			    0, credp, called_from);
841 			break;
842 
843 		case 2:
844 			/* erase whole line */
845 			tem_safe_clear_chars(tem,
846 			    tems.ts_c_dimension.width,
847 			    tem->tvs_c_cursor.row,
848 			    0, credp, called_from);
849 			break;
850 		}
851 		break;
852 
853 	case 'L':		/* insert line */
854 		tem_safe_send_data(tem, credp, called_from);
855 		tem_safe_setparam(tem, 1, 1);
856 		tem_safe_scroll(tem,
857 		    tem->tvs_c_cursor.row,
858 		    tems.ts_c_dimension.height - 1,
859 		    tem->tvs_params[0], TEM_SCROLL_DOWN,
860 		    credp, called_from);
861 		break;
862 
863 	case 'M':		/* delete line */
864 		tem_safe_send_data(tem, credp, called_from);
865 		tem_safe_setparam(tem, 1, 1);
866 		tem_safe_scroll(tem,
867 		    tem->tvs_c_cursor.row,
868 		    tems.ts_c_dimension.height - 1,
869 		    tem->tvs_params[0], TEM_SCROLL_UP,
870 		    credp, called_from);
871 		break;
872 
873 	case 'P':		/* DCH - delete char */
874 		tem_safe_setparam(tem, 1, 1);
875 		tem_safe_shift(tem, tem->tvs_params[0], TEM_SHIFT_LEFT,
876 		    credp, called_from);
877 		break;
878 
879 	case 'S':		/* scroll up */
880 		tem_safe_send_data(tem, credp, called_from);
881 		tem_safe_setparam(tem, 1, 1);
882 		tem_safe_scroll(tem, 0,
883 		    tems.ts_c_dimension.height - 1,
884 		    tem->tvs_params[0], TEM_SCROLL_UP,
885 		    credp, called_from);
886 		break;
887 
888 	case 'T':		/* scroll down */
889 		tem_safe_send_data(tem, credp, called_from);
890 		tem_safe_setparam(tem, 1, 1);
891 		tem_safe_scroll(tem, 0,
892 		    tems.ts_c_dimension.height - 1,
893 		    tem->tvs_params[0], TEM_SCROLL_DOWN,
894 		    credp, called_from);
895 		break;
896 
897 	case 'X':		/* erase char */
898 		tem_safe_setparam(tem, 1, 1);
899 		tem_safe_clear_chars(tem,
900 		    tem->tvs_params[0],
901 		    tem->tvs_c_cursor.row,
902 		    tem->tvs_c_cursor.col,
903 		    credp, called_from);
904 		break;
905 
906 	case 'Z':		/* cursor backward tabulation */
907 		tem_safe_setparam(tem, 1, 1);
908 
909 		/*
910 		 * Rule exception - We do sanity checking here.
911 		 *
912 		 * Restrict the count to a sane value to keep from
913 		 * looping for a long time.  There can't be more than one
914 		 * tab stop per column, so use that as a limit.
915 		 */
916 		if (tem->tvs_params[0] > tems.ts_c_dimension.width)
917 			tem->tvs_params[0] = tems.ts_c_dimension.width;
918 
919 		for (i = 0; i < tem->tvs_params[0]; i++)
920 			tem_safe_back_tab(tem, credp, called_from);
921 		break;
922 	}
923 	tem->tvs_state = A_STATE_START;
924 }
925 
926 
927 /*
928  * Gather the parameters of an ANSI escape sequence
929  */
930 static void
931 tem_safe_getparams(struct tem_vt_state *tem, tem_char_t ch,
932     cred_t *credp, enum called_from called_from)
933 {
934 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
935 	    MUTEX_HELD(&tem->tvs_lock));
936 
937 	if (ch >= '0' && ch <= '9') {
938 		tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0'));
939 		tem->tvs_gotparam = B_TRUE;  /* Remember got parameter */
940 		return; /* Return immediately */
941 	} else if (tem->tvs_state == A_STATE_CSI_EQUAL ||
942 	    tem->tvs_state == A_STATE_CSI_QMARK) {
943 		tem->tvs_state = A_STATE_START;
944 	} else {
945 		if (tem->tvs_curparam < TEM_MAXPARAMS) {
946 			if (tem->tvs_gotparam) {
947 				/* get the parameter value */
948 				tem->tvs_params[tem->tvs_curparam] =
949 				    tem->tvs_paramval;
950 			}
951 			tem->tvs_curparam++;
952 		}
953 
954 		if (ch == ';') {
955 			/* Restart parameter search */
956 			tem->tvs_gotparam = B_FALSE;
957 			tem->tvs_paramval = 0; /* No parame value yet */
958 		} else {
959 			/* Handle escape sequence */
960 			tem_safe_chkparam(tem, ch, credp, called_from);
961 		}
962 	}
963 }
964 
965 /*
966  * Add character to internal buffer.
967  * When its full, send it to the next layer.
968  */
969 
970 static void
971 tem_safe_outch(struct tem_vt_state *tem, tem_char_t ch,
972     cred_t *credp, enum called_from called_from)
973 {
974 
975 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
976 	    called_from == CALLED_FROM_STANDALONE);
977 
978 	/* buffer up the character until later */
979 
980 	tem->tvs_outbuf[tem->tvs_outindex++] = ch;
981 	tem->tvs_c_cursor.col++;
982 	if (tem->tvs_c_cursor.col >= tems.ts_c_dimension.width) {
983 		tem_safe_send_data(tem, credp, called_from);
984 		tem_safe_new_line(tem, credp, called_from);
985 	}
986 }
987 
988 static void
989 tem_safe_new_line(struct tem_vt_state *tem,
990     cred_t *credp, enum called_from called_from)
991 {
992 	tem_safe_cr(tem);
993 	tem_safe_lf(tem, credp, called_from);
994 }
995 
996 static void
997 tem_safe_cr(struct tem_vt_state *tem)
998 {
999 	tem->tvs_c_cursor.col = 0;
1000 	tem_safe_align_cursor(tem);
1001 }
1002 
1003 static void
1004 tem_safe_lf(struct tem_vt_state *tem,
1005     cred_t *credp, enum called_from called_from)
1006 {
1007 	int row;
1008 
1009 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1010 	    MUTEX_HELD(&tem->tvs_lock));
1011 
1012 	/*
1013 	 * Sanity checking notes:
1014 	 * . a_nscroll was validated when it was set.
1015 	 * . Regardless of that, tem_safe_scroll and tem_safe_mv_cursor
1016 	 *   will prevent anything bad from happening.
1017 	 */
1018 	row = tem->tvs_c_cursor.row + 1;
1019 
1020 	if (row >= tems.ts_c_dimension.height) {
1021 		if (tem->tvs_nscroll != 0) {
1022 			tem_safe_scroll(tem, 0,
1023 			    tems.ts_c_dimension.height - 1,
1024 			    tem->tvs_nscroll, TEM_SCROLL_UP,
1025 			    credp, called_from);
1026 			row = tems.ts_c_dimension.height -
1027 			    tem->tvs_nscroll;
1028 		} else {	/* no scroll */
1029 			/*
1030 			 * implement Esc[#r when # is zero.  This means no
1031 			 * scroll but just return cursor to top of screen,
1032 			 * do not clear screen.
1033 			 */
1034 			row = 0;
1035 		}
1036 	}
1037 
1038 	tem_safe_mv_cursor(tem, row, tem->tvs_c_cursor.col,
1039 	    credp, called_from);
1040 
1041 	if (tem->tvs_nscroll == 0) {
1042 		/* erase rest of cursor line */
1043 		tem_safe_clear_chars(tem,
1044 		    tems.ts_c_dimension.width -
1045 		    tem->tvs_c_cursor.col,
1046 		    tem->tvs_c_cursor.row,
1047 		    tem->tvs_c_cursor.col,
1048 		    credp, called_from);
1049 
1050 	}
1051 
1052 	tem_safe_align_cursor(tem);
1053 }
1054 
1055 static void
1056 tem_safe_send_data(struct tem_vt_state *tem, cred_t *credp,
1057     enum called_from called_from)
1058 {
1059 	text_color_t fg_color;
1060 	text_color_t bg_color;
1061 
1062 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1063 	    MUTEX_HELD(&tem->tvs_lock));
1064 
1065 	if (tem->tvs_outindex == 0) {
1066 		tem_safe_align_cursor(tem);
1067 		return;
1068 	}
1069 
1070 	tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_REVERSE);
1071 	tem_safe_virtual_display(tem,
1072 	    tem->tvs_outbuf, tem->tvs_outindex,
1073 	    tem->tvs_s_cursor.row, tem->tvs_s_cursor.col,
1074 	    fg_color, bg_color);
1075 
1076 	if (tem->tvs_isactive) {
1077 		/*
1078 		 * Call the primitive to render this data.
1079 		 */
1080 		tem_safe_callback_display(tem,
1081 		    tem->tvs_outbuf, tem->tvs_outindex,
1082 		    tem->tvs_s_cursor.row, tem->tvs_s_cursor.col,
1083 		    fg_color, bg_color,
1084 		    credp, called_from);
1085 	}
1086 
1087 	tem->tvs_outindex = 0;
1088 
1089 	tem_safe_align_cursor(tem);
1090 }
1091 
1092 
1093 /*
1094  * We have just done something to the current output point.  Reset the start
1095  * point for the buffered data in a_outbuf.  There shouldn't be any data
1096  * buffered yet.
1097  */
1098 static void
1099 tem_safe_align_cursor(struct tem_vt_state *tem)
1100 {
1101 	tem->tvs_s_cursor.row = tem->tvs_c_cursor.row;
1102 	tem->tvs_s_cursor.col = tem->tvs_c_cursor.col;
1103 }
1104 
1105 /*
1106  * State machine parser based on the current state and character input
1107  * major terminations are to control character or normal character
1108  */
1109 
1110 static void
1111 tem_safe_parse(struct tem_vt_state *tem, tem_char_t ch,
1112     cred_t *credp, enum called_from called_from)
1113 {
1114 	int	i;
1115 
1116 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1117 	    MUTEX_HELD(&tem->tvs_lock));
1118 
1119 	if (tem->tvs_state == A_STATE_START) {	/* Normal state? */
1120 		if (ch == A_CSI || ch == A_ESC || ch < ' ') {
1121 			/* Control */
1122 			tem_safe_control(tem, ch, credp, called_from);
1123 		} else {
1124 			/* Display */
1125 			tem_safe_outch(tem, ch, credp, called_from);
1126 		}
1127 		return;
1128 	}
1129 
1130 	/* In <ESC> sequence */
1131 	if (tem->tvs_state != A_STATE_ESC) {	/* Need to get parameters? */
1132 		if (tem->tvs_state != A_STATE_CSI) {
1133 			tem_safe_getparams(tem, ch, credp, called_from);
1134 			return;
1135 		}
1136 
1137 		switch (ch) {
1138 		case '?':
1139 			tem->tvs_state = A_STATE_CSI_QMARK;
1140 			return;
1141 		case '=':
1142 			tem->tvs_state = A_STATE_CSI_EQUAL;
1143 			return;
1144 		case 's':
1145 			/*
1146 			 * As defined below, this sequence
1147 			 * saves the cursor.  However, Sun
1148 			 * defines ESC[s as reset.  We resolved
1149 			 * the conflict by selecting reset as it
1150 			 * is exported in the termcap file for
1151 			 * sun-mon, while the "save cursor"
1152 			 * definition does not exist anywhere in
1153 			 * /etc/termcap.
1154 			 * However, having no coherent
1155 			 * definition of reset, we have not
1156 			 * implemented it.
1157 			 */
1158 
1159 			/*
1160 			 * Original code
1161 			 * tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1162 			 * tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1163 			 * tem->tvs_state = A_STATE_START;
1164 			 */
1165 
1166 			tem->tvs_state = A_STATE_START;
1167 			return;
1168 		case 'u':
1169 			tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row,
1170 			    tem->tvs_r_cursor.col, credp, called_from);
1171 			tem->tvs_state = A_STATE_START;
1172 			return;
1173 		case 'p':	/* sunbow */
1174 			tem_safe_send_data(tem, credp, called_from);
1175 			/*
1176 			 * Don't set anything if we are
1177 			 * already as we want to be.
1178 			 */
1179 			if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
1180 				tem->tvs_flags &= ~TEM_ATTR_SCREEN_REVERSE;
1181 				/*
1182 				 * If we have switched the characters to be the
1183 				 * inverse from the screen, then switch them as
1184 				 * well to keep them the inverse of the screen.
1185 				 */
1186 				if (tem->tvs_flags & TEM_ATTR_REVERSE)
1187 					tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1188 				else
1189 					tem->tvs_flags |= TEM_ATTR_REVERSE;
1190 			}
1191 			tem_safe_cls(tem, credp, called_from);
1192 			tem->tvs_state = A_STATE_START;
1193 			return;
1194 		case 'q':	/* sunwob */
1195 			tem_safe_send_data(tem, credp, called_from);
1196 			/*
1197 			 * Don't set anything if we are
1198 			 * already where as we want to be.
1199 			 */
1200 			if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) {
1201 				tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE;
1202 				/*
1203 				 * If we have switched the characters to be the
1204 				 * inverse from the screen, then switch them as
1205 				 * well to keep them the inverse of the screen.
1206 				 */
1207 				if (!(tem->tvs_flags & TEM_ATTR_REVERSE))
1208 					tem->tvs_flags |= TEM_ATTR_REVERSE;
1209 				else
1210 					tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1211 			}
1212 
1213 			tem_safe_cls(tem, credp, called_from);
1214 			tem->tvs_state = A_STATE_START;
1215 			return;
1216 		case 'r':	/* sunscrl */
1217 			/*
1218 			 * Rule exception:  check for validity here.
1219 			 */
1220 			tem->tvs_nscroll = tem->tvs_paramval;
1221 			if (tem->tvs_nscroll > tems.ts_c_dimension.height)
1222 				tem->tvs_nscroll = tems.ts_c_dimension.height;
1223 			if (tem->tvs_nscroll < 0)
1224 				tem->tvs_nscroll = 1;
1225 			tem->tvs_state = A_STATE_START;
1226 			return;
1227 		default:
1228 			tem_safe_getparams(tem, ch, credp, called_from);
1229 			return;
1230 		}
1231 	}
1232 
1233 	/* Previous char was <ESC> */
1234 	if (ch == '[') {
1235 		tem->tvs_curparam = 0;
1236 		tem->tvs_paramval = 0;
1237 		tem->tvs_gotparam = B_FALSE;
1238 		/* clear the parameters */
1239 		for (i = 0; i < TEM_MAXPARAMS; i++)
1240 			tem->tvs_params[i] = -1;
1241 		tem->tvs_state = A_STATE_CSI;
1242 	} else if (ch == 'Q') {	/* <ESC>Q ? */
1243 		tem->tvs_state = A_STATE_START;
1244 	} else if (ch == 'C') {	/* <ESC>C ? */
1245 		tem->tvs_state = A_STATE_START;
1246 	} else {
1247 		tem->tvs_state = A_STATE_START;
1248 		if (ch == 'c') {
1249 			/* ESC c resets display */
1250 			tem_safe_reset_display(tem, credp, called_from,
1251 			    B_TRUE, B_TRUE);
1252 		} else if (ch == 'H') {
1253 			/* ESC H sets a tab */
1254 			tem_safe_set_tab(tem);
1255 		} else if (ch == '7') {
1256 			/* ESC 7 Save Cursor position */
1257 			tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1258 			tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1259 		} else if (ch == '8') {
1260 			/* ESC 8 Restore Cursor position */
1261 			tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row,
1262 			    tem->tvs_r_cursor.col, credp, called_from);
1263 		/* check for control chars */
1264 		} else if (ch < ' ') {
1265 			tem_safe_control(tem, ch, credp, called_from);
1266 		} else {
1267 			tem_safe_outch(tem, ch, credp, called_from);
1268 		}
1269 	}
1270 }
1271 
1272 /* ARGSUSED */
1273 static void
1274 tem_safe_bell(struct tem_vt_state *tem, enum called_from called_from)
1275 {
1276 	if (called_from == CALLED_FROM_STANDALONE)
1277 		(void) beep_polled(BEEP_CONSOLE);
1278 	else
1279 		(void) beep(BEEP_CONSOLE);
1280 }
1281 
1282 
1283 static void
1284 tem_safe_scroll(struct tem_vt_state *tem, int start, int end, int count,
1285     int direction, cred_t *credp, enum called_from called_from)
1286 {
1287 	int	row;
1288 	int	lines_affected;
1289 
1290 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1291 	    called_from == CALLED_FROM_STANDALONE);
1292 
1293 	lines_affected = end - start + 1;
1294 	if (count > lines_affected)
1295 		count = lines_affected;
1296 	if (count <= 0)
1297 		return;
1298 
1299 	switch (direction) {
1300 	case TEM_SCROLL_UP:
1301 		if (count < lines_affected) {
1302 			tem_safe_copy_area(tem, 0, start + count,
1303 			    tems.ts_c_dimension.width - 1, end,
1304 			    0, start, credp, called_from);
1305 		}
1306 		for (row = (end - count) + 1; row <= end; row++) {
1307 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1308 			    row, 0, credp, called_from);
1309 		}
1310 		break;
1311 
1312 	case TEM_SCROLL_DOWN:
1313 		if (count < lines_affected) {
1314 			tem_safe_copy_area(tem, 0, start,
1315 			    tems.ts_c_dimension.width - 1,
1316 			    end - count, 0, start + count,
1317 			    credp, called_from);
1318 		}
1319 		for (row = start; row < start + count; row++) {
1320 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1321 			    row, 0, credp, called_from);
1322 		}
1323 		break;
1324 	}
1325 }
1326 
1327 static void
1328 tem_safe_copy_area(struct tem_vt_state *tem,
1329     screen_pos_t s_col, screen_pos_t s_row,
1330     screen_pos_t e_col, screen_pos_t e_row,
1331     screen_pos_t t_col, screen_pos_t t_row,
1332     cred_t *credp, enum called_from called_from)
1333 {
1334 	int rows;
1335 	int cols;
1336 
1337 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1338 	    called_from == CALLED_FROM_STANDALONE);
1339 
1340 	if (s_col < 0 || s_row < 0 ||
1341 	    e_col < 0 || e_row < 0 ||
1342 	    t_col < 0 || t_row < 0 ||
1343 	    s_col >= tems.ts_c_dimension.width ||
1344 	    e_col >= tems.ts_c_dimension.width ||
1345 	    t_col >= tems.ts_c_dimension.width ||
1346 	    s_row >= tems.ts_c_dimension.height ||
1347 	    e_row >= tems.ts_c_dimension.height ||
1348 	    t_row >= tems.ts_c_dimension.height)
1349 		return;
1350 
1351 	if (s_row > e_row || s_col > e_col)
1352 		return;
1353 
1354 	rows = e_row - s_row + 1;
1355 	cols = e_col - s_col + 1;
1356 	if (t_row + rows > tems.ts_c_dimension.height ||
1357 	    t_col + cols > tems.ts_c_dimension.width)
1358 		return;
1359 
1360 	tem_safe_virtual_copy(tem,
1361 	    s_col, s_row,
1362 	    e_col, e_row,
1363 	    t_col, t_row);
1364 
1365 	if (!tem->tvs_isactive)
1366 		return;
1367 
1368 	tem_safe_callback_copy(tem, s_col, s_row,
1369 	    e_col, e_row, t_col, t_row, credp, called_from);
1370 }
1371 
1372 static void
1373 tem_safe_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row,
1374     screen_pos_t col, cred_t *credp, enum called_from called_from)
1375 {
1376 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1377 	    called_from == CALLED_FROM_STANDALONE);
1378 
1379 	if (row < 0 || row >= tems.ts_c_dimension.height ||
1380 	    col < 0 || col >= tems.ts_c_dimension.width ||
1381 	    count < 0)
1382 		return;
1383 
1384 	/*
1385 	 * Note that very large values of "count" could cause col+count
1386 	 * to overflow, so we check "count" independently.
1387 	 */
1388 	if (count > tems.ts_c_dimension.width ||
1389 	    col + count > tems.ts_c_dimension.width)
1390 		count = tems.ts_c_dimension.width - col;
1391 
1392 	tem_safe_virtual_cls(tem, count, row, col);
1393 
1394 	if (!tem->tvs_isactive)
1395 		return;
1396 
1397 	tem_safe_callback_cls(tem, count, row, col, credp, called_from);
1398 }
1399 
1400 /*ARGSUSED*/
1401 void
1402 tem_safe_text_display(struct tem_vt_state *tem, tem_char_t *string,
1403     int count, screen_pos_t row, screen_pos_t col,
1404     text_color_t fg_color, text_color_t bg_color,
1405     cred_t *credp, enum called_from called_from)
1406 {
1407 	struct vis_consdisplay da;
1408 	int i;
1409 	uint8_t c;
1410 
1411 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1412 	    called_from == CALLED_FROM_STANDALONE);
1413 
1414 	da.data = &c;
1415 	da.width = 1;
1416 	da.row = row;
1417 	da.col = col;
1418 
1419 	da.fg_color = fg_color;
1420 	da.bg_color = bg_color;
1421 
1422 	for (i = 0; i < count; i++) {
1423 		/* VGATEXT can only display single byte chars. */
1424 		if (string[i] > 0xff)
1425 			c = '?';
1426 		else
1427 			c = (uint8_t)string[i];
1428 		tems_safe_display(&da, credp, called_from);
1429 		da.col++;
1430 	}
1431 }
1432 
1433 /*
1434  * This function is used to blit a rectangular color image,
1435  * unperturbed on the underlying framebuffer, to render
1436  * icons and pictures.  The data is a pixel pattern that
1437  * fills a rectangle bounded to the width and height parameters.
1438  * The color pixel data must to be pre-adjusted by the caller
1439  * for the current video depth.
1440  *
1441  * This function is unused now.
1442  */
1443 /*ARGSUSED*/
1444 static void
1445 tem_safe_image_display(struct tem_vt_state *tem, uchar_t *image,
1446     int height, int width, screen_pos_t row, screen_pos_t col,
1447     cred_t *credp, enum called_from called_from)
1448 {
1449 	struct vis_consdisplay da;
1450 
1451 	mutex_enter(&tems.ts_lock);
1452 	mutex_enter(&tem->tvs_lock);
1453 
1454 	da.data = image;
1455 	da.width = (screen_size_t)width;
1456 	da.height = (screen_size_t)height;
1457 	da.row = row;
1458 	da.col = col;
1459 
1460 	tems_safe_display(&da, credp, called_from);
1461 
1462 	mutex_exit(&tem->tvs_lock);
1463 	mutex_exit(&tems.ts_lock);
1464 }
1465 
1466 
1467 /*ARGSUSED*/
1468 void
1469 tem_safe_text_copy(struct tem_vt_state *tem,
1470     screen_pos_t s_col, screen_pos_t s_row,
1471     screen_pos_t e_col, screen_pos_t e_row,
1472     screen_pos_t t_col, screen_pos_t t_row,
1473     cred_t *credp, enum called_from called_from)
1474 {
1475 	struct vis_conscopy da;
1476 
1477 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1478 	    called_from == CALLED_FROM_STANDALONE);
1479 
1480 	da.s_row = s_row;
1481 	da.s_col = s_col;
1482 	da.e_row = e_row;
1483 	da.e_col = e_col;
1484 	da.t_row = t_row;
1485 	da.t_col = t_col;
1486 
1487 	tems_safe_copy(&da, credp, called_from);
1488 }
1489 
1490 void
1491 tem_safe_text_cls(struct tem_vt_state *tem,
1492     int count, screen_pos_t row, screen_pos_t col, cred_t *credp,
1493     enum called_from called_from)
1494 {
1495 	struct vis_consdisplay da;
1496 
1497 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1498 	    called_from == CALLED_FROM_STANDALONE);
1499 
1500 	da.data = (unsigned char *)tems.ts_blank_line;
1501 	da.width = (screen_size_t)count;
1502 	da.row = row;
1503 	da.col = col;
1504 
1505 	tem_safe_get_color(tem, &da.fg_color, &da.bg_color,
1506 	    TEM_ATTR_SCREEN_REVERSE);
1507 	tems_safe_display(&da, credp, called_from);
1508 }
1509 
1510 void
1511 tem_safe_pix_display(struct tem_vt_state *tem,
1512     tem_char_t *string, int count,
1513     screen_pos_t row, screen_pos_t col,
1514     text_color_t fg_color, text_color_t bg_color,
1515     cred_t *credp, enum called_from called_from)
1516 {
1517 	struct vis_consdisplay da;
1518 	int	i;
1519 
1520 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1521 	    called_from == CALLED_FROM_STANDALONE);
1522 
1523 	da.data = (uchar_t *)tem->tvs_pix_data;
1524 	da.width = tems.ts_font.width;
1525 	da.height = tems.ts_font.height;
1526 	da.row = (row * da.height) + tems.ts_p_offset.y;
1527 	da.col = (col * da.width) + tems.ts_p_offset.x;
1528 
1529 	for (i = 0; i < count; i++) {
1530 		tem_safe_callback_bit2pix(tem, string[i], fg_color, bg_color);
1531 		tems_safe_display(&da, credp, called_from);
1532 		da.col += da.width;
1533 	}
1534 }
1535 
1536 void
1537 tem_safe_pix_copy(struct tem_vt_state *tem,
1538     screen_pos_t s_col, screen_pos_t s_row,
1539     screen_pos_t e_col, screen_pos_t e_row,
1540     screen_pos_t t_col, screen_pos_t t_row,
1541     cred_t *credp,
1542     enum called_from called_from)
1543 {
1544 	struct vis_conscopy ma;
1545 	static boolean_t need_clear = B_TRUE;
1546 
1547 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1548 	    called_from == CALLED_FROM_STANDALONE);
1549 
1550 	if (need_clear && tem->tvs_first_line > 0) {
1551 		/*
1552 		 * Clear OBP output above our kernel console term
1553 		 * when our kernel console term begins to scroll up,
1554 		 * we hope it is user friendly.
1555 		 * (Also see comments on tem_safe_pix_clear_prom_output)
1556 		 *
1557 		 * This is only one time call.
1558 		 */
1559 		tem_safe_pix_clear_prom_output(tem, credp, called_from);
1560 	}
1561 	need_clear = B_FALSE;
1562 
1563 	ma.s_row = s_row * tems.ts_font.height + tems.ts_p_offset.y;
1564 	ma.e_row = (e_row + 1) * tems.ts_font.height + tems.ts_p_offset.y - 1;
1565 	ma.t_row = t_row * tems.ts_font.height + tems.ts_p_offset.y;
1566 
1567 	/*
1568 	 * Check if we're in process of clearing OBP's columns area,
1569 	 * which only happens when term scrolls up a whole line.
1570 	 */
1571 	if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 &&
1572 	    e_col == tems.ts_c_dimension.width - 1) {
1573 		/*
1574 		 * We need to clear OBP's columns area outside our kernel
1575 		 * console term. So that we set ma.e_col to entire row here.
1576 		 */
1577 		ma.s_col = s_col * tems.ts_font.width;
1578 		ma.e_col = tems.ts_p_dimension.width - 1;
1579 
1580 		ma.t_col = t_col * tems.ts_font.width;
1581 	} else {
1582 		ma.s_col = s_col * tems.ts_font.width + tems.ts_p_offset.x;
1583 		ma.e_col = (e_col + 1) * tems.ts_font.width +
1584 		    tems.ts_p_offset.x - 1;
1585 		ma.t_col = t_col * tems.ts_font.width + tems.ts_p_offset.x;
1586 	}
1587 
1588 	tems_safe_copy(&ma, credp, called_from);
1589 
1590 	if (tem->tvs_first_line > 0 && t_row < s_row) {
1591 		/* We have scrolled up (s_row - t_row) rows. */
1592 		tem->tvs_first_line -= (s_row - t_row);
1593 		if (tem->tvs_first_line <= 0) {
1594 			/* All OBP rows have been cleared. */
1595 			tem->tvs_first_line = 0;
1596 		}
1597 	}
1598 
1599 }
1600 
1601 void
1602 tem_safe_pix_bit2pix(struct tem_vt_state *tem, tem_char_t c,
1603     unsigned char fg, unsigned char bg)
1604 {
1605 	void (*fp)(struct tem_vt_state *, tem_char_t,
1606 	    unsigned char, unsigned char);
1607 
1608 	switch (tems.ts_pdepth) {
1609 	case 4:
1610 		fp = bit_to_pix4;
1611 		break;
1612 	case 8:
1613 		fp = bit_to_pix8;
1614 		break;
1615 	case 15:
1616 	case 16:
1617 		fp = bit_to_pix16;
1618 		break;
1619 	case 24:
1620 		fp = bit_to_pix24;
1621 		break;
1622 	case 32:
1623 		fp = bit_to_pix32;
1624 		break;
1625 	default:
1626 		return;
1627 	}
1628 
1629 	fp(tem, c, fg, bg);
1630 }
1631 
1632 
1633 /*
1634  * This function only clears count of columns in one row
1635  */
1636 void
1637 tem_safe_pix_cls(struct tem_vt_state *tem, int count,
1638     screen_pos_t row, screen_pos_t col, cred_t *credp,
1639     enum called_from called_from)
1640 {
1641 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1642 	    called_from == CALLED_FROM_STANDALONE);
1643 
1644 	tem_safe_pix_cls_range(tem, row, 1, tems.ts_p_offset.y,
1645 	    col, count, tems.ts_p_offset.x, B_FALSE, credp, called_from);
1646 }
1647 
1648 /*
1649  * This function clears OBP output above our kernel console term area
1650  * because OBP's term may have a bigger terminal window than that of
1651  * our kernel console term. So we need to clear OBP output garbage outside
1652  * of our kernel console term at a proper time, which is when the first
1653  * row output of our kernel console term scrolls at the first screen line.
1654  *
1655  *	_________________________________
1656  *	|   _____________________	|  ---> OBP's bigger term window
1657  *	|   |			|	|
1658  *	|___|			|	|
1659  *	| | |			|	|
1660  *	| | |			|	|
1661  *	|_|_|___________________|_______|
1662  *	  | |			|	   ---> first line
1663  *	  | |___________________|---> our kernel console term window
1664  *	  |
1665  *	  |---> columns area to be cleared
1666  *
1667  * This function only takes care of the output above our kernel console term,
1668  * and tem_prom_scroll_up takes care of columns area outside of our kernel
1669  * console term.
1670  */
1671 static void
1672 tem_safe_pix_clear_prom_output(struct tem_vt_state *tem, cred_t *credp,
1673     enum called_from called_from)
1674 {
1675 	int	nrows, ncols, width, height, offset;
1676 
1677 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1678 	    called_from == CALLED_FROM_STANDALONE);
1679 
1680 	width = tems.ts_font.width;
1681 	height = tems.ts_font.height;
1682 	offset = tems.ts_p_offset.y % height;
1683 
1684 	nrows = tems.ts_p_offset.y / height;
1685 	ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
1686 
1687 	if (nrows > 0)
1688 		tem_safe_pix_cls_range(tem, 0, nrows, offset, 0, ncols, 0,
1689 		    B_FALSE, credp, called_from);
1690 }
1691 
1692 /*
1693  * clear the whole screen for pixel mode, just clear the
1694  * physical screen.
1695  */
1696 void
1697 tem_safe_pix_clear_entire_screen(struct tem_vt_state *tem, cred_t *credp,
1698     enum called_from called_from)
1699 {
1700 	struct vis_consclear cl;
1701 	text_color_t fg_color;
1702 	text_color_t bg_color;
1703 	int	nrows, ncols, width, height;
1704 
1705 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1706 	    called_from == CALLED_FROM_STANDALONE);
1707 
1708 	/* call driver first, if error, clear terminal area */
1709 	tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_SCREEN_REVERSE);
1710 	cl.bg_color = bg_color;
1711 	if (tems_cls_layered(&cl, credp) == 0)
1712 		return;
1713 
1714 	width = tems.ts_font.width;
1715 	height = tems.ts_font.height;
1716 
1717 	nrows = (tems.ts_p_dimension.height + (height - 1))/ height;
1718 	ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
1719 
1720 	tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y, 0, ncols,
1721 	    tems.ts_p_offset.x, B_FALSE, credp, called_from);
1722 
1723 	/*
1724 	 * Since the whole screen is cleared, we don't need
1725 	 * to clear OBP output later.
1726 	 */
1727 	if (tem->tvs_first_line > 0)
1728 		tem->tvs_first_line = 0;
1729 }
1730 
1731 /*
1732  * clear the whole screen, including the virtual screen buffer,
1733  * and reset the cursor to start point.
1734  */
1735 static void
1736 tem_safe_cls(struct tem_vt_state *tem,
1737     cred_t *credp, enum called_from called_from)
1738 {
1739 	int	row;
1740 
1741 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1742 	    called_from == CALLED_FROM_STANDALONE);
1743 
1744 	if (tems.ts_display_mode == VIS_TEXT) {
1745 		for (row = 0; row < tems.ts_c_dimension.height; row++) {
1746 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1747 			    row, 0, credp, called_from);
1748 		}
1749 		tem->tvs_c_cursor.row = 0;
1750 		tem->tvs_c_cursor.col = 0;
1751 		tem_safe_align_cursor(tem);
1752 		return;
1753 	}
1754 
1755 	ASSERT(tems.ts_display_mode == VIS_PIXEL);
1756 
1757 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
1758 		tem_safe_virtual_cls(tem, tems.ts_c_dimension.width, row, 0);
1759 	}
1760 	tem->tvs_c_cursor.row = 0;
1761 	tem->tvs_c_cursor.col = 0;
1762 	tem_safe_align_cursor(tem);
1763 
1764 	if (!tem->tvs_isactive)
1765 		return;
1766 
1767 	tem_safe_pix_clear_entire_screen(tem, credp, called_from);
1768 }
1769 
1770 static void
1771 tem_safe_back_tab(struct tem_vt_state *tem,
1772     cred_t *credp, enum called_from called_from)
1773 {
1774 	int	i;
1775 	screen_pos_t	tabstop;
1776 
1777 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1778 	    called_from == CALLED_FROM_STANDALONE);
1779 
1780 	tabstop = 0;
1781 
1782 	for (i = tem->tvs_ntabs - 1; i >= 0; i--) {
1783 		if (tem->tvs_tabs[i] < tem->tvs_c_cursor.col) {
1784 			tabstop = tem->tvs_tabs[i];
1785 			break;
1786 		}
1787 	}
1788 
1789 	tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row,
1790 	    tabstop, credp, called_from);
1791 }
1792 
1793 static void
1794 tem_safe_tab(struct tem_vt_state *tem,
1795     cred_t *credp, enum called_from called_from)
1796 {
1797 	int	i;
1798 	screen_pos_t	tabstop;
1799 
1800 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1801 	    called_from == CALLED_FROM_STANDALONE);
1802 
1803 	tabstop = tems.ts_c_dimension.width - 1;
1804 
1805 	for (i = 0; i < tem->tvs_ntabs; i++) {
1806 		if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
1807 			tabstop = tem->tvs_tabs[i];
1808 			break;
1809 		}
1810 	}
1811 
1812 	tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row,
1813 	    tabstop, credp, called_from);
1814 }
1815 
1816 static void
1817 tem_safe_set_tab(struct tem_vt_state *tem)
1818 {
1819 	int	i;
1820 	int	j;
1821 
1822 	if (tem->tvs_ntabs == TEM_MAXTAB)
1823 		return;
1824 	if (tem->tvs_ntabs == 0 ||
1825 	    tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) {
1826 			tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col;
1827 			return;
1828 	}
1829 	for (i = 0; i < tem->tvs_ntabs; i++) {
1830 		if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col)
1831 			return;
1832 		if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
1833 			for (j = tem->tvs_ntabs - 1; j >= i; j--)
1834 				tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j];
1835 			tem->tvs_tabs[i] = tem->tvs_c_cursor.col;
1836 			tem->tvs_ntabs++;
1837 			return;
1838 		}
1839 	}
1840 }
1841 
1842 static void
1843 tem_safe_clear_tabs(struct tem_vt_state *tem, int action)
1844 {
1845 	int	i;
1846 	int	j;
1847 
1848 	switch (action) {
1849 	case 3: /* clear all tabs */
1850 		tem->tvs_ntabs = 0;
1851 		break;
1852 	case 0: /* clr tab at cursor */
1853 
1854 		for (i = 0; i < tem->tvs_ntabs; i++) {
1855 			if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) {
1856 				tem->tvs_ntabs--;
1857 				for (j = i; j < tem->tvs_ntabs; j++)
1858 					tem->tvs_tabs[j] = tem->tvs_tabs[j + 1];
1859 				return;
1860 			}
1861 		}
1862 		break;
1863 	}
1864 }
1865 
1866 static void
1867 tem_safe_mv_cursor(struct tem_vt_state *tem, int row, int col,
1868     cred_t *credp, enum called_from called_from)
1869 {
1870 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1871 	    called_from == CALLED_FROM_STANDALONE);
1872 
1873 	/*
1874 	 * Sanity check and bounds enforcement.  Out of bounds requests are
1875 	 * clipped to the screen boundaries.  This seems to be what SPARC
1876 	 * does.
1877 	 */
1878 	if (row < 0)
1879 		row = 0;
1880 	if (row >= tems.ts_c_dimension.height)
1881 		row = tems.ts_c_dimension.height - 1;
1882 	if (col < 0)
1883 		col = 0;
1884 	if (col >= tems.ts_c_dimension.width)
1885 		col = tems.ts_c_dimension.width - 1;
1886 
1887 	tem_safe_send_data(tem, credp, called_from);
1888 	tem->tvs_c_cursor.row = (screen_pos_t)row;
1889 	tem->tvs_c_cursor.col = (screen_pos_t)col;
1890 	tem_safe_align_cursor(tem);
1891 }
1892 
1893 /* ARGSUSED */
1894 void
1895 tem_safe_reset_emulator(struct tem_vt_state *tem,
1896     cred_t *credp, enum called_from called_from,
1897     boolean_t init_color)
1898 {
1899 	int j;
1900 
1901 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1902 	    called_from == CALLED_FROM_STANDALONE);
1903 
1904 	tem->tvs_c_cursor.row = 0;
1905 	tem->tvs_c_cursor.col = 0;
1906 	tem->tvs_r_cursor.row = 0;
1907 	tem->tvs_r_cursor.col = 0;
1908 	tem->tvs_s_cursor.row = 0;
1909 	tem->tvs_s_cursor.col = 0;
1910 	tem->tvs_outindex = 0;
1911 	tem->tvs_state = A_STATE_START;
1912 	tem->tvs_gotparam = B_FALSE;
1913 	tem->tvs_curparam = 0;
1914 	tem->tvs_paramval = 0;
1915 	tem->tvs_nscroll = 1;
1916 
1917 	if (init_color) {
1918 		/* use initial settings */
1919 		tem->tvs_fg_color = tems.ts_init_color.fg_color;
1920 		tem->tvs_bg_color = tems.ts_init_color.bg_color;
1921 		tem->tvs_flags = tems.ts_init_color.a_flags;
1922 	}
1923 
1924 	/*
1925 	 * set up the initial tab stops
1926 	 */
1927 	tem->tvs_ntabs = 0;
1928 	for (j = 8; j < tems.ts_c_dimension.width; j += 8)
1929 		tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j;
1930 
1931 	for (j = 0; j < TEM_MAXPARAMS; j++)
1932 		tem->tvs_params[j] = 0;
1933 }
1934 
1935 void
1936 tem_safe_reset_display(struct tem_vt_state *tem,
1937     cred_t *credp, enum called_from called_from,
1938     boolean_t clear_txt, boolean_t init_color)
1939 {
1940 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1941 	    called_from == CALLED_FROM_STANDALONE);
1942 
1943 	tem_safe_reset_emulator(tem, credp, called_from, init_color);
1944 
1945 	if (clear_txt) {
1946 		if (tem->tvs_isactive)
1947 			tem_safe_callback_cursor(tem,
1948 			    VIS_HIDE_CURSOR, credp, called_from);
1949 
1950 		tem_safe_cls(tem, credp, called_from);
1951 
1952 		if (tem->tvs_isactive)
1953 			tem_safe_callback_cursor(tem,
1954 			    VIS_DISPLAY_CURSOR, credp, called_from);
1955 	}
1956 }
1957 
1958 static void
1959 tem_safe_shift(
1960 	struct tem_vt_state *tem,
1961 	int count,
1962 	int direction,
1963 	cred_t *credp,
1964 	enum called_from called_from)
1965 {
1966 	int rest_of_line;
1967 
1968 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1969 	    called_from == CALLED_FROM_STANDALONE);
1970 
1971 	rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col;
1972 	if (count > rest_of_line)
1973 		count = rest_of_line;
1974 
1975 	if (count <= 0)
1976 		return;
1977 
1978 	switch (direction) {
1979 	case TEM_SHIFT_LEFT:
1980 		if (count < rest_of_line) {
1981 			tem_safe_copy_area(tem,
1982 			    tem->tvs_c_cursor.col + count,
1983 			    tem->tvs_c_cursor.row,
1984 			    tems.ts_c_dimension.width - 1,
1985 			    tem->tvs_c_cursor.row,
1986 			    tem->tvs_c_cursor.col,
1987 			    tem->tvs_c_cursor.row,
1988 			    credp, called_from);
1989 		}
1990 
1991 		tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
1992 		    (tems.ts_c_dimension.width - count), credp,
1993 		    called_from);
1994 		break;
1995 	case TEM_SHIFT_RIGHT:
1996 		if (count < rest_of_line) {
1997 			tem_safe_copy_area(tem,
1998 			    tem->tvs_c_cursor.col,
1999 			    tem->tvs_c_cursor.row,
2000 			    tems.ts_c_dimension.width - count - 1,
2001 			    tem->tvs_c_cursor.row,
2002 			    tem->tvs_c_cursor.col + count,
2003 			    tem->tvs_c_cursor.row,
2004 			    credp, called_from);
2005 		}
2006 
2007 		tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
2008 		    tem->tvs_c_cursor.col, credp, called_from);
2009 		break;
2010 	}
2011 }
2012 
2013 void
2014 tem_safe_text_cursor(struct tem_vt_state *tem, short action,
2015     cred_t *credp, enum called_from called_from)
2016 {
2017 	struct vis_conscursor	ca;
2018 
2019 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2020 	    called_from == CALLED_FROM_STANDALONE);
2021 
2022 	ca.row = tem->tvs_c_cursor.row;
2023 	ca.col = tem->tvs_c_cursor.col;
2024 	ca.action = action;
2025 
2026 	tems_safe_cursor(&ca, credp, called_from);
2027 
2028 	if (action == VIS_GET_CURSOR) {
2029 		tem->tvs_c_cursor.row = ca.row;
2030 		tem->tvs_c_cursor.col = ca.col;
2031 	}
2032 }
2033 
2034 void
2035 tem_safe_pix_cursor(struct tem_vt_state *tem, short action,
2036     cred_t *credp, enum called_from called_from)
2037 {
2038 	struct vis_conscursor	ca;
2039 	uint32_t color;
2040 	text_color_t fg, bg;
2041 
2042 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2043 	    called_from == CALLED_FROM_STANDALONE);
2044 
2045 	ca.row = tem->tvs_c_cursor.row * tems.ts_font.height +
2046 	    tems.ts_p_offset.y;
2047 	ca.col = tem->tvs_c_cursor.col * tems.ts_font.width +
2048 	    tems.ts_p_offset.x;
2049 	ca.width = tems.ts_font.width;
2050 	ca.height = tems.ts_font.height;
2051 
2052 	tem_safe_get_color(tem, &fg, &bg, TEM_ATTR_REVERSE);
2053 
2054 	switch (tems.ts_pdepth) {
2055 	case 4:
2056 	case 8:
2057 		ca.fg_color.mono = fg;
2058 		ca.bg_color.mono = bg;
2059 		break;
2060 	case 15:
2061 	case 16:
2062 		color = tems.ts_color_map(fg);
2063 		ca.fg_color.sixteen[0] = (color >> 8) & 0xFF;
2064 		ca.fg_color.sixteen[1] = color & 0xFF;
2065 		color = tems.ts_color_map(bg);
2066 		ca.bg_color.sixteen[0] = (color >> 8) & 0xFF;
2067 		ca.bg_color.sixteen[1] = color & 0xFF;
2068 		break;
2069 	case 24:
2070 	case 32:
2071 #ifdef _HAVE_TEM_FIRMWARE
2072 		/* Keeping this block to support old binary only drivers */
2073 		if (tem->tvs_flags & TEM_ATTR_REVERSE) {
2074 			ca.fg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED;
2075 			ca.fg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN;
2076 			ca.fg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE;
2077 
2078 			ca.bg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED;
2079 			ca.bg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN;
2080 			ca.bg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE;
2081 		} else {
2082 			ca.fg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED;
2083 			ca.fg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN;
2084 			ca.fg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE;
2085 
2086 			ca.bg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED;
2087 			ca.bg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN;
2088 			ca.bg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE;
2089 		}
2090 #else
2091 		color = tems.ts_color_map(fg);
2092 		ca.fg_color.twentyfour[0] = (color >> 16) & 0xFF;
2093 		ca.fg_color.twentyfour[1] = (color >> 8) & 0xFF;
2094 		ca.fg_color.twentyfour[2] = color & 0xFF;
2095 		color = tems.ts_color_map(bg);
2096 		ca.bg_color.twentyfour[0] = (color >> 16) & 0xFF;
2097 		ca.bg_color.twentyfour[1] = (color >> 8) & 0xFF;
2098 		ca.bg_color.twentyfour[2] = color & 0xFF;
2099 		break;
2100 #endif
2101 	}
2102 
2103 	ca.action = action;
2104 
2105 	tems_safe_cursor(&ca, credp, called_from);
2106 
2107 	if (action == VIS_GET_CURSOR) {
2108 		tem->tvs_c_cursor.row = (ca.row - tems.ts_p_offset.y) /
2109 		    tems.ts_font.height;
2110 		tem->tvs_c_cursor.col = (ca.col - tems.ts_p_offset.x) /
2111 		    tems.ts_font.width;
2112 	}
2113 }
2114 
2115 static void
2116 bit_to_pix4(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color,
2117     text_color_t bg_color)
2118 {
2119 	uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2120 	font_bit_to_pix4(&tems.ts_font, dest, c, fg_color, bg_color);
2121 }
2122 
2123 static void
2124 bit_to_pix8(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color,
2125     text_color_t bg_color)
2126 {
2127 	uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2128 	font_bit_to_pix8(&tems.ts_font, dest, c, fg_color, bg_color);
2129 }
2130 
2131 static void
2132 bit_to_pix16(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2133     text_color_t bg_color4)
2134 {
2135 	uint16_t fg_color16, bg_color16;
2136 	uint16_t *dest;
2137 
2138 	ASSERT(fg_color4 < 16 && bg_color4 < 16);
2139 
2140 	fg_color16 = (uint16_t)tems.ts_color_map(fg_color4);
2141 	bg_color16 = (uint16_t)tems.ts_color_map(bg_color4);
2142 
2143 	dest = (uint16_t *)tem->tvs_pix_data;
2144 	font_bit_to_pix16(&tems.ts_font, dest, c, fg_color16, bg_color16);
2145 }
2146 
2147 static void
2148 bit_to_pix24(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2149     text_color_t bg_color4)
2150 {
2151 	uint32_t fg_color32, bg_color32;
2152 	uint8_t *dest;
2153 
2154 	ASSERT(fg_color4 < 16 && bg_color4 < 16);
2155 
2156 #ifdef _HAVE_TEM_FIRMWARE
2157 	fg_color32 = PIX4TO32(fg_color4);
2158 	bg_color32 = PIX4TO32(bg_color4);
2159 #else
2160 	fg_color32 = tems.ts_color_map(fg_color4);
2161 	bg_color32 = tems.ts_color_map(bg_color4);
2162 #endif
2163 
2164 	dest = (uint8_t *)tem->tvs_pix_data;
2165 	font_bit_to_pix24(&tems.ts_font, dest, c, fg_color32, bg_color32);
2166 }
2167 
2168 static void
2169 bit_to_pix32(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2170     text_color_t bg_color4)
2171 {
2172 	uint32_t fg_color32, bg_color32, *dest;
2173 
2174 	ASSERT(fg_color4 < 16 && bg_color4 < 16);
2175 
2176 #ifdef _HAVE_TEM_FIRMWARE
2177 	fg_color32 = PIX4TO32(fg_color4);
2178 	bg_color32 = PIX4TO32(bg_color4);
2179 #else
2180 	fg_color32 = ((uint32_t)0xFF << 24) | tems.ts_color_map(fg_color4);
2181 	bg_color32 = ((uint32_t)0xFF << 24) | tems.ts_color_map(bg_color4);
2182 #endif
2183 
2184 	dest = (uint32_t *)tem->tvs_pix_data;
2185 	font_bit_to_pix32(&tems.ts_font, dest, c, fg_color32, bg_color32);
2186 }
2187 
2188 static text_color_t
2189 ansi_bg_to_solaris(struct tem_vt_state *tem, int ansi)
2190 {
2191 	if (tem->tvs_flags & TEM_ATTR_BRIGHT_BG)
2192 		return (brt_xlate[ansi]);
2193 	else
2194 		return (dim_xlate[ansi]);
2195 }
2196 
2197 static text_color_t
2198 ansi_fg_to_solaris(struct tem_vt_state *tem, int ansi)
2199 {
2200 	if (tem->tvs_flags & TEM_ATTR_BRIGHT_FG ||
2201 	    tem->tvs_flags & TEM_ATTR_BOLD) {
2202 		return (brt_xlate[ansi]);
2203 	} else {
2204 		return (dim_xlate[ansi]);
2205 	}
2206 }
2207 
2208 /*
2209  * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE
2210  */
2211 void
2212 tem_safe_get_color(struct tem_vt_state *tem, text_color_t *fg,
2213     text_color_t *bg, uint8_t flag)
2214 {
2215 	if (tem->tvs_flags & flag) {
2216 		*fg = ansi_fg_to_solaris(tem,
2217 		    tem->tvs_bg_color);
2218 		*bg = ansi_bg_to_solaris(tem,
2219 		    tem->tvs_fg_color);
2220 	} else {
2221 		*fg = ansi_fg_to_solaris(tem,
2222 		    tem->tvs_fg_color);
2223 		*bg = ansi_bg_to_solaris(tem,
2224 		    tem->tvs_bg_color);
2225 	}
2226 }
2227 
2228 /*
2229  * Clear a rectangle of screen for pixel mode.
2230  *
2231  * arguments:
2232  *    row:	start row#
2233  *    nrows:	the number of rows to clear
2234  *    offset_y:	the offset of height in pixels to begin clear
2235  *    col:	start col#
2236  *    ncols:	the number of cols to clear
2237  *    offset_x:	the offset of width in pixels to begin clear
2238  *    scroll_up: whether this function is called during sroll up,
2239  *		 which is called only once.
2240  */
2241 void
2242 tem_safe_pix_cls_range(struct tem_vt_state *tem,
2243     screen_pos_t row, int nrows, int offset_y,
2244     screen_pos_t col, int ncols, int offset_x,
2245     boolean_t sroll_up, cred_t *credp,
2246     enum called_from called_from)
2247 {
2248 	struct vis_consdisplay da;
2249 	int	i, j;
2250 	int	row_add = 0;
2251 	text_color_t fg_color;
2252 	text_color_t bg_color;
2253 
2254 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2255 	    called_from == CALLED_FROM_STANDALONE);
2256 
2257 	if (sroll_up)
2258 		row_add = tems.ts_c_dimension.height - 1;
2259 
2260 	da.width = tems.ts_font.width;
2261 	da.height = tems.ts_font.height;
2262 
2263 	tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_SCREEN_REVERSE);
2264 
2265 	tem_safe_callback_bit2pix(tem, ' ', fg_color, bg_color);
2266 	da.data = (uchar_t *)tem->tvs_pix_data;
2267 
2268 	for (i = 0; i < nrows; i++, row++) {
2269 		da.row = (row + row_add) * da.height + offset_y;
2270 		da.col = col * da.width + offset_x;
2271 		for (j = 0; j < ncols; j++) {
2272 			tems_safe_display(&da, credp, called_from);
2273 			da.col += da.width;
2274 		}
2275 	}
2276 }
2277 
2278 /*
2279  * virtual screen operations
2280  */
2281 static void
2282 tem_safe_virtual_display(struct tem_vt_state *tem, tem_char_t *string,
2283     int count, screen_pos_t row, screen_pos_t col,
2284     text_color_t fg_color, text_color_t bg_color)
2285 {
2286 	int i, width;
2287 	tem_char_t *addr;
2288 	text_color_t *pfgcolor;
2289 	text_color_t *pbgcolor;
2290 
2291 	if (row < 0 || row >= tems.ts_c_dimension.height ||
2292 	    col < 0 || col >= tems.ts_c_dimension.width ||
2293 	    col + count > tems.ts_c_dimension.width)
2294 		return;
2295 
2296 	width = tems.ts_c_dimension.width;
2297 	addr = tem->tvs_screen_buf +  (row * width + col);
2298 	pfgcolor = tem->tvs_fg_buf + (row * width + col);
2299 	pbgcolor = tem->tvs_bg_buf + (row * width + col);
2300 	for (i = 0; i < count; i++) {
2301 		*addr++ = string[i];
2302 		*pfgcolor++ = fg_color;
2303 		*pbgcolor++ = bg_color;
2304 	}
2305 }
2306 
2307 static void
2308 i_virtual_copy_tem_chars(tem_char_t *base,
2309     screen_pos_t s_col, screen_pos_t s_row,
2310     screen_pos_t e_col, screen_pos_t e_row,
2311     screen_pos_t t_col, screen_pos_t t_row)
2312 {
2313 	tem_char_t	*from;
2314 	tem_char_t	*to;
2315 	int		cnt;
2316 	screen_size_t chars_per_row;
2317 	tem_char_t	*to_row_start;
2318 	tem_char_t	*from_row_start;
2319 	screen_size_t   rows_to_move;
2320 	int		cols = tems.ts_c_dimension.width;
2321 
2322 	chars_per_row = e_col - s_col + 1;
2323 	rows_to_move = e_row - s_row + 1;
2324 
2325 	to_row_start = base + ((t_row * cols) + t_col);
2326 	from_row_start = base + ((s_row * cols) + s_col);
2327 
2328 	if (to_row_start < from_row_start) {
2329 		while (rows_to_move-- > 0) {
2330 			to = to_row_start;
2331 			from = from_row_start;
2332 			to_row_start += cols;
2333 			from_row_start += cols;
2334 			for (cnt = chars_per_row; cnt-- > 0; )
2335 				*to++ = *from++;
2336 		}
2337 	} else {
2338 		/*
2339 		 * Offset to the end of the region and copy backwards.
2340 		 */
2341 		cnt = rows_to_move * cols + chars_per_row;
2342 		to_row_start += cnt;
2343 		from_row_start += cnt;
2344 
2345 		while (rows_to_move-- > 0) {
2346 			to_row_start -= cols;
2347 			from_row_start -= cols;
2348 			to = to_row_start;
2349 			from = from_row_start;
2350 			for (cnt = chars_per_row; cnt-- > 0; )
2351 				*--to = *--from;
2352 		}
2353 	}
2354 }
2355 
2356 static void
2357 i_virtual_copy_colors(text_color_t *base,
2358     screen_pos_t s_col, screen_pos_t s_row,
2359     screen_pos_t e_col, screen_pos_t e_row,
2360     screen_pos_t t_col, screen_pos_t t_row)
2361 {
2362 	text_color_t	*from;
2363 	text_color_t	*to;
2364 	int		cnt;
2365 	screen_size_t chars_per_row;
2366 	text_color_t	*to_row_start;
2367 	text_color_t	*from_row_start;
2368 	screen_size_t   rows_to_move;
2369 	int		cols = tems.ts_c_dimension.width;
2370 
2371 	chars_per_row = e_col - s_col + 1;
2372 	rows_to_move = e_row - s_row + 1;
2373 
2374 	to_row_start = base + ((t_row * cols) + t_col);
2375 	from_row_start = base + ((s_row * cols) + s_col);
2376 
2377 	if (to_row_start < from_row_start) {
2378 		while (rows_to_move-- > 0) {
2379 			to = to_row_start;
2380 			from = from_row_start;
2381 			to_row_start += cols;
2382 			from_row_start += cols;
2383 			for (cnt = chars_per_row; cnt-- > 0; )
2384 				*to++ = *from++;
2385 		}
2386 	} else {
2387 		/*
2388 		 * Offset to the end of the region and copy backwards.
2389 		 */
2390 		cnt = rows_to_move * cols + chars_per_row;
2391 		to_row_start += cnt;
2392 		from_row_start += cnt;
2393 
2394 		while (rows_to_move-- > 0) {
2395 			to_row_start -= cols;
2396 			from_row_start -= cols;
2397 			to = to_row_start;
2398 			from = from_row_start;
2399 			for (cnt = chars_per_row; cnt-- > 0; )
2400 				*--to = *--from;
2401 		}
2402 	}
2403 }
2404 
2405 static void
2406 tem_safe_virtual_copy(struct tem_vt_state *tem,
2407     screen_pos_t s_col, screen_pos_t s_row,
2408     screen_pos_t e_col, screen_pos_t e_row,
2409     screen_pos_t t_col, screen_pos_t t_row)
2410 {
2411 	screen_size_t chars_per_row;
2412 	screen_size_t   rows_to_move;
2413 	int		rows = tems.ts_c_dimension.height;
2414 	int		cols = tems.ts_c_dimension.width;
2415 
2416 	if (s_col < 0 || s_col >= cols ||
2417 	    s_row < 0 || s_row >= rows ||
2418 	    e_col < 0 || e_col >= cols ||
2419 	    e_row < 0 || e_row >= rows ||
2420 	    t_col < 0 || t_col >= cols ||
2421 	    t_row < 0 || t_row >= rows ||
2422 	    s_col > e_col ||
2423 	    s_row > e_row)
2424 		return;
2425 
2426 	chars_per_row = e_col - s_col + 1;
2427 	rows_to_move = e_row - s_row + 1;
2428 
2429 	/* More sanity checks. */
2430 	if (t_row + rows_to_move > rows ||
2431 	    t_col + chars_per_row > cols)
2432 		return;
2433 
2434 	i_virtual_copy_tem_chars(tem->tvs_screen_buf, s_col, s_row,
2435 	    e_col, e_row, t_col, t_row);
2436 
2437 	i_virtual_copy_colors(tem->tvs_fg_buf,
2438 	    s_col, s_row, e_col, e_row, t_col, t_row);
2439 	i_virtual_copy_colors(tem->tvs_bg_buf,
2440 	    s_col, s_row, e_col, e_row, t_col, t_row);
2441 }
2442 
2443 static void
2444 tem_safe_virtual_cls(struct tem_vt_state *tem,
2445     int count, screen_pos_t row, screen_pos_t col)
2446 {
2447 	text_color_t fg_color;
2448 	text_color_t bg_color;
2449 
2450 	tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_SCREEN_REVERSE);
2451 	tem_safe_virtual_display(tem, tems.ts_blank_line, count, row, col,
2452 	    fg_color, bg_color);
2453 }
2454 
2455 /*
2456  * only blank screen, not clear our screen buffer
2457  */
2458 void
2459 tem_safe_blank_screen(struct tem_vt_state *tem, cred_t *credp,
2460     enum called_from called_from)
2461 {
2462 	int	row;
2463 
2464 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2465 	    called_from == CALLED_FROM_STANDALONE);
2466 
2467 	if (tems.ts_display_mode == VIS_PIXEL) {
2468 		tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2469 		return;
2470 	}
2471 
2472 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2473 		tem_safe_callback_cls(tem,
2474 		    tems.ts_c_dimension.width,
2475 		    row, 0, credp, called_from);
2476 	}
2477 }
2478 
2479 /*
2480  * unblank screen with associated tem from its screen buffer
2481  */
2482 void
2483 tem_safe_unblank_screen(struct tem_vt_state *tem, cred_t *credp,
2484     enum called_from called_from)
2485 {
2486 	text_color_t fg_color, fg_last;
2487 	text_color_t bg_color, bg_last;
2488 	size_t	tc_size = sizeof (text_color_t);
2489 	int	row, col, count, col_start;
2490 	int	width;
2491 	tem_char_t *buf;
2492 
2493 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2494 	    called_from == CALLED_FROM_STANDALONE);
2495 
2496 	if (tems.ts_display_mode == VIS_PIXEL)
2497 		tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2498 
2499 	tem_safe_callback_cursor(tem, VIS_HIDE_CURSOR, credp, called_from);
2500 
2501 	width = tems.ts_c_dimension.width;
2502 
2503 	/*
2504 	 * Display data in tvs_screen_buf to the actual framebuffer in a
2505 	 * row by row way.
2506 	 * When dealing with one row, output data with the same foreground
2507 	 * and background color all together.
2508 	 */
2509 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2510 		buf = tem->tvs_screen_buf + (row * width);
2511 		count = col_start = 0;
2512 		for (col = 0; col < width; col++) {
2513 			fg_color =
2514 			    tem->tvs_fg_buf[(row * width + col) * tc_size];
2515 			bg_color =
2516 			    tem->tvs_bg_buf[(row * width + col) * tc_size];
2517 			if (col == 0) {
2518 				fg_last = fg_color;
2519 				bg_last = bg_color;
2520 			}
2521 
2522 			if ((fg_color != fg_last) || (bg_color != bg_last)) {
2523 				/*
2524 				 * Call the primitive to render this data.
2525 				 */
2526 				tem_safe_callback_display(tem,
2527 				    buf, count, row, col_start,
2528 				    fg_last, bg_last, credp, called_from);
2529 				buf += count;
2530 				count = 1;
2531 				col_start = col;
2532 				fg_last = fg_color;
2533 				bg_last = bg_color;
2534 			} else {
2535 				count++;
2536 			}
2537 		}
2538 
2539 		if (col_start == (width - 1))
2540 			continue;
2541 
2542 		/*
2543 		 * Call the primitive to render this data.
2544 		 */
2545 		tem_safe_callback_display(tem,
2546 		    buf, count, row, col_start,
2547 		    fg_last, bg_last, credp, called_from);
2548 	}
2549 
2550 	tem_safe_callback_cursor(tem, VIS_DISPLAY_CURSOR, credp, called_from);
2551 }
2552