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