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