xref: /freebsd/sys/dev/syscons/scterm-sc.c (revision 2b944ee2b959e9b29fd72dcbf87aad8ad5537bc4)
1 /*-
2  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
3  * Copyright (c) 1992-1998 S�ren Schmidt
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer as
11  *    the first lines of this file unmodified.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29 
30 #include "sc.h"
31 #include "opt_syscons.h"
32 
33 #if NSC > 0
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/consio.h>
39 
40 #include <machine/pc/display.h>
41 
42 #include <dev/syscons/syscons.h>
43 #include <dev/syscons/sctermvar.h>
44 
45 #ifndef SC_DUMB_TERMINAL
46 
47 #define MAX_ESC_PAR	5
48 
49 /* attribute flags */
50 typedef struct {
51 	u_short		fg;			/* foreground color */
52 	u_short		bg;			/* background color */
53 } color_t;
54 
55 typedef struct {
56 	int		flags;
57 #define SCTERM_BUSY	(1 << 0)
58 	int		esc;
59 	int		num_param;
60 	int		last_param;
61 	int		param[MAX_ESC_PAR];
62 	int		saved_xpos;
63 	int		saved_ypos;
64 	int		attr_mask;		/* current logical attr mask */
65 #define NORMAL_ATTR	0x00
66 #define BLINK_ATTR	0x01
67 #define BOLD_ATTR	0x02
68 #define UNDERLINE_ATTR	0x04
69 #define REVERSE_ATTR	0x08
70 #define FG_CHANGED	0x10
71 #define BG_CHANGED	0x20
72 	int		cur_attr;		/* current hardware attr word */
73 	color_t		cur_color;		/* current hardware color */
74 	color_t		std_color;		/* normal hardware color */
75 	color_t		rev_color;		/* reverse hardware color */
76 	color_t		dflt_std_color;		/* default normal color */
77 	color_t		dflt_rev_color;		/* default reverse color */
78 } term_stat;
79 
80 static sc_term_init_t	scterm_init;
81 static sc_term_term_t	scterm_term;
82 static sc_term_puts_t	scterm_puts;
83 static sc_term_ioctl_t	scterm_ioctl;
84 static sc_term_reset_t	scterm_reset;
85 static sc_term_default_attr_t	scterm_default_attr;
86 static sc_term_clear_t	scterm_clear;
87 static sc_term_notify_t	scterm_notify;
88 static sc_term_input_t	scterm_input;
89 
90 static sc_term_sw_t sc_term_sc = {
91 	{ NULL, NULL },
92 	"sc",				/* emulator name */
93 	"syscons terminal",		/* description */
94 	"*",				/* matching renderer, any :-) */
95 	sizeof(term_stat),		/* softc size */
96 	0,
97 	scterm_init,
98 	scterm_term,
99 	scterm_puts,
100 	scterm_ioctl,
101 	scterm_reset,
102 	scterm_default_attr,
103 	scterm_clear,
104 	scterm_notify,
105 	scterm_input,
106 };
107 
108 SCTERM_MODULE(sc, sc_term_sc);
109 
110 static term_stat	reserved_term_stat;
111 static void		scterm_scan_esc(scr_stat *scp, term_stat *tcp,
112 					u_char c);
113 static int		mask2attr(term_stat *tcp);
114 
115 static int
116 scterm_init(scr_stat *scp, void **softc, int code)
117 {
118 	term_stat *tcp;
119 
120 	if (*softc == NULL) {
121 		if (reserved_term_stat.flags & SCTERM_BUSY)
122 			return EINVAL;
123 		*softc = &reserved_term_stat;
124 	}
125 	tcp = *softc;
126 
127 	switch (code) {
128 	case SC_TE_COLD_INIT:
129 		bzero(tcp, sizeof(*tcp));
130 		tcp->flags = SCTERM_BUSY;
131 		tcp->esc = 0;
132 		tcp->saved_xpos = -1;
133 		tcp->saved_ypos = -1;
134 		tcp->attr_mask = NORMAL_ATTR;
135 		/* XXX */
136 		tcp->dflt_std_color.fg = SC_NORM_ATTR & 0x0f;
137 		tcp->dflt_std_color.bg = (SC_NORM_ATTR >> 4) & 0x0f;
138 		tcp->dflt_rev_color.fg = SC_NORM_REV_ATTR & 0x0f;
139 		tcp->dflt_rev_color.bg = (SC_NORM_REV_ATTR >> 4) & 0x0f;
140 		tcp->std_color = tcp->dflt_std_color;
141 		tcp->rev_color = tcp->dflt_rev_color;
142 		tcp->cur_color = tcp->std_color;
143 		tcp->cur_attr = mask2attr(tcp);
144 		++sc_term_sc.te_refcount;
145 		break;
146 
147 	case SC_TE_WARM_INIT:
148 		tcp->esc = 0;
149 		tcp->saved_xpos = -1;
150 		tcp->saved_ypos = -1;
151 		tcp->std_color = tcp->dflt_std_color;
152 		tcp->rev_color = tcp->dflt_rev_color;
153 		tcp->cur_color = tcp->std_color;
154 		tcp->cur_attr = mask2attr(tcp);
155 		break;
156 	}
157 
158 	return 0;
159 }
160 
161 static int
162 scterm_term(scr_stat *scp, void **softc)
163 {
164 	if (*softc == &reserved_term_stat) {
165 		*softc = NULL;
166 		bzero(&reserved_term_stat, sizeof(reserved_term_stat));
167 	}
168 	--sc_term_sc.te_refcount;
169 	return 0;
170 }
171 
172 static void
173 scterm_scan_esc(scr_stat *scp, term_stat *tcp, u_char c)
174 {
175 	static u_char ansi_col[16] = {
176 		FG_BLACK,     FG_RED,          FG_GREEN,      FG_BROWN,
177 		FG_BLUE,      FG_MAGENTA,      FG_CYAN,       FG_LIGHTGREY,
178 		FG_DARKGREY,  FG_LIGHTRED,     FG_LIGHTGREEN, FG_YELLOW,
179 		FG_LIGHTBLUE, FG_LIGHTMAGENTA, FG_LIGHTCYAN,  FG_WHITE
180 	};
181 	sc_softc_t *sc;
182 	int i, n;
183 
184     i = n = 0;
185     sc = scp->sc;
186     if (tcp->esc == 1) {	/* seen ESC */
187 	switch (c) {
188 
189 	case '7':   /* Save cursor position */
190 	    tcp->saved_xpos = scp->xpos;
191 	    tcp->saved_ypos = scp->ypos;
192 	    break;
193 
194 	case '8':   /* Restore saved cursor position */
195 	    if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0)
196 		sc_move_cursor(scp, tcp->saved_xpos, tcp->saved_ypos);
197 	    break;
198 
199 	case '[':   /* Start ESC [ sequence */
200 	    tcp->esc = 2;
201 	    tcp->last_param = -1;
202 	    for (i = tcp->num_param; i < MAX_ESC_PAR; i++)
203 		tcp->param[i] = 1;
204 	    tcp->num_param = 0;
205 	    return;
206 
207 	case 'M':   /* Move cursor up 1 line, scroll if at top */
208 	    sc_term_up_scroll(scp, 1, sc->scr_map[0x20], tcp->cur_attr, 0, 0);
209 	    break;
210 #if notyet
211 	case 'Q':
212 	    tcp->esc = 4;
213 	    return;
214 #endif
215 	case 'c':   /* Clear screen & home */
216 	    sc_clear_screen(scp);
217 	    break;
218 
219 	case '(':   /* iso-2022: designate 94 character set to G0 */
220 	    tcp->esc = 5;
221 	    return;
222 	}
223     }
224     else if (tcp->esc == 2) {	/* seen ESC [ */
225 	if (c >= '0' && c <= '9') {
226 	    if (tcp->num_param < MAX_ESC_PAR) {
227 		if (tcp->last_param != tcp->num_param) {
228 		    tcp->last_param = tcp->num_param;
229 		    tcp->param[tcp->num_param] = 0;
230 		} else {
231 		    tcp->param[tcp->num_param] *= 10;
232 		}
233 		tcp->param[tcp->num_param] += c - '0';
234 		return;
235 	    }
236 	}
237 	tcp->num_param = tcp->last_param + 1;
238 	switch (c) {
239 
240 	case ';':
241 	    if (tcp->num_param < MAX_ESC_PAR)
242 		return;
243 	    break;
244 
245 	case '=':
246 	    tcp->esc = 3;
247 	    tcp->last_param = -1;
248 	    for (i = tcp->num_param; i < MAX_ESC_PAR; i++)
249 		tcp->param[i] = 1;
250 	    tcp->num_param = 0;
251 	    return;
252 
253 	case 'A':   /* up n rows */
254 	    sc_term_up(scp, tcp->param[0], 0);
255 	    break;
256 
257 	case 'B':   /* down n rows */
258 	    sc_term_down(scp, tcp->param[0], 0);
259 	    break;
260 
261 	case 'C':   /* right n columns */
262 	    sc_term_right(scp, tcp->param[0]);
263 	    break;
264 
265 	case 'D':   /* left n columns */
266 	    sc_term_left(scp, tcp->param[0]);
267 	    break;
268 
269 	case 'E':   /* cursor to start of line n lines down */
270 	    n = tcp->param[0]; if (n < 1) n = 1;
271 	    sc_move_cursor(scp, 0, scp->ypos + n);
272 	    break;
273 
274 	case 'F':   /* cursor to start of line n lines up */
275 	    n = tcp->param[0]; if (n < 1) n = 1;
276 	    sc_move_cursor(scp, 0, scp->ypos - n);
277 	    break;
278 
279 	case 'f':   /* Cursor move */
280 	case 'H':
281 	    if (tcp->num_param == 0)
282 		sc_move_cursor(scp, 0, 0);
283 	    else if (tcp->num_param == 2)
284 		sc_move_cursor(scp, tcp->param[1] - 1, tcp->param[0] - 1);
285 	    break;
286 
287 	case 'J':   /* Clear all or part of display */
288 	    if (tcp->num_param == 0)
289 		n = 0;
290 	    else
291 		n = tcp->param[0];
292 	    sc_term_clr_eos(scp, n, sc->scr_map[0x20], tcp->cur_attr);
293 	    break;
294 
295 	case 'K':   /* Clear all or part of line */
296 	    if (tcp->num_param == 0)
297 		n = 0;
298 	    else
299 		n = tcp->param[0];
300 	    sc_term_clr_eol(scp, n, sc->scr_map[0x20], tcp->cur_attr);
301 	    break;
302 
303 	case 'L':   /* Insert n lines */
304 	    sc_term_ins_line(scp, scp->ypos, tcp->param[0],
305 			     sc->scr_map[0x20], tcp->cur_attr, 0);
306 	    break;
307 
308 	case 'M':   /* Delete n lines */
309 	    sc_term_del_line(scp, scp->ypos, tcp->param[0],
310 			     sc->scr_map[0x20], tcp->cur_attr, 0);
311 	    break;
312 
313 	case 'P':   /* Delete n chars */
314 	    sc_term_del_char(scp, tcp->param[0],
315 			     sc->scr_map[0x20], tcp->cur_attr);
316 	    break;
317 
318 	case '@':   /* Insert n chars */
319 	    sc_term_ins_char(scp, tcp->param[0],
320 			     sc->scr_map[0x20], tcp->cur_attr);
321 	    break;
322 
323 	case 'S':   /* scroll up n lines */
324 	    sc_term_del_line(scp, 0, tcp->param[0],
325 			     sc->scr_map[0x20], tcp->cur_attr, 0);
326 	    break;
327 
328 	case 'T':   /* scroll down n lines */
329 	    sc_term_ins_line(scp, 0, tcp->param[0],
330 			     sc->scr_map[0x20], tcp->cur_attr, 0);
331 	    break;
332 
333 	case 'X':   /* erase n characters in line */
334 	    n = tcp->param[0]; if (n < 1)  n = 1;
335 	    if (n > scp->xsize - scp->xpos)
336 		n = scp->xsize - scp->xpos;
337 	    sc_vtb_erase(&scp->vtb, scp->cursor_pos, n,
338 			 sc->scr_map[0x20], tcp->cur_attr);
339 	    mark_for_update(scp, scp->cursor_pos);
340 	    mark_for_update(scp, scp->cursor_pos + n - 1);
341 	    break;
342 
343 	case 'Z':   /* move n tabs backwards */
344 	    sc_term_backtab(scp, tcp->param[0]);
345 	    break;
346 
347 	case '`':   /* move cursor to column n */
348 	    sc_term_col(scp, tcp->param[0]);
349 	    break;
350 
351 	case 'a':   /* move cursor n columns to the right */
352 	    sc_term_right(scp, tcp->param[0]);
353 	    break;
354 
355 	case 'd':   /* move cursor to row n */
356 	    sc_term_row(scp, tcp->param[0]);
357 	    break;
358 
359 	case 'e':   /* move cursor n rows down */
360 	    sc_term_down(scp, tcp->param[0], 0);
361 	    break;
362 
363 	case 'm':   /* change attribute */
364 	    if (tcp->num_param == 0) {
365 		tcp->attr_mask = NORMAL_ATTR;
366 		tcp->cur_color = tcp->std_color;
367 		tcp->cur_attr = mask2attr(tcp);
368 		break;
369 	    }
370 	    for (i = 0; i < tcp->num_param; i++) {
371 		switch (n = tcp->param[i]) {
372 		case 0: /* back to normal */
373 		    tcp->attr_mask = NORMAL_ATTR;
374 		    tcp->cur_color = tcp->std_color;
375 		    tcp->cur_attr = mask2attr(tcp);
376 		    break;
377 		case 1: /* bold */
378 		    tcp->attr_mask |= BOLD_ATTR;
379 		    tcp->cur_attr = mask2attr(tcp);
380 		    break;
381 		case 4: /* underline */
382 		    tcp->attr_mask |= UNDERLINE_ATTR;
383 		    tcp->cur_attr = mask2attr(tcp);
384 		    break;
385 		case 5: /* blink */
386 		    tcp->attr_mask |= BLINK_ATTR;
387 		    tcp->cur_attr = mask2attr(tcp);
388 		    break;
389 		case 7: /* reverse video */
390 		    tcp->attr_mask |= REVERSE_ATTR;
391 		    tcp->cur_attr = mask2attr(tcp);
392 		    break;
393 		case 30: case 31: /* set fg color */
394 		case 32: case 33: case 34:
395 		case 35: case 36: case 37:
396 		    tcp->attr_mask |= FG_CHANGED;
397 		    tcp->cur_color.fg = ansi_col[n - 30];
398 		    tcp->cur_attr = mask2attr(tcp);
399 		    break;
400 		case 40: case 41: /* set bg color */
401 		case 42: case 43: case 44:
402 		case 45: case 46: case 47:
403 		    tcp->attr_mask |= BG_CHANGED;
404 		    tcp->cur_color.bg = ansi_col[n - 40];
405 		    tcp->cur_attr = mask2attr(tcp);
406 		    break;
407 		}
408 	    }
409 	    break;
410 
411 	case 's':   /* Save cursor position */
412 	    tcp->saved_xpos = scp->xpos;
413 	    tcp->saved_ypos = scp->ypos;
414 	    break;
415 
416 	case 'u':   /* Restore saved cursor position */
417 	    if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0)
418 		sc_move_cursor(scp, tcp->saved_xpos, tcp->saved_ypos);
419 	    break;
420 
421 	case 'x':
422 	    if (tcp->num_param == 0)
423 		n = 0;
424 	    else
425 		n = tcp->param[0];
426 	    switch (n) {
427 	    case 0:     /* reset attributes */
428 		tcp->attr_mask = NORMAL_ATTR;
429 		tcp->cur_color = tcp->std_color = tcp->dflt_std_color;
430 		tcp->rev_color = tcp->dflt_rev_color;
431 		tcp->cur_attr = mask2attr(tcp);
432 		break;
433 	    case 1:     /* set ansi background */
434 		tcp->attr_mask &= ~BG_CHANGED;
435 		tcp->cur_color.bg = tcp->std_color.bg =
436 		    ansi_col[tcp->param[1] & 0x0f];
437 		tcp->cur_attr = mask2attr(tcp);
438 		break;
439 	    case 2:     /* set ansi foreground */
440 		tcp->attr_mask &= ~FG_CHANGED;
441 		tcp->cur_color.fg = tcp->std_color.fg =
442 		    ansi_col[tcp->param[1] & 0x0f];
443 		tcp->cur_attr = mask2attr(tcp);
444 		break;
445 	    case 3:     /* set ansi attribute directly */
446 		tcp->attr_mask &= ~(FG_CHANGED | BG_CHANGED);
447 		tcp->cur_color.fg = tcp->std_color.fg =
448 		    tcp->param[1] & 0x0f;
449 		tcp->cur_color.bg = tcp->std_color.bg =
450 		    (tcp->param[1] >> 4) & 0x0f;
451 		tcp->cur_attr = mask2attr(tcp);
452 		break;
453 	    case 5:     /* set ansi reverse video background */
454 		tcp->rev_color.bg = ansi_col[tcp->param[1] & 0x0f];
455 		tcp->cur_attr = mask2attr(tcp);
456 		break;
457 	    case 6:     /* set ansi reverse video foreground */
458 		tcp->rev_color.fg = ansi_col[tcp->param[1] & 0x0f];
459 		tcp->cur_attr = mask2attr(tcp);
460 		break;
461 	    case 7:     /* set ansi reverse video directly */
462 		tcp->rev_color.fg = tcp->param[1] & 0x0f;
463 		tcp->rev_color.bg = (tcp->param[1] >> 4) & 0x0f;
464 		tcp->cur_attr = mask2attr(tcp);
465 		break;
466 	    }
467 	    break;
468 
469 	case 'z':   /* switch to (virtual) console n */
470 	    if (tcp->num_param == 1)
471 		sc_switch_scr(sc, tcp->param[0]);
472 	    break;
473 	}
474     }
475     else if (tcp->esc == 3) {	/* seen ESC [0-9]+ = */
476 	if (c >= '0' && c <= '9') {
477 	    if (tcp->num_param < MAX_ESC_PAR) {
478 		if (tcp->last_param != tcp->num_param) {
479 		    tcp->last_param = tcp->num_param;
480 		    tcp->param[tcp->num_param] = 0;
481 		} else {
482 		    tcp->param[tcp->num_param] *= 10;
483 		}
484 		tcp->param[tcp->num_param] += c - '0';
485 		return;
486 	    }
487 	}
488 	tcp->num_param = tcp->last_param + 1;
489 	switch (c) {
490 
491 	case ';':
492 	    if (tcp->num_param < MAX_ESC_PAR)
493 		return;
494 	    break;
495 
496 	case 'A':   /* set display border color */
497 	    if (tcp->num_param == 1) {
498 		scp->border=tcp->param[0] & 0xff;
499 		if (scp == sc->cur_scp)
500 		    sc_set_border(scp, scp->border);
501             }
502 	    break;
503 
504 	case 'B':   /* set bell pitch and duration */
505 	    if (tcp->num_param == 2) {
506 		scp->bell_pitch = tcp->param[0];
507 		scp->bell_duration = tcp->param[1];
508 	    }
509 	    break;
510 
511 	case 'C':   /* set cursor type & shape */
512 	    if (!ISGRAPHSC(sc->cur_scp))
513 		sc_remove_cursor_image(sc->cur_scp);
514 	    if (tcp->num_param == 1) {
515 		if (tcp->param[0] & 0x01)
516 		    sc->flags |= SC_BLINK_CURSOR;
517 		else
518 		    sc->flags &= ~SC_BLINK_CURSOR;
519 		if (tcp->param[0] & 0x02)
520 		    sc->flags |= SC_CHAR_CURSOR;
521 		else
522 		    sc->flags &= ~SC_CHAR_CURSOR;
523 	    } else if (tcp->num_param == 2) {
524 		sc->cursor_base = scp->font_size
525 					- (tcp->param[1] & 0x1F) - 1;
526 		sc->cursor_height = (tcp->param[1] & 0x1F)
527 					- (tcp->param[0] & 0x1F) + 1;
528 	    }
529 	    /*
530 	     * The cursor shape is global property; all virtual consoles
531 	     * are affected. Update the cursor in the current console...
532 	     */
533 	    if (!ISGRAPHSC(sc->cur_scp)) {
534 		i = spltty();
535 		sc_set_cursor_image(sc->cur_scp);
536 		sc_draw_cursor_image(sc->cur_scp);
537 		splx(i);
538 	    }
539 	    break;
540 
541 	case 'F':   /* set ansi foreground */
542 	    if (tcp->num_param == 1) {
543 		tcp->attr_mask &= ~FG_CHANGED;
544 		tcp->cur_color.fg = tcp->std_color.fg = tcp->param[0] & 0x0f;
545 		tcp->cur_attr = mask2attr(tcp);
546 	    }
547 	    break;
548 
549 	case 'G':   /* set ansi background */
550 	    if (tcp->num_param == 1) {
551 		tcp->attr_mask &= ~BG_CHANGED;
552 		tcp->cur_color.bg = tcp->std_color.bg = tcp->param[0] & 0x0f;
553 		tcp->cur_attr = mask2attr(tcp);
554 	    }
555 	    break;
556 
557 	case 'H':   /* set ansi reverse video foreground */
558 	    if (tcp->num_param == 1) {
559 		tcp->rev_color.fg = tcp->param[0] & 0x0f;
560 		tcp->cur_attr = mask2attr(tcp);
561 	    }
562 	    break;
563 
564 	case 'I':   /* set ansi reverse video background */
565 	    if (tcp->num_param == 1) {
566 		tcp->rev_color.bg = tcp->param[0] & 0x0f;
567 		tcp->cur_attr = mask2attr(tcp);
568 	    }
569 	    break;
570 	}
571 
572 #if notyet
573 	} else if (tcp->esc == 4) {	/* seen ESC Q */
574 		/* to be filled */
575 #endif
576 	} else if (tcp->esc == 5) {	/* seen ESC ( */
577 		switch (c) {
578 		case 'B':   /* iso-2022: desginate ASCII into G0 */
579 			break;
580 			/* other items to be filled */
581 		default:
582 			break;
583 		}
584 	}
585 	tcp->esc = 0;
586 }
587 
588 static void
589 scterm_puts(scr_stat *scp, u_char *buf, int len)
590 {
591 	term_stat *tcp;
592 
593 	tcp = scp->ts;
594 outloop:
595 	scp->sc->write_in_progress++;
596 
597 	if (tcp->esc) {
598 		scterm_scan_esc(scp, tcp, *buf);
599 		buf++;
600 		len--;
601 	} else {
602 		switch (*buf) {
603 		case 0x1b:
604 			tcp->esc = 1;
605 			tcp->num_param = 0;
606 			buf++;
607 			len--;
608 			break;
609 		default:
610 			sc_term_gen_print(scp, &buf, &len, tcp->cur_attr);
611 			break;
612 		}
613 	}
614 
615 	sc_term_gen_scroll(scp, scp->sc->scr_map[0x20], tcp->cur_attr);
616 
617 	scp->sc->write_in_progress--;
618 	if (len)
619 		goto outloop;
620 }
621 
622 static int
623 scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data,
624 	     int flag, struct proc *p)
625 {
626 	term_stat *tcp = scp->ts;
627 	vid_info_t *vi;
628 
629 	switch (cmd) {
630 	case GIO_ATTR:      	/* get current attributes */
631 		/* FIXME: */
632 		*(int*)data = (tcp->cur_attr >> 8) & 0xff;
633 		return 0;
634 	case CONS_GETINFO:  	/* get current (virtual) console info */
635 		vi = (vid_info_t *)data;
636 		if (vi->size != sizeof(struct vid_info))
637 			return EINVAL;
638 		vi->mv_norm.fore = tcp->std_color.fg;
639 		vi->mv_norm.back = tcp->std_color.bg;
640 		vi->mv_rev.fore = tcp->rev_color.fg;
641 		vi->mv_rev.back = tcp->rev_color.bg;
642 		/*
643 		 * The other fields are filled by the upper routine. XXX
644 		 */
645 		return ENOIOCTL;
646 	}
647 	return ENOIOCTL;
648 }
649 
650 static int
651 scterm_reset(scr_stat *scp, int code)
652 {
653 	/* FIXME */
654 	return 0;
655 }
656 
657 static void
658 scterm_default_attr(scr_stat *scp, int color, int rev_color)
659 {
660 	term_stat *tcp = scp->ts;
661 
662 	tcp->dflt_std_color.fg = color & 0x0f;
663 	tcp->dflt_std_color.bg = (color >> 4) & 0x0f;
664 	tcp->dflt_rev_color.fg = rev_color & 0x0f;
665 	tcp->dflt_rev_color.bg = (rev_color >> 4) & 0x0f;
666 	tcp->std_color = tcp->dflt_std_color;
667 	tcp->rev_color = tcp->dflt_rev_color;
668 	tcp->cur_color = tcp->std_color;
669 	tcp->cur_attr = mask2attr(tcp);
670 }
671 
672 static void
673 scterm_clear(scr_stat *scp)
674 {
675 	term_stat *tcp = scp->ts;
676 
677 	sc_move_cursor(scp, 0, 0);
678 	sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], tcp->cur_attr);
679 	mark_all(scp);
680 }
681 
682 static void
683 scterm_notify(scr_stat *scp, int event)
684 {
685 	switch (event) {
686 	case SC_TE_NOTIFY_VTSWITCH_IN:
687 		break;
688 	case SC_TE_NOTIFY_VTSWITCH_OUT:
689 		break;
690 	}
691 }
692 
693 static int
694 scterm_input(scr_stat *scp, int c, struct tty *tp)
695 {
696 	return FALSE;
697 }
698 
699 /*
700  * Calculate hardware attributes word using logical attributes mask and
701  * hardware colors
702  */
703 
704 /* FIXME */
705 static int
706 mask2attr(term_stat *tcp)
707 {
708 	int attr, mask = tcp->attr_mask;
709 
710 	if (mask & REVERSE_ATTR) {
711 		attr = ((mask & FG_CHANGED) ?
712 			tcp->cur_color.bg : tcp->rev_color.fg) |
713 			(((mask & BG_CHANGED) ?
714 			tcp->cur_color.fg : tcp->rev_color.bg) << 4);
715 	} else
716 		attr = tcp->cur_color.fg | (tcp->cur_color.bg << 4);
717 
718 	/* XXX: underline mapping for Hercules adapter can be better */
719 	if (mask & (BOLD_ATTR | UNDERLINE_ATTR))
720 		attr ^= 0x08;
721 	if (mask & BLINK_ATTR)
722 		attr ^= 0x80;
723 
724 	return (attr << 8);
725 }
726 
727 #endif /* SC_DUMB_TERMINAL */
728 
729 #endif /* NSC > 0 */
730