xref: /netbsd/sys/dev/wscons/wsemul_vt100.c (revision bf9ec67e)
1 /* $NetBSD: wsemul_vt100.c,v 1.17 2002/01/12 16:41:02 tsutsui 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  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed for the NetBSD Project
18  *	by Matthias Drochner.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: wsemul_vt100.c,v 1.17 2002/01/12 16:41:02 tsutsui Exp $");
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/time.h>
41 #include <sys/malloc.h>
42 #include <sys/fcntl.h>
43 
44 #include <dev/wscons/wsconsio.h>
45 #include <dev/wscons/wsdisplayvar.h>
46 #include <dev/wscons/wsemulvar.h>
47 #include <dev/wscons/wsemul_vt100var.h>
48 #include <dev/wscons/ascii.h>
49 
50 #include "opt_wskernattr.h"
51 
52 void	*wsemul_vt100_cnattach(const struct wsscreen_descr *, void *,
53 			       int, int, long);
54 void	*wsemul_vt100_attach(int console, const struct wsscreen_descr *,
55 			     void *, int, int, void *, long);
56 void	wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int);
57 void	wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp);
58 void	wsemul_vt100_resetop(void *, enum wsemul_resetops);
59 
60 const struct wsemul_ops wsemul_vt100_ops = {
61 	"vt100",
62 	wsemul_vt100_cnattach,
63 	wsemul_vt100_attach,
64 	wsemul_vt100_output,
65 	wsemul_vt100_translate,
66 	wsemul_vt100_detach,
67 	wsemul_vt100_resetop
68 };
69 
70 struct wsemul_vt100_emuldata wsemul_vt100_console_emuldata;
71 
72 static void wsemul_vt100_init(struct wsemul_vt100_emuldata *,
73 			      const struct wsscreen_descr *,
74 			      void *, int, int, long);
75 
76 static void wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *,
77 				       u_char, int);
78 static void wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *,
79 				     u_char, int);
80 typedef u_int vt100_handler(struct wsemul_vt100_emuldata *, u_char);
81 static vt100_handler
82 wsemul_vt100_output_esc,
83 wsemul_vt100_output_csi,
84 wsemul_vt100_output_scs94,
85 wsemul_vt100_output_scs94_percent,
86 wsemul_vt100_output_scs96,
87 wsemul_vt100_output_scs96_percent,
88 wsemul_vt100_output_esc_hash,
89 wsemul_vt100_output_esc_spc,
90 wsemul_vt100_output_string,
91 wsemul_vt100_output_string_esc,
92 wsemul_vt100_output_dcs,
93 wsemul_vt100_output_dcs_dollar;
94 
95 #define	VT100_EMUL_STATE_NORMAL		0	/* normal processing */
96 #define	VT100_EMUL_STATE_ESC		1	/* got ESC */
97 #define	VT100_EMUL_STATE_CSI		2	/* got CSI (ESC[) */
98 #define	VT100_EMUL_STATE_SCS94		3	/* got ESC{()*+} */
99 #define	VT100_EMUL_STATE_SCS94_PERCENT	4	/* got ESC{()*+}% */
100 #define	VT100_EMUL_STATE_SCS96		5	/* got ESC{-./} */
101 #define	VT100_EMUL_STATE_SCS96_PERCENT	6	/* got ESC{-./}% */
102 #define	VT100_EMUL_STATE_ESC_HASH	7	/* got ESC# */
103 #define	VT100_EMUL_STATE_ESC_SPC	8	/* got ESC<SPC> */
104 #define	VT100_EMUL_STATE_STRING		9	/* waiting for ST (ESC\) */
105 #define	VT100_EMUL_STATE_STRING_ESC	10	/* waiting for ST, got ESC */
106 #define	VT100_EMUL_STATE_DCS		11	/* got DCS (ESC P) */
107 #define	VT100_EMUL_STATE_DCS_DOLLAR	12	/* got DCS<p>$ */
108 
109 vt100_handler *vt100_output[] = {
110 	wsemul_vt100_output_esc,
111 	wsemul_vt100_output_csi,
112 	wsemul_vt100_output_scs94,
113 	wsemul_vt100_output_scs94_percent,
114 	wsemul_vt100_output_scs96,
115 	wsemul_vt100_output_scs96_percent,
116 	wsemul_vt100_output_esc_hash,
117 	wsemul_vt100_output_esc_spc,
118 	wsemul_vt100_output_string,
119 	wsemul_vt100_output_string_esc,
120 	wsemul_vt100_output_dcs,
121 	wsemul_vt100_output_dcs_dollar,
122 };
123 
124 static void
125 wsemul_vt100_init(struct wsemul_vt100_emuldata *edp,
126 	const struct wsscreen_descr *type, void *cookie, int ccol, int crow,
127 	long defattr)
128 {
129 	edp->emulops = type->textops;
130 	edp->emulcookie = cookie;
131 	edp->scrcapabilities = type->capabilities;
132 	edp->nrows = type->nrows;
133 	edp->ncols = type->ncols;
134 	edp->crow = crow;
135 	edp->ccol = ccol;
136 	edp->defattr = defattr;
137 }
138 
139 void *
140 wsemul_vt100_cnattach(const struct wsscreen_descr *type, void *cookie,
141 	int ccol, int crow, long defattr)
142 {
143 	struct wsemul_vt100_emuldata *edp;
144 #if defined(WS_KERNEL_FG) || defined(WS_KERNEL_BG) || \
145   defined(WS_KERNEL_COLATTR) || defined(WS_KERNEL_MONOATTR)
146 	int res;
147 #endif
148 
149 	edp = &wsemul_vt100_console_emuldata;
150 	wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
151 #ifdef DIAGNOSTIC
152 	edp->console = 1;
153 #endif
154 	edp->cbcookie = NULL;
155 
156 #if defined(WS_KERNEL_FG) || defined(WS_KERNEL_BG) || \
157   defined(WS_KERNEL_COLATTR) || defined(WS_KERNEL_MONOATTR)
158 #ifndef WS_KERNEL_FG
159 #define WS_KERNEL_FG WSCOL_WHITE
160 #endif
161 #ifndef WS_KERNEL_BG
162 #define WS_KERNEL_BG WSCOL_BLACK
163 #endif
164 #ifndef WS_KERNEL_COLATTR
165 #define WS_KERNEL_COLATTR 0
166 #endif
167 #ifndef WS_KERNEL_MONOATTR
168 #define WS_KERNEL_MONOATTR 0
169 #endif
170 	if (type->capabilities & WSSCREEN_WSCOLORS)
171 		res = (*edp->emulops->alloc_attr)(cookie,
172 					    WS_KERNEL_FG, WS_KERNEL_BG,
173 					    WS_KERNEL_COLATTR | WSATTR_WSCOLORS,
174 					    &edp->kernattr);
175 	else
176 		res = (*edp->emulops->alloc_attr)(cookie, 0, 0,
177 					    WS_KERNEL_MONOATTR,
178 					    &edp->kernattr);
179 	if (res)
180 #endif
181 	edp->kernattr = defattr;
182 
183 	edp->tabs = 0;
184 	edp->dblwid = 0;
185 	edp->dw = 0;
186 	edp->dcsarg = 0;
187 	edp->isolatin1tab = edp->decgraphtab = edp->dectechtab = 0;
188 	edp->nrctab = 0;
189 	wsemul_vt100_reset(edp);
190 	return (edp);
191 }
192 
193 void *
194 wsemul_vt100_attach(int console, const struct wsscreen_descr *type,
195 	void *cookie, int ccol, int crow, void *cbcookie, long defattr)
196 {
197 	struct wsemul_vt100_emuldata *edp;
198 
199 	if (console) {
200 		edp = &wsemul_vt100_console_emuldata;
201 #ifdef DIAGNOSTIC
202 		KASSERT(edp->console == 1);
203 #endif
204 	} else {
205 		edp = malloc(sizeof *edp, M_DEVBUF, M_WAITOK);
206 		wsemul_vt100_init(edp, type, cookie, ccol, crow, defattr);
207 #ifdef DIAGNOSTIC
208 		edp->console = 0;
209 #endif
210 	}
211 	edp->cbcookie = cbcookie;
212 
213 	edp->tabs = malloc(edp->ncols, M_DEVBUF, M_NOWAIT);
214 	edp->dblwid = malloc(edp->nrows, M_DEVBUF, M_NOWAIT|M_ZERO);
215 	edp->dw = 0;
216 	edp->dcsarg = malloc(DCS_MAXLEN, M_DEVBUF, M_NOWAIT);
217 	edp->isolatin1tab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
218 	edp->decgraphtab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
219 	edp->dectechtab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
220 	edp->nrctab = malloc(128 * sizeof(int), M_DEVBUF, M_NOWAIT);
221 	vt100_initchartables(edp);
222 	wsemul_vt100_reset(edp);
223 	return (edp);
224 }
225 
226 void
227 wsemul_vt100_detach(void *cookie, u_int *crowp, u_int *ccolp)
228 {
229 	struct wsemul_vt100_emuldata *edp = cookie;
230 
231 	*crowp = edp->crow;
232 	*ccolp = edp->ccol;
233 #define f(ptr) if (ptr) {free(ptr, M_DEVBUF); ptr = 0;}
234 	f(edp->tabs)
235 	f(edp->dblwid)
236 	f(edp->dcsarg)
237 	f(edp->isolatin1tab)
238 	f(edp->decgraphtab)
239 	f(edp->dectechtab)
240 	f(edp->nrctab)
241 #undef f
242 	if (edp != &wsemul_vt100_console_emuldata)
243 		free(edp, M_DEVBUF);
244 }
245 
246 void
247 wsemul_vt100_resetop(void *cookie, enum wsemul_resetops op)
248 {
249 	struct wsemul_vt100_emuldata *edp = cookie;
250 
251 	switch (op) {
252 	case WSEMUL_RESET:
253 		wsemul_vt100_reset(edp);
254 		break;
255 	case WSEMUL_SYNCFONT:
256 		vt100_initchartables(edp);
257 		break;
258 	case WSEMUL_CLEARSCREEN:
259 		wsemul_vt100_ed(edp, 2);
260 		edp->ccol = edp->crow = 0;
261 		(*edp->emulops->cursor)(edp->emulcookie,
262 					edp->flags & VTFL_CURSORON, 0, 0);
263 		break;
264 	default:
265 		break;
266 	}
267 }
268 
269 void
270 wsemul_vt100_reset(struct wsemul_vt100_emuldata *edp)
271 {
272 	int i;
273 
274 	edp->state = VT100_EMUL_STATE_NORMAL;
275 	edp->flags = VTFL_DECAWM | VTFL_CURSORON;
276 	edp->bkgdattr = edp->curattr = edp->defattr;
277 	edp->attrflags = 0;
278 	edp->fgcol = WSCOL_WHITE;
279 	edp->bgcol = WSCOL_BLACK;
280 	edp->scrreg_startrow = 0;
281 	edp->scrreg_nrows = edp->nrows;
282 	if (edp->tabs) {
283 		memset(edp->tabs, 0, edp->ncols);
284 		for (i = 8; i < edp->ncols; i += 8)
285 			edp->tabs[i] = 1;
286 	}
287 	edp->dcspos = 0;
288 	edp->dcstype = 0;
289 	edp->chartab_G[0] = 0;
290 	edp->chartab_G[1] = edp->nrctab; /* ??? */
291 	edp->chartab_G[2] = edp->isolatin1tab;
292 	edp->chartab_G[3] = edp->isolatin1tab;
293 	edp->chartab0 = 0;
294 	edp->chartab1 = 2;
295 	edp->sschartab = 0;
296 }
297 
298 /*
299  * now all the state machine bits
300  */
301 
302 static void
303 wsemul_vt100_output_normal(struct wsemul_vt100_emuldata *edp, u_char c,
304 	int kernel)
305 {
306 	u_int *ct, dc;
307 
308 	if ((edp->flags & (VTFL_LASTCHAR | VTFL_DECAWM)) ==
309 	    (VTFL_LASTCHAR | VTFL_DECAWM)) {
310 		if (ROWS_BELOW > 0) {
311 			edp->crow++;
312 			CHECK_DW;
313 		} else
314 			wsemul_vt100_scrollup(edp, 1);
315 		edp->ccol = 0;
316 		edp->flags &= ~VTFL_LASTCHAR;
317 	}
318 
319 	if (c & 0x80) {
320 		c &= 0x7f;
321 		ct = edp->chartab_G[edp->chartab1];
322 	} else {
323 		if (edp->sschartab) {
324 			ct = edp->chartab_G[edp->sschartab];
325 			edp->sschartab = 0;
326 		} else
327 			ct = edp->chartab_G[edp->chartab0];
328 	}
329 	dc = (ct ? ct[c] : c);
330 
331 	if ((edp->flags & VTFL_INSERTMODE) && COLS_LEFT)
332 		COPYCOLS(edp->ccol, edp->ccol + 1, COLS_LEFT);
333 
334 	(*edp->emulops->putchar)(edp->emulcookie, edp->crow,
335 				 edp->ccol << edp->dw, dc,
336 				 kernel ? edp->kernattr : edp->curattr);
337 
338 	if (COLS_LEFT)
339 		edp->ccol++;
340 	else
341 		edp->flags |= VTFL_LASTCHAR;
342 }
343 
344 static void
345 wsemul_vt100_output_c0c1(struct wsemul_vt100_emuldata *edp, u_char c,
346 	int kernel)
347 {
348 	u_int n;
349 
350 	switch (c) {
351 	    case ASCII_NUL:
352 	    default:
353 		/* ignore */
354 		break;
355 	    case ASCII_BEL:
356 		wsdisplay_emulbell(edp->cbcookie);
357 		break;
358 	    case ASCII_BS:
359 		if (edp->ccol > 0) {
360 			edp->ccol--;
361 			edp->flags &= ~VTFL_LASTCHAR;
362 		}
363 		break;
364 	    case ASCII_CR:
365 		edp->ccol = 0;
366 		edp->flags &= ~VTFL_LASTCHAR;
367 		break;
368 	    case ASCII_HT:
369 		if (edp->tabs) {
370 			if (!COLS_LEFT)
371 				break;
372 			for (n = edp->ccol + 1; n < NCOLS - 1; n++)
373 				if (edp->tabs[n])
374 					break;
375 		} else {
376 			n = edp->ccol + min(8 - (edp->ccol & 7), COLS_LEFT);
377 		}
378 		edp->ccol = n;
379 		break;
380 	    case ASCII_SO: /* LS1 */
381 		edp->chartab0 = 1;
382 		break;
383 	    case ASCII_SI: /* LS0 */
384 		edp->chartab0 = 0;
385 		break;
386 	    case ASCII_ESC:
387 		if (kernel) {
388 			printf("wsemul_vt100_output_c0c1: ESC in kernel output ignored\n");
389 			break;	/* ignore the ESC */
390 		}
391 
392 		if (edp->state == VT100_EMUL_STATE_STRING) {
393 			/* might be a string end */
394 			edp->state = VT100_EMUL_STATE_STRING_ESC;
395 		} else {
396 			/* XXX cancel current escape sequence */
397 			edp->state = VT100_EMUL_STATE_ESC;
398 		}
399 		break;
400 #if 0
401 	    case CSI: /* 8-bit */
402 		/* XXX cancel current escape sequence */
403 		edp->nargs = 0;
404 		memset(edp->args, 0, sizeof (edp->args));
405 		edp->modif1 = edp->modif2 = '\0';
406 		edp->state = VT100_EMUL_STATE_CSI;
407 		break;
408 	    case DCS: /* 8-bit */
409 		/* XXX cancel current escape sequence */
410 		edp->nargs = 0;
411 		memset(edp->args, 0, sizeof (edp->args));
412 		edp->state = VT100_EMUL_STATE_DCS;
413 		break;
414 	    case ST: /* string end 8-bit */
415 		/* XXX only in VT100_EMUL_STATE_STRING */
416 		wsemul_vt100_handle_dcs(edp);
417 		return (VT100_EMUL_STATE_NORMAL);
418 #endif
419 	    case ASCII_LF:
420 	    case ASCII_VT:
421 	    case ASCII_FF:
422 		if (ROWS_BELOW > 0) {
423 			edp->crow++;
424 			CHECK_DW;
425 		} else
426 			wsemul_vt100_scrollup(edp, 1);
427 		break;
428 	}
429 }
430 
431 static u_int
432 wsemul_vt100_output_esc(struct wsemul_vt100_emuldata *edp, u_char c)
433 {
434 	u_int newstate = VT100_EMUL_STATE_NORMAL;
435 	int i;
436 
437 	switch (c) {
438 	    case '[': /* CSI */
439 		edp->nargs = 0;
440 		memset(edp->args, 0, sizeof (edp->args));
441 		edp->modif1 = edp->modif2 = '\0';
442 		newstate = VT100_EMUL_STATE_CSI;
443 		break;
444 	    case '7': /* DECSC */
445 		edp->savedcursor_row = edp->crow;
446 		edp->savedcursor_col = edp->ccol;
447 		edp->savedattr = edp->curattr;
448 		edp->savedbkgdattr = edp->bkgdattr;
449 		edp->savedattrflags = edp->attrflags;
450 		edp->savedfgcol = edp->fgcol;
451 		edp->savedbgcol = edp->bgcol;
452 		for (i = 0; i < 4; i++)
453 			edp->savedchartab_G[i] = edp->chartab_G[i];
454 		edp->savedchartab0 = edp->chartab0;
455 		edp->savedchartab1 = edp->chartab1;
456 		break;
457 	    case '8': /* DECRC */
458 		edp->crow = edp->savedcursor_row;
459 		edp->ccol = edp->savedcursor_col;
460 		edp->curattr = edp->savedattr;
461 		edp->bkgdattr = edp->savedbkgdattr;
462 		edp->attrflags = edp->savedattrflags;
463 		edp->fgcol = edp->savedfgcol;
464 		edp->bgcol = edp->savedbgcol;
465 		for (i = 0; i < 4; i++)
466 			edp->chartab_G[i] = edp->savedchartab_G[i];
467 		edp->chartab0 = edp->savedchartab0;
468 		edp->chartab1 = edp->savedchartab1;
469 		break;
470 	    case '=': /* DECKPAM application mode */
471 		edp->flags |= VTFL_APPLKEYPAD;
472 		break;
473 	    case '>': /* DECKPNM numeric mode */
474 		edp->flags &= ~VTFL_APPLKEYPAD;
475 		break;
476 	    case 'E': /* NEL */
477 		edp->ccol = 0;
478 		/* FALLTHRU */
479 	    case 'D': /* IND */
480 		if (ROWS_BELOW > 0) {
481 			edp->crow++;
482 			CHECK_DW;
483 			break;
484 		}
485 		wsemul_vt100_scrollup(edp, 1);
486 		break;
487 	    case 'H': /* HTS */
488 		KASSERT(edp->tabs != 0);
489 		edp->tabs[edp->ccol] = 1;
490 		break;
491 	    case '~': /* LS1R */
492 		edp->chartab1 = 1;
493 		break;
494 	    case 'n': /* LS2 */
495 		edp->chartab0 = 2;
496 		break;
497 	    case '}': /* LS2R */
498 		edp->chartab1 = 2;
499 		break;
500 	    case 'o': /* LS3 */
501 		edp->chartab0 = 3;
502 		break;
503 	    case '|': /* LS3R */
504 		edp->chartab1 = 3;
505 		break;
506 	    case 'N': /* SS2 */
507 		edp->sschartab = 2;
508 		break;
509 	    case 'O': /* SS3 */
510 		edp->sschartab = 3;
511 		break;
512 	    case 'M': /* RI */
513 		if (ROWS_ABOVE > 0) {
514 			edp->crow--;
515 			CHECK_DW;
516 			break;
517 		}
518 		wsemul_vt100_scrolldown(edp, 1);
519 		break;
520 	    case 'P': /* DCS */
521 		edp->nargs = 0;
522 		memset(edp->args, 0, sizeof (edp->args));
523 		newstate = VT100_EMUL_STATE_DCS;
524 		break;
525 	    case 'c': /* RIS */
526 		wsemul_vt100_reset(edp);
527 		wsemul_vt100_ed(edp, 2);
528 		edp->ccol = edp->crow = 0;
529 		break;
530 	    case '(': case ')': case '*': case '+': /* SCS */
531 		edp->designating = c - '(';
532 		newstate = VT100_EMUL_STATE_SCS94;
533 		break;
534 	    case '-': case '.': case '/': /* SCS */
535 		edp->designating = c - '-' + 1;
536 		newstate = VT100_EMUL_STATE_SCS96;
537 		break;
538 	    case '#':
539 		newstate = VT100_EMUL_STATE_ESC_HASH;
540 		break;
541 	    case ' ': /* 7/8 bit */
542 		newstate = VT100_EMUL_STATE_ESC_SPC;
543 		break;
544 	    case ']': /* OSC operating system command */
545 	    case '^': /* PM privacy message */
546 	    case '_': /* APC application program command */
547 		/* ignored */
548 		newstate = VT100_EMUL_STATE_STRING;
549 		break;
550 	    case '<': /* exit VT52 mode - ignored */
551 		break;
552 	    default:
553 #ifdef VT100_PRINTUNKNOWN
554 		printf("ESC%c unknown\n", c);
555 #endif
556 		break;
557 	}
558 
559 	return (newstate);
560 }
561 
562 static u_int
563 wsemul_vt100_output_scs94(struct wsemul_vt100_emuldata *edp, u_char c)
564 {
565 	u_int newstate = VT100_EMUL_STATE_NORMAL;
566 
567 	switch (c) {
568 	    case '%': /* probably DEC supplemental graphic */
569 		newstate = VT100_EMUL_STATE_SCS94_PERCENT;
570 		break;
571 	    case 'A': /* british / national */
572 		edp->chartab_G[edp->designating] = edp->nrctab;
573 		break;
574 	    case 'B': /* ASCII */
575 		edp->chartab_G[edp->designating] = 0;
576 		break;
577 	    case '<': /* user preferred supplemental */
578 		/* XXX not really "user" preferred */
579 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
580 		break;
581 	    case '0': /* DEC special graphic */
582 		edp->chartab_G[edp->designating] = edp->decgraphtab;
583 		break;
584 	    case '>': /* DEC tech */
585 		edp->chartab_G[edp->designating] = edp->dectechtab;
586 		break;
587 	    default:
588 #ifdef VT100_PRINTUNKNOWN
589 		printf("ESC%c%c unknown\n", edp->designating + '(', c);
590 #endif
591 		break;
592 	}
593 	return (newstate);
594 }
595 
596 static u_int
597 wsemul_vt100_output_scs94_percent(struct wsemul_vt100_emuldata *edp, u_char c)
598 {
599 	switch (c) {
600 	    case '5': /* DEC supplemental graphic */
601 		/* XXX there are differences */
602 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
603 		break;
604 	    default:
605 #ifdef VT100_PRINTUNKNOWN
606 		printf("ESC%c%%%c unknown\n", edp->designating + '(', c);
607 #endif
608 		break;
609 	}
610 	return (VT100_EMUL_STATE_NORMAL);
611 }
612 
613 static u_int
614 wsemul_vt100_output_scs96(struct wsemul_vt100_emuldata *edp, u_char c)
615 {
616 	u_int newstate = VT100_EMUL_STATE_NORMAL;
617 	int nrc;
618 
619 	switch (c) {
620 	    case '%': /* probably portugese */
621 		newstate = VT100_EMUL_STATE_SCS96_PERCENT;
622 		break;
623 	    case 'A': /* ISO-latin-1 supplemental */
624 		edp->chartab_G[edp->designating] = edp->isolatin1tab;
625 		break;
626 	    case '4': /* dutch */
627 		nrc = 1;
628 		goto setnrc;
629 	    case '5': case 'C': /* finnish */
630 		nrc = 2;
631 		goto setnrc;
632 	    case 'R': /* french */
633 		nrc = 3;
634 		goto setnrc;
635 	    case 'Q': /* french canadian */
636 		nrc = 4;
637 		goto setnrc;
638 	    case 'K': /* german */
639 		nrc = 5;
640 		goto setnrc;
641 	    case 'Y': /* italian */
642 		nrc = 6;
643 		goto setnrc;
644 	    case 'E': case '6': /* norwegian / danish */
645 		nrc = 7;
646 		goto setnrc;
647 	    case 'Z': /* spanish */
648 		nrc = 9;
649 		goto setnrc;
650 	    case '7': case 'H': /* swedish */
651 		nrc = 10;
652 		goto setnrc;
653 	    case '=': /* swiss */
654 		nrc = 11;
655 setnrc:
656 		vt100_setnrc(edp, nrc); /* what table ??? */
657 		break;
658 	    default:
659 #ifdef VT100_PRINTUNKNOWN
660 		printf("ESC%c%c unknown\n", edp->designating + '-' - 1, c);
661 #endif
662 		break;
663 	}
664 	return (newstate);
665 }
666 
667 static u_int
668 wsemul_vt100_output_scs96_percent(struct wsemul_vt100_emuldata *edp, u_char c)
669 {
670 	switch (c) {
671 	    case '6': /* portugese */
672 		vt100_setnrc(edp, 8);
673 		break;
674 	    default:
675 #ifdef VT100_PRINTUNKNOWN
676 		printf("ESC%c%%%c unknown\n", edp->designating + '-', c);
677 #endif
678 		break;
679 	}
680 	return (VT100_EMUL_STATE_NORMAL);
681 }
682 
683 static u_int
684 wsemul_vt100_output_esc_spc(struct wsemul_vt100_emuldata *edp, u_char c)
685 {
686 	switch (c) {
687 	    case 'F': /* 7-bit controls */
688 	    case 'G': /* 8-bit controls */
689 #ifdef VT100_PRINTNOTIMPL
690 		printf("ESC<SPC>%c ignored\n", c);
691 #endif
692 		break;
693 	    default:
694 #ifdef VT100_PRINTUNKNOWN
695 		printf("ESC<SPC>%c unknown\n", c);
696 #endif
697 		break;
698 	}
699 	return (VT100_EMUL_STATE_NORMAL);
700 }
701 
702 static u_int
703 wsemul_vt100_output_string(struct wsemul_vt100_emuldata *edp, u_char c)
704 {
705 	if (edp->dcstype && edp->dcspos < DCS_MAXLEN)
706 		edp->dcsarg[edp->dcspos++] = c;
707 	return (VT100_EMUL_STATE_STRING);
708 }
709 
710 static u_int
711 wsemul_vt100_output_string_esc(struct wsemul_vt100_emuldata *edp, u_char c)
712 {
713 	if (c == '\\') { /* ST complete */
714 		wsemul_vt100_handle_dcs(edp);
715 		return (VT100_EMUL_STATE_NORMAL);
716 	} else
717 		return (VT100_EMUL_STATE_STRING);
718 }
719 
720 static u_int
721 wsemul_vt100_output_dcs(struct wsemul_vt100_emuldata *edp, u_char c)
722 {
723 	u_int newstate = VT100_EMUL_STATE_DCS;
724 
725 	switch (c) {
726 	    case '0': case '1': case '2': case '3': case '4':
727 	    case '5': case '6': case '7': case '8': case '9':
728 		/* argument digit */
729 		if (edp->nargs > VT100_EMUL_NARGS - 1)
730 			break;
731 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
732 		    (c - '0');
733 		break;
734 	    case ';': /* argument terminator */
735 		edp->nargs++;
736 		break;
737 	    default:
738 		edp->nargs++;
739 		if (edp->nargs > VT100_EMUL_NARGS) {
740 #ifdef VT100_DEBUG
741 			printf("vt100: too many arguments\n");
742 #endif
743 			edp->nargs = VT100_EMUL_NARGS;
744 		}
745 		newstate = VT100_EMUL_STATE_STRING;
746 		switch (c) {
747 		    case '$':
748 			newstate = VT100_EMUL_STATE_DCS_DOLLAR;
749 			break;
750 		    case '{': /* DECDLD soft charset */
751 		    case '!': /* DECRQUPSS user preferred supplemental set */
752 			/* 'u' must follow - need another state */
753 		    case '|': /* DECUDK program F6..F20 */
754 #ifdef VT100_PRINTNOTIMPL
755 			printf("DCS%c ignored\n", c);
756 #endif
757 			break;
758 		    default:
759 #ifdef VT100_PRINTUNKNOWN
760 			printf("DCS%c (%d, %d) unknown\n", c, ARG(0), ARG(1));
761 #endif
762 			break;
763 		}
764 	}
765 
766 	return (newstate);
767 }
768 
769 static u_int
770 wsemul_vt100_output_dcs_dollar(struct wsemul_vt100_emuldata *edp, u_char c)
771 {
772 	switch (c) {
773 	    case 'p': /* DECRSTS terminal state restore */
774 	    case 'q': /* DECRQSS control function request */
775 #ifdef VT100_PRINTNOTIMPL
776 		printf("DCS$%c ignored\n", c);
777 #endif
778 		break;
779 	    case 't': /* DECRSPS restore presentation state */
780 		switch (ARG(0)) {
781 		    case 0: /* error */
782 			break;
783 		    case 1: /* cursor information restore */
784 #ifdef VT100_PRINTNOTIMPL
785 			printf("DCS1$t ignored\n");
786 #endif
787 			break;
788 		    case 2: /* tab stop restore */
789 			edp->dcspos = 0;
790 			edp->dcstype = DCSTYPE_TABRESTORE;
791 			break;
792 		    default:
793 #ifdef VT100_PRINTUNKNOWN
794 			printf("DCS%d$t unknown\n", ARG(0));
795 #endif
796 			break;
797 		}
798 		break;
799 	    default:
800 #ifdef VT100_PRINTUNKNOWN
801 		printf("DCS$%c (%d, %d) unknown\n", c, ARG(0), ARG(1));
802 #endif
803 		break;
804 	}
805 	return (VT100_EMUL_STATE_STRING);
806 }
807 
808 static u_int
809 wsemul_vt100_output_esc_hash(struct wsemul_vt100_emuldata *edp, u_char c)
810 {
811 	int i;
812 
813 	switch (c) {
814 	    case '5': /*  DECSWL single width, single height */
815 		if (edp->dw) {
816 			for (i = 0; i < edp->ncols / 2; i++)
817 				(*edp->emulops->copycols)(edp->emulcookie,
818 							  edp->crow,
819 							  2 * i, i, 1);
820 			(*edp->emulops->erasecols)(edp->emulcookie, edp->crow,
821 						   i, edp->ncols - i,
822 						   edp->bkgdattr);
823 			edp->dblwid[edp->crow] = 0;
824 			edp->dw = 0;
825 		}
826 		break;
827 	    case '6': /*  DECDWL double width, single height */
828 	    case '3': /*  DECDHL double width, double height, top half */
829 	    case '4': /*  DECDHL double width, double height, bottom half */
830 		if (!edp->dw) {
831 			for (i = edp->ncols / 2 - 1; i >= 0; i--)
832 				(*edp->emulops->copycols)(edp->emulcookie,
833 							  edp->crow,
834 							  i, 2 * i, 1);
835 			for (i = 0; i < edp->ncols / 2; i++)
836 				(*edp->emulops->erasecols)(edp->emulcookie,
837 							   edp->crow,
838 							   2 * i + 1, 1,
839 							   edp->bkgdattr);
840 			edp->dblwid[edp->crow] = 1;
841 			edp->dw = 1;
842 			if (edp->ccol > (edp->ncols >> 1) - 1)
843 				edp->ccol = (edp->ncols >> 1) - 1;
844 		}
845 		break;
846 	    case '8': { /* DECALN */
847 		int i, j;
848 		for (i = 0; i < edp->nrows; i++)
849 			for (j = 0; j < edp->ncols; j++)
850 				(*edp->emulops->putchar)(edp->emulcookie, i, j,
851 							 'E', edp->curattr);
852 		}
853 		edp->ccol = 0;
854 		edp->crow = 0;
855 		break;
856 	    default:
857 #ifdef VT100_PRINTUNKNOWN
858 		printf("ESC#%c unknown\n", c);
859 #endif
860 		break;
861 	}
862 	return (VT100_EMUL_STATE_NORMAL);
863 }
864 
865 static u_int
866 wsemul_vt100_output_csi(struct wsemul_vt100_emuldata *edp, u_char c)
867 {
868 	u_int newstate = VT100_EMUL_STATE_CSI;
869 
870 	switch (c) {
871 	    case '0': case '1': case '2': case '3': case '4':
872 	    case '5': case '6': case '7': case '8': case '9':
873 		/* argument digit */
874 		if (edp->nargs > VT100_EMUL_NARGS - 1)
875 			break;
876 		edp->args[edp->nargs] = (edp->args[edp->nargs] * 10) +
877 		    (c - '0');
878 		break;
879 	    case ';': /* argument terminator */
880 		edp->nargs++;
881 		break;
882 	    case '?': /* DEC specific */
883 	    case '>': /* DA query */
884 		edp->modif1 = c;
885 		break;
886 	    case '!':
887 	    case '"':
888 	    case '$':
889 	    case '&':
890 		edp->modif2 = c;
891 		break;
892 	    default: /* end of escape sequence */
893 		edp->nargs++;
894 		if (edp->nargs > VT100_EMUL_NARGS) {
895 #ifdef VT100_DEBUG
896 			printf("vt100: too many arguments\n");
897 #endif
898 			edp->nargs = VT100_EMUL_NARGS;
899 		}
900 		wsemul_vt100_handle_csi(edp, c);
901 		newstate = VT100_EMUL_STATE_NORMAL;
902 		break;
903 	}
904 	return (newstate);
905 }
906 
907 void
908 wsemul_vt100_output(void *cookie, const u_char *data, u_int count, int kernel)
909 {
910 	struct wsemul_vt100_emuldata *edp = cookie;
911 
912 #ifdef DIAGNOSTIC
913 	if (kernel && !edp->console)
914 		panic("wsemul_vt100_output: kernel output, not console");
915 #endif
916 
917 	if (edp->flags & VTFL_CURSORON)
918 		(*edp->emulops->cursor)(edp->emulcookie, 0,
919 					edp->crow, edp->ccol << edp->dw);
920 	for (; count > 0; data++, count--) {
921 		if ((*data & 0x7f) < 0x20) {
922 			wsemul_vt100_output_c0c1(edp, *data, kernel);
923 			continue;
924 		}
925 		if (edp->state == VT100_EMUL_STATE_NORMAL || kernel) {
926 			wsemul_vt100_output_normal(edp, *data, kernel);
927 			continue;
928 		}
929 #ifdef DIAGNOSTIC
930 		if (edp->state > sizeof(vt100_output) / sizeof(vt100_output[0]))
931 			panic("wsemul_vt100: invalid state %d\n", edp->state);
932 #endif
933 		edp->state = vt100_output[edp->state - 1](edp, *data);
934 	}
935 	if (edp->flags & VTFL_CURSORON)
936 		(*edp->emulops->cursor)(edp->emulcookie, 1,
937 					edp->crow, edp->ccol << edp->dw);
938 }
939