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