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