/*
 * Copyright (c) 1992, 1993, 1996
 *	Berkeley Software Design, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Berkeley Software
 *	Design, Inc.
 *
 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	BSDI tty.c,v 2.4 1996/04/08 22:03:27 prb Exp
 */

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <stdio.h>
#include <termios.h>
#include <limits.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include "machine/pccons.h"
#include <sys/mman.h>
#include <ctype.h> 
#include "doscmd.h"
#include "mouse.h"
#include "video.h"
#include "font.h"

static struct termios save = { 0 };
static struct termios raw = { 0 };
static int flags = 0;
static int mode = -1;
#define	vmem		((u_short *)0xB8000)
static int blink = 1;
int flipdelete = 1;		/* Flip meaning of delete and backspace */
extern int capture_fd;
static u_short break_code = 0x00;
static u_short scan_code = 0x00;

static Display *dpy = 0;
static Window win;
static XFontStruct *font;
static unsigned long black;
static unsigned long white;
static unsigned long pixels[16];
static char *color_names[16] = {
    "black",
    "medium blue",
    "olive drab",
    "turquoise3",
    "red1",
    "maroon",
    "goldenrod4",
    "gray80",
    "gray50",
    "sky blue",
    "spring green",
    "PaleTurquoise2",
    "pink",
    "violet red",
    "yellow1",
    "white",
};

static int FW, FH, FD;
static GC gc;
static GC cgc;
static int xfd;
int kbd_fd = -1;
int kbd_read = 0;
static int vattr = 0x0700;

static int width = 80;
static int height = 25;
static struct termios tty_cook, tty_raw;

typedef struct TextLine {
	u_short	*data;
	u_char	max_length;	/* Not used, but here for future use */
	u_char	changed:1;
} TextLine;
static TextLine *lines;

/*
 * 0040:0050 is a list of 16 bytes, 2 for each page, which contains
 * the column and row of each page
 */
#define	row BIOSDATA[0x51]
#define	col BIOSDATA[0x50]

u_char	*VREG;

inline SetVREGCur()
{
	int cp = row * width + col;
	VREG[MVC_CurHigh] = cp >> 8;
	VREG[MVC_CurLow] = cp & 0xff;
}

/*
 * 0040:0060 contains the start and end of the cursor
 */
#define	curs_end BIOSDATA[0x60]
#define	curs_start BIOSDATA[0x61]

#define	video_pate	BISODATA[0x62]

#define	IBMFONT	"ibmpc"

char *xfont = 0;

void video_async_event();
void debug_event();
int video_event();
void tty_cooked();
void video_update();
unsigned char video_inb(int);
unsigned char inb_port60(int);
void video_outb(int, unsigned char);
void kbd_event(int fd, struct sigcontext *sc);
u_short read_raw_kbd(int fd, u_short *code);

#define	PEEKSZ	16

#define	K_NEXT		*(u_short *)0x41a
#define	K_FREE		*(u_short *)0x41c
#define	K_BUFSTARTP	*(u_short *)0x480
#define	K_BUFENDP	*(u_short *)0x482
#define	K_BUFSTART	((u_short *)(0x400 + *(u_short *)0x480))
#define	K_BUFEND	((u_short *)(0x400 + *(u_short *)0x482))

#define	K1_STATUS	BIOSDATA[0x17]
#define	K1_RSHIFT	0x01
#define	K1_LSHIFT	0x02
#define	K1_SHIFT	0x03
#define	K1_CTRL		0x04
#define	K1_ALT		0x08
#define	K1_SLOCK	0x10		/* Active */
#define	K1_NLOCK	0x20		/* Active */
#define	K1_CLOCK	0x40		/* Active */
#define	K1_INSERT	0x80		/* Active */

#define	K2_STATUS	BIOSDATA[0x18]
#define	K2_LCTRL	0x01
#define	K2_LALT		0x02
#define	K2_SYSREQ	0x04
#define	K2_PAUSE	0x08
#define	K2_SLOCK	0x10		/* Actually held down */
#define	K2_NLOCK	0x20		/* Actually held down */
#define	K2_CLOCK	0x40		/* Actually held down */
#define	K2_INSERT	0x80		/* Actually held down */

#define	K3_STATUS	BIOSDATA[0x96]
#define	K3_E1		0x01		/* Last code read was e1 */
#define	K3_E2		0x02		/* Last code read was e2 */
#define	K3_RCTRL	0x04
#define	K3_RALT		0x08
#define	K3_ENHANCED	0x10
#define	K3_FORCENLOCK	0x20
#define	K3_TWOBYTE	0x40		/* last code was first of 2 */
#define	K3_READID	0x80		/* read ID in progress */

#define	K4_STATUS	BIOSDATA[0x97]
#define	K4_SLOCK_LED	0x01
#define	K4_NLOCK_LED	0x02
#define	K4_CLOCK_LED	0x04
#define	K4_ACK		0x10		/* ACK recieved from keyboard */
#define	K4_RESEND	0x20		/* RESEND recieved from keyboard */
#define	K4_LED		0x40		/* LED update in progress */
#define	K4_ERROR	0x80

struct VideoSavePointerTable {
	u_short	video_parameter_tabel[2];
	u_short	parameter_dynamic_save_area[2];		/* Not used */
	u_short	alphanumeric_character_set_override[2];	/* Not used */
	u_short	graphics_character_set_override[2];	/* Not used */
	u_short	secondary_save_pointer_table[2];	/* Not used */
	u_short	mbz[4];
};

struct SecondaryVideoSavePointerTable {
	u_short	length;
	u_short	display_combination_code_table[2];
	u_short	alphanumeric_character_set_override[2];	/* Not used */
	u_short	user_palette_profile_table[2];		/* Not used */
	u_short	mbz[6];
};

int KbdEmpty();
void KbdWrite(u_short code);
void KbdRepl(u_short code);
u_short KbdRead();
u_short KbdPeek();

int redirect0;
int redirect1;
int redirect2;

static void
Failure()
{
        fprintf(stderr, "X Connection shutdown\n");
	quit(1);
}

struct VideoSavePointerTable *vsp;
struct SecondaryVideoSavePointerTable *svsp;

#include "vparams.h"

static void
console_denit(void *arg)
{
    int fd = *(int *)arg;

    if (ioctl(fd, CONSOLE_X_MODE_OFF, 0))
	perror("CONSOLE_X_MODE_OFF");
    if (tcsetattr(fd, TCSANOW, &tty_cook))
	perror("tcsetattr");
}

void
_kbd_event(int fd, struct sigcontext *sc)
{
	printf("_kbd_event: fd=%d\n", fd);
	kbd_read = 1;
}

void
console_init()
{
    int fd;
    caddr_t addr;

    if ((fd = open("/dev/vga", 2)) < 0) {
	perror("/dev/vga");
	quit(1);
    }
    addr = mmap((caddr_t)0xA0000, 5 * 64 * 1024,
		PROT_EXEC | PROT_READ | PROT_WRITE,
		MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED,
		fd, 0);
    if (addr != (caddr_t)0xA0000) {
	perror("mmap");
	quit(1);
    }

#if 0
    addr = mmap((caddr_t)0x100000 - 0x1000, 0x1000,
		PROT_EXEC | PROT_READ | PROT_WRITE,
		MAP_FILE | MAP_FIXED | MAP_INHERIT | MAP_SHARED,
		fd, 0);
    if (addr != (caddr_t)(0x100000 - 0x1000)) {
	perror("mmap");
	quit(1);
    }
#endif

    if ((fd = open("/dev/console", 2)) < 0) {
	perror("/dev/console");
	quit(1);
    }

    fd = squirrel_fd(fd);
    kbd_fd = fd;
    if (ioctl(fd, CONSOLE_X_MODE_ON, 0)) {
	perror("CONSOLE_X_MODE_ON");
	quit(1);    
    }

    call_on_quit(console_denit, &kbd_fd);

    if (fcntl(fd, F_SETFL, O_NDELAY|O_ASYNC) < 0) {
	perror("fcntl");
	quit(1);    
    }
    if (tcgetattr(fd, &tty_cook)) {
	perror("tcgetattr");
	quit(1);    
    }
    tty_raw = tty_cook;
    cfmakeraw(&tty_raw);
    if (tcsetattr(fd, TCSANOW, &tty_raw)) {
	perror("tcsetattr");
	quit(1);    
    }

#if 0
    _RegisterIO(0, debug_event, 0, Failure);
    _RegisterIO(fd, kbd_event, fd, Failure);
#endif
    _RegisterIO(fd, _kbd_event, fd, Failure);
}

void
video_setborder(int color)
{
	_BlockIO();
	XSetWindowBackground(dpy, win, pixels[color & 0xf]);
	_UnblockIO();
}
void
video_blink(int mode)
{
	blink = mode;
}

static int show = 1;

setgc(u_short attr)
{
	XGCValues v;
	if (blink && !show && (attr & 0x8000))
		v.foreground = pixels[(attr >> 12) & 0x07];
	else
		v.foreground = pixels[(attr >> 8) & 0x0f];

	v.background = pixels[(attr >> 12) & (blink ? 0x07 : 0x0f)];
#if 0
    	if (v.foreground == v.background) {
		v.foreground = pixels[15];
		v.background = pixels[0];
	}
#endif
	XChangeGC(dpy, gc, GCForeground|GCBackground, &v);
}

void
video_update(struct sigcontext *sc)
{
    	static int or = -1;
    	static int oc = -1;

	static int icnt = 4;

	static char buf[256];
	int r, c;
	int attr = vmem[0] & 0xff00;
	XGCValues v;

	if (kbd_read)
	    kbd_event(kbd_fd, sc);

    	if (--icnt == 0) {

	    icnt = 4;

	    lpt_poll();		/* Handle timeout on lpt code */

	    if (xmode) {
		wakeup_poll();	/* Wake up anyone waiting on kbd poll */

		show ^= 1;

		setgc(attr);

		for (r = 0; r < height; ++r) {
			int cc = 0;

			if (!lines[r].changed) {
			    if ((r == or || r == row) && (or != row || oc != col))
				lines[r].changed = 1;
			    else {
				for (c = 0; c < width; ++c) {
				    if (lines[r].data[c] != vmem[r * width + c]) {
					lines[r].changed = 1;
					break;
				    }
				    if (blink && lines[r].data[c] & 0x8000) {
					lines[r].changed = 1;
					break;
				    }
				}
			    }
			}

			if (!lines[r].changed)
			    continue;

			reset_poll();
			lines[r].changed = 0;
			memcpy(lines[r].data,
			       &vmem[r * width], sizeof(u_short) * width);

			for (c = 0; c < width; ++c) {
				int cv = vmem[r * width + c];
				if ((cv & 0xff00) != attr) {
				    if (cc < c)
					XDrawImageString(dpy, win, gc,
							 2 + cc * FW,
							 2 + (r + 1) * FH,
							 buf + cc, c - cc);
				    cc = c;
				    attr = cv  & 0xff00;
				    setgc(attr);
				}
				buf[c] = (cv & 0xff) ? cv & 0xff : ' ';
			}
			if (cc < c) {
				XDrawImageString(dpy, win, gc,
						 2 + cc * FW,
						 2 + (r + 1) * FH,
						 buf + cc, c - cc);
			}
		}
		or = row;
		oc = col;

		if (curs_start <= curs_end && curs_end <= FH &&
		    show && row < height && col < width) {
			int start, end;

			attr = vmem[row * width + col] & 0xff00;
			v.foreground = pixels[(attr >> 8) & 0x0f] ^
				       pixels[(attr >> 12) & (blink ? 0x07 : 0x0f)];
			if (v.foreground) {
				v.function = GXxor;
			} else {
				v.foreground = pixels[7];
				v.function = GXcopy;
			}
			XChangeGC(dpy, cgc, GCForeground | GCFunction, &v);
			start = curs_start * FH / 8;
			end = curs_end * FH / 8;
			XFillRectangle(dpy, win, cgc,
				       2 + col * FW,
				       2 + row * FH + start + FD,
				       FW, end + 1 - start);
		}

		if (mouse_status.installed && mouse_status.show) {
		    c = mouse_status.x / mouse_status.hmickey;
		    r = mouse_status.y / mouse_status.vmickey;

		    lines[r].changed = 1;
		    attr = vmem[r * width + c] & 0xff00;
		    v.foreground = pixels[(attr >> 8) & 0x0f] ^
				   pixels[(attr >> 12) & 0x0f];
		    if (v.foreground) {
			    v.function = GXxor;
		    } else {
			    v.foreground = pixels[7];
			    v.function = GXcopy;
		    }
		    XChangeGC(dpy, cgc, GCForeground | GCFunction, &v);
		    XFillRectangle(dpy, win, cgc,
				   2 + c * FW,
				   2 + r * FH + 2,
				   FW, FH);
		}
		
		XFlush(dpy);
	    }
    	}

    	if (!booting) {
	    *(u_long *)&BIOSDATA[0x6c] += 1;    /* Timer ticks since midnight... */
	    while (*(u_long *)&BIOSDATA[0x6c] >= 24*60*6*182) {
		*(u_long *)&BIOSDATA[0x6c] -= 24*60*6*182;
		BIOSDATA[0x70]++;		    /* BIOSDATA[0x70] # times past mn */
	    }
    	}
}

static u_short Ascii2Scan[] = {
 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
 0x000e, 0x000f, 0xffff, 0xffff, 0xffff, 0x001c, 0xffff, 0xffff,
 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
 0xffff, 0xffff, 0xffff, 0x0001, 0xffff, 0xffff, 0xffff, 0xffff,
 0x0039, 0x0102, 0x0128, 0x0104, 0x0105, 0x0106, 0x0108, 0x0028,
 0x010a, 0x010b, 0x0109, 0x010d, 0x0033, 0x000c, 0x0034, 0x0035,
 0x000b, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
 0x0009, 0x000a, 0x0127, 0x0027, 0x0133, 0x000d, 0x0134, 0x0135,
 0x0103, 0x011e, 0x0130, 0x012e, 0x0120, 0x0112, 0x0121, 0x0122,
 0x0123, 0x0117, 0x0124, 0x0125, 0x0126, 0x0132, 0x0131, 0x0118,
 0x0119, 0x0110, 0x0113, 0x011f, 0x0114, 0x0116, 0x012f, 0x0111,
 0x012d, 0x0115, 0x012c, 0x001a, 0x002b, 0x001b, 0x0107, 0x010c,
 0x0029, 0x001e, 0x0030, 0x002e, 0x0020, 0x0012, 0x0021, 0x0022,
 0x0023, 0x0017, 0x0024, 0x0025, 0x0026, 0x0032, 0x0031, 0x0018,
 0x0019, 0x0010, 0x0013, 0x001f, 0x0014, 0x0016, 0x002f, 0x0011,
 0x002d, 0x0015, 0x002c, 0x011a, 0x012b, 0x011b, 0x0129, 0xffff,
};

struct {
    u_short	base;
    u_short	shift;
    u_short	ctrl;
    u_short	alt;
} ScanCodes[] = {
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key  0 */
    {	0x011b, 0x011b, 0x011b, 0xffff }, /* key  1 - Escape key */
    {	0x0231, 0x0221, 0xffff, 0x7800 }, /* key  2 - '1' */
    {	0x0332, 0x0340, 0x0300, 0x7900 }, /* key  3 - '2' - special handling */
    {	0x0433, 0x0423, 0xffff, 0x7a00 }, /* key  4 - '3' */
    {	0x0534, 0x0524, 0xffff, 0x7b00 }, /* key  5 - '4' */
    {	0x0635, 0x0625, 0xffff, 0x7c00 }, /* key  6 - '5' */
    {	0x0736, 0x075e, 0x071e, 0x7d00 }, /* key  7 - '6' */
    {	0x0837, 0x0826, 0xffff, 0x7e00 }, /* key  8 - '7' */
    {	0x0938, 0x092a, 0xffff, 0x7f00 }, /* key  9 - '8' */
    {	0x0a39, 0x0a28, 0xffff, 0x8000 }, /* key 10 - '9' */
    {	0x0b30, 0x0b29, 0xffff, 0x8100 }, /* key 11 - '0' */
    {	0x0c2d, 0x0c5f, 0x0c1f, 0x8200 }, /* key 12 - '-' */
    {	0x0d3d, 0x0d2b, 0xffff, 0x8300 }, /* key 13 - '=' */
    {	0x0e08, 0x0e08, 0x0e7f, 0xffff }, /* key 14 - backspace */
    {	0x0f09, 0xffff, 0xffff, 0xffff }, /* key 15 - tab */
    {	0x1071, 0x1051, 0x1011, 0x1000 }, /* key 16 - 'Q' */
    {	0x1177, 0x1157, 0x1117, 0x1100 }, /* key 17 - 'W' */
    {	0x1265, 0x1245, 0x1205, 0x1200 }, /* key 18 - 'E' */
    {	0x1372, 0x1352, 0x1312, 0x1300 }, /* key 19 - 'R' */
    {	0x1474, 0x1454, 0x1414, 0x1400 }, /* key 20 - 'T' */
    {	0x1579, 0x1559, 0x1519, 0x1500 }, /* key 21 - 'Y' */
    {	0x1675, 0x1655, 0x1615, 0x1600 }, /* key 22 - 'U' */
    {	0x1769, 0x1749, 0x1709, 0x1700 }, /* key 23 - 'I' */
    {	0x186f, 0x184f, 0x180f, 0x1800 }, /* key 24 - 'O' */
    {	0x1970, 0x1950, 0x1910, 0x1900 }, /* key 25 - 'P' */
    {	0x1a5b, 0x1a7b, 0x1a1b, 0xffff }, /* key 26 - '[' */
    {	0x1b5d, 0x1b7d, 0x1b1d, 0xffff }, /* key 27 - ']' */
    {	0x1c0d, 0x1c0d, 0x1c0a, 0xffff }, /* key 28 - CR */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 29 - control */
    {	0x1e61, 0x1e41, 0x1e01, 0x1e00 }, /* key 30 - 'A' */
    {	0x1f73, 0x1f53, 0x1f13, 0x1f00 }, /* key 31 - 'S' */
    {	0x2064, 0x2044, 0x2004, 0x2000 }, /* key 32 - 'D' */
    {	0x2166, 0x2146, 0x2106, 0x2100 }, /* key 33 - 'F' */
    {	0x2267, 0x2247, 0x2207, 0x2200 }, /* key 34 - 'G' */
    {	0x2368, 0x2348, 0x2308, 0x2300 }, /* key 35 - 'H' */
    {	0x246a, 0x244a, 0x240a, 0x2400 }, /* key 36 - 'J' */
    {	0x256b, 0x254b, 0x250b, 0x2500 }, /* key 37 - 'K' */
    {	0x266c, 0x264c, 0x260c, 0x2600 }, /* key 38 - 'L' */
    {	0x273b, 0x273a, 0xffff, 0xffff }, /* key 39 - ';' */
    {	0x2827, 0x2822, 0xffff, 0xffff }, /* key 40 - ''' */
    {	0x2960, 0x297e, 0xffff, 0xffff }, /* key 41 - '`' */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 42 - left shift */
    {	0x2b5c, 0x2b7c, 0x2b1c, 0xffff }, /* key 43 - '' */
    {	0x2c7a, 0x2c5a, 0x2c1a, 0x2c00 }, /* key 44 - 'Z' */
    {	0x2d78, 0x2d58, 0x2d18, 0x2d00 }, /* key 45 - 'X' */
    {	0x2e63, 0x2e43, 0x2e03, 0x2e00 }, /* key 46 - 'C' */
    {	0x2f76, 0x2f56, 0x2f16, 0x2f00 }, /* key 47 - 'V' */
    {	0x3062, 0x3042, 0x3002, 0x3000 }, /* key 48 - 'B' */
    {	0x316e, 0x314e, 0x310e, 0x3100 }, /* key 49 - 'N' */
    {	0x326d, 0x324d, 0x320d, 0x3200 }, /* key 50 - 'M' */
    {	0x332c, 0x333c, 0xffff, 0xffff }, /* key 51 - ',' */
    {	0x342e, 0x343e, 0xffff, 0xffff }, /* key 52 - '.' */
    {	0x352f, 0x353f, 0xffff, 0xffff }, /* key 53 - '/' */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 54 - right shift - */
    {	0x372a, 0xffff, 0x3772, 0xffff }, /* key 55 - prt-scr - */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 56 - Alt - */
    {	0x3920, 0x3920, 0x3920, 0x3920 }, /* key 57 - space bar */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 58 - caps-lock -  */
    {	0x3b00, 0x5400, 0x5e00, 0x6800 }, /* key 59 - F1 */
    {	0x3c00, 0x5500, 0x5f00, 0x6900 }, /* key 60 - F2 */
    {	0x3d00, 0x5600, 0x6000, 0x6a00 }, /* key 61 - F3 */
    {	0x3e00, 0x5700, 0x6100, 0x6b00 }, /* key 62 - F4 */
    {	0x3f00, 0x5800, 0x6200, 0x6c00 }, /* key 63 - F5 */
    {	0x4000, 0x5900, 0x6300, 0x6d00 }, /* key 64 - F6 */
    {	0x4100, 0x5a00, 0x6400, 0x6e00 }, /* key 65 - F7 */
    {	0x4200, 0x5b00, 0x6500, 0x6f00 }, /* key 66 - F8 */
    {	0x4300, 0x5c00, 0x6600, 0x7000 }, /* key 67 - F9 */
    {	0x4400, 0x5d00, 0x6700, 0x7100 }, /* key 68 - F10 */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 69 - num-lock - */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 70 - scroll-lock -  */
    {	0x4700, 0x4737, 0x7700, 0xffff }, /* key 71 - home */
    {	0x4800, 0x4838, 0xffff, 0xffff }, /* key 72 - cursor up */
    {	0x4900, 0x4939, 0x8400, 0xffff }, /* key 73 - page up */
    {	0x2d00, 0x4a2d, 0xffff, 0xffff }, /* key 74 - minus sign */
    {	0x4b00, 0x4b34, 0x7300, 0xffff }, /* key 75 - cursor left */
    {	0xffff, 0x4c35, 0xffff, 0xffff }, /* key 76 - center key */
    {	0x4d00, 0x4d36, 0x7400, 0xffff }, /* key 77 - cursor right */
    {	0x2b00, 0x4e2b, 0xffff, 0xffff }, /* key 78 - plus sign */
    {	0x4f00, 0x4f31, 0x7500, 0xffff }, /* key 79 - end */
    {	0x5000, 0x5032, 0xffff, 0xffff }, /* key 80 - cursor down */
    {	0x5100, 0x5133, 0x7600, 0xffff }, /* key 81 - page down */
    {	0x5200, 0x5230, 0xffff, 0xffff }, /* key 82 - insert */
    {	0x5300, 0x532e, 0xffff, 0xffff }, /* key 83 - delete */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 84 - sys key */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 85 */
    {	0xffff, 0xffff, 0xffff, 0xffff }, /* key 86 */
    {	0x8500, 0x5787, 0x8900, 0x8b00 }, /* key 87 - F11 */
    {	0x8600, 0x5888, 0x8a00, 0x8c00 }, /* key 88 - F12 */
};

void
debug_event(int fd, struct sigcontext *sc)
{
    static char ibuf[1024];
    static char icnt = 0;
    static u_short ds = 0;
    static u_short di = 0;
    static u_short cnt = 16 * 8;
    char *ep;

    int r;

    r = read(0, ibuf + icnt, sizeof(ibuf) - icnt);
    if (r <= 0)
	return;

    icnt += r;

    ibuf[icnt] = 0;
    while (ep = strchr(ibuf, '\n')) {
    	int ac;
    	char *_av[16];
    	char **av;

    	*ep++ = 0;
    	ac = ParseBuffer(ibuf, av = _av, 16);

    	if (ac > 0) {
    	    if (!strcasecmp(av[0], "dump")) {
    	    	if (ac > 1) {
		    char *c;
		    if (c = strchr(av[1], ':')) {
			ds = strtol(av[1], 0, 16);
			di = strtol(c+1, 0, 16);
		    } else
			di = strtol(av[1], 0, 16);
    	    	}
		if (ac > 2)
		    cnt = strtol(av[2], 0, 0);
	    	cnt = (cnt + 0xf) & ~0xf;
		if (cnt == 0)
		    cnt = 0x10;
	    	di &= ~0xf;

    	    	for (r = 0; r < cnt; r += 0x10, di = (di + 0x10) & 0xffff) {
		    int i;
		    u_char *ap = (u_char *)(((u_long)ds << 4) + di);

		    printf("%04x:%04x:", ds, di);
		    for (i = 0; i < 8; ++i)
			printf(" %02x", ap[i]);
    	    	    printf(" ");
		    for (i = 8; i < 16; ++i)
			printf(" %02x", ap[i]);
    	    	    printf(": ");
		    for (i = 0; i < 8; ++i)
			printf("%c",(ap[i] < ' ' || ap[i] > '~') ? '.' : ap[i]);
    	    	    printf(" ");
		    for (i = 8; i < 16; ++i)
			printf("%c",(ap[i] < ' ' || ap[i] > '~') ? '.' : ap[i]);
		    printf("\n");
    	    	}
    	    } else if (!strcasecmp(av[0], "dis")) {
		int r;
		u_char *ap = (u_char *)(((u_long)ds << 4) + di);

    	    	if (ac > 1) {
		    char *c;
		    if (c = strchr(av[1], ':')) {
			ds = strtol(av[1], 0, 16);
			di = strtol(c+1, 0, 16);
		    } else
			di = strtol(av[1], 0, 16);
    	    	}
		if (ac > 2)
		    cnt = strtol(av[2], 0, 0);

    	    	for (r = 0; r < cnt; ++r) {
    	    	    char buf[16];
		    int c = i386dis(ds, di, ap, buf, 0);
		    printf("%04x:%04x %s\n", ds, di, buf);
		    di += c;
		    ap += c;
    	    	}
    	    } else if (!strcasecmp(av[0], "regs")) {
		dump_regs(sc);
    	    } else if (!strcasecmp(av[0], "force")) {
		char *p = av[1];

    	    	while (p = *++av) {
		    while (*p) {
			if (*p >= ' ' && *p <= '~')
			    KbdWrite(ScanCodes[Ascii2Scan[*p] & 0xff].base);
			++p;
    	    	    }
    	    	}
		KbdWrite(ScanCodes[28].base);
	    } else if (!strcasecmp(av[0], "bell")) {
		XBell(dpy, 0);
                XFlush(dpy);
    	    } else {
		fprintf(stderr, "%s: unknown command\n", av[0]);
    	    }
    	}

    	if (ep < ibuf + icnt) {
    	    char *f = ep;
    	    char *t = ibuf;
    	    icnt -= ep - ibuf;
    	    while (icnt--)
	    	*t++ = *f++;
    	} else
	    icnt = 0;
	ibuf[icnt] = 0;
    }
}

unsigned char
inb_port60(int port)
{       
    int r = break_code;
    break_code = 0;
    scan_code = 0xffff;
    return(r);
}       

void
kbd_event(int fd, struct sigcontext *sc)
{
    kbd_read = 0;

    printf("kbd_event: fd=%d\n", fd);
    if ((break_code = read_raw_kbd(fd, &scan_code)) != 0xffff)
	hardint(0x09);
}

void
int09(struct sigcontext *sc)
{
    if (raw_kbd) {
	if (scan_code != 0xffff) {
	    KbdWrite(scan_code);
	    break_code = 0;
	    scan_code = 0xffff;
#if 0
	    kbd_event(kbd_fd, sc);
#endif
	}
    }
}

u_short
read_raw_kbd(int fd, u_short *code)
{
    unsigned char c;
    unsigned char oldled = K4_STATUS & 0x7;

    *code = 0xffff;

    if (read(fd, &c, 1) == 1) {
    	if (c == 0xe0) {
	    K3_STATUS |= K3_TWOBYTE;
	    return(c);
    	}
    	switch (c) {
    	case 29:	/* Control */
	    K1_STATUS |= K1_CTRL;
	    if (K3_STATUS & K3_TWOBYTE)
		K3_STATUS |= K3_RCTRL;
	    else
		K2_STATUS |= K2_LCTRL;
	    break;
    	case 29 | 0x80:	/* Control */
	    K1_STATUS &= ~K1_CTRL;
	    if (K3_STATUS & K3_TWOBYTE)
		K3_STATUS &= ~K3_RCTRL;
	    else
		K2_STATUS &= ~K2_LCTRL;
	    break;

	case 42:	/* left shift */
	    K1_STATUS |= K1_LSHIFT;
	    break;
	case 42 | 0x80:	/* left shift */
	    K1_STATUS &= ~K1_LSHIFT;
	    break;

	case 54:	/* right shift */
	    K1_STATUS |= K1_RSHIFT;
	    break;
	case 54 | 0x80:	/* right shift */
	    K1_STATUS &= ~K1_RSHIFT;
	    break;

	case 56:	/* Alt */
	    K1_STATUS |= K1_ALT;
	    if (K3_STATUS & K3_TWOBYTE)
		K3_STATUS |= K3_RALT;
	    else
		K2_STATUS |= K2_LALT;
	    break;
	case 56 | 0x80:	/* Alt */
	    K1_STATUS &= ~K1_ALT;
	    if (K3_STATUS & K3_TWOBYTE)
		K3_STATUS &= ~K3_RALT;
	    else
		K2_STATUS &= ~K2_LALT;
	    break;

	case 58:	/* caps-lock */
	    K1_STATUS ^= K1_CLOCK;
    	    if (K1_STATUS & K1_CLOCK)
	    	K4_STATUS |= K4_CLOCK_LED;
	    else
	    	K4_STATUS &= ~K4_CLOCK_LED;
	    K2_STATUS |= K2_CLOCK;
    	    break;
	case 58 | 0x80:	/* caps-lock */
	    K2_STATUS &= ~K2_CLOCK;
    	    break;

	case 69:	/* num-lock */
	    K1_STATUS ^= K1_NLOCK;
    	    if (K1_STATUS & K1_NLOCK)
	    	K4_STATUS |= K4_NLOCK_LED;
	    else
	    	K4_STATUS &= ~K4_NLOCK_LED;
	    K2_STATUS |= K2_NLOCK;
    	    break;
	case 69 | 0x80:	/* num-lock */
	    K2_STATUS &= ~K2_NLOCK;
    	    break;

	case 70:	/* scroll-lock */
	    K1_STATUS ^= K1_SLOCK;
    	    if (K1_STATUS & K1_SLOCK)
	    	K4_STATUS |= K4_SLOCK_LED;
	    else
	    	K4_STATUS &= ~K4_SLOCK_LED;
	    K2_STATUS |= K2_SLOCK;
    	    break;
	case 70 | 0x80:	/* scroll-lock */
	    K2_STATUS &= ~K2_SLOCK;
    	    break;

	case 82:	/* insert */
	    K1_STATUS ^= K1_INSERT;
	    K2_STATUS |= K2_INSERT;
    	    break;
	case 82 | 0x80:	/* insert */
	    K2_STATUS &= ~K2_INSERT;
    	    break;

    	}

#if 0 /*XXXXX*/
	if ((K4_STATUS & 0x07) != oldled) {
	    oldled = K4_STATUS & 0x07;
	    ioctl (fd, PCCONIOCSETLED, &oldled);
	}
#endif

    	if (c == 83 && (K1_STATUS & (K1_ALT|K1_CTRL)) == (K1_ALT|K1_CTRL))
	    quit(0);

    	if (c < 89) {
	    u_short scode;

	    if (K1_STATUS & K1_ALT) {
		scode = ScanCodes[c].alt;
	    } else if (K1_STATUS & K1_CTRL) {
		scode = ScanCodes[c].ctrl;
	    } else if (K1_STATUS & K1_SHIFT) {
		scode = ScanCodes[c].shift;
	    } else {
		scode = ScanCodes[c].base;
		if (K1_STATUS & K1_CLOCK) {
		    if (islower(scode & 0xff)) {
		    	scode = (scode & 0xff00) | toupper(scode & 0xff);
		    }
		}
		if ((K1_STATUS & K1_NLOCK) && (K3_STATUS & K3_TWOBYTE) == 0) {
		    switch (c) {
		    case 71: /* home */
		    case 72: /* cursor up */
		    case 73: /* page up */
		    case 75: /* cursor left */
		    case 76: /* center key */
		    case 77: /* cursor right */
		    case 79: /* end */
		    case 80: /* cursor down */
		    case 81: /* page down */
		    case 82: /* insert */
		    case 83: /* delete */
			scode = ScanCodes[c].shift;
		    	break;
		    }
    	    	}
	    }
	    *code = scode;
    	}
	K3_STATUS &= ~K3_TWOBYTE;
	if ((K1_STATUS&(K1_ALT|K1_CTRL)) == (K1_ALT|K1_CTRL)) {
	    switch (c) {
	    case 0x14:	/* T */
		tmode ^= 1;
		if (!tmode)
		    resettrace(&saved_sigframe->sf_sc); 
		return(0xffff);
	    case 0x53:	/* DEL */
		quit(0);
	    }
    	}
	return(c);
    } else {
	return(0xffff);
    }
}

void
video_async_event(int fd, struct sigcontext *sc)
{
    	int int09 = 0;

	for (;;) {
                int x;
                fd_set fdset;
                XEvent ev;  
                static struct timeval tv = { 0 };
 
                /*
                 * Handle any events just sitting around...
                 */
                XFlush(dpy);
                while (QLength(dpy) > 0) {
                        XNextEvent(dpy, &ev);
                        int09 |= video_event(&ev);
                }

                FD_ZERO(&fdset);
                FD_SET(fd, &fdset);

                x = select(FD_SETSIZE, &fdset, 0, 0, &tv);

                switch (x) {  
                case -1:
                        /*
                         * Errno might be wrong, so we just select again.
                         * This could cause a problem is something really
                         * was wrong with select....
                         */
                        perror("select");
                        return;
                case 0:
			XFlush(dpy);
			if (int09)
			    hardint(0x09);
                        return;
                default:
                        if (FD_ISSET(fd, &fdset)) {
                                do {
                                        XNextEvent(dpy, &ev);
                                        int09 |= video_event(&ev);
                                } while (QLength(dpy));
                        }
                        break;
                }
        }
}

void
kbd_async_event(int fd, struct sigcontext *sc)
{
    unsigned char c;

    unsigned char oldled = K4_STATUS & 0x7;

    while (read(fd, &c, 1) == 1) {
    	switch (c) {
    	case 29:	/* Control */
	    K1_STATUS |= K1_CTRL;
	    K2_STATUS |= K2_LCTRL;
	    break;
    	case 29 | 0x80:	/* Control */
	    K1_STATUS &= ~K1_CTRL;
	    K2_STATUS &= ~K2_LCTRL;
	    break;

	case 42:	/* left shift */
	    K1_STATUS |= K1_LSHIFT;
	    break;
	case 42 | 0x80:	/* left shift */
	    K1_STATUS &= ~K1_LSHIFT;
	    break;

	case 54:	/* right shift */
	    K1_STATUS |= K1_RSHIFT;
	    break;
	case 54 | 0x80:	/* right shift */
	    K1_STATUS &= ~K1_RSHIFT;
	    break;

	case 56:	/* Alt */
	    K1_STATUS |= K1_ALT;
	    K2_STATUS |= K2_LALT;
	    break;
	case 56 | 0x80:	/* Alt */
	    K1_STATUS &= ~K1_ALT;
	    K2_STATUS &= ~K2_LALT;
	    break;

	case 58:	/* caps-lock */
	    if (K1_STATUS ^= K1_CLOCK)
	    	K4_STATUS &= ~K4_CLOCK_LED;
	    else
	    	K4_STATUS |= K4_CLOCK_LED;
	    K2_STATUS |= K2_CLOCK;
    	    break;
	case 58 | 0x80:	/* caps-lock */
	    K2_STATUS &= ~K2_CLOCK;
    	    break;

	case 69:	/* num-lock */
	    if (K1_STATUS ^= K1_CLOCK)
	    	K4_STATUS &= ~K4_NLOCK_LED;
	    else
	    	K4_STATUS |= K4_NLOCK_LED;
	    K2_STATUS |= K2_NLOCK;
    	    break;
	case 69 | 0x80:	/* num-lock */
	    K2_STATUS &= ~K2_NLOCK;
    	    break;

	case 70:	/* scroll-lock */
	    if (K1_STATUS ^= K1_SLOCK)
	    	K4_STATUS &= ~K4_SLOCK_LED;
	    else
	    	K4_STATUS |= K4_SLOCK_LED;
	    K2_STATUS |= K2_SLOCK;
    	    break;
	case 70 | 0x80:	/* scroll-lock */
	    K2_STATUS &= ~K2_SLOCK;
    	    break;

	case 82:	/* insert */
	    K1_STATUS ^= K1_INSERT;
	    K2_STATUS |= K2_INSERT;
    	    break;
	case 82 | 0x80:	/* insert */
	    K2_STATUS &= ~K2_INSERT;
    	    break;

    	}

    }

#if 0 /*XXXXX*/
    if ((K4_STATUS & 0x07) != oldled) {
	oldled = K4_STATUS & 0x07;
	    ioctl (fd, PCCONIOCSETLED, &oldled);
    }
#endif
}

int
video_event(XEvent *ev)
{
	switch (ev->type) {
	case MotionNotify: {
		XMotionEvent *me = (XMotionEvent *)ev;
		me->x -= 2;
		me->y -= 2;

		mouse_status.x = (me->x < mouse_status.range.x)
				    ? mouse_status.range.x
				    : (me->x > mouse_status.range.w)
				    ? mouse_status.range.w : me->x;
		mouse_status.y = (me->y < mouse_status.range.y)
				    ? mouse_status.range.y
				    : (me->y > mouse_status.range.h)
				    ? mouse_status.range.h : me->y;
		break;
	    }
	case ButtonRelease: {
		XButtonEvent *be = (XButtonEvent *)ev;
		be->x -= 2;
		be->y -= 2;

		if (be->button < 3)
		    mouse_status.ups[be->button]++;

		mouse_status.x = (be->x < mouse_status.range.x)
				    ? mouse_status.range.x
				    : (be->x > mouse_status.range.w)
				    ? mouse_status.range.w : be->x;
		mouse_status.y = (be->y < mouse_status.range.y)
				    ? mouse_status.range.y
				    : (be->y > mouse_status.range.h)
				    ? mouse_status.range.h : be->y;
		break;
	    }
	case ButtonPress: {
		XButtonEvent *be = (XButtonEvent *)ev;
		be->x -= 2;
		be->y -= 2;

		if (be->button < 3)
		    mouse_status.downs[be->button]++;

		mouse_status.x = (be->x < mouse_status.range.x)
				    ? mouse_status.range.x
				    : (be->x > mouse_status.range.w)
				    ? mouse_status.range.w : be->x;
		mouse_status.y = (be->y < mouse_status.range.y)
				    ? mouse_status.range.y
				    : (be->y > mouse_status.range.h)
				    ? mouse_status.range.h : be->y;

		if ((K1_STATUS & (K1_ALT|K1_CTRL)) == (K1_ALT|K1_CTRL)) {
		    quit(0);
		}
		break;
	    }
        case NoExpose:
                break;
        case GraphicsExpose:
        case Expose: {
		int r;
		for (r = 0; r < height; ++r)
		    lines[r].changed = 1;
		break;
	    }
	case KeyRelease: {
		static char buf[128];
		KeySym ks;
		int n;

		break_code |= 0x80;

    	    	if (!(ev->xkey.state & ShiftMask)) {
		    K1_STATUS &= ~K1_LSHIFT;
		    K1_STATUS &= ~K1_RSHIFT;
		}
    	    	if (!(ev->xkey.state & ControlMask)) {
			K1_STATUS &= ~K1_CTRL;
			K2_STATUS &= ~K2_LCTRL;
			K3_STATUS &= ~K3_RCTRL;
		}
    	    	if (!(ev->xkey.state & Mod1Mask)) {
                        K1_STATUS &= ~K1_ALT;
                        K2_STATUS &= ~K2_LALT;
                        K3_STATUS &= ~K3_RALT;
		}
    	    	if (!(ev->xkey.state & LockMask)) {
                        K2_STATUS &= ~K2_CLOCK;
		}

		XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &ks, 0);
		switch (ks) {
		case XK_Shift_L:
			K1_STATUS &= ~K1_LSHIFT;
			break;
		case XK_Shift_R:
			K1_STATUS &= ~K1_RSHIFT;
			break;
		case XK_Control_L:
			K1_STATUS &= ~K1_CTRL;
			K2_STATUS &= ~K2_LCTRL;
			break;
		case XK_Control_R:
			K1_STATUS &= ~K1_CTRL;
			K3_STATUS &= ~K3_RCTRL;
			break;
		case XK_Alt_L:
			K1_STATUS &= ~K1_ALT;
			K2_STATUS &= ~K2_LALT;
			break;
		case XK_Alt_R:
			K1_STATUS &= ~K1_ALT;
			K3_STATUS &= ~K3_RALT;
			break;
		case XK_Scroll_Lock:
			K2_STATUS &= ~K2_SLOCK;
			break;
		case XK_Num_Lock:
			K2_STATUS &= ~K2_NLOCK;
			break;
		case XK_Caps_Lock:
			K2_STATUS &= ~K2_CLOCK;
			break;
		case XK_Insert:
			K2_STATUS &= ~K2_INSERT;
			break;
		}
		return(1);
	    }
	case KeyPress: {
		static char buf[128];
		KeySym ks;
		int n;
    	    	int nlock = 0;
		u_short scan = 0xffff;

    	    	if (!(ev->xkey.state & ShiftMask)) {
		    K1_STATUS &= ~K1_LSHIFT;
		    K1_STATUS &= ~K1_RSHIFT;
		}
    	    	if (!(ev->xkey.state & ControlMask)) {
			K1_STATUS &= ~K1_CTRL;
			K2_STATUS &= ~K2_LCTRL;
			K3_STATUS &= ~K3_RCTRL;
		}
    	    	if (!(ev->xkey.state & Mod1Mask)) {
                        K1_STATUS &= ~K1_ALT;
                        K2_STATUS &= ~K2_LALT;
                        K3_STATUS &= ~K3_RALT;
		}
    	    	if (!(ev->xkey.state & LockMask)) {
                        K2_STATUS &= ~K2_CLOCK;
		}

		n = XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &ks, 0);

		switch (ks) {
		case XK_Shift_L:
			K1_STATUS |= K1_LSHIFT;
			break;
		case XK_Shift_R:
			K1_STATUS |= K1_RSHIFT;
			break;
		case XK_Control_L:
			K1_STATUS |= K1_CTRL;
			K2_STATUS |= K2_LCTRL;
			break;
		case XK_Control_R:
			K1_STATUS |= K1_CTRL;
			K3_STATUS |= K3_RCTRL;
			break;
		case XK_Alt_L:
			K1_STATUS |= K1_ALT;
			K2_STATUS |= K2_LALT;
			break;
		case XK_Alt_R:
			K1_STATUS |= K1_ALT;
			K3_STATUS |= K3_RALT;
			break;
		case XK_Scroll_Lock:
			K1_STATUS ^= K1_SLOCK;
			K2_STATUS |= K2_SLOCK;
			break;
		case XK_Num_Lock:
			K1_STATUS ^= K1_NLOCK;
			K2_STATUS |= K2_NLOCK;
			break;
		case XK_Caps_Lock:
			K1_STATUS ^= K1_CLOCK;
			K2_STATUS |= K2_CLOCK;
			break;
		case XK_Insert:
			K1_STATUS ^= K1_INSERT;
			K2_STATUS |= K2_INSERT;
			scan = 82;
			goto docode;

		case XK_Escape:
			scan = 1;
			goto docode;

		case XK_Tab:
			scan = 15;
			goto docode;
			
    	    	case XK_Return:
		case XK_KP_Enter:
			scan = 28;
		    	goto docode;

    	    	case XK_Print:
			scan = 55;
			goto docode;

		case XK_F1:
		case XK_F2:
		case XK_F3:
		case XK_F4:
		case XK_F5:
		case XK_F6:
		case XK_F7:
		case XK_F8:
		case XK_F9:
		case XK_F10:
			scan = ks - XK_F1 + 59;
			goto docode;

    	    	case XK_KP_7:
			nlock = 1;
		case XK_Home:
			scan = 71;
			goto docode;
    	    	case XK_KP_8:
			nlock = 1;
		case XK_Up:
			scan = 72;
			goto docode;
    	    	case XK_KP_9:
			nlock = 1;
		case XK_Prior:
			scan = 73;
			goto docode;
    	    	case XK_KP_Subtract:
			scan = 74;
			goto docode;
    	    	case XK_KP_4:
			nlock = 1;
		case XK_Left:
			scan = 75;
			goto docode;
    	    	case XK_KP_5:
			nlock = 1;
		case XK_Begin:
			scan = 76;
			goto docode;
    	    	case XK_KP_6:
			nlock = 1;
		case XK_Right:
			scan = 77;
			goto docode;
    	    	case XK_KP_Add:
			scan = 78;
			goto docode;
    	    	case XK_KP_1:
			nlock = 1;
		case XK_End:
			scan = 79;
			goto docode;
    	    	case XK_KP_2:
			nlock = 1;
		case XK_Down:
			scan = 80;
			goto docode;
    	    	case XK_KP_3:
			nlock = 1;
		case XK_Next:
			scan = 81;
			goto docode;
    	    	case XK_KP_0:
			nlock = 1;
    	    	/* case XK_Insert: This is above */
			scan = 82;
			goto docode;

    	    	case XK_KP_Decimal:
			nlock = 1;
			scan = 83;
			goto docode;

    	    	case XK_Delete:
			scan = flipdelete ? 14 : 83;
			goto docode;

		case XK_BackSpace:
			scan = flipdelete ? 83 : 14;
			goto docode;

    	    	case XK_F11:
			scan = 87;
			goto docode;
    	    	case XK_F12:
			scan = 88;
			goto docode;


		case XK_KP_Divide:
			scan = Ascii2Scan['/'];
			goto docode;

		case XK_KP_Multiply:
			scan = Ascii2Scan['*'];
			goto docode;

		default:
    	    	    	if ((K1_STATUS&(K1_ALT|K1_CTRL)) == (K1_ALT|K1_CTRL)) {
				if (ks == 'T' || ks == 't') {
				    tmode ^= 1;
				    if (!tmode)
					    resettrace(&saved_sigframe->sf_sc); 
				    break;
				}
			}
			if (ks < ' ' || ks > '~')
				break;
			scan = Ascii2Scan[ks]; 
    	    	docode:
			if (nlock)
			    scan |= 0x100;

    	    	    	if ((scan & ~0x100) > 88) {
			    scan = 0xffff;
			    break;
    	    	    	}

    	    	    	if ((K1_STATUS & K1_SHIFT) || (scan & 0x100)) {
			    scan = ScanCodes[scan & 0xff].shift;
			} else if (K1_STATUS & K1_CTRL) {
			    scan = ScanCodes[scan & 0xff].ctrl;
			} else if (K1_STATUS & K1_ALT) {
			    scan = ScanCodes[scan & 0xff].alt;
			}  else
			    scan = ScanCodes[scan & 0xff].base;

			break;
		}
		if (scan != 0xffff) {
			break_code = scan >> 8;
			KbdWrite(scan);
		}
		return(1);
	    }
	default:
		break;
	}
    	return(0);
}

#define	R03D4	BIOSDATA[0x65]
static u_char	R03BA = 0;
static u_char	R03DA = 0;

unsigned char
video_inb(int port)
{
	switch(port) {
	case CGA_Status:
		R03DA += 1;	/* Just cylce throught the values */
		return(R03DA &= 0x0f);
	case 0x03c2:	/* Misc */
	case 0x03cc:	/* Misc */
		return(0xc3);
	case CVC_Data:
		if (R03D4 < 0x10) {
			return(VREG[R03D4]);
		}
		break;
	}
}

void
video_outb(int port, unsigned char value)
{
	int cp;

	switch(port) {
	case 0x03cc:
	case 0x03c2:
		if ((value & 0x1) == 0)	/* Trying to request monochrome */
			break;
		return;
	case CGA_Control:
		if (value & 0x22)	/* Trying to select graphics */
			break;
		return;
	case CVC_Address:
		R03D4 = value & 0x1f;
		return;
	case CVC_Data:
		if (R03D4 > 0x0f)
			break;
		VREG[R03D4] = value;
		switch (R03D4) {
		case MVC_CurHigh:
			cp = row * width + col;
			cp &= 0xff;
			cp |= (value << 8) & 0xff00;
			row = cp / width;
			col = cp % width;
			break;
		case MVC_CurLow:
			cp = row * width + col;
			cp &= 0xff00;
			cp |= value & 0xff;
			row = cp / width;
			col = cp % width;
			break;
		}
		return;
	}
}

void
tty_move(int r, int c)
{
	row = r;
	col = c;
	SetVREGCur();
}

void
tty_report(int *r, int *c)
{
	*r = row;
	*c = col;
}

void
tty_flush()
{
	K_NEXT = K_FREE = 0;
}

void
tty_index()
{
	int i;

	if (row > (height - 1))
		row = 0;
	else if (++row >= height) {
		row = height - 1;
		memcpy(vmem, &vmem[width], 2 * width * (height - 1));
		for (i = 0; i < width; ++i)
		    vmem[(height - 1) * width + i] = vattr | ' ';
	}
	SetVREGCur();
}

void
tty_write(int c, int attr)
{
    	if (attr == TTYF_REDIRECT) {
		if (redirect1) {
		    char tc = c;
		    write(1, &c, 1);
		    return;
		}
		attr = -1;
	}
    	if (capture_fd >= 0) {
	    char cc = c;
	    write(capture_fd, &cc, 1);
    	}
	c &= 0xff;
	switch (c) {
	case 0x07:
		if (xmode) {
			XBell(dpy, 0);
		} else
			write(1, "\007", 1);
		break;
	case 0x08:
		if (row > (height - 1) || col > width)
			break;
		if (col > 0)
			--col;
		vmem[row * width + col] &= 0xff00;
		break;
	case '\t':
		if (row > (height - 1))
			row = 0;
		col = (col + 8) & ~0x07;
		if (col > width) {
			col = 0;
			tty_index();
		}
		break;
	case '\r':
		col = 0;
		break;
	case '\n':
		tty_index();
		break;
	default:
		if (col >= width) {
			col = 0;
			tty_index();
		}
		if (row > (height - 1))
			row = 0;
		if (attr >= 0)
			vmem[row * width + col] = attr & 0xff00;
		else
			vmem[row * width + col] &= 0xff00;
		vmem[row * width + col++] |= c;
		break;
	}
	SetVREGCur();
}

void
tty_rwrite(int n, int c, int attr)
{
	u_char srow, scol;
	c &= 0xff;

	srow = row;
	scol = col;
	while (n--) {
		if (col >= width) {
			col = 0;
			tty_index();
		}
		if (row > (height - 1))
			row = 0;
		if (attr >= 0)
			vmem[row * width + col] = attr & 0xff00;
		else
			vmem[row * width + col] &= 0xff00;
		vmem[row * width + col++] |= c;
	}
	row = srow;
	col = scol;
	SetVREGCur();
}

void
tty_pause()
{
	sigset_t set;

	sigprocmask(0, 0, &set);
	sigdelset(&set, SIGIO);
	sigdelset(&set, SIGALRM);
	sigsuspend(&set);
}

static int nextchar = 0;

int
tty_read(struct sigcontext *sc, int flag)
{
    int r;

    if (r = nextchar) {
	nextchar = 0;
	return(r & 0xff);
    }

    if ((flag & TTYF_REDIRECT) && redirect0) {
	char c;
    	if (read(0, &c, 1) != 1)
	    return(-1);
	if (c == '\n')
	    c = '\r';
	return(c);
    }

    if (KbdEmpty()) {
	if (flag & TTYF_BLOCK) {
	    while (KbdEmpty())
		tty_pause();
	} else {
	    return(-1);
	}
    }

    r = KbdRead();
    if ((r & 0xff) == 0)
	nextchar = r >> 8;
    r &= 0xff;
    if (flag & TTYF_CTRL) {
	if (r == 3) {
	    /*
	     * XXX - Not quite sure where we should return, maybe not
	     *       all the way to the user, but...
	     */
	    if (ivec[0x23] && (ivec[0x23] >> 16) != 0xF000) {
    	    	fake_int(sc, 0x23);
		SET16(sc->sc_eip, GET16(sc->sc_eip) - 2);
		return(-2);
	    }
    	}
    }
    if (flag & TTYF_ECHO) {
	if ((flag & TTYF_ECHONL) && (r == '\n' || r == '\r')) { 
	    tty_write('\r', -1);
	    tty_write('\n', -1);
    	} else
	    tty_write(r, -1);
    }
    return(r & 0xff);
}

int
tty_peek(struct sigcontext *sc, int flag)
{
	int c;

    	if (c == nextchar)
	    return(nextchar & 0xff);

	if (KbdEmpty()) {
		if (flag & TTYF_POLL) {
			sleep_poll();
			if (KbdEmpty())
				return(0);
		} else if (flag & TTYF_BLOCK) {
			while (KbdEmpty())
				tty_pause();
		} else
			return(0);
	}
	c = KbdPeek();
    	if ((c & 0xff) == 3) {
	    /*
	     * XXX - Not quite sure where we should return, maybe not
	     *       all the way to the user, but...
	     */
	    if (ivec[0x23] && (ivec[0x23] >> 16) != 0xF000) {
    	    	fake_int(sc, 0x23);
		SET16(sc->sc_eip, GET16(sc->sc_eip) - 2);
		return(-2);
	    }
    	}
	return(0xff);
}

int
tty_state()
{
	return(K1_STATUS);
}

tty_estate()
{
    int state = 0;
    if (K2_STATUS & K2_SYSREQ)
    	state |= 0x80;
    if (K2_STATUS & K2_CLOCK)
    	state |= 0x40;
    if (K2_STATUS & K2_NLOCK)
    	state |= 0x20;
    if (K2_STATUS & K2_SLOCK)
    	state |= 0x10;
    if (K3_STATUS & K3_RALT)
    	state |= 0x08;
    if (K3_STATUS & K3_RCTRL)
    	state |= 0x04;
    if (K2_STATUS & K2_LALT)
    	state |= 0x02;
    if (K2_STATUS & K2_LCTRL)
    	state |= 0x01;
    return(state);
}

inline int
inrange(int a, int n, int x)
{
	return(a < n ? n : a > x ? x : a);
}

void
tty_scroll(int sr, int sc, int er, int ec, int n, int attr)
{
	int i, j;

	sr = inrange(sr, 0, height);
	er = inrange(er, 0, height);
	sc = inrange(sc, 0, width);
	ec = inrange(ec, 0, width);
	if (sr > er || sc > ec)
		return;
	++er;
	++ec;

	attr &= 0xff00;
	attr |= ' ';

	if (n > 0 && n < er - sr) {
		for (j = sr; j < er - n; ) {
			memcpy(&vmem[j * width + sc],
			       &vmem[(j + n) * width + sc],
			       sizeof(vmem[0]) * (ec - sc));
			++j;
		}
	} else
		n = er - sr;
	for (j = er - n; j < er; ) {
		for (i = sc; i < ec; ++i)
			vmem[j * width + i] = attr;
		++j;
	}
}

void
tty_rscroll(int sr, int sc, int er, int ec, int n, int attr)
{
	int i, j;

	sr = inrange(sr, 0, height);
	er = inrange(er, 0, height);
	sc = inrange(sc, 0, width);
	ec = inrange(ec, 0, width);
	if (sr > er || sc > ec)
		return;
	++er;
	++ec;

	attr &= 0xff00;
	attr |= ' ';

	if (n > 0 && n < er - sr) {
		for (j = er; j > sr + n; ) {
			--j;
			memcpy(&vmem[j * width + sc],
			       &vmem[(j - n) * width + sc],
			       sizeof(vmem[0]) * (ec - sc));
		}
	} else
		n = er - sr;
	for (j = sr + n; j > sr; ) {
		--j;
		for (i = sc; i < ec; ++i)
			vmem[j * width + i] = attr;
	}
}

int
tty_char(int r, int c)
{
	if (r == -1)
		r = row;
	if (c == -1)
		c = col;
	r = inrange(r, 0, height);
	c = inrange(c, 0, width);
	return(vmem[r * width + c]);
}

int
KbdEmpty()
{
	return(K_NEXT == K_FREE);
}

void
KbdWrite(u_short code)
{
	int klen = K_BUFEND - K_BUFSTART;
	int kf = (K_FREE + 1) % klen;
	if (kf == K_NEXT) {
		XBell(dpy, 0);
		return;
	}
	K_BUFSTART[K_FREE] = code;
	K_FREE = kf;
}

void
KbdRepl(u_short code)
{
	K_BUFSTART[K_NEXT] = code;
}

u_short
KbdRead()
{
	int klen = K_BUFEND - K_BUFSTART;
	int kf = K_NEXT;
	K_NEXT = (K_NEXT + 1) % klen;
	return(K_BUFSTART[kf]);
}

u_short
KbdPeek()
{
	return(K_BUFSTART[K_NEXT]);
}

void int10(struct sigcontext *sc);

void
video_init()
{
    u_long vec;
    XSizeHints sh;
    XGCValues gcv;
    XColor ccd, rgb;
    unsigned long mask;
    int level;
    int i, j;

    VREG = (u_char *)malloc(32);

    /*
     * Define all known I/O port handlers
     */
    define_input_port_handler(0x60, inb_port60);

    if (!raw_kbd) {
	define_input_port_handler(CGA_Status, video_inb);
	define_input_port_handler(0x03c2, video_inb);
	define_input_port_handler(0x03cc, video_inb);
	define_input_port_handler(CVC_Data, video_inb);
	define_output_port_handler(CGA_Control, video_outb);
	define_output_port_handler(0x03c2, video_outb);
	define_output_port_handler(0x03cc, video_outb);
	define_output_port_handler(CVC_Address, video_outb);
	define_output_port_handler(CVC_Data, video_outb);
    }

    redirect0 = isatty(0) == 0 || !xmode ;
    redirect1 = isatty(1) == 0 || !xmode ;
    redirect2 = isatty(2) == 0 || !xmode ;

    K_BUFSTARTP = 0x1e;	/* Start of keyboard buffer */
    K_BUFENDP = 0x3e;	/* End of keyboard buffer */
    K_NEXT = K_FREE = 0x00;

    vec = insert_hardint_trampoline();
    ivec[0x09] = vec;
    register_callback(vec, int09, "int 09");

    /*
     * Initialize video memory with black background, white foreground
     */
    for (i = 0; i < height * width; ++i)
	vmem[i] = vattr;

    if (!xmode)
	return;

    if (!(lines = (TextLine *)malloc(sizeof(TextLine) * height))) {
	fprintf(stderr, "Could not allocate data structure for text lines\n");
	quit(1);
    }
    for (i = 0; i < height; ++i) {
	lines[i].max_length = width;
	if (!(lines[i].data = (u_short *)malloc(width * sizeof(u_short)))) {
	    fprintf(stderr,
		    "Could not allocate data structure for text lines\n");
	    quit(1);
	}
	lines[i].changed = 1;
    }


    {
	/*
	 * Arg...  I can no longer change X's fd out from under it.
	 * Open up all the available fd's, leave 3 behind for X
	 * to play with, open X and then release all the other fds
	 */
    	int nfds = sysconf(_SC_OPEN_MAX);
    	int *fds = malloc(sizeof(int) * nfds);
	i = 0;
    	if (fds)
	    for (i = 0; i < nfds && (i == 0 || fds[i-1] < 63); ++i)
		if ((fds[i] = open("/dev/null", 0)) < 0)
		    break;
	/*
	 * Leave 3 fds behind for X to play with
	 */
    	if (i > 0) close(fds[--i]);
    	if (i > 0) close(fds[--i]);
    	if (i > 0) close(fds[--i]);

	dpy = XOpenDisplay(NULL);

    	while (i > 0)
	    close(fds[--i]);
    }

    if (dpy == NULL) {
	    fprintf(stderr, "Could not open display ``%s''\n",
		    XDisplayName(NULL));
	    quit(1);
    }
    xfd = ConnectionNumber(dpy);

    _RegisterIO(xfd, video_async_event, xfd, Failure);
    if (debug_flags & D_DEBUGIN)
	    _RegisterIO(0, debug_event, 0, Failure);
    _BlockIO();

    pixels[0] = BlackPixel(dpy, DefaultScreen(dpy));
    pixels[15] = WhitePixel(dpy, DefaultScreen(dpy));
    for (i = 0; i < 15; ++i) {
	    if (XAllocNamedColor(dpy,
				 DefaultColormap(dpy, DefaultScreen(dpy)),
				 color_names[i], &ccd, &rgb)) {
		    pixels[i] = ccd.pixel;
	    } else if (i < 7)
		    pixels[i] = pixels[0];
	    else
		    pixels[i] = pixels[15];
    }

    if (!xfont)
	xfont = IBMFONT;

    font = XLoadQueryFont(dpy, xfont);

    if (font == NULL)
	font = XLoadQueryFont(dpy, IBMFONT);

    if (font == NULL) {
	    fprintf(stderr, "Could not open font ``%s''\n", xfont);
	    quit(1);
    }

    FW = font->max_bounds.width;
    FH = font->max_bounds.ascent + font->max_bounds.descent;
    FD = font->max_bounds.descent;

    curs_start = 6;
    curs_end = 7;

    sh.width = FW * width + 4;
    sh.height = FH * height + 4;

    sh.width += 4;
    sh.height += 4;

    sh.min_width = sh.max_width = sh.width;
    sh.min_height = sh.max_height = sh.height;

    sh.flags = USSize | PMinSize | PMaxSize | PSize;

    win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0,
			      sh.width, sh.height, 2, black, black);
    if (win == NULL) {
	    fprintf(stderr, "Could not create window\n");
	    quit(1);
    }

    gcv.foreground = white;
    gcv.background = black;
    gcv.font = font->fid;

    mask = GCForeground | GCBackground | GCFont;

    gc = XCreateGC(dpy, win, mask, &gcv);

    gcv.foreground = 1;
    gcv.background = 0;
    gcv.function = GXxor;
    cgc = XCreateGC(dpy, win, GCForeground|GCBackground|GCFunction, &gcv);

    XSetNormalHints(dpy, win, &sh);
    if (raw_kbd) {
	XSelectInput(dpy, win, ExposureMask | ButtonPressMask
			       | ButtonReleaseMask | PointerMotionMask );
    } else {
	XSelectInput(dpy, win, KeyReleaseMask | KeyPressMask |
			       ExposureMask | ButtonPressMask
			       | ButtonReleaseMask | PointerMotionMask );
    }

    XMapWindow(dpy, win);
    XFlush(dpy);

    _UnblockIO();
}

void
video_bios_init()
{
	u_char *p;
	u_long vec;

    	if (raw_kbd)
		return;

	/*
	 * Put the Video Save Pointer table @ C000:0000
	 * Put the Secondary Video Save Pointer table @ C000:0020
	 * Put the Display Combination code table @ C000:0040
	 * Put the Video Parameter table @ C000:1000 - C000:2FFF
	 * Put the default Font @ C000:3000 - C000:3FFF
	 */
	
	*(u_long *)&BIOSDATA[0xA8] = 0xC0000000; /* 0040:00A8 points to us */

	vsp = (struct VideoSavePointerTable *)0xC0000L;
	memset(vsp, 0, sizeof(struct VideoSavePointerTable));
	svsp = (struct SecondaryVideoSavePointerTable *)0xC0020L;

	vsp->video_parameter_tabel[0] = 0x1000;
	vsp->video_parameter_tabel[1] = 0xC000;

	vsp->secondary_save_pointer_table[0] = 0x0020;
	vsp->secondary_save_pointer_table[1] = 0xC000;

	svsp->display_combination_code_table[0] = 0x0040;
	svsp->display_combination_code_table[1] = 0xC000;

	p = (u_char *)0xC0040;
	*p++ = 2;		/* Only support 2 combinations currently */
	*p++ = 1;		/* Version # */
	*p++ = 8;		/* We wont use more than type 8 */
	*p++ = 0;		/* Resereved */
	*p++ = 0; *p++ = 0;	/* No Display No Display */
	*p++ = 0; *p++ = 8;	/* No Display VGA Color */

	memcpy((void *)0xC1000, videoparams, sizeof(videoparams));

	ivec[0x1d] = 0xC0001000L;	/* Video Parameter Table */
	ivec[0x1e] = 0xC0003000L;	
	ivec[0x42] = ivec[0x10];	/* Copy of video interrupt */
        memcpy((void *)0xC3000L, ascii_font, sizeof(ascii_font));

                     BIOSDATA[0x49] = 3;        /* Video Mode */
        *(u_short *)&BIOSDATA[0x4a] = 80;       /* Columns on Screen */
        *(u_short *)&BIOSDATA[0x4c] = 0;        /* Page */
        *(u_short *)&BIOSDATA[0x4e] = 0;        /* Offset into video memory */
        *(u_short *)&BIOSDATA[0x63] = 0x03d4;   /* controller base reg */
                     BIOSDATA[0x84] = 24;       /* Rows on screen */
        *(u_short *)&BIOSDATA[0x85] = 16;       /* font height */
                     BIOSDATA[0x87] = 0;        /* video ram etc. */
                     BIOSDATA[0x88] = 0xf9;     /* video switches */
                     BIOSDATA[0x89] = 0x11;     /* video stats */
                     BIOSDATA[0x8a] = 1;        /* Index into DCC table */
		     BIOSDATA[0x96] = 0x10;	
	K1_STATUS = 0;
	K2_STATUS = 0;
	K3_STATUS = 0;
	K4_STATUS = 0;

	vec = insert_softint_trampoline();
	ivec[0x10] = vec;
	register_callback(vec, int10, "int 10");
}
