/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */

#ifndef lint
static char    *rcsid = "$Header:pckbd.c 12.0$";
#endif

#include <dos.h>
#include <sys\types.h>
#include "pcparam.h"
#include "rb.h"
#include "bios.h"
#include "pckbd.h"
#include "vars.h"
#include "trace.h"

#define FAIL(n) DEBUGF(*dbugptr&KBDDEBUG, printf("return %d\n",n))
 /* ; return(n)  /* */

extern void     kbd_intercept();
extern void     kbd_int();		/* direct irq1 intercept */
extern void     irq1();			/* actual irq1 routine */
extern int      pckbd;
u_short         def_scan = 1;	/* default DOS scan code set */
u_short         unix_scan = 3;	/* scan code to use for UNIX */
u_short		fake_mouse;		/* use keyboard as a fake mouse */

struct kbdata   kbdata[NCPU] = {0};
/*
 * we use NOP_SC to pass along a dummy break code when a make-break key
 * has been consumed by conversion to a mouse movement.
 */
#define NOP_SC	0x0a			/* an unused scan code */

static int kbd_in_empty(void);
static void scan_init(void);
static int kbd_mouse(u_short, u_short, u_short);
void kbsend(u_short,struct cbcb *);
static  void queuekb(unsigned short, struct cbcb *);
static void kbd_mouse_reset();

static int kbd_cmd_ack(int *);

/*
 *      This version of init_kbd came from ATR full-kernel version,
 *      and includes many fixes since original stand-alone version.
 */

int             init_kbd_flag = 0;
int             kbd_leds_state;


u_short         kyb_cntiw = 0, kyb_cntir = 0, kyb_read = 0, kyb_write = 0;

u_short         biosint15seg;
u_short         biosint15off;
u_long          kbd_save;
u_long          kbd1_save;	/* old keyboard int (irq1) save location */

int init_kbd()
{
	int             s;
	int far        *biosint15;

	if (init_kbd_flag++)
		return (0);	/* Don't re-init if called previously */

	DEBUGF(*dbugptr & KBDDEBUG, printf("init init_kbd\n"));

	s = spl();

	TAKE_VECTOR(0x15, kbd_save, kbd_intercept);
	biosint15 = (int far *) kbd_save;
	biosint15seg = FP_SEG(biosint15);
	biosint15off = FP_OFF(biosint15);

	if (option_flag&OPTION_OLDKBD) {
		TAKE_VECTOR(0x09, kbd1_save, irq1);
	}

	if (option_flag&OPTION_SCAN1) {
		scan_init();
		splx(s);
		return(0);		/* leave keyboard as-is */
	}

	kyb_cntiw = KYB_CNTIW;
	kyb_cntir = KYB_CNTIR;
	kyb_read = KYB_READ;
	kyb_write = KYB_WRITE;


	kbd_reset(1);		/* reset the keyboard */

	clear_kbd(40);		/* clear pipeline */
	if ((int) IOIN(kyb_cntir) & KYB_OBF)
	{
		printf("keyboard pipeline won't empty\n");
		splx(s);
		FAIL(1);
	}
	DEBUGF(*dbugptr & KBDDEBUG, printf("init_kbd: normal return\n"));

	kbd_aux_int();

	splx(s);
	return (0);
}

static
kbd_aux_int()
{
	int             c_command_byte;
	/*
	 * Make sure the Auxiliary Interrupt Enable bit does not get turned
	 * off. 
	 */

	(void) s8042_cmd(CMD(CC_READ, 0));
	while ((IOIN(kyb_cntir) & KYB_OBF) != 1);
	c_command_byte = (IOIN(kyb_read) & 0x02) | CCB_SYSFLAG | CCB_OBFINT;
	(void) s8042_cmd(CMD(CC_WRITE, c_command_byte));
	clear_kbd(10);
}

static void 
clear_kbd(n)
	register int    n;
{
	register int    c;

	while (--n >= 0)
	{
		c = (int) IOIN(kyb_read);
		(void) c;
		delay(1);
	}
}

void
kbd_reset(flag)
	int		flag;
{
	u_char          resp;

	int             opt;

	/* Enable Keyboard Interface        */

	if (s8042_cmd(CMD(CC_ENABLE_KYBD, 0)) != 0)
		printf("kbd_reset: unable to enable keyboard interface\n");
	else
		DEBUGF(*dbugptr & KBDDEBUG, printf("kbd_reset: enabled keyboard interface ...\n"));
	delay(50);

/*      Reset the keyboard - This performs BAT  */


	kbd_cmd(CMD(KBC_RESET, 0));

	delay(50);

	/* See if BAT executed OK */

	resp = IOIN(kyb_read) & 0x00ff;

	if (resp == KBR_OK)
	{
		DEBUGF(*dbugptr & KBDDEBUG, printf("BAT completed successfully!\n"));
	} else
		DEBUGF(*dbugptr & KBDDEBUG, printf("BAT error Returns %x\n", resp));

	delay(50);



/*      Enable the keyboard     */

	kbd_cmd(CMD(KBC_ENABLE, 0));
	DEBUGF(*dbugptr & KBDDEBUG, printf("Successfully enabled keyboard ...\n"));
	delay(50);

	/* Set the Enhanced keyboard to send RT scan codes ... */

	opt = flag ? unix_scan : def_scan;	/* select proper keyboard
						 * codes */
	kbd_cmd(CMD(KBC_SCANSELECT, opt));
	DEBUGF(*dbugptr & KBDDEBUG, printf("Resetting Enhanced Keyboard to scan code set %x\n", opt));
	delay(50);

	/* set all keys to typematic/make-break */

	if (kbd_cmd(CMD(KBC_SETALL_TMB, 0)) != KBR_ACK)
		DEBUGF(*dbugptr & KBDDEBUG, printf("Unable to set keys to typematic/make-break \n"));


}

static int
kbd_cmd(cmd)			/* Send the keyboard a command      */
	register int    cmd;
{
/*
 * Wait for the 8042 to indicate 'input buffer not full'. Then send it
 * the command 'cmd'.
  
 * Wait for the response.
 */
	int             iid;
	int             takes_param;
	int             bcmd, bparam;

	bcmd = (int) (cmd & 0xff);
	bparam = (int) ((cmd & 0xff00) >> 8);
	takes_param = 0;
	if ((bcmd == KBC_LEDS) || (bcmd == KBC_TYPEMATIC) || (bcmd == KBC_SCANSELECT))
		takes_param = 1;
	DEBUGF(*dbugptr & KBDDEBUG, printf("kbd_cmd(%x)\n", cmd));

	if (kbd_in_empty())
		return (1);		/* wouldn't empty */
	DEBUGF(*dbugptr & KBDDEBUG, printf("Sending command %x\n", bcmd));
cmd:	IOOUT(kyb_write, bcmd);	/* Send the controller a command */

	delay(1);		/* Wait for command to be accepted */
	if (kbd_cmd_ack(&iid))
		return (2);	/* didn't ack */
data:	if (takes_param)
	{
		DEBUGF(*dbugptr & KBDDEBUG, printf("takes_param = %d\n", takes_param));
		/*
		 * If command takes additional param, send and wait for
		 * additional acknowledgement 
		 */
		IOOUT(kyb_write, bparam);
		delay(1);
		if (kbd_cmd_ack(&iid))
			return (3);
	}
	return (iid);
}



int
s8042_cmd(cmd)
	register int    cmd;
{
/*
 * Wait for the 8042 to  be empty. Then send it the command. Then
 * wait for the input buffer to empty again, signifying receipt
 * of the command.
 *
 * If command takes a parameter (CC_WRITE), send this to 0x60 and
 * wait for input buffer to empty before returning.
 */

	register int    ret = 0;
	int             takes_param;
	int             bcmd, bparam;

	bcmd = (int) (cmd & 0xff);
	bparam = (int) ((cmd & 0xff00) >> 8);
	takes_param = (bcmd == CC_WRITE) ? 1 : 0;
	DEBUGF(*dbugptr & KBDDEBUG, printf("s8042_cmd: cmd = %x\n", cmd));

	/* wait for input buffer to empty */
	if (kbd_in_empty())
		ret = -1;
cmd:	IOOUT(kyb_cntiw, bcmd);	/* Send the controller a command */

	DEBUGF(*dbugptr & KBDDEBUG, printf("s8042_cmd: bcmd = %x\n", bcmd));
	/* wait for input buffer to re-empty */
	if (kbd_in_empty())
		ret = -1;
data:	if (takes_param)
	{
		IOOUT(kyb_write, bparam);	/* If command takes
						 * additional  */
		/* param, send and wait for   */
		/* additional acknowledgement   */

		/* wait for input buffer to re-empty */
		if (kbd_in_empty())
			ret = -1;
	}
	DEBUGF(*dbugptr & KBDDEBUG, printf("s8042_cmd: returning ...\n"));
	/* */
	return (ret);
}


void
delay(n)
	int             n;
{
	int             i;

	int		dcount = (cpu_type == MODEL_80 ? 2000 : 800);

	while (n--)
		for (i = 0; i < dcount; i++);
}




/*
 * kbdcmd() - process a keyboard command from Unix.
 *
 */

void
kbdcmd()
{
	register struct kbdata *kb = &kbdata[screen_cbcb->cpu];
	register int    bcmd = kb->cmd_code;
	int             i;

	/*
	 * Wait for the keyboard buffer to empty before we send a command 
	 */
	for (i = 0; (IOIN(KYB_CNTIR) & KYB_IBF) && i < 200; ++i);
	DEBUGF(*dbugptr & KBDDEBUG, printf("kbd_cmd took %d iterations for busy to clear\n", i));

	/*
	 * If we waited too long, then complain 
	 */

	if (i >= 200)
		printf("ps_code (kbdcmd): input buffer won't empty\n");
	else
	{
		DEBUGF(*dbugptr & KBDDEBUG, printf("Sending command %x\n", bcmd));
		IOOUT(KYB_WRITE, bcmd);
	}

}

/*
 * Scan code generated by the Enhanced PS/2 keyboard (when using RT
 * scan code set).
 */

#define BREAK		240
#define ALT		25
#define CTL		17
#define SCROLL_LOCK	95
#define DEL		113
#define PAUSE		98
#define PRINT_SCREEN	87
#define NUM_LOCK	118
#define ESC_SC		8

/* following are in "mode" below */
#define ALT_MODE        1		/* ALT key is down */
#define CTL_MODE        2		/* CONTROL key is down */

struct kbqueue  kbqueue[NCPU] = {0};

#define SCAN1_ESCAPE	0xe0	/* next character is special */
#define SCAN1_ESCAPE2	0xe1	/* next character is extra special */
#define SCAN1_BREAK	0x80	/* bit for scan 1 break */
#define SCAN3_BREAK	0xf0	/* value for scan 3 break */
#define NONE	0x00		/* no scan code/key */

static unsigned char far scan1_table[128] = 
{ /*
 0     1     2     3     4     5     6     7     8     9    	       */
NONE, 0x29, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,	/*  0x */
0x0a, 0x0b, 0x0c, 0x0d, NONE, 0x0e, 0x0f, 0x10, 0x11, 0x12,	/*  1x */
0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x2b, 	/*  2x */
0x3a, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,	/*  3x */
0x27, 0x28, 0x2b, 0x1c, 0x2a, 0x56, 0x2c, 0x2d, 0x2e, 0x2f,	/*  4x */
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, NONE, 0x36, 0x1d, NONE,	/*  5x */
0x38, 0x39, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,	/*  6x */
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,	/*  7x */
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,	/*  8x */
0x45, 0x47, 0x4b, 0x4f, NONE, NONE, 0x48, 0x4c, 0x50, 0x52,	/*  9x */
0x37, 0x49, 0x4d, 0x51, 0x53, 0x4a, 0x4e, NONE, NONE, NONE,	/* 10x */
0x01, NONE, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42,	/* 11x */
0x43, 0x44, 0x57, 0x58, 0x54, 0x46, NONE };			/* 12x */

static unsigned char scan3_table[128] = 
{ /*
 0     1     2     3     4     5     6     7     8     9    	       */
0x00, 0x0e, 0x16, 0x1e, 0x26, 0x25, 0x2e, 0x36, 0x3d, 0x3e,	/*  0x */
0x46, 0x45, 0x4e, 0x55, NONE, 0x66, 0x0d, 0x15, 0x1d, 0x24,	/*  1x */
0x2d, 0x2c, 0x35, 0x3c, 0x43, 0x44, 0x4d, 0x54, 0x5b, 0x5c,	/*  2x */
0x14, 0x1c, 0x1b, 0x23, 0x2b, 0x34, 0x33, 0x3b, 0x42, 0x4b,	/*  3x */
0x4c, 0x52, 0x53, 0x5a, 0x12, 0x13, 0x1a, 0x22, 0x21, 0x2a,	/*  4x */
0x32, 0x31, 0x3a, 0x41, 0x49, 0x4a, NONE, 0x59, 0x11, NONE,	/*  5x */
0x19, 0x29, 0x39, NONE, 0x58, NONE, NONE, NONE, NONE, NONE,	/*  6x */
NONE, NONE, NONE, NONE, NONE, 0x67, 0x64, NONE, NONE, 0x61,	/*  7x */
0x6e, 0x65, NONE, 0x63, 0x60, 0x6f, 0x6d, NONE, NONE, 0x6a,	/*  8x */
0x76, 0x6c, 0x6b, 0x69, NONE, 0x77, 0x75, 0x73, 0x72, 0x70,	/*  9x */
0x7e, 0x7d, 0x74, 0x7a, 0x71, 0x84, 0x7c, NONE, 0x79, NONE,	/* 10x */
0x08, NONE, 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37, 0x3f,	/* 11x */
0x47, 0x4f, 0x56, 0x5e, 0x57, 0x5f, 0x62 };			/* 12x */

static unsigned char far scan1_to_scan3[256];


/*
 * determine if we are using scan code set 1. If not we just call
 * kbscan to process the scan code set 3 value. 
 * if using scan code set 1 then we map the scan code to the appropriate
 * scan code set 3 value and invoke kbscan.
 */

/*
 * build the mapping table from scan 1 codes to scan 3 codes 
 */
static void scan_init()
{
	unsigned int i, j;

	DEBUGF(*dbugptr & KBDDEBUG, printf("scan_init:\n"));

	for (i=0; i<128; ++i) {
		j = scan1_table[i];
		if (j != NONE && scan1_to_scan3[j] == NONE)
			scan1_to_scan3[j] = scan3_table[i];
/*		DEBUGF(*dbugptr & KBDDEBUG,
			printf("scan1_to_scan3[%x] = %x (%x)\n",j,
			scan1_to_scan3[j], scan3_table[i], i));		*/
	}
	/*
	 * handle special cases where we first have E0 then
	 * the scan code set 1 code, this allows use of all
	 * keyboard keys even when in PC compatible scan code
	 * sets.
	 */
	scan1_to_scan3[0x38+0x80] = 0x39;	/* key 62 */
	scan1_to_scan3[0x1d+0x80] = 0x58;	/* key 64 */
	scan1_to_scan3[0x52+0x80] = 0x67;	/* key 75 */
	scan1_to_scan3[0x53+0x80] = 0x64;	/* key 76 */
	scan1_to_scan3[0x4b+0x80] = 0x61;	/* key 79 */
	scan1_to_scan3[0x47+0x80] = 0x6e;	/* key 80 */
	scan1_to_scan3[0x4f+0x80] = 0x65;	/* key 81 */
	scan1_to_scan3[0x48+0x80] = 0x63;	/* key 83 */
	scan1_to_scan3[0x50+0x80] = 0x60;	/* key 84 */
	scan1_to_scan3[0x49+0x80] = 0x6f;	/* key 85 */
	scan1_to_scan3[0x51+0x80] = 0x6d;	/* key 86 */
	scan1_to_scan3[0x4d+0x80] = 0x6a;	/* key 89 */
	scan1_to_scan3[0x35+0x80] = 0x77;	/* key 95 */
	scan1_to_scan3[0x1c+0x80] = 0x79;	/* key 108 */
	scan1_to_scan3[0x37+0x80] = 0x57;	/* key 124 */
	scan1_to_scan3[0x45+0x80] = 0x62;	/* key 126 */
	scan1_to_scan3[0x46+0x80] = 0x62;	/* key 126 */

	if (option_flag&OPTION_AT) {
		scan1_to_scan3[0x37] = 0x57; /* PrtSc */
		scan1_to_scan3[0x54] = 0x62; /* SysReq */
	}
	DEBUGF(*dbugptr & KBDDEBUG,
		for (i=0; i<256; ++i) {
			printf("%02x ", scan1_to_scan3[i]);
			if ((i&0xf) == 0xf)
				printf("%02x...%02x\n",i&0xf0,i);
		} );
}
					
void
kbintr(sc,old_stack)
	u_short         sc;
	struct i_stack far *old_stack;
{
	u_short		temp;
	static u_short	lastscan;
#ifdef DEBUG
	struct kbd_trace {
		char far *stack;
		u_short	ip;
		u_short cs;
		u_short flags;
		u_short	sc;
	} kbd_trace;
	kbd_trace.sc = sc;
	kbd_trace.stack = (char far *)old_stack;
	kbd_trace.ip = old_stack->IP;
	kbd_trace.cs = old_stack->CS;
	kbd_trace.flags = old_stack->flags;
#endif /* DEBUG */

	TRACE(TRACE_KBDINT,&kbd_trace,sizeof(kbd_trace));
	DEBUGF(*dbugptr & KBDDEBUG, rbprintf("kbintr: %x\n",sc));
	sc &= 0xff;		/* mask off top byte */

	if ((option_flag&OPTION_SCAN1) == 0) {
		kbscan(sc);
		return;
	}

/*
 * when using scan code set 1 we use the scan1_to_scan3
 * translation table, also taking care of the difference
 * in make/break indication.
 * an added complication is the addition of a flag (SCAN1_ESCAPE)
 * that indicates an extended scan code.
 */
	if (lastscan == SCAN1_ESCAPE2) {
		if ((sc&~SCAN1_BREAK) == 0x1d)
			return;
		lastscan = SCAN1_ESCAPE;	/* lets pretend */
	}
	if (lastscan != SCAN1_ESCAPE) {
		temp = scan1_to_scan3[sc&~SCAN1_BREAK];
		DEBUGF(*dbugptr & KBDDEBUG, rbprintf("kbintr: scan1_to_scan3[%x] = %x\n",sc&~SCAN1_BREAK,scan1_to_scan3[sc&~SCAN1_BREAK]));
		if (temp) {
			if (sc&SCAN1_BREAK)
				kbscan(SCAN3_BREAK);
			kbscan(temp);	/* pass it along */
		}
	} else {
		if (temp = scan1_to_scan3[sc|0x80]) {
			if (sc&SCAN1_BREAK)
				kbscan(SCAN3_BREAK);
			kbscan(temp);	/* pass it along */
		}
	}
	lastscan = sc;
}

void
kbscan(sc)
	u_short         sc;
{

	int             i;
	static int      mode;
	static int      make_break;
	extern struct msdata msdata;

	DEBUGF(*dbugptr & KBDDEBUG, rbprintf("kbscan: %x\n",sc));
	/*
	 * we watch the scan codes to look for the system attention sequence:
	 * control+alt+scroll_lock. If we see it then we send a level zero
	 * interrupt to the romp (which should kick us into the debugger). 
	 * we also watch for:
	 * control+alt+del	set flag for return to DOS 
	 * control+alt+pause	reboot
	 */
	i = LOBYTE(sc);		/* get the scan code */
	switch (i)
	{
	case BREAK:
		make_break = 1;
		break;
	case ALT:
		if (make_break)
		{
			mode &= ~ALT_MODE;
			make_break = 0;
		} else
			mode |= ALT_MODE;
		break;
	case CTL:
		if (make_break)
		{
			mode &= ~CTL_MODE;
			make_break = 0;
		} else
			mode |= CTL_MODE;
		break;
	case DEL:
		if (!make_break && mode == (CTL_MODE | ALT_MODE))
			do_exit = 1;
		if (fake_mouse)
			if (kbd_mouse(mode,make_break,sc&0xff))
				if (make_break)
					sc = NOP_SC;		/* nop it */
				else
					return;
		make_break = 0;
		break;
	case ESC_SC:
		if (!make_break && mode == (CTL_MODE | ALT_MODE))
		{
			next_screen();	/* go to next screen in group */
			return;
		}
		make_break = 0;
		break;
	case PAUSE:
		if (!make_break && mode == (CTL_MODE | ALT_MODE))
			do_reboot = 1;
		make_break = 0;
		break;
#ifdef DEBUG
	case NUM_LOCK:
		if (!make_break && mode == (CTL_MODE | ALT_MODE))
			trace_print();
		make_break = 0;
		break;
#endif
	case PRINT_SCREEN:
		if (!make_break && mode == (CTL_MODE | ALT_MODE))
		{
			fake_mouse = !fake_mouse;
			kbd_mouse_reset();
			return;	/* ignore the character */
		}
	case SCROLL_LOCK:
		if (!make_break && mode == (CTL_MODE | ALT_MODE))
		{
			rompint0();
			return;	/* ignore the character */
		}
		/* fall thru to default case */
	default:
		if (fake_mouse)
			if (kbd_mouse(mode,make_break,sc))
				if (make_break)
					sc = NOP_SC;		/* nop it */
				else
					return;
		make_break = 0;
	}
	sc = (sc&0xff) | (KBD_DATA<<8);
	kbsend(sc,screen_cbcb);
}

void
kbsend(sc,cbcb)
	u_short         sc;
	struct cbcb    *cbcb;
{
	register struct kbdata *kb = &kbdata[cbcb->cpu];
	register struct kbqueue *kbq = &kbqueue[cbcb->cpu];
	int             i;
	int		s;
	/*
	 * If we are called from interrupt level then check to see if the
	 * previous scan code was processed by unix (kbstatus = 0 if it was.)
	 * If the scan code was not processed then we place it into a queue
	 * and exit. 
	 *
	 * We arrange that we do not resend a HOT_KEY event if there is 
	 * already one in the queue being sent.
	 *
	 * If it WAS processed then we need to check to see if there are any
	 * scan codes pending in the queue, if there is then queue this one
	 * and process the next item in the queue. 
	 */

	DEBUGF(*dbugptr & KBDDEBUG, rbprintf("       send %x\n",sc));
	s = spl();
	if (kb->kbstatus != KBD_EMPTY || msdata.status)
					/* If previous code still being */
	{				/* processed, then queue this   */
					/* one and exit.                */
		i = HIBYTE(sc);
		if (i != KBD_HOT_KEY ||
				i != kb->kbstatus || LOBYTE(sc) != kb->kbsc)
			queuekb(sc,cbcb);
		splx(s);
		return;
	}
	if ((i = kbq->head) != kbq->tail)	/* If queue is not empty  */
	{
		queuekb(sc,cbcb);	/* queue this scan code   */
		sc = kbq->buff[i];	/* get the one at the head */
		i = (i + 1) % MAXKBBUFF;	/* bump head index        */
		kbq->head = i;
	}
	kbforward(sc,cbcb);
	splx(s);
}


/*
 * take the given scan code (sc) and send it to the
 * ROMP. If forwarding interrupts then send an interrupt
 * too.
 */
kbforward(sc,cbcb)
	u_short         sc;
	struct cbcb    *cbcb;
{
	register struct kbdata *kb = &kbdata[cbcb->cpu];

	kb->kbsc = LOBYTE(sc);
	kb->kbstatus = HIBYTE(sc);
	if (kb->fwd_int)
		rompint3(3,FAKEPOLL(KBIRQ), 0, 0, cbcb);
}


/*
 * take the given scan code (sc) and queue it 
 * for later transmission to the ROMP.
 */
static void
queuekb(sc,cbcb)
	u_short         sc;
	struct cbcb    *cbcb;
{
	register struct kbqueue *kbq = &kbqueue[cbcb->cpu];
	register int    i;


	i = kbq->tail;
	kbq->buff[i] = sc;
	i = (i + 1) % MAXKBBUFF;
	if (i != kbq->head)
		kbq->tail = i;
}

reset_kbd()
{
	RESET_VECTOR(0x15, kbd_save);

	if (option_flag&OPTION_OLDKBD) {
		RESET_VECTOR(0x09, kbd1_save);
	}

	if (option_flag&OPTION_SCAN1)
		return;		/* that's it */

	kbd_reset(0);
	clear_kbd(40);		/* clear pipeline */
	kbd_aux_int();		/* be sure interrupts enabled */
	init_kbd_flag = 0;
}

/* 
 * wait for input buffer to empty
 */
static int kbd_in_empty()
{
	register int	i;

	for (i = 0; (IOIN(kyb_cntir) & KYB_IBF) && i < 200; ++i);
	DEBUGF(*dbugptr & KBDDEBUG, printf("kbd_cmd took %d iterations for busy to clear\n", i));
	if (i >= 200)
	{

		printf("kbd_cmd: input buffer won't empty\n");
		return (1);
	}

	return (0);
}

/* 
 * wait for keyboard to ack or timeout
 */
static int kbd_cmd_ack(iidptr)
	int	       *iidptr;
{
	register int	i;
	int	iid;

	for (i = 0; i < 1000; i++)
	{
		iid = (int) IOIN(kyb_read);
		if (iid == KBR_ACK)
			break;
	}
	*iidptr = iid;
	DEBUGF(*dbugptr & KBDDEBUG, printf("kbd_cmd: resp to cmd after %d iterations = %x\n", i, iid));
	if (i >= 1000)
	{
		printf("kbd_cmd: command not acknowledged!\n");
		return (2);
	}
	return (0);
}

/*
 * preload the keyboard buffer with the script file
 */

void kbd_script(file)
char *file;
{
	int fd, l, i;
	unsigned char buff[MAXKBBUFF+1];

	if ((fd = open(file, 0)) < 0) {
		printf("couldn't open input script file %s\n",file);
		exit(1);
	}

	if ((l = read(fd, buff, sizeof buff)) > 0) {
		if (l > MAXKBBUFF) {
			printf("input script file %s: only %d bytes used\n",file,MAXKBBUFF);
			l = MAXKBBUFF;
		}
		for (i=0; i<l; ++i)
			queuekb(buff[i] | (KBD_ASCII<<8), cbcbptr);
	}
	close(fd);
}

/*
 * function invoked from irq1 interrupts for older PC's that don't have
 * the keyboard intercept bios code.
 * we read the scan code, call kbintr and then do a specific eoi to clear
 * the interrupt and return.
 */

#define SEOI	0x60		/* specific EOI command */

void kbd_int(old_stack)
	struct i_stack far *old_stack;
{
	int c;

	c = (int) IOIN(KYB_READ);
	DEBUGF(*dbugptr & KBDDEBUG, rbprintf("kbd_int: %x\n",c));
	kbintr(c,old_stack);
	eoi_master(1+SEOI);
}

/*
 *
 *	We simulate the PS/2 planar mouse (for use on an AT) and also for the case
 *	where a mouse is missing or inoperative.
 *
 *	We use the numeric pad that we put into mouse simulation mode by means of
 *	control-alt-print_screen. when in that mode the 8 keys around the 5 key move
 *	the mouse in the appropriate way. The 0 key simulates the left button, the
 *	. key simulates the right button. Holding down the 5 key causes the mouse 
 *	movements to be limited (a single movement key then generates a 1 x or y 
 *	increment.
 *
 *	DATA FORMAT:
 *		3-BYTE BINARY
 *
 *
 *	BYTE 1 - Status/Sync
 *
 *		Bit 0	Left button status	1 = Depressed
 *		Bit 1	Right button status	1 = Depressed
 *		Bit 2	Sync bit		Always 0
 *		Bit 3	Sync bit		Always 1
 *		Bit 4	X Data Sign		1 = Negative
 *		Bit 5	Y Data Sign		1 = Negative
 *		Bit 6	X Data Overflow		1 = Overflow
 *		Bit 7	Y Data Overflow		1 = Overflow
 *
 *	BYTE 2 - X Data Byte  (Two's complement, Bit 0 = LSB)
 *
 *	BYTE 3 - Y Data Byte  (Two's complement, Bit 0 = LSB)
 *
 */
#define XNEG		0x10
#define YNEG		0x20

#define ATR_RIGHT_BUT	0x02
#define ATR_LEFT_BUT	0x01
#define BUT_MASK	0x03
#define ATR_SYNC	0x08	/* always present */

#define NUM_1	0x69	/* lower left */
#define NUM_2	0x72	/* down arrow */
#define NUM_3	0x7a
#define NUM_4	0x6b	/* left arrow */
#define NUM_5	0x73
#define NUM_6	0x74	/* right arrow */
#define NUM_7	0x6c
#define NUM_8	0x75	/* up arrow */
#define NUM_9	0x7d
#define NUM_0	 0x70	/* insert key */
#define NUM_DOT	0x71	/* dot key */
#define NUM_SLASH 0x77
#define NUM_STAR  0x7e
#define NUM_MINUS 0x84

#define XY_MASK	0xff

char far key_state[256];		/* if its up or down */
static u_short		ms_status=0, msstatus_last=0;

#define XY_INC		5	/* basic increment shift amount */
#define XY_FAST		1	/* how much faster repeat gets */

#define XY_BASIC	(1<<XY_INC)
#define XY_SLOW		XY_INC	/* how much to slow down */

static void kbd_mouse_reset()
{
	int i;
	for (i=0; i<256; ++i)
		key_state[i] = 0;
	ms_status = 0;
	msstatus_last = 0;
}

static int kbd_mouse(mode, make_break, sc)
	u_short mode, make_break, sc;
{

	extern u_short		msstatus, msx, msy;
	short			x=0, y=0;	/* must be signed! */

	msstatus = ATR_SYNC | ms_status;

	switch(sc)
		{
	case NUM_4:		/* left arrow */
		if (!make_break)
			x = -XY_BASIC;
		break;
	case NUM_8:		/* up arrow */
		if (!make_break)
			y = XY_BASIC;
		break;
	case NUM_6:		/* right arrow */
		if (!make_break)
			x = XY_BASIC;
		break;
	case NUM_2:		/* down arrow */
		if (!make_break)
			y = -XY_BASIC;
		break;
	case NUM_3:		/* right down arrow */
		if (!make_break)
			x = XY_BASIC, y = -XY_BASIC;
		break;
	case NUM_7:		/* left up arrow */
		if (!make_break)
			x = -XY_BASIC, y = XY_BASIC;
		break;
	case NUM_9:		/* right up arrow */
		if (!make_break)
			x = XY_BASIC, y = XY_BASIC;
		break;
	case NUM_1:		/* left down arrow */
		if (!make_break)
			x = -XY_BASIC, y = -XY_BASIC;
		break;
	case NUM_0:		/* 0 (left button) */
	case NUM_SLASH:		/* / (left button) */
		if (!make_break)
			ms_status |= ATR_LEFT_BUT;
		else
			ms_status &= ~ATR_LEFT_BUT;
		msstatus = ATR_SYNC | ms_status;
		break;
	case NUM_STAR:		/* * (middle button) */
		if (!make_break)
			ms_status |= ATR_LEFT_BUT|ATR_RIGHT_BUT;
		else
			ms_status &= ~(ATR_LEFT_BUT|ATR_RIGHT_BUT);
		msstatus = ATR_SYNC | ms_status;
		break;
	case NUM_MINUS:		/* - (right button) */
	case NUM_DOT:		/* . (right button) */
		if (!make_break)
			ms_status |= ATR_RIGHT_BUT;
		else
			ms_status &= ~ATR_RIGHT_BUT;
		msstatus = ATR_SYNC | ms_status;
		break;
	case NUM_5:
		if (!make_break && (option_flag&OPTION_SCAN1)) {
			key_state[sc] = !key_state[sc];	/* invert it */
			return(1);
		}
		break;
	default:
		return(0);		/* not a mouse key */
		}
	if (x) {
		if (key_state[sc])
			x <<= XY_FAST;		/* repeat makes it faster */
		if (key_state[NUM_5])
			x >>= XY_SLOW;		/* and 5 makes it slower */
		if ((short) x < 0)
			msstatus |= XNEG;
	}
	if (y) {
		if (key_state[sc])
			y <<= XY_FAST;
		if (key_state[NUM_5])
			y >>= XY_SLOW;
		if ((short) y < 0)
			msstatus |= YNEG;
	}
	if ((option_flag&OPTION_SCAN1) == 0) {	/* otherwise not make/break */
		if (make_break)
			key_state[sc] = 0;
		else
			key_state[sc] = 1;	/* now its up */
	}
	if (msstatus != msstatus_last || x || y) {
		msx = x&XY_MASK; msy = y&XY_MASK;
		DEBUGF(*dbugptr & KBDDEBUG, rbprintf("break=%x msstatus=%04x x=%02x y=%02x\n",
			make_break, msstatus, msx, msy));
		mouseint(0,0);			/* simulate the mouse */
	}
	msstatus_last = msstatus;
	return(1);			/* processed it */
}


reset_sysflag()
{
	int c;

	s8042_cmd(CMD(CC_READ, 0));
	c = (IOIN(KYB_READ) & 0x02) | CCB_OBFINT;
	s8042_cmd(CMD(CC_WRITE, c));
}
