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