wsemul_vt100.c revision 1.47
1/* $OpenBSD: wsemul_vt100.c,v 1.28 2011/08/04 04:18:42 miod 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.47 2021/06/07 15:52:08 christos Exp $");
31
32#ifdef _KERNEL_OPT
33#include "opt_wsmsgattrs.h"
34#endif
35
36#include <sys/param.h>
37#include <sys/systm.h>
38#include <sys/time.h>
39#include <sys/malloc.h>
40#include <sys/fcntl.h>
41
42#include <dev/wscons/wsconsio.h>
43#include <dev/wscons/wsdisplayvar.h>
44#include <dev/wscons/wsemulvar.h>
45#include <dev/wscons/wsemul_vt100var.h>
46#include <dev/wscons/ascii.h>
47
48void	*wsemul_vt100_cnattach(const struct wsscreen_descr *, void *,
49			       int, int, long);
50void	*wsemul_vt100_attach(int console, const struct wsscreen_descr *,
51			     void *, int, int, void *, long);
52void	wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int);
53void	wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp);
54void	wsemul_vt100_resetop(void *, enum wsemul_resetops);
55#ifdef WSDISPLAY_CUSTOM_OUTPUT
56static void wsemul_vt100_getmsgattrs(void *, struct wsdisplay_msgattrs *);
57static void wsemul_vt100_setmsgattrs(void *, const struct wsscreen_descr *,
58                                     const struct wsdisplay_msgattrs *);
59#endif /* WSDISPLAY_CUSTOM_OUTPUT */
60static void wsemul_vt100_resize(void *, const struct wsscreen_descr *);
61
62const struct wsemul_ops wsemul_vt100_ops = {
63	.name = "vt100",
64	.cnattach = wsemul_vt100_cnattach,
65	.attach = wsemul_vt100_attach,
66	.output = wsemul_vt100_output,
67	.translate = wsemul_vt100_translate,
68	.detach = wsemul_vt100_detach,
69	.reset = wsemul_vt100_resetop,
70#ifdef WSDISPLAY_CUSTOM_OUTPUT
71	.getmsgattrs = wsemul_vt100_getmsgattrs,
72	.setmsgattrs = wsemul_vt100_setmsgattrs,
73#else
74	.getmsgattrs = NULL,
75	.setmsgattrs = NULL,
76#endif
77	.resize = wsemul_vt100_resize
78};
79
80struct wsemul_vt100_emuldata wsemul_vt100_console_emuldata;
81
82static void wsemul_vt100_init(struct wsemul_vt100_emuldata *,
83			      const struct wsscreen_descr *,
84			      void *, int, int, long);
85
86static void wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *,
87				       u_char, int);
88static void wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *,
89				     u_char, int);
90static void wsemul_vt100_nextline(struct wsemul_vt100_emuldata *);
91typedef u_int vt100_handler(struct wsemul_vt100_emuldata *, u_char);
92
93static vt100_handler
94wsemul_vt100_output_esc,
95wsemul_vt100_output_csi,
96wsemul_vt100_output_scs94,
97wsemul_vt100_output_scs94_percent,
98wsemul_vt100_output_scs96,
99wsemul_vt100_output_scs96_percent,
100wsemul_vt100_output_esc_hash,
101wsemul_vt100_output_esc_spc,
102wsemul_vt100_output_string,
103wsemul_vt100_output_string_esc,
104wsemul_vt100_output_dcs,
105wsemul_vt100_output_dcs_dollar;
106
107#define	VT100_EMUL_STATE_NORMAL		0	/* normal processing */
108#define	VT100_EMUL_STATE_ESC		1	/* got ESC */
109#define	VT100_EMUL_STATE_CSI		2	/* got CSI (ESC[) */
110#define	VT100_EMUL_STATE_SCS94		3	/* got ESC{()*+} */
111#define	VT100_EMUL_STATE_SCS94_PERCENT	4	/* got ESC{()*+}% */
112#define	VT100_EMUL_STATE_SCS96		5	/* got ESC{-./} */
113#define	VT100_EMUL_STATE_SCS96_PERCENT	6	/* got ESC{-./}% */
114#define	VT100_EMUL_STATE_ESC_HASH	7	/* got ESC# */
115#define	VT100_EMUL_STATE_ESC_SPC	8	/* got ESC<SPC> */
116#define	VT100_EMUL_STATE_STRING		9	/* waiting for ST (ESC\) */
117#define	VT100_EMUL_STATE_STRING_ESC	10	/* waiting for ST, got ESC */
118#define	VT100_EMUL_STATE_DCS		11	/* got DCS (ESC P) */
119#define	VT100_EMUL_STATE_DCS_DOLLAR	12	/* got DCS<p>$ */
120
121vt100_handler *vt100_output[] = {
122	wsemul_vt100_output_esc,
123	wsemul_vt100_output_csi,
124	wsemul_vt100_output_scs94,
125	wsemul_vt100_output_scs94_percent,
126	wsemul_vt100_output_scs96,
127	wsemul_vt100_output_scs96_percent,
128	wsemul_vt100_output_esc_hash,
129	wsemul_vt100_output_esc_spc,
130	wsemul_vt100_output_string,
131	wsemul_vt100_output_string_esc,
132	wsemul_vt100_output_dcs,
133	wsemul_vt100_output_dcs_dollar,
134};
135
136static void
137wsemul_vt100_init(struct wsemul_vt100_emuldata *edp,
138	const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
139	long defattr)
140{
141	struct vt100base_data *vd = &edp->bd;
142	int error;
143
144	vd->emulops = type->textops;
145	vd->emulcookie = cookie;
146	vd->scrcapabilities = type->capabilities;
147	vd->nrows = type->nrows;
148	vd->ncols = type->ncols;
149	vd->crow = crow;
150	vd->ccol = ccol;
151
152	/* The underlying driver has already allocated a default and simple
153	 * attribute for us, which is stored in defattr.  We try to set the
154	 * values specified by the kernel options below, but in case of
155	 * failure we fallback to the value given by the driver. */
156
157	if (type->capabilities & WSSCREEN_WSCOLORS) {
158		vd->msgattrs.default_attrs = WS_DEFAULT_COLATTR |
159		    WSATTR_WSCOLORS;
160		vd->msgattrs.default_bg = WS_DEFAULT_BG;
161		vd->msgattrs.default_fg = WS_DEFAULT_FG;
162
163		vd->msgattrs.kernel_attrs = WS_KERNEL_COLATTR |
164		    WSATTR_WSCOLORS;
165		vd->msgattrs.kernel_bg = WS_KERNEL_BG;
166		vd->msgattrs.kernel_fg = WS_KERNEL_FG;
167	} else {
168		vd->msgattrs.default_attrs = WS_DEFAULT_MONOATTR;
169		vd->msgattrs.default_bg = vd->msgattrs.default_fg = 0;
170
171		vd->msgattrs.kernel_attrs = WS_KERNEL_MONOATTR;
172		vd->msgattrs.kernel_bg = vd->msgattrs.kernel_fg = 0;
173	}
174
175	error = (*vd->emulops->allocattr)(cookie,
176					   vd->msgattrs.default_fg,
177					   vd->msgattrs.default_bg,
178					   vd->msgattrs.default_attrs,
179					   &vd->defattr);
180	if (error) {
181		vd->defattr = defattr;
182		/* XXX This assumes the driver has allocated white on black
183		 * XXX as the default attribute, which is not always true.
184		 * XXX Maybe we need an emulop that, given an attribute,
185		 * XXX (defattr) returns its flags and colors? */
186		vd->msgattrs.default_attrs = 0;
187		vd->msgattrs.default_bg = WSCOL_BLACK;
188		vd->msgattrs.default_fg = WSCOL_WHITE;
189	} else {
190		if (vd->emulops->replaceattr != NULL)
191			(*vd->emulops->replaceattr)(cookie, defattr,
192			                             vd->defattr);
193	}
194
195#if defined(WS_KERNEL_CUSTOMIZED)
196	/* Set up kernel colors, in case they were customized by the user;
197	 * otherwise default to the colors specified for the console.
198	 * In case of failure, we use console colors too; we can assume
199	 * they are good as they have been previously allocated and
200	 * verified. */
201	error = (*vd->emulops->allocattr)(cookie,
202					   vd->msgattrs.kernel_fg,
203					   vd->msgattrs.kernel_bg,
204					   vd->msgattrs.kernel_attrs,
205					   &edp->kernattr);
206	if (error)
207#endif
208	edp->kernattr = vd->defattr;
209}
210
211void *
212wsemul_vt100_cnattach(const struct wsscreen_descr *type, void *cookie,
213	int ccol, int crow, long defattr)
214{
215	struct wsemul_vt100_emuldata *edp;
216	struct vt100base_data *vd;
217
218	edp = &wsemul_vt100_console_emuldata;
219	vd = &edp->bd;
220	wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
221#ifdef DIAGNOSTIC
222	edp->console = 1;
223#endif
224	vd->cbcookie = NULL;
225
226	vd->tabs = 0;
227	vd->dblwid = 0;
228	vd->dw = 0;
229	vd->dcsarg = 0;
230	edp->isolatin1tab = edp->decgraphtab = edp->dectechtab = 0;
231	edp->nrctab = 0;
232	wsemul_vt100_reset(edp);
233	return edp;
234}
235
236void *
237wsemul_vt100_attach(int console, const struct wsscreen_descr *type,
238	void *cookie, int ccol, int crow, void *cbcookie, long defattr)
239{
240	struct wsemul_vt100_emuldata *edp;
241	struct vt100base_data *vd;
242
243	if (console) {
244		edp = &wsemul_vt100_console_emuldata;
245		KASSERT(edp->console == 1);
246	} else {
247		edp = malloc(sizeof *edp, M_DEVBUF, M_WAITOK);
248		wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
249#ifdef DIAGNOSTIC
250		edp->console = 0;
251#endif
252	}
253	vd = &edp->bd;
254	vd->cbcookie = cbcookie;
255
256	vd->tabs = malloc(1024, M_DEVBUF, M_WAITOK);
257	vd->dblwid = malloc(1024, M_DEVBUF, M_WAITOK|M_ZERO);
258	vd->dw = 0;
259	vd->dcsarg = malloc(DCS_MAXLEN, M_DEVBUF, M_WAITOK);
260	edp->isolatin1tab = malloc(128 * sizeof(int), M_DEVBUF, M_WAITOK);
261	edp->decgraphtab = malloc(128 * sizeof(int), M_DEVBUF, M_WAITOK);
262	edp->dectechtab = malloc(128 * sizeof(int), M_DEVBUF, M_WAITOK);
263	edp->nrctab = malloc(128 * sizeof(int), M_DEVBUF, M_WAITOK);
264	vt100_initchartables(edp);
265	wsemul_vt100_reset(edp);
266	return edp;
267}
268
269void
270wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp)
271{
272	struct wsemul_vt100_emuldata *edp = cookie;
273	struct vt100base_data *vd = &edp->bd;
274
275	*crowp = vd->crow;
276	*ccolp = vd->ccol;
277#define f(ptr) if (ptr) {free(ptr, M_DEVBUF); ptr = 0;}
278	f(vd->tabs)
279	f(vd->dblwid)
280	f(vd->dcsarg)
281	f(edp->isolatin1tab)
282	f(edp->decgraphtab)
283	f(edp->dectechtab)
284	f(edp->nrctab)
285#undef f
286	if (edp != &wsemul_vt100_console_emuldata)
287		free(edp, M_DEVBUF);
288}
289
290static void
291wsemul_vt100_resize(void * cookie, const struct wsscreen_descr *type)
292{
293	struct wsemul_vt100_emuldata *edp = cookie;
294
295	edp->bd.nrows = type->nrows;
296	edp->bd.ncols = type->ncols;
297	wsemul_vt100_reset(edp);
298	wsemul_vt100_resetop(cookie, WSEMUL_CLEARSCREEN);
299}
300
301void
302wsemul_vt100_resetop(void *cookie, enum wsemul_resetops op)
303{
304	struct wsemul_vt100_emuldata *edp = cookie;
305	struct vt100base_data *vd = &edp->bd;
306
307	switch (op) {
308	case WSEMUL_RESET:
309		wsemul_vt100_reset(edp);
310		break;
311	case WSEMUL_SYNCFONT:
312		vt100_initchartables(edp);
313		break;
314	case WSEMUL_CLEARSCREEN:
315		wsemul_vt100_ed(vd, 2);
316		vd->ccol = vd->crow = 0;
317		(*vd->emulops->cursor)(vd->emulcookie,
318					vd->flags & VTFL_CURSORON, 0, 0);
319		break;
320	default:
321		break;
322	}
323}
324
325void
326wsemul_vt100_reset(struct wsemul_vt100_emuldata *edp)
327{
328	struct vt100base_data *vd = &edp->bd;
329	int i;
330
331	edp->state = VT100_EMUL_STATE_NORMAL;
332	vd->flags = VTFL_DECAWM | VTFL_CURSORON;
333	vd->bkgdattr = vd->curattr = vd->defattr;
334	vd->attrflags = vd->msgattrs.default_attrs;
335	vd->fgcol = vd->msgattrs.default_fg;
336	vd->bgcol = vd->msgattrs.default_bg;
337	vd->scrreg_startrow = 0;
338	vd->scrreg_nrows = vd->nrows;
339	if (vd->tabs) {
340		memset(vd->tabs, 0, vd->ncols);
341		for (i = 8; i < vd->ncols; i += 8)
342			vd->tabs[i] = 1;
343	}
344	vd->dcspos = 0;
345	vd->dcstype = 0;
346	edp->chartab_G[0] = 0;
347	edp->chartab_G[1] = edp->nrctab; /* ??? */
348	edp->chartab_G[2] = edp->isolatin1tab;
349	edp->chartab_G[3] = edp->isolatin1tab;
350	edp->chartab0 = 0;
351	edp->chartab1 = 2;
352	edp->sschartab = 0;
353}
354
355/*
356 * now all the state machine bits
357 */
358
359/*
360 * Move the cursor to the next line if possible. If the cursor is at
361 * the bottom of the scroll area, then scroll it up. If the cursor is
362 * at the bottom of the screen then don't move it down.
363 */
364static void
365wsemul_vt100_nextline(struct wsemul_vt100_emuldata *edp)
366{
367	struct vt100base_data *vd = &edp->bd;
368
369	if (ROWS_BELOW(vd) == 0) {
370		/* Bottom of the scroll region. */
371	  	wsemul_vt100_scrollup(vd, 1);
372	} else {
373		if ((vd->crow+1) < vd->nrows)
374			/* Cursor not at the bottom of the screen. */
375			vd->crow++;
376		CHECK_DW(vd);
377	}
378}
379
380static void
381wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *edp, u_char c,
382	int kernel)
383{
384	struct vt100base_data *vd = &edp->bd;
385	u_int *ct, dc;
386
387	if ((vd->flags & (VTFL_LASTCHAR | VTFL_DECAWM)) ==
388	    (VTFL_LASTCHAR | VTFL_DECAWM)) {
389		wsemul_vt100_nextline(edp);
390		vd->ccol = 0;
391		vd->flags &= ~VTFL_LASTCHAR;
392	}
393
394	if (c & 0x80) {
395		c &= 0x7f;
396		ct = edp->chartab_G[edp->chartab1];
397	} else {
398		if (edp->sschartab) {
399			ct = edp->chartab_G[edp->sschartab];
400			edp->sschartab = 0;
401		} else
402			ct = edp->chartab_G[edp->chartab0];
403	}
404	dc = (ct ? ct[c] : c);
405
406	if ((vd->flags & VTFL_INSERTMODE) && COLS_LEFT(vd))
407		COPYCOLS(vd, vd->ccol, vd->ccol + 1, COLS_LEFT(vd));
408
409	(*vd->emulops->putchar)(vd->emulcookie, vd->crow,
410				 vd->ccol << vd->dw, dc,
411				 kernel ? edp->kernattr : vd->curattr);
412
413	if (COLS_LEFT(vd))
414		vd->ccol++;
415	else
416		vd->flags |= VTFL_LASTCHAR;
417}
418
419static void
420wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *edp, u_char c,
421	int kernel)
422{
423	struct vt100base_data *vd = &edp->bd;
424	u_int n;
425
426	switch (c) {
427	case ASCII_NUL:
428	default:
429		/* ignore */
430		break;
431	case ASCII_BEL:
432		if (edp->state == VT100_EMUL_STATE_STRING) {
433			/* acts as an equivalent to the ``ESC \'' string end */
434			wsemul_vt100_handle_dcs(vd);
435			edp->state = VT100_EMUL_STATE_NORMAL;
436		} else {
437			wsdisplay_emulbell(vd->cbcookie);
438		}
439		break;
440	case ASCII_BS:
441		if (vd->ccol > 0) {
442			vd->ccol--;
443			vd->flags &= ~VTFL_LASTCHAR;
444		}
445		break;
446	case ASCII_CR:
447		vd->ccol = 0;
448		vd->flags &= ~VTFL_LASTCHAR;
449		break;
450	case ASCII_HT:
451		if (vd->tabs) {
452			if (!COLS_LEFT(vd))
453				break;
454			for (n = vd->ccol + 1; n < NCOLS(vd) - 1; n++)
455				if (vd->tabs[n])
456					break;
457		} else {
458			n = vd->ccol + uimin(8 - (vd->ccol & 7), COLS_LEFT(vd));
459		}
460		vd->ccol = n;
461		break;
462	case ASCII_SO: /* LS1 */
463		edp->chartab0 = 1;
464		break;
465	case ASCII_SI: /* LS0 */
466		edp->chartab0 = 0;
467		break;
468	case ASCII_ESC:
469		if (kernel) {
470			printf("%s: ESC in kernel output ignored\n", __func__);
471			break;	/* ignore the ESC */
472		}
473
474		if (edp->state == VT100_EMUL_STATE_STRING) {
475			/* might be a string end */
476			edp->state = VT100_EMUL_STATE_STRING_ESC;
477		} else {
478			/* XXX cancel current escape sequence */
479			edp->state = VT100_EMUL_STATE_ESC;
480		}
481		break;
482#if 0
483	case CSI: /* 8-bit */
484		/* XXX cancel current escape sequence */
485		edp->nargs = 0;
486		memset(edp->args, 0, sizeof (edp->args));
487		edp->modif1 = edp->modif2 = '\0';
488		edp->state = VT100_EMUL_STATE_CSI;
489		break;
490	case DCS: /* 8-bit */
491		/* XXX cancel current escape sequence */
492		edp->nargs = 0;
493		memset(edp->args, 0, sizeof (edp->args));
494		edp->state = VT100_EMUL_STATE_DCS;
495		break;
496	case ST: /* string end 8-bit */
497		/* XXX only in VT100_EMUL_STATE_STRING */
498		wsemul_vt100_handle_dcs(vd);
499		edp->state = VT100_EMUL_STATE_NORMAL;
500		break;
501#endif
502	case ASCII_LF:
503	case ASCII_VT:
504	case ASCII_FF:
505		wsemul_vt100_nextline(edp);
506		break;
507	}
508}
509
510static u_int
511wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
512{
513	struct vt100base_data *vd = &edp->bd;
514	int i;
515
516	switch (c) {
517	case '[': /* CSI */
518		vd->nargs = 0;
519		memset(vd->args, 0, sizeof (vd->args));
520		vd->modif1 = vd->modif2 = '\0';
521		return VT100_EMUL_STATE_CSI;
522	case '7': /* DECSC */
523		vd->flags |= VTFL_SAVEDCURS;
524		edp->savedcursor_row = vd->crow;
525		edp->savedcursor_col = vd->ccol;
526		edp->savedattr = vd->curattr;
527		edp->savedbkgdattr = vd->bkgdattr;
528		edp->savedattrflags = vd->attrflags;
529		edp->savedfgcol = vd->fgcol;
530		edp->savedbgcol = vd->bgcol;
531		for (i = 0; i < 4; i++)
532			edp->savedchartab_G[i] = edp->chartab_G[i];
533		edp->savedchartab0 = edp->chartab0;
534		edp->savedchartab1 = edp->chartab1;
535		break;
536	case '8': /* DECRC */
537		if ((vd->flags & VTFL_SAVEDCURS) == 0)
538			break;
539		vd->crow = edp->savedcursor_row;
540		vd->ccol = edp->savedcursor_col;
541		vd->curattr = edp->savedattr;
542		vd->bkgdattr = edp->savedbkgdattr;
543		vd->attrflags = edp->savedattrflags;
544		vd->fgcol = edp->savedfgcol;
545		vd->bgcol = edp->savedbgcol;
546		for (i = 0; i < 4; i++)
547			edp->chartab_G[i] = edp->savedchartab_G[i];
548		edp->chartab0 = edp->savedchartab0;
549		edp->chartab1 = edp->savedchartab1;
550		break;
551	case '=': /* DECKPAM application mode */
552		vd->flags |= VTFL_APPLKEYPAD;
553		break;
554	case '>': /* DECKPNM numeric mode */
555		vd->flags &= ~VTFL_APPLKEYPAD;
556		break;
557	case 'E': /* NEL */
558		vd->ccol = 0;
559		/* FALLTHRU */
560	case 'D': /* IND */
561		wsemul_vt100_nextline(edp);
562		break;
563	case 'H': /* HTS */
564		KASSERT(vd->tabs != 0);
565		vd->tabs[vd->ccol] = 1;
566		break;
567	case '~': /* LS1R */
568		edp->chartab1 = 1;
569		break;
570	case 'n': /* LS2 */
571		edp->chartab0 = 2;
572		break;
573	case '}': /* LS2R */
574		edp->chartab1 = 2;
575		break;
576	case 'o': /* LS3 */
577		edp->chartab0 = 3;
578		break;
579	case '|': /* LS3R */
580		edp->chartab1 = 3;
581		break;
582	case 'N': /* SS2 */
583		edp->sschartab = 2;
584		break;
585	case 'O': /* SS3 */
586		edp->sschartab = 3;
587		break;
588	case 'M': /* RI */
589		if (ROWS_ABOVE(vd) > 0) {
590			vd->crow--;
591			CHECK_DW(vd);
592			break;
593		}
594		wsemul_vt100_scrolldown(vd, 1);
595		break;
596	case 'P': /* DCS */
597		vd->nargs = 0;
598		memset(vd->args, 0, sizeof (vd->args));
599		return VT100_EMUL_STATE_DCS;
600	case 'c': /* RIS */
601		wsemul_vt100_reset(edp);
602		wsemul_vt100_ed(vd, 2);
603		vd->ccol = vd->crow = 0;
604		break;
605	case '(': case ')': case '*': case '+': /* SCS */
606		edp->designating = c - '(';
607		return VT100_EMUL_STATE_SCS94;
608	case '-': case '.': case '/': /* SCS */
609		edp->designating = c - '-' + 1;
610		return VT100_EMUL_STATE_SCS96;
611	case '#':
612		return VT100_EMUL_STATE_ESC_HASH;
613	case ' ': /* 7/8 bit */
614		return VT100_EMUL_STATE_ESC_SPC;
615	case ']': /* OSC operating system command */
616	case '^': /* PM privacy message */
617	case '_': /* APC application program command */
618		/* ignored */
619		return VT100_EMUL_STATE_STRING;
620	case '<': /* exit VT52 mode - ignored */
621		break;
622	default:
623#ifdef VT100_PRINTUNKNOWN
624		printf("ESC%c unknown\n", c);
625#endif
626		break;
627	}
628	return VT100_EMUL_STATE_NORMAL;
629}
630
631static u_int
632wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, u_char c)
633{
634	switch (c) {
635	case '%': /* probably DEC supplemental graphic */
636		return VT100_EMUL_STATE_SCS94_PERCENT;
637	case 'A': /* british / national */
638		edp->chartab_G[edp->designating] = edp->nrctab;
639		break;
640	case 'B': /* ASCII */
641		edp->chartab_G[edp->designating] = 0;
642		break;
643	case '<': /* user preferred supplemental */
644		/* XXX not really "user" preferred */
645		edp->chartab_G[edp->designating] = edp->isolatin1tab;
646		break;
647	case '0': /* DEC special graphic */
648		edp->chartab_G[edp->designating] = edp->decgraphtab;
649		break;
650	case '>': /* DEC tech */
651		edp->chartab_G[edp->designating] = edp->dectechtab;
652		break;
653	default:
654#ifdef VT100_PRINTUNKNOWN
655		printf("ESC%c%c unknown\n", edp->designating + '(', c);
656#endif
657		break;
658	}
659	return VT100_EMUL_STATE_NORMAL;
660}
661
662static u_int
663wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, u_char c)
664{
665	switch (c) {
666	case '5': /* DEC supplemental graphic */
667		/* XXX there are differences */
668		edp->chartab_G[edp->designating] = edp->isolatin1tab;
669		break;
670	default:
671#ifdef VT100_PRINTUNKNOWN
672		printf("ESC%c%%%c unknown\n", edp->designating + '(', c);
673#endif
674		break;
675	}
676	return VT100_EMUL_STATE_NORMAL;
677}
678
679static u_int
680wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp, u_char c)
681{
682	int nrc;
683
684	switch (c) {
685	case '%': /* probably portuguese */
686		return VT100_EMUL_STATE_SCS96_PERCENT;
687	case 'A': /* ISO-latin-1 supplemental */
688		edp->chartab_G[edp->designating] = edp->isolatin1tab;
689		break;
690	case '4': /* dutch */
691		nrc = 1;
692		goto setnrc;
693	case '5': case 'C': /* finnish */
694		nrc = 2;
695		goto setnrc;
696	case 'R': /* french */
697		nrc = 3;
698		goto setnrc;
699	case 'Q': /* french canadian */
700		nrc = 4;
701		goto setnrc;
702	case 'K': /* german */
703		nrc = 5;
704		goto setnrc;
705	case 'Y': /* italian */
706		nrc = 6;
707		goto setnrc;
708	case 'E': case '6': /* norwegian / danish */
709		nrc = 7;
710		goto setnrc;
711	case 'Z': /* spanish */
712		nrc = 9;
713		goto setnrc;
714	case '7': case 'H': /* swedish */
715		nrc = 10;
716		goto setnrc;
717	case '=': /* swiss */
718		nrc = 11;
719setnrc:
720		vt100_setnrc(edp, nrc); /* what table ??? */
721		break;
722	default:
723#ifdef VT100_PRINTUNKNOWN
724		printf("ESC%c%c unknown\n", edp->designating + '-' - 1, c);
725#endif
726		break;
727	}
728	return VT100_EMUL_STATE_NORMAL;
729}
730
731static u_int
732wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, u_char c)
733{
734	switch (c) {
735	case '6': /* portuguese */
736		vt100_setnrc(edp, 8);
737		break;
738	default:
739#ifdef VT100_PRINTUNKNOWN
740		printf("ESC%c%%%c unknown\n", edp->designating + '-', c);
741#endif
742		break;
743	}
744	return VT100_EMUL_STATE_NORMAL;
745}
746
747static u_int
748wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp,
749    u_char c)
750{
751	switch (c) {
752	case 'F': /* 7-bit controls */
753	case 'G': /* 8-bit controls */
754#ifdef VT100_PRINTNOTIMPL
755		printf("ESC<SPC>%c ignored\n", c);
756#endif
757		break;
758	default:
759#ifdef VT100_PRINTUNKNOWN
760		printf("ESC<SPC>%c unknown\n", c);
761#endif
762		break;
763	}
764	return VT100_EMUL_STATE_NORMAL;
765}
766
767static u_int
768wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp, u_char c)
769{
770	struct vt100base_data *vd = &edp->bd;
771
772	if (vd->dcstype && vd->dcspos < DCS_MAXLEN)
773		vd->dcsarg[vd->dcspos++] = c;
774	return VT100_EMUL_STATE_STRING;
775}
776
777static u_int
778wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, u_char c)
779{
780	struct vt100base_data *vd = &edp->bd;
781
782	if (c == '\\') { /* ST complete */
783		wsemul_vt100_handle_dcs(vd);
784		return VT100_EMUL_STATE_NORMAL;
785	} else
786		return VT100_EMUL_STATE_STRING;
787}
788
789static u_int
790wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, u_char c)
791{
792	struct vt100base_data *vd = &edp->bd;
793
794	switch (c) {
795	case '0': case '1': case '2': case '3': case '4':
796	case '5': case '6': case '7': case '8': case '9':
797		/* argument digit */
798		if (vd->nargs > VT100_EMUL_NARGS - 1)
799			break;
800		vd->args[vd->nargs] = (vd->args[vd->nargs] * 10) +
801		    (c - '0');
802		break;
803	case ';': /* argument terminator */
804		vd->nargs++;
805		break;
806	default:
807		vd->nargs++;
808		if (vd->nargs > VT100_EMUL_NARGS) {
809#ifdef VT100_DEBUG
810			printf("vt100: too many arguments\n");
811#endif
812			vd->nargs = VT100_EMUL_NARGS;
813		}
814		switch (c) {
815		case '$':
816			return VT100_EMUL_STATE_DCS_DOLLAR;
817		case '{': /* DECDLD soft charset */
818		case '!': /* DECRQUPSS user preferred supplemental set */
819			/* 'u' must follow - need another state */
820		case '|': /* DECUDK program F6..F20 */
821#ifdef VT100_PRINTNOTIMPL
822			printf("DCS%c ignored\n", c);
823#endif
824			break;
825		default:
826#ifdef VT100_PRINTUNKNOWN
827			printf("DCS%c (%d, %d) unknown\n", c, ARG(vd, 0), ARG(vd, 1));
828#endif
829			break;
830		}
831		return VT100_EMUL_STATE_STRING;
832	}
833
834	return VT100_EMUL_STATE_DCS;
835}
836
837static u_int
838wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, u_char c)
839{
840	struct vt100base_data *vd = &edp->bd;
841
842	switch (c) {
843	case 'p': /* DECRSTS terminal state restore */
844	case 'q': /* DECRQSS control function request */
845#ifdef VT100_PRINTNOTIMPL
846		printf("DCS$%c ignored\n", c);
847#endif
848		break;
849	case 't': /* DECRSPS restore presentation state */
850		switch (ARG(vd, 0)) {
851		case 0: /* error */
852			break;
853		case 1: /* cursor information restore */
854#ifdef VT100_PRINTNOTIMPL
855			printf("DCS1$t ignored\n");
856#endif
857			break;
858		case 2: /* tab stop restore */
859			vd->dcspos = 0;
860			vd->dcstype = DCSTYPE_TABRESTORE;
861			break;
862		default:
863#ifdef VT100_PRINTUNKNOWN
864			printf("DCS%d$t unknown\n", ARG(vd, 0));
865#endif
866			break;
867		}
868		break;
869	default:
870#ifdef VT100_PRINTUNKNOWN
871		printf("DCS$%c (%d, %d) unknown\n", c, ARG(vd, 0), ARG(vd, 1));
872#endif
873		break;
874	}
875	return VT100_EMUL_STATE_STRING;
876}
877
878static u_int
879wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
880{
881	struct vt100base_data *vd = &edp->bd;
882	int i, j;
883
884	switch (c) {
885	case '5': /*  DECSWL single width, single height */
886		if (vd->dw) {
887			for (i = 0; i < vd->ncols / 2; i++)
888				(*vd->emulops->copycols)(vd->emulcookie,
889							  vd->crow,
890							  2 * i, i, 1);
891			(*vd->emulops->erasecols)(vd->emulcookie, vd->crow,
892						   i, vd->ncols - i,
893						   vd->bkgdattr);
894			vd->dblwid[vd->crow] = 0;
895			vd->dw = 0;
896		}
897		break;
898	case '6': /*  DECDWL double width, single height */
899	case '3': /*  DECDHL double width, double height, top half */
900	case '4': /*  DECDHL double width, double height, bottom half */
901		if (!vd->dw) {
902			for (i = vd->ncols / 2 - 1; i >= 0; i--)
903				(*vd->emulops->copycols)(vd->emulcookie,
904							  vd->crow,
905							  i, 2 * i, 1);
906			for (i = 0; i < vd->ncols / 2; i++)
907				(*vd->emulops->erasecols)(vd->emulcookie,
908							   vd->crow,
909							   2 * i + 1, 1,
910							   vd->bkgdattr);
911			vd->dblwid[vd->crow] = 1;
912			vd->dw = 1;
913			if (vd->ccol > (vd->ncols >> 1) - 1)
914				vd->ccol = (vd->ncols >> 1) - 1;
915		}
916		break;
917	case '8': /* DECALN */
918		for (i = 0; i < vd->nrows; i++)
919			for (j = 0; j < vd->ncols; j++)
920				(*vd->emulops->putchar)(vd->emulcookie, i, j,
921							 'E', vd->curattr);
922		vd->ccol = 0;
923		vd->crow = 0;
924		break;
925	default:
926#ifdef VT100_PRINTUNKNOWN
927		printf("ESC#%c unknown\n", c);
928#endif
929		break;
930	}
931	return VT100_EMUL_STATE_NORMAL;
932}
933
934static u_int
935wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, u_char c)
936{
937	struct vt100base_data *vd = &edp->bd;
938
939	switch (c) {
940	case '0': case '1': case '2': case '3': case '4':
941	case '5': case '6': case '7': case '8': case '9':
942		/* argument digit */
943		if (vd->nargs > VT100_EMUL_NARGS - 1)
944			break;
945		vd->args[vd->nargs] = (vd->args[vd->nargs] * 10) +
946		    (c - '0');
947		break;
948	case ';': /* argument terminator */
949		vd->nargs++;
950		break;
951	case '?': /* DEC specific */
952	case '>': /* DA query */
953		vd->modif1 = c;
954		break;
955	case '!':
956	case '"':
957	case '$':
958	case '&':
959		vd->modif2 = c;
960		break;
961	default: /* end of escape sequence */
962		vd->nargs++;
963		if (vd->nargs > VT100_EMUL_NARGS) {
964#ifdef VT100_DEBUG
965			printf("vt100: too many arguments\n");
966#endif
967			vd->nargs = VT100_EMUL_NARGS;
968		}
969		wsemul_vt100_handle_csi(vd, c);
970		return VT100_EMUL_STATE_NORMAL;
971	}
972	return VT100_EMUL_STATE_CSI;
973}
974
975void
976wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
977{
978	struct wsemul_vt100_emuldata *edp = cookie;
979	struct vt100base_data *vd = &edp->bd;
980
981#ifdef DIAGNOSTIC
982	if (kernel && !edp->console)
983		panic("%s: kernel output, not console", __func__);
984#endif
985
986	if (vd->flags & VTFL_CURSORON)
987		(*vd->emulops->cursor)(vd->emulcookie, 0,
988					vd->crow, vd->ccol << vd->dw);
989	for (; count > 0; data++, count--) {
990		if ((*data & 0x7f) < 0x20) {
991			wsemul_vt100_output_c0c1(edp, *data, kernel);
992			continue;
993		}
994		if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) {
995			wsemul_vt100_output_normal(edp, *data, kernel);
996			continue;
997		}
998		int state = edp->state - 1;
999		KASSERT(state < __arraycount(vt100_output));
1000		edp->state = vt100_output[state](edp, *data);
1001	}
1002	if (vd->flags & VTFL_CURSORON)
1003		(*vd->emulops->cursor)(vd->emulcookie, 1,
1004		    vd->crow, vd->ccol << vd->dw);
1005}
1006
1007#ifdef WSDISPLAY_CUSTOM_OUTPUT
1008static void
1009wsemul_vt100_getmsgattrs(void *cookie, struct wsdisplay_msgattrs *ma)
1010{
1011	struct wsemul_vt100_emuldata *edp = cookie;
1012	struct vt100base_data *vd = &edp->bd;
1013
1014	*ma = vd->msgattrs;
1015}
1016
1017static void
1018wsemul_vt100_setmsgattrs(void *cookie, const struct wsscreen_descr *type,
1019                         const struct wsdisplay_msgattrs *ma)
1020{
1021	int error;
1022	long tmp;
1023	struct wsemul_vt100_emuldata *edp = cookie;
1024	struct vt100base_data *vd = &edp->bd;
1025
1026	vd->msgattrs = *ma;
1027	if (type->capabilities & WSSCREEN_WSCOLORS) {
1028		vd->msgattrs.default_attrs |= WSATTR_WSCOLORS;
1029		vd->msgattrs.kernel_attrs |= WSATTR_WSCOLORS;
1030	} else {
1031		vd->msgattrs.default_bg = vd->msgattrs.kernel_bg = 0;
1032		vd->msgattrs.default_fg = vd->msgattrs.kernel_fg = 0;
1033	}
1034
1035	error = (*vd->emulops->allocattr)(vd->emulcookie,
1036	                                   vd->msgattrs.default_fg,
1037					   vd->msgattrs.default_bg,
1038	                                   vd->msgattrs.default_attrs,
1039	                                   &tmp);
1040#ifndef VT100_DEBUG
1041	__USE(error);
1042#else
1043	if (error)
1044		printf("vt100: failed to allocate attribute for default "
1045		       "messages\n");
1046	else
1047#endif
1048	{
1049		if (vd->curattr == vd->defattr) {
1050			vd->bkgdattr = vd->curattr = tmp;
1051			vd->attrflags = vd->msgattrs.default_attrs;
1052			vd->bgcol = vd->msgattrs.default_bg;
1053			vd->fgcol = vd->msgattrs.default_fg;
1054		} else {
1055			edp->savedbkgdattr = edp->savedattr = tmp;
1056			edp->savedattrflags = vd->msgattrs.default_attrs;
1057			edp->savedbgcol = vd->msgattrs.default_bg;
1058			edp->savedfgcol = vd->msgattrs.default_fg;
1059		}
1060		if (vd->emulops->replaceattr != NULL)
1061			(*vd->emulops->replaceattr)(vd->emulcookie,
1062			                             vd->defattr, tmp);
1063		vd->defattr = tmp;
1064	}
1065
1066	error = (*vd->emulops->allocattr)(vd->emulcookie,
1067	                                   vd->msgattrs.kernel_fg,
1068					   vd->msgattrs.kernel_bg,
1069	                                   vd->msgattrs.kernel_attrs,
1070	                                   &tmp);
1071#ifdef VT100_DEBUG
1072	if (error)
1073		printf("vt100: failed to allocate attribute for kernel "
1074		       "messages\n");
1075	else
1076#endif
1077	{
1078		if (vd->emulops->replaceattr != NULL)
1079			(*vd->emulops->replaceattr)(vd->emulcookie,
1080			                             edp->kernattr, tmp);
1081		edp->kernattr = tmp;
1082	}
1083}
1084#endif /* WSDISPLAY_CUSTOM_OUTPUT */
1085