wsemul_vt100.c revision 1.25
1/* $NetBSD: wsemul_vt100.c,v 1.25 2004/03/24 17:26:53 drochner Exp $ */
2
3/*
4 * Copyright (c) 1998
5 *	Matthias Drochner.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: wsemul_vt100.c,v 1.25 2004/03/24 17:26:53 drochner Exp $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/time.h>
35#include <sys/malloc.h>
36#include <sys/fcntl.h>
37
38#include <dev/wscons/wsconsio.h>
39#include <dev/wscons/wsdisplayvar.h>
40#include <dev/wscons/wsemulvar.h>
41#include <dev/wscons/wsemul_vt100var.h>
42#include <dev/wscons/ascii.h>
43
44#include "opt_wskernattr.h"
45
46void	*wsemul_vt100_cnattach(const struct wsscreen_descr *, void *,
47			       int, int, long);
48void	*wsemul_vt100_attach(int console, const struct wsscreen_descr *,
49			     void *, int, int, void *, long);
50void	wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int);
51void	wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp);
52void	wsemul_vt100_resetop(void *, enum wsemul_resetops);
53
54const struct wsemul_ops wsemul_vt100_ops = {
55	"vt100",
56	wsemul_vt100_cnattach,
57	wsemul_vt100_attach,
58	wsemul_vt100_output,
59	wsemul_vt100_translate,
60	wsemul_vt100_detach,
61	wsemul_vt100_resetop
62};
63
64struct wsemul_vt100_emuldata wsemul_vt100_console_emuldata;
65
66static void wsemul_vt100_init(struct wsemul_vt100_emuldata *,
67			      const struct wsscreen_descr *,
68			      void *, int, int, long);
69
70static void wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *,
71				       u_char, int);
72static void wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *,
73				     u_char, int);
74static void wsemul_vt100_nextline(struct wsemul_vt100_emuldata *);
75typedef u_int vt100_handler(struct wsemul_vt100_emuldata *, u_char);
76
77static vt100_handler
78wsemul_vt100_output_esc,
79wsemul_vt100_output_csi,
80wsemul_vt100_output_scs94,
81wsemul_vt100_output_scs94_percent,
82wsemul_vt100_output_scs96,
83wsemul_vt100_output_scs96_percent,
84wsemul_vt100_output_esc_hash,
85wsemul_vt100_output_esc_spc,
86wsemul_vt100_output_string,
87wsemul_vt100_output_string_esc,
88wsemul_vt100_output_dcs,
89wsemul_vt100_output_dcs_dollar;
90
91#define	VT100_EMUL_STATE_NORMAL		0	/* normal processing */
92#define	VT100_EMUL_STATE_ESC		1	/* got ESC */
93#define	VT100_EMUL_STATE_CSI		2	/* got CSI (ESC[) */
94#define	VT100_EMUL_STATE_SCS94		3	/* got ESC{()*+} */
95#define	VT100_EMUL_STATE_SCS94_PERCENT	4	/* got ESC{()*+}% */
96#define	VT100_EMUL_STATE_SCS96		5	/* got ESC{-./} */
97#define	VT100_EMUL_STATE_SCS96_PERCENT	6	/* got ESC{-./}% */
98#define	VT100_EMUL_STATE_ESC_HASH	7	/* got ESC# */
99#define	VT100_EMUL_STATE_ESC_SPC	8	/* got ESC<SPC> */
100#define	VT100_EMUL_STATE_STRING		9	/* waiting for ST (ESC\) */
101#define	VT100_EMUL_STATE_STRING_ESC	10	/* waiting for ST, got ESC */
102#define	VT100_EMUL_STATE_DCS		11	/* got DCS (ESC P) */
103#define	VT100_EMUL_STATE_DCS_DOLLAR	12	/* got DCS<p>$ */
104
105vt100_handler *vt100_output[] = {
106	wsemul_vt100_output_esc,
107	wsemul_vt100_output_csi,
108	wsemul_vt100_output_scs94,
109	wsemul_vt100_output_scs94_percent,
110	wsemul_vt100_output_scs96,
111	wsemul_vt100_output_scs96_percent,
112	wsemul_vt100_output_esc_hash,
113	wsemul_vt100_output_esc_spc,
114	wsemul_vt100_output_string,
115	wsemul_vt100_output_string_esc,
116	wsemul_vt100_output_dcs,
117	wsemul_vt100_output_dcs_dollar,
118};
119
120static void
121wsemul_vt100_init(struct wsemul_vt100_emuldata *edp,
122	const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
123	long defattr)
124{
125	edp->emulops = type->textops;
126	edp->emulcookie = cookie;
127	edp->scrcapabilities = type->capabilities;
128	edp->nrows = type->nrows;
129	edp->ncols = type->ncols;
130	edp->crow = crow;
131	edp->ccol = ccol;
132	edp->defattr = defattr;
133}
134
135void *
136wsemul_vt100_cnattach(const struct wsscreen_descr *type, void *cookie,
137	int ccol, int crow, long defattr)
138{
139	struct wsemul_vt100_emuldata *edp;
140#if defined(WS_KERNEL_FG) || defined(WS_KERNEL_BG) || \
141  defined(WS_KERNEL_COLATTR) || defined(WS_KERNEL_MONOATTR)
142	int res;
143#endif
144
145	edp = &wsemul_vt100_console_emuldata;
146	wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
147#ifdef DIAGNOSTIC
148	edp->console = 1;
149#endif
150	edp->cbcookie = NULL;
151
152#if defined(WS_KERNEL_FG) || defined(WS_KERNEL_BG) || \
153  defined(WS_KERNEL_COLATTR) || defined(WS_KERNEL_MONOATTR)
154#ifndef WS_KERNEL_FG
155#define WS_KERNEL_FG WSCOL_WHITE
156#endif
157#ifndef WS_KERNEL_BG
158#define WS_KERNEL_BG WSCOL_BLACK
159#endif
160#ifndef WS_KERNEL_COLATTR
161#define WS_KERNEL_COLATTR 0
162#endif
163#ifndef WS_KERNEL_MONOATTR
164#define WS_KERNEL_MONOATTR 0
165#endif
166	if (type->capabilities & WSSCREEN_WSCOLORS)
167		res = (*edp->emulops->allocattr)(cookie,
168					    WS_KERNEL_FG, WS_KERNEL_BG,
169					    WS_KERNEL_COLATTR | WSATTR_WSCOLORS,
170					    &edp->kernattr);
171	else
172		res = (*edp->emulops->allocattr)(cookie, 0, 0,
173					    WS_KERNEL_MONOATTR,
174					    &edp->kernattr);
175	if (res)
176#endif
177	edp->kernattr = defattr;
178
179	edp->tabs = 0;
180	edp->dblwid = 0;
181	edp->dw = 0;
182	edp->dcsarg = 0;
183	edp->isolatin1tab = edp->decgraphtab = edp->dectechtab = 0;
184	edp->nrctab = 0;
185	wsemul_vt100_reset(edp);
186	return (edp);
187}
188
189void *
190wsemul_vt100_attach(int console, const struct wsscreen_descr *type,
191	void *cookie, int ccol, int crow, void *cbcookie, long defattr)
192{
193	struct wsemul_vt100_emuldata *edp;
194
195	if (console) {
196		edp = &wsemul_vt100_console_emuldata;
197#ifdef DIAGNOSTIC
198		KASSERT(edp->console == 1);
199#endif
200	} else {
201		edp = malloc(sizeof *edp, M_DEVBUF, M_WAITOK);
202		wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
203#ifdef DIAGNOSTIC
204		edp->console = 0;
205#endif
206	}
207	edp->cbcookie = cbcookie;
208
209	edp->tabs = malloc(edp->ncols, M_DEVBUF, M_NOWAIT);
210	edp->dblwid = malloc(edp->nrows, M_DEVBUF, M_NOWAIT|M_ZERO);
211	edp->dw = 0;
212	edp->dcsarg = malloc(DCS_MAXLEN, M_DEVBUF, M_NOWAIT);
213	edp->isolatin1tab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
214	edp->decgraphtab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
215	edp->dectechtab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
216	edp->nrctab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
217	vt100_initchartables(edp);
218	wsemul_vt100_reset(edp);
219	return (edp);
220}
221
222void
223wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp)
224{
225	struct wsemul_vt100_emuldata *edp = cookie;
226
227	*crowp = edp->crow;
228	*ccolp = edp->ccol;
229#define f(ptr) if (ptr) {free(ptr, M_DEVBUF); ptr = 0;}
230	f(edp->tabs)
231	f(edp->dblwid)
232	f(edp->dcsarg)
233	f(edp->isolatin1tab)
234	f(edp->decgraphtab)
235	f(edp->dectechtab)
236	f(edp->nrctab)
237#undef f
238	if (edp != &wsemul_vt100_console_emuldata)
239		free(edp, M_DEVBUF);
240}
241
242void
243wsemul_vt100_resetop(void *cookie, enum wsemul_resetops op)
244{
245	struct wsemul_vt100_emuldata *edp = cookie;
246
247	switch (op) {
248	case WSEMUL_RESET:
249		wsemul_vt100_reset(edp);
250		break;
251	case WSEMUL_SYNCFONT:
252		vt100_initchartables(edp);
253		break;
254	case WSEMUL_CLEARSCREEN:
255		wsemul_vt100_ed(edp, 2);
256		edp->ccol = edp->crow = 0;
257		(*edp->emulops->cursor)(edp->emulcookie,
258					edp->flags & VTFL_CURSORON, 0, 0);
259		break;
260	default:
261		break;
262	}
263}
264
265void
266wsemul_vt100_reset(struct wsemul_vt100_emuldata *edp)
267{
268	int i;
269
270	edp->state = VT100_EMUL_STATE_NORMAL;
271	edp->flags = VTFL_DECAWM | VTFL_CURSORON;
272	edp->bkgdattr = edp->curattr = edp->defattr;
273	edp->attrflags = 0;
274	edp->fgcol = WSCOL_WHITE;
275	edp->bgcol = WSCOL_BLACK;
276	edp->scrreg_startrow = 0;
277	edp->scrreg_nrows = edp->nrows;
278	if (edp->tabs) {
279		memset(edp->tabs, 0, edp->ncols);
280		for (i = 8; i < edp->ncols; i += 8)
281			edp->tabs[i] = 1;
282	}
283	edp->dcspos = 0;
284	edp->dcstype = 0;
285	edp->chartab_G[0] = 0;
286	edp->chartab_G[1] = edp->nrctab; /* ??? */
287	edp->chartab_G[2] = edp->isolatin1tab;
288	edp->chartab_G[3] = edp->isolatin1tab;
289	edp->chartab0 = 0;
290	edp->chartab1 = 2;
291	edp->sschartab = 0;
292}
293
294/*
295 * now all the state machine bits
296 */
297
298/*
299 * Move the cursor to the next line if possible. If the cursor is at
300 * the bottom of the scroll area, then scroll it up. If the cursor is
301 * at the bottom of the screen then don't move it down.
302 */
303static void
304wsemul_vt100_nextline(struct wsemul_vt100_emuldata *edp)
305{
306	if (ROWS_BELOW == 0) {
307		/* Bottom of the scroll region. */
308	  	wsemul_vt100_scrollup(edp, 1);
309	} else {
310		if ((edp->crow+1) < edp->nrows)
311			/* Cursor not at the bottom of the screen. */
312			edp->crow++;
313		CHECK_DW;
314	}
315}
316
317static void
318wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *edp, u_char c,
319	int kernel)
320{
321	u_int *ct, dc;
322
323	if ((edp->flags & (VTFL_LASTCHAR | VTFL_DECAWM)) ==
324	    (VTFL_LASTCHAR | VTFL_DECAWM)) {
325		wsemul_vt100_nextline(edp);
326		edp->ccol = 0;
327		edp->flags &= ~VTFL_LASTCHAR;
328	}
329
330	if (c & 0x80) {
331		c &= 0x7f;
332		ct = edp->chartab_G[edp->chartab1];
333	} else {
334		if (edp->sschartab) {
335			ct = edp->chartab_G[edp->sschartab];
336			edp->sschartab = 0;
337		} else
338			ct = edp->chartab_G[edp->chartab0];
339	}
340	dc = (ct ? ct[c] : c);
341
342	if ((edp->flags & VTFL_INSERTMODE) && COLS_LEFT)
343		COPYCOLS(edp->ccol, edp->ccol + 1, COLS_LEFT);
344
345	(*edp->emulops->putchar)(edp->emulcookie, edp->crow,
346				 edp->ccol << edp->dw, dc,
347				 kernel ? edp->kernattr : edp->curattr);
348
349	if (COLS_LEFT)
350		edp->ccol++;
351	else
352		edp->flags |= VTFL_LASTCHAR;
353}
354
355static void
356wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *edp, u_char c,
357	int kernel)
358{
359	u_int n;
360
361	switch (c) {
362	case ASCII_NUL:
363	default:
364		/* ignore */
365		break;
366	case ASCII_BEL:
367		wsdisplay_emulbell(edp->cbcookie);
368		break;
369	case ASCII_BS:
370		if (edp->ccol > 0) {
371			edp->ccol--;
372			edp->flags &= ~VTFL_LASTCHAR;
373		}
374		break;
375	case ASCII_CR:
376		edp->ccol = 0;
377		edp->flags &= ~VTFL_LASTCHAR;
378		break;
379	case ASCII_HT:
380		if (edp->tabs) {
381			if (!COLS_LEFT)
382				break;
383			for (n = edp->ccol + 1; n < NCOLS - 1; n++)
384				if (edp->tabs[n])
385					break;
386		} else {
387			n = edp->ccol + min(8 - (edp->ccol & 7), COLS_LEFT);
388		}
389		edp->ccol = n;
390		break;
391	case ASCII_SO: /* LS1 */
392		edp->chartab0 = 1;
393		break;
394	case ASCII_SI: /* LS0 */
395		edp->chartab0 = 0;
396		break;
397	case ASCII_ESC:
398		if (kernel) {
399			printf("wsemul_vt100_output_c0c1: ESC in kernel output ignored\n");
400			break;	/* ignore the ESC */
401		}
402
403		if (edp->state == VT100_EMUL_STATE_STRING) {
404			/* might be a string end */
405			edp->state = VT100_EMUL_STATE_STRING_ESC;
406		} else {
407			/* XXX cancel current escape sequence */
408			edp->state = VT100_EMUL_STATE_ESC;
409		}
410		break;
411#if 0
412	case CSI: /* 8-bit */
413		/* XXX cancel current escape sequence */
414		edp->nargs = 0;
415		memset(edp->args, 0, sizeof (edp->args));
416		edp->modif1 = edp->modif2 = '\0';
417		edp->state = VT100_EMUL_STATE_CSI;
418		break;
419	case DCS: /* 8-bit */
420		/* XXX cancel current escape sequence */
421		edp->nargs = 0;
422		memset(edp->args, 0, sizeof (edp->args));
423		edp->state = VT100_EMUL_STATE_DCS;
424		break;
425	case ST: /* string end 8-bit */
426		/* XXX only in VT100_EMUL_STATE_STRING */
427		wsemul_vt100_handle_dcs(edp);
428		return (VT100_EMUL_STATE_NORMAL);
429#endif
430	case ASCII_LF:
431	case ASCII_VT:
432	case ASCII_FF:
433		wsemul_vt100_nextline(edp);
434		break;
435	}
436}
437
438static u_int
439wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
440{
441	u_int newstate = VT100_EMUL_STATE_NORMAL;
442	int i;
443
444	switch (c) {
445	case '[': /* CSI */
446		edp->nargs = 0;
447		memset(edp->args, 0, sizeof (edp->args));
448		edp->modif1 = edp->modif2 = '\0';
449		newstate = VT100_EMUL_STATE_CSI;
450		break;
451	case '7': /* DECSC */
452		edp->flags |= VTFL_SAVEDCURS;
453		edp->savedcursor_row = edp->crow;
454		edp->savedcursor_col = edp->ccol;
455		edp->savedattr = edp->curattr;
456		edp->savedbkgdattr = edp->bkgdattr;
457		edp->savedattrflags = edp->attrflags;
458		edp->savedfgcol = edp->fgcol;
459		edp->savedbgcol = edp->bgcol;
460		for (i = 0; i < 4; i++)
461			edp->savedchartab_G[i] = edp->chartab_G[i];
462		edp->savedchartab0 = edp->chartab0;
463		edp->savedchartab1 = edp->chartab1;
464		break;
465	case '8': /* DECRC */
466		if ((edp->flags & VTFL_SAVEDCURS) == 0)
467			break;
468		edp->crow = edp->savedcursor_row;
469		edp->ccol = edp->savedcursor_col;
470		edp->curattr = edp->savedattr;
471		edp->bkgdattr = edp->savedbkgdattr;
472		edp->attrflags = edp->savedattrflags;
473		edp->fgcol = edp->savedfgcol;
474		edp->bgcol = edp->savedbgcol;
475		for (i = 0; i < 4; i++)
476			edp->chartab_G[i] = edp->savedchartab_G[i];
477		edp->chartab0 = edp->savedchartab0;
478		edp->chartab1 = edp->savedchartab1;
479		break;
480	case '=': /* DECKPAM application mode */
481		edp->flags |= VTFL_APPLKEYPAD;
482		break;
483	case '>': /* DECKPNM numeric mode */
484		edp->flags &= ~VTFL_APPLKEYPAD;
485		break;
486	case 'E': /* NEL */
487		edp->ccol = 0;
488		/* FALLTHRU */
489	case 'D': /* IND */
490		wsemul_vt100_nextline(edp);
491		break;
492	case 'H': /* HTS */
493		KASSERT(edp->tabs != 0);
494		edp->tabs[edp->ccol] = 1;
495		break;
496	case '~': /* LS1R */
497		edp->chartab1 = 1;
498		break;
499	case 'n': /* LS2 */
500		edp->chartab0 = 2;
501		break;
502	case '}': /* LS2R */
503		edp->chartab1 = 2;
504		break;
505	case 'o': /* LS3 */
506		edp->chartab0 = 3;
507		break;
508	case '|': /* LS3R */
509		edp->chartab1 = 3;
510		break;
511	case 'N': /* SS2 */
512		edp->sschartab = 2;
513		break;
514	case 'O': /* SS3 */
515		edp->sschartab = 3;
516		break;
517	case 'M': /* RI */
518		if (ROWS_ABOVE > 0) {
519			edp->crow--;
520			CHECK_DW;
521			break;
522		}
523		wsemul_vt100_scrolldown(edp, 1);
524		break;
525	case 'P': /* DCS */
526		edp->nargs = 0;
527		memset(edp->args, 0, sizeof (edp->args));
528		newstate = VT100_EMUL_STATE_DCS;
529		break;
530	case 'c': /* RIS */
531		wsemul_vt100_reset(edp);
532		wsemul_vt100_ed(edp, 2);
533		edp->ccol = edp->crow = 0;
534		break;
535	case '(': case ')': case '*': case '+': /* SCS */
536		edp->designating = c - '(';
537		newstate = VT100_EMUL_STATE_SCS94;
538		break;
539	case '-': case '.': case '/': /* SCS */
540		edp->designating = c - '-' + 1;
541		newstate = VT100_EMUL_STATE_SCS96;
542		break;
543	case '#':
544		newstate = VT100_EMUL_STATE_ESC_HASH;
545		break;
546	case ' ': /* 7/8 bit */
547		newstate = VT100_EMUL_STATE_ESC_SPC;
548		break;
549	case ']': /* OSC operating system command */
550	case '^': /* PM privacy message */
551	case '_': /* APC application program command */
552		/* ignored */
553		newstate = VT100_EMUL_STATE_STRING;
554		break;
555	case '<': /* exit VT52 mode - ignored */
556		break;
557	default:
558#ifdef VT100_PRINTUNKNOWN
559		printf("ESC%c unknown\n", c);
560#endif
561		break;
562	}
563
564	return (newstate);
565}
566
567static u_int
568wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, u_char c)
569{
570	u_int newstate = VT100_EMUL_STATE_NORMAL;
571
572	switch (c) {
573	case '%': /* probably DEC supplemental graphic */
574		newstate = VT100_EMUL_STATE_SCS94_PERCENT;
575		break;
576	case 'A': /* british / national */
577		edp->chartab_G[edp->designating] = edp->nrctab;
578		break;
579	case 'B': /* ASCII */
580		edp->chartab_G[edp->designating] = 0;
581		break;
582	case '<': /* user preferred supplemental */
583		/* XXX not really "user" preferred */
584		edp->chartab_G[edp->designating] = edp->isolatin1tab;
585		break;
586	case '0': /* DEC special graphic */
587		edp->chartab_G[edp->designating] = edp->decgraphtab;
588		break;
589	case '>': /* DEC tech */
590		edp->chartab_G[edp->designating] = edp->dectechtab;
591		break;
592	default:
593#ifdef VT100_PRINTUNKNOWN
594		printf("ESC%c%c unknown\n", edp->designating + '(', c);
595#endif
596		break;
597	}
598	return (newstate);
599}
600
601static u_int
602wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, u_char c)
603{
604	switch (c) {
605	case '5': /* DEC supplemental graphic */
606		/* XXX there are differences */
607		edp->chartab_G[edp->designating] = edp->isolatin1tab;
608		break;
609	default:
610#ifdef VT100_PRINTUNKNOWN
611		printf("ESC%c%%%c unknown\n", edp->designating + '(', c);
612#endif
613		break;
614	}
615	return (VT100_EMUL_STATE_NORMAL);
616}
617
618static u_int
619wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp, u_char c)
620{
621	u_int newstate = VT100_EMUL_STATE_NORMAL;
622	int nrc;
623
624	switch (c) {
625	case '%': /* probably portugese */
626		newstate = VT100_EMUL_STATE_SCS96_PERCENT;
627		break;
628	case 'A': /* ISO-latin-1 supplemental */
629		edp->chartab_G[edp->designating] = edp->isolatin1tab;
630		break;
631	case '4': /* dutch */
632		nrc = 1;
633		goto setnrc;
634	case '5': case 'C': /* finnish */
635		nrc = 2;
636		goto setnrc;
637	case 'R': /* french */
638		nrc = 3;
639		goto setnrc;
640	case 'Q': /* french canadian */
641		nrc = 4;
642		goto setnrc;
643	case 'K': /* german */
644		nrc = 5;
645		goto setnrc;
646	case 'Y': /* italian */
647		nrc = 6;
648		goto setnrc;
649	case 'E': case '6': /* norwegian / danish */
650		nrc = 7;
651		goto setnrc;
652	case 'Z': /* spanish */
653		nrc = 9;
654		goto setnrc;
655	case '7': case 'H': /* swedish */
656		nrc = 10;
657		goto setnrc;
658	case '=': /* swiss */
659		nrc = 11;
660setnrc:
661		vt100_setnrc(edp, nrc); /* what table ??? */
662		break;
663	default:
664#ifdef VT100_PRINTUNKNOWN
665		printf("ESC%c%c unknown\n", edp->designating + '-' - 1, c);
666#endif
667		break;
668	}
669	return (newstate);
670}
671
672static u_int
673wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, u_char c)
674{
675	switch (c) {
676	case '6': /* portugese */
677		vt100_setnrc(edp, 8);
678		break;
679	default:
680#ifdef VT100_PRINTUNKNOWN
681		printf("ESC%c%%%c unknown\n", edp->designating + '-', c);
682#endif
683		break;
684	}
685	return (VT100_EMUL_STATE_NORMAL);
686}
687
688static u_int
689wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp, u_char c)
690{
691	switch (c) {
692	case 'F': /* 7-bit controls */
693	case 'G': /* 8-bit controls */
694#ifdef VT100_PRINTNOTIMPL
695		printf("ESC<SPC>%c ignored\n", c);
696#endif
697		break;
698	default:
699#ifdef VT100_PRINTUNKNOWN
700		printf("ESC<SPC>%c unknown\n", c);
701#endif
702		break;
703	}
704	return (VT100_EMUL_STATE_NORMAL);
705}
706
707static u_int
708wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp, u_char c)
709{
710	if (edp->dcstype && edp->dcspos < DCS_MAXLEN)
711		edp->dcsarg[edp->dcspos++] = c;
712	return (VT100_EMUL_STATE_STRING);
713}
714
715static u_int
716wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, u_char c)
717{
718	if (c == '\\') { /* ST complete */
719		wsemul_vt100_handle_dcs(edp);
720		return (VT100_EMUL_STATE_NORMAL);
721	} else
722		return (VT100_EMUL_STATE_STRING);
723}
724
725static u_int
726wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, u_char c)
727{
728	u_int newstate = VT100_EMUL_STATE_DCS;
729
730	switch (c) {
731	case '0': case '1': case '2': case '3': case '4':
732	case '5': case '6': case '7': case '8': case '9':
733		/* argument digit */
734		if (edp->nargs > VT100_EMUL_NARGS - 1)
735			break;
736		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
737		    (c - '0');
738		break;
739	case ';': /* argument terminator */
740		edp->nargs++;
741		break;
742	default:
743		edp->nargs++;
744		if (edp->nargs > VT100_EMUL_NARGS) {
745#ifdef VT100_DEBUG
746			printf("vt100: too many arguments\n");
747#endif
748			edp->nargs = VT100_EMUL_NARGS;
749		}
750		newstate = VT100_EMUL_STATE_STRING;
751		switch (c) {
752		case '$':
753			newstate = VT100_EMUL_STATE_DCS_DOLLAR;
754			break;
755		case '{': /* DECDLD soft charset */
756		case '!': /* DECRQUPSS user preferred supplemental set */
757			/* 'u' must follow - need another state */
758		case '|': /* DECUDK program F6..F20 */
759#ifdef VT100_PRINTNOTIMPL
760			printf("DCS%c ignored\n", c);
761#endif
762			break;
763		default:
764#ifdef VT100_PRINTUNKNOWN
765			printf("DCS%c (%d, %d) unknown\n", c, ARG(0), ARG(1));
766#endif
767			break;
768		}
769	}
770
771	return (newstate);
772}
773
774static u_int
775wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, u_char c)
776{
777	switch (c) {
778	case 'p': /* DECRSTS terminal state restore */
779	case 'q': /* DECRQSS control function request */
780#ifdef VT100_PRINTNOTIMPL
781		printf("DCS$%c ignored\n", c);
782#endif
783		break;
784	case 't': /* DECRSPS restore presentation state */
785		switch (ARG(0)) {
786		case 0: /* error */
787			break;
788		case 1: /* cursor information restore */
789#ifdef VT100_PRINTNOTIMPL
790			printf("DCS1$t ignored\n");
791#endif
792			break;
793		case 2: /* tab stop restore */
794			edp->dcspos = 0;
795			edp->dcstype = DCSTYPE_TABRESTORE;
796			break;
797		default:
798#ifdef VT100_PRINTUNKNOWN
799			printf("DCS%d$t unknown\n", ARG(0));
800#endif
801			break;
802		}
803		break;
804	default:
805#ifdef VT100_PRINTUNKNOWN
806		printf("DCS$%c (%d, %d) unknown\n", c, ARG(0), ARG(1));
807#endif
808		break;
809	}
810	return (VT100_EMUL_STATE_STRING);
811}
812
813static u_int
814wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
815{
816	int i, j;
817
818	switch (c) {
819	case '5': /*  DECSWL single width, single height */
820		if (edp->dw) {
821			for (i = 0; i < edp->ncols / 2; i++)
822				(*edp->emulops->copycols)(edp->emulcookie,
823							  edp->crow,
824							  2 * i, i, 1);
825			(*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
826						   i, edp->ncols - i,
827						   edp->bkgdattr);
828			edp->dblwid[edp->crow] = 0;
829			edp->dw = 0;
830		}
831		break;
832	case '6': /*  DECDWL double width, single height */
833	case '3': /*  DECDHL double width, double height, top half */
834	case '4': /*  DECDHL double width, double height, bottom half */
835		if (!edp->dw) {
836			for (i = edp->ncols / 2 - 1; i >= 0; i--)
837				(*edp->emulops->copycols)(edp->emulcookie,
838							  edp->crow,
839							  i, 2 * i, 1);
840			for (i = 0; i < edp->ncols / 2; i++)
841				(*edp->emulops->erasecols)(edp->emulcookie,
842							   edp->crow,
843							   2 * i + 1, 1,
844							   edp->bkgdattr);
845			edp->dblwid[edp->crow] = 1;
846			edp->dw = 1;
847			if (edp->ccol > (edp->ncols >> 1) - 1)
848				edp->ccol = (edp->ncols >> 1) - 1;
849		}
850		break;
851	case '8': /* DECALN */
852		for (i = 0; i < edp->nrows; i++)
853			for (j = 0; j < edp->ncols; j++)
854				(*edp->emulops->putchar)(edp->emulcookie, i, j,
855							 'E', edp->curattr);
856		edp->ccol = 0;
857		edp->crow = 0;
858		break;
859	default:
860#ifdef VT100_PRINTUNKNOWN
861		printf("ESC#%c unknown\n", c);
862#endif
863		break;
864	}
865	return (VT100_EMUL_STATE_NORMAL);
866}
867
868static u_int
869wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, u_char c)
870{
871	u_int newstate = VT100_EMUL_STATE_CSI;
872
873	switch (c) {
874	case '0': case '1': case '2': case '3': case '4':
875	case '5': case '6': case '7': case '8': case '9':
876		/* argument digit */
877		if (edp->nargs > VT100_EMUL_NARGS - 1)
878			break;
879		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
880		    (c - '0');
881		break;
882	case ';': /* argument terminator */
883		edp->nargs++;
884		break;
885	case '?': /* DEC specific */
886	case '>': /* DA query */
887		edp->modif1 = c;
888		break;
889	case '!':
890	case '"':
891	case '$':
892	case '&':
893		edp->modif2 = c;
894		break;
895	default: /* end of escape sequence */
896		edp->nargs++;
897		if (edp->nargs > VT100_EMUL_NARGS) {
898#ifdef VT100_DEBUG
899			printf("vt100: too many arguments\n");
900#endif
901			edp->nargs = VT100_EMUL_NARGS;
902		}
903		wsemul_vt100_handle_csi(edp, c);
904		newstate = VT100_EMUL_STATE_NORMAL;
905		break;
906	}
907	return (newstate);
908}
909
910void
911wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
912{
913	struct wsemul_vt100_emuldata *edp = cookie;
914
915#ifdef DIAGNOSTIC
916	if (kernel && !edp->console)
917		panic("wsemul_vt100_output: kernel output, not console");
918#endif
919
920	if (edp->flags & VTFL_CURSORON)
921		(*edp->emulops->cursor)(edp->emulcookie, 0,
922					edp->crow, edp->ccol << edp->dw);
923	for (; count > 0; data++, count--) {
924		if ((*data & 0x7f) < 0x20) {
925			wsemul_vt100_output_c0c1(edp, *data, kernel);
926			continue;
927		}
928		if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) {
929			wsemul_vt100_output_normal(edp, *data, kernel);
930			continue;
931		}
932#ifdef DIAGNOSTIC
933		if (edp->state > sizeof(vt100_output) / sizeof(vt100_output[0]))
934			panic("wsemul_vt100: invalid state %d", edp->state);
935#endif
936		edp->state = vt100_output[edp->state - 1](edp, *data);
937	}
938	if (edp->flags & VTFL_CURSORON)
939		(*edp->emulops->cursor)(edp->emulcookie, 1,
940					edp->crow, edp->ccol << edp->dw);
941}
942