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