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