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