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