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