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