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