scterm-sck.c revision 199171
1/*-
2 * Copyright (c) 1999 FreeBSD(98) Porting Team.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer as
10 *    the first lines of this file unmodified.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/pc98/cbus/scterm-sck.c 199171 2009-11-11 08:20:19Z ed $
27 */
28
29#include "opt_syscons.h"
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/module.h>
35#include <sys/consio.h>
36
37#include <machine/pc/display.h>
38
39#include <dev/syscons/syscons.h>
40#include <pc98/cbus/sctermvar.h>
41
42#define MAX_ESC_PAR	5
43
44#ifdef KANJI
45#define IS_KTYPE_ASCII_or_HANKAKU(A)	(!((A) & 0xee))
46#define IS_KTYPE_KANA(A)		((A) & 0x11)
47#define KTYPE_MASK_CTRL(A)		((A) &= 0xF0)
48#endif /* KANJI */
49
50/* attribute flags */
51typedef struct {
52	u_short		fg;			/* foreground color */
53	u_short		bg;			/* background color */
54} color_t;
55
56typedef struct {
57	int		flags;
58#define SCTERM_BUSY	(1 << 0)
59	int		esc;
60	int		num_param;
61	int		last_param;
62	int		param[MAX_ESC_PAR];
63	int		saved_xpos;
64	int		saved_ypos;
65
66#ifdef KANJI
67	u_char		kanji_1st_char;
68	u_char		kanji_type;
69#define KTYPE_ASCII	0			/* ASCII */
70#define KTYPE_KANA	1			/* HANKAKU */
71#define KTYPE_JKANA	0x10			/* JIS HANKAKU */
72#define KTYPE_7JIS	0x20			/* JIS */
73#define KTYPE_SJIS	2			/* Shift JIS */
74#define KTYPE_UJIS	4			/* UJIS */
75#define KTYPE_SUKANA	3			/* Shift JIS or UJIS HANKAKU */
76#define KTYPE_SUJIS	6			/* SHift JIS or UJIS */
77#define KTYPE_KANIN	0x80			/* Kanji Invoke sequence */
78#define KTYPE_ASCIN	0x40			/* ASCII Invoke sequence */
79#endif /* KANJI */
80
81	int		attr_mask;		/* current logical attr mask */
82#define NORMAL_ATTR	0x00
83#define BLINK_ATTR	0x01
84#define BOLD_ATTR	0x02
85#define UNDERLINE_ATTR	0x04
86#define REVERSE_ATTR	0x08
87#define FG_CHANGED	0x10
88#define BG_CHANGED	0x20
89	int		cur_attr;		/* current hardware attr word */
90	color_t		cur_color;		/* current hardware color */
91	color_t		std_color;		/* normal hardware color */
92	color_t		rev_color;		/* reverse hardware color */
93	color_t		dflt_std_color;		/* default normal color */
94	color_t		dflt_rev_color;		/* default reverse color */
95} term_stat;
96
97static sc_term_init_t		scterm_init;
98static sc_term_term_t		scterm_term;
99static sc_term_puts_t		scterm_puts;
100static sc_term_ioctl_t		scterm_ioctl;
101static sc_term_reset_t		scterm_reset;
102static sc_term_default_attr_t	scterm_default_attr;
103static sc_term_clear_t		scterm_clear;
104static sc_term_notify_t		scterm_notify;
105static sc_term_input_t		scterm_input;
106static sc_term_fkeystr_t	scterm_fkeystr;
107
108static sc_term_sw_t sc_term_sc = {
109	{ NULL, NULL },
110	"sck",					/* emulator name */
111	"syscons kanji terminal",		/* description */
112	"*",					/* matching renderer, any :-) */
113	sizeof(term_stat),			/* softc size */
114	0,
115	scterm_init,
116	scterm_term,
117	scterm_puts,
118	scterm_ioctl,
119	scterm_reset,
120	scterm_default_attr,
121	scterm_clear,
122	scterm_notify,
123	scterm_input,
124	scterm_fkeystr,
125};
126
127SCTERM_MODULE(sc, sc_term_sc);
128
129static term_stat	reserved_term_stat;
130static int		default_kanji = UJIS;
131static void		scterm_scan_esc(scr_stat *scp, term_stat *tcp,
132					u_char c);
133static int		mask2attr(term_stat *tcp);
134
135#ifdef KANJI
136__inline static u_char
137iskanji1(u_char mode, u_char c)
138{
139	if (c > 0x80) {
140		if ((c >= 0xa1) && (c <= 0xdf)) {
141			if (default_kanji == UJIS) {
142				/* UJIS */
143				return KTYPE_UJIS;
144			}
145			if (default_kanji == SJIS) {
146				/* SJIS HANKAKU */
147				return KTYPE_KANA;
148			}
149		}
150
151		if (c <= 0x9f) {
152			if (c == 0x8e) {
153				/* SJIS or UJIS HANKAKU */
154				return KTYPE_SUKANA;
155			}
156
157			/* SJIS */
158			default_kanji = SJIS;
159			return KTYPE_SJIS;
160		}
161
162		if ((c >= 0xe0) && (c <= 0xef)) {
163			/* SJIS or UJIS */
164			return KTYPE_SUJIS;
165		}
166
167		if ((c >= 0xf0) && (c <= 0xfe)) {
168			/* UJIS */
169			default_kanji = UJIS;
170			return KTYPE_UJIS;
171		}
172	} else {
173		if ((mode == KTYPE_7JIS) && (c >= 0x21) && (c <= 0x7e)) {
174			/* JIS */
175			default_kanji = UJIS;
176			return KTYPE_7JIS;
177		}
178
179		if ((mode == KTYPE_JKANA) && (c >= 0x21) && (c <= 0x5f)) {
180			/* JIS HANKAKU */
181			default_kanji = UJIS;
182			return KTYPE_JKANA;
183		}
184	}
185
186	return KTYPE_ASCII;
187}
188
189__inline static u_char
190iskanji2(u_char mode, u_char c)
191{
192	switch (mode) {
193	case KTYPE_7JIS:
194		if ((c >= 0x21) && (c <= 0x7e)) {
195			/* JIS */
196			return KTYPE_7JIS;
197		}
198		break;
199	case KTYPE_SJIS:
200		if ((c >= 0x40) && (c <= 0xfc) && (c != 0x7f)) {
201			/* SJIS */
202			return KTYPE_SJIS;
203		}
204		break;
205	case KTYPE_UJIS:
206		if ((c >= 0xa1) && (c <= 0xfe)) {
207			/* UJIS */
208			return KTYPE_UJIS;
209		}
210		break;
211	case KTYPE_SUKANA:
212		if ((c >= 0xa1) && (c <= 0xdf) && (default_kanji == UJIS)) {
213			/* UJIS HANKAKU */
214			return KTYPE_KANA;
215		}
216		if ((c >= 0x40) && (c <= 0xfc) && (c != 0x7f)) {
217			/* SJIS */
218			default_kanji = SJIS;
219			return KTYPE_SJIS;
220		}
221		break;
222	case KTYPE_SUJIS:
223		if ((c >= 0x40) && (c <= 0xa0) && (c != 0x7f)) {
224			/* SJIS */
225			default_kanji = SJIS;
226			return KTYPE_SJIS;
227		}
228		if ((c == 0xfd) || (c == 0xfe)) {
229			/* UJIS */
230			default_kanji = UJIS;
231			return KTYPE_UJIS;
232		}
233		if ((c >= 0xa1) && (c <= 0xfc)) {
234			if (default_kanji == SJIS)
235				return KTYPE_SJIS;
236			if (default_kanji == UJIS)
237				return KTYPE_UJIS;
238		}
239		break;
240	}
241
242	return KTYPE_ASCII;
243}
244
245/*
246 * JIS X0208-83 keisen conversion table
247 */
248static u_short keiConv[32] = {
249	0x240c, 0x260c, 0x300c, 0x340c, 0x3c0c, 0x380c, 0x400c, 0x500c,
250	0x480c, 0x580c, 0x600c, 0x250c, 0x270c, 0x330c, 0x370c, 0x3f0c,
251	0x3b0c, 0x470c, 0x570c, 0x4f0c, 0x5f0c, 0x6f0c, 0x440c, 0x530c,
252	0x4c0c, 0x5b0c, 0x630c, 0x410c, 0x540c, 0x490c, 0x5c0c, 0x660c
253};
254
255static u_short
256kanji_convert(u_char mode, u_char h, u_char l)
257{
258	u_short tmp, high, low, c;
259
260	high = (u_short) h;
261	low  = (u_short) l;
262
263	switch (mode) {
264	case KTYPE_SJIS: /* SHIFT JIS */
265		if (low >= 0xe0) {
266			low -= 0x40;
267		}
268		low = (low - 0x81) * 2 + 0x21;
269		if (high > 0x7f) {
270			high--;
271		}
272		if (high > 0x9d) {
273			low++;
274			high -= 0x9e - 0x21;
275		} else {
276			high -= 0x40 - 0x21;
277		}
278		high &= 0x7F;
279		low  &= 0x7F;
280		tmp = ((high << 8) | low) - 0x20;
281		break;
282	case KTYPE_7JIS: /* JIS */
283	case KTYPE_UJIS: /* UJIS */
284		high &= 0x7F;
285		low &= 0x7F;
286		tmp = ((high << 8) | low) - 0x20;
287		break;
288	default:
289		tmp = 0;
290		break;
291	}
292
293	/* keisen */
294	c = ((tmp & 0xff) << 8) | (tmp >> 8);
295	/* 0x2821 .. 0x2840 */
296	if (0x0821 <= c && c <= 0x0840)
297		tmp = keiConv[c - 0x0821];
298
299	return (tmp);
300}
301#endif /* KANJI */
302
303static int
304scterm_init(scr_stat *scp, void **softc, int code)
305{
306	term_stat *tcp;
307
308	if (*softc == NULL) {
309		if (reserved_term_stat.flags & SCTERM_BUSY)
310			return EINVAL;
311		*softc = &reserved_term_stat;
312	}
313	tcp = *softc;
314
315	switch (code) {
316	case SC_TE_COLD_INIT:
317		bzero(tcp, sizeof(*tcp));
318		tcp->flags = SCTERM_BUSY;
319		tcp->esc = 0;
320		tcp->saved_xpos = -1;
321		tcp->saved_ypos = -1;
322#ifdef KANJI
323		tcp->kanji_1st_char = 0;
324		tcp->kanji_type = KTYPE_ASCII;
325#endif
326		tcp->attr_mask = NORMAL_ATTR;
327		/* XXX */
328		tcp->dflt_std_color.fg = SC_NORM_ATTR & 0x0f;
329		tcp->dflt_std_color.bg = (SC_NORM_ATTR >> 4) & 0x0f;
330		tcp->dflt_rev_color.fg = SC_NORM_REV_ATTR & 0x0f;
331		tcp->dflt_rev_color.bg = (SC_NORM_REV_ATTR >> 4) & 0x0f;
332		tcp->std_color = tcp->dflt_std_color;
333		tcp->rev_color = tcp->dflt_rev_color;
334		tcp->cur_color = tcp->std_color;
335		tcp->cur_attr = mask2attr(tcp);
336		++sc_term_sc.te_refcount;
337		break;
338
339	case SC_TE_WARM_INIT:
340		tcp->esc = 0;
341		tcp->saved_xpos = -1;
342		tcp->saved_ypos = -1;
343#if 0
344		tcp->std_color = tcp->dflt_std_color;
345		tcp->rev_color = tcp->dflt_rev_color;
346#endif
347		tcp->cur_color = tcp->std_color;
348		tcp->cur_attr = mask2attr(tcp);
349		break;
350	}
351
352	return 0;
353}
354
355static int
356scterm_term(scr_stat *scp, void **softc)
357{
358	if (*softc == &reserved_term_stat) {
359		*softc = NULL;
360		bzero(&reserved_term_stat, sizeof(reserved_term_stat));
361	}
362	--sc_term_sc.te_refcount;
363	return 0;
364}
365
366static void
367scterm_scan_esc(scr_stat *scp, term_stat *tcp, u_char c)
368{
369	static u_char ansi_col[16] = {
370		FG_BLACK,     FG_RED,          FG_GREEN,      FG_BROWN,
371		FG_BLUE,      FG_MAGENTA,      FG_CYAN,       FG_LIGHTGREY,
372		FG_DARKGREY,  FG_LIGHTRED,     FG_LIGHTGREEN, FG_YELLOW,
373		FG_LIGHTBLUE, FG_LIGHTMAGENTA, FG_LIGHTCYAN,  FG_WHITE
374	};
375	static int cattrs[] = {
376		0,					/* block */
377		CONS_BLINK_CURSOR,			/* blinking block */
378		CONS_CHAR_CURSOR,			/* underline */
379		CONS_CHAR_CURSOR | CONS_BLINK_CURSOR,	/* blinking underline */
380		CONS_RESET_CURSOR,			/* reset to default */
381		CONS_HIDDEN_CURSOR,			/* hide cursor */
382	};
383	static int tcattrs[] = {
384		CONS_RESET_CURSOR | CONS_LOCAL_CURSOR,	/* normal */
385		CONS_HIDDEN_CURSOR | CONS_LOCAL_CURSOR,	/* invisible */
386		CONS_BLINK_CURSOR | CONS_LOCAL_CURSOR,	/* very visible */
387	};
388	sc_softc_t *sc;
389	int v0, v1, v2;
390	int i, n;
391
392	i = n = 0;
393	sc = scp->sc;
394	if (tcp->esc == 1) {	/* seen ESC */
395#ifdef KANJI
396		switch (tcp->kanji_type) {
397		case KTYPE_KANIN:	/* Kanji Invoke sequence */
398			switch (c) {
399			case 'B':
400			case '@':
401				tcp->kanji_type = KTYPE_7JIS;
402				tcp->esc = 0;
403				tcp->kanji_1st_char = 0;
404				return;
405			default:
406				tcp->kanji_type = KTYPE_ASCII;
407				tcp->esc = 0;
408				break;
409			}
410			break;
411		case KTYPE_ASCIN:	/* Ascii Invoke sequence */
412			switch (c) {
413			case 'J':
414			case 'B':
415			case 'H':
416				tcp->kanji_type = KTYPE_ASCII;
417				tcp->esc = 0;
418				tcp->kanji_1st_char = 0;
419				return;
420			case 'I':
421				tcp->kanji_type = KTYPE_JKANA;
422				tcp->esc = 0;
423				tcp->kanji_1st_char = 0;
424				return;
425			default:
426				tcp->kanji_type = KTYPE_ASCII;
427				tcp->esc = 0;
428				break;
429			}
430			break;
431		default:
432			break;
433		}
434#endif
435		switch (c) {
436
437		case '7':	/* Save cursor position */
438			tcp->saved_xpos = scp->xpos;
439			tcp->saved_ypos = scp->ypos;
440			break;
441
442		case '8':	/* Restore saved cursor position */
443			if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0)
444				sc_move_cursor(scp, tcp->saved_xpos,
445					       tcp->saved_ypos);
446			break;
447
448		case '[':	/* Start ESC [ sequence */
449			tcp->esc = 2;
450			tcp->last_param = -1;
451			for (i = tcp->num_param; i < MAX_ESC_PAR; i++)
452				tcp->param[i] = 1;
453			tcp->num_param = 0;
454			return;
455
456#ifdef KANJI
457		case '$':	/* Kanji Invoke sequence */
458			tcp->kanji_type = KTYPE_KANIN;
459			return;
460#endif
461
462		case 'M':	/* Move cursor up 1 line, scroll if at top */
463			sc_term_up_scroll(scp, 1, sc->scr_map[0x20],
464					  tcp->cur_attr, 0, 0);
465			break;
466#ifdef notyet
467		case 'Q':
468			tcp->esc = 4;
469			return;
470#endif
471		case 'c':       /* reset */
472			tcp->attr_mask = NORMAL_ATTR;
473			tcp->cur_color = tcp->std_color
474				       = tcp->dflt_std_color;
475			tcp->rev_color = tcp->dflt_rev_color;
476			tcp->cur_attr = mask2attr(tcp);
477			sc_change_cursor_shape(scp,
478			    CONS_RESET_CURSOR | CONS_LOCAL_CURSOR, -1, -1);
479			sc_clear_screen(scp);
480			break;
481
482		case '(':	/* iso-2022: designate 94 character set to G0 */
483#ifdef KANJI
484			tcp->kanji_type = KTYPE_ASCIN;
485#else
486			tcp->esc = 5;
487#endif
488			return;
489		}
490	} else if (tcp->esc == 2) {	/* seen ESC [ */
491		if (c >= '0' && c <= '9') {
492			if (tcp->num_param < MAX_ESC_PAR) {
493				if (tcp->last_param != tcp->num_param) {
494					tcp->last_param = tcp->num_param;
495					tcp->param[tcp->num_param] = 0;
496				} else {
497					tcp->param[tcp->num_param] *= 10;
498				}
499				tcp->param[tcp->num_param] += c - '0';
500				return;
501			}
502		}
503		tcp->num_param = tcp->last_param + 1;
504		switch (c) {
505
506		case ';':
507			if (tcp->num_param < MAX_ESC_PAR)
508				return;
509			break;
510
511		case '=':
512			tcp->esc = 3;
513			tcp->last_param = -1;
514			for (i = tcp->num_param; i < MAX_ESC_PAR; i++)
515				tcp->param[i] = 1;
516			tcp->num_param = 0;
517			return;
518
519		case 'A':	/* up n rows */
520			sc_term_up(scp, tcp->param[0], 0);
521			break;
522
523		case 'B':	/* down n rows */
524			sc_term_down(scp, tcp->param[0], 0);
525			break;
526
527		case 'C':	/* right n columns */
528			sc_term_right(scp, tcp->param[0]);
529			break;
530
531		case 'D':	/* left n columns */
532			sc_term_left(scp, tcp->param[0]);
533			break;
534
535		case 'E':	/* cursor to start of line n lines down */
536			n = tcp->param[0];
537			if (n < 1)
538				n = 1;
539			sc_move_cursor(scp, 0, scp->ypos + n);
540			break;
541
542		case 'F':	/* cursor to start of line n lines up */
543			n = tcp->param[0];
544			if (n < 1)
545				n = 1;
546			sc_move_cursor(scp, 0, scp->ypos - n);
547			break;
548
549		case 'f':	/* Cursor move */
550		case 'H':
551			if (tcp->num_param == 0)
552				sc_move_cursor(scp, 0, 0);
553			else if (tcp->num_param == 2)
554				sc_move_cursor(scp, tcp->param[1] - 1,
555					       tcp->param[0] - 1);
556			break;
557
558		case 'J':	/* Clear all or part of display */
559			if (tcp->num_param == 0)
560				n = 0;
561			else
562				n = tcp->param[0];
563			sc_term_clr_eos(scp, n, sc->scr_map[0x20],
564					tcp->cur_attr);
565			break;
566
567		case 'K':	/* Clear all or part of line */
568			if (tcp->num_param == 0)
569				n = 0;
570			else
571				n = tcp->param[0];
572			sc_term_clr_eol(scp, n, sc->scr_map[0x20],
573					tcp->cur_attr);
574			break;
575
576		case 'L':	/* Insert n lines */
577			sc_term_ins_line(scp, scp->ypos, tcp->param[0],
578					 sc->scr_map[0x20], tcp->cur_attr, 0);
579			break;
580
581		case 'M':	/* Delete n lines */
582			sc_term_del_line(scp, scp->ypos, tcp->param[0],
583					 sc->scr_map[0x20], tcp->cur_attr, 0);
584			break;
585
586		case 'P':	/* Delete n chars */
587			sc_term_del_char(scp, tcp->param[0],
588					 sc->scr_map[0x20], tcp->cur_attr);
589			break;
590
591		case '@':	/* Insert n chars */
592			sc_term_ins_char(scp, tcp->param[0],
593					 sc->scr_map[0x20], tcp->cur_attr);
594			break;
595
596		case 'S':	/* scroll up n lines */
597			sc_term_del_line(scp, 0, tcp->param[0],
598					 sc->scr_map[0x20], tcp->cur_attr, 0);
599			break;
600
601		case 'T':	/* scroll down n lines */
602			sc_term_ins_line(scp, 0, tcp->param[0],
603					 sc->scr_map[0x20], tcp->cur_attr, 0);
604			break;
605
606		case 'X':	/* erase n characters in line */
607			n = tcp->param[0];
608			if (n < 1)
609				n = 1;
610			if (n > scp->xsize - scp->xpos)
611				n = scp->xsize - scp->xpos;
612			sc_vtb_erase(&scp->vtb, scp->cursor_pos, n,
613				     sc->scr_map[0x20], tcp->cur_attr);
614			mark_for_update(scp, scp->cursor_pos);
615			mark_for_update(scp, scp->cursor_pos + n - 1);
616			break;
617
618		case 'Z':	/* move n tabs backwards */
619			sc_term_backtab(scp, tcp->param[0]);
620			break;
621
622		case '`':	/* move cursor to column n */
623			sc_term_col(scp, tcp->param[0]);
624			break;
625
626		case 'a':	/* move cursor n columns to the right */
627			sc_term_right(scp, tcp->param[0]);
628			break;
629
630		case 'd':	/* move cursor to row n */
631			sc_term_row(scp, tcp->param[0]);
632			break;
633
634		case 'e':	/* move cursor n rows down */
635			sc_term_down(scp, tcp->param[0], 0);
636			break;
637
638		case 'm':	/* change attribute */
639			if (tcp->num_param == 0) {
640				tcp->attr_mask = NORMAL_ATTR;
641				tcp->cur_color = tcp->std_color;
642				tcp->cur_attr = mask2attr(tcp);
643				break;
644			}
645			for (i = 0; i < tcp->num_param; i++) {
646				switch (n = tcp->param[i]) {
647				case 0:	/* back to normal */
648					tcp->attr_mask = NORMAL_ATTR;
649					tcp->cur_color = tcp->std_color;
650					tcp->cur_attr = mask2attr(tcp);
651					break;
652				case 1:	/* bold */
653					tcp->attr_mask |= BOLD_ATTR;
654					tcp->cur_attr = mask2attr(tcp);
655					break;
656				case 4:	/* underline */
657					tcp->attr_mask |= UNDERLINE_ATTR;
658					tcp->cur_attr = mask2attr(tcp);
659					break;
660				case 5:	/* blink */
661					tcp->attr_mask |= BLINK_ATTR;
662					tcp->cur_attr = mask2attr(tcp);
663					break;
664				case 7: /* reverse */
665					tcp->attr_mask |= REVERSE_ATTR;
666					tcp->cur_attr = mask2attr(tcp);
667					break;
668				case 22: /* remove bold (or dim) */
669					tcp->attr_mask &= ~BOLD_ATTR;
670					tcp->cur_attr = mask2attr(tcp);
671					break;
672				case 24: /* remove underline */
673					tcp->attr_mask &= ~UNDERLINE_ATTR;
674					tcp->cur_attr = mask2attr(tcp);
675					break;
676				case 25: /* remove blink */
677					tcp->attr_mask &= ~BLINK_ATTR;
678					tcp->cur_attr = mask2attr(tcp);
679					break;
680				case 27: /* remove reverse */
681					tcp->attr_mask &= ~REVERSE_ATTR;
682					tcp->cur_attr = mask2attr(tcp);
683					break;
684				case 30: case 31: /* set ansi fg color */
685				case 32: case 33: case 34:
686				case 35: case 36: case 37:
687					tcp->attr_mask |= FG_CHANGED;
688					tcp->cur_color.fg = ansi_col[n - 30];
689					tcp->cur_attr = mask2attr(tcp);
690					break;
691				case 39: /* restore fg color back to normal */
692					tcp->attr_mask &= ~(FG_CHANGED|BOLD_ATTR);
693					tcp->cur_color.fg = tcp->std_color.fg;
694					tcp->cur_attr = mask2attr(tcp);
695					break;
696				case 40: case 41: /* set ansi bg color */
697				case 42: case 43: case 44:
698				case 45: case 46: case 47:
699					tcp->attr_mask |= BG_CHANGED;
700					tcp->cur_color.bg = ansi_col[n - 40];
701					tcp->cur_attr = mask2attr(tcp);
702					break;
703				case 49: /* restore bg color back to normal */
704					tcp->attr_mask &= ~BG_CHANGED;
705					tcp->cur_color.bg = tcp->std_color.bg;
706					tcp->cur_attr = mask2attr(tcp);
707					break;
708				}
709			}
710			break;
711
712		case 's':	/* Save cursor position */
713			tcp->saved_xpos = scp->xpos;
714			tcp->saved_ypos = scp->ypos;
715			break;
716
717		case 'u':	/* Restore saved cursor position */
718			if (tcp->saved_xpos >= 0 && tcp->saved_ypos >= 0)
719				sc_move_cursor(scp, tcp->saved_xpos,
720					       tcp->saved_ypos);
721			break;
722
723		case 'x':
724			if (tcp->num_param == 0)
725				n = 0;
726			else
727				n = tcp->param[0];
728			switch (n) {
729			case 0: /* reset colors and attributes back to normal */
730				tcp->attr_mask = NORMAL_ATTR;
731				tcp->cur_color = tcp->std_color
732					       = tcp->dflt_std_color;
733				tcp->rev_color = tcp->dflt_rev_color;
734				tcp->cur_attr = mask2attr(tcp);
735				break;
736			case 1:	/* set ansi background */
737				tcp->attr_mask &= ~BG_CHANGED;
738				tcp->cur_color.bg = tcp->std_color.bg
739						  = ansi_col[tcp->param[1] & 0x0f];
740				tcp->cur_attr = mask2attr(tcp);
741				break;
742			case 2:	/* set ansi foreground */
743				tcp->attr_mask &= ~FG_CHANGED;
744				tcp->cur_color.fg = tcp->std_color.fg
745						  = ansi_col[tcp->param[1] & 0x0f];
746				tcp->cur_attr = mask2attr(tcp);
747				break;
748			case 3: /* set adapter attribute directly */
749				tcp->attr_mask &= ~(FG_CHANGED | BG_CHANGED);
750				tcp->cur_color.fg = tcp->std_color.fg
751						  = tcp->param[1] & 0x0f;
752				tcp->cur_color.bg = tcp->std_color.bg
753						  = (tcp->param[1] >> 4) & 0x0f;
754				tcp->cur_attr = mask2attr(tcp);
755				break;
756			case 5: /* set ansi reverse background */
757				tcp->rev_color.bg = ansi_col[tcp->param[1] & 0x0f];
758				tcp->cur_attr = mask2attr(tcp);
759				break;
760			case 6: /* set ansi reverse foreground */
761				tcp->rev_color.fg = ansi_col[tcp->param[1] & 0x0f];
762				tcp->cur_attr = mask2attr(tcp);
763				break;
764			case 7: /* set adapter reverse attribute directly */
765				tcp->rev_color.fg = tcp->param[1] & 0x0f;
766				tcp->rev_color.bg = (tcp->param[1] >> 4) & 0x0f;
767				tcp->cur_attr = mask2attr(tcp);
768				break;
769			}
770			break;
771
772		case 'z':	/* switch to (virtual) console n */
773			if (tcp->num_param == 1)
774				sc_switch_scr(sc, tcp->param[0]);
775			break;
776		}
777	} else if (tcp->esc == 3) {	/* seen ESC [0-9]+ = */
778		if (c >= '0' && c <= '9') {
779			if (tcp->num_param < MAX_ESC_PAR) {
780				if (tcp->last_param != tcp->num_param) {
781					tcp->last_param = tcp->num_param;
782					tcp->param[tcp->num_param] = 0;
783				} else {
784					tcp->param[tcp->num_param] *= 10;
785				}
786				tcp->param[tcp->num_param] += c - '0';
787				return;
788			}
789		}
790		tcp->num_param = tcp->last_param + 1;
791		switch (c) {
792
793		case ';':
794			if (tcp->num_param < MAX_ESC_PAR)
795				return;
796			break;
797
798		case 'A':   /* set display border color */
799			if (tcp->num_param == 1) {
800				scp->border=tcp->param[0] & 0xff;
801				if (scp == sc->cur_scp)
802					sc_set_border(scp, scp->border);
803			}
804			break;
805
806		case 'B':   /* set bell pitch and duration */
807			if (tcp->num_param == 2) {
808				scp->bell_pitch = tcp->param[0];
809				scp->bell_duration =
810				    (tcp->param[1] * hz + 99) / 100;
811			}
812			break;
813
814		case 'C':   /* set global/parmanent cursor type & shape */
815			i = spltty();
816			n = tcp->num_param;
817			v0 = tcp->param[0];
818			v1 = tcp->param[1];
819			v2 = tcp->param[2];
820			switch (n) {
821			case 1:	/* flags only */
822				if (v0 < sizeof(cattrs)/sizeof(cattrs[0]))
823					v0 = cattrs[v0];
824				else	/* backward compatibility */
825					v0 = cattrs[v0 & 0x3];
826				sc_change_cursor_shape(scp, v0, -1, -1);
827				break;
828			case 2:
829				v2 = 0;
830				v0 &= 0x1f;	/* backward compatibility */
831				v1 &= 0x1f;
832				/* FALL THROUGH */
833			case 3:	/* base and height */
834				if (v2 == 0)	/* count from top */
835					sc_change_cursor_shape(scp, -1,
836					    scp->font_size - v1 - 1,
837					    v1 - v0 + 1);
838				else if (v2 == 1) /* count from bottom */
839					sc_change_cursor_shape(scp, -1,
840					    v0, v1 - v0 + 1);
841				break;
842			}
843			splx(i);
844			break;
845
846		case 'F':   /* set adapter foreground */
847			if (tcp->num_param == 1) {
848				tcp->attr_mask &= ~FG_CHANGED;
849				tcp->cur_color.fg = tcp->std_color.fg
850						  = tcp->param[0] & 0x0f;
851				tcp->cur_attr = mask2attr(tcp);
852			}
853			break;
854
855		case 'G':   /* set adapter background */
856			if (tcp->num_param == 1) {
857				tcp->attr_mask &= ~BG_CHANGED;
858				tcp->cur_color.bg = tcp->std_color.bg
859						  = tcp->param[0] & 0x0f;
860				tcp->cur_attr = mask2attr(tcp);
861			}
862			break;
863
864		case 'H':   /* set adapter reverse foreground */
865			if (tcp->num_param == 1) {
866				tcp->rev_color.fg = tcp->param[0] & 0x0f;
867				tcp->cur_attr = mask2attr(tcp);
868			}
869			break;
870
871		case 'I':   /* set adapter reverse background */
872			if (tcp->num_param == 1) {
873				tcp->rev_color.bg = tcp->param[0] & 0x0f;
874				tcp->cur_attr = mask2attr(tcp);
875			}
876			break;
877
878		case 'S':   /* set local/temporary cursor type & shape */
879			i = spltty();
880			n = tcp->num_param;
881			v0 = tcp->param[0];
882			switch (n) {
883			case 0:
884				v0 = 0;
885				/* FALL THROUGH */
886			case 1:
887				if (v0 < sizeof(tcattrs)/sizeof(tcattrs[0]))
888					sc_change_cursor_shape(scp,
889					    tcattrs[v0], -1, -1);
890				break;
891			}
892			splx(i);
893			break;
894		}
895#ifdef notyet
896	} else if (tcp->esc == 4) {	/* seen ESC Q */
897		/* to be filled */
898#endif
899	} else if (tcp->esc == 5) {	/* seen ESC ( */
900		switch (c) {
901		case 'B':   /* iso-2022: desginate ASCII into G0 */
902			break;
903		/* other items to be filled */
904		default:
905			break;
906		}
907	}
908	tcp->esc = 0;
909}
910
911static void
912scterm_puts(scr_stat *scp, u_char *buf, int len, int kernel)
913{
914	term_stat *tcp;
915	u_char *ptr;
916#ifdef KANJI
917	u_short kanji_code;
918#endif
919	color_t backup;
920
921	tcp = scp->ts;
922	ptr = buf;
923outloop:
924	scp->sc->write_in_progress++;
925	backup = tcp->cur_color;
926	if (kernel) {
927		tcp->cur_color.fg = SC_KERNEL_CONS_ATTR & 0x0f;
928		tcp->cur_color.bg = (SC_KERNEL_CONS_ATTR >> 4) & 0x0f;
929	}
930
931	if (tcp->esc) {
932		scterm_scan_esc(scp, tcp, *ptr++);
933		len--;
934	} else if (PRINTABLE(*ptr)) {     /* Print only printables */
935		vm_offset_t p;
936		u_char *map;
937		int attr;
938		int i;
939		int cnt;
940#ifdef KANJI
941		u_char c;
942#endif
943
944		p = sc_vtb_pointer(&scp->vtb, scp->cursor_pos);
945		map = scp->sc->scr_map;
946		attr = tcp->cur_attr;
947
948#ifdef KANJI
949		c = *ptr;
950		if (tcp->kanji_1st_char == 0) {
951		    tcp->kanji_type = iskanji1(tcp->kanji_type, c);
952		    if (!IS_KTYPE_ASCII_or_HANKAKU(tcp->kanji_type)) {
953			/* not Ascii & not HANKAKU */
954			tcp->kanji_1st_char = c;
955			goto kanji_end;
956		    } else if (tcp->kanji_type == KTYPE_ASCII) {
957			cnt = imin(len, scp->xsize - scp->xpos);
958			i = cnt;
959			do {
960			    p = sc_vtb_putchar(&scp->vtb, p, map[c], attr);
961			    c = *++ptr;
962			    --i;
963			} while (i > 0 && PRINTABLE(c) &&
964				 iskanji1(tcp->kanji_type, c) == KTYPE_ASCII);
965
966			len -= cnt - i;
967			mark_for_update(scp, scp->cursor_pos);
968			scp->cursor_pos += cnt - i;
969			mark_for_update(scp, scp->cursor_pos - 1);
970			scp->xpos += cnt - i;
971			KTYPE_MASK_CTRL(tcp->kanji_type);
972			goto ascii_end;
973		    }
974		} else {
975		    if ((tcp->kanji_type =
976			 iskanji2(tcp->kanji_type, c)) & 0xee) {
977			/* print kanji on TEXT VRAM */
978			kanji_code = kanji_convert(tcp->kanji_type, c,
979						   tcp->kanji_1st_char);
980			mark_for_update(scp, scp->cursor_pos);
981			for (i = 0; i < 2; i++) {
982			    /* *cursor_pos = (kanji_code | (i*0x80)); */
983			    p = sc_vtb_putchar(&scp->vtb, p,
984			       kanji_code | ((i == 0) ? 0x00 : 0x80), attr);
985			    ++scp->cursor_pos;
986			    if (++scp->xpos >= scp->xsize) {
987				scp->xpos = 0;
988				scp->ypos++;
989			    }
990			}
991			mark_for_update(scp, scp->cursor_pos - 1);
992			KTYPE_MASK_CTRL(tcp->kanji_type);
993			tcp->kanji_1st_char = 0;
994			goto kanji_end;
995		    } else {
996			tcp->kanji_1st_char = 0;
997		    }
998		}
999		if (IS_KTYPE_KANA(tcp->kanji_type))
1000		    c |= 0x80;
1001		KTYPE_MASK_CTRL(tcp->kanji_type);
1002		sc_vtb_putchar(&scp->vtb, p, map[c], attr);
1003		mark_for_update(scp, scp->cursor_pos);
1004		mark_for_update(scp, scp->cursor_pos);
1005		++scp->cursor_pos;
1006		++scp->xpos;
1007kanji_end:
1008		++ptr;
1009		--len;
1010ascii_end:
1011#else /* !KANJI */
1012		cnt = imin(len, scp->xsize - scp->xpos);
1013		i = cnt;
1014		do {
1015		    /*
1016		     * gcc-2.6.3 generates poor (un)sign extension code.
1017		     * Casting the pointers in the following to volatile should
1018		     * have no effect, but in fact speeds up this inner loop
1019		     * from 26 to 18 cycles (+ cache misses) on i486's.
1020		     */
1021#define	UCVP(ucp)	((u_char volatile *)(ucp))
1022		    p = sc_vtb_putchar(&scp->vtb, p, UCVP(map)[*UCVP(ptr)],
1023				       attr);
1024		    ++ptr;
1025		    --i;
1026		} while (i > 0 && PRINTABLE(*ptr));
1027
1028		len -= cnt - i;
1029		mark_for_update(scp, scp->cursor_pos);
1030		scp->cursor_pos += cnt - i;
1031		mark_for_update(scp, scp->cursor_pos - 1);
1032		scp->xpos += cnt - i;
1033#endif /* !KANJI */
1034
1035		if (scp->xpos >= scp->xsize) {
1036			scp->xpos = 0;
1037			scp->ypos++;
1038		}
1039	} else {
1040		switch (*ptr) {
1041		case 0x07:
1042			sc_bell(scp, scp->bell_pitch, scp->bell_duration);
1043			break;
1044
1045		case 0x08:	/* non-destructive backspace */
1046			if (scp->cursor_pos > 0) {
1047				mark_for_update(scp, scp->cursor_pos);
1048				scp->cursor_pos--;
1049				mark_for_update(scp, scp->cursor_pos);
1050				if (scp->xpos > 0)
1051					scp->xpos--;
1052				else {
1053					scp->xpos += scp->xsize - 1;
1054					scp->ypos--;
1055				}
1056			}
1057			break;
1058
1059		case 0x09:	/* non-destructive tab */
1060			mark_for_update(scp, scp->cursor_pos);
1061			scp->cursor_pos += (8 - scp->xpos % 8u);
1062			scp->xpos += (8 - scp->xpos % 8u);
1063			if (scp->xpos >= scp->xsize) {
1064				scp->xpos = 0;
1065				scp->ypos++;
1066				scp->cursor_pos = scp->xsize * scp->ypos;
1067			}
1068			mark_for_update(scp, scp->cursor_pos);
1069			break;
1070
1071		case 0x0a:	/* newline, same pos */
1072			mark_for_update(scp, scp->cursor_pos);
1073			scp->cursor_pos += scp->xsize;
1074			mark_for_update(scp, scp->cursor_pos);
1075			scp->ypos++;
1076			break;
1077
1078		case 0x0c:	/* form feed, clears screen */
1079			sc_clear_screen(scp);
1080			break;
1081
1082		case 0x0d:	/* return, return to pos 0 */
1083			mark_for_update(scp, scp->cursor_pos);
1084			scp->cursor_pos -= scp->xpos;
1085			mark_for_update(scp, scp->cursor_pos);
1086			scp->xpos = 0;
1087			break;
1088
1089		case 0x0e:	/* ^N */
1090			tcp->kanji_type = KTYPE_JKANA;
1091			tcp->esc = 0;
1092			tcp->kanji_1st_char = 0;
1093			break;
1094
1095		case 0x0f:	/* ^O */
1096			tcp->kanji_type = KTYPE_ASCII;
1097			tcp->esc = 0;
1098			tcp->kanji_1st_char = 0;
1099			break;
1100
1101		case 0x1b:	/* start escape sequence */
1102			tcp->esc = 1;
1103			tcp->num_param = 0;
1104			break;
1105		}
1106		ptr++;
1107		len--;
1108	}
1109
1110	sc_term_gen_scroll(scp, scp->sc->scr_map[0x20], tcp->cur_attr);
1111
1112	if (kernel)
1113		tcp->cur_color = backup;
1114	scp->sc->write_in_progress--;
1115	if (len)
1116		goto outloop;
1117}
1118
1119static int
1120scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data,
1121	     struct thread *td)
1122{
1123	term_stat *tcp = scp->ts;
1124	vid_info_t *vi;
1125
1126	switch (cmd) {
1127	case GIO_ATTR:      	/* get current attributes */
1128		/* FIXME: */
1129		*(int*)data = (tcp->cur_attr >> 8) & 0xff;
1130		return 0;
1131	case CONS_GETINFO:  	/* get current (virtual) console info */
1132		vi = (vid_info_t *)data;
1133		if (vi->size != sizeof(struct vid_info))
1134			return EINVAL;
1135		vi->mv_norm.fore = tcp->std_color.fg;
1136		vi->mv_norm.back = tcp->std_color.bg;
1137		vi->mv_rev.fore = tcp->rev_color.fg;
1138		vi->mv_rev.back = tcp->rev_color.bg;
1139		/*
1140		 * The other fields are filled by the upper routine. XXX
1141		 */
1142		return ENOIOCTL;
1143	}
1144	return ENOIOCTL;
1145}
1146
1147static int
1148scterm_reset(scr_stat *scp, int code)
1149{
1150	/* FIXME */
1151	return 0;
1152}
1153
1154static void
1155scterm_default_attr(scr_stat *scp, int color, int rev_color)
1156{
1157	term_stat *tcp = scp->ts;
1158
1159	tcp->dflt_std_color.fg = color & 0x0f;
1160	tcp->dflt_std_color.bg = (color >> 4) & 0x0f;
1161	tcp->dflt_rev_color.fg = rev_color & 0x0f;
1162	tcp->dflt_rev_color.bg = (rev_color >> 4) & 0x0f;
1163	tcp->std_color = tcp->dflt_std_color;
1164	tcp->rev_color = tcp->dflt_rev_color;
1165	tcp->cur_color = tcp->std_color;
1166	tcp->cur_attr = mask2attr(tcp);
1167}
1168
1169static void
1170scterm_clear(scr_stat *scp)
1171{
1172	term_stat *tcp = scp->ts;
1173
1174	sc_move_cursor(scp, 0, 0);
1175	sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], tcp->cur_attr);
1176	mark_all(scp);
1177}
1178
1179static void
1180scterm_notify(scr_stat *scp, int event)
1181{
1182	switch (event) {
1183	case SC_TE_NOTIFY_VTSWITCH_IN:
1184		break;
1185	case SC_TE_NOTIFY_VTSWITCH_OUT:
1186		break;
1187	}
1188}
1189
1190static int
1191scterm_input(scr_stat *scp, int c, struct tty *tp)
1192{
1193	return FALSE;
1194}
1195
1196static const char *
1197scterm_fkeystr(scr_stat *scp, int c)
1198{
1199
1200	return (NULL);
1201}
1202
1203/*
1204 * Calculate hardware attributes word using logical attributes mask and
1205 * hardware colors
1206 */
1207
1208/* FIXME */
1209static int
1210mask2attr(term_stat *tcp)
1211{
1212	int attr, mask = tcp->attr_mask;
1213
1214	if (mask & REVERSE_ATTR) {
1215		attr = ((mask & FG_CHANGED) ?
1216			tcp->cur_color.bg : tcp->rev_color.fg) |
1217			(((mask & BG_CHANGED) ?
1218			tcp->cur_color.fg : tcp->rev_color.bg) << 4);
1219	} else
1220		attr = tcp->cur_color.fg | (tcp->cur_color.bg << 4);
1221
1222	/* XXX: underline mapping for Hercules adapter can be better */
1223	if (mask & (BOLD_ATTR | UNDERLINE_ATTR))
1224		attr ^= 0x08;
1225	if (mask & BLINK_ATTR)
1226		attr ^= 0x80;
1227
1228	return (attr << 8);
1229}
1230