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