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