#include <sys/indirect.h>
#include <sys/bios.h>
#include "keyb.h"

#define CONTROL 0x1001
#define ALT 0x1002
#define SHIFT 0x1003
#define CAPS 0x1004
#define OPTION 0x1005

#define SHIFT_FLAG 1
#define CONTROL_FLAG 2
#define ALT_FLAG 4

static int code2key[128] = {
   'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', 
     'c', 'v', -1, 'b', 'q', 'w', 'e', 'r', 
     'y', 't', '1', '2', '3', '4', '6', '5', 
     '=', '9', '7', '-', '8', '0', ']', 'o', 
     'u', '[', 'i', 'p', ENTER, 'l', 'j', '\'',
     'k', ';', '\\', ',', '/', 'n', 'm', '.',
     TAB, ' ', '`', BACKSPACE, -1, -1, -1, ALT,
     SHIFT, CAPS, OPTION, CONTROL, SHIFT, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1,
     -1, -1, -1, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, -1
};
static int code2key_shift[128] = {
   'A', 'S', 'D', 'F', 'H', 'G', 'Z', 'X', 
     'C', 'V', -1, 'B', 'Q', 'W', 'E', 'R',
     'Y', 'T', '!', '@', '#', '$', '^', '%', 
     '+', '(', '&', '_', '*', ')', '}', 'O', 
     'U', '{', 'I', 'P', ENTER, 'L', 'J', '\"',
     'K', ':', '|', '<', '?', 'N', 'M', '>',
     TAB, ' ', '~', BACKSPACE, -1, -1, -1, ALT, 
     SHIFT, CAPS, OPTION, CONTROL, SHIFT, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1, 
     -1, -1, -1, -1, -1, -1, -1, -1,
     -1, -1, -1, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, -1
};


extern unsigned *il_get_ds();
extern void far *il_to_far(void near *p);
extern KeybIL keybIL;
typedef struct {
  int mode;
  int key_flag;
} KeybData;

static ILinfo keyb_info = {
   "Keyb", 
     "keyb", 
     "0.0",
     "Keyboard IL",
     (char far* far*)0,
};

ILinfo far* far keyb_get_info(){
  return (ILinfo far*) il_to_far(&keyb_info);
}

int far getch(){
  int c, ch;
  int far * c2k;
  int far * c2k_s;
  KeybData far *p;

  p = sys_get_my_iram();

  c2k = il_to_far(code2key);
  c2k_s = il_to_far(code2key_shift);

  c = comm_receive_with_timeout(0);
  if (p->mode == TERMINAL_MODE) 
    return c;

  /* PDAKB_MODE */
  if (c < 0)
    return -1;
  switch(c2k[c&0x7f]) {
   case SHIFT:
    if (c & 0x80)
      p->key_flag |= SHIFT_FLAG;
    else 
      p->key_flag &= ~SHIFT_FLAG;
    return -1;
   case CONTROL:
   case CAPS:
    if (c & 0x80)
      p->key_flag |= CONTROL_FLAG;
    else 
      p->key_flag &= ~CONTROL_FLAG;
    break;
   case ALT: 
    if (c & 0x80)
      p->key_flag |= ALT_FLAG;
    else 
      p->key_flag &= ~ALT_FLAG;
    break;
   default:
    if (c & 0x80) {
      ch = (p->key_flag & SHIFT_FLAG) ? 
	c2k_s[c&0x7f]: c2k[c&0x7f];
      if (p->key_flag & ALT_FLAG) {
	if (ch >= 'a' && ch <= 'z')
	  return ch-'a'+128;
	else 
	  break;
      } else if (p->key_flag & CONTROL_FLAG) {
	switch(ch){
	 case ' ':
	  return 0;
	 case '[':
	  return 27;
	 case '\\':
	  return 28;
	 case ']':
	  return 29;
	 case '^':
	 case '6':
	  return 30;
	 case '_':
	 case '/':
	  return 31;
	}
	if (ch >= 'a' && ch <= 'z')
	  return ch-'a'+1;
	else 
	  break;
      } else
	return ch;
    }
    break;
  }
  return -1;
}

int far setmode(int mode) {
  KeybData far *p;

  p = sys_get_my_iram();
  p->mode = mode;
  comm_set_baudrate((mode==PDAKB_MODE)? 0:1);
  return 0;
}


int far init() {
  int i, j;
  KeybData far *p;

  if (!(p = sys_get_my_iram())) {
    p = sys_alloc_iram(0, sizeof(KeybData));
  }
  p->key_flag = 0;
  setmode(TERMINAL_MODE);
  comm_open();
  return 0;
}

void far close(){
  sys_free_iram(sys_get_my_iram());
  comm_close();
  comm_set_baudrate(1);
}
