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