/*
 * keybrd.pc:
 *	functions, maps and interrupt handlers for IBM PC keyboards
 *
 * Copyright (c) 1989 University of Toronto. All rights reserved.
 * Anyone may use or copy this software, except that it may not be
 * sold for profit, that this copyright notice remain intact, and that
 * credit is given where it is due. The University of Toronto and the
 * author make no warranty and accept no liability for this software.
 */
static char rcsid[] = "$Header: /homes/ugw/pkern/src/cterm/RCS/keybrd.pc,v 1.6 89/10/04 19:49:57 pkern Exp $";

#include <dos.h>
#include "cterm.h"
#include "pc.h"

#define word	unsigned int

uchar keyboom=0;

static union REGS r;
static uchar f_map=0, f_vec=0, kb_mode=0;

extern uchar flowctl, local, xoff;

#ifdef KB_INT
#define INTA00	0x20
#define KB_DATA	0x60
#define KB_CTL	0x61
#define EOI	0x20

static word scan[89 * 5] = {	/* scan code re-map table */
/* map-codes (norm, shift, ctrl, alt) &  hit-flag */
	0, 0, 0, 0, 0,
#ifdef MVESC
	'`', '~', 0, 0, 0,
#else
	27, 27, 27, 0, 0,			/* Esc */
#endif
	'1', '!', 0, 0x7800, 0,
	'2', '@', 0x300, 0x7900, 0,
	'3', '#', 0, 0x7a00, 0,
	'4', '$', 0, 0x7b00, 0,
	'5', '%', 0, 0x7c00, 0,
	'6', '^', 0x1e, 0x7d00, 0,
	'7', '&', 0, 0x7e00, 0,
	'8', '*', 0, 0x7f00, 0,
	'9', '(', 0, 0x8000, 0,
	'0', ')', 0, 0x8100, 0,
	'-', '_', 0x1f, 0x8200, 0,
	'=', '+', 0, 0x8300, 0,
	8, 8, 127, 127, 0,		/* backspace */
	9, 0x0f00, 0, 0, 0,		/* tab */
/* 0x10 */
	'q', 'Q', 0x11, 0x1000, 0,
	'w', 'W', 0x17, 0x1100, 0,
	'e', 'E', 0x05, 0x1200, 0,
	'r', 'R', 0x12, 0x1300, 0,
	't', 'T', 0x14, 0x1400, 0,
	'y', 'Y', 0x19, 0x1500, 0,
	'u', 'U', 0x15, 0x1600, 0,
	'i', 'I', 0x09, 0x1700, 0,
	'o', 'O', 0x0f, 0x1800, 0,
	'p', 'P', 0x10, 0x1900, 0,
	'[', '{', 0x1b, 0, 0,
	']', '}', 0x1d, 0, 0,
	13, 13, 10, 0, 0,		/* return */
/* #define CTRL	149	/* 29 * 5 + 4 */
/*	0, 0, 0, 0, 0,		/* CTRL */
#define CAPS	149	/* 29 * 5 + 4 */
	0, 0, 0, 0, 0,		/* CAPS */
	'a', 'A', 0x01, 0x1e00, 0,
	's', 'S', 0x13, 0x1f00, 0,
/* 0x20 */
	'd', 'D', 0x04, 0x2000, 0,
	'f', 'F', 0x06, 0x2100, 0,
	'g', 'G', 0x07, 0x2200, 0,
	'h', 'H', 0x08, 0x2300, 0,
	'j', 'J', 0x0a, 0x2400, 0,
	'k', 'K', 0x0b, 0x2500, 0,
	'l', 'L', 0x0c, 0x2600, 0,
	';', ':', 0, 0, 0,
	'\'', '"', 0, 0, 0,
#ifdef MVESC
	27, 27, 27, 0, 0,			/* Esc */
#else
	'`', '~', 0, 0, 0,
#endif
#define LSHIFT	214	/* 42 * 5 + 4 */
	0, 0, 0, 0, 0,		/* left SHIFT */
	'\\', '|', 0x1c, 0, 0,
	'z', 'Z', 0x1a, 0x2c00, 0,
	'x', 'X', 0x18, 0x2d00, 0,
	'c', 'C', 0x03, 0x2e00, 0,
	'v', 'V', 0x16, 0x2f00, 0,
/* 0x30 */
	'b', 'B', 0x02, 0x3000, 0,
	'n', 'N', 0x0e, 0x3100, 0,
	'm', 'M', 0x0d, 0x3200, 0,
	',', '<', 0, 0, 0,
	'.', '>', 0, 0, 0,
	'/', '?', 0, 0, 0,
#define RSHIFT	274	/* 54 * 5 + 4 */
	0, 0, 0, 0, 0,		/* right SHIFT */
	0x7200, 0x7200, 0x7200, 0x7200, 0,	/* PrtSc/* -> - */
/*	'*', 0, 0, 0, 0,			/* PrtSc/* */
#define ALT	284	/* 56 * 5 + 4 */
	0, 0, 0, 0, 0,		/* ALT */
	' ', ' ', 0x300, ' ', 0,		/* space bar */
/* #define CAPS	294	/* 58 * 5 + 4 */
/*	0, 0, 0, 0, 0,		/* Caps Lock */
#define CTRL	294	/* 58 * 5 + 4 */
	0, 0, 0, 0, 0,		/* Ctrl */

	0x3b00, 0x5400, 0x5e00, 0x6800, 0,	/* F1 */
#ifdef MVESC
	27, 27, 27, 0, 0,		/* F2 -> Esc */
#else
	0x3c00, 0x5500, 0x5f00, 0x6900, 0,	/* F2 */
#endif
	0x3d00, 0x5600, 0x6000, 0x6a00, 0,	/* F3 */
	0x3e00, 0x5700, 0x6100, 0x6b00, 0,	/* F4 */
	0x3f00, 0x5800, 0x6200, 0x6c00, 0,	/* F5 */
/* 0x40 */
	0x4000, 0x5900, 0x6300, 0x6d00, 0,	/* F6 */
	0x4100, 0x5a00, 0x6400, 0x6e00, 0,	/* F7 */
	0x4200, 0x5b00, 0x6500, 0x6f00, 0,	/* F8 */
	0x4300, 0x5c00, 0x6600, 0x7000, 0,	/* F9 */
	0x4400, 0x5d00, 0x6700, 0x7100, 0,	/* F10 */

	0x8d00, 0x8d00, 0x8d00, 0x8d00, 0,	/* Num Lock/Pause -> PF2 */
	0x8e00, 0x8e00, 0x100, 0x8e00, 0,	/* Scroll Lock/Break -> PF3 */
	0x4700, 0x4700, 0x7700, 0x4700, 0,	/* 7/Home */
	0x4800, 0x4800, 0x4800, 0x6e00, 0,	/* 8/<up> */
	0x4900, 0x4900, 0x8400, 0x4900, 0,	/* 9/PgUp */
	0x4a00, 0x4a00, 0x4a00, 0x4a00, 0,	  /* - -> ' */
	0x4b00, 0x4b00, 0x7300, 0x7000, 0,	/* 4/<left> */
	0x4c00, 0x4c00, 0x4c00, 0x4c00, 0,	/* 5 */
	0x4d00, 0x4d00, 0x7400, 0x7100, 0,	/* 6/<right> */
	0x4e00, 0x4e00, 0x4e00, 0x4e00, 0,	  /* + -> ENTER */
	0x4f00, 0x4f00, 0x7500, 0x4f00, 0,	/* 1/End */
/* 0x50 */
	0x5000, 0x5000, 0x5000, 0x6f00, 0,	/* 2/<down> */
	0x5100, 0x5100, 0x7600, 0x5100, 0,	/* 3/PgDn */
	0x5200, 0x5200, 0x5200, 0x5200, 0,	/* 0/Ins */
	0x5300, 0x5300, 0x5300, 0x5300, 0,	/* ./Del */
	0x8f00, 0x8f00, 0x8f00, 0x8f00, 0,	  /* Sys Req -> PF4 */
	0, 0, 0, 0, 0,
	'\\', '|', 0x1c, 0, 0,				/* non-US keyboards */
	0x5700, 0x5700, 0x5700, 0x5700, 0,	    /* F11 */
	0x5800, 0x5800, 0x5800, 0x5800, 0,	    /* F12 */
	0x5900, 0x5900, 0x5900, 0x5900, 0,
	0x5a00, 0x5a00, 0x5a00, 0x5a00, 0,
	0x5b00, 0x5b00, 0x5b00, 0x5b00, 0,
	0x5c00, 0x5c00, 0x5c00, 0x5c00, 0,
	0x5d00, 0x5d00, 0x5d00, 0x5d00, 0,
	0x5e00, 0x5e00, 0x5e00, 0x5e00, 0,
	0x5f00, 0x5f00, 0x5f00, 0x5f00, 0,
/* 0x60 */
	0, 0, 0, 0, 0,		/* Extension 0 -- 0xe0 */
	0, 0, 0, 0, 0		/* Extension 1 -- 0xe1 */
};

static word keybuf[256];
static uchar kbin=0, kbout=0, autorep;

void interrupt (*o_int)();

void interrupt
k_int()
{
	uchar ctl;
	word code, hit;

	code = inportb(KB_DATA);
	ctl = inportb(KB_CTL);
	outportb(KB_CTL, ctl | 0x80);
	outportb(KB_CTL, ctl);
	enable();

	ctl = code;
	code = (code & 0x7f) * 5;

	hit = ((ctl & 0x80) == 0);	/* 0x80: key up */
	
	if (!hit || !scan[code]) {
	    /* key has just lifted or it's a "shift" key */
		code += 4;
		scan[code] = hit;
		ctl = 0;
		switch (code) {
		case ALT:	ctl = 0x08; break;
		case CTRL:	ctl = 0x04; break;
		case LSHIFT:	ctl = 0x02; break;
		case RSHIFT:	ctl = 0x01; break;
		case CAPS:	ctl = 0x40;
			hit = (scan[CAPS-1] ^= hit);
			break;
		}
		if (ctl) {
			if (hit) kb_mode |= ctl;
			else	 kb_mode &= ~ctl;
		}
	}
	else if (!scan[code+4] || autorep) {
		scan[code+4] = hit;

		ctl = kb_mode & 0x43;
		if (kb_mode & 0x08)		/* ALT */
			code += 3;
		else if (kb_mode & 0x04)	/* CTRL */
			code += 2;
		else if (ctl == 0x40)		/* CAPS */
			code += 1;
		else if (0 < ctl && ctl < 4)	/* SHIFT */
			code += 1;

		code = scan[code];
		if (code & 0xf0ff)
			keybuf[kbin++] = code;
		else switch (code >> 8) {	/* special ! */
			case 1:		/* Ctrl Break */
				keyboom++; /* we go bye now! */
				break;
			case 0x3:	/* ctrl-2@, ctrl-space */
			case 0xf:	/* back-tab */
				keybuf[kbin++] = code;
				break;
			default:
				break;
		}
	}

	outportb(INTA00, EOI);
}

kbdhit()
{
	return(kbin != kbout);
}

void
kbflush()
{
	kbin = kbout;
}

void
kbautorep(sw)
int sw;
{
	autorep = sw;
}

word
kbdget()
{
	if (f_vec)
		return(keybuf[kbout++]);
	/* else use regular bios */
	r.h.ah = 0; int86(KBD_INT, &r, &r);
	if (r.x.ax & 0xff)
		r.x.ax &= 0xff;
	return((word) r.x.ax);
}

#else	/* KB_INT */

/* check for queued keys */
kbdhit()
{
	r.h.ah = 1;
	int86(KBD_INT, &r, &r);
	return (!(r.x.flags & Zflag));	/* ie. ! Z-flag */
}

/* flush keyboard type-ahead queue */
void
kbflush()
{
	r.h.ah = 3;
	int86(KBD_INT, &r, &r);
}

/* toggle keyboard autorepeat */
void
kbautorep(sw)
int sw;	/* switch : 1 = on, 0 = off */
{
	r.h.ah = 4;
	r.h.al = (sw) ? 1 : 2;
	int86(KBD_INT, &r, &r);
}

/* get keyboard word */
word
kbdget()
{
	r.h.ah = 2; int86(KBD_INT, &r, &r);
	kb_mode = r.h.al;
	r.h.ah = 0; int86(KBD_INT, &r, &r);
	if (r.x.ax & 0xff)
		r.x.ax &= 0xff;
	return((word) r.x.ax);
}
#endif /* KB_INT */

/* return a keypadv[] offset | 8-bit alternative */
kbdfn(kn)
word kn;
{
	word fn;
	extern word keymap[], alt8[];

	kn = kn >> 8;
	fn = alt8[kn] & 0x7f;
	if (fn) {
		if (kb_mode & 0x04)	/* Ctrl key */
			fn = (alt8[kn] >> 8) & 0x3f;
		else if (kb_mode & 0x03)	/* Shift keys */
			fn = alt8[kn] >> 8;
		fn |= 0x80;
	}
	return((fn << 8) | keymap[kn]);
}

/*
 *  keypad map (aN == alt N)
 * +-----+-----+-----+-----+
 * | PF1 | PF2 | PF3 | PF4 |		cursor code offsets:
 * | aF1 | aF2 | aF3 | aF4 |			UP - 0
 * +-----+-----+-----+-----+		      DOWN - 1
 * |  7  |  8  |  9  |  -  |		     RIGHT - 2
 * | a1  | a2  | a3  | a4  |		      LEFT - 3
 * +-----+-----+-----+-----+
 * |  4  |  5  |  6  |  ,  |	keypad code string offsets:
 * | aQ  | aW  | aE  | aR  |	8 - "0"	 10 - "8"	18 - "F3"
 * +-----+-----+-----+-----+	9 - "1"  11 - "9"	19 - "F4"
 * |  1  |  2  |  3  |     |	A - "2"  12 - "-"	1A - "\200"
 * | aA  | aS  | aD  |Enter|	B - "3"  13 - ","	1B - "\b"
 * +-----+-----+-----+     |	C - "4"  14 - "."	1C - "\003"
 * |     0     |  .  | aF  |	D - "5"  15 - "ENTER"	1D - "\r\n"
 * | aZ    aX  | aC  | aV  |	E - "6"  16 - "F1"	1E *-> ansback
 * +-----------+-----+-----+	F - "7"  17 - "F2"	1F - ""	 (empty)
 */

/*
 * keymap:
 *	keypadv[] offsets for all extended function codes
 */
static word keymap[0x90] = {
/* 0x0_ */	0x1f, 0x1f, 0x1f, 0x1a, 0x1f, 0x1f, 0x1f, 0x1f,
		0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x03,
/* 0x1_ */	0x0c, 0x0d, 0x0e, 0x13, 0x1f, 0x1f, 0x1f, 0x1f,
		0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x09, 0x0a,
/* 0x2_ */	0x0b, 0x15, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
		0x1f, 0x1f, 0x1f, 0x1f, 0x08, 0x08, 0x14, 0x15,
/* 0x3_ */	0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
		0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1b, 0x1f,
/* 0x4_ */	0x1f, 0x00, 0x01, 0x03, 0x02, 0x1f, 0x1f, 0x0f,
		0x10, 0x11, 0x13, 0x0c, 0x0d, 0x0e, 0x15, 0x09,
/* 0x5_ */	0x0a, 0x0b, 0x08, 0x14, 0x1f, 0x1f, 0x1f, 0x1f,
		0x1f, 0x1f, 0x00, 0x01, 0x03, 0x02, 0x1f, 0x1f,
/* 0x6_ */	0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x01, 0x03, 0x02,
		0x16, 0x17, 0x18, 0x19, 0x1f, 0x1f, 0x00, 0x01,
/* 0x7_ */	0x03, 0x02, 0x12, 0x03, 0x02, 0x1f, 0x01, 0x1f,
		0x0f, 0x10, 0x11, 0x12, 0x1f, 0x1f, 0x1f, 0x1f,
/* 0x8_ */	0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x1f, 0x1f, 0x1f,
		0x00, 0x01, 0x02, 0x03, 0x16, 0x17, 0x18, 0x19
};

/*
 * alt8:
 *	keymap[] ALTernative with 8th bit set
 *	(Turbo C direct storage: 'lsb msb')
 */
static word alt8[0x90] = {
/* 0x0_ */	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x1_ */	'qQ', 'wW', 'eE', 'rR',
		'tT', 'yY', 'uU', 'iI',
		'oO', 'pP', 0, 0, 0, 0, 'aA', 'sS',
/* 0x2_ */	'dD', 'fF', 'gG', 'hH',
		'jJ', 'kK', 'lL', 0, 0, 0, 0, 0,
		'zZ', 'xX', 'cC', 'vV', 
/* 0x3_ */	'bB', 'nN', 'mM',
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x4_ */	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x5_ */	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x6_ */	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x7_ */	0, 0, 0, 0, 0, 0, 0, 0,
		'1!', '2@', '3#', '4$',
		'5%', '6^', '7&', '8*',
/* 0x8_ */	'9(', '0)', '-_', '=+',
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

/* k_brk -- intercept Ctrl-Break interrupt */
void interrupt
k_brk(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags)
word bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flags;
{
	send_brk();
	flags |= Cflag;		/* ie. don't queue it */
	keyboom++;
}	

/*
 * o_brk : temp storage for old interrupt vectors
 */
void interrupt (*o_brk)();

void
init_kbd()
{
	if (f_vec) return;

	kbflush();

	/* remember old intr vector(s) */
	o_brk = getvect(KEYBRK);
#ifdef KB_INT
	o_int = getvect(KB_INT);
#endif
	/* install new one(s) */
	disable(); /* */
	setvect(KEYBRK, k_brk);
#ifdef KB_INT
	setvect(KB_INT, k_int);
#endif
	enable(); /* */

	f_vec = 1;	/* vector(s) installed */

	/* some important key mappings */
	keymap[K_SETUP >> 8] = 0x80;	/* flag the SETUP key */
#ifndef MVESC
	keymap[(K_SETUP >> 8)+1] = 0x80;	/* ditto on F2 */
#endif
	keymap[K_shBRK >> 8] = 0x40;	/* shiftBrk - hang up */
	keymap[K_NUL >> 8] = 0x41;	/* ctrl-2@  - send NUL */
	keymap[K_BREAK >> 8] = 0x42;	/* BREAK    - send BREAK */
	keymap[K_BKSP >> 8] = 0x1b;	/* backsp keypadv[] position */
	keymap[K_cBRK >> 8] = 0x1e;	/* ctrlBrk keypadv[] position */
	keyboom = 0;
}

void
reset_kbd()
{
	if (!f_vec) return;	/* no vectors to reset, yet */

	kbflush();

	/* restore old intr vector(s) */
	disable();
	setvect(KEYBRK, o_brk);
#ifdef KB_INT
	setvect(KB_INT, o_int);
#endif
	enable();

	f_vec = 0;	/* vector(s) reset */
}

/* switch newline mode */
void
nlmod(sw)
uchar sw;
{
	/* shift ENTER offset, if needed */
	keymap[0x21] = keymap[0x2f] = ((sw) ? 0x1d : 0x15);
}
