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