xref: /illumos-gate/usr/src/uts/common/io/tem_safe.c (revision 40d76caa635ecc58d52c30c4f19b57963a3e5a51)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Copyright 2016 Joyent, Inc.
29  */
30 
31 /*
32  * Polled I/O safe ANSI terminal emulator module;
33  * Supporting TERM types 'sun' and 'sun-color, parsing
34  * ANSI x3.64 escape sequences, and the like.  (See wscons(7d)
35  * for more information).
36  *
37  * IMPORTANT:
38  *
39  *   The functions in this file *must* be able to function in
40  *   standalone mode, ie. on a quiesced system.   In that state,
41  *   access is single threaded, only one CPU is running.
42  *   System services are NOT available.
43  *
44  * The following restrictions pertain to every function
45  * in this file:
46  *
47  *     - CANNOT use the DDI or LDI interfaces
48  *     - CANNOT call system services
49  *     - CANNOT use mutexes
50  *     - CANNOT wait for interrupts
51  *     - CANNOT allocate memory
52  *
53  * All non-static functions in this file which:
54  *     - Operates on tems and tem_vt_state
55  *     - Not only called from standalone mode, i.e. has
56  *       a "calledfrom" argument
57  * should assert this at the beginning:
58  *
59  *    ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
60  *        called_from == CALLED_FROM_STANDALONE);
61  */
62 
63 #include <sys/types.h>
64 #include <sys/ascii.h>
65 #include <sys/visual_io.h>
66 #include <sys/font.h>
67 #include <sys/tem.h>
68 #include <sys/tem_impl.h>
69 #include <sys/ksynch.h>
70 #include <sys/sysmacros.h>
71 #include <sys/mutex.h>
72 #include <sys/note.h>
73 #include <sys/t_lock.h>
74 
75 tem_safe_callbacks_t tem_safe_text_callbacks = {
76 	&tem_safe_text_display,
77 	&tem_safe_text_copy,
78 	&tem_safe_text_cursor,
79 	NULL,
80 	&tem_safe_text_cls
81 };
82 tem_safe_callbacks_t tem_safe_pix_callbacks = {
83 	&tem_safe_pix_display,
84 	&tem_safe_pix_copy,
85 	&tem_safe_pix_cursor,
86 	&tem_safe_pix_bit2pix,
87 	&tem_safe_pix_cls
88 };
89 
90 
91 static void	tem_safe_control(struct tem_vt_state *, uchar_t,
92 			cred_t *, enum called_from);
93 static void	tem_safe_setparam(struct tem_vt_state *, int, int);
94 static void	tem_safe_selgraph(struct tem_vt_state *);
95 static void	tem_safe_chkparam(struct tem_vt_state *, uchar_t,
96 			cred_t *, enum called_from);
97 static void	tem_safe_getparams(struct tem_vt_state *, uchar_t,
98 			cred_t *, enum called_from);
99 static void	tem_safe_outch(struct tem_vt_state *, uchar_t,
100 			cred_t *, enum called_from);
101 static void	tem_safe_parse(struct tem_vt_state *, uchar_t,
102 			cred_t *, enum called_from);
103 
104 static void	tem_safe_new_line(struct tem_vt_state *,
105 			cred_t *, enum called_from);
106 static void	tem_safe_cr(struct tem_vt_state *);
107 static void	tem_safe_lf(struct tem_vt_state *,
108 			cred_t *, enum called_from);
109 static void	tem_safe_send_data(struct tem_vt_state *, cred_t *,
110 			enum called_from);
111 static void	tem_safe_cls(struct tem_vt_state *,
112 			cred_t *, enum called_from);
113 static void	tem_safe_tab(struct tem_vt_state *,
114 			cred_t *, enum called_from);
115 static void	tem_safe_back_tab(struct tem_vt_state *,
116 			cred_t *, enum called_from);
117 static void	tem_safe_clear_tabs(struct tem_vt_state *, int);
118 static void	tem_safe_set_tab(struct tem_vt_state *);
119 static void	tem_safe_mv_cursor(struct tem_vt_state *, int, int,
120 			cred_t *, enum called_from);
121 static void	tem_safe_shift(struct tem_vt_state *, int, int,
122 			cred_t *, enum called_from);
123 static void	tem_safe_scroll(struct tem_vt_state *, int, int,
124 			int, int, cred_t *, enum called_from);
125 static void	tem_safe_clear_chars(struct tem_vt_state *tem,
126 			int count, screen_pos_t row, screen_pos_t col,
127 			cred_t *credp, enum called_from called_from);
128 static void	tem_safe_copy_area(struct tem_vt_state *tem,
129 			screen_pos_t s_col, screen_pos_t s_row,
130 			screen_pos_t e_col, screen_pos_t e_row,
131 			screen_pos_t t_col, screen_pos_t t_row,
132 			cred_t *credp, enum called_from called_from);
133 static void	tem_safe_image_display(struct tem_vt_state *, uchar_t *,
134 			int, int, screen_pos_t, screen_pos_t,
135 			cred_t *, enum called_from);
136 static void	tem_safe_bell(struct tem_vt_state *tem,
137 			enum called_from called_from);
138 static void	tem_safe_pix_clear_prom_output(struct tem_vt_state *tem,
139 			cred_t *credp, enum called_from called_from);
140 
141 static void	tem_safe_virtual_cls(struct tem_vt_state *, int, screen_pos_t,
142 		    screen_pos_t);
143 static void	tem_safe_virtual_display(struct tem_vt_state *,
144 		    unsigned char *, int, screen_pos_t, screen_pos_t,
145 		    text_color_t, text_color_t);
146 static void	tem_safe_virtual_copy(struct tem_vt_state *, screen_pos_t,
147 		    screen_pos_t, screen_pos_t, screen_pos_t,
148 		    screen_pos_t, screen_pos_t);
149 static void	tem_safe_align_cursor(struct tem_vt_state *tem);
150 static void	bit_to_pix4(struct tem_vt_state *tem, uchar_t c,
151 		    text_color_t fg_color, text_color_t bg_color);
152 static void	bit_to_pix8(struct tem_vt_state *tem, uchar_t c,
153 		    text_color_t fg_color, text_color_t bg_color);
154 static void	bit_to_pix24(struct tem_vt_state *tem, uchar_t c,
155 		    text_color_t fg_color, text_color_t bg_color);
156 static void	bit_to_pix32(struct tem_vt_state *tem, uchar_t c,
157 		    text_color_t fg_color, text_color_t bg_color);
158 
159 /* BEGIN CSTYLED */
160 /*                                  Bk  Rd  Gr  Br  Bl  Mg  Cy  Wh */
161 static text_color_t dim_xlate[] = {  1,  5,  3,  7,  2,  6,  4,  8 };
162 static text_color_t brt_xlate[] = {  9, 13, 11, 15, 10, 14, 12,  0 };
163 /* END CSTYLED */
164 
165 
166 text_cmap_t cmap4_to_24 = {
167 /* BEGIN CSTYLED */
168 /* 0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15
169   Wh+  Bk   Bl   Gr   Cy   Rd   Mg   Br   Wh   Bk+  Bl+  Gr+  Cy+  Rd+  Mg+  Yw */
170   0xff,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x40,0x00,0x00,0x00,0xff,0xff,0xff,
171   0xff,0x00,0x00,0x80,0x80,0x00,0x00,0x80,0x80,0x40,0x00,0xff,0xff,0x00,0x00,0xff,
172   0xff,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x40,0xff,0x00,0xff,0x00,0xff,0x00
173 /* END CSTYLED */
174 };
175 
176 #define	PIX4TO32(pix4) (uint32_t)(  \
177     cmap4_to_24.red[pix4] << 16 |  \
178     cmap4_to_24.green[pix4] << 8 | \
179     cmap4_to_24.blue[pix4])
180 
181 #define	INVERSE(ch) (ch ^ 0xff)
182 
183 #define	tem_safe_callback_display	(*tems.ts_callbacks->tsc_display)
184 #define	tem_safe_callback_copy		(*tems.ts_callbacks->tsc_copy)
185 #define	tem_safe_callback_cursor	(*tems.ts_callbacks->tsc_cursor)
186 #define	tem_safe_callback_cls		(*tems.ts_callbacks->tsc_cls)
187 #define	tem_safe_callback_bit2pix(tem, c, fg, bg)	{		\
188 	ASSERT(tems.ts_callbacks->tsc_bit2pix != NULL);			\
189 	(void) (*tems.ts_callbacks->tsc_bit2pix)((tem), (c), (fg), (bg));\
190 }
191 
192 void
193 tem_safe_check_first_time(
194     struct tem_vt_state *tem,
195     cred_t *credp,
196     enum called_from called_from)
197 {
198 	static int first_time = 1;
199 
200 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
201 	    called_from == CALLED_FROM_STANDALONE);
202 
203 	/*
204 	 * Realign the console cursor. We did this in tem_init().
205 	 * However, drivers in the console stream may emit additional
206 	 * messages before we are ready. This causes text overwrite
207 	 * on the screen. This is a workaround.
208 	 */
209 	if (!first_time)
210 		return;
211 
212 	first_time = 0;
213 	if (tems.ts_display_mode == VIS_TEXT)
214 		tem_safe_text_cursor(tem, VIS_GET_CURSOR, credp, called_from);
215 	else
216 		tem_safe_pix_cursor(tem, VIS_GET_CURSOR, credp, called_from);
217 	tem_safe_align_cursor(tem);
218 }
219 
220 /*
221  * This entry point handles output requests from restricted contexts like
222  * kmdb, where services like mutexes are not available. This function
223  * is entered when OBP or when a kernel debugger (such as kmdb)
224  * are generating console output.  In those cases, power management
225  * concerns are handled by the abort sequence initiation (ie. when
226  * the user hits L1+A or the equivalent to enter OBP or the debugger.).
227  * It is also entered when the kernel is panicing.
228  */
229 void
230 tem_safe_polled_write(
231     tem_vt_state_t tem_arg,
232     uchar_t *buf,
233     int len)
234 {
235 	struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
236 
237 #ifdef	__lock_lint
238 	_NOTE(NO_COMPETING_THREADS_NOW)
239 	_NOTE(NO_COMPETING_THREADS_AS_SIDE_EFFECT)
240 #endif
241 
242 	if (!tem->tvs_initialized) {
243 		return;
244 	}
245 
246 	tem_safe_check_first_time(tem, kcred, CALLED_FROM_STANDALONE);
247 	tem_safe_terminal_emulate(tem, buf, len, NULL, CALLED_FROM_STANDALONE);
248 }
249 
250 /* Process partial UTF-8 sequence. */
251 static void
252 tem_safe_input_partial(struct tem_vt_state *tem, cred_t *credp,
253     enum called_from called_from)
254 {
255 	int i;
256 	uint8_t c;
257 
258 	if (tem->tvs_utf8_left == 0)
259 		return;
260 
261 	for (i = 0; i < sizeof (tem->tvs_utf8_partial); i++) {
262 		c = (tem->tvs_utf8_partial >> (24 - (i << 3))) & 0xff;
263 		if (c != 0) {
264 			tem_safe_parse(tem, c, credp, called_from);
265 		}
266 	}
267 	tem->tvs_utf8_left = 0;
268 	tem->tvs_utf8_partial = 0;
269 }
270 
271 /*
272  * Handle UTF-8 sequences.
273  */
274 static void
275 tem_safe_input_byte(struct tem_vt_state *tem, uchar_t c, cred_t *credp,
276     enum called_from called_from)
277 {
278 	/*
279 	 * Check for UTF-8 code points. In case of error fall back to
280 	 * 8-bit code. As we only have 8859-1 fonts for console, this will set
281 	 * the limits on what chars we actually can display, therefore we
282 	 * have to return to this code once we have solved the font issue.
283 	 */
284 	if ((c & 0x80) == 0x00) {
285 		/* One-byte sequence. */
286 		tem_safe_input_partial(tem, credp, called_from);
287 		tem_safe_parse(tem, c, credp, called_from);
288 		return;
289 	}
290 	if ((c & 0xe0) == 0xc0) {
291 		/* Two-byte sequence. */
292 		tem_safe_input_partial(tem, credp, called_from);
293 		tem->tvs_utf8_left = 1;
294 		tem->tvs_utf8_partial = c;
295 		return;
296 	}
297 	if ((c & 0xf0) == 0xe0) {
298 		/* Three-byte sequence. */
299 		tem_safe_input_partial(tem, credp, called_from);
300 		tem->tvs_utf8_left = 2;
301 		tem->tvs_utf8_partial = c;
302 		return;
303 	}
304 	if ((c & 0xf8) == 0xf0) {
305 		/* Four-byte sequence. */
306 		tem_safe_input_partial(tem, credp, called_from);
307 		tem->tvs_utf8_left = 3;
308 		tem->tvs_utf8_partial = c;
309 		return;
310 	}
311 	if ((c & 0xc0) == 0x80) {
312 		/* Invalid state? */
313 		if (tem->tvs_utf8_left == 0) {
314 			tem_safe_parse(tem, c, credp, called_from);
315 			return;
316 		}
317 		tem->tvs_utf8_left--;
318 		tem->tvs_utf8_partial = (tem->tvs_utf8_partial << 8) | c;
319 		if (tem->tvs_utf8_left == 0) {
320 			tem_char_t v, u;
321 			uint8_t b;
322 
323 			/*
324 			 * Transform the sequence of 2 to 4 bytes to
325 			 * unicode number.
326 			 */
327 			v = 0;
328 			u = tem->tvs_utf8_partial;
329 			b = (u >> 24) & 0xff;
330 			if (b != 0) {		/* Four-byte sequence */
331 				v = b & 0x07;
332 				b = (u >> 16) & 0xff;
333 				v = (v << 6) | (b & 0x3f);
334 				b = (u >> 8) & 0xff;
335 				v = (v << 6) | (b & 0x3f);
336 				b = u & 0xff;
337 				v = (v << 6) | (b & 0x3f);
338 			} else if ((b = (u >> 16) & 0xff) != 0) {
339 				v = b & 0x0f;	/* Three-byte sequence */
340 				b = (u >> 8) & 0xff;
341 				v = (v << 6) | (b & 0x3f);
342 				b = u & 0xff;
343 				v = (v << 6) | (b & 0x3f);
344 			} else if ((b = (u >> 8) & 0xff) != 0) {
345 				v = b & 0x1f;	/* Two-byte sequence */
346 				b = u & 0xff;
347 				v = (v << 6) | (b & 0x3f);
348 			}
349 
350 			/* Use '?' as replacement if needed. */
351 			if (v > 0xff)
352 				v = '?';
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)
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)
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, uchar_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 /*
539  * select graphics mode based on the param vals stored in a_params
540  */
541 static void
542 tem_safe_selgraph(struct tem_vt_state *tem)
543 {
544 	int curparam;
545 	int count = 0;
546 	int param;
547 
548 	tem->tvs_state = A_STATE_START;
549 
550 	curparam = tem->tvs_curparam;
551 	do {
552 		param = tem->tvs_params[count];
553 
554 		switch (param) {
555 		case -1:
556 		case 0:
557 			/* reset to initial normal settings */
558 			tem->tvs_fg_color = tems.ts_init_color.fg_color;
559 			tem->tvs_bg_color = tems.ts_init_color.bg_color;
560 			tem->tvs_flags = tems.ts_init_color.a_flags;
561 			break;
562 
563 		case 1: /* Bold Intense */
564 			tem->tvs_flags |= TEM_ATTR_BOLD;
565 			break;
566 
567 		case 2: /* Faint Intense */
568 			tem->tvs_flags &= ~TEM_ATTR_BOLD;
569 			break;
570 
571 		case 5: /* Blink */
572 			tem->tvs_flags |= TEM_ATTR_BLINK;
573 			break;
574 
575 		case 7: /* Reverse video */
576 			if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
577 				tem->tvs_flags &= ~TEM_ATTR_REVERSE;
578 			} else {
579 				tem->tvs_flags |= TEM_ATTR_REVERSE;
580 			}
581 			break;
582 
583 		case 30: /* black	(grey)		foreground */
584 		case 31: /* red		(light red)	foreground */
585 		case 32: /* green	(light green)	foreground */
586 		case 33: /* brown	(yellow)	foreground */
587 		case 34: /* blue	(light blue)	foreground */
588 		case 35: /* magenta	(light magenta)	foreground */
589 		case 36: /* cyan	(light cyan)	foreground */
590 		case 37: /* white	(bright white)	foreground */
591 			tem->tvs_fg_color = param - 30;
592 			tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
593 			break;
594 
595 		case 39:
596 			/*
597 			 * Reset the foreground colour and brightness.
598 			 */
599 			tem->tvs_fg_color = tems.ts_init_color.fg_color;
600 			if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_FG)
601 				tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
602 			else
603 				tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
604 			break;
605 
606 		case 40: /* black	(grey)		background */
607 		case 41: /* red		(light red)	background */
608 		case 42: /* green	(light green)	background */
609 		case 43: /* brown	(yellow)	background */
610 		case 44: /* blue	(light blue)	background */
611 		case 45: /* magenta	(light magenta)	background */
612 		case 46: /* cyan	(light cyan)	background */
613 		case 47: /* white	(bright white)	background */
614 			tem->tvs_bg_color = param - 40;
615 			tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
616 			break;
617 
618 		case 49:
619 			/*
620 			 * Reset the background colour and brightness.
621 			 */
622 			tem->tvs_bg_color = tems.ts_init_color.bg_color;
623 			if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_BG)
624 				tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
625 			else
626 				tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
627 			break;
628 
629 		case 90: /* black	(grey)		foreground */
630 		case 91: /* red		(light red)	foreground */
631 		case 92: /* green	(light green)	foreground */
632 		case 93: /* brown	(yellow)	foreground */
633 		case 94: /* blue	(light blue)	foreground */
634 		case 95: /* magenta	(light magenta)	foreground */
635 		case 96: /* cyan	(light cyan)	foreground */
636 		case 97: /* white	(bright white)	foreground */
637 			tem->tvs_fg_color = param - 90;
638 			tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
639 			break;
640 
641 		case 100: /* black	(grey)		background */
642 		case 101: /* red	(light red)	background */
643 		case 102: /* green	(light green)	background */
644 		case 103: /* brown	(yellow)	background */
645 		case 104: /* blue	(light blue)	background */
646 		case 105: /* magenta	(light magenta)	background */
647 		case 106: /* cyan	(light cyan)	background */
648 		case 107: /* white	(bright white)	background */
649 			tem->tvs_bg_color = param - 100;
650 			tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
651 			break;
652 
653 		default:
654 			break;
655 		}
656 		count++;
657 		curparam--;
658 
659 	} while (curparam > 0);
660 }
661 
662 /*
663  * perform the appropriate action for the escape sequence
664  *
665  * General rule:  This code does not validate the arguments passed.
666  *                It assumes that the next lower level will do so.
667  */
668 static void
669 tem_safe_chkparam(struct tem_vt_state *tem, uchar_t ch, cred_t *credp,
670     enum called_from called_from)
671 {
672 	int	i;
673 	int	row;
674 	int	col;
675 
676 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
677 	    MUTEX_HELD(&tem->tvs_lock));
678 
679 	row = tem->tvs_c_cursor.row;
680 	col = tem->tvs_c_cursor.col;
681 
682 	switch (ch) {
683 
684 	case 'm': /* select terminal graphics mode */
685 		tem_safe_send_data(tem, credp, called_from);
686 		tem_safe_selgraph(tem);
687 		break;
688 
689 	case '@':		/* insert char */
690 		tem_safe_setparam(tem, 1, 1);
691 		tem_safe_shift(tem, tem->tvs_params[0], TEM_SHIFT_RIGHT,
692 		    credp, called_from);
693 		break;
694 
695 	case 'A':		/* cursor up */
696 		tem_safe_setparam(tem, 1, 1);
697 		tem_safe_mv_cursor(tem, row - tem->tvs_params[0], col,
698 		    credp, called_from);
699 		break;
700 
701 	case 'd':		/* VPA - vertical position absolute */
702 		tem_safe_setparam(tem, 1, 1);
703 		tem_safe_mv_cursor(tem, tem->tvs_params[0] - 1, col,
704 		    credp, called_from);
705 		break;
706 
707 	case 'e':		/* VPR - vertical position relative */
708 	case 'B':		/* cursor down */
709 		tem_safe_setparam(tem, 1, 1);
710 		tem_safe_mv_cursor(tem, row + tem->tvs_params[0], col,
711 		    credp, called_from);
712 		break;
713 
714 	case 'a':		/* HPR - horizontal position relative */
715 	case 'C':		/* cursor right */
716 		tem_safe_setparam(tem, 1, 1);
717 		tem_safe_mv_cursor(tem, row, col + tem->tvs_params[0],
718 		    credp, called_from);
719 		break;
720 
721 	case '`':		/* HPA - horizontal position absolute */
722 		tem_safe_setparam(tem, 1, 1);
723 		tem_safe_mv_cursor(tem, row, tem->tvs_params[0] - 1,
724 		    credp, called_from);
725 		break;
726 
727 	case 'D':		/* cursor left */
728 		tem_safe_setparam(tem, 1, 1);
729 		tem_safe_mv_cursor(tem, row, col - tem->tvs_params[0],
730 		    credp, called_from);
731 		break;
732 
733 	case 'E':		/* CNL cursor next line */
734 		tem_safe_setparam(tem, 1, 1);
735 		tem_safe_mv_cursor(tem, row + tem->tvs_params[0], 0,
736 		    credp, called_from);
737 		break;
738 
739 	case 'F':		/* CPL cursor previous line */
740 		tem_safe_setparam(tem, 1, 1);
741 		tem_safe_mv_cursor(tem, row - tem->tvs_params[0], 0,
742 		    credp, called_from);
743 		break;
744 
745 	case 'G':		/* cursor horizontal position */
746 		tem_safe_setparam(tem, 1, 1);
747 		tem_safe_mv_cursor(tem, row, tem->tvs_params[0] - 1,
748 		    credp, called_from);
749 		break;
750 
751 	case 'g':		/* clear tabs */
752 		tem_safe_setparam(tem, 1, 0);
753 		tem_safe_clear_tabs(tem, tem->tvs_params[0]);
754 		break;
755 
756 	case 'f':		/* HVP Horizontal and Vertical Position */
757 	case 'H':		/* CUP position cursor */
758 		tem_safe_setparam(tem, 2, 1);
759 		tem_safe_mv_cursor(tem,
760 		    tem->tvs_params[0] - 1,
761 		    tem->tvs_params[1] - 1,
762 		    credp, called_from);
763 		break;
764 
765 	case 'I':		/* CHT - Cursor Horizontal Tab */
766 		/* Not implemented */
767 		break;
768 
769 	case 'J':		/* ED - Erase in Display */
770 		tem_safe_send_data(tem, credp, called_from);
771 		tem_safe_setparam(tem, 1, 0);
772 		switch (tem->tvs_params[0]) {
773 		case 0:
774 			/* erase cursor to end of screen */
775 			/* FIRST erase cursor to end of line */
776 			tem_safe_clear_chars(tem,
777 			    tems.ts_c_dimension.width -
778 			    tem->tvs_c_cursor.col,
779 			    tem->tvs_c_cursor.row,
780 			    tem->tvs_c_cursor.col, credp, called_from);
781 
782 			/* THEN erase lines below the cursor */
783 			for (row = tem->tvs_c_cursor.row + 1;
784 			    row < tems.ts_c_dimension.height;
785 			    row++) {
786 				tem_safe_clear_chars(tem,
787 				    tems.ts_c_dimension.width,
788 				    row, 0, credp, called_from);
789 			}
790 			break;
791 
792 		case 1:
793 			/* erase beginning of screen to cursor */
794 			/* FIRST erase lines above the cursor */
795 			for (row = 0;
796 			    row < tem->tvs_c_cursor.row;
797 			    row++) {
798 				tem_safe_clear_chars(tem,
799 				    tems.ts_c_dimension.width,
800 				    row, 0, credp, called_from);
801 			}
802 			/* THEN erase beginning of line to cursor */
803 			tem_safe_clear_chars(tem,
804 			    tem->tvs_c_cursor.col + 1,
805 			    tem->tvs_c_cursor.row,
806 			    0, credp, called_from);
807 			break;
808 
809 		case 2:
810 			/* erase whole screen */
811 			for (row = 0;
812 			    row < tems.ts_c_dimension.height;
813 			    row++) {
814 				tem_safe_clear_chars(tem,
815 				    tems.ts_c_dimension.width,
816 				    row, 0, credp, called_from);
817 			}
818 			break;
819 		}
820 		break;
821 
822 	case 'K':		/* EL - Erase in Line */
823 		tem_safe_send_data(tem, credp, called_from);
824 		tem_safe_setparam(tem, 1, 0);
825 		switch (tem->tvs_params[0]) {
826 		case 0:
827 			/* erase cursor to end of line */
828 			tem_safe_clear_chars(tem,
829 			    (tems.ts_c_dimension.width -
830 			    tem->tvs_c_cursor.col),
831 			    tem->tvs_c_cursor.row,
832 			    tem->tvs_c_cursor.col,
833 			    credp, called_from);
834 			break;
835 
836 		case 1:
837 			/* erase beginning of line to cursor */
838 			tem_safe_clear_chars(tem,
839 			    tem->tvs_c_cursor.col + 1,
840 			    tem->tvs_c_cursor.row,
841 			    0, credp, called_from);
842 			break;
843 
844 		case 2:
845 			/* erase whole line */
846 			tem_safe_clear_chars(tem,
847 			    tems.ts_c_dimension.width,
848 			    tem->tvs_c_cursor.row,
849 			    0, credp, called_from);
850 			break;
851 		}
852 		break;
853 
854 	case 'L':		/* insert line */
855 		tem_safe_send_data(tem, credp, called_from);
856 		tem_safe_setparam(tem, 1, 1);
857 		tem_safe_scroll(tem,
858 		    tem->tvs_c_cursor.row,
859 		    tems.ts_c_dimension.height - 1,
860 		    tem->tvs_params[0], TEM_SCROLL_DOWN,
861 		    credp, called_from);
862 		break;
863 
864 	case 'M':		/* delete line */
865 		tem_safe_send_data(tem, credp, called_from);
866 		tem_safe_setparam(tem, 1, 1);
867 		tem_safe_scroll(tem,
868 		    tem->tvs_c_cursor.row,
869 		    tems.ts_c_dimension.height - 1,
870 		    tem->tvs_params[0], TEM_SCROLL_UP,
871 		    credp, called_from);
872 		break;
873 
874 	case 'P':		/* DCH - delete char */
875 		tem_safe_setparam(tem, 1, 1);
876 		tem_safe_shift(tem, tem->tvs_params[0], TEM_SHIFT_LEFT,
877 		    credp, called_from);
878 		break;
879 
880 	case 'S':		/* scroll up */
881 		tem_safe_send_data(tem, credp, called_from);
882 		tem_safe_setparam(tem, 1, 1);
883 		tem_safe_scroll(tem, 0,
884 		    tems.ts_c_dimension.height - 1,
885 		    tem->tvs_params[0], TEM_SCROLL_UP,
886 		    credp, called_from);
887 		break;
888 
889 	case 'T':		/* scroll down */
890 		tem_safe_send_data(tem, credp, called_from);
891 		tem_safe_setparam(tem, 1, 1);
892 		tem_safe_scroll(tem, 0,
893 		    tems.ts_c_dimension.height - 1,
894 		    tem->tvs_params[0], TEM_SCROLL_DOWN,
895 		    credp, called_from);
896 		break;
897 
898 	case 'X':		/* erase char */
899 		tem_safe_setparam(tem, 1, 1);
900 		tem_safe_clear_chars(tem,
901 		    tem->tvs_params[0],
902 		    tem->tvs_c_cursor.row,
903 		    tem->tvs_c_cursor.col,
904 		    credp, called_from);
905 		break;
906 
907 	case 'Z':		/* cursor backward tabulation */
908 		tem_safe_setparam(tem, 1, 1);
909 
910 		/*
911 		 * Rule exception - We do sanity checking here.
912 		 *
913 		 * Restrict the count to a sane value to keep from
914 		 * looping for a long time.  There can't be more than one
915 		 * tab stop per column, so use that as a limit.
916 		 */
917 		if (tem->tvs_params[0] > tems.ts_c_dimension.width)
918 			tem->tvs_params[0] = tems.ts_c_dimension.width;
919 
920 		for (i = 0; i < tem->tvs_params[0]; i++)
921 			tem_safe_back_tab(tem, credp, called_from);
922 		break;
923 	}
924 	tem->tvs_state = A_STATE_START;
925 }
926 
927 
928 /*
929  * Gather the parameters of an ANSI escape sequence
930  */
931 static void
932 tem_safe_getparams(struct tem_vt_state *tem, uchar_t ch,
933     cred_t *credp, enum called_from called_from)
934 {
935 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
936 	    MUTEX_HELD(&tem->tvs_lock));
937 
938 	if (ch >= '0' && ch <= '9') {
939 		tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0'));
940 		tem->tvs_gotparam = B_TRUE;  /* Remember got parameter */
941 		return; /* Return immediately */
942 	} else if (tem->tvs_state == A_STATE_CSI_EQUAL ||
943 	    tem->tvs_state == A_STATE_CSI_QMARK) {
944 		tem->tvs_state = A_STATE_START;
945 	} else {
946 		if (tem->tvs_curparam < TEM_MAXPARAMS) {
947 			if (tem->tvs_gotparam) {
948 				/* get the parameter value */
949 				tem->tvs_params[tem->tvs_curparam] =
950 				    tem->tvs_paramval;
951 			}
952 			tem->tvs_curparam++;
953 		}
954 
955 		if (ch == ';') {
956 			/* Restart parameter search */
957 			tem->tvs_gotparam = B_FALSE;
958 			tem->tvs_paramval = 0; /* No parame value yet */
959 		} else {
960 			/* Handle escape sequence */
961 			tem_safe_chkparam(tem, ch, credp, called_from);
962 		}
963 	}
964 }
965 
966 /*
967  * Add character to internal buffer.
968  * When its full, send it to the next layer.
969  */
970 
971 static void
972 tem_safe_outch(struct tem_vt_state *tem, uchar_t ch,
973     cred_t *credp, enum called_from called_from)
974 {
975 
976 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
977 	    called_from == CALLED_FROM_STANDALONE);
978 
979 	/* buffer up the character until later */
980 
981 	tem->tvs_outbuf[tem->tvs_outindex++] = ch;
982 	tem->tvs_c_cursor.col++;
983 	if (tem->tvs_c_cursor.col >= tems.ts_c_dimension.width) {
984 		tem_safe_send_data(tem, credp, called_from);
985 		tem_safe_new_line(tem, credp, called_from);
986 	}
987 }
988 
989 static void
990 tem_safe_new_line(struct tem_vt_state *tem,
991     cred_t *credp, enum called_from called_from)
992 {
993 	tem_safe_cr(tem);
994 	tem_safe_lf(tem, credp, called_from);
995 }
996 
997 static void
998 tem_safe_cr(struct tem_vt_state *tem)
999 {
1000 	tem->tvs_c_cursor.col = 0;
1001 	tem_safe_align_cursor(tem);
1002 }
1003 
1004 static void
1005 tem_safe_lf(struct tem_vt_state *tem,
1006     cred_t *credp, enum called_from called_from)
1007 {
1008 	int row;
1009 
1010 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1011 	    MUTEX_HELD(&tem->tvs_lock));
1012 
1013 	/*
1014 	 * Sanity checking notes:
1015 	 * . a_nscroll was validated when it was set.
1016 	 * . Regardless of that, tem_safe_scroll and tem_safe_mv_cursor
1017 	 *   will prevent anything bad from happening.
1018 	 */
1019 	row = tem->tvs_c_cursor.row + 1;
1020 
1021 	if (row >= tems.ts_c_dimension.height) {
1022 		if (tem->tvs_nscroll != 0) {
1023 			tem_safe_scroll(tem, 0,
1024 			    tems.ts_c_dimension.height - 1,
1025 			    tem->tvs_nscroll, TEM_SCROLL_UP,
1026 			    credp, called_from);
1027 			row = tems.ts_c_dimension.height -
1028 			    tem->tvs_nscroll;
1029 		} else {	/* no scroll */
1030 			/*
1031 			 * implement Esc[#r when # is zero.  This means no
1032 			 * scroll but just return cursor to top of screen,
1033 			 * do not clear screen.
1034 			 */
1035 			row = 0;
1036 		}
1037 	}
1038 
1039 	tem_safe_mv_cursor(tem, row, tem->tvs_c_cursor.col,
1040 	    credp, called_from);
1041 
1042 	if (tem->tvs_nscroll == 0) {
1043 		/* erase rest of cursor line */
1044 		tem_safe_clear_chars(tem,
1045 		    tems.ts_c_dimension.width -
1046 		    tem->tvs_c_cursor.col,
1047 		    tem->tvs_c_cursor.row,
1048 		    tem->tvs_c_cursor.col,
1049 		    credp, called_from);
1050 
1051 	}
1052 
1053 	tem_safe_align_cursor(tem);
1054 }
1055 
1056 static void
1057 tem_safe_send_data(struct tem_vt_state *tem, cred_t *credp,
1058     enum called_from called_from)
1059 {
1060 	text_color_t fg_color;
1061 	text_color_t bg_color;
1062 
1063 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1064 	    MUTEX_HELD(&tem->tvs_lock));
1065 
1066 	if (tem->tvs_outindex == 0) {
1067 		tem_safe_align_cursor(tem);
1068 		return;
1069 	}
1070 
1071 	tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_REVERSE);
1072 	tem_safe_virtual_display(tem,
1073 	    tem->tvs_outbuf, tem->tvs_outindex,
1074 	    tem->tvs_s_cursor.row, tem->tvs_s_cursor.col,
1075 	    fg_color, bg_color);
1076 
1077 	if (tem->tvs_isactive) {
1078 		/*
1079 		 * Call the primitive to render this data.
1080 		 */
1081 		tem_safe_callback_display(tem,
1082 		    tem->tvs_outbuf, tem->tvs_outindex,
1083 		    tem->tvs_s_cursor.row, tem->tvs_s_cursor.col,
1084 		    fg_color, bg_color,
1085 		    credp, called_from);
1086 	}
1087 
1088 	tem->tvs_outindex = 0;
1089 
1090 	tem_safe_align_cursor(tem);
1091 }
1092 
1093 
1094 /*
1095  * We have just done something to the current output point.  Reset the start
1096  * point for the buffered data in a_outbuf.  There shouldn't be any data
1097  * buffered yet.
1098  */
1099 static void
1100 tem_safe_align_cursor(struct tem_vt_state *tem)
1101 {
1102 	tem->tvs_s_cursor.row = tem->tvs_c_cursor.row;
1103 	tem->tvs_s_cursor.col = tem->tvs_c_cursor.col;
1104 }
1105 
1106 /*
1107  * State machine parser based on the current state and character input
1108  * major terminations are to control character or normal character
1109  */
1110 
1111 static void
1112 tem_safe_parse(struct tem_vt_state *tem, uchar_t ch,
1113     cred_t *credp, enum called_from called_from)
1114 {
1115 	int	i;
1116 
1117 	ASSERT((called_from == CALLED_FROM_STANDALONE) ||
1118 	    MUTEX_HELD(&tem->tvs_lock));
1119 
1120 	if (tem->tvs_state == A_STATE_START) {	/* Normal state? */
1121 		if (ch == A_CSI || ch == A_ESC || ch < ' ') {
1122 			/* Control */
1123 			tem_safe_control(tem, ch, credp, called_from);
1124 		} else {
1125 			/* Display */
1126 			tem_safe_outch(tem, ch, credp, called_from);
1127 		}
1128 		return;
1129 	}
1130 
1131 	/* In <ESC> sequence */
1132 	if (tem->tvs_state != A_STATE_ESC) {	/* Need to get parameters? */
1133 		if (tem->tvs_state != A_STATE_CSI) {
1134 			tem_safe_getparams(tem, ch, credp, called_from);
1135 			return;
1136 		}
1137 
1138 		switch (ch) {
1139 		case '?':
1140 			tem->tvs_state = A_STATE_CSI_QMARK;
1141 			return;
1142 		case '=':
1143 			tem->tvs_state = A_STATE_CSI_EQUAL;
1144 			return;
1145 		case 's':
1146 			/*
1147 			 * As defined below, this sequence
1148 			 * saves the cursor.  However, Sun
1149 			 * defines ESC[s as reset.  We resolved
1150 			 * the conflict by selecting reset as it
1151 			 * is exported in the termcap file for
1152 			 * sun-mon, while the "save cursor"
1153 			 * definition does not exist anywhere in
1154 			 * /etc/termcap.
1155 			 * However, having no coherent
1156 			 * definition of reset, we have not
1157 			 * implemented it.
1158 			 */
1159 
1160 			/*
1161 			 * Original code
1162 			 * tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1163 			 * tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1164 			 * tem->tvs_state = A_STATE_START;
1165 			 */
1166 
1167 			tem->tvs_state = A_STATE_START;
1168 			return;
1169 		case 'u':
1170 			tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row,
1171 			    tem->tvs_r_cursor.col, credp, called_from);
1172 			tem->tvs_state = A_STATE_START;
1173 			return;
1174 		case 'p':	/* sunbow */
1175 			tem_safe_send_data(tem, credp, called_from);
1176 			/*
1177 			 * Don't set anything if we are
1178 			 * already as we want to be.
1179 			 */
1180 			if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
1181 				tem->tvs_flags &= ~TEM_ATTR_SCREEN_REVERSE;
1182 				/*
1183 				 * If we have switched the characters to be the
1184 				 * inverse from the screen, then switch them as
1185 				 * well to keep them the inverse of the screen.
1186 				 */
1187 				if (tem->tvs_flags & TEM_ATTR_REVERSE)
1188 					tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1189 				else
1190 					tem->tvs_flags |= TEM_ATTR_REVERSE;
1191 			}
1192 			tem_safe_cls(tem, credp, called_from);
1193 			tem->tvs_state = A_STATE_START;
1194 			return;
1195 		case 'q':	/* sunwob */
1196 			tem_safe_send_data(tem, credp, called_from);
1197 			/*
1198 			 * Don't set anything if we are
1199 			 * already where as we want to be.
1200 			 */
1201 			if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) {
1202 				tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE;
1203 				/*
1204 				 * If we have switched the characters to be the
1205 				 * inverse from the screen, then switch them as
1206 				 * well to keep them the inverse of the screen.
1207 				 */
1208 				if (!(tem->tvs_flags & TEM_ATTR_REVERSE))
1209 					tem->tvs_flags |= TEM_ATTR_REVERSE;
1210 				else
1211 					tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1212 			}
1213 
1214 			tem_safe_cls(tem, credp, called_from);
1215 			tem->tvs_state = A_STATE_START;
1216 			return;
1217 		case 'r':	/* sunscrl */
1218 			/*
1219 			 * Rule exception:  check for validity here.
1220 			 */
1221 			tem->tvs_nscroll = tem->tvs_paramval;
1222 			if (tem->tvs_nscroll > tems.ts_c_dimension.height)
1223 				tem->tvs_nscroll = tems.ts_c_dimension.height;
1224 			if (tem->tvs_nscroll < 0)
1225 				tem->tvs_nscroll = 1;
1226 			tem->tvs_state = A_STATE_START;
1227 			return;
1228 		default:
1229 			tem_safe_getparams(tem, ch, credp, called_from);
1230 			return;
1231 		}
1232 	}
1233 
1234 	/* Previous char was <ESC> */
1235 	if (ch == '[') {
1236 		tem->tvs_curparam = 0;
1237 		tem->tvs_paramval = 0;
1238 		tem->tvs_gotparam = B_FALSE;
1239 		/* clear the parameters */
1240 		for (i = 0; i < TEM_MAXPARAMS; i++)
1241 			tem->tvs_params[i] = -1;
1242 		tem->tvs_state = A_STATE_CSI;
1243 	} else if (ch == 'Q') {	/* <ESC>Q ? */
1244 		tem->tvs_state = A_STATE_START;
1245 	} else if (ch == 'C') {	/* <ESC>C ? */
1246 		tem->tvs_state = A_STATE_START;
1247 	} else {
1248 		tem->tvs_state = A_STATE_START;
1249 		if (ch == 'c') {
1250 			/* ESC c resets display */
1251 			tem_safe_reset_display(tem, credp, called_from,
1252 			    B_TRUE, B_TRUE);
1253 		} else if (ch == 'H') {
1254 			/* ESC H sets a tab */
1255 			tem_safe_set_tab(tem);
1256 		} else if (ch == '7') {
1257 			/* ESC 7 Save Cursor position */
1258 			tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1259 			tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1260 		} else if (ch == '8') {
1261 			/* ESC 8 Restore Cursor position */
1262 			tem_safe_mv_cursor(tem, tem->tvs_r_cursor.row,
1263 			    tem->tvs_r_cursor.col, credp, called_from);
1264 		/* check for control chars */
1265 		} else if (ch < ' ') {
1266 			tem_safe_control(tem, ch, credp, called_from);
1267 		} else {
1268 			tem_safe_outch(tem, ch, credp, called_from);
1269 		}
1270 	}
1271 }
1272 
1273 /* ARGSUSED */
1274 static void
1275 tem_safe_bell(struct tem_vt_state *tem, enum called_from called_from)
1276 {
1277 	if (called_from == CALLED_FROM_STANDALONE)
1278 		(void) beep_polled(BEEP_CONSOLE);
1279 	else
1280 		(void) beep(BEEP_CONSOLE);
1281 }
1282 
1283 
1284 static void
1285 tem_safe_scroll(struct tem_vt_state *tem, int start, int end, int count,
1286     int direction, cred_t *credp, enum called_from called_from)
1287 {
1288 	int	row;
1289 	int	lines_affected;
1290 
1291 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1292 	    called_from == CALLED_FROM_STANDALONE);
1293 
1294 	lines_affected = end - start + 1;
1295 	if (count > lines_affected)
1296 		count = lines_affected;
1297 	if (count <= 0)
1298 		return;
1299 
1300 	switch (direction) {
1301 	case TEM_SCROLL_UP:
1302 		if (count < lines_affected) {
1303 			tem_safe_copy_area(tem, 0, start + count,
1304 			    tems.ts_c_dimension.width - 1, end,
1305 			    0, start, credp, called_from);
1306 		}
1307 		for (row = (end - count) + 1; row <= end; row++) {
1308 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1309 			    row, 0, credp, called_from);
1310 		}
1311 		break;
1312 
1313 	case TEM_SCROLL_DOWN:
1314 		if (count < lines_affected) {
1315 			tem_safe_copy_area(tem, 0, start,
1316 			    tems.ts_c_dimension.width - 1,
1317 			    end - count, 0, start + count,
1318 			    credp, called_from);
1319 		}
1320 		for (row = start; row < start + count; row++) {
1321 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1322 			    row, 0, credp, called_from);
1323 		}
1324 		break;
1325 	}
1326 }
1327 
1328 static void
1329 tem_safe_copy_area(struct tem_vt_state *tem,
1330     screen_pos_t s_col, screen_pos_t s_row,
1331     screen_pos_t e_col, screen_pos_t e_row,
1332     screen_pos_t t_col, screen_pos_t t_row,
1333     cred_t *credp, enum called_from called_from)
1334 {
1335 	int rows;
1336 	int cols;
1337 
1338 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1339 	    called_from == CALLED_FROM_STANDALONE);
1340 
1341 	if (s_col < 0 || s_row < 0 ||
1342 	    e_col < 0 || e_row < 0 ||
1343 	    t_col < 0 || t_row < 0 ||
1344 	    s_col >= tems.ts_c_dimension.width ||
1345 	    e_col >= tems.ts_c_dimension.width ||
1346 	    t_col >= tems.ts_c_dimension.width ||
1347 	    s_row >= tems.ts_c_dimension.height ||
1348 	    e_row >= tems.ts_c_dimension.height ||
1349 	    t_row >= tems.ts_c_dimension.height)
1350 		return;
1351 
1352 	if (s_row > e_row || s_col > e_col)
1353 		return;
1354 
1355 	rows = e_row - s_row + 1;
1356 	cols = e_col - s_col + 1;
1357 	if (t_row + rows > tems.ts_c_dimension.height ||
1358 	    t_col + cols > tems.ts_c_dimension.width)
1359 		return;
1360 
1361 	tem_safe_virtual_copy(tem,
1362 	    s_col, s_row,
1363 	    e_col, e_row,
1364 	    t_col, t_row);
1365 
1366 	if (!tem->tvs_isactive)
1367 		return;
1368 
1369 	tem_safe_callback_copy(tem, s_col, s_row,
1370 	    e_col, e_row, t_col, t_row, credp, called_from);
1371 }
1372 
1373 static void
1374 tem_safe_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row,
1375     screen_pos_t col, cred_t *credp, enum called_from called_from)
1376 {
1377 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1378 	    called_from == CALLED_FROM_STANDALONE);
1379 
1380 	if (row < 0 || row >= tems.ts_c_dimension.height ||
1381 	    col < 0 || col >= tems.ts_c_dimension.width ||
1382 	    count < 0)
1383 		return;
1384 
1385 	/*
1386 	 * Note that very large values of "count" could cause col+count
1387 	 * to overflow, so we check "count" independently.
1388 	 */
1389 	if (count > tems.ts_c_dimension.width ||
1390 	    col + count > tems.ts_c_dimension.width)
1391 		count = tems.ts_c_dimension.width - col;
1392 
1393 	tem_safe_virtual_cls(tem, count, row, col);
1394 
1395 	if (!tem->tvs_isactive)
1396 		return;
1397 
1398 	tem_safe_callback_cls(tem, count, row, col, credp, called_from);
1399 }
1400 
1401 /*ARGSUSED*/
1402 void
1403 tem_safe_text_display(struct tem_vt_state *tem, uchar_t *string,
1404     int count, screen_pos_t row, screen_pos_t col,
1405     text_color_t fg_color, text_color_t bg_color,
1406     cred_t *credp, enum called_from called_from)
1407 {
1408 	struct vis_consdisplay da;
1409 
1410 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1411 	    called_from == CALLED_FROM_STANDALONE);
1412 
1413 	da.data = string;
1414 	da.width = (screen_size_t)count;
1415 	da.row = row;
1416 	da.col = col;
1417 
1418 	da.fg_color = fg_color;
1419 	da.bg_color = bg_color;
1420 
1421 	tems_safe_display(&da, credp, called_from);
1422 }
1423 
1424 /*
1425  * This function is used to blit a rectangular color image,
1426  * unperturbed on the underlying framebuffer, to render
1427  * icons and pictures.  The data is a pixel pattern that
1428  * fills a rectangle bounded to the width and height parameters.
1429  * The color pixel data must to be pre-adjusted by the caller
1430  * for the current video depth.
1431  *
1432  * This function is unused now.
1433  */
1434 /*ARGSUSED*/
1435 static void
1436 tem_safe_image_display(struct tem_vt_state *tem, uchar_t *image,
1437     int height, int width, screen_pos_t row, screen_pos_t col,
1438     cred_t *credp, enum called_from called_from)
1439 {
1440 	struct vis_consdisplay da;
1441 
1442 	mutex_enter(&tems.ts_lock);
1443 	mutex_enter(&tem->tvs_lock);
1444 
1445 	da.data = image;
1446 	da.width = (screen_size_t)width;
1447 	da.height = (screen_size_t)height;
1448 	da.row = row;
1449 	da.col = col;
1450 
1451 	tems_safe_display(&da, credp, called_from);
1452 
1453 	mutex_exit(&tem->tvs_lock);
1454 	mutex_exit(&tems.ts_lock);
1455 }
1456 
1457 
1458 /*ARGSUSED*/
1459 void
1460 tem_safe_text_copy(struct tem_vt_state *tem,
1461     screen_pos_t s_col, screen_pos_t s_row,
1462     screen_pos_t e_col, screen_pos_t e_row,
1463     screen_pos_t t_col, screen_pos_t t_row,
1464     cred_t *credp, enum called_from called_from)
1465 {
1466 	struct vis_conscopy da;
1467 
1468 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1469 	    called_from == CALLED_FROM_STANDALONE);
1470 
1471 	da.s_row = s_row;
1472 	da.s_col = s_col;
1473 	da.e_row = e_row;
1474 	da.e_col = e_col;
1475 	da.t_row = t_row;
1476 	da.t_col = t_col;
1477 
1478 	tems_safe_copy(&da, credp, called_from);
1479 }
1480 
1481 void
1482 tem_safe_text_cls(struct tem_vt_state *tem,
1483     int count, screen_pos_t row, screen_pos_t col, cred_t *credp,
1484     enum called_from called_from)
1485 {
1486 	struct vis_consdisplay da;
1487 
1488 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1489 	    called_from == CALLED_FROM_STANDALONE);
1490 
1491 	da.data = tems.ts_blank_line;
1492 	da.width = (screen_size_t)count;
1493 	da.row = row;
1494 	da.col = col;
1495 
1496 	tem_safe_get_color(tem, &da.fg_color, &da.bg_color,
1497 	    TEM_ATTR_SCREEN_REVERSE);
1498 	tems_safe_display(&da, credp, called_from);
1499 }
1500 
1501 void
1502 tem_safe_pix_display(struct tem_vt_state *tem,
1503     uchar_t *string, int count,
1504     screen_pos_t row, screen_pos_t col,
1505     text_color_t fg_color, text_color_t bg_color,
1506     cred_t *credp, enum called_from called_from)
1507 {
1508 	struct vis_consdisplay da;
1509 	int	i;
1510 
1511 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1512 	    called_from == CALLED_FROM_STANDALONE);
1513 
1514 	da.data = (uchar_t *)tem->tvs_pix_data;
1515 	da.width = tems.ts_font.width;
1516 	da.height = tems.ts_font.height;
1517 	da.row = (row * da.height) + tems.ts_p_offset.y;
1518 	da.col = (col * da.width) + tems.ts_p_offset.x;
1519 
1520 	for (i = 0; i < count; i++) {
1521 		tem_safe_callback_bit2pix(tem, string[i], fg_color, bg_color);
1522 		tems_safe_display(&da, credp, called_from);
1523 		da.col += da.width;
1524 	}
1525 }
1526 
1527 void
1528 tem_safe_pix_copy(struct tem_vt_state *tem,
1529     screen_pos_t s_col, screen_pos_t s_row,
1530     screen_pos_t e_col, screen_pos_t e_row,
1531     screen_pos_t t_col, screen_pos_t t_row,
1532     cred_t *credp,
1533     enum called_from called_from)
1534 {
1535 	struct vis_conscopy ma;
1536 	static boolean_t need_clear = B_TRUE;
1537 
1538 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1539 	    called_from == CALLED_FROM_STANDALONE);
1540 
1541 	if (need_clear && tem->tvs_first_line > 0) {
1542 		/*
1543 		 * Clear OBP output above our kernel console term
1544 		 * when our kernel console term begins to scroll up,
1545 		 * we hope it is user friendly.
1546 		 * (Also see comments on tem_safe_pix_clear_prom_output)
1547 		 *
1548 		 * This is only one time call.
1549 		 */
1550 		tem_safe_pix_clear_prom_output(tem, credp, called_from);
1551 	}
1552 	need_clear = B_FALSE;
1553 
1554 	ma.s_row = s_row * tems.ts_font.height + tems.ts_p_offset.y;
1555 	ma.e_row = (e_row + 1) * tems.ts_font.height + tems.ts_p_offset.y - 1;
1556 	ma.t_row = t_row * tems.ts_font.height + tems.ts_p_offset.y;
1557 
1558 	/*
1559 	 * Check if we're in process of clearing OBP's columns area,
1560 	 * which only happens when term scrolls up a whole line.
1561 	 */
1562 	if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 &&
1563 	    e_col == tems.ts_c_dimension.width - 1) {
1564 		/*
1565 		 * We need to clear OBP's columns area outside our kernel
1566 		 * console term. So that we set ma.e_col to entire row here.
1567 		 */
1568 		ma.s_col = s_col * tems.ts_font.width;
1569 		ma.e_col = tems.ts_p_dimension.width - 1;
1570 
1571 		ma.t_col = t_col * tems.ts_font.width;
1572 	} else {
1573 		ma.s_col = s_col * tems.ts_font.width + tems.ts_p_offset.x;
1574 		ma.e_col = (e_col + 1) * tems.ts_font.width +
1575 		    tems.ts_p_offset.x - 1;
1576 		ma.t_col = t_col * tems.ts_font.width + tems.ts_p_offset.x;
1577 	}
1578 
1579 	tems_safe_copy(&ma, credp, called_from);
1580 
1581 	if (tem->tvs_first_line > 0 && t_row < s_row) {
1582 		/* We have scrolled up (s_row - t_row) rows. */
1583 		tem->tvs_first_line -= (s_row - t_row);
1584 		if (tem->tvs_first_line <= 0) {
1585 			/* All OBP rows have been cleared. */
1586 			tem->tvs_first_line = 0;
1587 		}
1588 	}
1589 
1590 }
1591 
1592 void
1593 tem_safe_pix_bit2pix(struct tem_vt_state *tem, unsigned char c,
1594     unsigned char fg, unsigned char bg)
1595 {
1596 	void (*fp)(struct tem_vt_state *, unsigned char,
1597 	    unsigned char, unsigned char);
1598 
1599 	switch (tems.ts_pdepth) {
1600 	case 4:
1601 		fp = bit_to_pix4;
1602 		break;
1603 	case 8:
1604 		fp = bit_to_pix8;
1605 		break;
1606 	case 24:
1607 		fp = bit_to_pix24;
1608 		break;
1609 	case 32:
1610 		fp = bit_to_pix32;
1611 		break;
1612 	default:
1613 		return;
1614 	}
1615 
1616 	fp(tem, c, fg, bg);
1617 }
1618 
1619 
1620 /*
1621  * This function only clears count of columns in one row
1622  */
1623 void
1624 tem_safe_pix_cls(struct tem_vt_state *tem, int count,
1625     screen_pos_t row, screen_pos_t col, cred_t *credp,
1626     enum called_from called_from)
1627 {
1628 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1629 	    called_from == CALLED_FROM_STANDALONE);
1630 
1631 	tem_safe_pix_cls_range(tem, row, 1, tems.ts_p_offset.y,
1632 	    col, count, tems.ts_p_offset.x, B_FALSE, credp, called_from);
1633 }
1634 
1635 /*
1636  * This function clears OBP output above our kernel console term area
1637  * because OBP's term may have a bigger terminal window than that of
1638  * our kernel console term. So we need to clear OBP output garbage outside
1639  * of our kernel console term at a proper time, which is when the first
1640  * row output of our kernel console term scrolls at the first screen line.
1641  *
1642  *	_________________________________
1643  *	|   _____________________	|  ---> OBP's bigger term window
1644  *	|   |			|	|
1645  *	|___|			|	|
1646  *	| | |			|	|
1647  *	| | |			|	|
1648  *	|_|_|___________________|_______|
1649  *	  | |			|	   ---> first line
1650  *	  | |___________________|---> our kernel console term window
1651  *	  |
1652  *	  |---> columns area to be cleared
1653  *
1654  * This function only takes care of the output above our kernel console term,
1655  * and tem_prom_scroll_up takes care of columns area outside of our kernel
1656  * console term.
1657  */
1658 static void
1659 tem_safe_pix_clear_prom_output(struct tem_vt_state *tem, cred_t *credp,
1660     enum called_from called_from)
1661 {
1662 	int	nrows, ncols, width, height;
1663 
1664 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1665 	    called_from == CALLED_FROM_STANDALONE);
1666 
1667 	width = tems.ts_font.width;
1668 	height = tems.ts_font.height;
1669 
1670 	nrows = (tems.ts_p_offset.y + (height - 1))/ height;
1671 	ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
1672 
1673 	tem_safe_pix_cls_range(tem, 0, nrows, 0, 0, ncols, 0,
1674 	    B_FALSE, credp, called_from);
1675 }
1676 
1677 /*
1678  * clear the whole screen for pixel mode, just clear the
1679  * physical screen.
1680  */
1681 void
1682 tem_safe_pix_clear_entire_screen(struct tem_vt_state *tem, cred_t *credp,
1683     enum called_from called_from)
1684 {
1685 	int	nrows, ncols, width, height;
1686 
1687 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1688 	    called_from == CALLED_FROM_STANDALONE);
1689 
1690 	width = tems.ts_font.width;
1691 	height = tems.ts_font.height;
1692 
1693 	nrows = (tems.ts_p_dimension.height + (height - 1))/ height;
1694 	ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
1695 
1696 	tem_safe_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y, 0, ncols,
1697 	    tems.ts_p_offset.x, B_FALSE, credp, called_from);
1698 
1699 	/*
1700 	 * Since the whole screen is cleared, we don't need
1701 	 * to clear OBP output later.
1702 	 */
1703 	if (tem->tvs_first_line > 0)
1704 		tem->tvs_first_line = 0;
1705 }
1706 
1707 /*
1708  * clear the whole screen, including the virtual screen buffer,
1709  * and reset the cursor to start point.
1710  */
1711 static void
1712 tem_safe_cls(struct tem_vt_state *tem,
1713     cred_t *credp, enum called_from called_from)
1714 {
1715 	int	row;
1716 
1717 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1718 	    called_from == CALLED_FROM_STANDALONE);
1719 
1720 	if (tems.ts_display_mode == VIS_TEXT) {
1721 		for (row = 0; row < tems.ts_c_dimension.height; row++) {
1722 			tem_safe_clear_chars(tem, tems.ts_c_dimension.width,
1723 			    row, 0, credp, called_from);
1724 		}
1725 		tem->tvs_c_cursor.row = 0;
1726 		tem->tvs_c_cursor.col = 0;
1727 		tem_safe_align_cursor(tem);
1728 		return;
1729 	}
1730 
1731 	ASSERT(tems.ts_display_mode == VIS_PIXEL);
1732 
1733 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
1734 		tem_safe_virtual_cls(tem, tems.ts_c_dimension.width, row, 0);
1735 	}
1736 	tem->tvs_c_cursor.row = 0;
1737 	tem->tvs_c_cursor.col = 0;
1738 	tem_safe_align_cursor(tem);
1739 
1740 	if (!tem->tvs_isactive)
1741 		return;
1742 
1743 	tem_safe_pix_clear_entire_screen(tem, credp, called_from);
1744 }
1745 
1746 static void
1747 tem_safe_back_tab(struct tem_vt_state *tem,
1748     cred_t *credp, enum called_from called_from)
1749 {
1750 	int	i;
1751 	screen_pos_t	tabstop;
1752 
1753 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1754 	    called_from == CALLED_FROM_STANDALONE);
1755 
1756 	tabstop = 0;
1757 
1758 	for (i = tem->tvs_ntabs - 1; i >= 0; i--) {
1759 		if (tem->tvs_tabs[i] < tem->tvs_c_cursor.col) {
1760 			tabstop = tem->tvs_tabs[i];
1761 			break;
1762 		}
1763 	}
1764 
1765 	tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row,
1766 	    tabstop, credp, called_from);
1767 }
1768 
1769 static void
1770 tem_safe_tab(struct tem_vt_state *tem,
1771     cred_t *credp, enum called_from called_from)
1772 {
1773 	int	i;
1774 	screen_pos_t	tabstop;
1775 
1776 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1777 	    called_from == CALLED_FROM_STANDALONE);
1778 
1779 	tabstop = tems.ts_c_dimension.width - 1;
1780 
1781 	for (i = 0; i < tem->tvs_ntabs; i++) {
1782 		if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
1783 			tabstop = tem->tvs_tabs[i];
1784 			break;
1785 		}
1786 	}
1787 
1788 	tem_safe_mv_cursor(tem, tem->tvs_c_cursor.row,
1789 	    tabstop, credp, called_from);
1790 }
1791 
1792 static void
1793 tem_safe_set_tab(struct tem_vt_state *tem)
1794 {
1795 	int	i;
1796 	int	j;
1797 
1798 	if (tem->tvs_ntabs == TEM_MAXTAB)
1799 		return;
1800 	if (tem->tvs_ntabs == 0 ||
1801 	    tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) {
1802 			tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col;
1803 			return;
1804 	}
1805 	for (i = 0; i < tem->tvs_ntabs; i++) {
1806 		if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col)
1807 			return;
1808 		if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
1809 			for (j = tem->tvs_ntabs - 1; j >= i; j--)
1810 				tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j];
1811 			tem->tvs_tabs[i] = tem->tvs_c_cursor.col;
1812 			tem->tvs_ntabs++;
1813 			return;
1814 		}
1815 	}
1816 }
1817 
1818 static void
1819 tem_safe_clear_tabs(struct tem_vt_state *tem, int action)
1820 {
1821 	int	i;
1822 	int	j;
1823 
1824 	switch (action) {
1825 	case 3: /* clear all tabs */
1826 		tem->tvs_ntabs = 0;
1827 		break;
1828 	case 0: /* clr tab at cursor */
1829 
1830 		for (i = 0; i < tem->tvs_ntabs; i++) {
1831 			if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) {
1832 				tem->tvs_ntabs--;
1833 				for (j = i; j < tem->tvs_ntabs; j++)
1834 					tem->tvs_tabs[j] = tem->tvs_tabs[j + 1];
1835 				return;
1836 			}
1837 		}
1838 		break;
1839 	}
1840 }
1841 
1842 static void
1843 tem_safe_mv_cursor(struct tem_vt_state *tem, int row, int col,
1844     cred_t *credp, enum called_from called_from)
1845 {
1846 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1847 	    called_from == CALLED_FROM_STANDALONE);
1848 
1849 	/*
1850 	 * Sanity check and bounds enforcement.  Out of bounds requests are
1851 	 * clipped to the screen boundaries.  This seems to be what SPARC
1852 	 * does.
1853 	 */
1854 	if (row < 0)
1855 		row = 0;
1856 	if (row >= tems.ts_c_dimension.height)
1857 		row = tems.ts_c_dimension.height - 1;
1858 	if (col < 0)
1859 		col = 0;
1860 	if (col >= tems.ts_c_dimension.width)
1861 		col = tems.ts_c_dimension.width - 1;
1862 
1863 	tem_safe_send_data(tem, credp, called_from);
1864 	tem->tvs_c_cursor.row = (screen_pos_t)row;
1865 	tem->tvs_c_cursor.col = (screen_pos_t)col;
1866 	tem_safe_align_cursor(tem);
1867 }
1868 
1869 /* ARGSUSED */
1870 void
1871 tem_safe_reset_emulator(struct tem_vt_state *tem,
1872     cred_t *credp, enum called_from called_from,
1873     boolean_t init_color)
1874 {
1875 	int j;
1876 
1877 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1878 	    called_from == CALLED_FROM_STANDALONE);
1879 
1880 	tem->tvs_c_cursor.row = 0;
1881 	tem->tvs_c_cursor.col = 0;
1882 	tem->tvs_r_cursor.row = 0;
1883 	tem->tvs_r_cursor.col = 0;
1884 	tem->tvs_s_cursor.row = 0;
1885 	tem->tvs_s_cursor.col = 0;
1886 	tem->tvs_outindex = 0;
1887 	tem->tvs_state = A_STATE_START;
1888 	tem->tvs_gotparam = B_FALSE;
1889 	tem->tvs_curparam = 0;
1890 	tem->tvs_paramval = 0;
1891 	tem->tvs_nscroll = 1;
1892 
1893 	if (init_color) {
1894 		/* use initial settings */
1895 		tem->tvs_fg_color = tems.ts_init_color.fg_color;
1896 		tem->tvs_bg_color = tems.ts_init_color.bg_color;
1897 		tem->tvs_flags = tems.ts_init_color.a_flags;
1898 	}
1899 
1900 	/*
1901 	 * set up the initial tab stops
1902 	 */
1903 	tem->tvs_ntabs = 0;
1904 	for (j = 8; j < tems.ts_c_dimension.width; j += 8)
1905 		tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j;
1906 
1907 	for (j = 0; j < TEM_MAXPARAMS; j++)
1908 		tem->tvs_params[j] = 0;
1909 }
1910 
1911 void
1912 tem_safe_reset_display(struct tem_vt_state *tem,
1913     cred_t *credp, enum called_from called_from,
1914     boolean_t clear_txt, boolean_t init_color)
1915 {
1916 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1917 	    called_from == CALLED_FROM_STANDALONE);
1918 
1919 	tem_safe_reset_emulator(tem, credp, called_from, init_color);
1920 
1921 	if (clear_txt) {
1922 		if (tem->tvs_isactive)
1923 			tem_safe_callback_cursor(tem,
1924 			    VIS_HIDE_CURSOR, credp, called_from);
1925 
1926 		tem_safe_cls(tem, credp, called_from);
1927 
1928 		if (tem->tvs_isactive)
1929 			tem_safe_callback_cursor(tem,
1930 			    VIS_DISPLAY_CURSOR, credp, called_from);
1931 	}
1932 }
1933 
1934 static void
1935 tem_safe_shift(
1936 	struct tem_vt_state *tem,
1937 	int count,
1938 	int direction,
1939 	cred_t *credp,
1940 	enum called_from called_from)
1941 {
1942 	int rest_of_line;
1943 
1944 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1945 	    called_from == CALLED_FROM_STANDALONE);
1946 
1947 	rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col;
1948 	if (count > rest_of_line)
1949 		count = rest_of_line;
1950 
1951 	if (count <= 0)
1952 		return;
1953 
1954 	switch (direction) {
1955 	case TEM_SHIFT_LEFT:
1956 		if (count < rest_of_line) {
1957 			tem_safe_copy_area(tem,
1958 			    tem->tvs_c_cursor.col + count,
1959 			    tem->tvs_c_cursor.row,
1960 			    tems.ts_c_dimension.width - 1,
1961 			    tem->tvs_c_cursor.row,
1962 			    tem->tvs_c_cursor.col,
1963 			    tem->tvs_c_cursor.row,
1964 			    credp, called_from);
1965 		}
1966 
1967 		tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
1968 		    (tems.ts_c_dimension.width - count), credp,
1969 		    called_from);
1970 		break;
1971 	case TEM_SHIFT_RIGHT:
1972 		if (count < rest_of_line) {
1973 			tem_safe_copy_area(tem,
1974 			    tem->tvs_c_cursor.col,
1975 			    tem->tvs_c_cursor.row,
1976 			    tems.ts_c_dimension.width - count - 1,
1977 			    tem->tvs_c_cursor.row,
1978 			    tem->tvs_c_cursor.col + count,
1979 			    tem->tvs_c_cursor.row,
1980 			    credp, called_from);
1981 		}
1982 
1983 		tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
1984 		    tem->tvs_c_cursor.col, credp, called_from);
1985 		break;
1986 	}
1987 }
1988 
1989 void
1990 tem_safe_text_cursor(struct tem_vt_state *tem, short action,
1991     cred_t *credp, enum called_from called_from)
1992 {
1993 	struct vis_conscursor	ca;
1994 
1995 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
1996 	    called_from == CALLED_FROM_STANDALONE);
1997 
1998 	ca.row = tem->tvs_c_cursor.row;
1999 	ca.col = tem->tvs_c_cursor.col;
2000 	ca.action = action;
2001 
2002 	tems_safe_cursor(&ca, credp, called_from);
2003 
2004 	if (action == VIS_GET_CURSOR) {
2005 		tem->tvs_c_cursor.row = ca.row;
2006 		tem->tvs_c_cursor.col = ca.col;
2007 	}
2008 }
2009 
2010 void
2011 tem_safe_pix_cursor(struct tem_vt_state *tem, short action,
2012     cred_t *credp, enum called_from called_from)
2013 {
2014 	struct vis_conscursor	ca;
2015 
2016 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2017 	    called_from == CALLED_FROM_STANDALONE);
2018 
2019 	ca.row = tem->tvs_c_cursor.row * tems.ts_font.height +
2020 	    tems.ts_p_offset.y;
2021 	ca.col = tem->tvs_c_cursor.col * tems.ts_font.width +
2022 	    tems.ts_p_offset.x;
2023 	ca.width = tems.ts_font.width;
2024 	ca.height = tems.ts_font.height;
2025 	if (tems.ts_pdepth == 8 || tems.ts_pdepth == 4) {
2026 		if (tem->tvs_flags & TEM_ATTR_REVERSE) {
2027 			ca.fg_color.mono = TEM_TEXT_WHITE;
2028 			ca.bg_color.mono = TEM_TEXT_BLACK;
2029 		} else {
2030 			ca.fg_color.mono = TEM_TEXT_BLACK;
2031 			ca.bg_color.mono = TEM_TEXT_WHITE;
2032 		}
2033 	} else if (tems.ts_pdepth == 24 || tems.ts_pdepth == 32) {
2034 		if (tem->tvs_flags & TEM_ATTR_REVERSE) {
2035 			ca.fg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED;
2036 			ca.fg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN;
2037 			ca.fg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE;
2038 
2039 			ca.bg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED;
2040 			ca.bg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN;
2041 			ca.bg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE;
2042 		} else {
2043 			ca.fg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED;
2044 			ca.fg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN;
2045 			ca.fg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE;
2046 
2047 			ca.bg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED;
2048 			ca.bg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN;
2049 			ca.bg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE;
2050 		}
2051 	}
2052 
2053 	ca.action = action;
2054 
2055 	tems_safe_cursor(&ca, credp, called_from);
2056 
2057 	if (action == VIS_GET_CURSOR) {
2058 		tem->tvs_c_cursor.row = (ca.row - tems.ts_p_offset.y) /
2059 		    tems.ts_font.height;
2060 		tem->tvs_c_cursor.col = (ca.col - tems.ts_p_offset.x) /
2061 		    tems.ts_font.width;
2062 	}
2063 }
2064 
2065 static void
2066 bit_to_pix4(struct tem_vt_state *tem, uchar_t c, text_color_t fg_color,
2067     text_color_t bg_color)
2068 {
2069 	uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2070 	font_bit_to_pix4(&tems.ts_font, dest, c, fg_color, bg_color);
2071 }
2072 
2073 static void
2074 bit_to_pix8(struct tem_vt_state *tem, uchar_t c, text_color_t fg_color,
2075     text_color_t bg_color)
2076 {
2077 	uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2078 	font_bit_to_pix8(&tems.ts_font, dest, c, fg_color, bg_color);
2079 }
2080 
2081 static void
2082 bit_to_pix24(struct tem_vt_state *tem, uchar_t c, text_color_t fg_color4,
2083     text_color_t bg_color4)
2084 {
2085 	uint32_t fg_color32, bg_color32;
2086 	uint8_t *dest;
2087 
2088 	ASSERT(fg_color4 < 16 && bg_color4 < 16);
2089 
2090 	fg_color32 = PIX4TO32(fg_color4);
2091 	bg_color32 = PIX4TO32(bg_color4);
2092 
2093 	dest = (uint8_t *)tem->tvs_pix_data;
2094 	font_bit_to_pix24(&tems.ts_font, dest, c, fg_color32, bg_color32);
2095 }
2096 
2097 static void
2098 bit_to_pix32(struct tem_vt_state *tem, uchar_t c, text_color_t fg_color4,
2099     text_color_t bg_color4)
2100 {
2101 	uint32_t fg_color32, bg_color32, *dest;
2102 
2103 	ASSERT(fg_color4 < 16 && bg_color4 < 16);
2104 
2105 	fg_color32 = PIX4TO32(fg_color4);
2106 	bg_color32 = PIX4TO32(bg_color4);
2107 
2108 	dest = (uint32_t *)tem->tvs_pix_data;
2109 	font_bit_to_pix32(&tems.ts_font, dest, c, fg_color32, bg_color32);
2110 }
2111 
2112 static text_color_t
2113 ansi_bg_to_solaris(struct tem_vt_state *tem, int ansi)
2114 {
2115 	if (tem->tvs_flags & TEM_ATTR_BRIGHT_BG)
2116 		return (brt_xlate[ansi]);
2117 	else
2118 		return (dim_xlate[ansi]);
2119 }
2120 
2121 static text_color_t
2122 ansi_fg_to_solaris(struct tem_vt_state *tem, int ansi)
2123 {
2124 	if (tem->tvs_flags & TEM_ATTR_BRIGHT_FG ||
2125 	    tem->tvs_flags & TEM_ATTR_BOLD) {
2126 		return (brt_xlate[ansi]);
2127 	} else {
2128 		return (dim_xlate[ansi]);
2129 	}
2130 }
2131 
2132 /*
2133  * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE
2134  */
2135 void
2136 tem_safe_get_color(struct tem_vt_state *tem, text_color_t *fg,
2137     text_color_t *bg, uint8_t flag)
2138 {
2139 	if (tem->tvs_flags & flag) {
2140 		*fg = ansi_fg_to_solaris(tem,
2141 		    tem->tvs_bg_color);
2142 		*bg = ansi_bg_to_solaris(tem,
2143 		    tem->tvs_fg_color);
2144 	} else {
2145 		*fg = ansi_fg_to_solaris(tem,
2146 		    tem->tvs_fg_color);
2147 		*bg = ansi_bg_to_solaris(tem,
2148 		    tem->tvs_bg_color);
2149 	}
2150 }
2151 
2152 /*
2153  * Clear a rectangle of screen for pixel mode.
2154  *
2155  * arguments:
2156  *    row:	start row#
2157  *    nrows:	the number of rows to clear
2158  *    offset_y:	the offset of height in pixels to begin clear
2159  *    col:	start col#
2160  *    ncols:	the number of cols to clear
2161  *    offset_x:	the offset of width in pixels to begin clear
2162  *    scroll_up: whether this function is called during sroll up,
2163  *		 which is called only once.
2164  */
2165 void
2166 tem_safe_pix_cls_range(struct tem_vt_state *tem,
2167     screen_pos_t row, int nrows, int offset_y,
2168     screen_pos_t col, int ncols, int offset_x,
2169     boolean_t sroll_up, cred_t *credp,
2170     enum called_from called_from)
2171 {
2172 	struct vis_consdisplay da;
2173 	int	i, j;
2174 	int	row_add = 0;
2175 	text_color_t fg_color;
2176 	text_color_t bg_color;
2177 
2178 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2179 	    called_from == CALLED_FROM_STANDALONE);
2180 
2181 	if (sroll_up)
2182 		row_add = tems.ts_c_dimension.height - 1;
2183 
2184 	da.width = tems.ts_font.width;
2185 	da.height = tems.ts_font.height;
2186 
2187 	tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_SCREEN_REVERSE);
2188 
2189 	tem_safe_callback_bit2pix(tem, ' ', fg_color, bg_color);
2190 	da.data = (uchar_t *)tem->tvs_pix_data;
2191 
2192 	for (i = 0; i < nrows; i++, row++) {
2193 		da.row = (row + row_add) * da.height + offset_y;
2194 		da.col = col * da.width + offset_x;
2195 		for (j = 0; j < ncols; j++) {
2196 			tems_safe_display(&da, credp, called_from);
2197 			da.col += da.width;
2198 		}
2199 	}
2200 }
2201 
2202 /*
2203  * virtual screen operations
2204  */
2205 static void
2206 tem_safe_virtual_display(struct tem_vt_state *tem, unsigned char *string,
2207     int count, screen_pos_t row, screen_pos_t col,
2208     text_color_t fg_color, text_color_t bg_color)
2209 {
2210 	int i, width;
2211 	unsigned char *addr;
2212 	text_color_t *pfgcolor;
2213 	text_color_t *pbgcolor;
2214 
2215 	if (row < 0 || row >= tems.ts_c_dimension.height ||
2216 	    col < 0 || col >= tems.ts_c_dimension.width ||
2217 	    col + count > tems.ts_c_dimension.width)
2218 		return;
2219 
2220 	width = tems.ts_c_dimension.width;
2221 	addr = tem->tvs_screen_buf +  (row * width + col);
2222 	pfgcolor = tem->tvs_fg_buf + (row * width + col);
2223 	pbgcolor = tem->tvs_bg_buf + (row * width + col);
2224 	for (i = 0; i < count; i++) {
2225 		*addr++ = string[i];
2226 		*pfgcolor++ = fg_color;
2227 		*pbgcolor++ = bg_color;
2228 	}
2229 }
2230 
2231 static void
2232 i_virtual_copy(unsigned char *base,
2233     screen_pos_t s_col, screen_pos_t s_row,
2234     screen_pos_t e_col, screen_pos_t e_row,
2235     screen_pos_t t_col, screen_pos_t t_row)
2236 {
2237 	unsigned char   *from;
2238 	unsigned char   *to;
2239 	int		cnt;
2240 	screen_size_t chars_per_row;
2241 	unsigned char   *to_row_start;
2242 	unsigned char   *from_row_start;
2243 	screen_size_t   rows_to_move;
2244 	int		cols = tems.ts_c_dimension.width;
2245 
2246 	chars_per_row = e_col - s_col + 1;
2247 	rows_to_move = e_row - s_row + 1;
2248 
2249 	to_row_start = base + ((t_row * cols) + t_col);
2250 	from_row_start = base + ((s_row * cols) + s_col);
2251 
2252 	if (to_row_start < from_row_start) {
2253 		while (rows_to_move-- > 0) {
2254 			to = to_row_start;
2255 			from = from_row_start;
2256 			to_row_start += cols;
2257 			from_row_start += cols;
2258 			for (cnt = chars_per_row; cnt-- > 0; )
2259 				*to++ = *from++;
2260 		}
2261 	} else {
2262 		/*
2263 		 * Offset to the end of the region and copy backwards.
2264 		 */
2265 		cnt = rows_to_move * cols + chars_per_row;
2266 		to_row_start += cnt;
2267 		from_row_start += cnt;
2268 
2269 		while (rows_to_move-- > 0) {
2270 			to_row_start -= cols;
2271 			from_row_start -= cols;
2272 			to = to_row_start;
2273 			from = from_row_start;
2274 			for (cnt = chars_per_row; cnt-- > 0; )
2275 				*--to = *--from;
2276 		}
2277 	}
2278 }
2279 
2280 static void
2281 tem_safe_virtual_copy(struct tem_vt_state *tem,
2282     screen_pos_t s_col, screen_pos_t s_row,
2283     screen_pos_t e_col, screen_pos_t e_row,
2284     screen_pos_t t_col, screen_pos_t t_row)
2285 {
2286 	screen_size_t chars_per_row;
2287 	screen_size_t   rows_to_move;
2288 	int		rows = tems.ts_c_dimension.height;
2289 	int		cols = tems.ts_c_dimension.width;
2290 
2291 	if (s_col < 0 || s_col >= cols ||
2292 	    s_row < 0 || s_row >= rows ||
2293 	    e_col < 0 || e_col >= cols ||
2294 	    e_row < 0 || e_row >= rows ||
2295 	    t_col < 0 || t_col >= cols ||
2296 	    t_row < 0 || t_row >= rows ||
2297 	    s_col > e_col ||
2298 	    s_row > e_row)
2299 		return;
2300 
2301 	chars_per_row = e_col - s_col + 1;
2302 	rows_to_move = e_row - s_row + 1;
2303 
2304 	/* More sanity checks. */
2305 	if (t_row + rows_to_move > rows ||
2306 	    t_col + chars_per_row > cols)
2307 		return;
2308 
2309 	i_virtual_copy(tem->tvs_screen_buf, s_col, s_row,
2310 	    e_col, e_row, t_col, t_row);
2311 
2312 	/* text_color_t is the same size as char */
2313 	i_virtual_copy((unsigned char *)tem->tvs_fg_buf,
2314 	    s_col, s_row, e_col, e_row, t_col, t_row);
2315 	i_virtual_copy((unsigned char *)tem->tvs_bg_buf,
2316 	    s_col, s_row, e_col, e_row, t_col, t_row);
2317 
2318 }
2319 
2320 static void
2321 tem_safe_virtual_cls(struct tem_vt_state *tem,
2322     int count, screen_pos_t row, screen_pos_t col)
2323 {
2324 	text_color_t fg_color;
2325 	text_color_t bg_color;
2326 
2327 	tem_safe_get_color(tem, &fg_color, &bg_color, TEM_ATTR_SCREEN_REVERSE);
2328 	tem_safe_virtual_display(tem, tems.ts_blank_line, count, row, col,
2329 	    fg_color, bg_color);
2330 }
2331 
2332 /*
2333  * only blank screen, not clear our screen buffer
2334  */
2335 void
2336 tem_safe_blank_screen(struct tem_vt_state *tem, cred_t *credp,
2337     enum called_from called_from)
2338 {
2339 	int	row;
2340 
2341 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2342 	    called_from == CALLED_FROM_STANDALONE);
2343 
2344 	if (tems.ts_display_mode == VIS_PIXEL) {
2345 		tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2346 		return;
2347 	}
2348 
2349 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2350 		tem_safe_callback_cls(tem,
2351 		    tems.ts_c_dimension.width,
2352 		    row, 0, credp, called_from);
2353 	}
2354 }
2355 
2356 /*
2357  * unblank screen with associated tem from its screen buffer
2358  */
2359 void
2360 tem_safe_unblank_screen(struct tem_vt_state *tem, cred_t *credp,
2361     enum called_from called_from)
2362 {
2363 	text_color_t fg_color, fg_last;
2364 	text_color_t bg_color, bg_last;
2365 	size_t	tc_size = sizeof (text_color_t);
2366 	int	row, col, count, col_start;
2367 	int	width;
2368 	unsigned char *buf;
2369 
2370 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2371 	    called_from == CALLED_FROM_STANDALONE);
2372 
2373 	if (tems.ts_display_mode == VIS_PIXEL)
2374 		tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2375 
2376 	tem_safe_callback_cursor(tem, VIS_HIDE_CURSOR, credp, called_from);
2377 
2378 	width = tems.ts_c_dimension.width;
2379 
2380 	/*
2381 	 * Display data in tvs_screen_buf to the actual framebuffer in a
2382 	 * row by row way.
2383 	 * When dealing with one row, output data with the same foreground
2384 	 * and background color all together.
2385 	 */
2386 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2387 		buf = tem->tvs_screen_buf + (row * width);
2388 		count = col_start = 0;
2389 		for (col = 0; col < width; col++) {
2390 			fg_color =
2391 			    tem->tvs_fg_buf[(row * width + col) * tc_size];
2392 			bg_color =
2393 			    tem->tvs_bg_buf[(row * width + col) * tc_size];
2394 			if (col == 0) {
2395 				fg_last = fg_color;
2396 				bg_last = bg_color;
2397 			}
2398 
2399 			if ((fg_color != fg_last) || (bg_color != bg_last)) {
2400 				/*
2401 				 * Call the primitive to render this data.
2402 				 */
2403 				tem_safe_callback_display(tem,
2404 				    buf, count, row, col_start,
2405 				    fg_last, bg_last, credp, called_from);
2406 				buf += count;
2407 				count = 1;
2408 				col_start = col;
2409 				fg_last = fg_color;
2410 				bg_last = bg_color;
2411 			} else {
2412 				count++;
2413 			}
2414 		}
2415 
2416 		if (col_start == (width - 1))
2417 			continue;
2418 
2419 		/*
2420 		 * Call the primitive to render this data.
2421 		 */
2422 		tem_safe_callback_display(tem,
2423 		    buf, count, row, col_start,
2424 		    fg_last, bg_last, credp, called_from);
2425 	}
2426 
2427 	tem_safe_callback_cursor(tem, VIS_DISPLAY_CURSOR, credp, called_from);
2428 }
2429