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