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