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