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