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