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