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