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