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