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