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