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