xref: /netbsd-src/sys/dev/wscons/wsemul_vt100.c (revision a04395531661c5e8d314125d5ae77d4cbedd5d73)
1 /*	$NetBSD: wsemul_vt100.c,v 1.48 2021/06/07 16:37:34 christos 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.48 2021/06/07 16:37:34 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 
48 void	*wsemul_vt100_cnattach(const struct wsscreen_descr *, void *,
49 			       int, int, long);
50 void	*wsemul_vt100_attach(int console, const struct wsscreen_descr *,
51 			     void *, int, int, void *, long);
52 void	wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int);
53 void	wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp);
54 void	wsemul_vt100_resetop(void *, enum wsemul_resetops);
55 #ifdef WSDISPLAY_CUSTOM_OUTPUT
56 static void wsemul_vt100_getmsgattrs(void *, struct wsdisplay_msgattrs *);
57 static void wsemul_vt100_setmsgattrs(void *, const struct wsscreen_descr *,
58                                      const struct wsdisplay_msgattrs *);
59 #endif /* WSDISPLAY_CUSTOM_OUTPUT */
60 static void wsemul_vt100_resize(void *, const struct wsscreen_descr *);
61 
62 const 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 
80 struct wsemul_vt100_emuldata wsemul_vt100_console_emuldata;
81 
82 static void wsemul_vt100_init(struct wsemul_vt100_emuldata *,
83 			      const struct wsscreen_descr *,
84 			      void *, int, int, long);
85 
86 static void wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *,
87 				       u_char, int);
88 static void wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *,
89 				     u_char, int);
90 static void wsemul_vt100_nextline(struct wsemul_vt100_emuldata *);
91 typedef u_int vt100_handler(struct wsemul_vt100_emuldata *, u_char);
92 
93 static vt100_handler
94 wsemul_vt100_output_esc,
95 wsemul_vt100_output_csi,
96 wsemul_vt100_output_scs94,
97 wsemul_vt100_output_scs94_percent,
98 wsemul_vt100_output_scs96,
99 wsemul_vt100_output_scs96_percent,
100 wsemul_vt100_output_esc_hash,
101 wsemul_vt100_output_esc_spc,
102 wsemul_vt100_output_string,
103 wsemul_vt100_output_string_esc,
104 wsemul_vt100_output_dcs,
105 wsemul_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 
121 vt100_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 
136 static void
137 wsemul_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 
211 void *
212 wsemul_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 
236 void *
237 wsemul_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 
269 void
270 wsemul_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 
290 static void
291 wsemul_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 
301 void
302 wsemul_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 
325 void
326 wsemul_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  */
364 static void
365 wsemul_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 
380 static void
381 wsemul_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 
419 static void
420 wsemul_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 
510 static u_int
511 wsemul_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 
631 static u_int
632 wsemul_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 
662 static u_int
663 wsemul_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 
679 static u_int
680 wsemul_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;
719 setnrc:
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 
731 static u_int
732 wsemul_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 
747 static u_int
748 wsemul_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 
767 static u_int
768 wsemul_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 
777 static u_int
778 wsemul_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 
789 static u_int
790 wsemul_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 
837 static u_int
838 wsemul_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 
878 static u_int
879 wsemul_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 
934 static u_int
935 wsemul_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 
975 void
976 wsemul_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
1008 static void
1009 wsemul_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 
1017 static void
1018 wsemul_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