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