/*
 * DANG_BEGIN_MODULE
 * 
 * This handles the keyboard.
 * 
 * Two keyboard modes are handled 'raw' and 'xlate'. 'Raw' works with codes
 * as sent out by the kernel and 'xlate' uses plain ASCII as used over
 * serial lines. The mapping for different languages & the two ALT-keys is
 * done here, but the definitions are elsewhere. Only the default (US)
 * keymap is stored here.
 * 
 * DANG_END_MODULE
 * 
 * DANG_BEGIN_CHANGELOG
 * 
 * $Date: $
 * $Source: $
 * $Revision: $
 * $State: $
 * 
 * $Log: $
 * 
 * DANG_END_CHANGELOG
 */

#include "dosemu.h"
#include "base/memory.h"
#include "base/termio.h"
#include "base/shared.h"
#include "base/keyb.h"
#include "base/mouse.h"
#include "base/bios.h"
#include "base/pic.h"
#include "base/keymaps.h"
#include "base/bios_entry.h"
#include "base/ioctlq.h"

#include <termios.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/vt.h>
#include <linux/kd.h>
#include <linux/time.h>

/* Was a toggle key already port in'd */
static Boolean  ins_stat    = FALSE;
static Boolean  scroll_stat = FALSE;
static Boolean  num_stat    = FALSE;
static Boolean  caps_stat   = FALSE;
static Bit16u   altchar     = 0;
static Bit8u    resetid     = 0;
static Bit8u    firstid     = 0;
static Bit16u   queue;

static inline void put_queue(Bit16u psc)
{
  queue = psc;
}

/**************** key-area-related functions **************/
/*
 * we no longer ignore any changes a user program has made...this is
 * good.  Now, we can let users mess around with our ALT flags, as we use
 * no longer use those to change consoles.
 */

static void set_kbd_flag(int flag)
{
  WRITE_WORD(KBDFLAG_ADDR, READ_WORD(KBDFLAG_ADDR) | (1 << flag));
}

static void clr_kbd_flag(int flag)
{
  WRITE_WORD(KBDFLAG_ADDR, READ_WORD(KBDFLAG_ADDR) & ~(1 << flag));
}

static void chg_kbd_flag(int flag)
{
  WRITE_WORD(KBDFLAG_ADDR, READ_WORD(KBDFLAG_ADDR) ^ (1 << flag));
}

int kbd_flag(int flag)
{
  return ((READ_WORD(KBDFLAG_ADDR) >> flag) & 1);
}

/*
 * These are added to allow DOSEMU process to keep its own flags on
 * keyboard status
 */

static void set_key_flag(int flag)
{
  WRITE_WORD(KEYFLAG_ADDR, READ_WORD(KEYFLAG_ADDR) | (1 << flag));
}

static void clr_key_flag(int flag)
{
  WRITE_WORD(KEYFLAG_ADDR, READ_WORD(KEYFLAG_ADDR) & ~(1 << flag));
}

static void chg_key_flag(int flag)
{
  WRITE_WORD(KEYFLAG_ADDR, READ_WORD(KEYFLAG_ADDR) ^ (1 << flag));
}

static int key_flag(int flag)
{
  return ((READ_WORD(KEYFLAG_ADDR) >> flag) & 1);
}

/*
 * Dump the keyboard buffer
 */

static void keybuf_dump(void)
{
  unsigned short *ptr = BIOS_DATA_PTR(READ_WORD(KBD_START));
  int i;

  k_printf("KEYBUFFER DUMP: 0x%02x 0x%02x\n",
	   READ_WORD(KBD_HEAD) - READ_WORD(KBD_START),
	   READ_WORD(KBD_TAIL) - READ_WORD(KBD_START));
  for (i = 0; i < 16; i++)
    k_printf("%04x ", READ_WORD(ptr + i));
  k_printf("\n");
}

/*
 * Clear the keyboard buffer
 */

void keybuf_clear(void)
{
  WRITE_WORD(KBD_HEAD, READ_WORD(KBD_START));
  WRITE_WORD(KBD_TAIL, READ_WORD(KBD_START));
  keybuf_dump();
}

/*
 * InsKeyboard returns 1 if a character could be inserted into Kbuffer
 */
static int InsKeyboard(unsigned short scancode)
{
  unsigned short nextpos;

  /* First of all compute the position of the new tail pointer */
  if ((nextpos = READ_WORD(KBD_TAIL) + 2) >= READ_WORD(KBD_END))
    nextpos = READ_WORD(KBD_START);
  if (nextpos == READ_WORD(KBD_HEAD)) {	/* queue full ? */
    if (scancode == 0x2e03) {
      keybuf_clear();
      k_printf("KBD: Buffer cleared for ctrl-break\n");
      WRITE_BYTE(0x471, 0x80);	/* ctrl-break flag */
      WRITE_WORD(BIOS_DATA_PTR(READ_WORD(KBD_START)), 0);
      return (1);
    }
    return (0);
  }

  WRITE_WORD(BIOS_DATA_PTR(READ_WORD(KBD_TAIL)), scancode);
  WRITE_WORD(KBD_TAIL, nextpos);

  return 1;
}

static void ctrl(unsigned int sc)
{
  if (key_flag(KKF_E0))
    set_key_flag(KKF_RCTRL);
  else
    set_kbd_flag(EKF_LCTRL);

  set_kbd_flag(KF_CTRL);
}

static void alt(unsigned int sc)
{
  if (key_flag(KKF_E0))
    set_key_flag(KKF_RALT);
  else
    set_kbd_flag(EKF_LALT);

  set_kbd_flag(KF_ALT);
}

static void Unctrl(unsigned int sc)
{
  if (key_flag(KKF_E0))
    clr_key_flag(KKF_RCTRL);
  else
    clr_kbd_flag(EKF_LCTRL);

  if (!kbd_flag(EKF_LCTRL) && !key_flag(KKF_RCTRL))
    clr_kbd_flag(KF_CTRL);
}

static void unalt(unsigned int sc)
{
  /* if (!resetid) { */
  if (key_flag(KKF_E0))
    clr_key_flag(KKF_RALT);
  else
    clr_kbd_flag(EKF_LALT);

  if (!(kbd_flag(EKF_LALT) && key_flag(KKF_RALT))) {
    clr_kbd_flag(KF_ALT);

    /* this is for hold-alt-and-keypad entry method */
    if (altchar) {
      /*
       * perhaps anding with 0xff is incorrect.  how about filtering out
       * anything > 255?
       */
      put_queue(altchar & 0xff);/* just the ASCII */
    }
    altchar = 0;
  }
  /* } */
}

static void lshift(unsigned int sc)
{
  set_kbd_flag(KF_LSHIFT);
}

static void unlshift(unsigned int sc)
{
  if (!resetid)
    clr_kbd_flag(KF_LSHIFT);
}

static void rshift(unsigned int sc)
{
  set_kbd_flag(KF_RSHIFT);
}

static void unrshift(unsigned int sc)
{
  if (!resetid)
    clr_kbd_flag(KF_RSHIFT);
}

static void caps(unsigned int sc)
{
  if (kbd_flag(KKF_RCTRL) && kbd_flag(EKF_LCTRL)) {
    keyboard_mouse = keyboard_mouse ? 0 : 1;
    m_printf("MOUSE: toggled keyboard mouse %s\n",
	     keyboard_mouse ? "on" : "off");
  }
  else {
    set_kbd_flag(EKF_CAPSLOCK);
    if (!caps_stat) {
      if (keepkey)
	chg_kbd_flag(KF_CAPSLOCK);	/* toggle; this means SET/UNSET */
      keyb_set_leds_area();
      caps_stat = TRUE;
    }
  }
}

static void uncaps(unsigned int sc)
{
  if (!resetid) {
    clr_kbd_flag(EKF_CAPSLOCK);
    caps_stat = FALSE;
  }
}

static void sysreq(unsigned int sc)
{
  g_printf("Regs requested: SYSREQ\n");
  show_regs(__FILE__, __LINE__);
}

static void ScrollLock(unsigned int sc)
{
  if (key_flag(KKF_E0)) {
    k_printf("KBD: ctrl-break!\n");
    WRITE_BYTE(0x471, 0x80);	/* ctrl-break flag */
    WRITE_WORD(KBD_HEAD, READ_WORD(KBD_START));
    WRITE_WORD(KBD_TAIL, READ_WORD(KBD_START));
    WRITE_WORD(BIOS_DATA_PTR(READ_WORD(KBD_START)), 0);
  }
  else if (kbd_flag(KKF_RCTRL))
    show_ints(0, 0x33);
  else if (kbd_flag(KKF_RALT))
    show_regs(__FILE__, __LINE__);
  else if (kbd_flag(KF_RSHIFT)) {
    warn("timer int 8 requested...\n");
    pic_request(PIC_IRQ0);
  }
  else if (kbd_flag(KF_LSHIFT)) {
    warn("keyboard int 9 requested...\n");
    keybuf_dump();
  }
  else {
    set_kbd_flag(EKF_SCRLOCK);
    if (!scroll_stat) {
      if (keepkey) {
	chg_kbd_flag(KF_SCRLOCK);
      }
      keyb_set_leds_area();
      scroll_stat = TRUE;
    }
  }
}

static void unscroll(unsigned int sc)
{
  clr_kbd_flag(EKF_SCRLOCK);
  scroll_stat = FALSE;
}

static void num(unsigned int sc)
{
  if (kbd_flag(EKF_LCTRL)) {
    k_printf("KBD: PAUSE!\n");
  }
  else {
    set_kbd_flag(EKF_NUMLOCK);
    if (!num_stat) {
      k_printf("KBD: NUMLOCK!\n");
      if (keepkey)
	chg_kbd_flag(KF_NUMLOCK);
      k_printf("KBD: kbd=%d\n", kbd_flag(KF_NUMLOCK));
      keyb_set_leds_area();
      num_stat = TRUE;
    }
  }
}

static void unnum(unsigned int sc)
{
  num_stat = FALSE;
  clr_kbd_flag(EKF_NUMLOCK);
}

static void get_leds(void)
{
  unsigned int led_state = 0;

  ioctl_execute(kbd_fd, KDGETLED, (int)&led_state);

  if (led_state & (1 << LED_SCRLOCK))
    set_kbd_flag(KF_SCRLOCK);
  else
    clr_kbd_flag(KF_SCRLOCK);

  if (led_state & (1 << LED_NUMLOCK))
    set_kbd_flag(KF_NUMLOCK);
  else
    clr_kbd_flag(KF_NUMLOCK);

  if (led_state & (1 << LED_CAPSLOCK))
    set_kbd_flag(KF_CAPSLOCK);
  else
    clr_kbd_flag(KF_CAPSLOCK);

  k_printf("KBD: LEDS: key 96 0x%02x, 97 0x%02x, kbc1 0x%02x, kbc2 0x%02x\n",
	   READ_BYTE(0x496), READ_BYTE(0x497), READ_BYTE(0x417),
	   READ_BYTE(0x418));
}

void keyboard_flags_init(void)
{
  WRITE_WORD(KBDFLAG_ADDR, 0);
  WRITE_WORD(KEYFLAG_ADDR, 0);
  get_leds();
  set_key_flag(KKF_KBD102);
}

void keyb_set_leds_area(void)
{
  if (kbd_flag(KF_SCRLOCK))
    set_key_flag(KKF_SCRLOCK);
  else
    clr_key_flag(KKF_SCRLOCK);

  if (kbd_flag(KF_NUMLOCK))
    set_key_flag(KKF_NUMLOCK);
  else
    clr_key_flag(KKF_NUMLOCK);

  if (kbd_flag(KF_CAPSLOCK))
    set_key_flag(KKF_CAPSLOCK);
  else
    clr_key_flag(KKF_CAPSLOCK);

  k_printf("KBD: set_leds_area() called\n");
  if (config.console_keyb)
    keyb_set_leds();
}

static void do_self(unsigned int sc)
{
  static unsigned char accent = 0;
  unsigned char ch;
  int i;

  if (kbd_flag(KF_ALT)) {
    /*
     * On a german keyboard the Left-Alt- (Alt-) and the Right-Alt-
     * (Alt-Gr-) Keys are different. The keys pressed with AltGr return
     * the codes defined in keymaps.c in the alt_map. Pressed with the
     * Left-Alt-Key they return the normal symbol with the alt-modifier.
     * I've tested this with the 4DOS-Alias- Command.
     * hein@tlaloc.in.tu-clausthal.de
     */
    if (config.keyboard == KEYB_DE_LATIN1) {
      if (kbd_flag(EKF_LALT))	/* Left-Alt-Key pressed ?            */
	ch = config.alt_map[0];	/* Return Key with Alt-modifier      */
      else			/* otherwise (this is Alt-Gr)        */
	ch = config.alt_map[sc];/* Return key from alt_map          */
    }
    else			/* or no DE_LATIN1-keyboard          */
      ch = config.alt_map[sc];	/* Return key from alt_map           */

    if ((sc >= 2) && (sc <= 0xb))	/* numbers */
      sc += 0x76;
    else if (sc == 0xd)
      sc = 0x83;		/* = */
    else if (sc == 0xc)
      sc = 0x82;		/* - */
  }

  else if (kbd_flag(KF_LSHIFT) || kbd_flag(KF_RSHIFT) || kbd_flag(KF_CTRL))
    ch = config.shift_map[sc];
  else
    ch = config.key_map[sc];

  /* check for a dead key */
  for (i = 0; dead_key_table[i] != 0; i++) {
    if (ch == dead_key_table[i]) {
      if (accent != ch) {
	accent = ch;
	return;
      }
    }
  }

  if (accent) {			/* translate dead keys */
    for (i = 0; dos850_dead_map[i].d_key != 0; i++) {
      if (accent == dos850_dead_map[i].d_key &&
	  dos850_dead_map[i].in_key == ch) {

	ch = dos850_dead_map[i].out_key;
	sc = 0;			/* keyb.exe uses 0 for dead keys */
	break;
      }
    }
    accent = 0;
  }
  if (kbd_flag(KF_CTRL) || kbd_flag(KF_CAPSLOCK))
    if ((ch >= 'a' && ch <= 'z') || (ch >= 224 && ch <= 254))
      ch -= 32;

  if (kbd_flag(KF_CTRL))	/* ctrl */
    ch &= 0x1f;

  k_printf("KBD: sc=%02x, ch=%02x\n", sc, ch);

  put_queue((sc << 8) | ch);
}

static void spacebar(unsigned int sc)
{
  put_queue(0x3920);
}

/*
 * 0x47-0x53, indexed by sc-0x47 , goes like: home, up, pgup, kp -, left,
 * kp 5, right, kp +, end, down, pgdn, ins, del
 */
unsigned short ctrl_cursor[] =
{
  0x7700, 0x8d00, 0x8400, 0x8e00, 0x7300, 0x8f00, 0x7400,
  0, 0x7500, 0x9100, 0x7600, 0x9200, 0x9300
};

unsigned short alt_cursor[] =
{
  0x9700, 0x9800, 0x9900, 0x4a00, 0x9b00, 0, 0x9d00, 0x4e00, 0x9f00, 0xa000,
  0xa100, 0xa200, 0xa300
};

unsigned short shift_cursor[] =
{
  0x4737, 0x4838, 0x4939, 0x4a2d, 0x4b34, 0x0000, 0x4d36, 0x4e2b, 0x4f31,
  0x5032, 0x5133, 0x5230, 0x532e
};

static void unins(unsigned int sc)
{
  ins_stat = FALSE;
}

static void cursor(unsigned int sc)
{
  int old_sc;

  old_sc = sc;

  if (sc < 0x47 || sc > 0x53)
    return;

  /* do dos_ctrl_alt_del on C-A-Del and C-A-PGUP */
  if (kbd_flag(KF_CTRL) && kbd_flag(KF_ALT)) {
    if (sc == 0x53 /* del */  || sc == 0x49 /* pgup */ )
      dos_ctrl_alt_del();
    if (sc == 0x51) {		/* pgdn */
      k_printf("KBD: ctrl-alt-pgdn taking her down!\n");
      leavedos(0);
    }
  }

  /* if the arrow keys, or home end, do keyboard mouse */
  if ((keyboard_mouse) && (sc == 0x50 || sc == 0x4b || sc == 0x48 ||
			   sc == 0x4d || sc == 0x47 || sc == 0x4f)) {
    mouse_keyboard(sc);
    return;
  }

  if (sc == 0x52) {
    if (!ins_stat) {
      chg_kbd_flag(KF_INSERT);
      ins_stat = TRUE;
    }
    else
      return;
  }

  sc -= 0x47;

  /*
   * ENHANCED CURSOR KEYS:  only ctrl and alt may modify them.
   */
  if (key_flag(KKF_E0)) {
    if (kbd_flag(KF_ALT))
      put_queue(alt_cursor[sc]);
    else if (kbd_flag(KF_CTRL))
      put_queue(ctrl_cursor[sc]);
    else if (key_flag(KKF_E0))
      put_queue (old_sc << 8 | 0xe0);
    else
      put_queue(old_sc << 8);
    return;
  }

  /*
   * everything below this must be a keypad key, as the check for KKF_E0
   * above filters out enhanced cursor (gray) keys
   */

  /* this is the hold-alt-and-type numbers thing */
  if (kbd_flag(KF_ALT)) {
    int digit;

    if ((digit = config.num_table[sc] - '0') <= 9)	/* is a number */
      altchar = altchar * 10 + digit;
  }
  else if (kbd_flag(KF_CTRL))
    put_queue(ctrl_cursor[sc]);
  else if (kbd_flag(KF_NUMLOCK) || kbd_flag(KF_LSHIFT) || kbd_flag(KF_RSHIFT))
    put_queue((old_sc << 8) | config.num_table[sc]);
  else
    put_queue(old_sc << 8);
}

static void backspace(unsigned int sc)
{
k_printf("backspace: kbd_flag(KF_CTRL)=%d, KF_ALT=%d, key_flag(KKF_E0)=%d\n",
	 kbd_flag(KF_CTRL), kbd_flag(KF_ALT), key_flag(KKF_E0));

  if (kbd_flag(KF_CTRL))
    put_queue(0x0e7f);
  else if (kbd_flag(KF_ALT))
    put_queue(0x0e00);
  else
    put_queue(0x0e08);
}

static void Tab(unsigned int sc)
{
  if (kbd_flag(KF_CTRL))
    put_queue(0x9400);
  else if (kbd_flag(KF_ALT))
    put_queue(0xa500);
  else if (kbd_flag(KF_LSHIFT) || kbd_flag(KF_RSHIFT))
    put_queue(sc << 8);
  else
    put_queue(sc << 8 | config.key_map[sc]);
}

static void func(unsigned int sc)
{
  int fnum = sc - 0x3a;

  if (fnum > 10)
    fnum -= 0xc;		/* adjust if f11 or f12 */

  /* this checks for the VC-switch key sequence */
  if (kbd_flag(EKF_LALT) && !key_flag(KKF_RALT) && !kbd_flag(KF_RSHIFT)
      && !kbd_flag(KF_LSHIFT) && kbd_flag(EKF_LCTRL)) {
    clr_kbd_flag(EKF_LALT);
    clr_kbd_flag(KF_ALT);
    clr_kbd_flag(EKF_LCTRL);
    clr_kbd_flag(KF_CTRL);
    return;
  }

  /*
   * FCH (Fkey CHoose):   returns a if n is f11 or f12, else it returns b
   * PC scancodes for fkeys are "orthogonal" except F11 and F12.
   */

#define FCH(n,a,b) ((n <= 10) ? a : b)

  k_printf("KBD: sc=%x, fnum=%x\n", sc, fnum);

  if (kbd_flag(KF_LSHIFT) || kbd_flag(KF_RSHIFT))
    put_queue((sc + FCH(fnum, 0x19, 0x30)) << 8);
  else if (kbd_flag(KF_CTRL))
    put_queue((sc + FCH(fnum, 0x23, 0x32)) << 8);
  else if (kbd_flag(KF_ALT))
    put_queue((sc + FCH(fnum, 0x2d, 0x34)) << 8);
  else
    put_queue(FCH(fnum, sc, (sc + 0x2e)) << 8);
}

static void slash(unsigned int sc)
{
  if (!key_flag(KKF_E0))
    do_self(sc);
  else
    put_queue(sc << 8 | '/');
}

static void star(unsigned int sc)
{
  do_self(sc);
}

static void enter(unsigned int sc)
{
  if (kbd_flag(KF_CTRL))
    put_queue(sc << 8 | 0x0a);
  else if (kbd_flag(KF_ALT))
    put_queue(0xa600);
  else if (key_flag(KKF_E0))
    put_queue(0xe00d);
  else
    put_queue(sc << 8 | 0x0d);
}

static void minus(unsigned int sc)
{
  do_self(sc);
}

static void plus(unsigned int sc)
{
  do_self(sc);
}

static void none(unsigned int sc)
{
}

/************* end of key-related functions *************/

#define SCANQ_LEN 100
static u_short *scan_queue;
static int *scan_queue_start;
static int *scan_queue_end;
static int next_scancode;

void shared_keyboard_init(void)
{
  (u_char *) scan_queue = shared_qf_memory + SHARED_KEYBOARD_OFFSET + 8;
  (u_char *) scan_queue_start = shared_qf_memory + SHARED_KEYBOARD_OFFSET;
  (u_char *) scan_queue_end = shared_qf_memory + SHARED_KEYBOARD_OFFSET + 4;
  *scan_queue_start = 0;
  *scan_queue_end = 0;
}

/* This is used to run the keyboard interrupt */
void do_irq1(void)
{
  keyb_dequeue_scancode();

  /* reschedule if queue not empty */
  if (*scan_queue_start != *scan_queue_end) {
    k_printf("KBD: Requesting next keyboard interrupt startstop %d:%d\n",
	     *scan_queue_start, *scan_queue_end);
    pic_request(PIC_IRQ1);
  }
  if (keys_ready)
    do_irq();
  keys_ready = 0;
}

void insert_into_keybuffer(void)
{
  /* int15 fn=4f will reset CF if scan key is not to be used */

  if (!config.keybint || (READ_FLAGS() & CF))
    keepkey = 1;
  else
    keepkey = 0;

  if (next_scancode && keepkey) {
    k_printf("KBD: putting key in buffer\n");
    if (InsKeyboard(next_scancode))
      keybuf_dump();
    else
      error("ERROR: InsKeyboard could not put key into buffer!\n");
  }
}

void keyb_queue_scancode(Bit8u scan)
{
  k_printf("KBD: Adding scancode to queue: scan %04x, startq=%d, endq=%d\n",
	   scan, *scan_queue_start, *scan_queue_end);
  scan_queue[*scan_queue_end] = scan;
  *scan_queue_end = (*scan_queue_end + 1) % SCANQ_LEN;
  if (config.keybint) {
    k_printf("KBD: Hard queue\n");
    pic_request(PIC_IRQ1);
  }
  else {
    k_printf("KBD: NOT Hard queue\n");
    keyb_dequeue_scancode();
    scan_to_buffer();
    keys_ready = 0;
  }
}

void keyb_dequeue_scancode(void)
{
  if (!keys_ready) {		/* make sure last character has been read */
    if (*scan_queue_start != *scan_queue_end) {
      keys_ready = 1;
      next_scancode = scan_queue[*scan_queue_start];
      if (*scan_queue_start != *scan_queue_end)
	*scan_queue_start = (*scan_queue_start + 1) % SCANQ_LEN;
      WRITE_WORD(LASTSCAN_ADD, next_scancode);
    }
  }
  else
    k_printf("KBD: read_next_scancode key not Read!\n");

  k_printf("KBD: Next key 96 0x%02x, 97 0x%02x, kbc1 0x%02x, kbc2 0x%02x\n",
	   READ_BYTE(0x496), READ_BYTE(0x497), READ_BYTE(0x417),
	   READ_BYTE(0x418));
  k_printf("KBD: start=%d, end=%d, LASTSCAN=%x\n", *scan_queue_start,
	   *scan_queue_end, READ_WORD(LASTSCAN_ADD));
}


typedef void (*fptr) (unsigned int);
static const fptr key_table[] =
{
  none, do_self, do_self, do_self,	/* 00-03 s0 esc 1 2 */
  do_self, do_self, do_self, do_self,	/* 04-07 3 4 5 6 */
  do_self, do_self, do_self, do_self,	/* 08-0B 7 8 9 0 */
  do_self, do_self, backspace, Tab,	/* 0C-0F + ' bs tab */
  do_self, do_self, do_self, do_self,	/* 10-13 q w e r */
  do_self, do_self, do_self, do_self,	/* 14-17 t y u i */
  do_self, do_self, do_self, do_self,	/* 18-1B o p } ^ */
  enter, ctrl, do_self, do_self,/* 1C-1F enter ctrl a s */
  do_self, do_self, do_self, do_self,	/* 20-23 d f g h */
  do_self, do_self, do_self, do_self,	/* 24-27 j k l | */
  do_self, do_self, lshift, do_self,	/* 28-2B { para lshift , */
  do_self, do_self, do_self, do_self,	/* 2C-2F z x c v */
  do_self, do_self, do_self, do_self,	/* 30-33 b n m , */
  do_self, slash, rshift, star,	/* 34-37 . / rshift * */
  alt, spacebar, caps, func,	/* 38-3B alt sp caps f1 */
  func, func, func, func,	/* 3C-3F f2 f3 f4 f5 */
  func, func, func, func,	/* 40-43 f6 f7 f8 f9 */
  func, num, ScrollLock, cursor,/* 44-47 f10 num scr home */
  cursor, cursor, minus, cursor,/* 48-4B up pgup - left */
  cursor, cursor, plus, cursor,	/* 4C-4F n5 right + end */
  cursor, cursor, cursor, cursor,	/* 50-53 dn pgdn ins del */
  sysreq, none, do_self, func,	/* 54-57 sysreq ? < f11 */
  func, none, none, none,	/* 58-5B f12 ? ? ? */
  none, none, none, none,	/* 5C-5F ? ? ? ? */
  none, none, none, none,	/* 60-63 ? ? ? ? */
  none, none, none, none,	/* 64-67 ? ? ? ? */
  none, none, none, none,	/* 68-6B ? ? ? ? */
  none, none, none, none,	/* 6C-6F ? ? ? ? */
  none, none, none, none,	/* 70-73 ? ? ? ? */
  none, none, none, none,	/* 74-77 ? ? ? ? */
  none, none, none, none,	/* 78-7B ? ? ? ? */
  none, none, none, none,	/* 7C-7F ? ? ? ? */
  none, none, none, none,	/* 80-83 ? br br br */
  none, none, none, none,	/* 84-87 br br br br */
  none, none, none, none,	/* 88-8B br br br br */
  none, none, none, none,	/* 8C-8F br br br br */
  none, none, none, none,	/* 90-93 br br br br */
  none, none, none, none,	/* 94-97 br br br br */
  none, none, none, none,	/* 98-9B br br br br */
  none, Unctrl, none, none,	/* 9C-9F br unctrl br br */
  none, none, none, none,	/* A0-A3 br br br br */
  none, none, none, none,	/* A4-A7 br br br br */
  none, none, unlshift, none,	/* A8-AB br br unlshift br */
  none, none, none, none,	/* AC-AF br br br br */
  none, none, none, none,	/* B0-B3 br br br br */
  none, none, unrshift, none,	/* B4-B7 br br unrshift br */
  unalt, none, uncaps, none,	/* B8-BB unalt br uncaps br */
  none, none, none, none,	/* BC-BF br br br br */
  none, none, none, none,	/* C0-C3 br br br br */
  none, unnum, unscroll, none,	/* C4-C7 br br br br */
  none, none, none, none,	/* C8-CB br br br br */
  none, none, none, none,	/* CC-CF br br br br */
  none, none, unins, none,	/* D0-D3 br br unins br */
  none, none, none, none,	/* D4-D7 br br br br */
  none, none, none, none,	/* D8-DB br ? ? ? */
  none, none, none, none,	/* DC-DF ? ? ? ? */
  none, none, none, none,	/* E0-E3 e0 e1 ? ? */
  none, none, none, none,	/* E4-E7 ? ? ? ? */
  none, none, none, none,	/* E8-EB ? ? ? ? */
  none, none, none, none,	/* EC-EF ? ? ? ? */
  none, none, none, none,	/* F0-F3 ? ? ? ? */
  none, none, none, none,	/* F4-F7 ? ? ? ? */
  none, none, none, none,	/* F8-FB ? ? ? ? */
  none, none, none, none	/* FC-FF ? ? ? ? */
};

/***************************************************************
 * this was copied verbatim from the Linux kernel (keyboard.c) *
 * and then modified unscrupously by hackers on the team       *
 ***************************************************************/

static unsigned int convscanKey(unsigned char scancode)
{
  static unsigned char rep = 0xff;

  k_printf("KBD: convscanKey scancode = 0x%04x\n", scancode);

  if (scancode == 0xe0) {
    set_key_flag(KKF_E0);
    set_key_flag(KKF_FIRSTID);
    set_key_flag(KKF_READID);
    resetid = 1;
    firstid = 0;
  }
  else if (scancode == 0xe1) {
    set_key_flag(KKF_E1);
    set_key_flag(KKF_FIRSTID);
    set_key_flag(KKF_READID);
    resetid = 1;
    firstid = 0;
  }

  if (scancode == 0xe0 || scancode == 0xe1)
    return (0);
  /*
   * The keyboard maintains its own internal caps lock and num lock
   * statuses. In caps lock mode E0 AA precedes make code and E0 2A
   * follows break code. In num lock mode, E0 2A precedes make code and E0
   * AA follows break code. We do our own book-keeping, so we will just
   * ignore these.
   */
  if (key_flag(KKF_E0) && (scancode == 0x2a || scancode == 0xaa)) {
    clr_key_flag(KKF_E0);
    clr_key_flag(KKF_E1);
    clr_key_flag(KKF_FIRSTID);
    resetid = 0;
    firstid = 0;
    return (0);
  }
  /*
   * Repeat a key only if the input buffers are empty or the characters
   * get echoed locally. This makes key repeat usable with slow
   * applications and unders heavy loads.
   */

  rep = scancode;
  queue = 0;
  key_table[scancode] (scancode);

  k_printf("KBD: resetid = %d firstid = %d\n", resetid, firstid);
  if (resetid) {
    if (firstid)
      clr_key_flag(KKF_FIRSTID);
    clr_key_flag(KKF_READID);
    resetid = firstid = 0;
  }

  clr_key_flag(KKF_E0);
  clr_key_flag(KKF_E1);

  return (queue);
}

static void set_keyboard_bios(void)
{
  if (config.keybint)
    next_scancode = convscanKey(_AH);
  else
    next_scancode = convscanKey(next_scancode);
  keys_ready = 0;
  k_printf("KEYB: keyboard bios next_scancode = 0x%04x\n", next_scancode);
}

void scan_to_buffer(void)
{
  k_printf("KBD: scan_to_buffer LASTSCAN_ADD = 0x%04x\n",
	   READ_WORD(LASTSCAN_ADD));
  set_keyboard_bios();
  insert_into_keybuffer();
}
