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