xref: /freebsd/sys/dev/syscons/scterm-sc.c (revision f157ca4696f5922275d5d451736005b9332eb136)
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 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 #include "opt_syscons.h"
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/consio.h>
38 
39 #if defined(__arm__) || defined(__mips__) || \
40 	defined(__powerpc__) || defined(__sparc64__)
41 #include <machine/sc_machdep.h>
42 #else
43 #include <machine/pc/display.h>
44 #endif
45 
46 #include <dev/syscons/syscons.h>
47 #include <dev/syscons/sctermvar.h>
48 
49 #define MAX_ESC_PAR	5
50 
51 /* attribute flags */
52 typedef struct {
53 	u_short		fg;			/* foreground color */
54 	u_short		bg;			/* background color */
55 } color_t;
56 
57 typedef struct {
58 	int		flags;
59 #define SCTERM_BUSY	(1 << 0)
60 	int		esc;
61 	int		num_param;
62 	int		last_param;
63 	int		param[MAX_ESC_PAR];
64 	int		saved_xpos;
65 	int		saved_ypos;
66 	int		attr_mask;		/* current logical attr mask */
67 #define NORMAL_ATTR	0x00
68 #define BLINK_ATTR	0x01
69 #define BOLD_ATTR	0x02
70 #define UNDERLINE_ATTR	0x04
71 #define REVERSE_ATTR	0x08
72 #define FG_CHANGED	0x10
73 #define BG_CHANGED	0x20
74 	int		cur_attr;		/* current hardware attr word */
75 	color_t		cur_color;		/* current hardware color */
76 	color_t		std_color;		/* normal hardware color */
77 	color_t		rev_color;		/* reverse hardware color */
78 	color_t		dflt_std_color;		/* default normal color */
79 	color_t		dflt_rev_color;		/* default reverse color */
80 } term_stat;
81 
82 static sc_term_init_t	scterm_init;
83 static sc_term_term_t	scterm_term;
84 static sc_term_puts_t	scterm_puts;
85 static sc_term_ioctl_t	scterm_ioctl;
86 static sc_term_reset_t	scterm_reset;
87 static sc_term_default_attr_t	scterm_default_attr;
88 static sc_term_clear_t	scterm_clear;
89 static sc_term_notify_t	scterm_notify;
90 static sc_term_input_t	scterm_input;
91 static sc_term_fkeystr_t	scterm_fkeystr;
92 static sc_term_sync_t	scterm_sync;
93 
94 static sc_term_sw_t sc_term_sc = {
95 	{ NULL, NULL },
96 	"sc",					/* emulator name */
97 	"syscons terminal",			/* description */
98 	"*",					/* matching renderer, any :-) */
99 	sizeof(term_stat),			/* softc size */
100 	0,
101 	scterm_init,
102 	scterm_term,
103 	scterm_puts,
104 	scterm_ioctl,
105 	scterm_reset,
106 	scterm_default_attr,
107 	scterm_clear,
108 	scterm_notify,
109 	scterm_input,
110 	scterm_fkeystr,
111 	scterm_sync,
112 };
113 
114 SCTERM_MODULE(sc, sc_term_sc);
115 
116 static term_stat	reserved_term_stat;
117 static void		scterm_scan_esc(scr_stat *scp, term_stat *tcp,
118 					u_char c);
119 static int		mask2attr(term_stat *tcp);
120 
121 static int
122 scterm_init(scr_stat *scp, void **softc, int code)
123 {
124 	term_stat *tcp;
125 
126 	if (*softc == NULL) {
127 		if (reserved_term_stat.flags & SCTERM_BUSY)
128 			return EINVAL;
129 		*softc = &reserved_term_stat;
130 	}
131 	tcp = *softc;
132 
133 	switch (code) {
134 	case SC_TE_COLD_INIT:
135 		bzero(tcp, sizeof(*tcp));
136 		tcp->flags = SCTERM_BUSY;
137 		tcp->esc = 0;
138 		tcp->saved_xpos = -1;
139 		tcp->saved_ypos = -1;
140 		tcp->attr_mask = NORMAL_ATTR;
141 		/* XXX */
142 		tcp->dflt_std_color.fg = SC_NORM_ATTR & 0x0f;
143 		tcp->dflt_std_color.bg = (SC_NORM_ATTR >> 4) & 0x0f;
144 		tcp->dflt_rev_color.fg = SC_NORM_REV_ATTR & 0x0f;
145 		tcp->dflt_rev_color.bg = (SC_NORM_REV_ATTR >> 4) & 0x0f;
146 		tcp->std_color = tcp->dflt_std_color;
147 		tcp->rev_color = tcp->dflt_rev_color;
148 		tcp->cur_color = tcp->std_color;
149 		tcp->cur_attr = mask2attr(tcp);
150 		++sc_term_sc.te_refcount;
151 		break;
152 
153 	case SC_TE_WARM_INIT:
154 		tcp->esc = 0;
155 		tcp->saved_xpos = -1;
156 		tcp->saved_ypos = -1;
157 #if 0
158 		tcp->std_color = tcp->dflt_std_color;
159 		tcp->rev_color = tcp->dflt_rev_color;
160 #endif
161 		tcp->cur_color = tcp->std_color;
162 		tcp->cur_attr = mask2attr(tcp);
163 		break;
164 	}
165 
166 	return 0;
167 }
168 
169 static int
170 scterm_term(scr_stat *scp, void **softc)
171 {
172 	if (*softc == &reserved_term_stat) {
173 		*softc = NULL;
174 		bzero(&reserved_term_stat, sizeof(reserved_term_stat));
175 	}
176 	--sc_term_sc.te_refcount;
177 	return 0;
178 }
179 
180 static void
181 scterm_scan_esc(scr_stat *scp, term_stat *tcp, u_char c)
182 {
183 	static u_char ansi_col[16] = {
184 		FG_BLACK,     FG_RED,          FG_GREEN,      FG_BROWN,
185 		FG_BLUE,      FG_MAGENTA,      FG_CYAN,       FG_LIGHTGREY,
186 		FG_DARKGREY,  FG_LIGHTRED,     FG_LIGHTGREEN, FG_YELLOW,
187 		FG_LIGHTBLUE, FG_LIGHTMAGENTA, FG_LIGHTCYAN,  FG_WHITE
188 	};
189 	static int cattrs[] = {
190 		0,					/* block */
191 		CONS_BLINK_CURSOR,			/* blinking block */
192 		CONS_CHAR_CURSOR,			/* underline */
193 		CONS_CHAR_CURSOR | CONS_BLINK_CURSOR,	/* blinking underline */
194 		CONS_RESET_CURSOR,			/* reset to default */
195 		CONS_HIDDEN_CURSOR,			/* hide cursor */
196 	};
197 	static int tcattrs[] = {
198 		CONS_RESET_CURSOR | CONS_LOCAL_CURSOR,	/* normal */
199 		CONS_HIDDEN_CURSOR | CONS_LOCAL_CURSOR,	/* invisible */
200 		CONS_BLINK_CURSOR | CONS_LOCAL_CURSOR,	/* very visible */
201 	};
202 	sc_softc_t *sc;
203 	int v0, v1, v2;
204 	int i, n;
205 
206 	i = n = 0;
207 	sc = scp->sc;
208 	if (tcp->esc == 1) {	/* seen ESC */
209 		switch (c) {
210 
211 		case '7':	/* Save cursor position */
212 			tcp->saved_xpos = scp->xpos;
213 			tcp->saved_ypos = scp->ypos;
214 			break;
215 
216 		case '8':	/* Restore saved cursor position */
217 			if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0)
218 				sc_move_cursor(scp, tcp->saved_xpos,
219 					       tcp->saved_ypos);
220 			break;
221 
222 		case '[':	/* Start ESC [ sequence */
223 			tcp->esc = 2;
224 			tcp->last_param = -1;
225 			for (i = tcp->num_param; i < MAX_ESC_PAR; i++)
226 				tcp->param[i] = 1;
227 			tcp->num_param = 0;
228 			return;
229 
230 		case 'M':	/* Move cursor up 1 line, scroll if at top */
231 			sc_term_up_scroll(scp, 1, sc->scr_map[0x20],
232 					  tcp->cur_attr, 0, 0);
233 			break;
234 #ifdef notyet
235 		case 'Q':
236 			tcp->esc = 4;
237 			return;
238 #endif
239 		case 'c':       /* reset */
240 			tcp->attr_mask = NORMAL_ATTR;
241 			tcp->cur_color = tcp->std_color
242 				       = tcp->dflt_std_color;
243 			tcp->rev_color = tcp->dflt_rev_color;
244 			tcp->cur_attr = mask2attr(tcp);
245 			sc_change_cursor_shape(scp,
246 			    CONS_RESET_CURSOR | CONS_LOCAL_CURSOR, -1, -1);
247 			sc_clear_screen(scp);
248 			break;
249 
250 		case '(':	/* iso-2022: designate 94 character set to G0 */
251 			tcp->esc = 5;
252 			return;
253 		}
254 	} else if (tcp->esc == 2) {	/* seen ESC [ */
255 		if (c >= '0' && c <= '9') {
256 			if (tcp->num_param < MAX_ESC_PAR) {
257 				if (tcp->last_param != tcp->num_param) {
258 					tcp->last_param = tcp->num_param;
259 					tcp->param[tcp->num_param] = 0;
260 				} else {
261 					tcp->param[tcp->num_param] *= 10;
262 				}
263 				tcp->param[tcp->num_param] += c - '0';
264 				return;
265 			}
266 		}
267 		tcp->num_param = tcp->last_param + 1;
268 		switch (c) {
269 
270 		case ';':
271 			if (tcp->num_param < MAX_ESC_PAR)
272 				return;
273 			break;
274 
275 		case '=':
276 			tcp->esc = 3;
277 			tcp->last_param = -1;
278 			for (i = tcp->num_param; i < MAX_ESC_PAR; i++)
279 				tcp->param[i] = 1;
280 			tcp->num_param = 0;
281 			return;
282 
283 		case 'A':	/* up n rows */
284 			sc_term_up(scp, tcp->param[0], 0);
285 			break;
286 
287 		case 'B':	/* down n rows */
288 			sc_term_down(scp, tcp->param[0], 0);
289 			break;
290 
291 		case 'C':	/* right n columns */
292 			sc_term_right(scp, tcp->param[0]);
293 			break;
294 
295 		case 'D':	/* left n columns */
296 			sc_term_left(scp, tcp->param[0]);
297 			break;
298 
299 		case 'E':	/* cursor to start of line n lines down */
300 			n = tcp->param[0];
301 			if (n < 1)
302 				n = 1;
303 			sc_move_cursor(scp, 0, scp->ypos + n);
304 			break;
305 
306 		case 'F':	/* cursor to start of line n lines up */
307 			n = tcp->param[0];
308 			if (n < 1)
309 				n = 1;
310 			sc_move_cursor(scp, 0, scp->ypos - n);
311 			break;
312 
313 		case 'f':	/* Cursor move */
314 		case 'H':
315 			if (tcp->num_param == 0)
316 				sc_move_cursor(scp, 0, 0);
317 			else if (tcp->num_param == 2)
318 				sc_move_cursor(scp, tcp->param[1] - 1,
319 					       tcp->param[0] - 1);
320 			break;
321 
322 		case 'J':	/* Clear all or part of display */
323 			if (tcp->num_param == 0)
324 				n = 0;
325 			else
326 				n = tcp->param[0];
327 			sc_term_clr_eos(scp, n, sc->scr_map[0x20],
328 					tcp->cur_attr);
329 			break;
330 
331 		case 'K':	/* Clear all or part of line */
332 			if (tcp->num_param == 0)
333 				n = 0;
334 			else
335 				n = tcp->param[0];
336 			sc_term_clr_eol(scp, n, sc->scr_map[0x20],
337 					tcp->cur_attr);
338 			break;
339 
340 		case 'L':	/* Insert n lines */
341 			sc_term_ins_line(scp, scp->ypos, tcp->param[0],
342 					 sc->scr_map[0x20], tcp->cur_attr, 0);
343 			break;
344 
345 		case 'M':	/* Delete n lines */
346 			sc_term_del_line(scp, scp->ypos, tcp->param[0],
347 					 sc->scr_map[0x20], tcp->cur_attr, 0);
348 			break;
349 
350 		case 'P':	/* Delete n chars */
351 			sc_term_del_char(scp, tcp->param[0],
352 					 sc->scr_map[0x20], tcp->cur_attr);
353 			break;
354 
355 		case '@':	/* Insert n chars */
356 			sc_term_ins_char(scp, tcp->param[0],
357 					 sc->scr_map[0x20], tcp->cur_attr);
358 			break;
359 
360 		case 'S':	/* scroll up n lines */
361 			sc_term_del_line(scp, 0, tcp->param[0],
362 					 sc->scr_map[0x20], tcp->cur_attr, 0);
363 			break;
364 
365 		case 'T':	/* scroll down n lines */
366 			sc_term_ins_line(scp, 0, tcp->param[0],
367 					 sc->scr_map[0x20], tcp->cur_attr, 0);
368 			break;
369 
370 		case 'X':	/* erase n characters in line */
371 			n = tcp->param[0];
372 			if (n < 1)
373 				n = 1;
374 			if (n > scp->xsize - scp->xpos)
375 				n = scp->xsize - scp->xpos;
376 			sc_vtb_erase(&scp->vtb, scp->cursor_pos, n,
377 				     sc->scr_map[0x20], tcp->cur_attr);
378 			mark_for_update(scp, scp->cursor_pos);
379 			mark_for_update(scp, scp->cursor_pos + n - 1);
380 			break;
381 
382 		case 'Z':	/* move n tabs backwards */
383 			sc_term_backtab(scp, tcp->param[0]);
384 			break;
385 
386 		case '`':	/* move cursor to column n */
387 			sc_term_col(scp, tcp->param[0]);
388 			break;
389 
390 		case 'a':	/* move cursor n columns to the right */
391 			sc_term_right(scp, tcp->param[0]);
392 			break;
393 
394 		case 'd':	/* move cursor to row n */
395 			sc_term_row(scp, tcp->param[0]);
396 			break;
397 
398 		case 'e':	/* move cursor n rows down */
399 			sc_term_down(scp, tcp->param[0], 0);
400 			break;
401 
402 		case 'm':	/* change attribute */
403 			if (tcp->num_param == 0) {
404 				tcp->attr_mask = NORMAL_ATTR;
405 				tcp->cur_color = tcp->std_color;
406 				tcp->cur_attr = mask2attr(tcp);
407 				break;
408 			}
409 			for (i = 0; i < tcp->num_param; i++) {
410 				switch (n = tcp->param[i]) {
411 				case 0:	/* back to normal */
412 					tcp->attr_mask = NORMAL_ATTR;
413 					tcp->cur_color = tcp->std_color;
414 					tcp->cur_attr = mask2attr(tcp);
415 					break;
416 				case 1:	/* bold */
417 					tcp->attr_mask |= BOLD_ATTR;
418 					tcp->cur_attr = mask2attr(tcp);
419 					break;
420 				case 4:	/* underline */
421 					tcp->attr_mask |= UNDERLINE_ATTR;
422 					tcp->cur_attr = mask2attr(tcp);
423 					break;
424 				case 5:	/* blink */
425 					tcp->attr_mask |= BLINK_ATTR;
426 					tcp->cur_attr = mask2attr(tcp);
427 					break;
428 				case 7: /* reverse */
429 					tcp->attr_mask |= REVERSE_ATTR;
430 					tcp->cur_attr = mask2attr(tcp);
431 					break;
432 				case 22: /* remove bold (or dim) */
433 					tcp->attr_mask &= ~BOLD_ATTR;
434 					tcp->cur_attr = mask2attr(tcp);
435 					break;
436 				case 24: /* remove underline */
437 					tcp->attr_mask &= ~UNDERLINE_ATTR;
438 					tcp->cur_attr = mask2attr(tcp);
439 					break;
440 				case 25: /* remove blink */
441 					tcp->attr_mask &= ~BLINK_ATTR;
442 					tcp->cur_attr = mask2attr(tcp);
443 					break;
444 				case 27: /* remove reverse */
445 					tcp->attr_mask &= ~REVERSE_ATTR;
446 					tcp->cur_attr = mask2attr(tcp);
447 					break;
448 				case 30: case 31: /* set ansi fg color */
449 				case 32: case 33: case 34:
450 				case 35: case 36: case 37:
451 					tcp->attr_mask |= FG_CHANGED;
452 					tcp->cur_color.fg = ansi_col[n - 30];
453 					tcp->cur_attr = mask2attr(tcp);
454 					break;
455 				case 39: /* restore fg color back to normal */
456 					tcp->attr_mask &= ~(FG_CHANGED|BOLD_ATTR);
457 					tcp->cur_color.fg = tcp->std_color.fg;
458 					tcp->cur_attr = mask2attr(tcp);
459 					break;
460 				case 40: case 41: /* set ansi bg color */
461 				case 42: case 43: case 44:
462 				case 45: case 46: case 47:
463 					tcp->attr_mask |= BG_CHANGED;
464 					tcp->cur_color.bg = ansi_col[n - 40];
465 					tcp->cur_attr = mask2attr(tcp);
466 		    			break;
467 				case 49: /* restore bg color back to normal */
468 					tcp->attr_mask &= ~BG_CHANGED;
469 					tcp->cur_color.bg = tcp->std_color.bg;
470 					tcp->cur_attr = mask2attr(tcp);
471 					break;
472 				}
473 			}
474 			break;
475 
476 		case 's':	/* Save cursor position */
477 			tcp->saved_xpos = scp->xpos;
478 			tcp->saved_ypos = scp->ypos;
479 			break;
480 
481 		case 'u':	/* Restore saved cursor position */
482 			if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0)
483 				sc_move_cursor(scp, tcp->saved_xpos,
484 					       tcp->saved_ypos);
485 			break;
486 
487 		case 'x':
488 			if (tcp->num_param == 0)
489 				n = 0;
490 			else
491 				n = tcp->param[0];
492 			switch (n) {
493 			case 0: /* reset colors and attributes back to normal */
494 				tcp->attr_mask = NORMAL_ATTR;
495 				tcp->cur_color = tcp->std_color
496 					       = tcp->dflt_std_color;
497 				tcp->rev_color = tcp->dflt_rev_color;
498 				tcp->cur_attr = mask2attr(tcp);
499 				break;
500 			case 1:	/* set ansi background */
501 				tcp->attr_mask &= ~BG_CHANGED;
502 				tcp->cur_color.bg = tcp->std_color.bg
503 						  = ansi_col[tcp->param[1] & 0x0f];
504 				tcp->cur_attr = mask2attr(tcp);
505 				break;
506 			case 2:	/* set ansi foreground */
507 				tcp->attr_mask &= ~FG_CHANGED;
508 				tcp->cur_color.fg = tcp->std_color.fg
509 						  = ansi_col[tcp->param[1] & 0x0f];
510 				tcp->cur_attr = mask2attr(tcp);
511 				break;
512 			case 3: /* set adapter attribute directly */
513 				tcp->attr_mask &= ~(FG_CHANGED | BG_CHANGED);
514 				tcp->cur_color.fg = tcp->std_color.fg
515 						  = tcp->param[1] & 0x0f;
516 				tcp->cur_color.bg = tcp->std_color.bg
517 						  = (tcp->param[1] >> 4) & 0x0f;
518 				tcp->cur_attr = mask2attr(tcp);
519 				break;
520 			case 5: /* set ansi reverse background */
521 				tcp->rev_color.bg = ansi_col[tcp->param[1] & 0x0f];
522 				tcp->cur_attr = mask2attr(tcp);
523 				break;
524 			case 6: /* set ansi reverse foreground */
525 				tcp->rev_color.fg = ansi_col[tcp->param[1] & 0x0f];
526 				tcp->cur_attr = mask2attr(tcp);
527 				break;
528 			case 7: /* set adapter reverse attribute directly */
529 				tcp->rev_color.fg = tcp->param[1] & 0x0f;
530 				tcp->rev_color.bg = (tcp->param[1] >> 4) & 0x0f;
531 				tcp->cur_attr = mask2attr(tcp);
532 				break;
533 			}
534 			break;
535 
536 		case 'z':	/* switch to (virtual) console n */
537 			if (tcp->num_param == 1)
538 				sc_switch_scr(sc, tcp->param[0]);
539 			break;
540 		}
541 	} else if (tcp->esc == 3) {	/* seen ESC [0-9]+ = */
542 		if (c >= '0' && c <= '9') {
543 			if (tcp->num_param < MAX_ESC_PAR) {
544 				if (tcp->last_param != tcp->num_param) {
545 					tcp->last_param = tcp->num_param;
546 					tcp->param[tcp->num_param] = 0;
547 				} else {
548 					tcp->param[tcp->num_param] *= 10;
549 				}
550 				tcp->param[tcp->num_param] += c - '0';
551 				return;
552 			}
553 		}
554 		tcp->num_param = tcp->last_param + 1;
555 		switch (c) {
556 
557 		case ';':
558 			if (tcp->num_param < MAX_ESC_PAR)
559 				return;
560 			break;
561 
562 		case 'A':   /* set display border color */
563 			if (tcp->num_param == 1) {
564 				scp->border=tcp->param[0] & 0xff;
565 				if (scp == sc->cur_scp)
566 					sc_set_border(scp, scp->border);
567 			}
568 			break;
569 
570 		case 'B':   /* set bell pitch and duration */
571 			if (tcp->num_param == 2) {
572 				scp->bell_pitch = tcp->param[0];
573 				scp->bell_duration =
574 				    (tcp->param[1] * hz + 99) / 100;
575 			}
576 			break;
577 
578 		case 'C':   /* set global/parmanent cursor type & shape */
579 			i = spltty();
580 			n = tcp->num_param;
581 			v0 = tcp->param[0];
582 			v1 = tcp->param[1];
583 			v2 = tcp->param[2];
584 			switch (n) {
585 			case 1:	/* flags only */
586 				if (v0 < sizeof(cattrs)/sizeof(cattrs[0]))
587 					v0 = cattrs[v0];
588 				else	/* backward compatibility */
589 					v0 = cattrs[v0 & 0x3];
590 				sc_change_cursor_shape(scp, v0, -1, -1);
591 				break;
592 			case 2:
593 				v2 = 0;
594 				v0 &= 0x1f;	/* backward compatibility */
595 				v1 &= 0x1f;
596 				/* FALL THROUGH */
597 			case 3:	/* base and height */
598 				if (v2 == 0)	/* count from top */
599 					sc_change_cursor_shape(scp, -1,
600 					    scp->font_size - v1 - 1,
601 					    v1 - v0 + 1);
602 				else if (v2 == 1) /* count from bottom */
603 					sc_change_cursor_shape(scp, -1,
604 					    v0, v1 - v0 + 1);
605 				break;
606 			}
607 			splx(i);
608 			break;
609 
610 		case 'F':   /* set adapter foreground */
611 			if (tcp->num_param == 1) {
612 				tcp->attr_mask &= ~FG_CHANGED;
613 				tcp->cur_color.fg = tcp->std_color.fg
614 						  = tcp->param[0] & 0x0f;
615 				tcp->cur_attr = mask2attr(tcp);
616 			}
617 			break;
618 
619 		case 'G':   /* set adapter background */
620 			if (tcp->num_param == 1) {
621 				tcp->attr_mask &= ~BG_CHANGED;
622 				tcp->cur_color.bg = tcp->std_color.bg
623 						  = tcp->param[0] & 0x0f;
624 				tcp->cur_attr = mask2attr(tcp);
625 			}
626 			break;
627 
628 		case 'H':   /* set adapter reverse foreground */
629 			if (tcp->num_param == 1) {
630 				tcp->rev_color.fg = tcp->param[0] & 0x0f;
631 				tcp->cur_attr = mask2attr(tcp);
632 			}
633 			break;
634 
635 		case 'I':   /* set adapter reverse background */
636 			if (tcp->num_param == 1) {
637 				tcp->rev_color.bg = tcp->param[0] & 0x0f;
638 				tcp->cur_attr = mask2attr(tcp);
639 			}
640 			break;
641 
642 		case 'S':   /* set local/temporary cursor type & shape */
643 			i = spltty();
644 			n = tcp->num_param;
645 			v0 = tcp->param[0];
646 			switch (n) {
647 			case 0:
648 				v0 = 0;
649 				/* FALL THROUGH */
650 			case 1:
651 				if (v0 < sizeof(tcattrs)/sizeof(tcattrs[0]))
652 					sc_change_cursor_shape(scp,
653 					    tcattrs[v0], -1, -1);
654 				break;
655 			}
656 			splx(i);
657 			break;
658 		}
659 #ifdef notyet
660 	} else if (tcp->esc == 4) {	/* seen ESC Q */
661 		/* to be filled */
662 #endif
663 	} else if (tcp->esc == 5) {	/* seen ESC ( */
664 		switch (c) {
665 		case 'B':   /* iso-2022: desginate ASCII into G0 */
666 			break;
667 		/* other items to be filled */
668 		default:
669 			break;
670 		}
671 	}
672 	tcp->esc = 0;
673 }
674 
675 static void
676 scterm_puts(scr_stat *scp, u_char *buf, int len)
677 {
678 	term_stat *tcp;
679 
680 	tcp = scp->ts;
681 outloop:
682 	scp->sc->write_in_progress++;
683 
684 	if (tcp->esc) {
685 		scterm_scan_esc(scp, tcp, *buf);
686 		buf++;
687 		len--;
688 	} else {
689 		switch (*buf) {
690 		case 0x1b:
691 			tcp->esc = 1;
692 			tcp->num_param = 0;
693 			buf++;
694 			len--;
695 			break;
696 		default:
697 			sc_term_gen_print(scp, &buf, &len, tcp->cur_attr);
698 			break;
699 		}
700 	}
701 
702 	sc_term_gen_scroll(scp, scp->sc->scr_map[0x20], tcp->cur_attr);
703 
704 	scp->sc->write_in_progress--;
705 	if (len)
706 		goto outloop;
707 }
708 
709 static int
710 scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data,
711 	     struct thread *td)
712 {
713 	term_stat *tcp = scp->ts;
714 	vid_info_t *vi;
715 
716 	switch (cmd) {
717 	case GIO_ATTR:      	/* get current attributes */
718 		/* FIXME: */
719 		*(int*)data = (tcp->cur_attr >> 8) & 0xff;
720 		return 0;
721 	case CONS_GETINFO:  	/* get current (virtual) console info */
722 		vi = (vid_info_t *)data;
723 		if (vi->size != sizeof(struct vid_info))
724 			return EINVAL;
725 		vi->mv_norm.fore = tcp->std_color.fg;
726 		vi->mv_norm.back = tcp->std_color.bg;
727 		vi->mv_rev.fore = tcp->rev_color.fg;
728 		vi->mv_rev.back = tcp->rev_color.bg;
729 		/*
730 		 * The other fields are filled by the upper routine. XXX
731 		 */
732 		return ENOIOCTL;
733 	}
734 	return ENOIOCTL;
735 }
736 
737 static int
738 scterm_reset(scr_stat *scp, int code)
739 {
740 	/* FIXME */
741 	return 0;
742 }
743 
744 static void
745 scterm_default_attr(scr_stat *scp, int color, int rev_color)
746 {
747 	term_stat *tcp = scp->ts;
748 
749 	tcp->dflt_std_color.fg = color & 0x0f;
750 	tcp->dflt_std_color.bg = (color >> 4) & 0x0f;
751 	tcp->dflt_rev_color.fg = rev_color & 0x0f;
752 	tcp->dflt_rev_color.bg = (rev_color >> 4) & 0x0f;
753 	tcp->std_color = tcp->dflt_std_color;
754 	tcp->rev_color = tcp->dflt_rev_color;
755 	tcp->cur_color = tcp->std_color;
756 	tcp->cur_attr = mask2attr(tcp);
757 }
758 
759 static void
760 scterm_clear(scr_stat *scp)
761 {
762 	term_stat *tcp = scp->ts;
763 
764 	sc_move_cursor(scp, 0, 0);
765 	sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], tcp->cur_attr);
766 	mark_all(scp);
767 }
768 
769 static void
770 scterm_notify(scr_stat *scp, int event)
771 {
772 	switch (event) {
773 	case SC_TE_NOTIFY_VTSWITCH_IN:
774 		break;
775 	case SC_TE_NOTIFY_VTSWITCH_OUT:
776 		break;
777 	}
778 }
779 
780 static int
781 scterm_input(scr_stat *scp, int c, struct tty *tp)
782 {
783 	return FALSE;
784 }
785 
786 static const char *
787 scterm_fkeystr(scr_stat *scp, int c)
788 {
789 	return (NULL);
790 }
791 
792 static void
793 scterm_sync(scr_stat *scp)
794 {
795 }
796 
797 /*
798  * Calculate hardware attributes word using logical attributes mask and
799  * hardware colors
800  */
801 
802 /* FIXME */
803 static int
804 mask2attr(term_stat *tcp)
805 {
806 	int attr, mask = tcp->attr_mask;
807 
808 	if (mask & REVERSE_ATTR) {
809 		attr = ((mask & FG_CHANGED) ?
810 			tcp->cur_color.bg : tcp->rev_color.fg) |
811 			(((mask & BG_CHANGED) ?
812 			tcp->cur_color.fg : tcp->rev_color.bg) << 4);
813 	} else
814 		attr = tcp->cur_color.fg | (tcp->cur_color.bg << 4);
815 
816 	/* XXX: underline mapping for Hercules adapter can be better */
817 	if (mask & (BOLD_ATTR | UNDERLINE_ATTR))
818 		attr ^= 0x08;
819 	if (mask & BLINK_ATTR)
820 		attr ^= 0x80;
821 
822 	return (attr << 8);
823 }
824