Unleashed v1.4
[unleashed.git] / usr / src / boot / sys / boot / common / tem.c
blob8399f835efcd4137a0d7d1fc3299b0688ba6ce4a
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2016 Joyent, Inc.
29 * ANSI terminal emulator module; parse ANSI X3.64 escape sequences and
30 * the like.
32 * How Virtual Terminal Emulator Works:
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.
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).
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().
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.
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>
71 extern int lz4_decompress(void *, void *, size_t, size_t, int);
73 /* Terminal emulator internal helper functions */
74 static void tems_setup_terminal(struct vis_devinit *, size_t, size_t);
75 static void tems_modechange_callback(struct vis_modechg_arg *,
76 struct vis_devinit *);
78 static void tems_reset_colormap(void);
80 static void tem_free_buf(struct tem_vt_state *);
81 static void tem_internal_init(struct tem_vt_state *, boolean_t, boolean_t);
82 static void tems_get_initial_color(tem_color_t *pcolor);
84 static void tem_control(struct tem_vt_state *, uint8_t);
85 static void tem_setparam(struct tem_vt_state *, int, int);
86 static void tem_selgraph(struct tem_vt_state *);
87 static void tem_chkparam(struct tem_vt_state *, uint8_t);
88 static void tem_getparams(struct tem_vt_state *, uint8_t);
89 static void tem_outch(struct tem_vt_state *, tem_char_t);
90 static void tem_parse(struct tem_vt_state *, tem_char_t);
92 static void tem_new_line(struct tem_vt_state *);
93 static void tem_cr(struct tem_vt_state *);
94 static void tem_lf(struct tem_vt_state *);
95 static void tem_send_data(struct tem_vt_state *);
96 static void tem_cls(struct tem_vt_state *);
97 static void tem_tab(struct tem_vt_state *);
98 static void tem_back_tab(struct tem_vt_state *);
99 static void tem_clear_tabs(struct tem_vt_state *, int);
100 static void tem_set_tab(struct tem_vt_state *);
101 static void tem_mv_cursor(struct tem_vt_state *, int, int);
102 static void tem_shift(struct tem_vt_state *, int, int);
103 static void tem_scroll(struct tem_vt_state *, int, int, int, int);
104 static void tem_clear_chars(struct tem_vt_state *tem,
105 int count, screen_pos_t row, screen_pos_t col);
106 static void tem_copy_area(struct tem_vt_state *tem,
107 screen_pos_t s_col, screen_pos_t s_row,
108 screen_pos_t e_col, screen_pos_t e_row,
109 screen_pos_t t_col, screen_pos_t t_row);
110 static void tem_bell(struct tem_vt_state *tem);
111 static void tem_pix_clear_prom_output(struct tem_vt_state *tem);
113 static void tem_virtual_cls(struct tem_vt_state *, size_t, screen_pos_t,
114 screen_pos_t);
115 static void tem_virtual_display(struct tem_vt_state *, term_char_t *,
116 size_t, screen_pos_t, screen_pos_t);
117 static void tem_align_cursor(struct tem_vt_state *tem);
119 static void tem_check_first_time(struct tem_vt_state *tem);
120 static void tem_reset_display(struct tem_vt_state *, boolean_t, boolean_t);
121 static void tem_terminal_emulate(struct tem_vt_state *, uint8_t *, int);
122 static void tem_text_cursor(struct tem_vt_state *, short);
123 static void tem_text_cls(struct tem_vt_state *,
124 int count, screen_pos_t row, screen_pos_t col);
125 static void tem_pix_display(struct tem_vt_state *, term_char_t *,
126 int, screen_pos_t, screen_pos_t);
127 static void tem_pix_copy(struct tem_vt_state *,
128 screen_pos_t, screen_pos_t,
129 screen_pos_t, screen_pos_t,
130 screen_pos_t, screen_pos_t);
131 static void tem_pix_cursor(struct tem_vt_state *, short);
132 static void tem_get_attr(struct tem_vt_state *, text_color_t *,
133 text_color_t *, text_attr_t *, uint8_t);
134 static void tem_get_color(text_color_t *, text_color_t *, term_char_t);
135 static void tem_pix_align(struct tem_vt_state *);
136 static void tem_text_display(struct tem_vt_state *, term_char_t *, int,
137 screen_pos_t, screen_pos_t);
138 static void tem_text_copy(struct tem_vt_state *,
139 screen_pos_t, screen_pos_t, screen_pos_t, screen_pos_t,
140 screen_pos_t, screen_pos_t);
141 static void tem_pix_bit2pix(struct tem_vt_state *, term_char_t);
142 static void tem_pix_cls_range(struct tem_vt_state *, screen_pos_t, int,
143 int, screen_pos_t, int, int, boolean_t);
144 static void tem_pix_cls(struct tem_vt_state *, int,
145 screen_pos_t, screen_pos_t);
147 static void bit_to_pix4(struct tem_vt_state *tem, tem_char_t c,
148 text_color_t fg_color, text_color_t bg_color);
149 static void bit_to_pix8(struct tem_vt_state *tem, tem_char_t c,
150 text_color_t fg_color, text_color_t bg_color);
151 static void bit_to_pix16(struct tem_vt_state *tem, tem_char_t c,
152 text_color_t fg_color, text_color_t bg_color);
153 static void bit_to_pix24(struct tem_vt_state *tem, tem_char_t c,
154 text_color_t fg_color, text_color_t bg_color);
155 static void bit_to_pix32(struct tem_vt_state *tem, tem_char_t c,
156 text_color_t fg_color, text_color_t bg_color);
159 * Globals
161 tem_state_t tems; /* common term info */
163 tem_callbacks_t tem_text_callbacks = {
164 .tsc_display = &tem_text_display,
165 .tsc_copy = &tem_text_copy,
166 .tsc_cursor = &tem_text_cursor,
167 .tsc_bit2pix = NULL,
168 .tsc_cls = &tem_text_cls
170 tem_callbacks_t tem_pix_callbacks = {
171 .tsc_display = &tem_pix_display,
172 .tsc_copy = &tem_pix_copy,
173 .tsc_cursor = &tem_pix_cursor,
174 .tsc_bit2pix = &tem_pix_bit2pix,
175 .tsc_cls = &tem_pix_cls
178 #define tem_callback_display (*tems.ts_callbacks->tsc_display)
179 #define tem_callback_copy (*tems.ts_callbacks->tsc_copy)
180 #define tem_callback_cursor (*tems.ts_callbacks->tsc_cursor)
181 #define tem_callback_cls (*tems.ts_callbacks->tsc_cls)
182 #define tem_callback_bit2pix (*tems.ts_callbacks->tsc_bit2pix)
184 static void
185 tem_add(struct tem_vt_state *tem)
187 list_insert_head(&tems.ts_list, tem);
191 * This is the main entry point to the module. It handles output requests
192 * during normal system operation, when (e.g.) mutexes are available.
194 void
195 tem_write(tem_vt_state_t tem_arg, uint8_t *buf, ssize_t len)
197 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
199 if (!tem->tvs_initialized) {
200 return;
203 tem_check_first_time(tem);
204 tem_terminal_emulate(tem, buf, len);
207 static void
208 tem_internal_init(struct tem_vt_state *ptem,
209 boolean_t init_color, boolean_t clear_screen)
211 size_t size, width, height;
213 if (tems.ts_display_mode == VIS_PIXEL) {
214 ptem->tvs_pix_data_size = tems.ts_pix_data_size;
215 ptem->tvs_pix_data = malloc(ptem->tvs_pix_data_size);
218 width = tems.ts_c_dimension.width;
219 height = tems.ts_c_dimension.height;
221 size = width * sizeof (tem_char_t);
222 ptem->tvs_outbuf = malloc(size);
223 if (ptem->tvs_outbuf == NULL)
224 panic("out of memory in tem_internal_init()\n");
226 tem_reset_display(ptem, clear_screen, init_color);
228 ptem->tvs_utf8_left = 0;
229 ptem->tvs_utf8_partial = 0;
231 ptem->tvs_initialized = true;
234 * Out of memory is not fatal there, without the screen history,
235 * we can not optimize the screen copy.
237 size = width * height * sizeof (term_char_t);
238 ptem->tvs_screen_buf = malloc(size);
239 tem_virtual_cls(ptem, width * height, 0, 0);
243 tem_initialized(tem_vt_state_t tem_arg)
245 struct tem_vt_state *ptem = (struct tem_vt_state *)tem_arg;
247 return (ptem->tvs_initialized);
250 tem_vt_state_t
251 tem_init(void)
253 struct tem_vt_state *ptem;
255 ptem = malloc(sizeof (struct tem_vt_state));
256 if (ptem == NULL)
257 return ((tem_vt_state_t)ptem);
258 bzero(ptem, sizeof (*ptem));
260 ptem->tvs_isactive = false;
261 ptem->tvs_fbmode = KD_TEXT;
264 * A tem is regarded as initialized only after tem_internal_init(),
265 * will be set at the end of tem_internal_init().
267 ptem->tvs_initialized = 0;
269 if (!tems.ts_initialized) {
271 * Only happens during early console configuration.
273 tem_add(ptem);
274 return ((tem_vt_state_t)ptem);
277 tem_internal_init(ptem, B_TRUE, B_FALSE);
278 tem_add(ptem);
280 return ((tem_vt_state_t)ptem);
284 * re-init the tem after video mode has changed and tems_info has
285 * been re-inited.
287 static void
288 tem_reinit(struct tem_vt_state *tem, boolean_t reset_display)
290 tem_free_buf(tem); /* only free virtual buffers */
292 /* reserve color */
293 tem_internal_init(tem, B_FALSE, reset_display);
296 static void
297 tem_free_buf(struct tem_vt_state *tem)
299 free(tem->tvs_outbuf);
300 tem->tvs_outbuf = NULL;
302 free(tem->tvs_pix_data);
303 tem->tvs_pix_data = NULL;
305 free(tem->tvs_screen_buf);
306 tem->tvs_screen_buf = NULL;
309 static int
310 tems_failed(boolean_t finish_ioctl)
312 if (finish_ioctl && tems.ts_hdl != NULL)
313 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_DEVFINI, NULL);
315 tems.ts_hdl = NULL;
316 return (ENXIO);
320 * Only called once during boot
323 tem_info_init(struct console *cp)
325 int ret;
326 struct vis_devinit temargs;
327 size_t height = 0;
328 size_t width = 0;
329 struct tem_vt_state *p;
331 if (tems.ts_initialized) {
332 return (0);
335 list_create(&tems.ts_list, sizeof (struct tem_vt_state),
336 __offsetof(struct tem_vt_state, tvs_list_node));
337 tems.ts_active = NULL;
339 tems.ts_hdl = cp;
340 bzero(&temargs, sizeof (temargs));
341 temargs.modechg_cb = (vis_modechg_cb_t)tems_modechange_callback;
342 temargs.modechg_arg = NULL;
345 * Initialize the console and get the device parameters
347 if (cp->c_ioctl(cp, VIS_DEVINIT, &temargs) != 0) {
348 printf("terminal emulator: Compatible fb not found\n");
349 ret = tems_failed(B_FALSE);
350 return (ret);
353 /* Make sure the fb driver and terminal emulator versions match */
354 if (temargs.version != VIS_CONS_REV) {
355 printf(
356 "terminal emulator: VIS_CONS_REV %d (see sys/visual_io.h) "
357 "of console fb driver not supported\n", temargs.version);
358 ret = tems_failed(B_TRUE);
359 return (ret);
362 /* other sanity checks */
363 if (!((temargs.depth == 4) || (temargs.depth == 8) ||
364 (temargs.depth == 15) || (temargs.depth == 16) ||
365 (temargs.depth == 24) || (temargs.depth == 32))) {
366 printf("terminal emulator: unsupported depth\n");
367 ret = tems_failed(B_TRUE);
368 return (ret);
371 if ((temargs.mode != VIS_TEXT) && (temargs.mode != VIS_PIXEL)) {
372 printf("terminal emulator: unsupported mode\n");
373 ret = tems_failed(B_TRUE);
374 return (ret);
377 plat_tem_get_prom_size(&height, &width);
380 * Initialize the common terminal emulator info
382 tems_setup_terminal(&temargs, height, width);
384 tems_reset_colormap();
385 tems_get_initial_color(&tems.ts_init_color);
387 tems.ts_initialized = 1; /* initialization flag */
389 for (p = list_head(&tems.ts_list); p != NULL;
390 p = list_next(&tems.ts_list, p)) {
391 tem_internal_init(p, B_TRUE, B_FALSE);
392 if (temargs.mode == VIS_PIXEL)
393 tem_pix_align(p);
396 return (0);
399 #define TEMS_DEPTH_DIFF 0x01
400 #define TEMS_DIMENSION_DIFF 0x02
402 static uint8_t
403 tems_check_videomode(struct vis_devinit *tp)
405 uint8_t result = 0;
407 if (tems.ts_pdepth != tp->depth)
408 result |= TEMS_DEPTH_DIFF;
410 if (tp->mode == VIS_TEXT) {
411 if (tems.ts_c_dimension.width != tp->width ||
412 tems.ts_c_dimension.height != tp->height)
413 result |= TEMS_DIMENSION_DIFF;
414 } else {
415 if (tems.ts_p_dimension.width != tp->width ||
416 tems.ts_p_dimension.height != tp->height)
417 result |= TEMS_DIMENSION_DIFF;
419 if (tems.update_font == true)
420 result |= TEMS_DIMENSION_DIFF;
422 return (result);
425 static int
426 env_screen_nounset(struct env_var *ev __unused)
428 if (tems.ts_p_dimension.width == 0 &&
429 tems.ts_p_dimension.height == 0)
430 return (0);
431 return(EPERM);
434 static void
435 tems_setup_terminal(struct vis_devinit *tp, size_t height, size_t width)
437 bitmap_data_t *font_data;
438 int i;
439 char env[8];
441 tems.ts_pdepth = tp->depth;
442 tems.ts_linebytes = tp->linebytes;
443 tems.ts_display_mode = tp->mode;
444 tems.ts_color_map = tp->color_map;
446 switch (tp->mode) {
447 case VIS_TEXT:
448 tems.ts_p_dimension.width = 0;
449 tems.ts_p_dimension.height = 0;
450 tems.ts_c_dimension.width = tp->width;
451 tems.ts_c_dimension.height = tp->height;
452 tems.ts_callbacks = &tem_text_callbacks;
454 snprintf(env, sizeof (env), "%d", tems.ts_c_dimension.height);
455 env_setenv("screen-#rows", EV_VOLATILE | EV_NOHOOK, env,
456 env_noset, env_nounset);
457 snprintf(env, sizeof (env), "%d", tems.ts_c_dimension.width);
458 env_setenv("screen-#cols", EV_VOLATILE | EV_NOHOOK, env,
459 env_noset, env_nounset);
461 /* ensure the following are not set for text mode */
462 unsetenv("screen-height");
463 unsetenv("screen-width");
464 break;
466 case VIS_PIXEL:
468 * First check to see if the user has specified a screen size.
469 * If so, use those values. Else use 34x80 as the default.
471 if (width == 0) {
472 width = TEM_DEFAULT_COLS;
473 height = TEM_DEFAULT_ROWS;
475 tems.ts_c_dimension.height = (screen_size_t)height;
476 tems.ts_c_dimension.width = (screen_size_t)width;
478 tems.ts_p_dimension.height = tp->height;
479 tems.ts_p_dimension.width = tp->width;
481 tems.ts_callbacks = &tem_pix_callbacks;
484 * set_font() will select a appropriate sized font for
485 * the number of rows and columns selected. If we don't
486 * have a font that will fit, then it will use the
487 * default builtin font and adjust the rows and columns
488 * to fit on the screen.
490 font_data = set_font(&tems.ts_c_dimension.height,
491 &tems.ts_c_dimension.width,
492 tems.ts_p_dimension.height,
493 tems.ts_p_dimension.width);
496 * The built in font is compressed, to use it, we
497 * uncompress it into the allocated buffer.
498 * To use loaded font, we assign the loaded buffer.
499 * In case of next load, the previously loaded data
500 * is freed by the process of loading the new font.
502 tems.update_font = false;
504 if (tems.ts_font.vf_bytes == NULL) {
505 for (i = 0; i < VFNT_MAPS; i++) {
506 tems.ts_font.vf_map[i] =
507 font_data->font->vf_map[i];
510 if (font_data->compressed_size != 0) {
512 * We only expect this allocation to
513 * happen at startup, and therefore not to fail.
515 tems.ts_font.vf_bytes =
516 malloc(font_data->uncompressed_size);
517 if (tems.ts_font.vf_bytes == NULL)
518 panic("out of memory\n");
519 (void)lz4_decompress(font_data->compressed_data,
520 tems.ts_font.vf_bytes,
521 font_data->compressed_size,
522 font_data->uncompressed_size, 0);
523 } else {
524 tems.ts_font.vf_bytes =
525 font_data->font->vf_bytes;
527 tems.ts_font.vf_width = font_data->font->vf_width;
528 tems.ts_font.vf_height = font_data->font->vf_height;
529 for (i = 0; i < VFNT_MAPS; i++) {
530 tems.ts_font.vf_map_count[i] =
531 font_data->font->vf_map_count[i];
535 snprintf(env, sizeof (env), "%d", tems.ts_c_dimension.height);
536 env_setenv("screen-#rows", EV_VOLATILE | EV_NOHOOK, env,
537 env_noset, env_nounset);
538 snprintf(env, sizeof (env), "%d", tems.ts_c_dimension.width);
539 env_setenv("screen-#cols", EV_VOLATILE | EV_NOHOOK, env,
540 env_noset, env_nounset);
542 snprintf(env, sizeof (env), "%d", tems.ts_p_dimension.height);
543 env_setenv("screen-height", EV_VOLATILE | EV_NOHOOK, env,
544 env_noset, env_screen_nounset);
545 snprintf(env, sizeof (env), "%d", tems.ts_p_dimension.width);
546 env_setenv("screen-width", EV_VOLATILE | EV_NOHOOK, env,
547 env_noset, env_screen_nounset);
549 snprintf(env, sizeof (env), "%dx%d", tems.ts_font.vf_width,
550 tems.ts_font.vf_height);
551 env_setenv("screen-font", EV_VOLATILE | EV_NOHOOK, env, NULL,
552 NULL);
554 tems.ts_p_offset.y = (tems.ts_p_dimension.height -
555 (tems.ts_c_dimension.height * tems.ts_font.vf_height)) / 2;
556 tems.ts_p_offset.x = (tems.ts_p_dimension.width -
557 (tems.ts_c_dimension.width * tems.ts_font.vf_width)) / 2;
559 tems.ts_pix_data_size =
560 tems.ts_font.vf_width * tems.ts_font.vf_height;
562 tems.ts_pix_data_size *= 4;
564 tems.ts_pdepth = tp->depth;
566 break;
571 * This is a callback function that we register with the frame
572 * buffer driver layered underneath. It gets invoked from
573 * the underlying frame buffer driver to reconfigure the terminal
574 * emulator to a new screen size and depth in conjunction with
575 * framebuffer videomode changes.
576 * Here we keep the foreground/background color and attributes,
577 * which may be different with the initial settings, so that
578 * the color won't change while the framebuffer videomode changes.
579 * And we also reset the kernel terminal emulator and clear the
580 * whole screen.
582 /* ARGSUSED */
583 void
584 tems_modechange_callback(struct vis_modechg_arg *arg __unused,
585 struct vis_devinit *devinit)
587 uint8_t diff;
588 struct tem_vt_state *p;
589 tem_modechg_cb_t cb;
590 tem_modechg_cb_arg_t cb_arg;
591 size_t height = 0;
592 size_t width = 0;
594 diff = tems_check_videomode(devinit);
595 if (diff == 0) {
597 * This is color related change, reset color and redraw the
598 * screen. Only need to reinit the active tem.
600 struct tem_vt_state *active = tems.ts_active;
601 tems_get_initial_color(&tems.ts_init_color);
602 active->tvs_fg_color = tems.ts_init_color.fg_color;
603 active->tvs_bg_color = tems.ts_init_color.bg_color;
604 active->tvs_flags = tems.ts_init_color.a_flags;
605 tem_reinit(active, B_TRUE);
606 return;
609 diff = diff & TEMS_DIMENSION_DIFF;
611 if (diff == 0) {
613 * Only need to reinit the active tem.
615 struct tem_vt_state *active = tems.ts_active;
616 tems.ts_pdepth = devinit->depth;
617 /* color depth did change, reset colors */
618 tems_reset_colormap();
619 tems_get_initial_color(&tems.ts_init_color);
620 tem_reinit(active, B_TRUE);
622 return;
625 plat_tem_get_prom_size(&height, &width);
627 tems_setup_terminal(devinit, height, width);
629 tems_reset_colormap();
630 tems_get_initial_color(&tems.ts_init_color);
632 for (p = list_head(&tems.ts_list); p != NULL;
633 p = list_next(&tems.ts_list, p)) {
634 tem_reinit(p, p->tvs_isactive);
638 if (tems.ts_modechg_cb == NULL) {
639 return;
642 cb = tems.ts_modechg_cb;
643 cb_arg = tems.ts_modechg_arg;
645 cb(cb_arg);
649 * This function is used to clear entire screen via the underlying framebuffer
650 * driver.
653 tems_cls(struct vis_consclear *pda)
655 if (tems.ts_hdl == NULL)
656 return (1);
657 return (tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCLEAR, pda));
661 * This function is used to display a rectangular blit of data
662 * of a given size and location via the underlying framebuffer driver.
663 * The blit can be as small as a pixel or as large as the screen.
665 void
666 tems_display(struct vis_consdisplay *pda)
668 if (tems.ts_hdl != NULL)
669 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSDISPLAY, pda);
673 * This function is used to invoke a block copy operation in the
674 * underlying framebuffer driver. Rectangle copies are how scrolling
675 * is implemented, as well as horizontal text shifting escape seqs.
676 * such as from vi when deleting characters and words.
678 void
679 tems_copy(struct vis_conscopy *pma)
681 if (tems.ts_hdl != NULL)
682 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCOPY, pma);
686 * This function is used to show or hide a rectangluar monochrom
687 * pixel inverting, text block cursor via the underlying framebuffer.
689 void
690 tems_cursor(struct vis_conscursor *pca)
692 if (tems.ts_hdl != NULL)
693 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, VIS_CONSCURSOR, pca);
696 static void
697 tem_kdsetmode(int mode)
699 if (tems.ts_hdl != NULL)
700 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl, KDSETMODE,
701 (void *)(intptr_t)mode);
704 static void
705 tems_reset_colormap(void)
707 struct vis_cmap cm;
709 switch (tems.ts_pdepth) {
710 case 8:
711 cm.index = 0;
712 cm.count = 16;
713 /* 8-bits (1/3 of TrueColor 24) */
714 cm.red = (uint8_t *)cmap4_to_24.red;
715 /* 8-bits (1/3 of TrueColor 24) */
716 cm.blue = (uint8_t *)cmap4_to_24.blue;
717 /* 8-bits (1/3 of TrueColor 24) */
718 cm.green = (uint8_t *)cmap4_to_24.green;
719 if (tems.ts_hdl != NULL)
720 (void) tems.ts_hdl->c_ioctl(tems.ts_hdl,
721 VIS_PUTCMAP, &cm);
722 break;
726 void
727 tem_get_size(uint16_t *r, uint16_t *c, uint16_t *x, uint16_t *y)
729 *r = (uint16_t)tems.ts_c_dimension.height;
730 *c = (uint16_t)tems.ts_c_dimension.width;
731 *x = (uint16_t)tems.ts_p_dimension.width;
732 *y = (uint16_t)tems.ts_p_dimension.height;
736 * Loader extension. Store important data in environment. Intended to be used
737 * just before booting the OS to make the data available in kernel
738 * environment module.
740 void
741 tem_save_state(void)
743 struct tem_vt_state *active = tems.ts_active;
744 char buf[80];
747 * We already have in environment:
748 * tem.inverse, tem.inverse_screen
749 * tem.fg_color, tem.bg_color.
750 * So we only need to add the position of the cursor.
753 if (active != NULL) {
754 snprintf(buf, sizeof (buf), "%d", active->tvs_c_cursor.col);
755 setenv("tem.cursor.col", buf, 1);
756 snprintf(buf, sizeof (buf), "%d", active->tvs_c_cursor.row);
757 setenv("tem.cursor.row", buf, 1);
761 void
762 tem_register_modechg_cb(tem_modechg_cb_t func, tem_modechg_cb_arg_t arg)
764 tems.ts_modechg_cb = func;
765 tems.ts_modechg_arg = arg;
769 * This function is to scroll up the OBP output, which has
770 * different screen height and width with our kernel console.
772 static void
773 tem_prom_scroll_up(struct tem_vt_state *tem, int nrows)
775 struct vis_conscopy ma;
776 int ncols, width;
778 /* copy */
779 ma.s_row = nrows * tems.ts_font.vf_height;
780 ma.e_row = tems.ts_p_dimension.height - 1;
781 ma.t_row = 0;
783 ma.s_col = 0;
784 ma.e_col = tems.ts_p_dimension.width - 1;
785 ma.t_col = 0;
787 tems_copy(&ma);
789 /* clear */
790 width = tems.ts_font.vf_width;
791 ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
793 tem_pix_cls_range(tem, 0, nrows, tems.ts_p_offset.y,
794 0, ncols, 0, B_TRUE);
798 * This function is to compute the starting row of the console, according to
799 * PROM cursor's position. Here we have to take different fonts into account.
801 static int
802 tem_adjust_row(struct tem_vt_state *tem, int prom_row)
804 int tem_row;
805 int tem_y;
806 int prom_charheight = 0;
807 int prom_window_top = 0;
808 int scroll_up_lines;
810 plat_tem_get_prom_font_size(&prom_charheight, &prom_window_top);
811 if (prom_charheight == 0)
812 prom_charheight = tems.ts_font.vf_height;
814 tem_y = (prom_row + 1) * prom_charheight + prom_window_top -
815 tems.ts_p_offset.y;
816 tem_row = (tem_y + tems.ts_font.vf_height - 1) /
817 tems.ts_font.vf_height - 1;
819 if (tem_row < 0) {
820 tem_row = 0;
821 } else if (tem_row >= (tems.ts_c_dimension.height - 1)) {
823 * Scroll up the prom outputs if the PROM cursor's position is
824 * below our tem's lower boundary.
826 scroll_up_lines = tem_row -
827 (tems.ts_c_dimension.height - 1);
828 tem_prom_scroll_up(tem, scroll_up_lines);
829 tem_row = tems.ts_c_dimension.height - 1;
832 return (tem_row);
835 static void
836 tem_pix_align(struct tem_vt_state *tem)
838 uint32_t row = 0;
839 uint32_t col = 0;
841 if (plat_stdout_is_framebuffer()) {
842 plat_tem_hide_prom_cursor();
845 * We are getting the current cursor position in pixel
846 * mode so that we don't over-write the console output
847 * during boot.
849 plat_tem_get_prom_pos(&row, &col);
852 * Adjust the row if necessary when the font of our
853 * kernel console tem is different with that of prom
854 * tem.
856 row = tem_adjust_row(tem, row);
858 /* first line of our kernel console output */
859 tem->tvs_first_line = row + 1;
861 /* re-set and align cursor position */
862 tem->tvs_s_cursor.row = tem->tvs_c_cursor.row =
863 (screen_pos_t)row;
864 tem->tvs_s_cursor.col = tem->tvs_c_cursor.col = 0;
865 } else {
866 tem_reset_display(tem, B_TRUE, B_TRUE);
870 static void
871 tems_get_inverses(boolean_t *p_inverse, boolean_t *p_inverse_screen)
873 int i_inverse = 0;
874 int i_inverse_screen = 0;
876 plat_tem_get_inverses(&i_inverse, &i_inverse_screen);
878 *p_inverse = (i_inverse == 0) ? B_FALSE : B_TRUE;
879 *p_inverse_screen = (i_inverse_screen == 0) ? B_FALSE : B_TRUE;
883 * Get the foreground/background color and attributes from environment.
885 static void
886 tems_get_initial_color(tem_color_t *pcolor)
888 boolean_t inverse, inverse_screen;
889 unsigned short flags = 0;
891 pcolor->fg_color = DEFAULT_ANSI_FOREGROUND;
892 pcolor->bg_color = DEFAULT_ANSI_BACKGROUND;
893 plat_tem_get_colors(&pcolor->fg_color, &pcolor->bg_color);
895 tems_get_inverses(&inverse, &inverse_screen);
896 if (inverse)
897 flags |= TEM_ATTR_REVERSE;
898 if (inverse_screen)
899 flags |= TEM_ATTR_SCREEN_REVERSE;
902 * In case of black on white we want bright white for BG.
903 * In case if white on black, to improve readability,
904 * we want bold white.
906 if (flags != 0) {
908 * If either reverse flag is set, the screen is in
909 * white-on-black mode. We set the bold flag to
910 * improve readability.
912 flags |= TEM_ATTR_BOLD;
913 } else {
915 * Otherwise, the screen is in black-on-white mode.
916 * The SPARC PROM console, which starts in this mode,
917 * uses the bright white background colour so we
918 * match it here.
920 if (pcolor->bg_color == ANSI_COLOR_WHITE)
921 flags |= TEM_ATTR_BRIGHT_BG;
924 pcolor->a_flags = flags;
927 void
928 tem_activate(tem_vt_state_t tem_arg, boolean_t unblank)
930 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
932 tems.ts_active = tem;
933 tem->tvs_isactive = true;
935 tem_kdsetmode(tem->tvs_fbmode);
937 if (unblank)
938 tem_cls(tem);
941 static void
942 tem_check_first_time(struct tem_vt_state *tem)
944 static int first_time = 1;
947 * Realign the console cursor. We did this in tem_init().
948 * However, drivers in the console stream may emit additional
949 * messages before we are ready. This causes text overwrite
950 * on the screen. This is a workaround.
952 if (!first_time)
953 return;
955 first_time = 0;
956 if (tems.ts_display_mode == VIS_TEXT)
957 tem_text_cursor(tem, VIS_GET_CURSOR);
958 else
959 tem_pix_cursor(tem, VIS_GET_CURSOR);
960 tem_align_cursor(tem);
963 /* Process partial UTF-8 sequence. */
964 static void
965 tem_input_partial(struct tem_vt_state *tem)
967 unsigned i;
968 tem_char_t c;
970 if (tem->tvs_utf8_left == 0)
971 return;
973 for (i = 0; i < sizeof (tem->tvs_utf8_partial); i++) {
974 c = (tem->tvs_utf8_partial >> (24 - (i << 3))) & 0xff;
975 if (c != 0) {
976 tem_parse(tem, c);
979 tem->tvs_utf8_left = 0;
980 tem->tvs_utf8_partial = 0;
984 * Handle UTF-8 sequences.
986 static void
987 tem_input_byte(struct tem_vt_state *tem, uint8_t c)
990 * Check for UTF-8 code points. In case of error fall back to
991 * 8-bit code. As we only have 8859-1 fonts for console, this will set
992 * the limits on what chars we actually can display, therefore we
993 * have to return to this code once we have solved the font issue.
995 if ((c & 0x80) == 0x00) {
996 /* One-byte sequence. */
997 tem_input_partial(tem);
998 tem_parse(tem, c);
999 return;
1001 if ((c & 0xe0) == 0xc0) {
1002 /* Two-byte sequence. */
1003 tem_input_partial(tem);
1004 tem->tvs_utf8_left = 1;
1005 tem->tvs_utf8_partial = c;
1006 return;
1008 if ((c & 0xf0) == 0xe0) {
1009 /* Three-byte sequence. */
1010 tem_input_partial(tem);
1011 tem->tvs_utf8_left = 2;
1012 tem->tvs_utf8_partial = c;
1013 return;
1015 if ((c & 0xf8) == 0xf0) {
1016 /* Four-byte sequence. */
1017 tem_input_partial(tem);
1018 tem->tvs_utf8_left = 3;
1019 tem->tvs_utf8_partial = c;
1020 return;
1022 if ((c & 0xc0) == 0x80) {
1023 /* Invalid state? */
1024 if (tem->tvs_utf8_left == 0) {
1025 tem_parse(tem, c);
1026 return;
1028 tem->tvs_utf8_left--;
1029 tem->tvs_utf8_partial = (tem->tvs_utf8_partial << 8) | c;
1030 if (tem->tvs_utf8_left == 0) {
1031 tem_char_t v, u;
1032 uint8_t b;
1035 * Transform the sequence of 2 to 4 bytes to
1036 * unicode number.
1038 v = 0;
1039 u = tem->tvs_utf8_partial;
1040 b = (u >> 24) & 0xff;
1041 if (b != 0) { /* Four-byte sequence */
1042 v = b & 0x07;
1043 b = (u >> 16) & 0xff;
1044 v = (v << 6) | (b & 0x3f);
1045 b = (u >> 8) & 0xff;
1046 v = (v << 6) | (b & 0x3f);
1047 b = u & 0xff;
1048 v = (v << 6) | (b & 0x3f);
1049 } else if ((b = (u >> 16) & 0xff) != 0) {
1050 v = b & 0x0f; /* Three-byte sequence */
1051 b = (u >> 8) & 0xff;
1052 v = (v << 6) | (b & 0x3f);
1053 b = u & 0xff;
1054 v = (v << 6) | (b & 0x3f);
1055 } else if ((b = (u >> 8) & 0xff) != 0) {
1056 v = b & 0x1f; /* Two-byte sequence */
1057 b = u & 0xff;
1058 v = (v << 6) | (b & 0x3f);
1061 tem_parse(tem, v);
1062 tem->tvs_utf8_partial = 0;
1064 return;
1066 /* Anything left is illegal in UTF-8 sequence. */
1067 tem_input_partial(tem);
1068 tem_parse(tem, c);
1072 * This is the main entry point into the terminal emulator.
1074 * For each data message coming downstream, ANSI assumes that it is composed
1075 * of ASCII characters, which are treated as a byte-stream input to the
1076 * parsing state machine. All data is parsed immediately -- there is
1077 * no enqueing.
1079 static void
1080 tem_terminal_emulate(struct tem_vt_state *tem, uint8_t *buf, int len)
1082 if (tem->tvs_isactive)
1083 tem_callback_cursor(tem, VIS_HIDE_CURSOR);
1085 for (; len > 0; len--, buf++)
1086 tem_input_byte(tem, *buf);
1089 * Send the data we just got to the framebuffer.
1091 tem_send_data(tem);
1093 if (tem->tvs_isactive)
1094 tem_callback_cursor(tem, VIS_DISPLAY_CURSOR);
1098 * send the appropriate control message or set state based on the
1099 * value of the control character ch
1102 static void
1103 tem_control(struct tem_vt_state *tem, uint8_t ch)
1105 tem->tvs_state = A_STATE_START;
1106 switch (ch) {
1107 case A_BEL:
1108 tem_bell(tem);
1109 break;
1111 case A_BS:
1112 tem_mv_cursor(tem,
1113 tem->tvs_c_cursor.row,
1114 tem->tvs_c_cursor.col - 1);
1115 break;
1117 case A_HT:
1118 tem_tab(tem);
1119 break;
1121 case A_NL:
1123 * tem_send_data(tem, credp, called_from);
1124 * tem_new_line(tem, credp, called_from);
1125 * break;
1128 case A_VT:
1129 tem_send_data(tem);
1130 tem_lf(tem);
1131 break;
1133 case A_FF:
1134 tem_send_data(tem);
1135 tem_cls(tem);
1136 break;
1138 case A_CR:
1139 tem_send_data(tem);
1140 tem_cr(tem);
1141 break;
1143 case A_ESC:
1144 tem->tvs_state = A_STATE_ESC;
1145 break;
1147 case A_CSI:
1149 int i;
1150 tem->tvs_curparam = 0;
1151 tem->tvs_paramval = 0;
1152 tem->tvs_gotparam = B_FALSE;
1153 /* clear the parameters */
1154 for (i = 0; i < TEM_MAXPARAMS; i++)
1155 tem->tvs_params[i] = -1;
1156 tem->tvs_state = A_STATE_CSI;
1158 break;
1160 case A_GS:
1161 tem_back_tab(tem);
1162 break;
1164 default:
1165 break;
1171 * if parameters [0..count - 1] are not set, set them to the value
1172 * of newparam.
1175 static void
1176 tem_setparam(struct tem_vt_state *tem, int count, int newparam)
1178 int i;
1180 for (i = 0; i < count; i++) {
1181 if (tem->tvs_params[i] == -1)
1182 tem->tvs_params[i] = newparam;
1188 * select graphics mode based on the param vals stored in a_params
1190 static void
1191 tem_selgraph(struct tem_vt_state *tem)
1193 int curparam;
1194 int count = 0;
1195 int param;
1197 tem->tvs_state = A_STATE_START;
1199 curparam = tem->tvs_curparam;
1200 do {
1201 param = tem->tvs_params[count];
1203 switch (param) {
1204 case -1:
1205 case 0:
1206 /* reset to initial normal settings */
1207 tem->tvs_fg_color = tems.ts_init_color.fg_color;
1208 tem->tvs_bg_color = tems.ts_init_color.bg_color;
1209 tem->tvs_flags = tems.ts_init_color.a_flags;
1210 break;
1212 case 1: /* Bold Intense */
1213 tem->tvs_flags |= TEM_ATTR_BOLD;
1214 break;
1216 case 2: /* Faint Intense */
1217 tem->tvs_flags &= ~TEM_ATTR_BOLD;
1218 break;
1220 case 4: /* Underline */
1221 tem->tvs_flags |= TEM_ATTR_UNDERLINE;
1222 break;
1224 case 5: /* Blink */
1225 tem->tvs_flags |= TEM_ATTR_BLINK;
1226 break;
1228 case 7: /* Reverse video */
1229 if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
1230 tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1231 } else {
1232 tem->tvs_flags |= TEM_ATTR_REVERSE;
1234 break;
1236 case 22: /* Remove Bold */
1237 tem->tvs_flags &= ~TEM_ATTR_BOLD;
1238 break;
1240 case 24: /* Remove Underline */
1241 tem->tvs_flags &= ~TEM_ATTR_UNDERLINE;
1242 break;
1244 case 25: /* Remove Blink */
1245 tem->tvs_flags &= ~TEM_ATTR_BLINK;
1246 break;
1248 case 27: /* Remove Reverse */
1249 if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
1250 tem->tvs_flags |= TEM_ATTR_REVERSE;
1251 } else {
1252 tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1254 break;
1256 case 30: /* black (grey) foreground */
1257 case 31: /* red (light red) foreground */
1258 case 32: /* green (light green) foreground */
1259 case 33: /* brown (yellow) foreground */
1260 case 34: /* blue (light blue) foreground */
1261 case 35: /* magenta (light magenta) foreground */
1262 case 36: /* cyan (light cyan) foreground */
1263 case 37: /* white (bright white) foreground */
1264 tem->tvs_fg_color = param - 30;
1265 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
1266 break;
1268 case 39:
1270 * Reset the foreground colour and brightness.
1272 tem->tvs_fg_color = tems.ts_init_color.fg_color;
1273 if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_FG)
1274 tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
1275 else
1276 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_FG;
1277 break;
1279 case 40: /* black (grey) background */
1280 case 41: /* red (light red) background */
1281 case 42: /* green (light green) background */
1282 case 43: /* brown (yellow) background */
1283 case 44: /* blue (light blue) background */
1284 case 45: /* magenta (light magenta) background */
1285 case 46: /* cyan (light cyan) background */
1286 case 47: /* white (bright white) background */
1287 tem->tvs_bg_color = param - 40;
1288 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
1289 break;
1291 case 49:
1293 * Reset the background colour and brightness.
1295 tem->tvs_bg_color = tems.ts_init_color.bg_color;
1296 if (tems.ts_init_color.a_flags & TEM_ATTR_BRIGHT_BG)
1297 tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
1298 else
1299 tem->tvs_flags &= ~TEM_ATTR_BRIGHT_BG;
1300 break;
1302 case 90: /* black (grey) foreground */
1303 case 91: /* red (light red) foreground */
1304 case 92: /* green (light green) foreground */
1305 case 93: /* brown (yellow) foreground */
1306 case 94: /* blue (light blue) foreground */
1307 case 95: /* magenta (light magenta) foreground */
1308 case 96: /* cyan (light cyan) foreground */
1309 case 97: /* white (bright white) foreground */
1310 tem->tvs_fg_color = param - 90;
1311 tem->tvs_flags |= TEM_ATTR_BRIGHT_FG;
1312 break;
1314 case 100: /* black (grey) background */
1315 case 101: /* red (light red) background */
1316 case 102: /* green (light green) background */
1317 case 103: /* brown (yellow) background */
1318 case 104: /* blue (light blue) background */
1319 case 105: /* magenta (light magenta) background */
1320 case 106: /* cyan (light cyan) background */
1321 case 107: /* white (bright white) background */
1322 tem->tvs_bg_color = param - 100;
1323 tem->tvs_flags |= TEM_ATTR_BRIGHT_BG;
1324 break;
1326 default:
1327 break;
1329 count++;
1330 curparam--;
1332 } while (curparam > 0);
1336 * perform the appropriate action for the escape sequence
1338 * General rule: This code does not validate the arguments passed.
1339 * It assumes that the next lower level will do so.
1341 static void
1342 tem_chkparam(struct tem_vt_state *tem, uint8_t ch)
1344 int i;
1345 int row;
1346 int col;
1348 row = tem->tvs_c_cursor.row;
1349 col = tem->tvs_c_cursor.col;
1351 switch (ch) {
1353 case 'm': /* select terminal graphics mode */
1354 tem_send_data(tem);
1355 tem_selgraph(tem);
1356 break;
1358 case '@': /* insert char */
1359 tem_setparam(tem, 1, 1);
1360 tem_shift(tem, tem->tvs_params[0], TEM_SHIFT_RIGHT);
1361 break;
1363 case 'A': /* cursor up */
1364 tem_setparam(tem, 1, 1);
1365 tem_mv_cursor(tem, row - tem->tvs_params[0], col);
1366 break;
1368 case 'd': /* VPA - vertical position absolute */
1369 tem_setparam(tem, 1, 1);
1370 tem_mv_cursor(tem, tem->tvs_params[0] - 1, col);
1371 break;
1373 case 'e': /* VPR - vertical position relative */
1374 case 'B': /* cursor down */
1375 tem_setparam(tem, 1, 1);
1376 tem_mv_cursor(tem, row + tem->tvs_params[0], col);
1377 break;
1379 case 'a': /* HPR - horizontal position relative */
1380 case 'C': /* cursor right */
1381 tem_setparam(tem, 1, 1);
1382 tem_mv_cursor(tem, row, col + tem->tvs_params[0]);
1383 break;
1385 case '`': /* HPA - horizontal position absolute */
1386 tem_setparam(tem, 1, 1);
1387 tem_mv_cursor(tem, row, tem->tvs_params[0] - 1);
1388 break;
1390 case 'D': /* cursor left */
1391 tem_setparam(tem, 1, 1);
1392 tem_mv_cursor(tem, row, col - tem->tvs_params[0]);
1393 break;
1395 case 'E': /* CNL cursor next line */
1396 tem_setparam(tem, 1, 1);
1397 tem_mv_cursor(tem, row + tem->tvs_params[0], 0);
1398 break;
1400 case 'F': /* CPL cursor previous line */
1401 tem_setparam(tem, 1, 1);
1402 tem_mv_cursor(tem, row - tem->tvs_params[0], 0);
1403 break;
1405 case 'G': /* cursor horizontal position */
1406 tem_setparam(tem, 1, 1);
1407 tem_mv_cursor(tem, row, tem->tvs_params[0] - 1);
1408 break;
1410 case 'g': /* clear tabs */
1411 tem_setparam(tem, 1, 0);
1412 tem_clear_tabs(tem, tem->tvs_params[0]);
1413 break;
1415 case 'f': /* HVP Horizontal and Vertical Position */
1416 case 'H': /* CUP position cursor */
1417 tem_setparam(tem, 2, 1);
1418 tem_mv_cursor(tem,
1419 tem->tvs_params[0] - 1, tem->tvs_params[1] - 1);
1420 break;
1422 case 'I': /* CHT - Cursor Horizontal Tab */
1423 /* Not implemented */
1424 break;
1426 case 'J': /* ED - Erase in Display */
1427 tem_send_data(tem);
1428 tem_setparam(tem, 1, 0);
1429 switch (tem->tvs_params[0]) {
1430 case 0:
1431 /* erase cursor to end of screen */
1432 /* FIRST erase cursor to end of line */
1433 tem_clear_chars(tem,
1434 tems.ts_c_dimension.width -
1435 tem->tvs_c_cursor.col,
1436 tem->tvs_c_cursor.row,
1437 tem->tvs_c_cursor.col);
1439 /* THEN erase lines below the cursor */
1440 for (row = tem->tvs_c_cursor.row + 1;
1441 row < tems.ts_c_dimension.height;
1442 row++) {
1443 tem_clear_chars(tem,
1444 tems.ts_c_dimension.width, row, 0);
1446 break;
1448 case 1:
1449 /* erase beginning of screen to cursor */
1450 /* FIRST erase lines above the cursor */
1451 for (row = 0;
1452 row < tem->tvs_c_cursor.row;
1453 row++) {
1454 tem_clear_chars(tem,
1455 tems.ts_c_dimension.width, row, 0);
1457 /* THEN erase beginning of line to cursor */
1458 tem_clear_chars(tem,
1459 tem->tvs_c_cursor.col + 1,
1460 tem->tvs_c_cursor.row, 0);
1461 break;
1463 case 2:
1464 /* erase whole screen */
1465 for (row = 0;
1466 row < tems.ts_c_dimension.height;
1467 row++) {
1468 tem_clear_chars(tem,
1469 tems.ts_c_dimension.width, row, 0);
1471 break;
1473 break;
1475 case 'K': /* EL - Erase in Line */
1476 tem_send_data(tem);
1477 tem_setparam(tem, 1, 0);
1478 switch (tem->tvs_params[0]) {
1479 case 0:
1480 /* erase cursor to end of line */
1481 tem_clear_chars(tem,
1482 (tems.ts_c_dimension.width -
1483 tem->tvs_c_cursor.col),
1484 tem->tvs_c_cursor.row,
1485 tem->tvs_c_cursor.col);
1486 break;
1488 case 1:
1489 /* erase beginning of line to cursor */
1490 tem_clear_chars(tem,
1491 tem->tvs_c_cursor.col + 1,
1492 tem->tvs_c_cursor.row, 0);
1493 break;
1495 case 2:
1496 /* erase whole line */
1497 tem_clear_chars(tem,
1498 tems.ts_c_dimension.width,
1499 tem->tvs_c_cursor.row, 0);
1500 break;
1502 break;
1504 case 'L': /* insert line */
1505 tem_send_data(tem);
1506 tem_setparam(tem, 1, 1);
1507 tem_scroll(tem,
1508 tem->tvs_c_cursor.row,
1509 tems.ts_c_dimension.height - 1,
1510 tem->tvs_params[0], TEM_SCROLL_DOWN);
1511 break;
1513 case 'M': /* delete line */
1514 tem_send_data(tem);
1515 tem_setparam(tem, 1, 1);
1516 tem_scroll(tem,
1517 tem->tvs_c_cursor.row,
1518 tems.ts_c_dimension.height - 1,
1519 tem->tvs_params[0], TEM_SCROLL_UP);
1520 break;
1522 case 'P': /* DCH - delete char */
1523 tem_setparam(tem, 1, 1);
1524 tem_shift(tem, tem->tvs_params[0], TEM_SHIFT_LEFT);
1525 break;
1527 case 'S': /* scroll up */
1528 tem_send_data(tem);
1529 tem_setparam(tem, 1, 1);
1530 tem_scroll(tem, 0,
1531 tems.ts_c_dimension.height - 1,
1532 tem->tvs_params[0], TEM_SCROLL_UP);
1533 break;
1535 case 'T': /* scroll down */
1536 tem_send_data(tem);
1537 tem_setparam(tem, 1, 1);
1538 tem_scroll(tem, 0,
1539 tems.ts_c_dimension.height - 1,
1540 tem->tvs_params[0], TEM_SCROLL_DOWN);
1541 break;
1543 case 'X': /* erase char */
1544 tem_setparam(tem, 1, 1);
1545 tem_clear_chars(tem,
1546 tem->tvs_params[0],
1547 tem->tvs_c_cursor.row,
1548 tem->tvs_c_cursor.col);
1549 break;
1551 case 'Z': /* cursor backward tabulation */
1552 tem_setparam(tem, 1, 1);
1555 * Rule exception - We do sanity checking here.
1557 * Restrict the count to a sane value to keep from
1558 * looping for a long time. There can't be more than one
1559 * tab stop per column, so use that as a limit.
1561 if (tem->tvs_params[0] > tems.ts_c_dimension.width)
1562 tem->tvs_params[0] = tems.ts_c_dimension.width;
1564 for (i = 0; i < tem->tvs_params[0]; i++)
1565 tem_back_tab(tem);
1566 break;
1568 tem->tvs_state = A_STATE_START;
1573 * Gather the parameters of an ANSI escape sequence
1575 static void
1576 tem_getparams(struct tem_vt_state *tem, uint8_t ch)
1578 if (ch >= '0' && ch <= '9') {
1579 tem->tvs_paramval = ((tem->tvs_paramval * 10) + (ch - '0'));
1580 tem->tvs_gotparam = B_TRUE; /* Remember got parameter */
1581 return; /* Return immediately */
1582 } else if (tem->tvs_state == A_STATE_CSI_EQUAL ||
1583 tem->tvs_state == A_STATE_CSI_QMARK) {
1584 tem->tvs_state = A_STATE_START;
1585 } else {
1586 if (tem->tvs_curparam < TEM_MAXPARAMS) {
1587 if (tem->tvs_gotparam) {
1588 /* get the parameter value */
1589 tem->tvs_params[tem->tvs_curparam] =
1590 tem->tvs_paramval;
1592 tem->tvs_curparam++;
1595 if (ch == ';') {
1596 /* Restart parameter search */
1597 tem->tvs_gotparam = B_FALSE;
1598 tem->tvs_paramval = 0; /* No parame value yet */
1599 } else {
1600 /* Handle escape sequence */
1601 tem_chkparam(tem, ch);
1607 * Add character to internal buffer.
1608 * When its full, send it to the next layer.
1610 static void
1611 tem_outch(struct tem_vt_state *tem, tem_char_t ch)
1613 text_color_t fg;
1614 text_color_t bg;
1615 text_attr_t attr;
1617 /* buffer up the character until later */
1618 tem_get_attr(tem, &fg, &bg, &attr, TEM_ATTR_REVERSE);
1619 tem->tvs_outbuf[tem->tvs_outindex].tc_char = ch | TEM_ATTR(attr);
1620 tem->tvs_outbuf[tem->tvs_outindex].tc_fg_color = fg;
1621 tem->tvs_outbuf[tem->tvs_outindex].tc_bg_color = bg;
1622 tem->tvs_outindex++;
1623 tem->tvs_c_cursor.col++;
1624 if (tem->tvs_c_cursor.col >= tems.ts_c_dimension.width) {
1625 tem_send_data(tem);
1626 tem_new_line(tem);
1630 static void
1631 tem_new_line(struct tem_vt_state *tem)
1633 tem_cr(tem);
1634 tem_lf(tem);
1637 static void
1638 tem_cr(struct tem_vt_state *tem)
1640 tem->tvs_c_cursor.col = 0;
1641 tem_align_cursor(tem);
1644 static void
1645 tem_lf(struct tem_vt_state *tem)
1647 int row;
1650 * Sanity checking notes:
1651 * . a_nscroll was validated when it was set.
1652 * . Regardless of that, tem_scroll and tem_mv_cursor
1653 * will prevent anything bad from happening.
1655 row = tem->tvs_c_cursor.row + 1;
1657 if (row >= tems.ts_c_dimension.height) {
1658 if (tem->tvs_nscroll != 0) {
1659 tem_scroll(tem, 0,
1660 tems.ts_c_dimension.height - 1,
1661 tem->tvs_nscroll, TEM_SCROLL_UP);
1662 row = tems.ts_c_dimension.height -
1663 tem->tvs_nscroll;
1664 } else { /* no scroll */
1666 * implement Esc[#r when # is zero. This means no
1667 * scroll but just return cursor to top of screen,
1668 * do not clear screen.
1670 row = 0;
1674 tem_mv_cursor(tem, row, tem->tvs_c_cursor.col);
1676 if (tem->tvs_nscroll == 0) {
1677 /* erase rest of cursor line */
1678 tem_clear_chars(tem,
1679 tems.ts_c_dimension.width -
1680 tem->tvs_c_cursor.col,
1681 tem->tvs_c_cursor.row,
1682 tem->tvs_c_cursor.col);
1686 tem_align_cursor(tem);
1689 static void
1690 tem_send_data(struct tem_vt_state *tem)
1692 if (tem->tvs_outindex == 0) {
1693 tem_align_cursor(tem);
1694 return;
1697 tem_virtual_display(tem, tem->tvs_outbuf, tem->tvs_outindex,
1698 tem->tvs_s_cursor.row, tem->tvs_s_cursor.col);
1700 if (tem->tvs_isactive) {
1702 * Call the primitive to render this data.
1704 tem_callback_display(tem,
1705 tem->tvs_outbuf, tem->tvs_outindex,
1706 tem->tvs_s_cursor.row, tem->tvs_s_cursor.col);
1709 tem->tvs_outindex = 0;
1711 tem_align_cursor(tem);
1716 * We have just done something to the current output point. Reset the start
1717 * point for the buffered data in a_outbuf. There shouldn't be any data
1718 * buffered yet.
1720 static void
1721 tem_align_cursor(struct tem_vt_state *tem)
1723 tem->tvs_s_cursor.row = tem->tvs_c_cursor.row;
1724 tem->tvs_s_cursor.col = tem->tvs_c_cursor.col;
1728 * State machine parser based on the current state and character input
1729 * major terminations are to control character or normal character
1732 static void
1733 tem_parse(struct tem_vt_state *tem, tem_char_t ch)
1735 int i;
1737 if (tem->tvs_state == A_STATE_START) { /* Normal state? */
1738 if (ch == A_CSI || ch == A_ESC || ch < ' ') {
1739 /* Control */
1740 tem_control(tem, ch);
1741 } else {
1742 /* Display */
1743 tem_outch(tem, ch);
1745 return;
1748 /* In <ESC> sequence */
1749 if (tem->tvs_state != A_STATE_ESC) { /* Need to get parameters? */
1750 if (tem->tvs_state != A_STATE_CSI) {
1751 tem_getparams(tem, ch);
1752 return;
1755 switch (ch) {
1756 case '?':
1757 tem->tvs_state = A_STATE_CSI_QMARK;
1758 return;
1759 case '=':
1760 tem->tvs_state = A_STATE_CSI_EQUAL;
1761 return;
1762 case 's':
1764 * As defined below, this sequence
1765 * saves the cursor. However, Sun
1766 * defines ESC[s as reset. We resolved
1767 * the conflict by selecting reset as it
1768 * is exported in the termcap file for
1769 * sun-mon, while the "save cursor"
1770 * definition does not exist anywhere in
1771 * /etc/termcap.
1772 * However, having no coherent
1773 * definition of reset, we have not
1774 * implemented it.
1778 * Original code
1779 * tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1780 * tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1781 * tem->tvs_state = A_STATE_START;
1784 tem->tvs_state = A_STATE_START;
1785 return;
1786 case 'u':
1787 tem_mv_cursor(tem, tem->tvs_r_cursor.row,
1788 tem->tvs_r_cursor.col);
1789 tem->tvs_state = A_STATE_START;
1790 return;
1791 case 'p': /* sunbow */
1792 tem_send_data(tem);
1794 * Don't set anything if we are
1795 * already as we want to be.
1797 if (tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE) {
1798 tem->tvs_flags &= ~TEM_ATTR_SCREEN_REVERSE;
1800 * If we have switched the characters to be the
1801 * inverse from the screen, then switch them as
1802 * well to keep them the inverse of the screen.
1804 if (tem->tvs_flags & TEM_ATTR_REVERSE)
1805 tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1806 else
1807 tem->tvs_flags |= TEM_ATTR_REVERSE;
1809 tem_cls(tem);
1810 tem->tvs_state = A_STATE_START;
1811 return;
1812 case 'q': /* sunwob */
1813 tem_send_data(tem);
1815 * Don't set anything if we are
1816 * already where as we want to be.
1818 if (!(tem->tvs_flags & TEM_ATTR_SCREEN_REVERSE)) {
1819 tem->tvs_flags |= TEM_ATTR_SCREEN_REVERSE;
1821 * If we have switched the characters to be the
1822 * inverse from the screen, then switch them as
1823 * well to keep them the inverse of the screen.
1825 if (!(tem->tvs_flags & TEM_ATTR_REVERSE))
1826 tem->tvs_flags |= TEM_ATTR_REVERSE;
1827 else
1828 tem->tvs_flags &= ~TEM_ATTR_REVERSE;
1831 tem_cls(tem);
1832 tem->tvs_state = A_STATE_START;
1833 return;
1834 case 'r': /* sunscrl */
1836 * Rule exception: check for validity here.
1838 tem->tvs_nscroll = tem->tvs_paramval;
1839 if (tem->tvs_nscroll > tems.ts_c_dimension.height)
1840 tem->tvs_nscroll = tems.ts_c_dimension.height;
1841 if (tem->tvs_nscroll < 0)
1842 tem->tvs_nscroll = 1;
1843 tem->tvs_state = A_STATE_START;
1844 return;
1845 default:
1846 tem_getparams(tem, ch);
1847 return;
1851 /* Previous char was <ESC> */
1852 if (ch == '[') {
1853 tem->tvs_curparam = 0;
1854 tem->tvs_paramval = 0;
1855 tem->tvs_gotparam = B_FALSE;
1856 /* clear the parameters */
1857 for (i = 0; i < TEM_MAXPARAMS; i++)
1858 tem->tvs_params[i] = -1;
1859 tem->tvs_state = A_STATE_CSI;
1860 } else if (ch == 'Q') { /* <ESC>Q ? */
1861 tem->tvs_state = A_STATE_START;
1862 } else if (ch == 'C') { /* <ESC>C ? */
1863 tem->tvs_state = A_STATE_START;
1864 } else {
1865 tem->tvs_state = A_STATE_START;
1866 if (ch == 'c') {
1867 /* ESC c resets display */
1868 tem_reset_display(tem, B_TRUE, B_TRUE);
1869 } else if (ch == 'H') {
1870 /* ESC H sets a tab */
1871 tem_set_tab(tem);
1872 } else if (ch == '7') {
1873 /* ESC 7 Save Cursor position */
1874 tem->tvs_r_cursor.row = tem->tvs_c_cursor.row;
1875 tem->tvs_r_cursor.col = tem->tvs_c_cursor.col;
1876 } else if (ch == '8') {
1877 /* ESC 8 Restore Cursor position */
1878 tem_mv_cursor(tem, tem->tvs_r_cursor.row,
1879 tem->tvs_r_cursor.col);
1880 /* check for control chars */
1881 } else if (ch < ' ') {
1882 tem_control(tem, ch);
1883 } else {
1884 tem_outch(tem, ch);
1889 /* ARGSUSED */
1890 static void
1891 tem_bell(struct tem_vt_state *tem __unused)
1893 /* (void) beep(BEEP_CONSOLE); */
1897 static void
1898 tem_scroll(struct tem_vt_state *tem, int start, int end, int count,
1899 int direction)
1901 int row;
1902 int lines_affected;
1904 lines_affected = end - start + 1;
1905 if (count > lines_affected)
1906 count = lines_affected;
1907 if (count <= 0)
1908 return;
1910 switch (direction) {
1911 case TEM_SCROLL_UP:
1912 if (count < lines_affected) {
1913 tem_copy_area(tem, 0, start + count,
1914 tems.ts_c_dimension.width - 1, end, 0, start);
1916 for (row = (end - count) + 1; row <= end; row++) {
1917 tem_clear_chars(tem, tems.ts_c_dimension.width, row, 0);
1919 break;
1921 case TEM_SCROLL_DOWN:
1922 if (count < lines_affected) {
1923 tem_copy_area(tem, 0, start,
1924 tems.ts_c_dimension.width - 1,
1925 end - count, 0, start + count);
1927 for (row = start; row < start + count; row++) {
1928 tem_clear_chars(tem, tems.ts_c_dimension.width, row, 0);
1930 break;
1934 static int
1935 tem_copy_width(term_char_t *src, term_char_t *dst, int cols)
1937 int width = cols - 1;
1939 while (width >= 0) {
1940 /* We do not have image bits to compare, stop there. */
1941 if (TEM_CHAR_ATTR(src[width].tc_char) == TEM_ATTR_IMAGE ||
1942 TEM_CHAR_ATTR(dst[width].tc_char) == TEM_ATTR_IMAGE)
1943 break;
1946 * Find difference on line, compare char with its attributes
1947 * and colors.
1949 if (src[width].tc_char != dst[width].tc_char ||
1950 src[width].tc_fg_color != dst[width].tc_fg_color ||
1951 src[width].tc_bg_color != dst[width].tc_bg_color) {
1952 break;
1954 width--;
1956 return (width + 1);
1959 static void
1960 tem_copy_area(struct tem_vt_state *tem,
1961 screen_pos_t s_col, screen_pos_t s_row,
1962 screen_pos_t e_col, screen_pos_t e_row,
1963 screen_pos_t t_col, screen_pos_t t_row)
1965 size_t soffset, toffset;
1966 term_char_t *src, *dst;
1967 int rows;
1968 int cols;
1970 if (s_col < 0 || s_row < 0 ||
1971 e_col < 0 || e_row < 0 ||
1972 t_col < 0 || t_row < 0 ||
1973 s_col >= tems.ts_c_dimension.width ||
1974 e_col >= tems.ts_c_dimension.width ||
1975 t_col >= tems.ts_c_dimension.width ||
1976 s_row >= tems.ts_c_dimension.height ||
1977 e_row >= tems.ts_c_dimension.height ||
1978 t_row >= tems.ts_c_dimension.height)
1979 return;
1981 if (s_row > e_row || s_col > e_col)
1982 return;
1984 rows = e_row - s_row + 1;
1985 cols = e_col - s_col + 1;
1986 if (t_row + rows > tems.ts_c_dimension.height ||
1987 t_col + cols > tems.ts_c_dimension.width)
1988 return;
1990 if (tem->tvs_screen_buf == NULL) {
1991 if (tem->tvs_isactive) {
1992 tem_callback_copy(tem, s_col, s_row,
1993 e_col, e_row, t_col, t_row);
1995 return;
1998 soffset = s_col + s_row * tems.ts_c_dimension.width;
1999 toffset = t_col + t_row * tems.ts_c_dimension.width;
2000 src = tem->tvs_screen_buf + soffset;
2001 dst = tem->tvs_screen_buf + toffset;
2004 * Copy line by line. We determine the length by comparing the
2005 * screen content from cached text in tvs_screen_buf.
2007 if (toffset <= soffset) {
2008 for (int i = 0; i < rows; i++) {
2009 int increment = i * tems.ts_c_dimension.width;
2010 int width;
2012 width = tem_copy_width(src + increment,
2013 dst + increment, cols);
2014 memmove(dst + increment, src + increment,
2015 width * sizeof (term_char_t));
2016 if (tem->tvs_isactive) {
2017 tem_callback_copy(tem, s_col, s_row + i,
2018 e_col - cols + width, s_row + i,
2019 t_col, t_row + i);
2022 } else {
2023 for (int i = rows - 1; i >= 0; i--) {
2024 int increment = i * tems.ts_c_dimension.width;
2025 int width;
2027 width = tem_copy_width(src + increment,
2028 dst + increment, cols);
2029 memmove(dst + increment, src + increment,
2030 width * sizeof (term_char_t));
2031 if (tem->tvs_isactive) {
2032 tem_callback_copy(tem, s_col, s_row + i,
2033 e_col - cols + width, s_row + i,
2034 t_col, t_row + i);
2040 static void
2041 tem_clear_chars(struct tem_vt_state *tem, int count, screen_pos_t row,
2042 screen_pos_t col)
2044 if (row < 0 || row >= tems.ts_c_dimension.height ||
2045 col < 0 || col >= tems.ts_c_dimension.width ||
2046 count < 0)
2047 return;
2050 * Note that very large values of "count" could cause col+count
2051 * to overflow, so we check "count" independently.
2053 if (count > tems.ts_c_dimension.width ||
2054 col + count > tems.ts_c_dimension.width)
2055 count = tems.ts_c_dimension.width - col;
2057 tem_virtual_cls(tem, count, row, col);
2059 if (!tem->tvs_isactive)
2060 return;
2062 tem_callback_cls(tem, count, row, col);
2065 static void
2066 tem_text_display(struct tem_vt_state *tem __unused, term_char_t *string,
2067 int count, screen_pos_t row, screen_pos_t col)
2069 struct vis_consdisplay da;
2070 int i;
2071 tem_char_t c;
2073 if (count == 0)
2074 return;
2076 da.data = (unsigned char *)&c;
2077 da.width = 1;
2078 da.row = row;
2079 da.col = col;
2081 for (i = 0; i < count; i++) {
2082 tem_get_color(&da.fg_color, &da.bg_color, string[i]);
2083 c = TEM_CHAR(string[i].tc_char);
2084 tems_display(&da);
2085 da.col++;
2090 * This function is used to mark a rectangular image area so the scrolling
2091 * will know we need to copy the data from there.
2093 void
2094 tem_image_display(struct tem_vt_state *tem, screen_pos_t s_row,
2095 screen_pos_t s_col, screen_pos_t e_row, screen_pos_t e_col)
2097 screen_pos_t i, j;
2098 term_char_t c;
2100 c.tc_char = TEM_ATTR(TEM_ATTR_IMAGE);
2102 for (i = s_row; i <= e_row; i++) {
2103 for (j = s_col; j <= e_col; j++) {
2104 tem_virtual_display(tem, &c, 1, i, j);
2109 /*ARGSUSED*/
2110 static void
2111 tem_text_copy(struct tem_vt_state *tem __unused,
2112 screen_pos_t s_col, screen_pos_t s_row,
2113 screen_pos_t e_col, screen_pos_t e_row,
2114 screen_pos_t t_col, screen_pos_t t_row)
2116 struct vis_conscopy da;
2118 da.s_row = s_row;
2119 da.s_col = s_col;
2120 da.e_row = e_row;
2121 da.e_col = e_col;
2122 da.t_row = t_row;
2123 da.t_col = t_col;
2124 tems_copy(&da);
2127 static void
2128 tem_text_cls(struct tem_vt_state *tem,
2129 int count, screen_pos_t row, screen_pos_t col)
2131 text_attr_t attr;
2132 term_char_t c;
2133 int i;
2135 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2136 TEM_ATTR_SCREEN_REVERSE);
2137 c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
2139 if (count > tems.ts_c_dimension.width ||
2140 col + count > tems.ts_c_dimension.width)
2141 count = tems.ts_c_dimension.width - col;
2143 for (i = 0; i < count; i++)
2144 tem_text_display(tem, &c, 1, row, col++);
2148 static void
2149 tem_pix_display(struct tem_vt_state *tem,
2150 term_char_t *string, int count,
2151 screen_pos_t row, screen_pos_t col)
2153 struct vis_consdisplay da;
2154 int i;
2156 da.data = (uint8_t *)tem->tvs_pix_data;
2157 da.width = tems.ts_font.vf_width;
2158 da.height = tems.ts_font.vf_height;
2159 da.row = (row * da.height) + tems.ts_p_offset.y;
2160 da.col = (col * da.width) + tems.ts_p_offset.x;
2162 for (i = 0; i < count; i++) {
2163 tem_callback_bit2pix(tem, string[i]);
2164 tems_display(&da);
2165 da.col += da.width;
2169 static void
2170 tem_pix_copy(struct tem_vt_state *tem,
2171 screen_pos_t s_col, screen_pos_t s_row,
2172 screen_pos_t e_col, screen_pos_t e_row,
2173 screen_pos_t t_col, screen_pos_t t_row)
2175 struct vis_conscopy ma;
2176 static boolean_t need_clear = B_TRUE;
2178 if (need_clear && tem->tvs_first_line > 0) {
2180 * Clear OBP output above our kernel console term
2181 * when our kernel console term begins to scroll up,
2182 * we hope it is user friendly.
2183 * (Also see comments on tem_pix_clear_prom_output)
2185 * This is only one time call.
2187 tem_pix_clear_prom_output(tem);
2189 need_clear = B_FALSE;
2191 ma.s_row = s_row * tems.ts_font.vf_height + tems.ts_p_offset.y;
2192 ma.e_row = (e_row + 1) * tems.ts_font.vf_height +
2193 tems.ts_p_offset.y - 1;
2194 ma.t_row = t_row * tems.ts_font.vf_height + tems.ts_p_offset.y;
2197 * Check if we're in process of clearing OBP's columns area,
2198 * which only happens when term scrolls up a whole line.
2200 if (tem->tvs_first_line > 0 && t_row < s_row && t_col == 0 &&
2201 e_col == tems.ts_c_dimension.width - 1) {
2203 * We need to clear OBP's columns area outside our kernel
2204 * console term. So that we set ma.e_col to entire row here.
2206 ma.s_col = s_col * tems.ts_font.vf_width;
2207 ma.e_col = tems.ts_p_dimension.width - 1;
2209 ma.t_col = t_col * tems.ts_font.vf_width;
2210 } else {
2211 ma.s_col = s_col * tems.ts_font.vf_width + tems.ts_p_offset.x;
2212 ma.e_col = (e_col + 1) * tems.ts_font.vf_width +
2213 tems.ts_p_offset.x - 1;
2214 ma.t_col = t_col * tems.ts_font.vf_width + tems.ts_p_offset.x;
2217 tems_copy(&ma);
2219 if (tem->tvs_first_line > 0 && t_row < s_row) {
2220 /* We have scrolled up (s_row - t_row) rows. */
2221 tem->tvs_first_line -= (s_row - t_row);
2222 if (tem->tvs_first_line <= 0) {
2223 /* All OBP rows have been cleared. */
2224 tem->tvs_first_line = 0;
2229 static void
2230 tem_pix_bit2pix(struct tem_vt_state *tem, term_char_t c)
2232 text_color_t fg, bg;
2233 void (*fp)(struct tem_vt_state *, tem_char_t,
2234 unsigned char, unsigned char);
2236 tem_get_color(&fg, &bg, c);
2237 switch (tems.ts_pdepth) {
2238 case 4:
2239 fp = bit_to_pix4;
2240 break;
2241 case 8:
2242 fp = bit_to_pix8;
2243 break;
2244 case 15:
2245 case 16:
2246 fp = bit_to_pix16;
2247 break;
2248 case 24:
2249 fp = bit_to_pix24;
2250 break;
2251 case 32:
2252 fp = bit_to_pix32;
2253 break;
2254 default:
2255 return;
2258 fp(tem, c.tc_char, fg, bg);
2263 * This function only clears count of columns in one row
2265 static void
2266 tem_pix_cls(struct tem_vt_state *tem, int count,
2267 screen_pos_t row, screen_pos_t col)
2269 tem_pix_cls_range(tem, row, 1, tems.ts_p_offset.y,
2270 col, count, tems.ts_p_offset.x, B_FALSE);
2274 * This function clears OBP output above our kernel console term area
2275 * because OBP's term may have a bigger terminal window than that of
2276 * our kernel console term. So we need to clear OBP output garbage outside
2277 * of our kernel console term at a proper time, which is when the first
2278 * row output of our kernel console term scrolls at the first screen line.
2280 * _________________________________
2281 * | _____________________ | ---> OBP's bigger term window
2282 * | | | |
2283 * |___| | |
2284 * | | | | |
2285 * | | | | |
2286 * |_|_|___________________|_______|
2287 * | | | ---> first line
2288 * | |___________________|---> our kernel console term window
2290 * |---> columns area to be cleared
2292 * This function only takes care of the output above our kernel console term,
2293 * and tem_prom_scroll_up takes care of columns area outside of our kernel
2294 * console term.
2296 static void
2297 tem_pix_clear_prom_output(struct tem_vt_state *tem)
2299 int nrows, ncols, width, height, offset;
2301 width = tems.ts_font.vf_width;
2302 height = tems.ts_font.vf_height;
2303 offset = tems.ts_p_offset.y % height;
2305 nrows = tems.ts_p_offset.y / height;
2306 ncols = (tems.ts_p_dimension.width + (width - 1))/ width;
2308 if (nrows > 0)
2309 tem_pix_cls_range(tem, 0, nrows, offset, 0, ncols, 0,
2310 B_FALSE);
2314 * Clear the whole screen and reset the cursor to start point.
2316 static void
2317 tem_cls(struct tem_vt_state *tem)
2319 struct vis_consclear cl;
2320 text_color_t fg_color;
2321 text_color_t bg_color;
2322 text_attr_t attr;
2323 term_char_t c;
2324 int row;
2326 for (row = 0; row < tems.ts_c_dimension.height; row++) {
2327 tem_virtual_cls(tem, tems.ts_c_dimension.width, row, 0);
2330 if (!tem->tvs_isactive)
2331 return;
2333 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2334 TEM_ATTR_SCREEN_REVERSE);
2335 c.tc_char = TEM_ATTR(attr);
2337 tem_get_color(&fg_color, &bg_color, c);
2338 cl.bg_color = bg_color;
2339 (void)tems_cls(&cl);
2341 tem->tvs_c_cursor.row = 0;
2342 tem->tvs_c_cursor.col = 0;
2343 tem_align_cursor(tem);
2346 static void
2347 tem_back_tab(struct tem_vt_state *tem)
2349 int i;
2350 screen_pos_t tabstop;
2352 tabstop = 0;
2354 for (i = tem->tvs_ntabs - 1; i >= 0; i--) {
2355 if (tem->tvs_tabs[i] < tem->tvs_c_cursor.col) {
2356 tabstop = tem->tvs_tabs[i];
2357 break;
2361 tem_mv_cursor(tem, tem->tvs_c_cursor.row, tabstop);
2364 static void
2365 tem_tab(struct tem_vt_state *tem)
2367 int i;
2368 screen_pos_t tabstop;
2370 tabstop = tems.ts_c_dimension.width - 1;
2372 for (i = 0; i < tem->tvs_ntabs; i++) {
2373 if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
2374 tabstop = tem->tvs_tabs[i];
2375 break;
2379 tem_mv_cursor(tem, tem->tvs_c_cursor.row, tabstop);
2382 static void
2383 tem_set_tab(struct tem_vt_state *tem)
2385 int i;
2386 int j;
2388 if (tem->tvs_ntabs == TEM_MAXTAB)
2389 return;
2390 if (tem->tvs_ntabs == 0 ||
2391 tem->tvs_tabs[tem->tvs_ntabs] < tem->tvs_c_cursor.col) {
2392 tem->tvs_tabs[tem->tvs_ntabs++] = tem->tvs_c_cursor.col;
2393 return;
2395 for (i = 0; i < tem->tvs_ntabs; i++) {
2396 if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col)
2397 return;
2398 if (tem->tvs_tabs[i] > tem->tvs_c_cursor.col) {
2399 for (j = tem->tvs_ntabs - 1; j >= i; j--)
2400 tem->tvs_tabs[j+ 1] = tem->tvs_tabs[j];
2401 tem->tvs_tabs[i] = tem->tvs_c_cursor.col;
2402 tem->tvs_ntabs++;
2403 return;
2408 static void
2409 tem_clear_tabs(struct tem_vt_state *tem, int action)
2411 int i;
2412 int j;
2414 switch (action) {
2415 case 3: /* clear all tabs */
2416 tem->tvs_ntabs = 0;
2417 break;
2418 case 0: /* clr tab at cursor */
2420 for (i = 0; i < tem->tvs_ntabs; i++) {
2421 if (tem->tvs_tabs[i] == tem->tvs_c_cursor.col) {
2422 tem->tvs_ntabs--;
2423 for (j = i; j < tem->tvs_ntabs; j++)
2424 tem->tvs_tabs[j] = tem->tvs_tabs[j + 1];
2425 return;
2428 break;
2432 static void
2433 tem_mv_cursor(struct tem_vt_state *tem, int row, int col)
2436 * Sanity check and bounds enforcement. Out of bounds requests are
2437 * clipped to the screen boundaries. This seems to be what SPARC
2438 * does.
2440 if (row < 0)
2441 row = 0;
2442 if (row >= tems.ts_c_dimension.height)
2443 row = tems.ts_c_dimension.height - 1;
2444 if (col < 0)
2445 col = 0;
2446 if (col >= tems.ts_c_dimension.width)
2447 col = tems.ts_c_dimension.width - 1;
2449 tem_send_data(tem);
2450 tem->tvs_c_cursor.row = (screen_pos_t)row;
2451 tem->tvs_c_cursor.col = (screen_pos_t)col;
2452 tem_align_cursor(tem);
2455 /* ARGSUSED */
2456 static void
2457 tem_reset_emulator(struct tem_vt_state *tem, boolean_t init_color)
2459 int j;
2461 tem->tvs_c_cursor.row = 0;
2462 tem->tvs_c_cursor.col = 0;
2463 tem->tvs_r_cursor.row = 0;
2464 tem->tvs_r_cursor.col = 0;
2465 tem->tvs_s_cursor.row = 0;
2466 tem->tvs_s_cursor.col = 0;
2467 tem->tvs_outindex = 0;
2468 tem->tvs_state = A_STATE_START;
2469 tem->tvs_gotparam = B_FALSE;
2470 tem->tvs_curparam = 0;
2471 tem->tvs_paramval = 0;
2472 tem->tvs_nscroll = 1;
2474 if (init_color) {
2475 /* use initial settings */
2476 tem->tvs_fg_color = tems.ts_init_color.fg_color;
2477 tem->tvs_bg_color = tems.ts_init_color.bg_color;
2478 tem->tvs_flags = tems.ts_init_color.a_flags;
2482 * set up the initial tab stops
2484 tem->tvs_ntabs = 0;
2485 for (j = 8; j < tems.ts_c_dimension.width; j += 8)
2486 tem->tvs_tabs[tem->tvs_ntabs++] = (screen_pos_t)j;
2488 for (j = 0; j < TEM_MAXPARAMS; j++)
2489 tem->tvs_params[j] = 0;
2492 static void
2493 tem_reset_display(struct tem_vt_state *tem,
2494 boolean_t clear_txt, boolean_t init_color)
2496 tem_reset_emulator(tem, init_color);
2498 if (clear_txt) {
2499 if (tem->tvs_isactive)
2500 tem_callback_cursor(tem, VIS_HIDE_CURSOR);
2502 tem_cls(tem);
2504 if (tem->tvs_isactive)
2505 tem_callback_cursor(tem, VIS_DISPLAY_CURSOR);
2509 static void
2510 tem_shift(struct tem_vt_state *tem, int count, int direction)
2512 int rest_of_line;
2514 rest_of_line = tems.ts_c_dimension.width - tem->tvs_c_cursor.col;
2515 if (count > rest_of_line)
2516 count = rest_of_line;
2518 if (count <= 0)
2519 return;
2521 switch (direction) {
2522 case TEM_SHIFT_LEFT:
2523 if (count < rest_of_line) {
2524 tem_copy_area(tem,
2525 tem->tvs_c_cursor.col + count,
2526 tem->tvs_c_cursor.row,
2527 tems.ts_c_dimension.width - 1,
2528 tem->tvs_c_cursor.row,
2529 tem->tvs_c_cursor.col,
2530 tem->tvs_c_cursor.row);
2533 tem_clear_chars(tem, count, tem->tvs_c_cursor.row,
2534 (tems.ts_c_dimension.width - count));
2535 break;
2536 case TEM_SHIFT_RIGHT:
2537 if (count < rest_of_line) {
2538 tem_copy_area(tem,
2539 tem->tvs_c_cursor.col,
2540 tem->tvs_c_cursor.row,
2541 tems.ts_c_dimension.width - count - 1,
2542 tem->tvs_c_cursor.row,
2543 tem->tvs_c_cursor.col + count,
2544 tem->tvs_c_cursor.row);
2547 tem_clear_chars(tem, count, tem->tvs_c_cursor.row,
2548 tem->tvs_c_cursor.col);
2549 break;
2553 static void
2554 tem_text_cursor(struct tem_vt_state *tem, short action)
2556 struct vis_conscursor ca;
2558 ca.row = tem->tvs_c_cursor.row;
2559 ca.col = tem->tvs_c_cursor.col;
2560 ca.action = action;
2562 tems_cursor(&ca);
2564 if (action == VIS_GET_CURSOR) {
2565 tem->tvs_c_cursor.row = ca.row;
2566 tem->tvs_c_cursor.col = ca.col;
2570 static void
2571 tem_pix_cursor(struct tem_vt_state *tem, short action)
2573 struct vis_conscursor ca;
2574 uint32_t color;
2575 text_color_t fg, bg;
2576 term_char_t c;
2577 text_attr_t attr;
2579 ca.row = tem->tvs_c_cursor.row * tems.ts_font.vf_height +
2580 tems.ts_p_offset.y;
2581 ca.col = tem->tvs_c_cursor.col * tems.ts_font.vf_width +
2582 tems.ts_p_offset.x;
2583 ca.width = tems.ts_font.vf_width;
2584 ca.height = tems.ts_font.vf_height;
2586 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2587 TEM_ATTR_REVERSE);
2588 c.tc_char = TEM_ATTR(attr);
2590 tem_get_color(&fg, &bg, c);
2592 switch (tems.ts_pdepth) {
2593 case 4:
2594 ca.fg_color.mono = fg;
2595 ca.bg_color.mono = bg;
2596 break;
2597 case 8:
2598 ca.fg_color.mono = tems.ts_color_map(fg);
2599 ca.bg_color.mono = tems.ts_color_map(bg);
2600 break;
2601 case 15:
2602 case 16:
2603 color = tems.ts_color_map(fg);
2604 ca.fg_color.sixteen[0] = (color >> 8) & 0xFF;
2605 ca.fg_color.sixteen[1] = color & 0xFF;
2606 color = tems.ts_color_map(bg);
2607 ca.bg_color.sixteen[0] = (color >> 8) & 0xFF;
2608 ca.bg_color.sixteen[1] = color & 0xFF;
2609 break;
2610 case 24:
2611 case 32:
2612 color = tems.ts_color_map(fg);
2613 ca.fg_color.twentyfour[0] = (color >> 16) & 0xFF;
2614 ca.fg_color.twentyfour[1] = (color >> 8) & 0xFF;
2615 ca.fg_color.twentyfour[2] = color & 0xFF;
2616 color = tems.ts_color_map(bg);
2617 ca.bg_color.twentyfour[0] = (color >> 16) & 0xFF;
2618 ca.bg_color.twentyfour[1] = (color >> 8) & 0xFF;
2619 ca.bg_color.twentyfour[2] = color & 0xFF;
2620 break;
2623 ca.action = action;
2625 tems_cursor(&ca);
2627 if (action == VIS_GET_CURSOR) {
2628 tem->tvs_c_cursor.row = 0;
2629 tem->tvs_c_cursor.col = 0;
2631 if (ca.row != 0) {
2632 tem->tvs_c_cursor.row = (ca.row - tems.ts_p_offset.y) /
2633 tems.ts_font.vf_height;
2635 if (ca.col != 0) {
2636 tem->tvs_c_cursor.col = (ca.col - tems.ts_p_offset.x) /
2637 tems.ts_font.vf_width;
2642 static void
2643 bit_to_pix4(struct tem_vt_state *tem,
2644 tem_char_t c,
2645 text_color_t fg_color,
2646 text_color_t bg_color)
2648 uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2649 font_bit_to_pix4(&tems.ts_font, dest, c, fg_color, bg_color);
2652 static void
2653 bit_to_pix8(struct tem_vt_state *tem,
2654 tem_char_t c,
2655 text_color_t fg_color,
2656 text_color_t bg_color)
2658 uint8_t *dest = (uint8_t *)tem->tvs_pix_data;
2660 fg_color = (text_color_t)tems.ts_color_map(fg_color);
2661 bg_color = (text_color_t)tems.ts_color_map(bg_color);
2662 font_bit_to_pix8(&tems.ts_font, dest, c, fg_color, bg_color);
2665 static void
2666 bit_to_pix16(struct tem_vt_state *tem,
2667 tem_char_t c,
2668 text_color_t fg_color4,
2669 text_color_t bg_color4)
2671 uint16_t fg_color16, bg_color16;
2672 uint16_t *dest;
2674 fg_color16 = (uint16_t)tems.ts_color_map(fg_color4);
2675 bg_color16 = (uint16_t)tems.ts_color_map(bg_color4);
2677 dest = (uint16_t *)tem->tvs_pix_data;
2678 font_bit_to_pix16(&tems.ts_font, dest, c, fg_color16, bg_color16);
2681 static void
2682 bit_to_pix24(struct tem_vt_state *tem,
2683 tem_char_t c,
2684 text_color_t fg_color4,
2685 text_color_t bg_color4)
2687 uint32_t fg_color32, bg_color32;
2688 uint8_t *dest;
2690 fg_color32 = tems.ts_color_map(fg_color4);
2691 bg_color32 = tems.ts_color_map(bg_color4);
2693 dest = (uint8_t *)tem->tvs_pix_data;
2694 font_bit_to_pix24(&tems.ts_font, dest, c, fg_color32, bg_color32);
2697 static void
2698 bit_to_pix32(struct tem_vt_state *tem,
2699 tem_char_t c,
2700 text_color_t fg_color4,
2701 text_color_t bg_color4)
2703 uint32_t fg_color32, bg_color32, *dest;
2705 fg_color32 = (0xFF << 24) | tems.ts_color_map(fg_color4);
2706 bg_color32 = (0xFF << 24) | tems.ts_color_map(bg_color4);
2708 dest = (uint32_t *)tem->tvs_pix_data;
2709 font_bit_to_pix32(&tems.ts_font, dest, c, fg_color32, bg_color32);
2713 * flag: TEM_ATTR_SCREEN_REVERSE or TEM_ATTR_REVERSE
2715 static void
2716 tem_get_attr(struct tem_vt_state *tem, text_color_t *fg,
2717 text_color_t *bg, text_attr_t *attr, uint8_t flag)
2719 if (tem->tvs_flags & flag) {
2720 *fg = tem->tvs_bg_color;
2721 *bg = tem->tvs_fg_color;
2722 } else {
2723 *fg = tem->tvs_fg_color;
2724 *bg = tem->tvs_bg_color;
2727 if (attr == NULL)
2728 return;
2730 *attr = tem->tvs_flags;
2733 static void
2734 tem_get_color(text_color_t *fg, text_color_t *bg, term_char_t c)
2736 if (TEM_CHAR_ATTR(c.tc_char) & (TEM_ATTR_BRIGHT_FG | TEM_ATTR_BOLD))
2737 *fg = brt_xlate[c.tc_fg_color];
2738 else
2739 *fg = dim_xlate[c.tc_fg_color];
2741 if (TEM_CHAR_ATTR(c.tc_char) & TEM_ATTR_BRIGHT_BG)
2742 *bg = brt_xlate[c.tc_bg_color];
2743 else
2744 *bg = dim_xlate[c.tc_bg_color];
2747 void
2748 tem_get_colors(tem_vt_state_t tem_arg, text_color_t *fg, text_color_t *bg)
2750 struct tem_vt_state *tem = (struct tem_vt_state *)tem_arg;
2751 text_attr_t attr;
2752 term_char_t c;
2754 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2755 TEM_ATTR_REVERSE);
2756 c.tc_char = TEM_ATTR(attr);
2757 tem_get_color(fg, bg, c);
2761 * Clear a rectangle of screen for pixel mode.
2763 * arguments:
2764 * row: start row#
2765 * nrows: the number of rows to clear
2766 * offset_y: the offset of height in pixels to begin clear
2767 * col: start col#
2768 * ncols: the number of cols to clear
2769 * offset_x: the offset of width in pixels to begin clear
2770 * scroll_up: whether this function is called during sroll up,
2771 * which is called only once.
2773 static void
2774 tem_pix_cls_range(struct tem_vt_state *tem,
2775 screen_pos_t row, int nrows, int offset_y,
2776 screen_pos_t col, int ncols, int offset_x,
2777 boolean_t sroll_up)
2779 struct vis_consdisplay da;
2780 int i, j;
2781 int row_add = 0;
2782 term_char_t c;
2783 text_attr_t attr;
2785 if (sroll_up)
2786 row_add = tems.ts_c_dimension.height - 1;
2788 da.width = tems.ts_font.vf_width;
2789 da.height = tems.ts_font.vf_height;
2791 tem_get_attr(tem, &c.tc_fg_color, &c.tc_bg_color, &attr,
2792 TEM_ATTR_SCREEN_REVERSE);
2793 /* Make sure we will not draw underlines */
2794 c.tc_char = TEM_ATTR(attr & ~TEM_ATTR_UNDERLINE) | ' ';
2796 tem_callback_bit2pix(tem, c);
2797 da.data = (uint8_t *)tem->tvs_pix_data;
2799 for (i = 0; i < nrows; i++, row++) {
2800 da.row = (row + row_add) * da.height + offset_y;
2801 da.col = col * da.width + offset_x;
2802 for (j = 0; j < ncols; j++) {
2803 tems_display(&da);
2804 da.col += da.width;
2810 * virtual screen operations
2812 static void
2813 tem_virtual_display(struct tem_vt_state *tem, term_char_t *string,
2814 size_t count, screen_pos_t row, screen_pos_t col)
2816 size_t i, width;
2817 term_char_t *addr;
2819 if (tem->tvs_screen_buf == NULL)
2820 return;
2822 if (row < 0 || row >= tems.ts_c_dimension.height ||
2823 col < 0 || col >= tems.ts_c_dimension.width ||
2824 col + count > (size_t)tems.ts_c_dimension.width)
2825 return;
2827 width = tems.ts_c_dimension.width;
2828 addr = tem->tvs_screen_buf + (row * width + col);
2829 for (i = 0; i < count; i++) {
2830 *addr++ = string[i];
2834 static void
2835 tem_virtual_cls(struct tem_vt_state *tem, size_t count,
2836 screen_pos_t row, screen_pos_t col)
2838 term_char_t c;
2840 c.tc_char = ' ';
2841 tem_get_colors((tem_vt_state_t)tem, &c.tc_fg_color, &c.tc_bg_color);
2843 while (count > 0) {
2844 tem_virtual_display(tem, &c, 1, row, col);
2845 col++;
2846 count--;