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