wsemul_vt100.c revision 1.44
1/* $NetBSD: wsemul_vt100.c,v 1.44 2018/01/21 10:30:51 martin 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.44 2018/01/21 10:30:51 martin 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_NOWAIT);
257	vd->dblwid = malloc(1024, M_DEVBUF, M_NOWAIT|M_ZERO);
258	vd->dw = 0;
259	vd->dcsarg = malloc(DCS_MAXLEN, M_DEVBUF, M_NOWAIT);
260	edp->isolatin1tab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
261	edp->decgraphtab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
262	edp->dectechtab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
263	edp->nrctab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
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		wsdisplay_emulbell(vd->cbcookie);
433		break;
434	case ASCII_BS:
435		if (vd->ccol > 0) {
436			vd->ccol--;
437			vd->flags &= ~VTFL_LASTCHAR;
438		}
439		break;
440	case ASCII_CR:
441		vd->ccol = 0;
442		vd->flags &= ~VTFL_LASTCHAR;
443		break;
444	case ASCII_HT:
445		if (vd->tabs) {
446			if (!COLS_LEFT(vd))
447				break;
448			for (n = vd->ccol + 1; n < NCOLS(vd) - 1; n++)
449				if (vd->tabs[n])
450					break;
451		} else {
452			n = vd->ccol + min(8 - (vd->ccol & 7), COLS_LEFT(vd));
453		}
454		vd->ccol = n;
455		break;
456	case ASCII_SO: /* LS1 */
457		edp->chartab0 = 1;
458		break;
459	case ASCII_SI: /* LS0 */
460		edp->chartab0 = 0;
461		break;
462	case ASCII_ESC:
463		if (kernel) {
464			printf("%s: ESC in kernel output ignored\n", __func__);
465			break;	/* ignore the ESC */
466		}
467
468		if (edp->state == VT100_EMUL_STATE_STRING) {
469			/* might be a string end */
470			edp->state = VT100_EMUL_STATE_STRING_ESC;
471		} else {
472			/* XXX cancel current escape sequence */
473			edp->state = VT100_EMUL_STATE_ESC;
474		}
475		break;
476#if 0
477	case CSI: /* 8-bit */
478		/* XXX cancel current escape sequence */
479		edp->nargs = 0;
480		memset(edp->args, 0, sizeof (edp->args));
481		edp->modif1 = edp->modif2 = '\0';
482		edp->state = VT100_EMUL_STATE_CSI;
483		break;
484	case DCS: /* 8-bit */
485		/* XXX cancel current escape sequence */
486		edp->nargs = 0;
487		memset(edp->args, 0, sizeof (edp->args));
488		edp->state = VT100_EMUL_STATE_DCS;
489		break;
490	case ST: /* string end 8-bit */
491		/* XXX only in VT100_EMUL_STATE_STRING */
492		wsemul_vt100_handle_dcs(edp);
493		edp->state = VT100_EMUL_STATE_NORMAL;
494		break;
495#endif
496	case ASCII_LF:
497	case ASCII_VT:
498	case ASCII_FF:
499		wsemul_vt100_nextline(edp);
500		break;
501	}
502}
503
504static u_int
505wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
506{
507	struct vt100base_data *vd = &edp->bd;
508	int i;
509
510	switch (c) {
511	case '[': /* CSI */
512		vd->nargs = 0;
513		memset(vd->args, 0, sizeof (vd->args));
514		vd->modif1 = vd->modif2 = '\0';
515		return VT100_EMUL_STATE_CSI;
516	case '7': /* DECSC */
517		vd->flags |= VTFL_SAVEDCURS;
518		edp->savedcursor_row = vd->crow;
519		edp->savedcursor_col = vd->ccol;
520		edp->savedattr = vd->curattr;
521		edp->savedbkgdattr = vd->bkgdattr;
522		edp->savedattrflags = vd->attrflags;
523		edp->savedfgcol = vd->fgcol;
524		edp->savedbgcol = vd->bgcol;
525		for (i = 0; i < 4; i++)
526			edp->savedchartab_G[i] = edp->chartab_G[i];
527		edp->savedchartab0 = edp->chartab0;
528		edp->savedchartab1 = edp->chartab1;
529		break;
530	case '8': /* DECRC */
531		if ((vd->flags & VTFL_SAVEDCURS) == 0)
532			break;
533		vd->crow = edp->savedcursor_row;
534		vd->ccol = edp->savedcursor_col;
535		vd->curattr = edp->savedattr;
536		vd->bkgdattr = edp->savedbkgdattr;
537		vd->attrflags = edp->savedattrflags;
538		vd->fgcol = edp->savedfgcol;
539		vd->bgcol = edp->savedbgcol;
540		for (i = 0; i < 4; i++)
541			edp->chartab_G[i] = edp->savedchartab_G[i];
542		edp->chartab0 = edp->savedchartab0;
543		edp->chartab1 = edp->savedchartab1;
544		break;
545	case '=': /* DECKPAM application mode */
546		vd->flags |= VTFL_APPLKEYPAD;
547		break;
548	case '>': /* DECKPNM numeric mode */
549		vd->flags &= ~VTFL_APPLKEYPAD;
550		break;
551	case 'E': /* NEL */
552		vd->ccol = 0;
553		/* FALLTHRU */
554	case 'D': /* IND */
555		wsemul_vt100_nextline(edp);
556		break;
557	case 'H': /* HTS */
558		KASSERT(vd->tabs != 0);
559		vd->tabs[vd->ccol] = 1;
560		break;
561	case '~': /* LS1R */
562		edp->chartab1 = 1;
563		break;
564	case 'n': /* LS2 */
565		edp->chartab0 = 2;
566		break;
567	case '}': /* LS2R */
568		edp->chartab1 = 2;
569		break;
570	case 'o': /* LS3 */
571		edp->chartab0 = 3;
572		break;
573	case '|': /* LS3R */
574		edp->chartab1 = 3;
575		break;
576	case 'N': /* SS2 */
577		edp->sschartab = 2;
578		break;
579	case 'O': /* SS3 */
580		edp->sschartab = 3;
581		break;
582	case 'M': /* RI */
583		if (ROWS_ABOVE(vd) > 0) {
584			vd->crow--;
585			CHECK_DW(vd);
586			break;
587		}
588		wsemul_vt100_scrolldown(vd, 1);
589		break;
590	case 'P': /* DCS */
591		vd->nargs = 0;
592		memset(vd->args, 0, sizeof (vd->args));
593		return VT100_EMUL_STATE_DCS;
594	case 'c': /* RIS */
595		wsemul_vt100_reset(edp);
596		wsemul_vt100_ed(vd, 2);
597		vd->ccol = vd->crow = 0;
598		break;
599	case '(': case ')': case '*': case '+': /* SCS */
600		edp->designating = c - '(';
601		return VT100_EMUL_STATE_SCS94;
602	case '-': case '.': case '/': /* SCS */
603		edp->designating = c - '-' + 1;
604		return VT100_EMUL_STATE_SCS96;
605	case '#':
606		return VT100_EMUL_STATE_ESC_HASH;
607	case ' ': /* 7/8 bit */
608		return VT100_EMUL_STATE_ESC_SPC;
609	case ']': /* OSC operating system command */
610	case '^': /* PM privacy message */
611	case '_': /* APC application program command */
612		/* ignored */
613		return VT100_EMUL_STATE_STRING;
614	case '<': /* exit VT52 mode - ignored */
615		break;
616	default:
617#ifdef VT100_PRINTUNKNOWN
618		printf("ESC%c unknown\n", c);
619#endif
620		break;
621	}
622	return VT100_EMUL_STATE_NORMAL;
623}
624
625static u_int
626wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, u_char c)
627{
628	switch (c) {
629	case '%': /* probably DEC supplemental graphic */
630		return VT100_EMUL_STATE_SCS94_PERCENT;
631	case 'A': /* british / national */
632		edp->chartab_G[edp->designating] = edp->nrctab;
633		break;
634	case 'B': /* ASCII */
635		edp->chartab_G[edp->designating] = 0;
636		break;
637	case '<': /* user preferred supplemental */
638		/* XXX not really "user" preferred */
639		edp->chartab_G[edp->designating] = edp->isolatin1tab;
640		break;
641	case '0': /* DEC special graphic */
642		edp->chartab_G[edp->designating] = edp->decgraphtab;
643		break;
644	case '>': /* DEC tech */
645		edp->chartab_G[edp->designating] = edp->dectechtab;
646		break;
647	default:
648#ifdef VT100_PRINTUNKNOWN
649		printf("ESC%c%c unknown\n", edp->designating + '(', c);
650#endif
651		break;
652	}
653	return VT100_EMUL_STATE_NORMAL;
654}
655
656static u_int
657wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, u_char c)
658{
659	switch (c) {
660	case '5': /* DEC supplemental graphic */
661		/* XXX there are differences */
662		edp->chartab_G[edp->designating] = edp->isolatin1tab;
663		break;
664	default:
665#ifdef VT100_PRINTUNKNOWN
666		printf("ESC%c%%%c unknown\n", edp->designating + '(', c);
667#endif
668		break;
669	}
670	return VT100_EMUL_STATE_NORMAL;
671}
672
673static u_int
674wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp, u_char c)
675{
676	int nrc;
677
678	switch (c) {
679	case '%': /* probably portuguese */
680		return VT100_EMUL_STATE_SCS96_PERCENT;
681	case 'A': /* ISO-latin-1 supplemental */
682		edp->chartab_G[edp->designating] = edp->isolatin1tab;
683		break;
684	case '4': /* dutch */
685		nrc = 1;
686		goto setnrc;
687	case '5': case 'C': /* finnish */
688		nrc = 2;
689		goto setnrc;
690	case 'R': /* french */
691		nrc = 3;
692		goto setnrc;
693	case 'Q': /* french canadian */
694		nrc = 4;
695		goto setnrc;
696	case 'K': /* german */
697		nrc = 5;
698		goto setnrc;
699	case 'Y': /* italian */
700		nrc = 6;
701		goto setnrc;
702	case 'E': case '6': /* norwegian / danish */
703		nrc = 7;
704		goto setnrc;
705	case 'Z': /* spanish */
706		nrc = 9;
707		goto setnrc;
708	case '7': case 'H': /* swedish */
709		nrc = 10;
710		goto setnrc;
711	case '=': /* swiss */
712		nrc = 11;
713setnrc:
714		vt100_setnrc(edp, nrc); /* what table ??? */
715		break;
716	default:
717#ifdef VT100_PRINTUNKNOWN
718		printf("ESC%c%c unknown\n", edp->designating + '-' - 1, c);
719#endif
720		break;
721	}
722	return VT100_EMUL_STATE_NORMAL;
723}
724
725static u_int
726wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, u_char c)
727{
728	switch (c) {
729	case '6': /* portuguese */
730		vt100_setnrc(edp, 8);
731		break;
732	default:
733#ifdef VT100_PRINTUNKNOWN
734		printf("ESC%c%%%c unknown\n", edp->designating + '-', c);
735#endif
736		break;
737	}
738	return VT100_EMUL_STATE_NORMAL;
739}
740
741static u_int
742wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp,
743    u_char c)
744{
745	switch (c) {
746	case 'F': /* 7-bit controls */
747	case 'G': /* 8-bit controls */
748#ifdef VT100_PRINTNOTIMPL
749		printf("ESC<SPC>%c ignored\n", c);
750#endif
751		break;
752	default:
753#ifdef VT100_PRINTUNKNOWN
754		printf("ESC<SPC>%c unknown\n", c);
755#endif
756		break;
757	}
758	return VT100_EMUL_STATE_NORMAL;
759}
760
761static u_int
762wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp, u_char c)
763{
764	struct vt100base_data *vd = &edp->bd;
765
766	if (vd->dcstype && vd->dcspos < DCS_MAXLEN)
767		vd->dcsarg[vd->dcspos++] = c;
768	return VT100_EMUL_STATE_STRING;
769}
770
771static u_int
772wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, u_char c)
773{
774	struct vt100base_data *vd = &edp->bd;
775
776	if (c == '\\') { /* ST complete */
777		wsemul_vt100_handle_dcs(vd);
778		return VT100_EMUL_STATE_NORMAL;
779	} else
780		return VT100_EMUL_STATE_STRING;
781}
782
783static u_int
784wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, u_char c)
785{
786	struct vt100base_data *vd = &edp->bd;
787
788	switch (c) {
789	case '0': case '1': case '2': case '3': case '4':
790	case '5': case '6': case '7': case '8': case '9':
791		/* argument digit */
792		if (vd->nargs > VT100_EMUL_NARGS - 1)
793			break;
794		vd->args[vd->nargs] = (vd->args[vd->nargs] * 10) +
795		    (c - '0');
796		break;
797	case ';': /* argument terminator */
798		vd->nargs++;
799		break;
800	default:
801		vd->nargs++;
802		if (vd->nargs > VT100_EMUL_NARGS) {
803#ifdef VT100_DEBUG
804			printf("vt100: too many arguments\n");
805#endif
806			vd->nargs = VT100_EMUL_NARGS;
807		}
808		switch (c) {
809		case '$':
810			return VT100_EMUL_STATE_DCS_DOLLAR;
811		case '{': /* DECDLD soft charset */
812		case '!': /* DECRQUPSS user preferred supplemental set */
813			/* 'u' must follow - need another state */
814		case '|': /* DECUDK program F6..F20 */
815#ifdef VT100_PRINTNOTIMPL
816			printf("DCS%c ignored\n", c);
817#endif
818			break;
819		default:
820#ifdef VT100_PRINTUNKNOWN
821			printf("DCS%c (%d, %d) unknown\n", c, ARG(vd, 0), ARG(vd, 1));
822#endif
823			break;
824		}
825		return VT100_EMUL_STATE_STRING;
826	}
827
828	return VT100_EMUL_STATE_DCS;
829}
830
831static u_int
832wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, u_char c)
833{
834	struct vt100base_data *vd = &edp->bd;
835
836	switch (c) {
837	case 'p': /* DECRSTS terminal state restore */
838	case 'q': /* DECRQSS control function request */
839#ifdef VT100_PRINTNOTIMPL
840		printf("DCS$%c ignored\n", c);
841#endif
842		break;
843	case 't': /* DECRSPS restore presentation state */
844		switch (ARG(vd, 0)) {
845		case 0: /* error */
846			break;
847		case 1: /* cursor information restore */
848#ifdef VT100_PRINTNOTIMPL
849			printf("DCS1$t ignored\n");
850#endif
851			break;
852		case 2: /* tab stop restore */
853			vd->dcspos = 0;
854			vd->dcstype = DCSTYPE_TABRESTORE;
855			break;
856		default:
857#ifdef VT100_PRINTUNKNOWN
858			printf("DCS%d$t unknown\n", ARG(vd, 0));
859#endif
860			break;
861		}
862		break;
863	default:
864#ifdef VT100_PRINTUNKNOWN
865		printf("DCS$%c (%d, %d) unknown\n", c, ARG(vd, 0), ARG(vd, 1));
866#endif
867		break;
868	}
869	return VT100_EMUL_STATE_STRING;
870}
871
872static u_int
873wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
874{
875	struct vt100base_data *vd = &edp->bd;
876	int i, j;
877
878	switch (c) {
879	case '5': /*  DECSWL single width, single height */
880		if (vd->dw) {
881			for (i = 0; i < vd->ncols / 2; i++)
882				(*vd->emulops->copycols)(vd->emulcookie,
883							  vd->crow,
884							  2 * i, i, 1);
885			(*vd->emulops->erasecols)(vd->emulcookie, vd->crow,
886						   i, vd->ncols - i,
887						   vd->bkgdattr);
888			vd->dblwid[vd->crow] = 0;
889			vd->dw = 0;
890		}
891		break;
892	case '6': /*  DECDWL double width, single height */
893	case '3': /*  DECDHL double width, double height, top half */
894	case '4': /*  DECDHL double width, double height, bottom half */
895		if (!vd->dw) {
896			for (i = vd->ncols / 2 - 1; i >= 0; i--)
897				(*vd->emulops->copycols)(vd->emulcookie,
898							  vd->crow,
899							  i, 2 * i, 1);
900			for (i = 0; i < vd->ncols / 2; i++)
901				(*vd->emulops->erasecols)(vd->emulcookie,
902							   vd->crow,
903							   2 * i + 1, 1,
904							   vd->bkgdattr);
905			vd->dblwid[vd->crow] = 1;
906			vd->dw = 1;
907			if (vd->ccol > (vd->ncols >> 1) - 1)
908				vd->ccol = (vd->ncols >> 1) - 1;
909		}
910		break;
911	case '8': /* DECALN */
912		for (i = 0; i < vd->nrows; i++)
913			for (j = 0; j < vd->ncols; j++)
914				(*vd->emulops->putchar)(vd->emulcookie, i, j,
915							 'E', vd->curattr);
916		vd->ccol = 0;
917		vd->crow = 0;
918		break;
919	default:
920#ifdef VT100_PRINTUNKNOWN
921		printf("ESC#%c unknown\n", c);
922#endif
923		break;
924	}
925	return VT100_EMUL_STATE_NORMAL;
926}
927
928static u_int
929wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, u_char c)
930{
931	struct vt100base_data *vd = &edp->bd;
932
933	switch (c) {
934	case '0': case '1': case '2': case '3': case '4':
935	case '5': case '6': case '7': case '8': case '9':
936		/* argument digit */
937		if (vd->nargs > VT100_EMUL_NARGS - 1)
938			break;
939		vd->args[vd->nargs] = (vd->args[vd->nargs] * 10) +
940		    (c - '0');
941		break;
942	case ';': /* argument terminator */
943		vd->nargs++;
944		break;
945	case '?': /* DEC specific */
946	case '>': /* DA query */
947		vd->modif1 = c;
948		break;
949	case '!':
950	case '"':
951	case '$':
952	case '&':
953		vd->modif2 = c;
954		break;
955	default: /* end of escape sequence */
956		vd->nargs++;
957		if (vd->nargs > VT100_EMUL_NARGS) {
958#ifdef VT100_DEBUG
959			printf("vt100: too many arguments\n");
960#endif
961			vd->nargs = VT100_EMUL_NARGS;
962		}
963		wsemul_vt100_handle_csi(vd, c);
964		return VT100_EMUL_STATE_NORMAL;
965	}
966	return VT100_EMUL_STATE_CSI;
967}
968
969void
970wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
971{
972	struct wsemul_vt100_emuldata *edp = cookie;
973	struct vt100base_data *vd = &edp->bd;
974
975#ifdef DIAGNOSTIC
976	if (kernel && !edp->console)
977		panic("%s: kernel output, not console", __func__);
978#endif
979
980	if (vd->flags & VTFL_CURSORON)
981		(*vd->emulops->cursor)(vd->emulcookie, 0,
982					vd->crow, vd->ccol << vd->dw);
983	for (; count > 0; data++, count--) {
984		if ((*data & 0x7f) < 0x20) {
985			wsemul_vt100_output_c0c1(edp, *data, kernel);
986			continue;
987		}
988		if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) {
989			wsemul_vt100_output_normal(edp, *data, kernel);
990			continue;
991		}
992		int state = edp->state - 1;
993		KASSERT(state < __arraycount(vt100_output));
994		edp->state = vt100_output[state](edp, *data);
995	}
996	if (vd->flags & VTFL_CURSORON)
997		(*vd->emulops->cursor)(vd->emulcookie, 1,
998		    vd->crow, vd->ccol << vd->dw);
999}
1000
1001#ifdef WSDISPLAY_CUSTOM_OUTPUT
1002static void
1003wsemul_vt100_getmsgattrs(void *cookie, struct wsdisplay_msgattrs *ma)
1004{
1005	struct wsemul_vt100_emuldata *edp = cookie;
1006	struct vt100base_data *vd = &edp->bd;
1007
1008	*ma = vd->msgattrs;
1009}
1010
1011static void
1012wsemul_vt100_setmsgattrs(void *cookie, const struct wsscreen_descr *type,
1013                         const struct wsdisplay_msgattrs *ma)
1014{
1015	int error;
1016	long tmp;
1017	struct wsemul_vt100_emuldata *edp = cookie;
1018	struct vt100base_data *vd = &edp->bd;
1019
1020	vd->msgattrs = *ma;
1021	if (type->capabilities & WSSCREEN_WSCOLORS) {
1022		vd->msgattrs.default_attrs |= WSATTR_WSCOLORS;
1023		vd->msgattrs.kernel_attrs |= WSATTR_WSCOLORS;
1024	} else {
1025		vd->msgattrs.default_bg = vd->msgattrs.kernel_bg = 0;
1026		vd->msgattrs.default_fg = vd->msgattrs.kernel_fg = 0;
1027	}
1028
1029	error = (*vd->emulops->allocattr)(vd->emulcookie,
1030	                                   vd->msgattrs.default_fg,
1031					   vd->msgattrs.default_bg,
1032	                                   vd->msgattrs.default_attrs,
1033	                                   &tmp);
1034#ifndef VT100_DEBUG
1035	__USE(error);
1036#else
1037	if (error)
1038		printf("vt100: failed to allocate attribute for default "
1039		       "messages\n");
1040	else
1041#endif
1042	{
1043		if (vd->curattr == vd->defattr) {
1044			vd->bkgdattr = vd->curattr = tmp;
1045			vd->attrflags = vd->msgattrs.default_attrs;
1046			vd->bgcol = vd->msgattrs.default_bg;
1047			vd->fgcol = vd->msgattrs.default_fg;
1048		} else {
1049			edp->savedbkgdattr = edp->savedattr = tmp;
1050			edp->savedattrflags = vd->msgattrs.default_attrs;
1051			edp->savedbgcol = vd->msgattrs.default_bg;
1052			edp->savedfgcol = vd->msgattrs.default_fg;
1053		}
1054		if (vd->emulops->replaceattr != NULL)
1055			(*vd->emulops->replaceattr)(vd->emulcookie,
1056			                             vd->defattr, tmp);
1057		vd->defattr = tmp;
1058	}
1059
1060	error = (*vd->emulops->allocattr)(vd->emulcookie,
1061	                                   vd->msgattrs.kernel_fg,
1062					   vd->msgattrs.kernel_bg,
1063	                                   vd->msgattrs.kernel_attrs,
1064	                                   &tmp);
1065#ifdef VT100_DEBUG
1066	if (error)
1067		printf("vt100: failed to allocate attribute for kernel "
1068		       "messages\n");
1069	else
1070#endif
1071	{
1072		if (vd->emulops->replaceattr != NULL)
1073			(*vd->emulops->replaceattr)(vd->emulcookie,
1074			                             edp->kernattr, tmp);
1075		edp->kernattr = tmp;
1076	}
1077}
1078#endif /* WSDISPLAY_CUSTOM_OUTPUT */
1079