-#include <palacios/8259a.h>
+#include <devices/8259a.h>
+#include <palacios/vmm_intr.h>
+#include <palacios/vmm_types.h>
 #include <palacios/vmm.h>
 
 
+typedef enum {RESET, ICW1, ICW2, ICW3, ICW4,  READY} pic_state_t;
+
+static const uint_t MASTER_PORT1 = 0x20;
+static const uint_t MASTER_PORT2 = 0x21;
+static const uint_t SLAVE_PORT1 = 0xA0;
+static const uint_t SLAVE_PORT2 = 0xA1;
+
+#define IS_OCW2(x) (((x & 0x18) >> 3) == 0x0)
+#define IS_OCW3(x) (((x & 0x18) >> 3) == 0x1)
+
+struct icw1 {
+  uint_t ic4    : 1;  // ICW4 has to be read
+  uint_t sngl   : 1;  // single (only one PIC)
+  uint_t adi    : 1;  // call address interval
+  uint_t ltim   : 1;  // level interrupt mode
+  uint_t one    : 1;
+  uint_t rsvd   : 3;
+};
+
+
+struct icw2 {
+  uint_t rsvd   : 3;
+  uint_t vector : 5;
+};
+
+
+// Each bit that is set indicates that the IR input has a slave
+struct icw3_master {
+  uint_t S0   : 1;
+  uint_t S1   : 1;
+  uint_t S2   : 1;
+  uint_t S3   : 1;
+  uint_t S4   : 1;
+  uint_t S5   : 1;
+  uint_t S6   : 1;
+  uint_t S7   : 1;
+};
+
+// The ID is the Slave device ID
+struct icw3_slave {
+  uint_t id     : 3;
+  uint_t zeroes : 5;
+};
+
+struct icw4 {
+  uint_t uPM    : 1;  // 1=x86
+  uint_t AEOI   : 1;  // Automatic End of Interrupt
+  uint_t M_S    : 1;  // only if buffered 1=master,0=slave 
+  uint_t BUF    : 1;  // buffered mode
+  uint_t SFNM   : 1;  // special fully nexted mode
+  uint_t zeroes : 3;
+};
+
+
+struct ocw1 {
+  uint_t m0     : 1;
+  uint_t m1     : 1;
+  uint_t m2     : 1;
+  uint_t m3     : 1;
+  uint_t m4     : 1;
+  uint_t m5     : 1;
+  uint_t m6     : 1;
+  uint_t m7     : 1;
+};
+
+struct ocw2 {
+  uint_t level  : 3;
+  uint_t cw_code : 2; // should be 00
+  uint_t EOI    : 1;
+  uint_t SL     : 1;
+  uint_t R      : 1;
+};
+
+struct ocw3 {
+  uint_t RIS    : 1;
+  uint_t RR     : 1;
+  uint_t P      : 1;
+  uint_t cw_code : 2; // should be 01
+  uint_t smm    : 1;
+  uint_t esmm   : 1;
+  uint_t zero2  : 1;
+};
+
+
+struct pic_internal {
+
+
+  char master_irr;
+  char slave_irr;
+  
+  char master_isr;
+  char slave_isr;
+
+  char master_icw1;
+  char master_icw2;
+  char master_icw3;
+  char master_icw4;
+
+
+  char slave_icw1;
+  char slave_icw2;
+  char slave_icw3;
+  char slave_icw4;
+
+
+  char master_imr;
+  char slave_imr;
+  char master_ocw2;
+  char master_ocw3;
+  char slave_ocw2;
+  char slave_ocw3;
+
+  pic_state_t master_state;
+  pic_state_t slave_state;
+};
+
+
+
+static int pic_raise_intr(void * private_data, int irq, int error_code) {
+  struct pic_internal * state = (struct pic_internal*)private_data;
+
+  if (irq == 2) {
+    irq = 9;
+  }
+
+  if (irq <= 7) {
+    state->master_irr |= 0x1 << irq;
+  } else if ((irq > 7) && (irq < 16)) {
+    state->slave_irr |= 0x1 << (irq - 7);
+  } else {
+    return -1;
+  }
+
+  return 0;
+}
+
+static int pic_intr_pending(void * private_data) {
+  struct pic_internal * state = (struct pic_internal*)private_data;
+
+  if ((state->master_irr & ~(state->master_imr)) || 
+      (state->slave_irr & ~(state->slave_imr))) {
+    return 1;
+  }
+
+  return 0;
+}
+
+static int pic_get_intr_number(void * private_data) {
+  struct pic_internal * state = (struct pic_internal*)private_data;
+  int i;
+
+  for (i = 0; i < 16; i++) {
+    if (i <= 7) {
+      if (((state->master_irr & ~(state->master_imr)) >> i) == 0x1) {
+       return i;
+      }
+    } else {
+      if (((state->slave_irr & ~(state->slave_imr)) >> i) == 0x1) {
+       return i;
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+static struct intr_ctrl_ops intr_ops = {
+  .intr_pending = pic_intr_pending,
+  .get_intr_number = pic_get_intr_number,
+  .raise_intr = pic_raise_intr
+};
+
+
+
+
+
+
+
+int read_master_port1(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+  struct pic_internal * state = (struct pic_internal*)dev->private_data;
+  if (length != 1) {
+    //error
+  }
+  
+  if ((state->master_ocw3 & 0x3) == 0x2) {
+    *(char *)dst = state->master_irr;
+  } else if ((state->master_ocw3 & 0x3) == 0x3) {
+    *(char *)dst = state->master_isr;
+  } else {
+    *(char *)dst = 0;
+  }
+  
+  return 1;
+}
+
+int read_master_port2(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+  struct pic_internal * state = (struct pic_internal*)dev->private_data;
+  if (length != 1) {
+    // error
+  }
+
+  *(char *)dst = state->master_imr;
+
+  return 1;
+  
+}
+
+int read_slave_port1(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+  struct pic_internal * state = (struct pic_internal*)dev->private_data;
+  if (length != 1) {
+    // error
+  }
+  
+  if ((state->slave_ocw3 & 0x3) == 0x2) {
+    *(char*)dst = state->slave_irr;
+  } else if ((state->slave_ocw3 & 0x3) == 0x3) {
+    *(char *)dst = state->slave_isr;
+  } else {
+    *(char *)dst = 0;
+  }
+
+  return 1;
+}
+
+int read_slave_port2(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+  struct pic_internal * state = (struct pic_internal*)dev->private_data;
+  if (length != 1) {
+    // error
+  }
+
+  *(char *)dst = state->slave_imr;
+
+  return 1;
+}
+
+
+int write_master_port1(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
+  struct pic_internal * state = (struct pic_internal*)dev->private_data;
+  char cw = *(char *)src;
+
+  if (length != 1) {
+    // error
+  }
+  
+  if (state->master_state == ICW1) {
+    state->master_icw1 = cw;
+    state->master_state = ICW2;
+  } else if (state->master_state == READY) {
+    if (IS_OCW2(cw)) {
+      state->master_ocw2 = cw;
+    } else if (IS_OCW3(cw)) {
+      state->master_ocw3 = cw;
+    } else {
+      // error
+    }
+  } else {
+    // error
+  }
+
+  return 1;
+}
+
+int write_master_port2(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
+    struct pic_internal * state = (struct pic_internal*)dev->private_data;
+    char cw = *(char *)src;    
+
+    if (length != 1) {
+      //error
+    }
+    
+    if (state->master_state == ICW2) {
+      struct icw1 * cw1 = (struct icw1 *)&(state->master_icw1);
+
+      state->master_icw2 = cw;
+
+      if (cw1->sngl == 0) {
+       state->master_state = ICW3;
+      } else if (cw1->ic4 == 1) {
+       state->master_state = ICW4;
+      } else {
+       state->master_state = READY;
+      }
+
+    } else if (state->master_state == ICW3) {
+      struct icw1 * cw1 = (struct icw1 *)&(state->master_icw1);
+
+      state->master_icw3 = cw;
+
+      if (cw1->ic4 == 1) {
+       state->master_state = ICW4;
+      } else {
+       state->master_state = READY;
+      }
+
+    } else if (state->master_state == ICW4) {
+      state->master_icw4 = cw;
+      state->master_state = READY;
+    } else if (state->master_state == READY) {
+      state->master_imr = cw;
+    } else {
+      // error
+    }
+
+    return 1;
+}
+
+int write_slave_port1(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
+  struct pic_internal * state = (struct pic_internal*)dev->private_data;
+  char cw = *(char *)src;
+
+  if (length != 1) {
+    // error
+  }
+
+  if (state->slave_state == ICW1) {
+    state->slave_icw1 = cw;
+    state->slave_state = ICW2;
+  } else if (state->slave_state == READY) {
+    if (IS_OCW2(cw)) {
+      state->slave_ocw2 = cw;
+    } else if (IS_OCW3(cw)) {
+      state->slave_ocw3 = cw;
+    } else {
+      // error
+    }
+  } else {
+    // error
+  }
+
+  return 1;
+}
+
+int write_slave_port2(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
+    struct pic_internal * state = (struct pic_internal*)dev->private_data;
+    char cw = *(char *)src;    
+
+    if (length != 1) {
+      //error
+    }
+
+    if (state->slave_state == ICW2) {
+      struct icw1 * cw1 =  (struct icw1 *)&(state->master_icw1);
+
+      state->slave_icw2 = cw;
+
+      if (cw1->sngl == 0) {
+       state->slave_state = ICW3;
+      } else if (cw1->ic4 == 1) {
+       state->slave_state = ICW4;
+      } else {
+       state->slave_state = READY;
+      }
+
+    } else if (state->slave_state == ICW3) {
+      struct icw1 * cw1 =  (struct icw1 *)&(state->master_icw1);
+
+      state->slave_icw3 = cw;
+
+      if (cw1->ic4 == 1) {
+       state->slave_state = ICW4;
+      } else {
+       state->slave_state = READY;
+      }
+
+    } else if (state->slave_state == ICW4) {
+      state->slave_icw4 = cw;
+      state->slave_state = READY;
+    } else if (state->slave_state == READY) {
+      state->slave_imr = cw;
+    } else {
+      // error
+    }
+
+    return 1;
+}
+
+
+
+
+
+
+
+
+int pic_init(struct vm_device * dev) {
+  struct pic_internal * state = (struct pic_internal*)dev->private_data;
+
+  set_intr_controller(dev->vm, &intr_ops, state);
+
+  state->master_irr = 0;
+  state->master_isr = 0;
+  state->master_icw1 = 0;
+  state->master_icw2 = 0;
+  state->master_icw3 = 0;
+  state->master_icw4 = 0;
+  state->master_imr = 0;
+  state->master_ocw2 = 0;
+  state->master_ocw3 = 0x2;
+  state->master_state = ICW1;
+
+
+  state->slave_irr = 0;
+  state->slave_isr = 0;
+  state->slave_icw1 = 0;
+  state->slave_icw2 = 0;
+  state->slave_icw3 = 0;
+  state->slave_icw4 = 0;
+  state->slave_imr = 0;
+  state->slave_ocw2 = 0;
+  state->slave_ocw3 = 0x2;
+  state->slave_state = ICW1;
+
+
+  dev_hook_io(dev, MASTER_PORT1, &read_master_port1, &write_master_port1);
+  dev_hook_io(dev, MASTER_PORT2, &read_master_port2, &write_master_port2);
+  dev_hook_io(dev, SLAVE_PORT1, &read_slave_port1, &write_slave_port1);
+  dev_hook_io(dev, SLAVE_PORT2, &read_slave_port2, &write_slave_port2);
+
+  return 0;
+}
+
+
+int pic_deinit(struct vm_device * dev) {
+  dev_unhook_io(dev, MASTER_PORT1);
+  dev_unhook_io(dev, MASTER_PORT2);
+  dev_unhook_io(dev, SLAVE_PORT1);
+  dev_unhook_io(dev, SLAVE_PORT2);
+
+  return 0;
+}
+
+
+
+
+
+
+
+static struct vm_device_ops dev_ops = {
+  .init = pic_init,
+  .deinit = pic_deinit,
+  .reset = NULL,
+  .start = NULL,
+  .stop = NULL,
+};
+
+
+struct vm_device * create_pic() {
+  struct pic_internal * state = NULL;
+  VMMMalloc(struct pic_internal *, state, sizeof(struct pic_internal));
+
+  struct vm_device *device = create_device("8259A", &dev_ops, state);
+
+  return device;
+}
+
+
+
 
-static const uint_t PIC1_PORT_1 0x20;
-static const uint_t PIC1_PORT_2 0x21;
-static const uint_t PIC2_PORT_1 0xA0;
-static const uint_t PIC2_PORT_2 0xA1;
 
 #include <devices/nvram.h>
 #include <devices/timer.h>
 #include <devices/simple_pic.h>
+#include <devices/8259a.h>
 #include <palacios/vmm_intr.h>
 
 
       hook_io_port(&(vm_info.io_map), 0x61, &IO_Read, &IO_Write, NULL);
       hook_io_port(&(vm_info.io_map), 0x05, &IO_Read, &IO_Write_to_Serial, NULL);
 
-      hook_io_port(&(vm_info.io_map), 0x20, &IO_Read, &IO_Write_to_Serial, NULL);
-      hook_io_port(&(vm_info.io_map), 0x21, &IO_Read, &IO_Write_to_Serial, NULL);
-      hook_io_port(&(vm_info.io_map), 0xa0, &IO_Read, &IO_Write_to_Serial, NULL);
-      hook_io_port(&(vm_info.io_map), 0xa1, &IO_Read, &IO_Write_to_Serial, NULL);
-
+      /*
+       hook_io_port(&(vm_info.io_map), 0x20, &IO_Read, &IO_Write_to_Serial, NULL);
+       hook_io_port(&(vm_info.io_map), 0x21, &IO_Read, &IO_Write_to_Serial, NULL);
+       hook_io_port(&(vm_info.io_map), 0xa0, &IO_Read, &IO_Write_to_Serial, NULL);
+       hook_io_port(&(vm_info.io_map), 0xa1, &IO_Read, &IO_Write_to_Serial, NULL);
+      */
       hook_io_port(&(vm_info.io_map), 0x400, &IO_Read, &IO_Write_to_Serial, NULL);
       hook_io_port(&(vm_info.io_map), 0x401, &IO_Read, &IO_Write_to_Serial, NULL);
       hook_io_port(&(vm_info.io_map), 0x402, &IO_Read, &IO_BOCHS_debug, NULL);