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