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