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