xref: /illumos-gate/usr/src/uts/common/io/tem_safe.c (revision 28ab0ca48b3e331cbbb231b1c8325f9f24f9af95)
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 	size_t	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 	size_t	i, j;
1987 
1988 	if (tem->tvs_ntabs == tem->tvs_maxtab)
1989 		return;
1990 	if (tem->tvs_ntabs == 0 ||
1991 	    tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) {
1992 			tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col;
1993 			return;
1994 	}
1995 	for (i = 0; i < tem->tvs_ntabs; i++) {
1996 		if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col)
1997 			return;
1998 		if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
1999 			for (j = tem->tvs_ntabs - 1; j >= i; j--)
2000 				tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j];
2001 			tem->tvs_tabs[i] = tem->tvs_c_cursor.col;
2002 			tem->tvs_ntabs++;
2003 			return;
2004 		}
2005 	}
2006 }
2007 
2008 static void
2009 tem_safe_clear_tabs(struct tem_vt_state *tem, int action)
2010 {
2011 	size_t	i, j;
2012 
2013 	switch (action) {
2014 	case 3: /* clear all tabs */
2015 		tem->tvs_ntabs = 0;
2016 		break;
2017 	case 0: /* clr tab at cursor */
2018 
2019 		for (i = 0; i < tem->tvs_ntabs; i++) {
2020 			if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) {
2021 				tem->tvs_ntabs--;
2022 				for (j = i; j < tem->tvs_ntabs; j++)
2023 					tem->tvs_tabs[j] = tem->tvs_tabs[j + 1];
2024 				return;
2025 			}
2026 		}
2027 		break;
2028 	}
2029 }
2030 
2031 static void
2032 tem_safe_mv_cursor(struct tem_vt_state *tem, int row, int col,
2033     cred_t *credp, enum called_from called_from)
2034 {
2035 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2036 	    called_from == CALLED_FROM_STANDALONE);
2037 
2038 	/*
2039 	 * Sanity check and bounds enforcement.  Out of bounds requests are
2040 	 * clipped to the screen boundaries.  This seems to be what SPARC
2041 	 * does.
2042 	 */
2043 	if (row < 0)
2044 		row = 0;
2045 	if (row >= tems.ts_c_dimension.height)
2046 		row = tems.ts_c_dimension.height - 1;
2047 	if (col < 0)
2048 		col = 0;
2049 	if (col >= tems.ts_c_dimension.width)
2050 		col = tems.ts_c_dimension.width - 1;
2051 
2052 	tem_safe_send_data(tem, credp, called_from);
2053 	tem->tvs_c_cursor.row = (screen_pos_t)row;
2054 	tem->tvs_c_cursor.col = (screen_pos_t)col;
2055 	tem_safe_align_cursor(tem);
2056 }
2057 
2058 /* ARGSUSED */
2059 void
2060 tem_safe_reset_emulator(struct tem_vt_state *tem,
2061     cred_t *credp, enum called_from called_from,
2062     boolean_t init_color)
2063 {
2064 	int j;
2065 
2066 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2067 	    called_from == CALLED_FROM_STANDALONE);
2068 
2069 	tem->tvs_c_cursor.row = 0;
2070 	tem->tvs_c_cursor.col = 0;
2071 	tem->tvs_r_cursor.row = 0;
2072 	tem->tvs_r_cursor.col = 0;
2073 	tem->tvs_s_cursor.row = 0;
2074 	tem->tvs_s_cursor.col = 0;
2075 	tem->tvs_outindex = 0;
2076 	tem->tvs_state = A_STATE_START;
2077 	tem->tvs_gotparam = B_FALSE;
2078 	tem->tvs_curparam = 0;
2079 	tem->tvs_paramval = 0;
2080 	tem->tvs_nscroll = 1;
2081 
2082 	if (init_color) {
2083 		/* use initial settings */
2084 		tem->tvs_fg_color = tems.ts_init_color.fg_color;
2085 		tem->tvs_bg_color = tems.ts_init_color.bg_color;
2086 		tem->tvs_flags = tems.ts_init_color.a_flags;
2087 	}
2088 
2089 	/*
2090 	 * set up the initial tab stops
2091 	 */
2092 	tem->tvs_ntabs = 0;
2093 	for (j = 8; j < tems.ts_c_dimension.width; j += 8)
2094 		tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j;
2095 
2096 	for (j = 0; j < TEM_MAXPARAMS; j++)
2097 		tem->tvs_params[j] = 0;
2098 }
2099 
2100 void
2101 tem_safe_reset_display(struct tem_vt_state *tem,
2102     cred_t *credp, enum called_from called_from,
2103     boolean_t clear_txt, boolean_t init_color)
2104 {
2105 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2106 	    called_from == CALLED_FROM_STANDALONE);
2107 
2108 	tem_safe_reset_emulator(tem, credp, called_from, init_color);
2109 
2110 	if (clear_txt) {
2111 		if (tem->tvs_isactive)
2112 			tem_safe_callback_cursor(tem,
2113 			    VIS_HIDE_CURSOR, credp, called_from);
2114 
2115 		tem_safe_cls(tem, credp, called_from);
2116 
2117 		if (tem->tvs_isactive)
2118 			tem_safe_callback_cursor(tem,
2119 			    VIS_DISPLAY_CURSOR, credp, called_from);
2120 	}
2121 }
2122 
2123 static void
2124 tem_safe_shift(
2125 	struct tem_vt_state *tem,
2126 	int count,
2127 	int direction,
2128 	cred_t *credp,
2129 	enum called_from called_from)
2130 {
2131 	int rest_of_line;
2132 
2133 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2134 	    called_from == CALLED_FROM_STANDALONE);
2135 
2136 	rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col;
2137 	if (count > rest_of_line)
2138 		count = rest_of_line;
2139 
2140 	if (count <= 0)
2141 		return;
2142 
2143 	switch (direction) {
2144 	case TEM_SHIFT_LEFT:
2145 		if (count < rest_of_line) {
2146 			tem_safe_copy_area(tem,
2147 			    tem->tvs_c_cursor.col + count,
2148 			    tem->tvs_c_cursor.row,
2149 			    tems.ts_c_dimension.width - 1,
2150 			    tem->tvs_c_cursor.row,
2151 			    tem->tvs_c_cursor.col,
2152 			    tem->tvs_c_cursor.row,
2153 			    credp, called_from);
2154 		}
2155 
2156 		tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
2157 		    (tems.ts_c_dimension.width - count), credp,
2158 		    called_from);
2159 		break;
2160 	case TEM_SHIFT_RIGHT:
2161 		if (count < rest_of_line) {
2162 			tem_safe_copy_area(tem,
2163 			    tem->tvs_c_cursor.col,
2164 			    tem->tvs_c_cursor.row,
2165 			    tems.ts_c_dimension.width - count - 1,
2166 			    tem->tvs_c_cursor.row,
2167 			    tem->tvs_c_cursor.col + count,
2168 			    tem->tvs_c_cursor.row,
2169 			    credp, called_from);
2170 		}
2171 
2172 		tem_safe_clear_chars(tem, count, tem->tvs_c_cursor.row,
2173 		    tem->tvs_c_cursor.col, credp, called_from);
2174 		break;
2175 	}
2176 }
2177 
2178 void
2179 tem_safe_text_cursor(struct tem_vt_state *tem, short action,
2180     cred_t *credp, enum called_from called_from)
2181 {
2182 	struct vis_conscursor	ca;
2183 
2184 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2185 	    called_from == CALLED_FROM_STANDALONE);
2186 
2187 	ca.row = tem->tvs_c_cursor.row;
2188 	ca.col = tem->tvs_c_cursor.col;
2189 	ca.action = action;
2190 
2191 	tems_safe_cursor(&ca, credp, called_from);
2192 
2193 	if (action == VIS_GET_CURSOR) {
2194 		tem->tvs_c_cursor.row = ca.row;
2195 		tem->tvs_c_cursor.col = ca.col;
2196 	}
2197 }
2198 
2199 void
2200 tem_safe_pix_cursor(struct tem_vt_state *tem, short action,
2201     cred_t *credp, enum called_from called_from)
2202 {
2203 	struct vis_conscursor	ca;
2204 	uint32_t color;
2205 	text_color_t fg, bg;
2206 	term_char_t c;
2207 	text_attr_t attr;
2208 
2209 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2210 	    called_from == CALLED_FROM_STANDALONE);
2211 
2212 	ca.row = tem->tvs_c_cursor.row * tems.ts_font.vf_height +
2213 	    tems.ts_p_offset.y;
2214 	ca.col = tem->tvs_c_cursor.col * tems.ts_font.vf_width +
2215 	    tems.ts_p_offset.x;
2216 	ca.width = (screen_size_t)tems.ts_font.vf_width;
2217 	ca.height = (screen_size_t)tems.ts_font.vf_height;
2218 
2219 	tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2220 	    TEM_ATTR_REVERSE);
2221 	c.tc_char = TEM_ATTR(attr);
2222 
2223 	tem_safe_get_color(&fg, &bg, c);
2224 
2225 	switch (tems.ts_pdepth) {
2226 	case 4:
2227 		ca.fg_color.mono = fg;
2228 		ca.bg_color.mono = bg;
2229 		break;
2230 	case 8:
2231 #ifdef _HAVE_TEM_FIRMWARE
2232 		ca.fg_color.mono = fg;
2233 		ca.bg_color.mono = bg;
2234 #else
2235 		ca.fg_color.mono = tems.ts_color_map(fg);
2236 		ca.bg_color.mono = tems.ts_color_map(bg);
2237 #endif
2238 		break;
2239 	case 15:
2240 	case 16:
2241 		color = tems.ts_color_map(fg);
2242 		ca.fg_color.sixteen[0] = (color >> 8) & 0xFF;
2243 		ca.fg_color.sixteen[1] = color & 0xFF;
2244 		color = tems.ts_color_map(bg);
2245 		ca.bg_color.sixteen[0] = (color >> 8) & 0xFF;
2246 		ca.bg_color.sixteen[1] = color & 0xFF;
2247 		break;
2248 	case 24:
2249 	case 32:
2250 #ifdef _HAVE_TEM_FIRMWARE
2251 		/* Keeping this block to support old binary only drivers */
2252 		if (tem->tvs_flags & TEM_ATTR_REVERSE) {
2253 			ca.fg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED;
2254 			ca.fg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN;
2255 			ca.fg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE;
2256 
2257 			ca.bg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED;
2258 			ca.bg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN;
2259 			ca.bg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE;
2260 		} else {
2261 			ca.fg_color.twentyfour[0] = TEM_TEXT_BLACK24_RED;
2262 			ca.fg_color.twentyfour[1] = TEM_TEXT_BLACK24_GREEN;
2263 			ca.fg_color.twentyfour[2] = TEM_TEXT_BLACK24_BLUE;
2264 
2265 			ca.bg_color.twentyfour[0] = TEM_TEXT_WHITE24_RED;
2266 			ca.bg_color.twentyfour[1] = TEM_TEXT_WHITE24_GREEN;
2267 			ca.bg_color.twentyfour[2] = TEM_TEXT_WHITE24_BLUE;
2268 		}
2269 #else
2270 		color = tems.ts_color_map(fg);
2271 		ca.fg_color.twentyfour[0] = (color >> 16) & 0xFF;
2272 		ca.fg_color.twentyfour[1] = (color >> 8) & 0xFF;
2273 		ca.fg_color.twentyfour[2] = color & 0xFF;
2274 		color = tems.ts_color_map(bg);
2275 		ca.bg_color.twentyfour[0] = (color >> 16) & 0xFF;
2276 		ca.bg_color.twentyfour[1] = (color >> 8) & 0xFF;
2277 		ca.bg_color.twentyfour[2] = color & 0xFF;
2278 #endif
2279 		break;
2280 	}
2281 
2282 	ca.action = action;
2283 
2284 	tems_safe_cursor(&ca, credp, called_from);
2285 
2286 	if (action == VIS_GET_CURSOR) {
2287 		tem->tvs_c_cursor.row = 0;
2288 		tem->tvs_c_cursor.col = 0;
2289 
2290 		if (ca.row != 0) {
2291 			tem->tvs_c_cursor.row = (ca.row - tems.ts_p_offset.y) /
2292 			    tems.ts_font.vf_height;
2293 		}
2294 		if (ca.col != 0) {
2295 			tem->tvs_c_cursor.col = (ca.col - tems.ts_p_offset.x) /
2296 			    tems.ts_font.vf_width;
2297 		}
2298 	}
2299 }
2300 
2301 static void
2302 bit_to_pix4(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color,
2303     text_color_t bg_color)
2304 {
2305 	uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2306 	font_bit_to_pix4(&tems.ts_font, dest, c, fg_color, bg_color);
2307 }
2308 
2309 static void
2310 bit_to_pix8(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color,
2311     text_color_t bg_color)
2312 {
2313 	uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2314 
2315 #ifndef _HAVE_TEM_FIRMWARE
2316 	fg_color = (text_color_t)tems.ts_color_map(fg_color);
2317 	bg_color = (text_color_t)tems.ts_color_map(bg_color);
2318 #endif
2319 	font_bit_to_pix8(&tems.ts_font, dest, c, fg_color, bg_color);
2320 }
2321 
2322 static void
2323 bit_to_pix16(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2324     text_color_t bg_color4)
2325 {
2326 	uint16_t fg_color16, bg_color16;
2327 	uint16_t *dest;
2328 
2329 	ASSERT(fg_color4 < 16 && bg_color4 < 16);
2330 
2331 	fg_color16 = (uint16_t)tems.ts_color_map(fg_color4);
2332 	bg_color16 = (uint16_t)tems.ts_color_map(bg_color4);
2333 
2334 	dest = (uint16_t *)tem->tvs_pix_data;
2335 	font_bit_to_pix16(&tems.ts_font, dest, c, fg_color16, bg_color16);
2336 }
2337 
2338 static void
2339 bit_to_pix24(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2340     text_color_t bg_color4)
2341 {
2342 	uint32_t fg_color32, bg_color32;
2343 	uint8_t *dest;
2344 
2345 #ifdef _HAVE_TEM_FIRMWARE
2346 	fg_color32 = PIX4TO32(fg_color4);
2347 	bg_color32 = PIX4TO32(bg_color4);
2348 #else
2349 	fg_color32 = tems.ts_color_map(fg_color4);
2350 	bg_color32 = tems.ts_color_map(bg_color4);
2351 #endif
2352 
2353 	dest = (uint8_t *)tem->tvs_pix_data;
2354 	font_bit_to_pix24(&tems.ts_font, dest, c, fg_color32, bg_color32);
2355 }
2356 
2357 static void
2358 bit_to_pix32(struct tem_vt_state *tem, tem_char_t c, text_color_t fg_color4,
2359     text_color_t bg_color4)
2360 {
2361 	uint32_t fg_color32, bg_color32, *dest;
2362 
2363 #ifdef _HAVE_TEM_FIRMWARE
2364 	fg_color32 = PIX4TO32(fg_color4);
2365 	bg_color32 = PIX4TO32(bg_color4);
2366 #else
2367 	fg_color32 = ((uint32_t)0xFF << 24) | tems.ts_color_map(fg_color4);
2368 	bg_color32 = ((uint32_t)0xFF << 24) | tems.ts_color_map(bg_color4);
2369 #endif
2370 
2371 	dest = (uint32_t *)tem->tvs_pix_data;
2372 	font_bit_to_pix32(&tems.ts_font, dest, c, fg_color32, bg_color32);
2373 }
2374 
2375 /*
2376  * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE
2377  */
2378 void
2379 tem_safe_get_attr(struct tem_vt_state *tem, text_color_t *fg,
2380     text_color_t *bg, text_attr_t *attr, uint8_t flag)
2381 {
2382 	if (tem->tvs_flags & flag) {
2383 		*fg = tem->tvs_bg_color;
2384 		*bg = tem->tvs_fg_color;
2385 	} else {
2386 		*fg = tem->tvs_fg_color;
2387 		*bg = tem->tvs_bg_color;
2388 	}
2389 
2390 	if (attr == NULL)
2391 		return;
2392 
2393 	*attr = tem->tvs_flags;
2394 }
2395 
2396 static void
2397 tem_safe_get_color(text_color_t *fg, text_color_t *bg, term_char_t c)
2398 {
2399 	boolean_t bold_font;
2400 
2401 	*fg = c.tc_fg_color;
2402 	*bg = c.tc_bg_color;
2403 
2404 	bold_font = tems.ts_font.vf_map_count[VFNT_MAP_BOLD] != 0;
2405 
2406 	/*
2407 	 * If we have both normal and bold font components,
2408 	 * we use bold font for TEM_ATTR_BOLD.
2409 	 * The bright color is traditionally used with TEM_ATTR_BOLD,
2410 	 * in case there is no bold font.
2411 	 */
2412 	if (c.tc_fg_color < XLATE_NCOLORS) {
2413 		if (TEM_ATTR_ISSET(c.tc_char, TEM_ATTR_BRIGHT_FG) ||
2414 		    (TEM_ATTR_ISSET(c.tc_char, TEM_ATTR_BOLD) && !bold_font))
2415 			*fg = brt_xlate[c.tc_fg_color];
2416 		else
2417 			*fg = dim_xlate[c.tc_fg_color];
2418 	}
2419 
2420 	if (c.tc_bg_color < XLATE_NCOLORS) {
2421 		if (TEM_ATTR_ISSET(c.tc_char, TEM_ATTR_BRIGHT_BG))
2422 			*bg = brt_xlate[c.tc_bg_color];
2423 		else
2424 			*bg = dim_xlate[c.tc_bg_color];
2425 	}
2426 }
2427 
2428 /*
2429  * Clear a rectangle of screen for pixel mode.
2430  *
2431  * arguments:
2432  *    row:	start row#
2433  *    nrows:	the number of rows to clear
2434  *    offset_y:	the offset of height in pixels to begin clear
2435  *    col:	start col#
2436  *    ncols:	the number of cols to clear
2437  *    offset_x:	the offset of width in pixels to begin clear
2438  *    scroll_up: whether this function is called during sroll up,
2439  *		 which is called only once.
2440  */
2441 void
2442 tem_safe_pix_cls_range(struct tem_vt_state *tem,
2443     screen_pos_t row, int nrows, int offset_y,
2444     screen_pos_t col, int ncols, int offset_x,
2445     boolean_t sroll_up, cred_t *credp,
2446     enum called_from called_from)
2447 {
2448 	struct vis_consdisplay da;
2449 	int	i, j;
2450 	int	row_add = 0;
2451 	term_char_t c;
2452 	text_attr_t attr;
2453 
2454 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2455 	    called_from == CALLED_FROM_STANDALONE);
2456 
2457 	if (sroll_up)
2458 		row_add = tems.ts_c_dimension.height - 1;
2459 
2460 	da.width = (screen_size_t)tems.ts_font.vf_width;
2461 	da.height = (screen_size_t)tems.ts_font.vf_height;
2462 
2463 	tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2464 	    TEM_ATTR_SCREEN_REVERSE);
2465 	/* Make sure we will not draw underlines */
2466 	c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
2467 
2468 	tem_safe_callback_bit2pix(tem, c);
2469 	da.data = (uchar_t *)tem->tvs_pix_data;
2470 
2471 	for (i = 0; i < nrows; i++, row++) {
2472 		da.row = (row + row_add) * da.height + offset_y;
2473 		da.col = col * da.width + offset_x;
2474 		for (j = 0; j < ncols; j++) {
2475 			tems_safe_display(&da, credp, called_from);
2476 			da.col += da.width;
2477 		}
2478 	}
2479 }
2480 
2481 /*
2482  * virtual screen operations
2483  */
2484 static void
2485 tem_safe_virtual_display(struct tem_vt_state *tem, term_char_t *string,
2486     int count, screen_pos_t row, screen_pos_t col)
2487 {
2488 	int i, width;
2489 	term_char_t *addr;
2490 
2491 	if (row < 0 || row >= tems.ts_c_dimension.height ||
2492 	    col < 0 || col >= tems.ts_c_dimension.width ||
2493 	    col + count > tems.ts_c_dimension.width)
2494 		return;
2495 
2496 	width = tems.ts_c_dimension.width;
2497 	addr = tem->tvs_screen_buf + (row * width + col);
2498 	for (i = 0; i < count; i++) {
2499 		*addr++ = string[i];
2500 	}
2501 }
2502 
2503 static void
2504 i_virtual_copy_tem_chars(term_char_t *base,
2505     screen_pos_t s_col, screen_pos_t s_row,
2506     screen_pos_t e_col, screen_pos_t e_row,
2507     screen_pos_t t_col, screen_pos_t t_row)
2508 {
2509 	term_char_t	*from;
2510 	term_char_t	*to;
2511 	int		cnt;
2512 	screen_size_t chars_per_row;
2513 	term_char_t	*to_row_start;
2514 	term_char_t	*from_row_start;
2515 	screen_size_t   rows_to_move;
2516 	int		cols = tems.ts_c_dimension.width;
2517 
2518 	chars_per_row = e_col - s_col + 1;
2519 	rows_to_move = e_row - s_row + 1;
2520 
2521 	to_row_start = base + ((t_row * cols) + t_col);
2522 	from_row_start = base + ((s_row * cols) + s_col);
2523 
2524 	if (to_row_start < from_row_start) {
2525 		while (rows_to_move-- > 0) {
2526 			to = to_row_start;
2527 			from = from_row_start;
2528 			to_row_start += cols;
2529 			from_row_start += cols;
2530 			for (cnt = chars_per_row; cnt-- > 0; )
2531 				*to++ = *from++;
2532 		}
2533 	} else {
2534 		/*
2535 		 * Offset to the end of the region and copy backwards.
2536 		 */
2537 		cnt = rows_to_move * cols + chars_per_row;
2538 		to_row_start += cnt;
2539 		from_row_start += cnt;
2540 
2541 		while (rows_to_move-- > 0) {
2542 			to_row_start -= cols;
2543 			from_row_start -= cols;
2544 			to = to_row_start;
2545 			from = from_row_start;
2546 			for (cnt = chars_per_row; cnt-- > 0; )
2547 				*--to = *--from;
2548 		}
2549 	}
2550 }
2551 
2552 static void
2553 tem_safe_virtual_copy(struct tem_vt_state *tem,
2554     screen_pos_t s_col, screen_pos_t s_row,
2555     screen_pos_t e_col, screen_pos_t e_row,
2556     screen_pos_t t_col, screen_pos_t t_row)
2557 {
2558 	screen_size_t chars_per_row;
2559 	screen_size_t   rows_to_move;
2560 	int		rows = tems.ts_c_dimension.height;
2561 	int		cols = tems.ts_c_dimension.width;
2562 
2563 	if (s_col < 0 || s_col >= cols ||
2564 	    s_row < 0 || s_row >= rows ||
2565 	    e_col < 0 || e_col >= cols ||
2566 	    e_row < 0 || e_row >= rows ||
2567 	    t_col < 0 || t_col >= cols ||
2568 	    t_row < 0 || t_row >= rows ||
2569 	    s_col > e_col ||
2570 	    s_row > e_row)
2571 		return;
2572 
2573 	chars_per_row = e_col - s_col + 1;
2574 	rows_to_move = e_row - s_row + 1;
2575 
2576 	/* More sanity checks. */
2577 	if (t_row + rows_to_move > rows ||
2578 	    t_col + chars_per_row > cols)
2579 		return;
2580 
2581 	i_virtual_copy_tem_chars(tem->tvs_screen_buf, s_col, s_row,
2582 	    e_col, e_row, t_col, t_row);
2583 }
2584 
2585 static void
2586 tem_safe_virtual_cls(struct tem_vt_state *tem,
2587     int count, screen_pos_t row, screen_pos_t col)
2588 {
2589 	int i;
2590 	text_attr_t attr;
2591 	term_char_t c;
2592 
2593 	tem_safe_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2594 	    TEM_ATTR_SCREEN_REVERSE);
2595 	c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
2596 
2597 	for (i = 0; i < tems.ts_c_dimension.width; i++)
2598 		tems.ts_blank_line[i] = c;
2599 
2600 	tem_safe_virtual_display(tem, tems.ts_blank_line, count, row, col);
2601 }
2602 
2603 /*
2604  * only blank screen, not clear our screen buffer
2605  */
2606 void
2607 tem_safe_blank_screen(struct tem_vt_state *tem, cred_t *credp,
2608     enum called_from called_from)
2609 {
2610 	int	row;
2611 
2612 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2613 	    called_from == CALLED_FROM_STANDALONE);
2614 
2615 	if (tems.ts_display_mode == VIS_PIXEL) {
2616 		tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2617 		return;
2618 	}
2619 
2620 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2621 		tem_safe_callback_cls(tem,
2622 		    tems.ts_c_dimension.width,
2623 		    row, 0, credp, called_from);
2624 	}
2625 }
2626 
2627 /*
2628  * unblank screen with associated tem from its screen buffer
2629  */
2630 void
2631 tem_safe_unblank_screen(struct tem_vt_state *tem, cred_t *credp,
2632     enum called_from called_from)
2633 {
2634 	int	row;
2635 
2636 	ASSERT((MUTEX_HELD(&tems.ts_lock) && MUTEX_HELD(&tem->tvs_lock)) ||
2637 	    called_from == CALLED_FROM_STANDALONE);
2638 
2639 	if (tems.ts_display_mode == VIS_PIXEL)
2640 		tem_safe_pix_clear_entire_screen(tem, credp, called_from);
2641 
2642 	tem_safe_callback_cursor(tem, VIS_HIDE_CURSOR, credp, called_from);
2643 
2644 	/*
2645 	 * Display data in tvs_screen_buf to the actual framebuffer in a
2646 	 * row by row way.
2647 	 * When dealing with one row, output data with the same foreground
2648 	 * and background color all together.
2649 	 */
2650 	for (row = 0; row < tems.ts_c_dimension.height; row++) {
2651 		tem_safe_callback_display(tem, tem->tvs_screen_rows[row],
2652 		    tems.ts_c_dimension.width, row, 0, credp, called_from);
2653 	}
2654 
2655 	tem_safe_callback_cursor(tem, VIS_DISPLAY_CURSOR, credp, called_from);
2656 }
2657