scterm-sck.c revision 145012
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 145012 2005-04-13 13:26:48Z nyan $
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 <dev/syscons/sctermvar.h>
41
42#ifndef SC_DUMB_TERMINAL
43
44#define MAX_ESC_PAR	5
45
46#ifdef KANJI
47#define IS_KTYPE_ASCII_or_HANKAKU(A)	(!((A) & 0xee))
48#define IS_KTYPE_KANA(A)		((A) & 0x11)
49#define KTYPE_MASK_CTRL(A)		((A) &= 0xF0)
50#endif /* KANJI */
51
52/* attribute flags */
53typedef struct {
54	u_short		fg;			/* foreground color */
55	u_short		bg;			/* background color */
56} color_t;
57
58typedef struct {
59	int		flags;
60#define SCTERM_BUSY	(1 << 0)
61	int		esc;
62	int		num_param;
63	int		last_param;
64	int		param[MAX_ESC_PAR];
65	int		saved_xpos;
66	int		saved_ypos;
67
68#ifdef KANJI
69	u_char		kanji_1st_char;
70	u_char		kanji_type;
71#define KTYPE_ASCII	0			/* ASCII */
72#define KTYPE_KANA	1			/* HANKAKU */
73#define KTYPE_JKANA	0x10			/* JIS HANKAKU */
74#define KTYPE_7JIS	0x20			/* JIS */
75#define KTYPE_SJIS	2			/* Shift JIS */
76#define KTYPE_UJIS	4			/* UJIS */
77#define KTYPE_SUKANA	3			/* Shift JIS or UJIS HANKAKU */
78#define KTYPE_SUJIS	6			/* SHift JIS or UJIS */
79#define KTYPE_KANIN	0x80			/* Kanji Invoke sequence */
80#define KTYPE_ASCIN	0x40			/* ASCII Invoke sequence */
81#endif /* KANJI */
82
83	int		attr_mask;		/* current logical attr mask */
84#define NORMAL_ATTR	0x00
85#define BLINK_ATTR	0x01
86#define BOLD_ATTR	0x02
87#define UNDERLINE_ATTR	0x04
88#define REVERSE_ATTR	0x08
89#define FG_CHANGED	0x10
90#define BG_CHANGED	0x20
91	int		cur_attr;		/* current hardware attr word */
92	color_t		cur_color;		/* current hardware color */
93	color_t		std_color;		/* normal hardware color */
94	color_t		rev_color;		/* reverse hardware color */
95	color_t		dflt_std_color;		/* default normal color */
96	color_t		dflt_rev_color;		/* default reverse color */
97} term_stat;
98
99static sc_term_init_t	scterm_init;
100static sc_term_term_t	scterm_term;
101static sc_term_puts_t	scterm_puts;
102static sc_term_ioctl_t	scterm_ioctl;
103static sc_term_reset_t	scterm_reset;
104static sc_term_default_attr_t	scterm_default_attr;
105static sc_term_clear_t	scterm_clear;
106static sc_term_notify_t	scterm_notify;
107static sc_term_input_t	scterm_input;
108
109static sc_term_sw_t sc_term_sc = {
110	{ NULL, NULL },
111	"sck",					/* emulator name */
112	"syscons kanji terminal",		/* description */
113	"*",					/* matching renderer, any :-) */
114	sizeof(term_stat),			/* softc size */
115	0,
116	scterm_init,
117	scterm_term,
118	scterm_puts,
119	scterm_ioctl,
120	scterm_reset,
121	scterm_default_attr,
122	scterm_clear,
123	scterm_notify,
124	scterm_input,
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#if 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#if 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)
913{
914	term_stat *tcp;
915	u_char *ptr;
916#ifdef KANJI
917	u_short kanji_code;
918#endif
919
920	tcp = scp->ts;
921	ptr = buf;
922outloop:
923	scp->sc->write_in_progress++;
924
925	if (tcp->esc) {
926		scterm_scan_esc(scp, tcp, *ptr++);
927		len--;
928	} else if (PRINTABLE(*ptr)) {     /* Print only printables */
929		vm_offset_t p;
930		u_char *map;
931		int attr;
932		int i;
933		int cnt;
934#ifdef KANJI
935		u_char c;
936#endif
937
938		p = sc_vtb_pointer(&scp->vtb, scp->cursor_pos);
939		map = scp->sc->scr_map;
940		attr = tcp->cur_attr;
941
942#ifdef KANJI
943		c = *ptr;
944		if (tcp->kanji_1st_char == 0) {
945		    tcp->kanji_type = iskanji1(tcp->kanji_type, c);
946		    if (!IS_KTYPE_ASCII_or_HANKAKU(tcp->kanji_type)) {
947			/* not Ascii & not HANKAKU */
948			tcp->kanji_1st_char = c;
949			goto kanji_end;
950		    } else if (tcp->kanji_type == KTYPE_ASCII) {
951			cnt = imin(len, scp->xsize - scp->xpos);
952			i = cnt;
953			do {
954			    p = sc_vtb_putchar(&scp->vtb, p, map[c], attr);
955			    c = *++ptr;
956			    --i;
957			} while (i > 0 && PRINTABLE(c) &&
958				 iskanji1(tcp->kanji_type, c) == KTYPE_ASCII);
959
960			len -= cnt - i;
961			mark_for_update(scp, scp->cursor_pos);
962			scp->cursor_pos += cnt - i;
963			mark_for_update(scp, scp->cursor_pos - 1);
964			scp->xpos += cnt - i;
965			KTYPE_MASK_CTRL(tcp->kanji_type);
966			goto ascii_end;
967		    }
968		} else {
969		    if ((tcp->kanji_type =
970			 iskanji2(tcp->kanji_type, c)) & 0xee) {
971			/* print kanji on TEXT VRAM */
972			kanji_code = kanji_convert(tcp->kanji_type, c,
973						   tcp->kanji_1st_char);
974			mark_for_update(scp, scp->cursor_pos);
975			for (i = 0; i < 2; i++) {
976			    /* *cursor_pos = (kanji_code | (i*0x80)); */
977			    p = sc_vtb_putchar(&scp->vtb, p,
978			       kanji_code | ((i == 0) ? 0x00 : 0x80), attr);
979			    ++scp->cursor_pos;
980			    if (++scp->xpos >= scp->xsize) {
981				scp->xpos = 0;
982				scp->ypos++;
983			    }
984			}
985			mark_for_update(scp, scp->cursor_pos - 1);
986			KTYPE_MASK_CTRL(tcp->kanji_type);
987			tcp->kanji_1st_char = 0;
988			goto kanji_end;
989		    } else {
990			tcp->kanji_1st_char = 0;
991		    }
992		}
993		if (IS_KTYPE_KANA(tcp->kanji_type))
994		    c |= 0x80;
995		KTYPE_MASK_CTRL(tcp->kanji_type);
996		sc_vtb_putchar(&scp->vtb, p, map[c], attr);
997		mark_for_update(scp, scp->cursor_pos);
998		mark_for_update(scp, scp->cursor_pos);
999		++scp->cursor_pos;
1000		++scp->xpos;
1001kanji_end:
1002		++ptr;
1003		--len;
1004ascii_end:
1005#else /* !KANJI */
1006		cnt = imin(len, scp->xsize - scp->xpos);
1007		i = cnt;
1008		do {
1009		    /*
1010		     * gcc-2.6.3 generates poor (un)sign extension code.
1011		     * Casting the pointers in the following to volatile should
1012		     * have no effect, but in fact speeds up this inner loop
1013		     * from 26 to 18 cycles (+ cache misses) on i486's.
1014		     */
1015#define	UCVP(ucp)	((u_char volatile *)(ucp))
1016		    p = sc_vtb_putchar(&scp->vtb, p, UCVP(map)[*UCVP(ptr)],
1017				       attr);
1018		    ++ptr;
1019		    --i;
1020		} while (i > 0 && PRINTABLE(*ptr));
1021
1022		len -= cnt - i;
1023		mark_for_update(scp, scp->cursor_pos);
1024		scp->cursor_pos += cnt - i;
1025		mark_for_update(scp, scp->cursor_pos - 1);
1026		scp->xpos += cnt - i;
1027#endif /* !KANJI */
1028
1029		if (scp->xpos >= scp->xsize) {
1030			scp->xpos = 0;
1031			scp->ypos++;
1032		}
1033	} else {
1034		switch (*ptr) {
1035		case 0x07:
1036			sc_bell(scp, scp->bell_pitch, scp->bell_duration);
1037			break;
1038
1039		case 0x08:	/* non-destructive backspace */
1040			if (scp->cursor_pos > 0) {
1041				mark_for_update(scp, scp->cursor_pos);
1042				scp->cursor_pos--;
1043				mark_for_update(scp, scp->cursor_pos);
1044				if (scp->xpos > 0)
1045					scp->xpos--;
1046				else {
1047					scp->xpos += scp->xsize - 1;
1048					scp->ypos--;
1049				}
1050			}
1051			break;
1052
1053		case 0x09:	/* non-destructive tab */
1054			mark_for_update(scp, scp->cursor_pos);
1055			scp->cursor_pos += (8 - scp->xpos % 8u);
1056			scp->xpos += (8 - scp->xpos % 8u);
1057			if (scp->xpos >= scp->xsize) {
1058				scp->xpos = 0;
1059				scp->ypos++;
1060				scp->cursor_pos = scp->xsize * scp->ypos;
1061			}
1062			mark_for_update(scp, scp->cursor_pos);
1063			break;
1064
1065		case 0x0a:	/* newline, same pos */
1066			mark_for_update(scp, scp->cursor_pos);
1067			scp->cursor_pos += scp->xsize;
1068			mark_for_update(scp, scp->cursor_pos);
1069			scp->ypos++;
1070			break;
1071
1072		case 0x0c:	/* form feed, clears screen */
1073			sc_clear_screen(scp);
1074			break;
1075
1076		case 0x0d:	/* return, return to pos 0 */
1077			mark_for_update(scp, scp->cursor_pos);
1078			scp->cursor_pos -= scp->xpos;
1079			mark_for_update(scp, scp->cursor_pos);
1080			scp->xpos = 0;
1081			break;
1082
1083		case 0x0e:	/* ^N */
1084			tcp->kanji_type = KTYPE_JKANA;
1085			tcp->esc = 0;
1086			tcp->kanji_1st_char = 0;
1087			break;
1088
1089		case 0x0f:	/* ^O */
1090			tcp->kanji_type = KTYPE_ASCII;
1091			tcp->esc = 0;
1092			tcp->kanji_1st_char = 0;
1093			break;
1094
1095		case 0x1b:	/* start escape sequence */
1096			tcp->esc = 1;
1097			tcp->num_param = 0;
1098			break;
1099		}
1100		ptr++;
1101		len--;
1102	}
1103
1104	sc_term_gen_scroll(scp, scp->sc->scr_map[0x20], tcp->cur_attr);
1105
1106	scp->sc->write_in_progress--;
1107	if (len)
1108		goto outloop;
1109}
1110
1111static int
1112scterm_ioctl(scr_stat *scp, struct tty *tp, u_long cmd, caddr_t data,
1113	     int flag, struct thread *td)
1114{
1115	term_stat *tcp = scp->ts;
1116	vid_info_t *vi;
1117
1118	switch (cmd) {
1119	case GIO_ATTR:      	/* get current attributes */
1120		/* FIXME: */
1121		*(int*)data = (tcp->cur_attr >> 8) & 0xff;
1122		return 0;
1123	case CONS_GETINFO:  	/* get current (virtual) console info */
1124		vi = (vid_info_t *)data;
1125		if (vi->size != sizeof(struct vid_info))
1126			return EINVAL;
1127		vi->mv_norm.fore = tcp->std_color.fg;
1128		vi->mv_norm.back = tcp->std_color.bg;
1129		vi->mv_rev.fore = tcp->rev_color.fg;
1130		vi->mv_rev.back = tcp->rev_color.bg;
1131		/*
1132		 * The other fields are filled by the upper routine. XXX
1133		 */
1134		return ENOIOCTL;
1135	}
1136	return ENOIOCTL;
1137}
1138
1139static int
1140scterm_reset(scr_stat *scp, int code)
1141{
1142	/* FIXME */
1143	return 0;
1144}
1145
1146static void
1147scterm_default_attr(scr_stat *scp, int color, int rev_color)
1148{
1149	term_stat *tcp = scp->ts;
1150
1151	tcp->dflt_std_color.fg = color & 0x0f;
1152	tcp->dflt_std_color.bg = (color >> 4) & 0x0f;
1153	tcp->dflt_rev_color.fg = rev_color & 0x0f;
1154	tcp->dflt_rev_color.bg = (rev_color >> 4) & 0x0f;
1155	tcp->std_color = tcp->dflt_std_color;
1156	tcp->rev_color = tcp->dflt_rev_color;
1157	tcp->cur_color = tcp->std_color;
1158	tcp->cur_attr = mask2attr(tcp);
1159}
1160
1161static void
1162scterm_clear(scr_stat *scp)
1163{
1164	term_stat *tcp = scp->ts;
1165
1166	sc_move_cursor(scp, 0, 0);
1167	sc_vtb_clear(&scp->vtb, scp->sc->scr_map[0x20], tcp->cur_attr);
1168	mark_all(scp);
1169}
1170
1171static void
1172scterm_notify(scr_stat *scp, int event)
1173{
1174	switch (event) {
1175	case SC_TE_NOTIFY_VTSWITCH_IN:
1176		break;
1177	case SC_TE_NOTIFY_VTSWITCH_OUT:
1178		break;
1179	}
1180}
1181
1182static int
1183scterm_input(scr_stat *scp, int c, struct tty *tp)
1184{
1185	return FALSE;
1186}
1187
1188/*
1189 * Calculate hardware attributes word using logical attributes mask and
1190 * hardware colors
1191 */
1192
1193/* FIXME */
1194static int
1195mask2attr(term_stat *tcp)
1196{
1197	int attr, mask = tcp->attr_mask;
1198
1199	if (mask & REVERSE_ATTR) {
1200		attr = ((mask & FG_CHANGED) ?
1201			tcp->cur_color.bg : tcp->rev_color.fg) |
1202			(((mask & BG_CHANGED) ?
1203			tcp->cur_color.fg : tcp->rev_color.bg) << 4);
1204	} else
1205		attr = tcp->cur_color.fg | (tcp->cur_color.bg << 4);
1206
1207	/* XXX: underline mapping for Hercules adapter can be better */
1208	if (mask & (BOLD_ATTR | UNDERLINE_ATTR))
1209		attr ^= 0x08;
1210	if (mask & BLINK_ATTR)
1211		attr ^= 0x80;
1212
1213	return (attr << 8);
1214}
1215
1216#endif /* SC_DUMB_TERMINAL */
1217