xref: /illumos-gate/usr/src/uts/common/io/tem_safe.c (revision dd72704bd9e794056c558153663c739e2012d721)
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 25: /* show cursor */
1181 			/*
1182 			 * Note that cursor is not displayed either way
1183 			 * at this entry point.  Clearing the flag ensures
1184 			 * that on exit from tem_safe_terminal_emulate
1185 			 * we will display the cursor.
1186 			 */
1187 			tem_safe_send_data(tem, credp, called_from);
1188 			tem->tvs_cursor_hidden = B_FALSE;
1189 			break;
1190 		}
1191 		break;
1192 
1193 	case 'l':
1194 		/* DEC private mode reset */
1195 		tem_safe_setparam(tem, 1, 1);
1196 		switch (tem->tvs_params[0]) {
1197 		case 25: /* hide cursor */
1198 			/*
1199 			 * Note that the cursor is not displayed already.
1200 			 * This is true regardless of the flag state.
1201 			 * Setting this flag ensures we won't display it
1202 			 * on exit from tem_safe_terminal_emulate.
1203 			 */
1204 			tem_safe_send_data(tem, credp, called_from);
1205 			tem->tvs_cursor_hidden = B_TRUE;
1206 			break;
1207 		}
1208 		break;
1209 	}
1210 	tem->tvs_state = A_STATE_START;
1211 }
1212 
1213 /*
1214  * Gather the parameters of an ANSI escape sequence
1215  */
1216 static void
1217 tem_safe_getparams(struct tem_vt_state *tem, tem_char_t ch,
1218     cred_t *credp, enum called_from called_from)
1219 {
1220 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1221 	    MUTEX_HELD(&tem->tvs_lock));
1222 
1223 	if (ch >= '0' && ch <= '9') {
1224 		tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0'));
1225 		tem->tvs_gotparam = B_TRUE;  /* Remember got parameter */
1226 		return; /* Return immediately */
1227 	} else if (tem->tvs_state == A_STATE_CSI_EQUAL) {
1228 		tem->tvs_state = A_STATE_START;
1229 	} else if (tem->tvs_state == A_STATE_CSI_QMARK) {
1230 		if (tem->tvs_curparam < TEM_MAXPARAMS) {
1231 			if (tem->tvs_gotparam) {
1232 				/* get the parameter value */
1233 				tem->tvs_params[tem->tvs_curparam] =
1234 				    tem->tvs_paramval;
1235 			}
1236 			tem->tvs_curparam++;
1237 		}
1238 		if (ch == ';') {
1239 			/* Restart parameter search */
1240 			tem->tvs_gotparam = B_FALSE;
1241 			tem->tvs_paramval = 0; /* No parameter value yet */
1242 		} else {
1243 			/* Handle escape sequence */
1244 			tem_safe_chkparam_qmark(tem, ch, credp, called_from);
1245 		}
1246 	} else {
1247 		if (tem->tvs_curparam < TEM_MAXPARAMS) {
1248 			if (tem->tvs_gotparam) {
1249 				/* get the parameter value */
1250 				tem->tvs_params[tem->tvs_curparam] =
1251 				    tem->tvs_paramval;
1252 			}
1253 			tem->tvs_curparam++;
1254 		}
1255 
1256 		if (ch == ';') {
1257 			/* Restart parameter search */
1258 			tem->tvs_gotparam = B_FALSE;
1259 			tem->tvs_paramval = 0; /* No parameter value yet */
1260 		} else {
1261 			/* Handle escape sequence */
1262 			tem_safe_chkparam(tem, ch, credp, called_from);
1263 		}
1264 	}
1265 }
1266 
1267 /*
1268  * Add character to internal buffer.
1269  * When its full, send it to the next layer.
1270  */
1271 
1272 static void
1273 tem_safe_outch(struct tem_vt_state *tem, tem_char_t ch,
1274     cred_t *credp, enum called_from called_from)
1275 {
1276 	text_color_t fg;
1277 	text_color_t bg;
1278 	text_attr_t attr;
1279 
1280 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1281 	    called_from == CALLED_FROM_STANDALONE);
1282 
1283 	/* buffer up the character until later */
1284 	tem_safe_get_attr(tem, &fg, &bg, &attr, TEM_ATTR_REVERSE);
1285 	tem->tvs_outbuf[tem->tvs_outindex].tc_char = ch | TEM_ATTR(attr);
1286 	tem->tvs_outbuf[tem->tvs_outindex].tc_fg_color = fg;
1287 	tem->tvs_outbuf[tem->tvs_outindex].tc_bg_color = bg;
1288 	tem->tvs_outindex++;
1289 	tem->tvs_c_cursor.col++;
1290 	if (tem->tvs_c_cursor.col >= tems.ts_c_dimension.width) {
1291 		tem_safe_send_data(tem, credp, called_from);
1292 		tem_safe_new_line(tem, credp, called_from);
1293 	}
1294 }
1295 
1296 static void
1297 tem_safe_new_line(struct tem_vt_state *tem,
1298     cred_t *credp, enum called_from called_from)
1299 {
1300 	tem_safe_cr(tem);
1301 	tem_safe_lf(tem, credp, called_from);
1302 }
1303 
1304 static void
1305 tem_safe_cr(struct tem_vt_state *tem)
1306 {
1307 	tem->tvs_c_cursor.col = 0;
1308 	tem_safe_align_cursor(tem);
1309 }
1310 
1311 static void
1312 tem_safe_lf(struct tem_vt_state *tem,
1313     cred_t *credp, enum called_from called_from)
1314 {
1315 	int row;
1316 
1317 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1318 	    MUTEX_HELD(&tem->tvs_lock));
1319 
1320 	/*
1321 	 * Sanity checking notes:
1322 	 * . a_nscroll was validated when it was set.
1323 	 * . Regardless of that, tem_safe_scroll and tem_safe_mv_cursor
1324 	 *   will prevent anything bad from happening.
1325 	 */
1326 	row = tem->tvs_c_cursor.row + 1;
1327 
1328 	if (row >= tems.ts_c_dimension.height) {
1329 		if (tem->tvs_nscroll != 0) {
1330 			tem_safe_scroll(tem, 0,
1331 			    tems.ts_c_dimension.height - 1,
1332 			    tem->tvs_nscroll, TEM_SCROLL_UP,
1333 			    credp, called_from);
1334 			row = tems.ts_c_dimension.height -
1335 			    tem->tvs_nscroll;
1336 		} else {	/* no scroll */
1337 			/*
1338 			 * implement Esc[#r when # is zero.  This means no
1339 			 * scroll but just return cursor to top of screen,
1340 			 * do not clear screen.
1341 			 */
1342 			row = 0;
1343 		}
1344 	}
1345 
1346 	tem_safe_mv_cursor(tem, row, tem->tvs_c_cursor.col,
1347 	    credp, called_from);
1348 
1349 	if (tem->tvs_nscroll == 0) {
1350 		/* erase rest of cursor line */
1351 		tem_safe_clear_chars(tem,
1352 		    tems.ts_c_dimension.width -
1353 		    tem->tvs_c_cursor.col,
1354 		    tem->tvs_c_cursor.row,
1355 		    tem->tvs_c_cursor.col,
1356 		    credp, called_from);
1357 
1358 	}
1359 
1360 	tem_safe_align_cursor(tem);
1361 }
1362 
1363 static void
1364 tem_safe_send_data(struct tem_vt_state *tem, cred_t *credp,
1365     enum called_from called_from)
1366 {
1367 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1368 	    MUTEX_HELD(&tem->tvs_lock));
1369 
1370 	if (tem->tvs_outindex == 0) {
1371 		tem_safe_align_cursor(tem);
1372 		return;
1373 	}
1374 
1375 	tem_safe_virtual_display(tem,
1376 	    tem->tvs_outbuf, tem->tvs_outindex,
1377 	    tem->tvs_s_cursor.row, tem->tvs_s_cursor.col);
1378 
1379 	if (tem->tvs_isactive) {
1380 		/*
1381 		 * Call the primitive to render this data.
1382 		 */
1383 		tem_safe_callback_display(tem,
1384 		    tem->tvs_outbuf, tem->tvs_outindex,
1385 		    tem->tvs_s_cursor.row, tem->tvs_s_cursor.col,
1386 		    credp, called_from);
1387 	}
1388 
1389 	tem->tvs_outindex = 0;
1390 
1391 	tem_safe_align_cursor(tem);
1392 }
1393 
1394 
1395 /*
1396  * We have just done something to the current output point.  Reset the start
1397  * point for the buffered data in a_outbuf.  There shouldn't be any data
1398  * buffered yet.
1399  */
1400 static void
1401 tem_safe_align_cursor(struct tem_vt_state *tem)
1402 {
1403 	tem->tvs_s_cursor.row = tem->tvs_c_cursor.row;
1404 	tem->tvs_s_cursor.col = tem->tvs_c_cursor.col;
1405 }
1406 
1407 /*
1408  * State machine parser based on the current state and character input
1409  * major terminations are to control character or normal character
1410  */
1411 
1412 static void
1413 tem_safe_parse(struct tem_vt_state *tem, tem_char_t ch,
1414     cred_t *credp, enum called_from called_from)
1415 {
1416 	int	i;
1417 
1418 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1419 	    MUTEX_HELD(&tem->tvs_lock));
1420 
1421 	if (tem->tvs_state == A_STATE_START) {	/* Normal state? */
1422 		if (ch == A_CSI || ch == A_ESC || ch < ' ') {
1423 			/* Control */
1424 			tem_safe_control(tem, ch, credp, called_from);
1425 		} else {
1426 			/* Display */
1427 			tem_safe_outch(tem, ch, credp, called_from);
1428 		}
1429 		return;
1430 	}
1431 
1432 	/* In <ESC> sequence */
1433 	if (tem->tvs_state != A_STATE_ESC) {	/* Need to get parameters? */
1434 		if (tem->tvs_state != A_STATE_CSI) {
1435 			tem_safe_getparams(tem, ch, credp, called_from);
1436 			return;
1437 		}
1438 
1439 		switch (ch) {
1440 		case '?':
1441 			tem->tvs_state = A_STATE_CSI_QMARK;
1442 			return;
1443 		case '=':
1444 			tem->tvs_state = A_STATE_CSI_EQUAL;
1445 			return;
1446 		case 's':
1447 			/*
1448 			 * As defined below, this sequence
1449 			 * saves the cursor.  However, Sun
1450 			 * defines ESC[s as reset.  We resolved
1451 			 * the conflict by selecting reset as it
1452 			 * is exported in the termcap file for
1453 			 * sun-mon, while the "save cursor"
1454 			 * definition does not exist anywhere in
1455 			 * /etc/termcap.
1456 			 * However, having no coherent
1457 			 * definition of reset, we have not
1458 			 * implemented it.
1459 			 */
1460 
1461 			/*
1462 			 * Original code
1463 			 * tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1464 			 * tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1465 			 * tem->tvs_state = A_STATE_START;
1466 			 */
1467 
1468 			tem->tvs_state = A_STATE_START;
1469 			return;
1470 		case 'u':
1471 			tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row,
1472 			    tem->tvs_r_cursor.col, credp, called_from);
1473 			tem->tvs_state = A_STATE_START;
1474 			return;
1475 		case 'p':	/* sunbow */
1476 			tem_safe_send_data(tem, credp, called_from);
1477 			/*
1478 			 * Don't set anything if we are
1479 			 * already as we want to be.
1480 			 */
1481 			if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
1482 				tem->tvs_flags &= ~TEM_ATTR_SCREEN_REVERSE;
1483 				/*
1484 				 * If we have switched the characters to be the
1485 				 * inverse from the screen, then switch them as
1486 				 * well to keep them the inverse of the screen.
1487 				 */
1488 				if (tem->tvs_flags & TEM_ATTR_REVERSE)
1489 					tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1490 				else
1491 					tem->tvs_flags |= TEM_ATTR_REVERSE;
1492 			}
1493 			tem_safe_cls(tem, credp, called_from);
1494 			tem->tvs_state = A_STATE_START;
1495 			return;
1496 		case 'q':	/* sunwob */
1497 			tem_safe_send_data(tem, credp, called_from);
1498 			/*
1499 			 * Don't set anything if we are
1500 			 * already where as we want to be.
1501 			 */
1502 			if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) {
1503 				tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE;
1504 				/*
1505 				 * If we have switched the characters to be the
1506 				 * inverse from the screen, then switch them as
1507 				 * well to keep them the inverse of the screen.
1508 				 */
1509 				if (!(tem->tvs_flags & TEM_ATTR_REVERSE))
1510 					tem->tvs_flags |= TEM_ATTR_REVERSE;
1511 				else
1512 					tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1513 			}
1514 
1515 			tem_safe_cls(tem, credp, called_from);
1516 			tem->tvs_state = A_STATE_START;
1517 			return;
1518 		case 'r':	/* sunscrl */
1519 			/*
1520 			 * Rule exception:  check for validity here.
1521 			 */
1522 			tem->tvs_nscroll = tem->tvs_paramval;
1523 			if (tem->tvs_nscroll > tems.ts_c_dimension.height)
1524 				tem->tvs_nscroll = tems.ts_c_dimension.height;
1525 			if (tem->tvs_nscroll < 0)
1526 				tem->tvs_nscroll = 1;
1527 			tem->tvs_state = A_STATE_START;
1528 			return;
1529 		default:
1530 			tem_safe_getparams(tem, ch, credp, called_from);
1531 			return;
1532 		}
1533 	}
1534 
1535 	/* Previous char was <ESC> */
1536 	if (ch == '[') {
1537 		tem->tvs_curparam = 0;
1538 		tem->tvs_paramval = 0;
1539 		tem->tvs_gotparam = B_FALSE;
1540 		/* clear the parameters */
1541 		for (i = 0; i < TEM_MAXPARAMS; i++)
1542 			tem->tvs_params[i] = -1;
1543 		tem->tvs_state = A_STATE_CSI;
1544 	} else if (ch == 'Q') {	/* <ESC>Q ? */
1545 		tem->tvs_state = A_STATE_START;
1546 	} else if (ch == 'C') {	/* <ESC>C ? */
1547 		tem->tvs_state = A_STATE_START;
1548 	} else {
1549 		tem->tvs_state = A_STATE_START;
1550 		if (ch == 'c') {
1551 			/* ESC c resets display */
1552 			tem_safe_reset_display(tem, credp, called_from,
1553 			    B_TRUE, B_TRUE);
1554 		} else if (ch == 'H') {
1555 			/* ESC H sets a tab */
1556 			tem_safe_set_tab(tem);
1557 		} else if (ch == '7') {
1558 			/* ESC 7 Save Cursor position */
1559 			tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1560 			tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1561 		} else if (ch == '8') {
1562 			/* ESC 8 Restore Cursor position */
1563 			tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row,
1564 			    tem->tvs_r_cursor.col, credp, called_from);
1565 		/* check for control chars */
1566 		} else if (ch < ' ') {
1567 			tem_safe_control(tem, ch, credp, called_from);
1568 		} else {
1569 			tem_safe_outch(tem, ch, credp, called_from);
1570 		}
1571 	}
1572 }
1573 
1574 /* ARGSUSED */
1575 static void
1576 tem_safe_bell(struct tem_vt_state *tem, enum called_from called_from)
1577 {
1578 	if (called_from == CALLED_FROM_STANDALONE)
1579 		(void) beep_polled(BEEP_CONSOLE);
1580 	else
1581 		(void) beep(BEEP_CONSOLE);
1582 }
1583 
1584 
1585 static void
1586 tem_safe_scroll(struct tem_vt_state *tem, int start, int end, int count,
1587     int direction, cred_t *credp, enum called_from called_from)
1588 {
1589 	int	row;
1590 	int	lines_affected;
1591 
1592 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1593 	    called_from == CALLED_FROM_STANDALONE);
1594 
1595 	lines_affected = end - start + 1;
1596 	if (count > lines_affected)
1597 		count = lines_affected;
1598 	if (count <= 0)
1599 		return;
1600 
1601 	switch (direction) {
1602 	case TEM_SCROLL_UP:
1603 		if (count < lines_affected) {
1604 			tem_safe_copy_area(tem, 0, start + count,
1605 			    tems.ts_c_dimension.width - 1, end,
1606 			    0, start, credp, called_from);
1607 		}
1608 		for (row = (end - count) + 1; row <= end; row++) {
1609 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1610 			    row, 0, credp, called_from);
1611 		}
1612 		break;
1613 
1614 	case TEM_SCROLL_DOWN:
1615 		if (count < lines_affected) {
1616 			tem_safe_copy_area(tem, 0, start,
1617 			    tems.ts_c_dimension.width - 1,
1618 			    end - count, 0, start + count,
1619 			    credp, called_from);
1620 		}
1621 		for (row = start; row < start + count; row++) {
1622 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1623 			    row, 0, credp, called_from);
1624 		}
1625 		break;
1626 	}
1627 }
1628 
1629 static int
1630 tem_copy_width(term_char_t *src, term_char_t *dst, int cols)
1631 {
1632 	int width = cols - 1;
1633 
1634 	while (width >= 0) {
1635 		/* We can't compare images. */
1636 		if (TEM_CHAR_ATTR(src[width].tc_char) == TEM_ATTR_IMAGE ||
1637 		    TEM_CHAR_ATTR(dst[width].tc_char) == TEM_ATTR_IMAGE)
1638 			break;
1639 
1640 		/*
1641 		 * Find difference on line, compare char with its attributes
1642 		 * and colors.
1643 		 */
1644 		if (src[width].tc_char != dst[width].tc_char ||
1645 		    src[width].tc_fg_color.n != dst[width].tc_fg_color.n ||
1646 		    src[width].tc_bg_color.n != dst[width].tc_bg_color.n) {
1647 			break;
1648 		}
1649 		width--;
1650 	}
1651 	return (width + 1);
1652 }
1653 
1654 static void
1655 tem_safe_copy_area(struct tem_vt_state *tem,
1656     screen_pos_t s_col, screen_pos_t s_row,
1657     screen_pos_t e_col, screen_pos_t e_row,
1658     screen_pos_t t_col, screen_pos_t t_row,
1659     cred_t *credp, enum called_from called_from)
1660 {
1661 	size_t soffset, toffset;
1662 	term_char_t *src, *dst;
1663 	int rows;
1664 	int cols;
1665 
1666 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1667 	    called_from == CALLED_FROM_STANDALONE);
1668 
1669 	if (s_col < 0 || s_row < 0 ||
1670 	    e_col < 0 || e_row < 0 ||
1671 	    t_col < 0 || t_row < 0 ||
1672 	    s_col >= tems.ts_c_dimension.width ||
1673 	    e_col >= tems.ts_c_dimension.width ||
1674 	    t_col >= tems.ts_c_dimension.width ||
1675 	    s_row >= tems.ts_c_dimension.height ||
1676 	    e_row >= tems.ts_c_dimension.height ||
1677 	    t_row >= tems.ts_c_dimension.height)
1678 		return;
1679 
1680 	if (s_row > e_row || s_col > e_col)
1681 		return;
1682 
1683 	rows = e_row - s_row + 1;
1684 	cols = e_col - s_col + 1;
1685 	if (t_row + rows > tems.ts_c_dimension.height ||
1686 	    t_col + cols > tems.ts_c_dimension.width)
1687 		return;
1688 
1689 	soffset = s_col + s_row * tems.ts_c_dimension.width;
1690 	toffset = t_col + t_row * tems.ts_c_dimension.width;
1691 	src = tem->tvs_screen_buf + soffset;
1692 	dst = tem->tvs_screen_buf + toffset;
1693 
1694 	/*
1695 	 * Copy line by line. We determine the length by comparing the
1696 	 * screen content from cached text in tvs_screen_buf.
1697 	 */
1698 	if (toffset <= soffset) {
1699 		for (int i = 0; i < rows; i++) {
1700 			int increment = i * tems.ts_c_dimension.width;
1701 			int width;
1702 
1703 			width = tem_copy_width(src + increment,
1704 			    dst + increment, cols);
1705 
1706 			tem_safe_virtual_copy(tem, s_col, s_row + i,
1707 			    e_col  - cols + width, s_row + i,
1708 			    t_col, t_row + i);
1709 
1710 			if (tem->tvs_isactive) {
1711 				tem_safe_callback_copy(tem, s_col, s_row + i,
1712 				    e_col - cols + width, s_row + i,
1713 				    t_col, t_row + i, credp, called_from);
1714 			}
1715 		}
1716 	} else {
1717 		for (int i = rows - 1; i >= 0; i--) {
1718 			int increment = i * tems.ts_c_dimension.width;
1719 			int width;
1720 
1721 			width = tem_copy_width(src + increment,
1722 			    dst + increment, cols);
1723 
1724 			tem_safe_virtual_copy(tem, s_col, s_row + i,
1725 			    e_col  - cols + width, s_row + i,
1726 			    t_col, t_row + i);
1727 
1728 			if (tem->tvs_isactive) {
1729 				tem_safe_callback_copy(tem, s_col, s_row + i,
1730 				    e_col - cols + width, s_row + i,
1731 				    t_col, t_row + i, credp, called_from);
1732 			}
1733 		}
1734 	}
1735 }
1736 
1737 static void
1738 tem_safe_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row,
1739     screen_pos_t col, cred_t *credp, enum called_from called_from)
1740 {
1741 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1742 	    called_from == CALLED_FROM_STANDALONE);
1743 
1744 	if (row < 0 || row >= tems.ts_c_dimension.height ||
1745 	    col < 0 || col >= tems.ts_c_dimension.width ||
1746 	    count < 0)
1747 		return;
1748 
1749 	/*
1750 	 * Note that very large values of "count" could cause col+count
1751 	 * to overflow, so we check "count" independently.
1752 	 */
1753 	if (count > tems.ts_c_dimension.width ||
1754 	    col + count > tems.ts_c_dimension.width)
1755 		count = tems.ts_c_dimension.width - col;
1756 
1757 	tem_safe_virtual_cls(tem, count, row, col);
1758 
1759 	if (!tem->tvs_isactive)
1760 		return;
1761 
1762 	tem_safe_callback_cls(tem, count, row, col, credp, called_from);
1763 }
1764 
1765 /*ARGSUSED*/
1766 void
1767 tem_safe_text_display(struct tem_vt_state *tem, term_char_t *string,
1768     int count, screen_pos_t row, screen_pos_t col,
1769     cred_t *credp, enum called_from called_from)
1770 {
1771 	struct vis_consdisplay da;
1772 	int i;
1773 	tem_char_t c;
1774 	text_color_t bg, fg;
1775 
1776 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1777 	    called_from == CALLED_FROM_STANDALONE);
1778 
1779 	da.data = (uint8_t *)&c;
1780 	da.width = 1;
1781 	da.row = row;
1782 	da.col = col;
1783 
1784 	for (i = 0; i < count; i++) {
1785 		tem_safe_get_color(tem, &fg, &bg, &string[i]);
1786 		tem_safe_set_color(&fg, &da.fg_color);
1787 		tem_safe_set_color(&bg, &da.bg_color);
1788 		c = TEM_CHAR(string[i].tc_char);
1789 		tems_safe_display(&da, credp, called_from);
1790 		da.col++;
1791 	}
1792 }
1793 
1794 #if 0
1795 /*
1796  * This function is used to blit a rectangular color image,
1797  * unperturbed on the underlying framebuffer, to render
1798  * icons and pictures.  The data is a pixel pattern that
1799  * fills a rectangle bounded to the width and height parameters.
1800  * The color pixel data must to be pre-adjusted by the caller
1801  * for the current video depth.
1802  *
1803  * This function is unused now.
1804  */
1805 /*ARGSUSED*/
1806 static void
1807 tem_safe_image_display(struct tem_vt_state *tem, uchar_t *image,
1808     int height, int width, screen_pos_t row, screen_pos_t col,
1809     cred_t *credp, enum called_from called_from)
1810 {
1811 	struct vis_consdisplay da;
1812 
1813 	mutex_enter(&tems.ts_lock);
1814 	mutex_enter(&tem->tvs_lock);
1815 
1816 	da.data = image;
1817 	da.width = (screen_size_t)width;
1818 	da.height = (screen_size_t)height;
1819 	da.row = row;
1820 	da.col = col;
1821 
1822 	tems_safe_display(&da, credp, called_from);
1823 
1824 	mutex_exit(&tem->tvs_lock);
1825 	mutex_exit(&tems.ts_lock);
1826 }
1827 #endif
1828 
1829 /*ARGSUSED*/
1830 void
1831 tem_safe_text_copy(struct tem_vt_state *tem,
1832     screen_pos_t s_col, screen_pos_t s_row,
1833     screen_pos_t e_col, screen_pos_t e_row,
1834     screen_pos_t t_col, screen_pos_t t_row,
1835     cred_t *credp, enum called_from called_from)
1836 {
1837 	struct vis_conscopy da;
1838 
1839 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1840 	    called_from == CALLED_FROM_STANDALONE);
1841 
1842 	da.s_row = s_row;
1843 	da.s_col = s_col;
1844 	da.e_row = e_row;
1845 	da.e_col = e_col;
1846 	da.t_row = t_row;
1847 	da.t_col = t_col;
1848 
1849 	tems_safe_copy(&da, credp, called_from);
1850 }
1851 
1852 void
1853 tem_safe_text_cls(struct tem_vt_state *tem,
1854     int count, screen_pos_t row, screen_pos_t col, cred_t *credp,
1855     enum called_from called_from)
1856 {
1857 	text_attr_t attr;
1858 	term_char_t c;
1859 	int i;
1860 
1861 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1862 	    called_from == CALLED_FROM_STANDALONE);
1863 
1864 	tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
1865 	    TEM_ATTR_SCREEN_REVERSE);
1866 	c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
1867 
1868 	if (count > tems.ts_c_dimension.width ||
1869 	    col + count > tems.ts_c_dimension.width)
1870 		count = tems.ts_c_dimension.width - col;
1871 
1872 	for (i = 0; i < count; i++)
1873 		tems.ts_blank_line[i] = c;
1874 
1875 	tem_safe_text_display(tem, tems.ts_blank_line, count, row, col,
1876 	    credp, called_from);
1877 }
1878 
1879 void
1880 tem_safe_pix_display(struct tem_vt_state *tem,
1881     term_char_t *string, int count,
1882     screen_pos_t row, screen_pos_t col,
1883     cred_t *credp, enum called_from called_from)
1884 {
1885 	struct vis_consdisplay da;
1886 	int	i;
1887 
1888 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1889 	    called_from == CALLED_FROM_STANDALONE);
1890 
1891 	da.data = (uchar_t *)tem->tvs_pix_data;
1892 	da.width = (screen_size_t)tems.ts_font.vf_width;
1893 	da.height = (screen_size_t)tems.ts_font.vf_height;
1894 	da.row = (row * da.height) + tems.ts_p_offset.y;
1895 	da.col = (col * da.width) + tems.ts_p_offset.x;
1896 
1897 	for (i = 0; i < count; i++) {
1898 		/* Do not display image area */
1899 		if (!TEM_ATTR_ISSET(string[i].tc_char, TEM_ATTR_IMAGE)) {
1900 			tem_safe_callback_bit2pix(tem, &string[i]);
1901 			tems_safe_display(&da, credp, called_from);
1902 		}
1903 		da.col += da.width;
1904 	}
1905 }
1906 
1907 void
1908 tem_safe_pix_copy(struct tem_vt_state *tem,
1909     screen_pos_t s_col, screen_pos_t s_row,
1910     screen_pos_t e_col, screen_pos_t e_row,
1911     screen_pos_t t_col, screen_pos_t t_row,
1912     cred_t *credp,
1913     enum called_from called_from)
1914 {
1915 	struct vis_conscopy ma;
1916 	static boolean_t need_clear = B_TRUE;
1917 
1918 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1919 	    called_from == CALLED_FROM_STANDALONE);
1920 
1921 	if (need_clear && tem->tvs_first_line > 0) {
1922 		/*
1923 		 * Clear OBP output above our kernel console term
1924 		 * when our kernel console term begins to scroll up,
1925 		 * we hope it is user friendly.
1926 		 * (Also see comments on tem_safe_pix_clear_prom_output)
1927 		 *
1928 		 * This is only one time call.
1929 		 */
1930 		tem_safe_pix_clear_prom_output(tem, credp, called_from);
1931 	}
1932 	need_clear = B_FALSE;
1933 
1934 	ma.s_row = s_row * tems.ts_font.vf_height + tems.ts_p_offset.y;
1935 	ma.e_row = (e_row + 1) * tems.ts_font.vf_height +
1936 	    tems.ts_p_offset.y - 1;
1937 	ma.t_row = t_row * tems.ts_font.vf_height + tems.ts_p_offset.y;
1938 
1939 	/*
1940 	 * Check if we're in process of clearing OBP's columns area,
1941 	 * which only happens when term scrolls up a whole line.
1942 	 */
1943 	if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 &&
1944 	    e_col == tems.ts_c_dimension.width - 1) {
1945 		/*
1946 		 * We need to clear OBP's columns area outside our kernel
1947 		 * console term. So that we set ma.e_col to entire row here.
1948 		 */
1949 		ma.s_col = s_col * tems.ts_font.vf_width;
1950 		ma.e_col = tems.ts_p_dimension.width - 1;
1951 
1952 		ma.t_col = t_col * tems.ts_font.vf_width;
1953 	} else {
1954 		ma.s_col = s_col * tems.ts_font.vf_width + tems.ts_p_offset.x;
1955 		ma.e_col = (e_col + 1) * tems.ts_font.vf_width +
1956 		    tems.ts_p_offset.x - 1;
1957 		ma.t_col = t_col * tems.ts_font.vf_width + tems.ts_p_offset.x;
1958 	}
1959 
1960 	tems_safe_copy(&ma, credp, called_from);
1961 
1962 	if (tem->tvs_first_line > 0 && t_row < s_row) {
1963 		/* We have scrolled up (s_row - t_row) rows. */
1964 		tem->tvs_first_line -= (s_row - t_row);
1965 		if (tem->tvs_first_line <= 0) {
1966 			/* All OBP rows have been cleared. */
1967 			tem->tvs_first_line = 0;
1968 		}
1969 	}
1970 
1971 }
1972 
1973 void
1974 tem_safe_pix_bit2pix(struct tem_vt_state *tem, term_char_t *c)
1975 {
1976 	text_color_t fg, bg;
1977 	void (*fp)(struct tem_vt_state *, tem_char_t,
1978 	    text_color_t, text_color_t);
1979 
1980 	tem_safe_get_color(tem, &fg, &bg, c);
1981 	switch (tems.ts_pdepth) {
1982 	case 4:
1983 		fp = bit_to_pix4;
1984 		break;
1985 	case 8:
1986 		fp = bit_to_pix8;
1987 		break;
1988 	case 15:
1989 	case 16:
1990 		fp = bit_to_pix16;
1991 		break;
1992 	case 24:
1993 		fp = bit_to_pix24;
1994 		break;
1995 	case 32:
1996 		fp = bit_to_pix32;
1997 		break;
1998 	default:
1999 		return;
2000 	}
2001 
2002 	fp(tem, c->tc_char, fg, bg);
2003 }
2004 
2005 
2006 /*
2007  * This function only clears count of columns in one row
2008  */
2009 void
2010 tem_safe_pix_cls(struct tem_vt_state *tem, int count,
2011     screen_pos_t row, screen_pos_t col, cred_t *credp,
2012     enum called_from called_from)
2013 {
2014 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2015 	    called_from == CALLED_FROM_STANDALONE);
2016 
2017 	tem_safe_pix_cls_range(tem, row, 1, tems.ts_p_offset.y,
2018 	    col, count, tems.ts_p_offset.x, B_FALSE, credp, called_from);
2019 }
2020 
2021 /*
2022  * This function clears OBP output above our kernel console term area
2023  * because OBP's term may have a bigger terminal window than that of
2024  * our kernel console term. So we need to clear OBP output garbage outside
2025  * of our kernel console term at a proper time, which is when the first
2026  * row output of our kernel console term scrolls at the first screen line.
2027  *
2028  *	_________________________________
2029  *	|   _____________________	|  ---> OBP's bigger term window
2030  *	|   |			|	|
2031  *	|___|			|	|
2032  *	| | |			|	|
2033  *	| | |			|	|
2034  *	|_|_|___________________|_______|
2035  *	  | |			|	   ---> first line
2036  *	  | |___________________|---> our kernel console term window
2037  *	  |
2038  *	  |---> columns area to be cleared
2039  *
2040  * This function only takes care of the output above our kernel console term,
2041  * and tem_prom_scroll_up takes care of columns area outside of our kernel
2042  * console term.
2043  */
2044 static void
2045 tem_safe_pix_clear_prom_output(struct tem_vt_state *tem, cred_t *credp,
2046     enum called_from called_from)
2047 {
2048 	int	nrows, ncols, width, height, offset;
2049 
2050 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2051 	    called_from == CALLED_FROM_STANDALONE);
2052 
2053 	width = tems.ts_font.vf_width;
2054 	height = tems.ts_font.vf_height;
2055 	offset = tems.ts_p_offset.y % height;
2056 
2057 	nrows = tems.ts_p_offset.y / height;
2058 	ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
2059 
2060 	if (nrows > 0)
2061 		tem_safe_pix_cls_range(tem, 0, nrows, offset, 0, ncols, 0,
2062 		    B_FALSE, credp, called_from);
2063 }
2064 
2065 /*
2066  * clear the whole screen for pixel mode, just clear the
2067  * physical screen.
2068  */
2069 void
2070 tem_safe_pix_clear_entire_screen(struct tem_vt_state *tem, cred_t *credp,
2071     enum called_from called_from)
2072 {
2073 	struct vis_consclear cl;
2074 	text_color_t fg_color;
2075 	text_color_t bg_color;
2076 	text_attr_t attr;
2077 	term_char_t c;
2078 	int nrows, ncols, width, height;
2079 
2080 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2081 	    called_from == CALLED_FROM_STANDALONE);
2082 
2083 	/* call driver first, if error, clear terminal area */
2084 	tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2085 	    TEM_ATTR_SCREEN_REVERSE);
2086 	c.tc_char = TEM_ATTR(attr);
2087 
2088 	tem_safe_get_color(tem, &fg_color, &bg_color, &c);
2089 	tem_safe_set_color(&bg_color, &cl.bg_color);
2090 	if (tems_cls_layered(&cl, credp) == 0)
2091 		return;
2092 
2093 	width = tems.ts_font.vf_width;
2094 	height = tems.ts_font.vf_height;
2095 
2096 	nrows = (tems.ts_p_dimension.height + (height - 1))/ height;
2097 	ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
2098 
2099 	tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y, 0, ncols,
2100 	    tems.ts_p_offset.x, B_FALSE, credp, called_from);
2101 
2102 	/*
2103 	 * Since the whole screen is cleared, we don't need
2104 	 * to clear OBP output later.
2105 	 */
2106 	if (tem->tvs_first_line > 0)
2107 		tem->tvs_first_line = 0;
2108 }
2109 
2110 /*
2111  * clear the whole screen, including the virtual screen buffer,
2112  * and reset the cursor to start point.
2113  */
2114 static void
2115 tem_safe_cls(struct tem_vt_state *tem,
2116     cred_t *credp, enum called_from called_from)
2117 {
2118 	int	row;
2119 
2120 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2121 	    called_from == CALLED_FROM_STANDALONE);
2122 
2123 	if (tems.ts_display_mode == VIS_TEXT) {
2124 		for (row = 0; row < tems.ts_c_dimension.height; row++) {
2125 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
2126 			    row, 0, credp, called_from);
2127 		}
2128 		tem->tvs_c_cursor.row = 0;
2129 		tem->tvs_c_cursor.col = 0;
2130 		tem_safe_align_cursor(tem);
2131 		return;
2132 	}
2133 
2134 	ASSERT(tems.ts_display_mode == VIS_PIXEL);
2135 
2136 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2137 		tem_safe_virtual_cls(tem, tems.ts_c_dimension.width, row, 0);
2138 	}
2139 	tem->tvs_c_cursor.row = 0;
2140 	tem->tvs_c_cursor.col = 0;
2141 	tem_safe_align_cursor(tem);
2142 
2143 	if (!tem->tvs_isactive)
2144 		return;
2145 
2146 	tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2147 }
2148 
2149 static void
2150 tem_safe_back_tab(struct tem_vt_state *tem,
2151     cred_t *credp, enum called_from called_from)
2152 {
2153 	int	i;
2154 	screen_pos_t	tabstop;
2155 
2156 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2157 	    called_from == CALLED_FROM_STANDALONE);
2158 
2159 	tabstop = 0;
2160 
2161 	for (i = tem->tvs_ntabs - 1; i >= 0; i--) {
2162 		if (tem->tvs_tabs[i] < tem->tvs_c_cursor.col) {
2163 			tabstop = tem->tvs_tabs[i];
2164 			break;
2165 		}
2166 	}
2167 
2168 	tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row,
2169 	    tabstop, credp, called_from);
2170 }
2171 
2172 static void
2173 tem_safe_tab(struct tem_vt_state *tem,
2174     cred_t *credp, enum called_from called_from)
2175 {
2176 	size_t	i;
2177 	screen_pos_t	tabstop;
2178 
2179 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2180 	    called_from == CALLED_FROM_STANDALONE);
2181 
2182 	tabstop = tems.ts_c_dimension.width - 1;
2183 
2184 	for (i = 0; i < tem->tvs_ntabs; i++) {
2185 		if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
2186 			tabstop = tem->tvs_tabs[i];
2187 			break;
2188 		}
2189 	}
2190 
2191 	tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row,
2192 	    tabstop, credp, called_from);
2193 }
2194 
2195 static void
2196 tem_safe_set_tab(struct tem_vt_state *tem)
2197 {
2198 	size_t	i, j;
2199 
2200 	if (tem->tvs_ntabs == tem->tvs_maxtab)
2201 		return;
2202 	if (tem->tvs_ntabs == 0 ||
2203 	    tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) {
2204 			tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col;
2205 			return;
2206 	}
2207 	for (i = 0; i < tem->tvs_ntabs; i++) {
2208 		if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col)
2209 			return;
2210 		if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
2211 			for (j = tem->tvs_ntabs - 1; j >= i; j--)
2212 				tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j];
2213 			tem->tvs_tabs[i] = tem->tvs_c_cursor.col;
2214 			tem->tvs_ntabs++;
2215 			return;
2216 		}
2217 	}
2218 }
2219 
2220 static void
2221 tem_safe_clear_tabs(struct tem_vt_state *tem, int action)
2222 {
2223 	size_t	i, j;
2224 
2225 	switch (action) {
2226 	case 3: /* clear all tabs */
2227 		tem->tvs_ntabs = 0;
2228 		break;
2229 	case 0: /* clr tab at cursor */
2230 
2231 		for (i = 0; i < tem->tvs_ntabs; i++) {
2232 			if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) {
2233 				tem->tvs_ntabs--;
2234 				for (j = i; j < tem->tvs_ntabs; j++)
2235 					tem->tvs_tabs[j] = tem->tvs_tabs[j + 1];
2236 				return;
2237 			}
2238 		}
2239 		break;
2240 	}
2241 }
2242 
2243 static void
2244 tem_safe_mv_cursor(struct tem_vt_state *tem, int row, int col,
2245     cred_t *credp, enum called_from called_from)
2246 {
2247 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2248 	    called_from == CALLED_FROM_STANDALONE);
2249 
2250 	/*
2251 	 * Sanity check and bounds enforcement.  Out of bounds requests are
2252 	 * clipped to the screen boundaries.  This seems to be what SPARC
2253 	 * does.
2254 	 */
2255 	if (row < 0)
2256 		row = 0;
2257 	if (row >= tems.ts_c_dimension.height)
2258 		row = tems.ts_c_dimension.height - 1;
2259 	if (col < 0)
2260 		col = 0;
2261 	if (col >= tems.ts_c_dimension.width)
2262 		col = tems.ts_c_dimension.width - 1;
2263 
2264 	tem_safe_send_data(tem, credp, called_from);
2265 	tem->tvs_c_cursor.row = (screen_pos_t)row;
2266 	tem->tvs_c_cursor.col = (screen_pos_t)col;
2267 	tem_safe_align_cursor(tem);
2268 }
2269 
2270 /* ARGSUSED */
2271 void
2272 tem_safe_reset_emulator(struct tem_vt_state *tem,
2273     cred_t *credp, enum called_from called_from,
2274     boolean_t init_color)
2275 {
2276 	int j;
2277 
2278 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2279 	    called_from == CALLED_FROM_STANDALONE);
2280 
2281 	tem->tvs_c_cursor.row = 0;
2282 	tem->tvs_c_cursor.col = 0;
2283 	tem->tvs_r_cursor.row = 0;
2284 	tem->tvs_r_cursor.col = 0;
2285 	tem->tvs_s_cursor.row = 0;
2286 	tem->tvs_s_cursor.col = 0;
2287 	tem->tvs_outindex = 0;
2288 	tem->tvs_state = A_STATE_START;
2289 	tem->tvs_gotparam = B_FALSE;
2290 	tem->tvs_curparam = 0;
2291 	tem->tvs_paramval = 0;
2292 	tem->tvs_nscroll = 1;
2293 
2294 	if (init_color) {
2295 		tem->tvs_alpha = 0xff;
2296 		tem->tvs_fg_color = tems.ts_init_color.fg_color;
2297 		tem->tvs_bg_color = tems.ts_init_color.bg_color;
2298 		tem->tvs_flags = tems.ts_init_color.a_flags;
2299 	}
2300 
2301 	/*
2302 	 * set up the initial tab stops
2303 	 */
2304 	tem->tvs_ntabs = 0;
2305 	for (j = 8; j < tems.ts_c_dimension.width; j += 8)
2306 		tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j;
2307 
2308 	for (j = 0; j < TEM_MAXPARAMS; j++)
2309 		tem->tvs_params[j] = 0;
2310 }
2311 
2312 void
2313 tem_safe_reset_display(struct tem_vt_state *tem,
2314     cred_t *credp, enum called_from called_from,
2315     boolean_t clear_txt, boolean_t init_color)
2316 {
2317 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2318 	    called_from == CALLED_FROM_STANDALONE);
2319 
2320 	tem_safe_reset_emulator(tem, credp, called_from, init_color);
2321 
2322 	if (clear_txt) {
2323 		if (tem->tvs_isactive)
2324 			tem_safe_callback_cursor(tem,
2325 			    VIS_HIDE_CURSOR, credp, called_from);
2326 
2327 		tem_safe_cls(tem, credp, called_from);
2328 
2329 		if (tem->tvs_isactive)
2330 			tem_safe_callback_cursor(tem,
2331 			    VIS_DISPLAY_CURSOR, credp, called_from);
2332 	}
2333 }
2334 
2335 static void
2336 tem_safe_shift(
2337 	struct tem_vt_state *tem,
2338 	int count,
2339 	int direction,
2340 	cred_t *credp,
2341 	enum called_from called_from)
2342 {
2343 	int rest_of_line;
2344 
2345 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2346 	    called_from == CALLED_FROM_STANDALONE);
2347 
2348 	rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col;
2349 	if (count > rest_of_line)
2350 		count = rest_of_line;
2351 
2352 	if (count <= 0)
2353 		return;
2354 
2355 	switch (direction) {
2356 	case TEM_SHIFT_LEFT:
2357 		if (count < rest_of_line) {
2358 			tem_safe_copy_area(tem,
2359 			    tem->tvs_c_cursor.col + count,
2360 			    tem->tvs_c_cursor.row,
2361 			    tems.ts_c_dimension.width - 1,
2362 			    tem->tvs_c_cursor.row,
2363 			    tem->tvs_c_cursor.col,
2364 			    tem->tvs_c_cursor.row,
2365 			    credp, called_from);
2366 		}
2367 
2368 		tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
2369 		    (tems.ts_c_dimension.width - count), credp,
2370 		    called_from);
2371 		break;
2372 	case TEM_SHIFT_RIGHT:
2373 		if (count < rest_of_line) {
2374 			tem_safe_copy_area(tem,
2375 			    tem->tvs_c_cursor.col,
2376 			    tem->tvs_c_cursor.row,
2377 			    tems.ts_c_dimension.width - count - 1,
2378 			    tem->tvs_c_cursor.row,
2379 			    tem->tvs_c_cursor.col + count,
2380 			    tem->tvs_c_cursor.row,
2381 			    credp, called_from);
2382 		}
2383 
2384 		tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
2385 		    tem->tvs_c_cursor.col, credp, called_from);
2386 		break;
2387 	}
2388 }
2389 
2390 void
2391 tem_safe_text_cursor(struct tem_vt_state *tem, short action,
2392     cred_t *credp, enum called_from called_from)
2393 {
2394 	struct vis_conscursor	ca;
2395 
2396 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2397 	    called_from == CALLED_FROM_STANDALONE);
2398 
2399 	ca.row = tem->tvs_c_cursor.row;
2400 	ca.col = tem->tvs_c_cursor.col;
2401 	ca.action = action;
2402 
2403 	tems_safe_cursor(&ca, credp, called_from);
2404 
2405 	if (action == VIS_GET_CURSOR) {
2406 		tem->tvs_c_cursor.row = ca.row;
2407 		tem->tvs_c_cursor.col = ca.col;
2408 	}
2409 }
2410 
2411 void
2412 tem_safe_pix_cursor(struct tem_vt_state *tem, short action,
2413     cred_t *credp, enum called_from called_from)
2414 {
2415 	struct vis_conscursor	ca;
2416 	text_color_t fg, bg;
2417 	term_char_t c;
2418 	text_attr_t attr;
2419 
2420 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2421 	    called_from == CALLED_FROM_STANDALONE);
2422 
2423 	ca.row = tem->tvs_c_cursor.row * tems.ts_font.vf_height +
2424 	    tems.ts_p_offset.y;
2425 	ca.col = tem->tvs_c_cursor.col * tems.ts_font.vf_width +
2426 	    tems.ts_p_offset.x;
2427 	ca.width = (screen_size_t)tems.ts_font.vf_width;
2428 	ca.height = (screen_size_t)tems.ts_font.vf_height;
2429 
2430 	tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2431 	    TEM_ATTR_REVERSE);
2432 	c.tc_char = TEM_ATTR(attr);
2433 
2434 	tem_safe_get_color(tem, &fg, &bg, &c);
2435 	tem_safe_set_color(&fg, &ca.fg_color);
2436 	tem_safe_set_color(&bg, &ca.bg_color);
2437 
2438 	ca.action = action;
2439 
2440 	tems_safe_cursor(&ca, credp, called_from);
2441 
2442 	if (action == VIS_GET_CURSOR) {
2443 		tem->tvs_c_cursor.row = 0;
2444 		tem->tvs_c_cursor.col = 0;
2445 
2446 		if (ca.row != 0) {
2447 			tem->tvs_c_cursor.row = (ca.row - tems.ts_p_offset.y) /
2448 			    tems.ts_font.vf_height;
2449 		}
2450 		if (ca.col != 0) {
2451 			tem->tvs_c_cursor.col = (ca.col - tems.ts_p_offset.x) /
2452 			    tems.ts_font.vf_width;
2453 		}
2454 	}
2455 }
2456 
2457 static void
2458 bit_to_pix4(struct tem_vt_state *tem, tem_char_t c, text_color_t fg,
2459     text_color_t bg)
2460 {
2461 	uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2462 
2463 	font_bit_to_pix4(&tems.ts_font, dest, c, fg.n, bg.n);
2464 }
2465 
2466 static void
2467 bit_to_pix8(struct tem_vt_state *tem, tem_char_t c, text_color_t fg,
2468     text_color_t bg)
2469 {
2470 	uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2471 
2472 	font_bit_to_pix8(&tems.ts_font, dest, c, fg.n, bg.n);
2473 }
2474 
2475 static void
2476 bit_to_pix16(struct tem_vt_state *tem, tem_char_t c, text_color_t fg,
2477     text_color_t bg)
2478 {
2479 	uint16_t *dest;
2480 
2481 	dest = (uint16_t *)tem->tvs_pix_data;
2482 	font_bit_to_pix16(&tems.ts_font, dest, c, fg.n, bg.n);
2483 }
2484 
2485 static void
2486 bit_to_pix24(struct tem_vt_state *tem, tem_char_t c, text_color_t fg,
2487     text_color_t bg)
2488 {
2489 	uint8_t *dest;
2490 
2491 	dest = (uint8_t *)tem->tvs_pix_data;
2492 	font_bit_to_pix24(&tems.ts_font, dest, c, fg.n, bg.n);
2493 }
2494 
2495 static void
2496 bit_to_pix32(struct tem_vt_state *tem, tem_char_t c, text_color_t fg,
2497     text_color_t bg)
2498 {
2499 	uint32_t *dest;
2500 
2501 	dest = (uint32_t *)tem->tvs_pix_data;
2502 	font_bit_to_pix32(&tems.ts_font, dest, c, fg.n, bg.n);
2503 }
2504 
2505 /*
2506  * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE
2507  */
2508 void
2509 tem_safe_get_attr(struct tem_vt_state *tem, text_color_t *fg,
2510     text_color_t *bg, text_attr_t *attr, uint8_t flag)
2511 {
2512 	if (tem->tvs_flags & flag) {
2513 		*fg = tem->tvs_bg_color;
2514 		*bg = tem->tvs_fg_color;
2515 	} else {
2516 		*fg = tem->tvs_fg_color;
2517 		*bg = tem->tvs_bg_color;
2518 	}
2519 
2520 	if (attr != NULL)
2521 		*attr = tem->tvs_flags;
2522 }
2523 
2524 static void
2525 tem_safe_get_color(struct tem_vt_state *tem, text_color_t *fg,
2526     text_color_t *bg, term_char_t *c)
2527 {
2528 	boolean_t bold_font;
2529 
2530 	*fg = c->tc_fg_color;
2531 	*bg = c->tc_bg_color;
2532 
2533 	bold_font = tems.ts_font.vf_map_count[VFNT_MAP_BOLD] != 0;
2534 
2535 	/*
2536 	 * If we have both normal and bold font components,
2537 	 * we use bold font for TEM_ATTR_BOLD.
2538 	 * The bright color is traditionally used with TEM_ATTR_BOLD,
2539 	 * in case there is no bold font.
2540 	 */
2541 	if (!TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_FG) &&
2542 	    c->tc_fg_color.n < XLATE_NCOLORS) {
2543 		if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BRIGHT_FG) ||
2544 		    (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BOLD) && !bold_font))
2545 			fg->n = brt_xlate[c->tc_fg_color.n];
2546 		else
2547 			fg->n = dim_xlate[c->tc_fg_color.n];
2548 	}
2549 
2550 	if (!TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_BG) &&
2551 	    c->tc_bg_color.n < XLATE_NCOLORS) {
2552 		if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_BRIGHT_BG))
2553 			bg->n = brt_xlate[c->tc_bg_color.n];
2554 		else
2555 			bg->n = dim_xlate[c->tc_bg_color.n];
2556 	}
2557 
2558 	if (tems.ts_display_mode == VIS_TEXT)
2559 		return;
2560 
2561 	if (tems.ts_pdepth == 8) {
2562 		/* 8-bit depth is using indexed colors. */
2563 #ifndef	_HAVE_TEM_FIRMWARE
2564 		fg->n = tems.ts_color_map(fg->n);
2565 		bg->n = tems.ts_color_map(bg->n);
2566 #endif
2567 		return;
2568 	}
2569 
2570 	/*
2571 	 * Translate fg and bg to RGB colors.
2572 	 */
2573 	if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_FG)) {
2574 		fg->n = rgb_to_color(&rgb_info,
2575 		    fg->rgb.a, fg->rgb.r, fg->rgb.g, fg->rgb.b);
2576 	} else {
2577 #ifdef _HAVE_TEM_FIRMWARE
2578 		if (tems.ts_pdepth == 24 || tems.ts_pdepth == 32)
2579 			fg->n = PIX4TO32(fg->n);
2580 #else
2581 		fg->n = rgb_color_map(&rgb_info, fg->n, tem->tvs_alpha);
2582 #endif
2583 	}
2584 
2585 	if (TEM_ATTR_ISSET(c->tc_char, TEM_ATTR_RGB_BG)) {
2586 		bg->n = rgb_to_color(&rgb_info,
2587 		    bg->rgb.a, bg->rgb.r, bg->rgb.g, bg->rgb.b);
2588 	} else {
2589 #ifdef _HAVE_TEM_FIRMWARE
2590 		if (tems.ts_pdepth == 24 || tems.ts_pdepth == 32)
2591 			bg->n = PIX4TO32(bg->n);
2592 #else
2593 		bg->n = rgb_color_map(&rgb_info, bg->n, tem->tvs_alpha);
2594 #endif
2595 	}
2596 }
2597 
2598 static void
2599 tem_safe_set_color(text_color_t *t, color_t *c)
2600 {
2601 	switch (tems.ts_pdepth) {
2602 	case 4:
2603 		c->four = t->n & 0xFF;
2604 		break;
2605 	case 8:
2606 		c->eight = t->n & 0xFF;
2607 		break;
2608 	case 15:
2609 	case 16:
2610 		c->sixteen[0] = (t->n >> 8) & 0xFF;
2611 		c->sixteen[1] = t->n & 0xFF;
2612 		break;
2613 	case 24:
2614 		c->twentyfour[0] = (t->n >> 16) & 0xFF;
2615 		c->twentyfour[1] = (t->n >> 8) & 0xFF;
2616 		c->twentyfour[2] = t->n & 0xFF;
2617 		break;
2618 	default:
2619 		*(uint32_t *)c = t->n;
2620 		break;
2621 	}
2622 }
2623 
2624 /*
2625  * Clear a rectangle of screen for pixel mode.
2626  *
2627  * arguments:
2628  *    row:	start row#
2629  *    nrows:	the number of rows to clear
2630  *    offset_y:	the offset of height in pixels to begin clear
2631  *    col:	start col#
2632  *    ncols:	the number of cols to clear
2633  *    offset_x:	the offset of width in pixels to begin clear
2634  *    scroll_up: whether this function is called during sroll up,
2635  *		 which is called only once.
2636  */
2637 void
2638 tem_safe_pix_cls_range(struct tem_vt_state *tem,
2639     screen_pos_t row, int nrows, int offset_y,
2640     screen_pos_t col, int ncols, int offset_x,
2641     boolean_t sroll_up, cred_t *credp,
2642     enum called_from called_from)
2643 {
2644 	struct vis_consdisplay da;
2645 	int	i, j;
2646 	int	row_add = 0;
2647 	term_char_t c;
2648 	text_attr_t attr;
2649 
2650 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2651 	    called_from == CALLED_FROM_STANDALONE);
2652 
2653 	if (sroll_up)
2654 		row_add = tems.ts_c_dimension.height - 1;
2655 
2656 	da.width = (screen_size_t)tems.ts_font.vf_width;
2657 	da.height = (screen_size_t)tems.ts_font.vf_height;
2658 
2659 	tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2660 	    TEM_ATTR_SCREEN_REVERSE);
2661 	/* Make sure we will not draw underlines */
2662 	c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
2663 
2664 	tem_safe_callback_bit2pix(tem, &c);
2665 	da.data = (uchar_t *)tem->tvs_pix_data;
2666 
2667 	for (i = 0; i < nrows; i++, row++) {
2668 		da.row = (row + row_add) * da.height + offset_y;
2669 		da.col = col * da.width + offset_x;
2670 		for (j = 0; j < ncols; j++) {
2671 			tems_safe_display(&da, credp, called_from);
2672 			da.col += da.width;
2673 		}
2674 	}
2675 }
2676 
2677 /*
2678  * virtual screen operations
2679  */
2680 static void
2681 tem_safe_virtual_display(struct tem_vt_state *tem, term_char_t *string,
2682     int count, screen_pos_t row, screen_pos_t col)
2683 {
2684 	int i, width;
2685 	term_char_t *addr;
2686 
2687 	if (row < 0 || row >= tems.ts_c_dimension.height ||
2688 	    col < 0 || col >= tems.ts_c_dimension.width ||
2689 	    col + count > tems.ts_c_dimension.width)
2690 		return;
2691 
2692 	width = tems.ts_c_dimension.width;
2693 	addr = tem->tvs_screen_buf + (row * width + col);
2694 	for (i = 0; i < count; i++) {
2695 		*addr++ = string[i];
2696 	}
2697 }
2698 
2699 static void
2700 i_virtual_copy_tem_chars(term_char_t *base,
2701     screen_pos_t s_col, screen_pos_t s_row,
2702     screen_pos_t e_col, screen_pos_t e_row,
2703     screen_pos_t t_col, screen_pos_t t_row)
2704 {
2705 	term_char_t	*from;
2706 	term_char_t	*to;
2707 	int		cnt;
2708 	screen_size_t chars_per_row;
2709 	term_char_t	*to_row_start;
2710 	term_char_t	*from_row_start;
2711 	screen_size_t   rows_to_move;
2712 	int		cols = tems.ts_c_dimension.width;
2713 
2714 	chars_per_row = e_col - s_col + 1;
2715 	rows_to_move = e_row - s_row + 1;
2716 
2717 	to_row_start = base + ((t_row * cols) + t_col);
2718 	from_row_start = base + ((s_row * cols) + s_col);
2719 
2720 	if (to_row_start < from_row_start) {
2721 		while (rows_to_move-- > 0) {
2722 			to = to_row_start;
2723 			from = from_row_start;
2724 			to_row_start += cols;
2725 			from_row_start += cols;
2726 			for (cnt = chars_per_row; cnt-- > 0; )
2727 				*to++ = *from++;
2728 		}
2729 	} else {
2730 		/*
2731 		 * Offset to the end of the region and copy backwards.
2732 		 */
2733 		cnt = rows_to_move * cols + chars_per_row;
2734 		to_row_start += cnt;
2735 		from_row_start += cnt;
2736 
2737 		while (rows_to_move-- > 0) {
2738 			to_row_start -= cols;
2739 			from_row_start -= cols;
2740 			to = to_row_start;
2741 			from = from_row_start;
2742 			for (cnt = chars_per_row; cnt-- > 0; )
2743 				*--to = *--from;
2744 		}
2745 	}
2746 }
2747 
2748 static void
2749 tem_safe_virtual_copy(struct tem_vt_state *tem,
2750     screen_pos_t s_col, screen_pos_t s_row,
2751     screen_pos_t e_col, screen_pos_t e_row,
2752     screen_pos_t t_col, screen_pos_t t_row)
2753 {
2754 	screen_size_t chars_per_row;
2755 	screen_size_t   rows_to_move;
2756 	int		rows = tems.ts_c_dimension.height;
2757 	int		cols = tems.ts_c_dimension.width;
2758 
2759 	if (s_col < 0 || s_col >= cols ||
2760 	    s_row < 0 || s_row >= rows ||
2761 	    e_col < 0 || e_col >= cols ||
2762 	    e_row < 0 || e_row >= rows ||
2763 	    t_col < 0 || t_col >= cols ||
2764 	    t_row < 0 || t_row >= rows ||
2765 	    s_col > e_col ||
2766 	    s_row > e_row)
2767 		return;
2768 
2769 	chars_per_row = e_col - s_col + 1;
2770 	rows_to_move = e_row - s_row + 1;
2771 
2772 	/* More sanity checks. */
2773 	if (t_row + rows_to_move > rows ||
2774 	    t_col + chars_per_row > cols)
2775 		return;
2776 
2777 	i_virtual_copy_tem_chars(tem->tvs_screen_buf, s_col, s_row,
2778 	    e_col, e_row, t_col, t_row);
2779 }
2780 
2781 static void
2782 tem_safe_virtual_cls(struct tem_vt_state *tem,
2783     int count, screen_pos_t row, screen_pos_t col)
2784 {
2785 	int i;
2786 	text_attr_t attr;
2787 	term_char_t c;
2788 
2789 	tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2790 	    TEM_ATTR_SCREEN_REVERSE);
2791 	c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
2792 
2793 	for (i = 0; i < tems.ts_c_dimension.width; i++)
2794 		tems.ts_blank_line[i] = c;
2795 
2796 	tem_safe_virtual_display(tem, tems.ts_blank_line, count, row, col);
2797 }
2798 
2799 /*
2800  * only blank screen, not clear our screen buffer
2801  */
2802 void
2803 tem_safe_blank_screen(struct tem_vt_state *tem, cred_t *credp,
2804     enum called_from called_from)
2805 {
2806 	int	row;
2807 
2808 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2809 	    called_from == CALLED_FROM_STANDALONE);
2810 
2811 	if (tems.ts_display_mode == VIS_PIXEL) {
2812 		tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2813 		return;
2814 	}
2815 
2816 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2817 		tem_safe_callback_cls(tem,
2818 		    tems.ts_c_dimension.width,
2819 		    row, 0, credp, called_from);
2820 	}
2821 }
2822 
2823 /*
2824  * unblank screen with associated tem from its screen buffer
2825  */
2826 void
2827 tem_safe_unblank_screen(struct tem_vt_state *tem, cred_t *credp,
2828     enum called_from called_from)
2829 {
2830 	int	row;
2831 
2832 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2833 	    called_from == CALLED_FROM_STANDALONE);
2834 
2835 	if (tems.ts_display_mode == VIS_PIXEL)
2836 		tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2837 
2838 	tem_safe_callback_cursor(tem, VIS_HIDE_CURSOR, credp, called_from);
2839 
2840 	/*
2841 	 * Display data in tvs_screen_buf to the actual framebuffer in a
2842 	 * row by row way.
2843 	 * When dealing with one row, output data with the same foreground
2844 	 * and background color all together.
2845 	 */
2846 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2847 		tem_safe_callback_display(tem, tem->tvs_screen_rows[row],
2848 		    tems.ts_c_dimension.width, row, 0, credp, called_from);
2849 	}
2850 
2851 	tem_safe_callback_cursor(tem, VIS_DISPLAY_CURSOR, credp, called_from);
2852 }
2853