#endif
 
 
-#define KEYBOARD_DATA_REG          0x60
-#define KEYBOARD_CONTROL_REG       0x64
+extern struct vmm_os_hooks *os_hooks;
+
+extern void SerialPrint(const char *format, ...);
+
+
+
+#define KEYBOARD_60H          0x60  // keyboard microcontroller
+#define KEYBOARD_64H          0x64  // onboard microcontroller
 
 #define KEYBOARD_IRQ    0x1
 
 
+// extract bits for status byte
+#define STATUS_OUTPUT_BUFFER_FULL   0x01  // 1=full
+#define STATUS_INPUT_BUFFER_FULL    0x02  // 1=full
+#define STATUS_SYSTEM               0x04  // 1=self-test-passed
+#define STATUS_COMMAND_DATA_AVAIL   0x08  // internal: 0=data on 60h, 0=cmd on 64h
+#define STATUS_ENABLED              0x10  // 1=keyboard is enabled
+#define STATUS_MOUSE_BUFFER_FULL    0x20  // 1= mouse output buffer full
+#define STATUS_TIMEOUT              0x40  // 1=timeout of keybd
+#define STATUS_PARITY               0x80  // 1=parity error
+
+// bits for cmd byte
+
+#define CMD_INTR                0x01  // 1=interrupts enabled
+#define CMD_MOUSE_INTR          0x02  // 1=interrupts enabled for mouse
+#define CMD_SYSTEM              0x04  // 1= self test passed
+#define CMD_OVERRIDE            0x08  // FORCE 0 for  PS2
+#define CMD_DISABLE             0x10  // 1=disabled keyboard
+#define CMD_MOUSE_DISABLE       0x20  // 1=disabled mouse
+#define CMD_SCANCODE_XLATE      0x40  // 1=translate to set 1 scancodes
+#define CMD_RESERVED            0x80  // should be zero
 
 // The currently targetted keyboard
 static struct vm_device *thekeyboard=NULL;
 
 
 struct keyboard_internal {
-  // read* is what is seen when reads are done from the VM
-  uchar_t read_status;
-  uchar_t read_scancode;
-  // write* is where we put the writes from the VM
-  uchar_t write_status;
-  uchar_t write_scancode;
-
-  uchar_t status_byte;      //  for on-board uC
+  // 
+  // 0x60 is the port for the keyboard microcontroller
+  //   writes are commands
+  //   reads from it usually return scancodes
+  //   however, it can also return other data 
+  //   depending on the state of the onboard microcontroller
+  //
+  // 0x64 is the port for the onboard microcontroller
+  //   writes are commands
+  //   reads are status
+  //
 
-  uchar_t input_queue;      //  input queue is for communication *to* the on-board uC
+  // state of the onboard microcontroller
+  // this is needed because sometimes 0x60 reads come
+  // from the onboard microcontroller
+  enum {NORMAL,
+       // after receiving cmd 0x20 
+       // keyboard uC cmd byte pushed onto output queue
+       WRITING_CMD_BYTE,  
+       // after receiving cmd 0x60
+       // keybaord uC cmd will subsequently arive on data port
+       TRANSMIT_PASSWD,
+       // after recieving 0xa5
+       // password arrives on data port, null terminated
+  } state;
+       
+
+
+  uchar_t cmd_byte;         //  for keyboard uC - read/written 
+                            //     via read/write cmd byte command
+  uchar_t status_byte;      //  for on-board uC - read via 64h
+
+  uchar_t input_queue;      //  Read via 60h
   uint_t  input_queue_len;  //  num items queued
-  uchar_t output_queue;     //  output queue is for communcation *from* the on-board uC
+  uchar_t output_queue;     //  Written by 60h
   uint_t  output_queue_len; //  num items queued 
 };
 
 
+// 
+// push item onto outputqueue, optionally overwriting if there is no room
+// returns 0 if successful
+//
+static int PushToOutputQueue(struct vm_device *dev, uchar_t value, uchar_t overwrite) 
+{
+  struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
+  
+  if (state->output_queue_len==0 || overwrite) { 
+    state->output_queue=value;
+    state->output_queue_len=1;
+    state->status_byte |= STATUS_OUTPUT_BUFFER_FULL;
+    return 0;
+  } else {
+    KEYBOARD_DEBUG_PRINT("keyboard: PushToOutputQueue Failed - Queue Full\n");
+    return -1;
+  }
+}
+
+#if 0
+// 
+// pull item from outputqueue 
+// returns 0 if successful
+//
+static int PullFromOutputQueue(struct vm_device *dev,uchar_t *value) 
+{
+  struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
+  if (state->output_queue_len==1) { 
+    *value=state->input_queue;
+    state->output_queue_len=0;
+    state->status_byte &= ~STATUS_OUTPUT_BUFFER_FULL;
+    return 0;
+  } else {
+    KEYBOARD_DEBUG_PRINT("keyboard: PullFromOutputQueue Failed - Queue Empty\n");
+    return -1;
+  }
+}
+#endif
+
+// 
+// push item onto inputqueue, optionally overwriting if there is no room
+// returns 0 if successful
+//
+static int PushToInputQueue(struct vm_device *dev, uchar_t value, uchar_t overwrite) 
+{
+  struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
+  if (state->input_queue_len==0 || overwrite) { 
+    state->input_queue=value;
+    state->input_queue_len=1;
+    state->status_byte |= STATUS_INPUT_BUFFER_FULL;
+    return 0;
+  } else {
+    KEYBOARD_DEBUG_PRINT("keyboard: PushToOutputQueue Failed - Queue Full\n");
+    return -1;
+  }
+}
+
+// 
+// pull item from inputqueue 
+// returns 0 if successful
+//
+static int PullFromInputQueue(struct vm_device *dev, uchar_t *value) 
+{
+  struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
+  if (state->input_queue_len==1) { 
+    *value=state->input_queue;
+    state->input_queue_len=0;
+    state->status_byte &=~STATUS_INPUT_BUFFER_FULL;
+    return 0;
+  } else {
+    KEYBOARD_DEBUG_PRINT("keyboard: PullFromInputQueue Failed - Queue Empty\n");
+    return -1;
+  }
+}
+
 static struct vm_device *demultiplex_injected_key(uchar_t status, uchar_t scancode)
 {
   // this currently does nothing
 
   KEYBOARD_DEBUG_PRINT("keyboard: injected status 0x%x, and scancode 0x%x\n", status,scancode);
   
-  // This is wrong - the read status should be some combination of the 
-  // status/scancode within the VM, and that of the actual device
-  state->read_status=status;
-  state->read_scancode=scancode;
+  if (    (state->status_byte & STATUS_ENABLED)      // onboard is enabled
+         && (!(state->cmd_byte & CMD_DISABLE)))  {   // keyboard is enabled
 
-  keyboard_interrupt(KEYBOARD_IRQ,dev);
+    PushToInputQueue(dev,scancode,1);
 
+    if (state->cmd_byte & CMD_INTR) { 
+      keyboard_interrupt(KEYBOARD_IRQ,dev);
+    }
+       
+  }
 }
 
 int keyboard_reset_device(struct vm_device * dev)
   struct keyboard_internal *data = (struct keyboard_internal *) dev->private_data;
   
   memset(data,0,sizeof(struct keyboard_internal));
+
+  data->cmd_byte =   
+      CMD_INTR        // interrupts on
+    | CMD_MOUSE_INTR  // mouse interupts on
+    | CMD_SYSTEM ;    // self test passed
+                      // PS2, keyboard+mouse enabled, generic translation    
   
+  data->status_byte = 
+      STATUS_SYSTEM     // self-tests passed
+    | STATUS_ENABLED ;  // keyboard ready
+                        // buffers empty, no errors
+
   KEYBOARD_DEBUG_PRINT("keyboard: reset device\n");
  
   return 0;
 
 
 
-
-
 int keyboard_start_device(struct vm_device *dev)
 {
   KEYBOARD_DEBUG_PRINT("keyboard: start device\n");
 
 
 
-int keyboard_write_control_port(ushort_t port,
-                               void * src, 
-                               uint_t length,
-                               struct vm_device * dev)
+int keyboard_write_command(ushort_t port,
+                          void * src, 
+                          uint_t length,
+                          struct vm_device * dev)
 {
   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
+  uchar_t cmd;
 
-  if (length==1) { 
-    uchar_t data = *((uchar_t*)src); 
-    KEYBOARD_DEBUG_PRINT("keyboard: write of 0x%x to control port\n",data);
-    state->write_status=data;
-    return 1;
-  } else {
-    KEYBOARD_DEBUG_PRINT("keyboard: unknown size write to control port!\n");
+  // Should always be single byte write
+
+  if (length!=1) { 
+    KEYBOARD_DEBUG_PRINT("keyboard: write of >1 bytes to 64h");
     return -1;
   }
+
+  cmd =  *((uchar_t*)src); 
+
+  if (state->state!=NORMAL) { 
+    KEYBOARD_DEBUG_PRINT("keyboard: warning - receiving command on 64h but state!=NORMAL\n");
+  }
+  
+  KEYBOARD_DEBUG_PRINT("keyboard: command 0x%x on 64h\n",cmd);
+
+  switch (cmd) { 
+
+  case 0x20:  // READ COMMAND BYTE (returned in 60h)
+    PushToInputQueue(dev,state->cmd_byte,1);
+    state->state=NORMAL;  // the next read on 0x60 will get the right data
+    break;
+
+  case 0x60:  // WRITE COMMAND BYTE (read from 60h)
+    state->state=WRITING_CMD_BYTE; // we need to make sure we send the next 0x60 byte appropriately
+    break;
+
+  // case 0x90-9f - write to output port  (?)
+
+  case 0xa1: // Get version number
+    PushToInputQueue(dev,0,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xa4:  // is password installed?  send result to 0x60
+    // we don't support passwords
+    PushToOutputQueue(dev,0xf1,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xa5:  // new password will arrive on 0x60
+    state->state=TRANSMIT_PASSWD;
+    break;
+
+  case 0xa6:  // check passwd;
+    // since we do not support passwords, we will simply ignore this
+    // the implication is that any password check immediately succeeds 
+    // with a blank password
+    state->state=NORMAL;
+    break;
+
+  case 0xa7:  // disable mouse
+    state->cmd_byte |= CMD_MOUSE_DISABLE;
+    state->state=NORMAL;
+    break;
+
+  case 0xa8:  // enable mouse
+    state->cmd_byte &= ~CMD_MOUSE_DISABLE;
+    state->state=NORMAL;
+    break;
+
+  case 0xa9:  // mouse interface test  (always succeeds)
+    PushToInputQueue(dev,0,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xaa:  // controller self test (always succeeds)
+    PushToInputQueue(dev,0x55,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xab:  // keyboard interface test (always succeeds)
+    PushToInputQueue(dev,0,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xad:  // disable keyboard
+    state->cmd_byte |= CMD_DISABLE;
+    state->state=NORMAL;
+    break;
+
+  case 0xae:  // enable keyboard
+    state->cmd_byte &= ~CMD_DISABLE;
+    state->state=NORMAL;
+    break;
+
+  case 0xaf:  // get version
+    PushToInputQueue(dev,0x00,1);
+    state->state=NORMAL;
+    break;
+
+  // case c0  read input port ?
+  // case c1  copy input port lsn to status
+  // case c2  copy input port msn to status
+  
+  // case d0 read output port
+  // case d1 write output port
+  // case d2 write keyboard buffer (inject key)
+  // case d3 write mouse buffer (inject mouse)
+  // case d4 write mouse device (command to mouse?)
+
+  // case e0 read test port
+  
+  // case f0..ff pulse output port ?
+
+   
+  default:
+    KEYBOARD_DEBUG_PRINT("keyboard: ignoring command (unimplemented)\n");
+    state->state=NORMAL;
+    break;
+  }
+
+  return 1;
+
 }
 
-int keyboard_read_control_port(ushort_t port,
-                              void * dest, 
-                              uint_t length,
-                              struct vm_device * dev)
+int keyboard_read_status(ushort_t port,
+                        void * dest, 
+                        uint_t length,
+                        struct vm_device * dev)
 {
   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
 
   if (length==1) { 
-    uchar_t data;
-    KEYBOARD_DEBUG_PRINT("keyboard: read from control port: ");
-    data=state->read_status;
-    KEYBOARD_DEBUG_PRINT("0x%x\n",data);
-    memcpy(dest,&data,length);
+    KEYBOARD_DEBUG_PRINT("keyboard: read status (64h): ");
+    *((uchar_t*)dest)=state->status_byte;
+    KEYBOARD_DEBUG_PRINT("0x%x\n",*((uchar_t*)dest));
     return 1;
   } else {
-    KEYBOARD_DEBUG_PRINT("keyboard: unknown size read from control port!\n");
+    KEYBOARD_DEBUG_PRINT("keyboard: >1 byte read for status (64h)\n");
     return -1;
   }
 }
 
-int keyboard_write_data_port(ushort_t port,
-                            void * src, 
-                            uint_t length,
-                            struct vm_device * dev)
+int keyboard_write_output(ushort_t port,
+                         void * src, 
+                         uint_t length,
+                         struct vm_device * dev)
 {
   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
 
-  if (length==1) { 
-    uchar_t data = *((uchar_t*)src); 
-    KEYBOARD_DEBUG_PRINT("keyboard: write of 0x%x to data port\n",data);
-    state->write_scancode=data;
-    return 1;
-  } else {
-    KEYBOARD_DEBUG_PRINT("keyboard: unknown size write to data port!\n");
+  if (length!=1) { 
+    KEYBOARD_DEBUG_PRINT("keyboard: write of 60h with >1 byte\n");
     return -1;
   }
+
+  uchar_t data=*((uchar_t*)src);
+  
+  switch (state->state) {
+  case WRITING_CMD_BYTE:
+    state->cmd_byte=data;
+    state->state=NORMAL;
+    break;
+  case TRANSMIT_PASSWD:
+    if (data) {
+      //ignore passwd
+    } else {
+      // end of password
+      state->state=NORMAL;
+    }
+    break;
+  case NORMAL:
+    // command is being sent to keyboard controller
+    switch (data) { 
+    case 0xff: // reset
+      PushToInputQueue(dev,0xfa,1); // ack
+      state->state=NORMAL;
+      break;
+    case 0xfe: // resend
+    case 0xfd: // set key type make
+    case 0xfc: // set key typ make/break
+    case 0xfb: // set key type typematic
+    case 0xfa: // set all typematic make/break/typematic
+    case 0xf9: // set all make
+    case 0xf8: // set all make/break
+    case 0xf7: // set all typemaktic
+    case 0xf6: // set defaults
+    case 0xf5: // disable scanning
+    case 0xf4: // enable scanning
+    case 0xf3: // set typematic delay/rate
+    default:
+      KEYBOARD_DEBUG_PRINT("keyboard: unhandled command 0x%x on output buffer (60h)\n");
+      break;
+    }
+  default:
+    KEYBOARD_DEBUG_PRINT("keyboard: unknown state %x on command 0x%x on output buffer (60h)\n",state->state, data);
+  }
+  
+  return 1;
 }
 
-int keyboard_read_data_port(ushort_t port,
-                           void * dest, 
-                           uint_t length,
-                           struct vm_device * dev)
+int keyboard_read_input(ushort_t port,
+                       void * dest, 
+                       uint_t length,
+                       struct vm_device * dev)
 {
-  struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
-
   if (length==1) { 
     uchar_t data;
-    KEYBOARD_DEBUG_PRINT("keyboard: read from data port: ");
-    data=state->read_scancode;
+    KEYBOARD_DEBUG_PRINT("keyboard: read from input (60h): ");
+    PullFromInputQueue(dev,&data);
     KEYBOARD_DEBUG_PRINT("0x%x\n",data);
-    memcpy(dest,&data,1);
+    *((uchar_t*)dest)=data;
     return 1;
   } else {
-    KEYBOARD_DEBUG_PRINT("keyboard: unknown size read from data port!\n");
+    KEYBOARD_DEBUG_PRINT("keyboard: unknown size read from input (60h)\n");
     return -1;
   }
 }
   keyboard_reset_device(dev);
 
   // hook ports
-  dev_hook_io(dev, KEYBOARD_DATA_REG, &keyboard_read_data_port, &keyboard_write_data_port);
-  dev_hook_io(dev, KEYBOARD_CONTROL_REG, &keyboard_read_control_port, &keyboard_write_control_port);
+  dev_hook_io(dev, KEYBOARD_64H, &keyboard_read_status, &keyboard_write_command);
+  dev_hook_io(dev, KEYBOARD_60H, &keyboard_read_input, &keyboard_write_output);
   
   //
   // We do not hook the IRQ here.  Instead, the underlying device driver
 int keyboard_deinit_device(struct vm_device *dev)
 {
 
-  dev_unhook_io(dev, KEYBOARD_DATA_REG);
-  dev_unhook_io(dev, KEYBOARD_CONTROL_REG);
-
+  dev_unhook_io(dev, KEYBOARD_60H);
+  dev_unhook_io(dev, KEYBOARD_64H);
   keyboard_reset_device(dev);
   return 0;
 }