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