#include <devices/ramdisk.h>
 #include <palacios/vmm.h>
-#include <string.h>
+#include <devices/cdrom.h>
+#include <devices/ide.h>
+#include <devices/atapi.h>
 
 #ifdef DEBUG_RAMDISK
 //#define Ramdisk_Print(_f, _a...) PrintTrace("\nramdisk.c(%d) " _f, __LINE__, ## _a)
 
 
 
+
+
+
 /*
  * Data type definitions
  *
 #define INDEX_PULSE_CYCLE 10
 
 #define MAX_ATA_CHANNEL 4
-#define RD_LITTLE_ENDIAN
 
 
 #define INTR_REASON_BIT_ERR           0x01
 #define READ_BUF_GT_512               0x04
 
 
-typedef enum _sense {
-      SENSE_NONE = 0, SENSE_NOT_READY = 2, SENSE_ILLEGAL_REQUEST = 5,
-      SENSE_UNIT_ATTENTION = 6
-} sense_t ;
 
-typedef enum _asc {
-      ASC_INV_FIELD_IN_CMD_PACKET = 0x24,
-      ASC_MEDIUM_NOT_PRESENT = 0x3a,
-      ASC_SAVING_PARAMETERS_NOT_SUPPORTED = 0x39,
-      ASC_LOGICAL_BLOCK_OOR = 0x21
-} asc_t ;
+#define PRI_DATA_PORT         0x1f0
+#define PRI_FEATURES_PORT     0x1f1
+#define PRI_SECT_CNT_PORT     0x1f2
+#define PRI_SECT_ADDR1_PORT   0x1f3
+#define PRI_SECT_ADDR2_PORT   0x1f4
+#define PRI_SECT_ADDR3_PORT   0x1f5
+#define PRI_DRV_SEL_PORT      0x1f6
+#define PRI_CMD_PORT          0x1f7
+#define PRI_CTRL_PORT         0x3f6
+#define PRI_ADDR_REG_PORT     0x3f7
+
+#define SEC_DATA_PORT         0x170
+#define SEC_FEATURES_PORT     0x171
+#define SEC_SECT_CNT_PORT     0x172
+#define SEC_SECT_ADDR1_PORT   0x173
+#define SEC_SECT_ADDR2_PORT   0x174
+#define SEC_SECT_ADDR3_PORT   0x175
+#define SEC_DRV_SEL_PORT      0x176
+#define SEC_CMD_PORT          0x177
+#define SEC_CTRL_PORT         0x376
+#define SEC_ADDR_REG_PORT     0x377
+
+
+
+
+
+#define PACKET_SIZE 12
+
 
 
 // FLAT MODE
 ssize_t rd_write (const void* buf, size_t count);
 
 
-typedef struct  {
-
-      unsigned cylinders               ;
-      unsigned heads                   ;
-      unsigned sectors                 ;
-
-  //iso file descriptor
-      int fd                           ;
-} device_image_t;
-
-
-
-struct  controller_t {
-  struct  {
-    rd_bool busy                       ;
-    rd_bool drive_ready                ;
-    rd_bool write_fault                ;
-    rd_bool seek_complete              ;
-    rd_bool drq                        ;
-    rd_bool corrected_data             ;
-    rd_bool index_pulse                ;
-    unsigned int index_pulse_count     ;
-    rd_bool err                        ;
-    } status;
-  Bit8u    error_register              ;
-  Bit8u    head_no                     ;
-  union  {
-    Bit8u    sector_count              ;
-    struct  {
-#ifdef RD_LITTLE_ENDIAN
-      unsigned  c_d : 1; 
-      unsigned  i_o : 1; 
-      unsigned  rel : 1; 
-      unsigned  tag : 5; 
-
-#else  /* RD_BIG_ENDIAN */
-      unsigned tag : 5;
-      unsigned rel : 1;
-      unsigned i_o : 1;
-      unsigned c_d : 1;
-#endif
-      
-    } interrupt_reason;
-  };
-  Bit8u    sector_no                   ;
-  union  {
-    Bit16u   cylinder_no               ;
-    Bit16u   byte_count                ;
-  };
-  Bit8u    buffer[2048];               ; 
-  Bit32u   buffer_index                ;
-  Bit32u   drq_index                   ;
-  Bit8u    current_command             ;
-  Bit8u    sectors_per_block           ;
-  Bit8u    lba_mode                    ;
-  struct  {
-    // 0=normal, 1=reset controller
-    rd_bool reset                      ;       
-    // 0=allow irq, 1=disable irq
-    rd_bool disable_irq                ; 
-    } control;
-  Bit8u    reset_in_progress           ;
-  Bit8u    features                    ;
-  };
-
-struct  sense_info_t{
-  sense_t sense_key                    ;
-  struct  {
-    Bit8u arr[4]                       ;
-  } information;
-  struct  {
-    Bit8u arr[4]                       ;
-  } specific_inf;
-  struct  {
-    Bit8u arr[3]                       ;
-  } key_spec;
-  Bit8u fruc                           ;
-  Bit8u asc                            ;
-  Bit8u ascq                           ;
-};
-
-struct  error_recovery_t {
-  unsigned char data[8]                ;
-
-  //  error_recovery_t ();
-};
-
-uint16 rd_read_16bit(const uint8* buf); //__attribute__(regparm(1))
-uint32 rd_read_32bit(const uint8* buf); //__attribute__(regparm(1))
-
-struct  cdrom_t {
-  rd_bool ready                                      ;
-  rd_bool locked                                     ;
-
-
-  struct cdrom_interface *cd                         ;
-
-  uint32 capacity                                    ;
-  int next_lba                                       ;
-  int remaining_blocks                               ;
-  struct  currentStruct {
-    struct error_recovery_t error_recovery           ;
-  } current;
-};
-
-struct  atapi_t {
-  uint8 command                                      ;
-  int drq_bytes                                      ;
-  int total_bytes_remaining                          ;
-};
-
-
-typedef enum {
-      IDE_NONE, IDE_DISK, IDE_CDROM
-} device_type_t ;
-
-
-  // FIXME:
-  // For each ATA channel we should have one controller struct
-  // and an array of two drive structs
-struct  channel_t {
-    struct  drive_t {
-      device_image_t  hard_drive                     ;
-      device_type_t device_type                      ;
-      // 512 byte buffer for ID drive command
-      // These words are stored in native word endian format, as
-      // they are fetched and returned via a return(), so
-      // there's no need to keep them in x86 endian format.
-      Bit16u id_drive[256]                           ;
-
-      struct controller_t controller                 ;
-      struct cdrom_t cdrom                           ;
-      struct sense_info_t sense                      ;
-      struct atapi_t atapi                           ;
-
-      Bit8u model_no[41]                             ;
-      } drives[2];
-    unsigned drive_select                            ;
-
-    Bit16u ioaddr1                                   ;
-    Bit16u ioaddr2                                   ;
-    Bit8u  irq                                       ;
-};
-
-struct ramdisk_t;
-
-struct ramdisk_ctrl_ops {
-  Bit32u (*init)(struct ramdisk_t *ramdisk,
-                struct vm_device *dev);
-  void   (*close)(struct ramdisk_t *ramdisk);
-  void   (*reset)(struct ramdisk_t *ramdisk, unsigned type);
-
-};
-
-struct ramdisk_emu_ops {
-
-  int (*read_port)(ushort_t port,
-                  void *dst,
-                  uint_t length,
-                  struct vm_device *dev);
-
-  int (*write_port)(ushort_t port,
-                   void *src,
-                   uint_t length,
-                   struct vm_device *dev);
-
-  int (*read_port_ignore)(ushort_t port,
-                         void *dst,
-                         uint_t length,
-                         struct vm_device *dev);
-
-  int (*write_port_ignore)(ushort_t port,
-                          void *src,
-                          uint_t length,
-                          struct vm_device *dev);
-};
-
 
 struct  ramdisk_t {
-  
   struct channel_t channels[MAX_ATA_CHANNEL];
-
-  struct ramdisk_ctrl_ops cops;
-
-  struct ramdisk_emu_ops eops;
-
-  void *private_data;
-  //  struct vm_device *dev;
 };
 
 
 #define INT13_ELTORITO                 0xe2 //0x2fa
 #define INT13_DISKETTE_FUNCTION        0xe3 //0x2fb
 
-// some packet handling macros
-#define EXTRACT_FIELD(arr,byte,start,num_bits) (((arr)[(byte)] >> (start)) & ((1 << (num_bits)) - 1))
-#define get_packet_field(c,b,s,n) (EXTRACT_FIELD((SELECTED_CONTROLLER((c)).buffer),(b),(s),(n)))
-#define get_packet_byte(c,b) (SELECTED_CONTROLLER((c)).buffer[(b)])
-#define get_packet_word(c,b) (((uint16)SELECTED_CONTROLLER((c)).buffer[(b)] << 8) | SELECTED_CONTROLLER((c)).buffer[(b)+1])
 
 
-#define CONTROLLER(c,a) (channels[(c)].drives[(a)]).controller
-#define DRIVE(c,a) (channels[(c)].drives[(a)])
-#define SELECTED_CONTROLLER(c) (CONTROLLER((c), channels[(c)].drive_select))
-#define SELECTED_DRIVE(c) (DRIVE((c), channels[(c)].drive_select))
-
-
-#define DRIVE_IS_PRESENT(c,a) (channels[(c)].drives[(a)].device_type != IDE_NONE)
-#define DRIVE_IS_HD(c,a) (channels[(c)].drives[(a)].device_type == IDE_DISK)
-#define DRIVE_IS_CD(c,a) (channels[(c)].drives[(a)].device_type == IDE_CDROM)
-#define SELECTED_MODEL(c) (channels[(c)].drives[channels[(c)].drive_select].model_no)
-
-#define MASTER_SELECTED(c) (!channels[(c)].drive_select)
-#define SLAVE_SELECTED(c)  (channels[(c)].drive_select)
-
-#define SELECTED_IS_PRESENT(c) (DRIVE_IS_PRESENT((c),SLAVE_SELECTED((c))))
-#define SELECTED_IS_HD(c) (DRIVE_IS_HD((c),SLAVE_SELECTED((c))))
-#define SELECTED_IS_CD(c) (DRIVE_IS_CD((c),SLAVE_SELECTED((c))))
-
-#define ANY_IS_PRESENT(c) (DRIVE_IS_PRESENT((c),0) || DRIVE_IS_PRESENT((c),1))
-#define SELECTED_TYPE_STRING(channel) ((SELECTED_IS_CD(channel)) ? "CD-ROM" : "NONE")
-
-#define WRITE_FEATURES(c,a) do { uint8 _a = a; CONTROLLER((c),0).features = _a; CONTROLLER((c),1).features = _a; } while(0)
-#define WRITE_SECTOR_COUNT(c,a) do { uint8 _a = a; CONTROLLER((c),0).sector_count = _a; CONTROLLER((c),1).sector_count = _a; } while(0)
-#define WRITE_SECTOR_NUMBER(c,a) do { uint8 _a = a; CONTROLLER((c),0).sector_no = _a; CONTROLLER((c),1).sector_no = _a; } while(0)
-#define WRITE_CYLINDER_LOW(c,a) do { uint8 _a = a; CONTROLLER((c),0).cylinder_no = (CONTROLLER((c),0).cylinder_no & 0xff00) | _a; CONTROLLER((c),1).cylinder_no = (CONTROLLER((c),1).cylinder_no & 0xff00) | _a; } while(0)
-#define WRITE_CYLINDER_HIGH(c,a) do { uint16 _a = a; CONTROLLER((c),0).cylinder_no = (_a << 8) | (CONTROLLER((c),0).cylinder_no & 0xff); CONTROLLER((c),1).cylinder_no = (_a << 8) | (CONTROLLER((c),1).cylinder_no & 0xff); } while(0)
-#define WRITE_HEAD_NO(c,a) do { uint8 _a = a; CONTROLLER((c),0).head_no = _a; CONTROLLER((c),1).head_no = _a; } while(0)
-#define WRITE_LBA_MODE(c,a) do { uint8 _a = a; CONTROLLER((c),0).lba_mode = _a; CONTROLLER((c),1).lba_mode = _a; } while(0)
-
-
-
-#define GOTO_RETURN_VALUE  if(io_len==4){\
-                             goto return_value32;\
-                             }\
-                           else if(io_len==2){\
-                             value16=(Bit16u)value32;\
-                             goto return_value16;\
-                             }\
-                           else{\
-                             value8=(Bit8u)value32;\
-                             goto return_value8;\
-                             }
-
-#define UNUSED(x) ((void)x)
-
-#define PACKET_SIZE 12
-
-static struct ramdisk_t *ramdisk_state;
-
 
+static const char cdrom_str[] = "CD-ROM";
+static const char harddisk_str[] = "HARDDISK";
+static const char none_str[] = "NONE";
 
 
+static inline const char * device_type_to_str(device_type_t type) {
+  switch (type) {
+  case IDE_DISK:
+    return harddisk_str;
+  case IDE_CDROM:
+    return cdrom_str;
+  case IDE_NONE:
+    return none_str;
+  default:
+    return NULL;
+  }
+}
 
 
-////////////////////////////////////////////////////////////////////////////
+static inline void write_features(struct channel_t * channel, uchar_t value) {
+  channel->drives[0].controller.features = value;
+  channel->drives[1].controller.features = value;
+}
 
-/*
- * Static routines
- */
 
-static
-int ramdisk_read_port(ushort_t port,
-                        void *dst,
-                        uint_t length,
-                        struct vm_device *dev);
+static inline void write_sector_count(struct channel_t * channel, uchar_t value) {
+  channel->drives[0].controller.sector_count = value;
+  channel->drives[1].controller.sector_count = value;
+}
 
-static
-int ramdisk_write_port(ushort_t port,
-                         void *src,
-                         uint_t length,
-                         struct vm_device *dev);
+static inline void write_sector_number(struct channel_t * channel, uchar_t value) {
+  channel->drives[0].controller.sector_no = value;
+  channel->drives[1].controller.sector_no = value;
+}
 
-static
-int ramdisk_read_port_ignore(ushort_t port,
-                               void *dst,
-                               uint_t length,
-                               struct vm_device *dev);
 
-static
-int ramdisk_write_port_ignore(ushort_t port,
-                                void *src,
-                                uint_t length,
-                                struct vm_device *dev);
+static inline void write_cylinder_low(struct channel_t * channel, uchar_t value) {
+  channel->drives[0].controller.cylinder_no &= 0xff00;
+  channel->drives[0].controller.cylinder_no |= value;
+  channel->drives[1].controller.cylinder_no &= 0xff00;
+  channel->drives[1].controller.cylinder_no |= value;
+}
 
+static inline void write_cylinder_high(struct channel_t * channel, uchar_t value) {
+  ushort_t val2 = value;
+  val2 = val2 << 8;
+  channel->drives[0].controller.cylinder_no &= 0x00ff;
+  channel->drives[0].controller.cylinder_no |= (val2 & 0xff00);
 
-static
-Bit32u rd_read_handler(struct channel_t *channels, Bit32u address, unsigned io_len);
+  channel->drives[1].controller.cylinder_no &= 0x00ff;
+  channel->drives[1].controller.cylinder_no |= (val2 & 0xff00);
+}
 
-static
-void rd_write_handler(struct channel_t *channels, Bit32u address, 
-                     Bit32u value, unsigned io_len);
+static inline void write_head_no(struct channel_t * channel, uchar_t value) {
+  channel->drives[0].controller.head_no = value;
+  channel->drives[1].controller.head_no = value;
+}
 
+static inline void write_lba_mode(struct channel_t * channel, uchar_t value) {
+  channel->drives[0].controller.lba_mode = value;
+  channel->drives[1].controller.lba_mode = value;
+}
 
 
-/*
- * ATAPI routines
- */
 
-static 
-void rd_identify_ATAPI_drive(struct channel_t *channels, Bit8u channel);
+static inline struct drive_t * get_selected_drive(struct channel_t * channel) {
+  return &(channel->drives[channel->drive_select]);
+}
 
 
-static 
-void rd_init_send_atapi_command(struct channel_t *channels, Bit8u channel, Bit8u command, int req_length, int alloc_length, bool lazy /*= false*/);
+static inline int is_primary_port(struct ramdisk_t * ramdisk, ushort_t port) {
+  switch(port) 
+    {
+    case PRI_DATA_PORT:
+    case PRI_FEATURES_PORT:
+    case PRI_SECT_CNT_PORT:
+    case PRI_SECT_ADDR1_PORT:
+    case PRI_SECT_ADDR2_PORT:
+    case PRI_SECT_ADDR3_PORT:
+    case PRI_DRV_SEL_PORT:
+    case PRI_CMD_PORT:
+    case PRI_CTRL_PORT:
+      return 1;
+    default:
+      return 0;
+    }
+}
 
-static 
-void rd_ready_to_send_atapi(struct channel_t *channels, Bit8u channel);
 
 
-static 
-void rd_atapi_cmd_error(struct channel_t *channels, Bit8u channel, sense_t sense_key, asc_t asc);
+static inline int is_secondary_port(struct ramdisk_t * ramdisk, ushort_t port) {
+  switch(port) 
+    {
+    case SEC_DATA_PORT:
+    case SEC_FEATURES_PORT:
+    case SEC_SECT_CNT_PORT:
+    case SEC_SECT_ADDR1_PORT:
+    case SEC_SECT_ADDR2_PORT:
+    case SEC_SECT_ADDR3_PORT:
+    case SEC_DRV_SEL_PORT:
+    case SEC_CMD_PORT:
+    case SEC_CTRL_PORT:
+      return 1;
+    default:
+      return 0;
+    }
+}
 
-static 
-void rd_init_mode_sense_single(struct channel_t *channels, Bit8u channel, const void* src, int size);
+static inline int num_drives_on_channel(struct channel_t * channel) {
+  if ((channel->drives[0].device_type == IDE_NONE) &&
+      (channel->drives[1].device_type == IDE_NONE)) {
+    return 0;
+  } else if ((channel->drives[0].device_type != IDE_NONE) &&
+            (channel->drives[1].device_type != IDE_NONE)) {
+    return 2;
+  } else {
+    return 1;
+  }
+}
 
-static 
-void rd_atapi_cmd_nop(struct channel_t *channels, Bit8u channel);
 
-static
-void rd_command_aborted(struct channel_t *channels, Bit8u channel, unsigned value);
 
+static inline uchar_t extract_bits(uchar_t * buf, uint_t buf_offset, uint_t bit_offset, uint_t num_bits) {
+  uchar_t val = buf[buf_offset];
+  val = val >> bit_offset;
+  val &= ((1 << num_bits) -1);
+  return val;
+}
 
-/*
- * Interrupt handling
- */
 
-static 
-void rd_raise_interrupt(struct channel_t *channels, Bit8u channel);
+static inline uchar_t get_packet_field(struct channel_t * channel, uint_t packet_offset, uint_t bit_offset, uint_t num_bits) {
+  struct drive_t * drive = get_selected_drive(channel);
+  return extract_bits(drive->controller.buffer, packet_offset, bit_offset, num_bits);
+}
 
-static 
-void rd_lower_irq(struct vm_device *dev, Bit32u irq);
 
+static inline uchar_t get_packet_byte(struct channel_t * channel, uint_t offset) {
+  struct drive_t * drive = get_selected_drive(channel);
+  return drive->controller.buffer[offset];
+}
 
+static inline uint16_t get_packet_word(struct channel_t * channel, uint_t offset) {
+  struct drive_t * drive = get_selected_drive(channel);
+  uint16_t val = drive->controller.buffer[offset];
+  val = val << 8;
+  val |= drive->controller.buffer[offset + 1];
+  return val;
+}
 
-/*
- * Helper routines
- */
 
-uint16 rd_read_16bit(const uint8* buf) 
-{
+static inline uint16_t rd_read_16bit(const uint8_t* buf) {
   return (buf[0] << 8) | buf[1];
 }
 
 
 
-uint32 rd_read_32bit(const uint8* buf) 
-{
+static inline uint32_t rd_read_32bit(const uint8_t* buf) {
   return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
 }
 
-////////////////////////////////////////////////////////////////////
-
-
-void rd_print_state(struct ramdisk_t *ramdisk, 
-                        struct vm_device *dev)
-//Bit32u   rd_init_harddrive(struct channel_t *channels)
-{
-
-  uchar_t channel; 
-  uchar_t device;
-  struct channel_t *channels = (struct channel_t *)(&(ramdisk->channels));
-
-  for (channel = 0; channel < MAX_ATA_CHANNEL; channel++)
-    memset((char *)(channels + channel), 0, sizeof(struct channel_t));
-
-  Ramdisk_Print("sizeof(*channels) = %d\n", sizeof((*channels)));
-  Ramdisk_Print("sizeof(channles->drives[0].controller) = %d\n", sizeof((channels->drives[0].controller)));
-  Ramdisk_Print("sizeof(channles->drives[0].cdrom) = %d\n", sizeof((channels->drives[0].cdrom)));
-  Ramdisk_Print("sizeof(channles->drives[0].sense) = %d\n", sizeof((channels->drives[0].sense)));
-  Ramdisk_Print("sizeof(channles->drives[0].atapi) = %d\n", sizeof((channels->drives[0].atapi)));
-
-
-  Ramdisk_Print("sizeof(channles->drives[0].controller.status) = %d\n", sizeof((channels->drives[0].controller.status)));
-  Ramdisk_Print("sizeof(channles->drives[0].controller.sector_count) = %d\n", sizeof((channels->drives[0].controller.sector_count)));
-  Ramdisk_Print("sizeof(channles->drives[0].controller.interrupt_reason) = %d\n", sizeof((channels->drives[0].controller.interrupt_reason)));
-
-  Ramdisk_Print("sizeof(channles->drives[0].controller.cylinder_no) = %d\n", sizeof((channels->drives[0].controller.cylinder_no)));
-  Ramdisk_Print("sizeof(channles->drives[0].controller.byte_count) = %d\n", sizeof((channels->drives[0].controller.byte_count)));
-
-
-  Ramdisk_Print("sizeof(channles->drives[0].controller.control) = %d\n", sizeof((channels->drives[0].controller.control)));
-
-
-  for (channel = 0; channel < MAX_ATA_CHANNEL; channel++){
-  
-    for (device = 0; device < 2; device++){
-                  
-      // Initialize controller state, even if device is not present
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.busy = %d\n",channel, device, channels[channel].drives[device].controller.status.busy);
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.drive_ready = %d\n", channel, device, channels[channel].drives[device].controller.status.drive_ready);
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.write_fault = %d\n", channel, device, channels[channel].drives[device].controller.status.write_fault);
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.seek_complete = %d\n", channel, device, channels[channel].drives[device].controller.status.seek_complete);
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.drq = %d\n", channel, device, channels[channel].drives[device].controller.status.drq);
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.corrected_data = %d\n", channel, device, channels[channel].drives[device].controller.status.corrected_data);
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.index_pulse = %d\n", channel, device, channels[channel].drives[device].controller.status.index_pulse);
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.index_pulse_count = %d\n", channel, device, channels[channel].drives[device].controller.status.index_pulse_count);
-      Ramdisk_Print("channels[%d].drives[%d].controller.status.err = %d\n", channel, device, channels[channel].drives[device].controller.status.err);
+////////////////////////////////////////////////////////////////////////////
 
 
-      Ramdisk_Print("channels[%d].drives[%d].controller.error_register = %d\n", channel, device, channels[channel].drives[device].controller.error_register);
-      Ramdisk_Print("channels[%d].drives[%d].controller.head_no = %d\n", channel, device, channels[channel].drives[device].controller.head_no);
-      Ramdisk_Print("channels[%d].drives[%d].controller.sector_count = %d\n", channel, device, channels[channel].drives[device].controller.sector_count);
-      Ramdisk_Print("channels[%d].drives[%d].controller.sector_no = %d\n", channel, device, channels[channel].drives[device].controller.sector_no);
-      Ramdisk_Print("channels[%d].drives[%d].controller.cylinder_no = %d\n", channel, device, channels[channel].drives[device].controller.cylinder_no);
-      Ramdisk_Print("channels[%d].drives[%d].controller.current_command = %02x\n", channel, device, channels[channel].drives[device].controller.current_command);
-      Ramdisk_Print("channels[%d].drives[%d].controller.buffer_index = %d\n", channel, device, channels[channel].drives[device].controller.buffer_index);
+/*
+ * ATAPI routines
+ */
 
 
-      Ramdisk_Print("channels[%d].drives[%d].controller.control.reset = %d\n", channel, device, channels[channel].drives[device].controller.control.reset);
-      Ramdisk_Print("channels[%d].drives[%d].controller.control.disable_irq = %d\n", channel, device, channels[channel].drives[device].controller.control.disable_irq);
+static void rd_init_mode_sense_single(struct vm_device * dev, struct channel_t * channel, const void * src, int size);
 
+static void rd_command_aborted(struct vm_device * dev, struct channel_t * channel, unsigned value);
 
-      Ramdisk_Print("channels[%d].drives[%d].controller.reset_in_progress = %d\n", channel, device, channels[channel].drives[device].controller.reset_in_progress);
-      Ramdisk_Print("channels[%d].drives[%d].controller.sectors_per_block = %02x\n", channel, device, channels[channel].drives[device].controller.sectors_per_block); 
-      Ramdisk_Print("channels[%d].drives[%d].controller.lba_mode = %d\n", channel, device, channels[channel].drives[device].controller.lba_mode); 
-      Ramdisk_Print("channels[%d].drives[%d].controller.features = %d\n", channel, device, channels[channel].drives[device].controller.features); 
 
+/*
+ * Interrupt handling
+ */
+static void rd_raise_interrupt(struct vm_device * dev, struct channel_t * channel);
+static void rd_lower_irq(struct vm_device *dev, Bit32u irq);
 
-      Ramdisk_Print("channels[%d].drives[%d].model_no = %s\n", channel, device, channels[channel].drives[device].model_no); 
-      Ramdisk_Print("channels[%d].drives[%d].device_type = %d\n", channel, device, channels[channel].drives[device].device_type); 
-      Ramdisk_Print("channels[%d].drives[%d].cdrom.locked = %d\n", channel, device, channels[channel].drives[device].cdrom.locked); 
-      Ramdisk_Print("channels[%d].drives[%d].sense.sense_key = %d\n", channel, device, channels[channel].drives[device].sense.sense_key); 
-      Ramdisk_Print("channels[%d].drives[%d].sense.asc = %d\n", channel, device, channels[channel].drives[device].sense.asc); 
-      Ramdisk_Print("channels[%d].drives[%d].sense.ascq = %d\n", channel, device, channels[channel].drives[device].sense.ascq); 
 
 
+/*
+ * Helper routines
+ */
 
-      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.c_d = %02x\n", channel, device, channels[channel].drives[device].controller.interrupt_reason.c_d);
 
-      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.i_o = %02x\n", channel, device, channels[channel].drives[device].controller.interrupt_reason.i_o);
 
-      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.rel = %02x\n", channel, device, channels[channel].drives[device].controller.interrupt_reason.rel);
+#ifdef DEBUG_RAMDISK
+static void rd_print_state(struct ramdisk_t *ramdisk,  struct vm_device *dev)
+static int check_bit_fields(struct controller_t * controller);
+#endif
+////////////////////////////////////////////////////////////////////
 
-      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.tag = %02x\n", channel, device, channels[channel].drives[device].controller.interrupt_reason.tag);
 
-      Ramdisk_Print("channels[%d].drives[%d].cdrom.ready = %d\n", channel, device, channels[channel].drives[device].cdrom.ready);
-      
-    }//for device
-  }//for channel
-  
-  return;
-}
 
 
-Bit32u rd_init_harddrive(struct ramdisk_t *ramdisk, 
-                        struct vm_device *dev)
-{
 
-  
-  uchar_t channel; 
-  uchar_t device;
+Bit32u rd_init_hardware(struct ramdisk_t *ramdisk) {
+  uint_t channel_num; 
+  uint_t device;
   struct channel_t *channels = (struct channel_t *)(&(ramdisk->channels));
 
   Ramdisk_Print("[rd_init_harddrive]\n");
-  for (channel = 0; channel < MAX_ATA_CHANNEL; channel++)
-    memset((char *)(channels + channel), 0, sizeof(struct channel_t));
 
+  for (channel_num = 0; channel_num < MAX_ATA_CHANNEL; channel_num++) {
+    memset((char *)(channels + channel_num), 0, sizeof(struct channel_t));
+  }
 
-  for (channel = 0; channel < MAX_ATA_CHANNEL; channel++){
-  
-    channels[channel].ioaddr1 = 0x0;
-    channels[channel].ioaddr2 = 0x0;
-    channels[channel].irq = 0;
+  for (channel_num = 0; channel_num < MAX_ATA_CHANNEL; channel_num++){
+    struct channel_t * channel = &(channels[channel_num]);
+
+    channel->ioaddr1 = 0x0;
+    channel->ioaddr2 = 0x0;
+    channel->irq = 0;
 
     for (device = 0; device < 2; device++){
+      struct drive_t * drive = &(channel->drives[device]);
+      struct controller_t * controller = &(drive->controller);
+
+      controller->status.busy = 0;
+      controller->status.drive_ready = 1;
+      controller->status.write_fault = 0;
+      controller->status.seek_complete = 1;
+      controller->status.drq = 0;
+      controller->status.corrected_data = 0;
+      controller->status.index_pulse = 0;
+      controller->status.index_pulse_count = 0;
+      controller->status.err = 0;
+
+      controller->error_register = 0x01; // diagnostic code: no error
+      controller->head_no = 0;
+      controller->sector_count = 1;
+      controller->sector_no = 1;
+      controller->cylinder_no = 0;
+      controller->current_command = 0x00;
+      controller->buffer_index = 0;
+
+      controller->control.reset = 0;
+      controller->control.disable_irq = 0;
+      controller->reset_in_progress = 0;
+
+      controller->sectors_per_block = 0x80;
+      controller->lba_mode = 0;
       
-
-      CONTROLLER(channel,device).status.busy           = 0;
-      CONTROLLER(channel,device).status.drive_ready    = 1;
-      CONTROLLER(channel,device).status.write_fault    = 0;
-      CONTROLLER(channel,device).status.seek_complete  = 1;
-      CONTROLLER(channel,device).status.drq            = 0;
-      CONTROLLER(channel,device).status.corrected_data = 0;
-      CONTROLLER(channel,device).status.index_pulse    = 0;
-      CONTROLLER(channel,device).status.index_pulse_count = 0;
-      CONTROLLER(channel,device).status.err            = 0;
-
-      CONTROLLER(channel,device).error_register = 0x01; // diagnostic code: no error
-      CONTROLLER(channel,device).head_no        = 0;
-      CONTROLLER(channel,device).sector_count   = 1;
-      CONTROLLER(channel,device).sector_no      = 1;
-      CONTROLLER(channel,device).cylinder_no    = 0;
-      CONTROLLER(channel,device).current_command = 0x00;
-      CONTROLLER(channel,device).buffer_index = 0;
-
-      CONTROLLER(channel,device).control.reset       = 0;
-      CONTROLLER(channel,device).control.disable_irq = 0;
-      CONTROLLER(channel,device).reset_in_progress   = 0;
-
-      CONTROLLER(channel,device).sectors_per_block   = 0x80;
-      CONTROLLER(channel,device).lba_mode            = 0;
       
-      CONTROLLER(channel,device).features            = 0;
+      controller->features = 0;
        
-         // If not present
-      channels[channel].drives[device].device_type           = IDE_NONE;
+      // If not present
+      drive->device_type = IDE_NONE;
 
       // Make model string
-      strncpy((char*)channels[channel].drives[device].model_no, 
-             "", 40);
-      while(strlen((char *)channels[channel].drives[device].model_no) < 40) {
-        strcat ((char*)channels[channel].drives[device].model_no, " ");
+      strncpy((char*)(drive->model_no), "", 40);
+      while(strlen((char *)(drive->model_no)) < 40) {
+        strcat ((char*)(drive->model_no), " ");
       }
 
-//      Ramdisk_Print("channels[%d].drives[%d].controller.current_command = %02x\n", channel, device, channels[channel].drives[device].controller.current_command);
-//      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.c_d = %02x\n", channel, device, channels[channel].drives[device].controller.interrupt_reason.c_d);
-
-//      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.i_o = %02x\n", channel, device, channels[channel].drives[device].controller.interrupt_reason.i_o);
-
-//      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.rel = %02x\n", channel, device, channels[channel].drives[device].controller.interrupt_reason.rel);
 
-//      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.tag = %02x\n", channel, device, channels[channel].drives[device].controller.interrupt_reason.tag);
       
-//      Ramdisk_Print("channels[%d].drives[%d].controller.control.disable_irq = %d\n", channel, device, channels[channel].drives[device].controller.control.disable_irq);
       
-      
-      
-      if (channel == 1) {
+      if (channel_num == 1) {
 
-       channels[channel].ioaddr1 = 0x170;
-       channels[channel].ioaddr2 = 0x370;
-       channels[channel].irq =  15;
-       channels[channel].drive_select = 0;
+       channel->ioaddr1 = 0x170;
+       channel->ioaddr2 = 0x370;
+       channel->irq =  15;
+       channel->drive_select = 0;
        
        if (device == 0) {
          // Make model string
-         strncpy((char*)channels[channel].drives[device].model_no, 
-                 "Zheng's Ramdisk", 40);
-         while (strlen((char *)channels[channel].drives[device].model_no) < 40) {
-           strcat ((char*)channels[channel].drives[device].model_no, " ");
+         strncpy((char*)(drive->model_no), "V3VEE Ramdisk", 40);
+         while (strlen((char *)(drive->model_no)) < 40) {
+           strcat ((char*)(drive->model_no), " ");
          }
-
-         Ramdisk_Print("CDROM on target %d/%d\n", channel, device);
          
-         channels[channel].drives[device].device_type = IDE_CDROM;
-         channels[channel].drives[device].cdrom.locked = 0;
-         channels[channel].drives[device].sense.sense_key = SENSE_NONE;
-         channels[channel].drives[device].sense.asc = 0;
-         channels[channel].drives[device].sense.ascq = 0;
+         Ramdisk_Print("CDROM on target %d/%d\n", channel, device);
          
-
-         //Check bit fields
-         channels[channel].drives[device].controller.sector_count = 0;
-         channels[channel].drives[device].controller.interrupt_reason.c_d = 1;
-         if (channels[channel].drives[device].controller.sector_count != 0x01) {
-           Ramdisk_Print("interrupt reason bit field error\n");
-           return INTR_REASON_BIT_ERR;
-         }
-
-         channels[channel].drives[device].controller.sector_count = 0;
-         channels[channel].drives[device].controller.interrupt_reason.i_o = 1;
-         if (channels[channel].drives[device].controller.sector_count != 0x02) {
-           Ramdisk_Print("interrupt reason bit field error\n");
-           return INTR_REASON_BIT_ERR;
-         }
+         drive->device_type = IDE_CDROM;
+         drive->cdrom.locked = 0;
+         drive->sense.sense_key = SENSE_NONE;
+         drive->sense.asc = 0;
+         drive->sense.ascq = 0;
          
-         channels[channel].drives[device].controller.sector_count = 0;
-         channels[channel].drives[device].controller.interrupt_reason.rel = 1;
-         if (channels[channel].drives[device].controller.sector_count != 0x04) {
-           Ramdisk_Print("interrupt reason bit field error\n");
+#ifdef RAMDISK_DEBUG
+         if (check_bit_fields(controller) == INTR_REASON_BIT_ERR) {
+           Ramdisk_Print("interrupt reason: bit field error\n");
            return INTR_REASON_BIT_ERR;
          }
-       
-         channels[channel].drives[device].controller.sector_count = 0;
-         channels[channel].drives[device].controller.interrupt_reason.tag = 3;
-         if (channels[channel].drives[device].controller.sector_count != 0x18) {
-           Ramdisk_Print("interrupt reason bit field error\n");
-           return INTR_REASON_BIT_ERR;
-         }
-         
+#endif
          
-         channels[channel].drives[device].controller.sector_count = 0;
+         controller->sector_count = 0;
 
          // allocate low level driver
-         channels[channel].drives[device].cdrom.cd = (struct cdrom_interface*)V3_Malloc(sizeof(struct cdrom_interface));
-         Ramdisk_Print("cd = %x\n", channels[channel].drives[device].cdrom.cd);
-         V3_ASSERT(channels[channel].drives[device].cdrom.cd != NULL);
+         drive->cdrom.cd = (struct cdrom_interface*)V3_Malloc(sizeof(struct cdrom_interface));
+         Ramdisk_Print("cd = %x\n", drive->cdrom.cd);
+         V3_ASSERT(drive->cdrom.cd != NULL);
          
-         struct cdrom_interface *cdif = channels[channel].drives[device].cdrom.cd;
+         struct cdrom_interface * cdif = drive->cdrom.cd;
          memset(cdif, 0, sizeof(struct cdrom_interface));
          init_cdrom(cdif);
          cdif->ops.init(cdif);
 
          Ramdisk_Print("\t\tCD on ata%d-%d: '%s'\n",channel, device, "");
          
-         if((channels[channel].drives[device].cdrom.cd->ops).insert_cdrom(cdif, NULL)) {
+         if((drive->cdrom.cd->ops).insert_cdrom(cdif, NULL)) {
            Ramdisk_Print("\t\tMedia present in CD-ROM drive\n");
-           channels[channel].drives[device].cdrom.ready = 1;
-           channels[channel].drives[device].cdrom.capacity = channels[channel].drives[device].cdrom.cd->ops.capacity(cdif);
+           drive->cdrom.ready = 1;
+           drive->cdrom.capacity = drive->cdrom.cd->ops.capacity(cdif);
          } else {                  
            Ramdisk_Print("\t\tCould not locate CD-ROM, continuing with media not present\n");
-           channels[channel].drives[device].cdrom.ready = 0;
+           drive->cdrom.ready = 0;
          }
          
        }//if device = 0
     }//for device
   }//for channel
 
-  ramdisk->private_data = dev;
-  ramdisk_state = ramdisk;
-  Ramdisk_Print("ramdisk_state = %x\n", ramdisk_state);
-  Ramdisk_Print("ramdisk = %x\n", ramdisk);
-  //  rd_print_state(ramdisk, dev);
+
   return 0;
 }
 
 
-void   rd_reset_harddrive(struct ramdisk_t *ramdisk, unsigned type)
-{
+/*
+  static void rd_reset_harddrive(struct ramdisk_t *ramdisk, unsigned type) {
   return;
-}
-
+  }
 
-void   rd_close_harddrive(struct ramdisk_t *ramdisk)
-{
+*/
+static void rd_close_harddrive(struct ramdisk_t *ramdisk) {
   return;
 }
 
 ////////////////////////////////////////////////////////////////////
 
 
-static
-Bit32u rd_read_handler(struct channel_t *channels, Bit32u address, unsigned io_len)
-{
-  Bit8u value8;
-  Bit16u value16;
-  Bit32u value32;
 
-  unsigned drive_select;
-  unsigned index;      
-  unsigned increment = 0;
+static int read_data_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+  struct ramdisk_t * ramdisk  = (struct ramdisk_t *)(dev->private_data);
+  struct channel_t * channel = NULL;
+  struct drive_t * drive = NULL;
+  struct controller_t * controller = NULL;
+  struct cdrom_interface *cdif = drive->cdrom.cd;
 
-  Bit8u  channel = MAX_ATA_CHANNEL;
-  Bit32u port = 0xff; // undefined
 
-  //Ramdisk_Print("[rd_read_handler]\n");
-
-  for (channel=0; channel<MAX_ATA_CHANNEL; channel++) {
-    if ((address & 0xfff8) == channels[channel].ioaddr1) {
-      port = address - channels[channel].ioaddr1;
-      break;
-      }
-    else if ((address & 0xfff8) == channels[channel].ioaddr2) {
-      port = address - channels[channel].ioaddr2 + 0x10;
-      break;
-      }
-    }
-
-  if (channel == MAX_ATA_CHANNEL) {
-    if ((address < 0x03f6) || (address > 0x03f7)) {
-      RD_PANIC("Error: read: unable to find ATA channel, ioport=0x%04x\n", address);
-      return 0;
-    } else {
-      channel = 0;
-      port = address - 0x03e0;
-    }
-  }
-
-  drive_select = channels[channel].drive_select;
-  if (port != 0x00){
-    Ramdisk_Print("[R_handler] IO read addr at %x, on drive %d/%d, curcmd = %02x\n", address, channel, drive_select, SELECTED_CONTROLLER(channel).current_command);
-  }else{
-    
+  if (is_primary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[0]);
+  } else if (is_secondary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[1]);
+  } else {
+    PrintError("Invalid Port: %d\n", port);
+    return -1;
   }
+  
+  drive = get_selected_drive(channel);
+  controller = &(drive->controller);
 
 
-  struct cdrom_interface *cdif = channels[channel].drives[drive_select].cdrom.cd;
-  switch (port) {
+  switch (controller->current_command) {
+  case 0xec:    // IDENTIFY DEVICE
+  case 0xa1:
+    {
 
-  case 0x00: // hard disk data (16bit) 0x1f0
 
-    switch (SELECTED_CONTROLLER(channel).current_command) {
-    case 0xec:    // IDENTIFY DEVICE
-    case 0xa1:
-
-      index = 0;
-      SELECTED_CONTROLLER(channel).status.busy = 0;
-      SELECTED_CONTROLLER(channel).status.drive_ready = 1;
-      SELECTED_CONTROLLER(channel).status.write_fault = 0;
-      SELECTED_CONTROLLER(channel).status.seek_complete = 1;
-      SELECTED_CONTROLLER(channel).status.corrected_data = 0;
-      SELECTED_CONTROLLER(channel).status.err = 0;
+      controller->status.busy = 0;
+      controller->status.drive_ready = 1;
+      controller->status.write_fault = 0;
+      controller->status.seek_complete = 1;
+      controller->status.corrected_data = 0;
+      controller->status.err = 0;
+      
+      /*
+       value32 = controller->buffer[index];
+       index++;
        
-      index = SELECTED_CONTROLLER(channel).buffer_index;
-      value32 = SELECTED_CONTROLLER(channel).buffer[index];
-      index++;
-
-      if (io_len >= 2) {
-       value32 |= (SELECTED_CONTROLLER(channel).buffer[index] << 8);
+       if (io_len >= 2) {
+       value32 |= (controller->buffer[index] << 8);
        index++;
-      }
-      if (io_len == 4) {
-       value32 |= (SELECTED_CONTROLLER(channel).buffer[index] << 16);
-       value32 |= (SELECTED_CONTROLLER(channel).buffer[index+1] << 24);
+       }
+       
+       if (io_len == 4) {
+       value32 |= (controller->buffer[index] << 16);
+       value32 |= (controller->buffer[index+1] << 24);
        index += 2;
-      }
-      SELECTED_CONTROLLER(channel).buffer_index = index;
-      
-      if (SELECTED_CONTROLLER(channel).buffer_index >= 512) {
+       }
        
-       SELECTED_CONTROLLER(channel).status.drq = 0;
+       controller->buffer_index = index;
+      */
+      /* JRL */
+      memcpy(dst, controller->buffer + controller->buffer_index, length);
+      controller->buffer_index += length;
+      
+      if (controller->buffer_index >= 512) {
+       controller->status.drq = 0;
       }
-      GOTO_RETURN_VALUE;
-
-    case 0xa0: //send packet cmd 
+      
+      return length;
+    }
+  case 0xa0: //send packet cmd 
+    {
+      uint_t index = controller->buffer_index;
 
-       index = SELECTED_CONTROLLER(channel).buffer_index;
-       increment = 0;
-       Ramdisk_Print("\t\tatapi.command(%02x), index(%d), cdrom.remaining_blocks(%d)\n", SELECTED_DRIVE(channel).atapi.command, index, SELECTED_DRIVE(channel).cdrom.remaining_blocks);
-       // Load block if necessary
-       if (index >= 2048) {
-         if (index > 2048)
-           RD_PANIC("\t\tindex > 2048 : 0x%x\n",index);
-         switch (SELECTED_DRIVE(channel).atapi.command) {
-         case 0x28: // read (10)
-         case 0xa8: // read (12)
-
-           if (!SELECTED_DRIVE(channel).cdrom.ready) {
-             RD_PANIC("\t\tRead with CDROM not ready\n");
-           } 
-           SELECTED_DRIVE(channel).cdrom.cd->ops.read_block(cdif, SELECTED_CONTROLLER(channel).buffer,
-                                                           SELECTED_DRIVE(channel).cdrom.next_lba);
-           SELECTED_DRIVE(channel).cdrom.next_lba++;
-           SELECTED_DRIVE(channel).cdrom.remaining_blocks--;
-           
-           
-           if (!SELECTED_DRIVE(channel).cdrom.remaining_blocks)
-             Ramdisk_Print("\t\tLast READ block loaded {CDROM}\n");
-           else
-             Ramdisk_Print("\t\tREAD block loaded (%d remaining) {CDROM}\n",
-                           SELECTED_DRIVE(channel).cdrom.remaining_blocks);
-           
-           // one block transfered, start at beginning
-           index = 0;
-           break;
-           
-         default: // no need to load a new block
-           break;
+      
+      Ramdisk_Print("\t\tatapi.command(%02x), index(%d), cdrom.remaining_blocks(%d)\n", 
+                   drive->atapi.command, 
+                   index, 
+                   drive->cdrom.remaining_blocks);
+      
+      // Load block if necessary
+      if (index >= 2048) {
+       
+       if (index > 2048) {
+         RD_PANIC("\t\tindex > 2048 : 0x%x\n",index);
+       }
+       
+       switch (drive->atapi.command) {
+       case 0x28: // read (10)
+       case 0xa8: // read (12)
+         
+         if (!(drive->cdrom.ready)) {
+           RD_PANIC("\t\tRead with CDROM not ready\n");
+         } 
+         
+         drive->cdrom.cd->ops.read_block(cdif, controller->buffer,
+                                         drive->cdrom.next_lba);
+         drive->cdrom.next_lba++;
+         drive->cdrom.remaining_blocks--;
+         
+         
+         if (!(drive->cdrom.remaining_blocks)) {
+           Ramdisk_Print("\t\tLast READ block loaded {CDROM}\n");
+         } else {
+           Ramdisk_Print("\t\tREAD block loaded (%d remaining) {CDROM}\n",
+                         drive->cdrom.remaining_blocks);
          }
+         
+         // one block transfered, start at beginning
+         index = 0;
+         break;
+         
+       default: // no need to load a new block
+         break;
        }
+      }
+    
 
-       value32 = SELECTED_CONTROLLER(channel).buffer[index+increment];
+      /*
+       increment = 0;
+       value32 = controller->buffer[index + increment];
        increment++;
+       
        if (io_len >= 2) {
-         value32 |= (SELECTED_CONTROLLER(channel).buffer[index+increment] << 8);
-         increment++;
+       value32 |= (controller->buffer[index + increment] << 8);
+       increment++;
        }
+       
        if (io_len == 4) {
-         value32 |= (SELECTED_CONTROLLER(channel).buffer[index+increment] << 16);
-         value32 |= (SELECTED_CONTROLLER(channel).buffer[index+increment+1] << 24);
-         increment += 2;
+       value32 |= (controller->buffer[index + increment] << 16);
+       value32 |= (controller->buffer[index + increment + 1] << 24);
+       increment += 2;
        }
-       SELECTED_CONTROLLER(channel).buffer_index = index + increment;
-       SELECTED_CONTROLLER(channel).drq_index += increment;
+
+       controller->buffer_index = index + increment;
+       controller->drq_index += increment;
+
+      */
+      /* JRL: CHECK THAT there is enough data in the buffer to copy.... */
+      {      
+       memcpy(dst, controller->buffer + index, length);
+       
+       controller->buffer_index  = index + length;
+       controller->drq_index += length;
+      }
+      
+      /* *** */
+      
+      if (controller->drq_index >= (unsigned)drive->atapi.drq_bytes) {
+       controller->status.drq = 0;
+       controller->drq_index = 0;
+       
+       drive->atapi.total_bytes_remaining -= drive->atapi.drq_bytes;
        
-       if (SELECTED_CONTROLLER(channel).drq_index >= (unsigned)SELECTED_DRIVE(channel).atapi.drq_bytes) {
-         SELECTED_CONTROLLER(channel).status.drq = 0;
-         SELECTED_CONTROLLER(channel).drq_index = 0;
+       if (drive->atapi.total_bytes_remaining > 0) {
+         // one or more blocks remaining (works only for single block commands)
          
-         SELECTED_DRIVE(channel).atapi.total_bytes_remaining -= SELECTED_DRIVE(channel).atapi.drq_bytes;
+         Ramdisk_Print("\t\tPACKET drq bytes read\n");
+         controller->interrupt_reason.i_o = 1;
+         controller->status.busy = 0;
+         controller->status.drq = 1;
+         controller->interrupt_reason.c_d = 0;
          
-         if (SELECTED_DRIVE(channel).atapi.total_bytes_remaining > 0) {
-           // one or more blocks remaining (works only for single block commands)
-
-           Ramdisk_Print("\t\tPACKET drq bytes read\n");
-           SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
-           SELECTED_CONTROLLER(channel).status.busy = 0;
-           SELECTED_CONTROLLER(channel).status.drq = 1;
-           SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 0;
-           
-           // set new byte count if last block
-           if (SELECTED_DRIVE(channel).atapi.total_bytes_remaining < SELECTED_CONTROLLER(channel).byte_count) {
-             SELECTED_CONTROLLER(channel).byte_count = SELECTED_DRIVE(channel).atapi.total_bytes_remaining;
-           }
-           SELECTED_DRIVE(channel).atapi.drq_bytes = SELECTED_CONTROLLER(channel).byte_count;
-           
-           rd_raise_interrupt(channels, channel);
-         } else {
-           // all bytes read
-           Ramdisk_Print("\t\tPACKET all bytes read\n");
-           SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
-           SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1;
-           SELECTED_CONTROLLER(channel).status.drive_ready = 1;
-           SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0;
-           SELECTED_CONTROLLER(channel).status.busy = 0;
-           SELECTED_CONTROLLER(channel).status.drq = 0;
-           SELECTED_CONTROLLER(channel).status.err = 0;
-           
-           rd_raise_interrupt(channels, channel);
+         // set new byte count if last block
+         if (drive->atapi.total_bytes_remaining < controller->byte_count) {
+           controller->byte_count = drive->atapi.total_bytes_remaining;
          }
+         drive->atapi.drq_bytes = controller->byte_count;
+         
+         rd_raise_interrupt(dev, channel);
+       } else {
+         // all bytes read
+         Ramdisk_Print("\t\tPACKET all bytes read\n");
+         
+         controller->interrupt_reason.i_o = 1;
+         controller->interrupt_reason.c_d = 1;
+         controller->status.drive_ready = 1;
+         controller->interrupt_reason.rel = 0;
+         controller->status.busy = 0;
+         controller->status.drq = 0;
+         controller->status.err = 0;
+         
+         rd_raise_interrupt(dev, channel);
        }
-       GOTO_RETURN_VALUE;
-       break;
-      
-    default:
-      Ramdisk_Print("\t\tread need support more command: %02x\n", SELECTED_CONTROLLER(channel).current_command);
+      }
+      return length;
       break;
     }
-    ///////////////////////////////////////////
-
-  case 0x01: // hard disk error register 0x1f1
 
-    SELECTED_CONTROLLER(channel).status.err = 0;
-    value8 = (!SELECTED_IS_PRESENT(channel)) ? 0 : SELECTED_CONTROLLER(channel).error_register;
-    goto return_value8;
-    break;
-  case 0x02: // hard disk sector count / interrupt reason 0x1f2
-    value8 = (!SELECTED_IS_PRESENT(channel)) ? 0 : SELECTED_CONTROLLER(channel).sector_count;
-    goto return_value8;
-    break;
-  case 0x03: // sector number 0x1f3
-    value8 = (!SELECTED_IS_PRESENT(channel)) ? 0 : SELECTED_CONTROLLER(channel).sector_no;
-    goto return_value8;
-  case 0x04: // cylinder low 0x1f4  
-    // -- WARNING : On real hardware the controller registers are shared between drives. 
-    // So we must respond even if the select device is not present. Some OS uses this fact 
-    // to detect the disks.... minix2 for example
-    value8 = (!ANY_IS_PRESENT(channel)) ? 0 : (SELECTED_CONTROLLER(channel).cylinder_no & 0x00ff);
-    goto return_value8;
-  case 0x05: // cylinder high 0x1f5
-    // -- WARNING : On real hardware the controller registers are shared between drives. 
-    // So we must respond even if the select device is not present. Some OS uses this fact 
-    // to detect the disks.... minix2 for example
-    value8 = (!ANY_IS_PRESENT(channel)) ? 0 : SELECTED_CONTROLLER(channel).cylinder_no >> 8;
-    goto return_value8;
-    
-  case 0x06: // hard disk drive and head register 0x1f6
-    // b7 Extended data field for ECC
-    // b6/b5: Used to be sector size.  00=256,01=512,10=1024,11=128
-    //   Since 512 was always used, bit 6 was taken to mean LBA mode:
-    //     b6 1=LBA mode, 0=CHS mode
-    //     b5 1
-    // b4: DRV
-    // b3..0 HD3..HD0
-    value8 = (1 << 7) |
-      ((SELECTED_CONTROLLER(channel).lba_mode>0) << 6) |
-      (1 << 5) | // 01b = 512 sector size
-      (channels[channel].drive_select << 4) |
-      (SELECTED_CONTROLLER(channel).head_no << 0);
-    goto return_value8;
+  default:
+    Ramdisk_Print("\t\tread need support more command: %02x\n", controller->current_command);
     break;
-    //CONTROLLER(channel,0).lba_mode
+  }
+
+  return -1;
+}
+
+
+
+
+static int write_data_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
+  struct ramdisk_t * ramdisk  = (struct ramdisk_t *)(dev->private_data);
+  struct channel_t * channel = NULL;
+  struct drive_t * drive = NULL;
+  struct controller_t * controller = NULL;
+
+  if (is_primary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[0]);
+  } else if (is_secondary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[1]);
+  } else {
+    PrintError("Invalid Port: %d\n", port);
+    return -1;
+  }
+  
+  drive = get_selected_drive(channel);
+  controller = &(drive->controller);
+
+  Ramdisk_Print("\t\twrite port 170\n");
+  
+  switch (controller->current_command) {
+  case 0x30: // WRITE SECTORS
+    RD_PANIC("\t\tneed to implement 0x30(write sector) to port 0x170\n");
+    return -1;
     
-  case 0x07: // Hard Disk Status 0x1f7
-  case 0x16: // Hard Disk Alternate Status 0x3f6
-    if (!ANY_IS_PRESENT(channel)) {
-      // (mch) Just return zero for these registers
-      value8 = 0;
-    } else {
-      value8 = (
-               (SELECTED_CONTROLLER(channel).status.busy << 7) |
-               (SELECTED_CONTROLLER(channel).status.drive_ready << 6) |
-               (SELECTED_CONTROLLER(channel).status.write_fault << 5) |
-               (SELECTED_CONTROLLER(channel).status.seek_complete << 4) |
-               (SELECTED_CONTROLLER(channel).status.drq << 3) |
-               (SELECTED_CONTROLLER(channel).status.corrected_data << 2) |
-               (SELECTED_CONTROLLER(channel).status.index_pulse << 1) |
-               (SELECTED_CONTROLLER(channel).status.err) );
-      SELECTED_CONTROLLER(channel).status.index_pulse_count++;
-      SELECTED_CONTROLLER(channel).status.index_pulse = 0;
-      if (SELECTED_CONTROLLER(channel).status.index_pulse_count >= INDEX_PULSE_CYCLE) {
-        SELECTED_CONTROLLER(channel).status.index_pulse = 1;
-        SELECTED_CONTROLLER(channel).status.index_pulse_count = 0;
-      }
-    }
-    if (port == 0x07) {
-      rd_lower_irq((struct vm_device *)(ramdisk_state->private_data), channels[channel].irq);
-    }
-    goto return_value8;
-    break;
+  case 0xa0: // PACKET
     
-  case 0x17: // Hard Disk Address Register 0x3f7
-    // Obsolete and unsupported register.  Not driven by hard
-    // disk controller.  Report all 1's.  If floppy controller
-    // is handling this address, it will call this function
-    // set/clear D7 (the only bit it handles), then return
-    // the combined value
-    value8 = 0xff;
-    goto return_value8;
-    break;
+    handle_atapi_packet_command(dev, channel, *(ushort_t *)src);
+
+    return length;
     
   default:
-    RD_PANIC("hard drive: io read to address %x unsupported\n",
-             (unsigned) address);
-    
+    RD_PANIC("\t\tIO write(0x%x): current command is %02xh\n", 
+            port, controller->current_command);
+
+    return -1;
+  }
+}
+
+
+
+
+
+
+
+static int read_status_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+  struct ramdisk_t * ramdisk  = (struct ramdisk_t *)(dev->private_data);
+  struct channel_t * channel = NULL;
+  struct drive_t * drive = NULL;
+  struct controller_t * controller = NULL;
+
+
+  if (length != 1) {
+    PrintError("Invalid Status port read length: %d (port=%d)\n", length, port);
+    return -1;
+  }
+
+  if (is_primary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[0]);
+  } else if (is_secondary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[1]);
+  } else {
+    PrintError("Invalid Port: %d\n", port);
+    return -1;
+  }
+  
+  drive = get_selected_drive(channel);
+  controller = &(drive->controller);
+
+
+  if (num_drives_on_channel(channel) == 0) {
+    // (mch) Just return zero for these registers
+    memset(dst, 0, length);
+
+  } else {
+    uchar_t val = (
+                  (controller->status.busy << 7)            |
+                  (controller->status.drive_ready << 6)     |
+                  (controller->status.write_fault << 5)     |
+                  (controller->status.seek_complete << 4)   |
+                  (controller->status.drq << 3)             |
+                  (controller->status.corrected_data << 2)  |
+                  (controller->status.index_pulse << 1)     |
+                  (controller->status.err) );
+   
+    memcpy(dst, &val, length);
+
+    controller->status.index_pulse_count++;
+    controller->status.index_pulse = 0;
     
-    ////////////////////////////////////////////
+    if (controller->status.index_pulse_count >= INDEX_PULSE_CYCLE) {
+      controller->status.index_pulse = 1;
+      controller->status.index_pulse_count = 0;
+    }
+  }
+  
+  if ((port == SEC_CMD_PORT) || (port == PRI_CMD_PORT)) {
+    rd_lower_irq(dev, channel->irq);
   }
   
-  Ramdisk_Print("\t\tError: hard drive: shouldnt get here!\n");
+  return length;
+  
+}
+
+
+static int write_cmd_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
+  struct ramdisk_t * ramdisk  = (struct ramdisk_t *)(dev->private_data);
+  struct channel_t * channel = NULL;
+  struct drive_t * drive = NULL;
+  struct controller_t * controller = NULL;
+  uchar_t value = *(uchar_t *)src;
+
+  if (length != 1) {
+    PrintError("Invalid Status port read length: %d (port=%d)\n", length, port);
+    return -1;
+  }
+
+  if (is_primary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[0]);
+  } else if (is_secondary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[1]);
+  } else {
+    PrintError("Invalid Port: %d\n", port);
+    return -1;
+  }
+  
+  drive = get_selected_drive(channel);
+  controller = &(drive->controller);
+
+
+  switch (value) {
+    // ATAPI commands
+  case 0xa1: // IDENTIFY PACKET DEVICE
+    {
+      if (drive->device_type == IDE_CDROM) {
+       controller->current_command = value;
+       controller->error_register = 0;
+       
+       controller->status.busy = 0;
+       controller->status.drive_ready = 1;
+       controller->status.write_fault = 0;
+       controller->status.drq   = 1;
+       controller->status.err   = 0;
+       
+       controller->status.seek_complete = 1;
+       controller->status.corrected_data = 0;
+       
+       controller->buffer_index = 0;
+       rd_raise_interrupt(dev, channel);
+       rd_identify_ATAPI_drive(dev, channel);
+      } else {
+       rd_command_aborted(dev, channel, 0xa1);
+      }
+      break;
+    }
+  case 0xa0: // SEND PACKET (atapi)
+    {
+      if (drive->device_type == IDE_CDROM) {
+       // PACKET
+       
+       if (controller->features & (1 << 0)) {
+         PrintError("\t\tPACKET-DMA not supported");
+         return -1;
+       }
+       
+       if (controller->features & (1 << 1)) {
+         PrintError("\t\tPACKET-overlapped not supported");
+         return -1;
+       }
+       
+       // We're already ready!
+       controller->sector_count = 1;
+       controller->status.busy = 0;
+       controller->status.write_fault = 0;
+
+
+typedef struct  {
+  unsigned cylinders;
+  unsigned heads;
+  unsigned sectors;
+} device_image_t;      // serv bit??
+       controller->status.drq = 1;
+       controller->status.err = 0;
+       
+       // NOTE: no interrupt here
+       controller->current_command = value;
+       controller->buffer_index = 0;
+      } else {
+       rd_command_aborted (dev, channel, 0xa0);
+      }
+      break;
+    }
+  default:
+    PrintError("\t\tneed translate command %2x\n", value);
+    return -1;
+
+  }
   return 0;
+}
+
+
+static int write_ctrl_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
+  struct ramdisk_t * ramdisk  = (struct ramdisk_t *)(dev->private_data);
+  struct channel_t * channel = NULL;
+  struct drive_t * master_drive = NULL;
+  struct drive_t * slave_drive = NULL;
+  struct controller_t * controller = NULL;
+  uchar_t value = *(uchar_t *)src;
+  rd_bool prev_control_reset;
+
+  if (length != 1) {
+    PrintError("Invalid Status port read length: %d (port=%d)\n", length, port);
+    return -1;
+  }
+
+  if (is_primary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[0]);
+  } else if (is_secondary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[1]);
+  } else {
+    PrintError("Invalid Port: %d\n", port);
+    return -1;
+  }
+
+  master_drive = &(channel->drives[0]);
+  slave_drive = &(channel->drives[1]);
+
+  controller = &(get_selected_drive(channel)->controller);
+
+
+  // (mch) Even if device 1 was selected, a write to this register
+  // goes to device 0 (if device 1 is absent)
   
-return_value32:
-  Ramdisk_Print("\t\t32-bit read from %04x = %08x {%s}\n",
-           (unsigned) address, value32, SELECTED_TYPE_STRING(channel));
-  return value32;
+  prev_control_reset = controller->control.reset;
+
+  master_drive->controller.control.reset         = value & 0x04;
+  slave_drive->controller.control.reset         = value & 0x04;
+
+  // CGS: was: SELECTED_CONTROLLER(channel).control.disable_irq    = value & 0x02;
+  master_drive->controller.control.disable_irq = value & 0x02;
+  slave_drive->controller.control.disable_irq = value & 0x02;
   
- return_value16:
-  Ramdisk_Print("\t\t16-bit read from %04x = %04x {%s}\n",
-           (unsigned) address, value16, SELECTED_TYPE_STRING(channel));
-  return value16;
+  Ramdisk_Print("\t\tadpater control reg: reset controller = %d\n",
+               (unsigned) (controller->control.reset) ? 1 : 0);
+  Ramdisk_Print("\t\tadpater control reg: disable_irq(X) = %d\n",
+               (unsigned) (controller->control.disable_irq) ? 1 : 0);
   
- return_value8:
-  Ramdisk_Print("\t\t8-bit read from %x = %02x {%s}\n",
-           (unsigned) address, value8, SELECTED_TYPE_STRING(channel));
-  return value8;
+  if ((!prev_control_reset) && (controller->control.reset)) {
+    uint_t id = 0;
+
+    // transition from 0 to 1 causes all drives to reset
+    Ramdisk_Print("\t\thard drive: RESET\n");
+    
+    // (mch) Set BSY, drive not ready
+    for (id = 0; id < 2; id++) {
+      struct controller_t * ctrl = NULL;
+
+      if (id == 0) {
+       ctrl = &(master_drive->controller);
+      } else if (id == 1) {
+       ctrl = &(slave_drive->controller);
+      }
+
+      ctrl->status.busy           = 1;
+      ctrl->status.drive_ready    = 0;
+      ctrl->reset_in_progress     = 1;
+      
+      ctrl->status.write_fault    = 0;
+      ctrl->status.seek_complete  = 1;
+      ctrl->status.drq            = 0;
+      ctrl->status.corrected_data = 0;
+      ctrl->status.err            = 0;
+      
+      ctrl->error_register = 0x01; // diagnostic code: no error
+      
+      ctrl->current_command = 0x00;
+      ctrl->buffer_index = 0;
+      
+      ctrl->sectors_per_block = 0x80;
+      ctrl->lba_mode          = 0;
+      
+      ctrl->control.disable_irq = 0;
+    }
+
+    rd_lower_irq(dev, channel->irq);
+
+  } else if ((controller->reset_in_progress) &&
+            (!controller->control.reset)) {
+    uint_t id;
+    // Clear BSY and DRDY
+    PrintDebug("\t\tReset complete {%s}\n", device_type_to_str(get_selected_drive(channel)->device_type));
+
+    for (id = 0; id < 2; id++) {
+      struct controller_t * ctrl = NULL;
+      struct drive_t * drv = NULL;
+
+      if (id == 0) {
+       ctrl = &(master_drive->controller);
+       drv = master_drive;
+      } else if (id == 1) {
+       ctrl = &(slave_drive->controller);
+       drv = slave_drive;
+      }
+
+      ctrl->status.busy           = 0;
+      ctrl->status.drive_ready    = 1;
+      ctrl->reset_in_progress     = 0;
+      
+      // Device signature
+      if (drv->device_type == IDE_DISK) {
+       PrintDebug("\t\tdrive %d/%d is harddrive\n", channel, id);
+       ctrl->head_no        = 0;
+       ctrl->sector_count   = 1;
+       ctrl->sector_no      = 1;
+       ctrl->cylinder_no    = 0;
+      } else {
+       ctrl->head_no        = 0;
+       ctrl->sector_count   = 1;
+       ctrl->sector_no      = 1;
+       ctrl->cylinder_no    = 0xeb14;
+      }
+    }
+  }
+
+  PrintDebug("\t\ts[0].controller.control.disable_irq = %02x\n", 
+            master_drive->controller.control.disable_irq);
+  PrintDebug("\t\ts[1].controller.control.disable_irq = %02x\n", 
+            slave_drive->controller.control.disable_irq);
+  return length;
 }
 
 
-static
-void rd_write_handler(struct channel_t *channels, Bit32u address, 
-                     Bit32u value, unsigned io_len)
-{
+static int read_general_port(ushort_t port, void * dst, uint_t length, struct vm_device * dev) {
+  struct ramdisk_t * ramdisk  = (struct ramdisk_t *)(dev->private_data);
+  struct channel_t * channel = NULL;
+  struct drive_t * drive = NULL;
+  struct controller_t * controller = NULL;
 
-  //  off_t logical_sector;
-  //  off_t ret;
-  rd_bool prev_control_reset;
 
-  Bit32u id;
-  int toc_length;
+  if (length != 1) {
+    PrintError("Invalid Status port read length: %d (port=%d)\n", length, port);
+    return -1;
+  }
 
-  Bit8u  channel = MAX_ATA_CHANNEL;
-  Bit32u port = 0xff; // undefined
+  if (is_primary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[0]);
+  } else if (is_secondary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[1]);
+  } else {
+    PrintError("Invalid Port: %d\n", port);
+    return -1;
+  }
+  
+  drive = get_selected_drive(channel);
+  controller = &(drive->controller);
 
-  //Ramdisk_Print("[rd_write_handler]\n");
-  //  Bit8u atapi_command;
-  //int alloc_length;
 
-  for (channel=0; channel<MAX_ATA_CHANNEL; channel++) {
-    if ((address & 0xfff8) == channels[channel].ioaddr1) {
-      port = address - channels[channel].ioaddr1;
+  Ramdisk_Print("[R_handler] IO read addr at %x, on drive %d/%d, curcmd = %02x\n", 
+               address, channel, 
+               drive_select, 
+               SELECTED_CONTROLLER(channel).current_command);
+  
+
+  switch (port) {
+  case PRI_FEATURES_PORT:
+  case SEC_FEATURES_PORT: // hard disk error register 0x1f1
+    {    
+      uchar_t val = (drive->device_type == IDE_NONE) ? 0 : controller->error_register;
+      
+      controller->status.err = 0;
+      
+      *(uchar_t *)dst = val;
+      return length;
+      
       break;
     }
-    else if ((address & 0xfff8) == channels[channel].ioaddr2) {
-      port = address - channels[channel].ioaddr2 + 0x10;
+
+  case PRI_SECT_CNT_PORT:
+  case SEC_SECT_CNT_PORT:  // hard disk sector count / interrupt reason 0x1f2
+    {
+      uchar_t val = (drive->device_type == IDE_NONE) ? 0 : controller->sector_count;
+
+      *(uchar_t *)dst = val;
+      return length;
+
       break;
-      }
     }
+  case PRI_SECT_ADDR1_PORT:
+  case SEC_SECT_ADDR1_PORT: // sector number 0x1f3
+    {
+      uchar_t val = (drive->device_type == IDE_NONE) ? 0 : controller->sector_no;
+
+      *(uchar_t *)dst = val;
+      return length;
 
-  if (channel == MAX_ATA_CHANNEL) {
-    if (address != 0x03f6) {
-      RD_PANIC("Panic: write: unable to find ATA channel, ioport=0x%04x\n", address);
-    } else {
-      channel = 0;
-      port = address - 0x03e0;
+      break;
     }
+
+  case PRI_SECT_ADDR2_PORT:
+  case SEC_SECT_ADDR2_PORT:  // cylinder low 0x1f4  
+    {
+      // -- WARNING : On real hardware the controller registers are shared between drives. 
+      // So we must respond even if the select device is not present. Some OS uses this fact 
+      // to detect the disks.... minix2 for example
+      uchar_t val = (num_drives_on_channel(channel) == 0) ? 0 : (controller->cylinder_no & 0x00ff);
+
+      *(uchar_t *)dst = val;
+      return length;
+
+      break;      
   }
 
-  Ramdisk_Print("[W_handler] IO write to %x = %02x, channel = %d\n", (unsigned) address, (unsigned) value, channel);
+  case PRI_SECT_ADDR3_PORT:
+  case SEC_SECT_ADDR3_PORT: // cylinder high 0x1f5
+    {
+      // -- WARNING : On real hardware the controller registers are shared between drives. 
+      // So we must respond even if the select device is not present. Some OS uses this fact 
+      // to detect the disks.... minix2 for example
+      uchar_t val = (num_drives_on_channel(channel) == 0) ? 0 : (controller->cylinder_no >> 8);
+
+      *(uchar_t *)dst = val;
+      return length;
+
+      break;    
+    }
+  case PRI_DRV_SEL_PORT:
+  case SEC_DRV_SEL_PORT:  // hard disk drive and head register 0x1f6
+    {
+      // b7 Extended data field for ECC
+      // b6/b5: Used to be sector size.  00=256,01=512,10=1024,11=128
+      //   Since 512 was always used, bit 6 was taken to mean LBA mode:
+      //     b6 1=LBA mode, 0=CHS mode
+      //     b5 1
+      // b4: DRV
+      // b3..0 HD3..HD0
+      uchar_t val = ((1 << 7)                          |
+                    ((controller->lba_mode > 0) << 6) |
+                    (1 << 5)                          |            // 01b = 512 sector size
+                    (channel->drive_select << 4)      |
+                    (controller->head_no << 0));
+      
+      *(uchar_t *)dst = val;
+      return length;
+
+      break;
+    }
+ case PRI_ADDR_REG_PORT:
+ case SEC_ADDR_REG_PORT: // Hard Disk Address Register 0x3f7
+   {
+     // Obsolete and unsupported register.  Not driven by hard
+     // disk controller.  Report all 1's.  If floppy controller
+     // is handling this address, it will call this function
+     // set/clear D7 (the only bit it handles), then return
+     // the combined value
+     *(uchar_t *)dst = 0xff;
+     return length;
+    }
+
+  default:
+    PrintError("Invalid Port: %d\n", port);
+    return -1;
+  }
+}
 
-  struct cdrom_interface *cdif = SELECTED_DRIVE(channel).cdrom.cd;
 
-  switch (port) {
 
-  case 0x00: // 0x1f0
-    Ramdisk_Print("\t\twrite port 170\n");
 
-    //////////////////////////////////////////////////////////
-    switch (SELECTED_CONTROLLER(channel).current_command) {
-    case 0x30: // WRITE SECTORS
-      RD_PANIC("\t\tneed to implement 0x30(write sector) to port 0x170\n");
+static int write_general_port(ushort_t port, void * src, uint_t length, struct vm_device * dev) {
+  struct ramdisk_t * ramdisk  = (struct ramdisk_t *)(dev->private_data);
+  struct channel_t * channel = NULL;
+  struct drive_t * drive = NULL;
+  struct controller_t * controller = NULL;
+  uchar_t value = *(uchar_t *)src;
+
+  if (length != 1) {
+    PrintError("Invalid Status port read length: %d (port=%d)\n", length, port);
+    return -1;
+  }
+
+  if (is_primary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[0]);
+  } else if (is_secondary_port(ramdisk, port)) {
+    channel = &(ramdisk->channels[1]);
+  } else {
+    PrintError("Invalid Port: %d\n", port);
+    return -1;
+  }
+  
+  drive = get_selected_drive(channel);
+  controller = &(drive->controller);
+
+
+  Ramdisk_Print("[W_handler] IO write to %x = %02x, channel = %d\n", 
+               port, (unsigned) value, channel);
+
+  switch (port) {
+
+  case PRI_FEATURES_PORT:
+  case SEC_FEATURES_PORT: // hard disk write precompensation 0x1f1
+    {
+      write_features(channel, value);
+      break;
+    }
+  case PRI_SECT_CNT_PORT:
+  case SEC_SECT_CNT_PORT: // hard disk sector count 0x1f2
+    {
+      write_sector_count(channel, value);
+      break;
+    }
+  case PRI_SECT_ADDR1_PORT:
+  case SEC_SECT_ADDR1_PORT: // hard disk sector number 0x1f3
+    {
+      write_sector_number(channel, value);
+      break;
+    }
+  case PRI_SECT_ADDR2_PORT:
+  case SEC_SECT_ADDR2_PORT: // hard disk cylinder low 0x1f4
+    {
+      write_cylinder_low(channel, value);
+      break;
+    }
+  case PRI_SECT_ADDR3_PORT:
+  case SEC_SECT_ADDR3_PORT: // hard disk cylinder high 0x1f5
+    {
+      write_cylinder_high(channel, value);
       break;
+    }
+  case PRI_DRV_SEL_PORT:
+  case SEC_DRV_SEL_PORT: // hard disk drive and head register 0x1f6
+    {
+      // b7 Extended data field for ECC
+      // b6/b5: Used to be sector size.  00=256,01=512,10=1024,11=128
+      //   Since 512 was always used, bit 6 was taken to mean LBA mode:
+      //     b6 1=LBA mode, 0=CHS mode
+      //     b5 1
+      // b4: DRV
+      // b3..0 HD3..HD0
+
+      // 1x1xxxxx      
+      if ((value & 0xa0) != 0xa0) { 
+       Ramdisk_Print("\t\tIO write 0x%x (%02x): not 1x1xxxxxb\n", address, (unsigned) value);
+      }
       
-    case 0xa0: // PACKET
-      if (SELECTED_CONTROLLER(channel).buffer_index >= PACKET_SIZE)
-       RD_PANIC("IO write(0x%04x): buffer_index >= PACKET_SIZE", address);
-      SELECTED_CONTROLLER(channel).buffer[SELECTED_CONTROLLER(channel).buffer_index] = value;
-      SELECTED_CONTROLLER(channel).buffer[SELECTED_CONTROLLER(channel).buffer_index+1] = (value >> 8);
-      SELECTED_CONTROLLER(channel).buffer_index += 2;
+      write_head_no(channel, value & 0xf);
+      if (controller->lba_mode == 0 && ((value >> 6) & 1) == 1){
+       PrintDebug("\t\tenabling LBA mode\n");
+      }
+
+      write_lba_mode(channel,(value >> 6) & 1);
+      drive->cdrom.cd->lba = (value >> 6) & 1;
       
-      /* if packet completely writtten */
-      if (SELECTED_CONTROLLER(channel).buffer_index >= PACKET_SIZE) {
-       // complete command received
-       Bit8u atapi_command = SELECTED_CONTROLLER(channel).buffer[0];
-       
-       Ramdisk_Print("\t\tcdrom: ATAPI command 0x%x started\n", atapi_command);
+      
+      if (drive->device_type == IDE_NONE) {
+       channel->drive_select = (value >> 4) & 0x01;
+#ifdef DEBUG_RAMDISK
+       PrintDebug("\t\tError: device set to %d which does not exist! channel = 0x%x\n",
+                  channel->drive_select, channel);
+#endif
+       controller->error_register = 0x04; // aborted
+       controller->status.err = 1;
+      }
+      
+      break;
+    }
+  default:
+    PrintError("\t\thard drive: io write to address %x  (value = %c)\n", port, value);
+    return -1;
+  }
 
-       switch (atapi_command) {
-       case 0x00: // test unit ready
-         if (SELECTED_DRIVE(channel).cdrom.ready) {
-           rd_atapi_cmd_nop(channels, channel);
-         } else {
-           rd_atapi_cmd_error(channels, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-         }
-         rd_raise_interrupt(channels, channel);
-         break;
-         
-       case 0x03: { // request sense
-         int alloc_length = SELECTED_CONTROLLER(channel).buffer[4];
-         rd_init_send_atapi_command(channels, channel, atapi_command, 18, alloc_length, false);
-                                   
-         // sense data
-         SELECTED_CONTROLLER(channel).buffer[0] = 0x70 | (1 << 7);
-         SELECTED_CONTROLLER(channel).buffer[1] = 0;
-         SELECTED_CONTROLLER(channel).buffer[2] = SELECTED_DRIVE(channel).sense.sense_key;
-         SELECTED_CONTROLLER(channel).buffer[3] = SELECTED_DRIVE(channel).sense.information.arr[0];
-         SELECTED_CONTROLLER(channel).buffer[4] = SELECTED_DRIVE(channel).sense.information.arr[1];
-         SELECTED_CONTROLLER(channel).buffer[5] = SELECTED_DRIVE(channel).sense.information.arr[2];
-         SELECTED_CONTROLLER(channel).buffer[6] = SELECTED_DRIVE(channel).sense.information.arr[3];
-         SELECTED_CONTROLLER(channel).buffer[7] = 17-7;
-         SELECTED_CONTROLLER(channel).buffer[8] = SELECTED_DRIVE(channel).sense.specific_inf.arr[0];
-         SELECTED_CONTROLLER(channel).buffer[9] = SELECTED_DRIVE(channel).sense.specific_inf.arr[1];
-         SELECTED_CONTROLLER(channel).buffer[10] = SELECTED_DRIVE(channel).sense.specific_inf.arr[2];
-         SELECTED_CONTROLLER(channel).buffer[11] = SELECTED_DRIVE(channel).sense.specific_inf.arr[3];
-         SELECTED_CONTROLLER(channel).buffer[12] = SELECTED_DRIVE(channel).sense.asc;
-         SELECTED_CONTROLLER(channel).buffer[13] = SELECTED_DRIVE(channel).sense.ascq;
-         SELECTED_CONTROLLER(channel).buffer[14] = SELECTED_DRIVE(channel).sense.fruc;
-         SELECTED_CONTROLLER(channel).buffer[15] = SELECTED_DRIVE(channel).sense.key_spec.arr[0];
-         SELECTED_CONTROLLER(channel).buffer[16] = SELECTED_DRIVE(channel).sense.key_spec.arr[1];
-         SELECTED_CONTROLLER(channel).buffer[17] = SELECTED_DRIVE(channel).sense.key_spec.arr[2];
-         
-         rd_ready_to_send_atapi(channels, channel);
-       }
-         break;
-         
-       case 0x1b: { // start stop unit
-         //bx_bool Immed = (SELECTED_CONTROLLER(channel).buffer[1] >> 0) & 1;
-         rd_bool LoEj = (SELECTED_CONTROLLER(channel).buffer[4] >> 1) & 1;
-         rd_bool Start = (SELECTED_CONTROLLER(channel).buffer[4] >> 0) & 1;
-         
-         if (!LoEj && !Start) { // stop the disc
-           RD_ERROR("FIXME: Stop disc not implemented\n");
-           rd_atapi_cmd_nop(channels, channel);
-           rd_raise_interrupt(channels, channel);
-         } else if (!LoEj && Start) { // start (spin up) the disc
-
-           SELECTED_DRIVE(channel).cdrom.cd->ops.start_cdrom(cdif);
-
-           RD_ERROR("FIXME: ATAPI start disc not reading TOC\n");
-           rd_atapi_cmd_nop(channels, channel);
-           rd_raise_interrupt(channels, channel);
-         } else if (LoEj && !Start) { // Eject the disc
-           rd_atapi_cmd_nop(channels, channel);
-           
-           if (SELECTED_DRIVE(channel).cdrom.ready) {
+  return length;
+}
 
-             SELECTED_DRIVE(channel).cdrom.cd->ops.eject_cdrom(cdif);
 
-             SELECTED_DRIVE(channel).cdrom.ready = 0;
-             //bx_options.atadevice[channel][SLAVE_SELECTED(channel)].Ostatus->set(EJECTED);
-             //bx_gui->update_drive_status_buttons();
-           }
-           rd_raise_interrupt(channels, channel);
-         } else { // Load the disc
-           // My guess is that this command only closes the tray, that's a no-op for us
-           rd_atapi_cmd_nop(channels, channel);
-           rd_raise_interrupt(channels, channel);
-         }
+ 
+
+
+static void rd_raise_interrupt(struct vm_device * dev, struct channel_t * channel) {
+  Bit32u irq;
+  //  struct ramdisk_t * ramdisk = (struct ramdisk_t *)(dev->private_data);
+  struct drive_t * drive = get_selected_drive(channel);
+  struct controller_t * controller = &(drive->controller);
+
+  Ramdisk_Print("[raise_interrupt] disable_irq = %02x\n", controller->control.disable_irq);
+
+  if (!(controller->control.disable_irq)) {
+    irq = channel->irq; 
+ 
+    Ramdisk_Print("\t\tRaising interrupt %d {%s}\n\n", irq, SELECTED_TYPE_STRING(channel));
+
+    dev->vm->vm_ops.raise_irq(dev->vm, irq);
+  } else {
+    Ramdisk_Print("\t\tirq is disabled\n");
+  }
+  
+  return;
+}
+
+static void rd_lower_irq(struct vm_device *dev, Bit32u irq)  // __attribute__(regparm(1))
+{
+  Ramdisk_Print("[lower_irq] irq = %d\n", irq);
+  dev->vm->vm_ops.lower_irq(dev->vm, irq);
+}
+
+
+
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+
+/*
+ * ATAPI subroutines
+ */
+
+
+
+
+
+
+
+
+int handle_atapi_packet_command(struct vm_device * dev, struct channel_t * channel, ushort_t value) {
+  //struct ramdisk_t * ramdisk  = (struct ramdisk_t *)(dev->private_data);
+  struct drive_t * drive = get_selected_drive(channel);
+  struct controller_t * controller = &(drive->controller);
+
+  if (controller->buffer_index >= PACKET_SIZE) {
+    PrintError("ATAPI packet exceeded maximum length: buffer_index >= PACKET_SIZE\n");
+    return -1;
+  }
+
+  controller->buffer[controller->buffer_index] = value;
+  controller->buffer[controller->buffer_index + 1] = (value >> 8);
+  controller->buffer_index += 2;
+  
+  
+  /* if packet completely writtten */
+  if (controller->buffer_index >= PACKET_SIZE) {
+    // complete command received
+    Bit8u atapi_command = controller->buffer[0];
+    
+    PrintDebug("\t\tcdrom: ATAPI command 0x%x started\n", atapi_command);
+    
+    switch (atapi_command) {
+    case 0x00: // test unit ready
+      {
+       if (drive->cdrom.ready) {
+         rd_atapi_cmd_nop(dev, channel);
+       } else {
+         rd_atapi_cmd_error(dev, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
        }
-         break;
-         
-       case 0xbd: { // mechanism status
-         uint16 alloc_length = rd_read_16bit(SELECTED_CONTROLLER(channel).buffer + 8);
-         
-         if (alloc_length == 0)
-           RD_PANIC("Zero allocation length to MECHANISM STATUS not impl.\n");
-         
-         rd_init_send_atapi_command(channels, channel, atapi_command, 8, alloc_length, false);
-         
-         SELECTED_CONTROLLER(channel).buffer[0] = 0; // reserved for non changers
-         SELECTED_CONTROLLER(channel).buffer[1] = 0; // reserved for non changers
-         
-         SELECTED_CONTROLLER(channel).buffer[2] = 0; // Current LBA (TODO!)
-         SELECTED_CONTROLLER(channel).buffer[3] = 0; // Current LBA (TODO!)
-         SELECTED_CONTROLLER(channel).buffer[4] = 0; // Current LBA (TODO!)
+       
+       rd_raise_interrupt(dev, channel);
+       
+       break;
+      }
+    case 0x03:  // request sense
+      {
+       int alloc_length = controller->buffer[4];
+       rd_init_send_atapi_command(dev, channel, atapi_command, 18, alloc_length, false);
+       
+       // sense data
+       controller->buffer[0] = 0x70 | (1 << 7);
+       controller->buffer[1] = 0;
+       controller->buffer[2] = drive->sense.sense_key;
+       controller->buffer[3] = drive->sense.information.arr[0];
+       controller->buffer[4] = drive->sense.information.arr[1];
+       controller->buffer[5] = drive->sense.information.arr[2];
+       controller->buffer[6] = drive->sense.information.arr[3];
+       controller->buffer[7] = 17 - 7;
+       controller->buffer[8] = drive->sense.specific_inf.arr[0];
+       controller->buffer[9] = drive->sense.specific_inf.arr[1];
+       controller->buffer[10] = drive->sense.specific_inf.arr[2];
+       controller->buffer[11] = drive->sense.specific_inf.arr[3];
+       controller->buffer[12] = drive->sense.asc;
+       controller->buffer[13] = drive->sense.ascq;
+       controller->buffer[14] = drive->sense.fruc;
+       controller->buffer[15] = drive->sense.key_spec.arr[0];
+       controller->buffer[16] = drive->sense.key_spec.arr[1];
+       controller->buffer[17] = drive->sense.key_spec.arr[2];
+       
+       rd_ready_to_send_atapi(dev, channel);
+       break;
+      }
+    case 0x1b:  // start stop unit
+      {
+       //bx_bool Immed = (controller->buffer[1] >> 0) & 1;
+       rd_bool LoEj = (controller->buffer[4] >> 1) & 1;
+       rd_bool Start = (controller->buffer[4] >> 0) & 1;
+
+       // stop the disc
+       if ((!LoEj) && (!Start)) { 
+         PrintError("FIXME: Stop disc not implemented\n");
+
+         rd_atapi_cmd_nop(dev, channel);
+         rd_raise_interrupt(dev, channel);
+       } else if (!LoEj && Start) { // start (spin up) the disc
          
-         SELECTED_CONTROLLER(channel).buffer[5] = 1; // one slot
+         drive->cdrom.cd->ops.start_cdrom(drive->cdrom.cd);
          
-         SELECTED_CONTROLLER(channel).buffer[6] = 0; // slot table length
-         SELECTED_CONTROLLER(channel).buffer[7] = 0; // slot table length
+         PrintError("FIXME: ATAPI start disc not reading TOC\n");
+         rd_atapi_cmd_nop(dev, channel);
+         rd_raise_interrupt(dev, channel);
+       } else if (LoEj && !Start) { // Eject the disc
+         rd_atapi_cmd_nop(dev, channel);
          
-         rd_ready_to_send_atapi(channels, channel);
+         if (drive->cdrom.ready) {
+           
+           drive->cdrom.cd->ops.eject_cdrom(drive->cdrom.cd);
+           
+           drive->cdrom.ready = 0;
+           //bx_options.atadevice[channel][SLAVE_SELECTED(channel)].Ostatus->set(EJECTED);
+           //bx_gui->update_drive_status_buttons();
+         }
+         rd_raise_interrupt(dev, channel);
+       } else { // Load the disc
+         // My guess is that this command only closes the tray, that's a no-op for us
+         rd_atapi_cmd_nop(dev, channel);
+         rd_raise_interrupt(dev, channel);
        }
-         break;
-         
-       case 0x5a: { // mode sense
-         uint16 alloc_length = rd_read_16bit(SELECTED_CONTROLLER(channel).buffer + 7);
-         
-         Bit8u PC = SELECTED_CONTROLLER(channel).buffer[2] >> 6;
-         Bit8u PageCode = SELECTED_CONTROLLER(channel).buffer[2] & 0x3f;
-         
-         switch (PC) {
-         case 0x0: // current values
+       break;
+      }
+    case 0xbd: // mechanism status
+      {
+       uint16_t alloc_length = rd_read_16bit(controller->buffer + 8);
+       
+       if (alloc_length == 0) {
+         RD_PANIC("Zero allocation length to MECHANISM STATUS not impl.\n");
+       }
+       
+       rd_init_send_atapi_command(dev, channel, atapi_command, 8, alloc_length, false);
+       
+       controller->buffer[0] = 0; // reserved for non changers
+       controller->buffer[1] = 0; // reserved for non changers
+       
+       controller->buffer[2] = 0; // Current LBA (TODO!)
+       controller->buffer[3] = 0; // Current LBA (TODO!)
+       controller->buffer[4] = 0; // Current LBA (TODO!)
+       
+       controller->buffer[5] = 1; // one slot
+       
+       controller->buffer[6] = 0; // slot table length
+       controller->buffer[7] = 0; // slot table length
+       
+       rd_ready_to_send_atapi(dev, channel);
+       break;
+      }
+    case 0x5a:  // mode sense
+      {
+       uint16_t alloc_length = rd_read_16bit(controller->buffer + 7);
+       
+       Bit8u PC = controller->buffer[2] >> 6;
+       Bit8u PageCode = controller->buffer[2] & 0x3f;
+       
+       switch (PC) {
+       case 0x0: // current values
+         {
            switch (PageCode) {
            case 0x01: // error recovery
-             rd_init_send_atapi_command(channels, channel, atapi_command, sizeof(struct error_recovery_t) + 8, alloc_length, false);
-             
-             rd_init_mode_sense_single(channels, channel, &SELECTED_DRIVE(channel).cdrom.current.error_recovery,
-                                    sizeof(struct error_recovery_t));
-             rd_ready_to_send_atapi(channels, channel);
-             break;
-             
+             {
+               rd_init_send_atapi_command(dev, channel, atapi_command, sizeof(struct error_recovery_t) + 8, alloc_length, false);
+               
+               rd_init_mode_sense_single(dev, channel, &(drive->cdrom.current.error_recovery),
+                                         sizeof(struct error_recovery_t));
+               rd_ready_to_send_atapi(dev, channel);
+               break;
+             }
            case 0x2a: // CD-ROM capabilities & mech. status
-             rd_init_send_atapi_command(channels, channel, atapi_command, 28, alloc_length, false);
-             rd_init_mode_sense_single(channels, channel, &SELECTED_CONTROLLER(channel).buffer[8], 28);
-             SELECTED_CONTROLLER(channel).buffer[8] = 0x2a;
-             SELECTED_CONTROLLER(channel).buffer[9] = 0x12;
-             SELECTED_CONTROLLER(channel).buffer[10] = 0x00;
-             SELECTED_CONTROLLER(channel).buffer[11] = 0x00;
-             // Multisession, Mode 2 Form 2, Mode 2 Form 1
-             SELECTED_CONTROLLER(channel).buffer[12] = 0x70; 
-             SELECTED_CONTROLLER(channel).buffer[13] = (3 << 5);
-             SELECTED_CONTROLLER(channel).buffer[14] = (unsigned char)
-               (1 |
-                (SELECTED_DRIVE(channel).cdrom.locked ? (1 << 1) : 0) |
-                (1 << 3) |
-                (1 << 5));
-             SELECTED_CONTROLLER(channel).buffer[15] = 0x00;
-             SELECTED_CONTROLLER(channel).buffer[16] = (706 >> 8) & 0xff;
-             SELECTED_CONTROLLER(channel).buffer[17] = 706 & 0xff;
-             SELECTED_CONTROLLER(channel).buffer[18] = 0;
-             SELECTED_CONTROLLER(channel).buffer[19] = 2;
-             SELECTED_CONTROLLER(channel).buffer[20] = (512 >> 8) & 0xff;
-             SELECTED_CONTROLLER(channel).buffer[21] = 512 & 0xff;
-             SELECTED_CONTROLLER(channel).buffer[22] = (706 >> 8) & 0xff;
-             SELECTED_CONTROLLER(channel).buffer[23] = 706 & 0xff;
-             SELECTED_CONTROLLER(channel).buffer[24] = 0;
-             SELECTED_CONTROLLER(channel).buffer[25] = 0;
-             SELECTED_CONTROLLER(channel).buffer[26] = 0;
-             SELECTED_CONTROLLER(channel).buffer[27] = 0;
-             rd_ready_to_send_atapi(channels, channel);
-             break;
-             
+             {
+               rd_init_send_atapi_command(dev, channel, atapi_command, 28, alloc_length, false);
+               rd_init_mode_sense_single(dev, channel, &(controller->buffer[8]), 28);
+               
+               controller->buffer[8] = 0x2a;
+               controller->buffer[9] = 0x12;
+               controller->buffer[10] = 0x00;
+               controller->buffer[11] = 0x00;
+               // Multisession, Mode 2 Form 2, Mode 2 Form 1
+               controller->buffer[12] = 0x70; 
+               controller->buffer[13] = (3 << 5);
+               controller->buffer[14] = (unsigned char) (1 |
+                                                         (drive->cdrom.locked ? (1 << 1) : 0) |
+                                                         (1 << 3) |
+                                                         (1 << 5));
+               controller->buffer[15] = 0x00;
+               controller->buffer[16] = (706 >> 8) & 0xff;
+               controller->buffer[17] = 706 & 0xff;
+               controller->buffer[18] = 0;
+               controller->buffer[19] = 2;
+               controller->buffer[20] = (512 >> 8) & 0xff;
+               controller->buffer[21] = 512 & 0xff;
+               controller->buffer[22] = (706 >> 8) & 0xff;
+               controller->buffer[23] = 706 & 0xff;
+               controller->buffer[24] = 0;
+               controller->buffer[25] = 0;
+               controller->buffer[26] = 0;
+               controller->buffer[27] = 0;
+               rd_ready_to_send_atapi(dev, channel);
+               break;
+             }
            case 0x0d: // CD-ROM
            case 0x0e: // CD-ROM audio control
            case 0x3f: // all
-             RD_ERROR("cdrom: MODE SENSE (curr), code=%x not implemented yet\n",
-                       PageCode);
-           rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST,
-                           ASC_INV_FIELD_IN_CMD_PACKET);
-           rd_raise_interrupt(channels, channel);
-           break;
-             
-         default:
-           // not implemeted by this device
-           Ramdisk_Print("\t\tcdrom: MODE SENSE PC=%x, PageCode=%x, not implemented by device\n",
-                    PC, PageCode);
-           rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST,
-                           ASC_INV_FIELD_IN_CMD_PACKET);
-           rd_raise_interrupt(channels, channel);
+             {
+               RD_ERROR("cdrom: MODE SENSE (curr), code=%x not implemented yet\n",
+                        PageCode);
+               rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST,
+                                  ASC_INV_FIELD_IN_CMD_PACKET);
+               rd_raise_interrupt(dev, channel);
+               break;
+             }
+           default:
+             {
+               // not implemeted by this device
+               Ramdisk_Print("\t\tcdrom: MODE SENSE PC=%x, PageCode=%x, not implemented by device\n",
+                             PC, PageCode);
+               rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST,
+                                  ASC_INV_FIELD_IN_CMD_PACKET);
+               rd_raise_interrupt(dev, channel);
+               break;
+             }
+           }
            break;
          }
-         break;
-           
-         case 0x1: // changeable values
+       case 0x1: // changeable values
+         {
            switch (PageCode) {
            case 0x01: // error recovery
            case 0x0d: // CD-ROM
            case 0x0e: // CD-ROM audio control
            case 0x2a: // CD-ROM capabilities & mech. status
            case 0x3f: // all
-             RD_ERROR("cdrom: MODE SENSE (chg), code=%x not implemented yet\n",
-                      PageCode);
-           rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST,
-                             ASC_INV_FIELD_IN_CMD_PACKET);
-           rd_raise_interrupt(channels, channel);
+             {
+               PrintError("cdrom: MODE SENSE (chg), code=%x not implemented yet\n",
+                          PageCode);
+               rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST,
+                                  ASC_INV_FIELD_IN_CMD_PACKET);
+               rd_raise_interrupt(dev, channel);
+               break;
+             }
+           default:
+             {
+               // not implemeted by this device
+               PrintDebug("\t\tcdrom: MODE SENSE PC=%x, PageCode=%x, not implemented by device\n",
+                          PC, PageCode);
+               rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST,
+                                  ASC_INV_FIELD_IN_CMD_PACKET);
+               rd_raise_interrupt(dev, channel);
+               break;
+             }
+           }
            break;
-           
-         default:
-             // not implemeted by this device
-           Ramdisk_Print("\t\tcdrom: MODE SENSE PC=%x, PageCode=%x, not implemented by device\n",
-                         PC, PageCode);
-         rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST,
-                            ASC_INV_FIELD_IN_CMD_PACKET);
-         rd_raise_interrupt(channels, channel);
-         break;
-       }
-         break;
-         
+         }
        case 0x2: // default values
-         switch (PageCode) {
-         case 0x01: // error recovery
-         case 0x0d: // CD-ROM
-         case 0x0e: // CD-ROM audio control
-         case 0x2a: // CD-ROM capabilities & mech. status
-         case 0x3f: // all
-           RD_PANIC("cdrom: MODE SENSE (dflt), code=%x\n",
-                    PageCode);
-           break;
-           
+         {
+           switch (PageCode) {
+           case 0x01: // error recovery
+           case 0x0d: // CD-ROM
+           case 0x0e: // CD-ROM audio control
+           case 0x2a: // CD-ROM capabilities & mech. status
+           case 0x3f: // all
+             PrintError("cdrom: MODE SENSE (dflt), code=%x\n",
+                      PageCode);
+             return -1;
+             
            default:
-             // not implemeted by this device
-             Ramdisk_Print("\t\tcdrom: MODE SENSE PC=%x, PageCode=%x, not implemented by device\n",
-                     PC, PageCode);
-             rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST,
-                                ASC_INV_FIELD_IN_CMD_PACKET);
-             rd_raise_interrupt(channels, channel);
-             break;
+             {
+               // not implemeted by this device
+               PrintDebug("\t\tcdrom: MODE SENSE PC=%x, PageCode=%x, not implemented by device\n",
+                             PC, PageCode);
+               rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST,
+                                  ASC_INV_FIELD_IN_CMD_PACKET);
+               rd_raise_interrupt(dev, channel);
+               break;
+             }
+           }
+           break;
          }
-         break;
-         
        case 0x3: // saved values not implemented
-         rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST, ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
-         rd_raise_interrupt(channels, channel);
-         break;
-         
+         {
+           rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST, ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
+           rd_raise_interrupt(dev, channel);
+           break;
+         }
        default:
-         RD_PANIC("Should not get here!\n");
-         break;
+         {
+           RD_PANIC("Should not get here!\n");
+           break;
+         }
        }
+       break;
       }
-      break;
-      
-    case 0x12: { // inquiry
-      uint8 alloc_length = SELECTED_CONTROLLER(channel).buffer[4];
-      
-      rd_init_send_atapi_command(channels, channel, atapi_command, 36, alloc_length, false);
-      
-      SELECTED_CONTROLLER(channel).buffer[0] = 0x05; // CD-ROM
-      SELECTED_CONTROLLER(channel).buffer[1] = 0x80; // Removable
-      SELECTED_CONTROLLER(channel).buffer[2] = 0x00; // ISO, ECMA, ANSI version
-      SELECTED_CONTROLLER(channel).buffer[3] = 0x21; // ATAPI-2, as specified
-      SELECTED_CONTROLLER(channel).buffer[4] = 31; // additional length (total 36)
-      SELECTED_CONTROLLER(channel).buffer[5] = 0x00; // reserved
-      SELECTED_CONTROLLER(channel).buffer[6] = 0x00; // reserved
-      SELECTED_CONTROLLER(channel).buffer[7] = 0x00; // reserved
-      
-      // Vendor ID
-      const char* vendor_id = "VTAB    ";
-      int i;
-      for (i = 0; i < 8; i++)
-       SELECTED_CONTROLLER(channel).buffer[8+i] = vendor_id[i];
-      
-      // Product ID
-      const char* product_id = "Turbo CD-ROM    ";
-      for (i = 0; i < 16; i++)
-       SELECTED_CONTROLLER(channel).buffer[16+i] = product_id[i];
-      
-      // Product Revision level
-      const char* rev_level = "1.0 ";
-      for (i = 0; i < 4; i++)
-       SELECTED_CONTROLLER(channel).buffer[32+i] = rev_level[i];
-      
-      rd_ready_to_send_atapi(channels, channel);
-    }
-      break;
-      
-       case 0x25: { // read cd-rom capacity
-         // no allocation length???
-         rd_init_send_atapi_command(channels, channel, atapi_command, 8, 8, false);
-      
-         if (SELECTED_DRIVE(channel).cdrom.ready) {
-           uint32 capacity = SELECTED_DRIVE(channel).cdrom.capacity;
-           Ramdisk_Print("\t\tCapacity is %d sectors (%d bytes)\n", capacity, capacity * 2048);
-           SELECTED_CONTROLLER(channel).buffer[0] = (capacity >> 24) & 0xff;
-           SELECTED_CONTROLLER(channel).buffer[1] = (capacity >> 16) & 0xff;
-           SELECTED_CONTROLLER(channel).buffer[2] = (capacity >> 8) & 0xff;
-           SELECTED_CONTROLLER(channel).buffer[3] = (capacity >> 0) & 0xff;
-           SELECTED_CONTROLLER(channel).buffer[4] = (2048 >> 24) & 0xff;
-           SELECTED_CONTROLLER(channel).buffer[5] = (2048 >> 16) & 0xff;
-           SELECTED_CONTROLLER(channel).buffer[6] = (2048 >> 8) & 0xff;
-           SELECTED_CONTROLLER(channel).buffer[7] = (2048 >> 0) & 0xff;
-           rd_ready_to_send_atapi(channels, channel);
-         } else {
-           rd_atapi_cmd_error(channels, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-           rd_raise_interrupt(channels, channel);
-         }
+    case 0x12: // inquiry
+      { 
+       uint8_t alloc_length = controller->buffer[4];
+       
+       rd_init_send_atapi_command(dev, channel, atapi_command, 36, alloc_length, false);
+       
+       controller->buffer[0] = 0x05; // CD-ROM
+       controller->buffer[1] = 0x80; // Removable
+       controller->buffer[2] = 0x00; // ISO, ECMA, ANSI version
+       controller->buffer[3] = 0x21; // ATAPI-2, as specified
+       controller->buffer[4] = 31; // additional length (total 36)
+       controller->buffer[5] = 0x00; // reserved
+       controller->buffer[6] = 0x00; // reserved
+       controller->buffer[7] = 0x00; // reserved
+       
+       // Vendor ID
+       const char* vendor_id = "VTAB    ";
+       int i;
+       for (i = 0; i < 8; i++) {
+         controller->buffer[8+i] = vendor_id[i];
        }
-         break;
-      
-       case 0xbe: { // read cd
-         if (SELECTED_DRIVE(channel).cdrom.ready) {
-           RD_ERROR("Read CD with CD present not implemented\n");
-           rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
-           rd_raise_interrupt(channels, channel);
-         } else {
-           rd_atapi_cmd_error(channels, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-           rd_raise_interrupt(channels, channel);
-         }
+
+       // Product ID
+       const char* product_id = "Turbo CD-ROM    ";
+       for (i = 0; i < 16; i++) {
+         controller->buffer[16+i] = product_id[i];
        }
-         break;
-      
-       case 0x43: { // read toc
-         if (SELECTED_DRIVE(channel).cdrom.ready) {
-           
-           bool msf = (SELECTED_CONTROLLER(channel).buffer[1] >> 1) & 1;
-           uint8 starting_track = SELECTED_CONTROLLER(channel).buffer[6];
-           
-           uint16 alloc_length = rd_read_16bit(SELECTED_CONTROLLER(channel).buffer + 7);
-           
-           uint8 format = (SELECTED_CONTROLLER(channel).buffer[9] >> 6);
-           int i;
-           switch (format) {
-           case 0:
-             
-             if (!(SELECTED_DRIVE(channel).cdrom.cd->ops.read_toc(cdif, SELECTED_CONTROLLER(channel).buffer,
-                                                              &toc_length, msf, starting_track))) {
-               rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST,
-                                  ASC_INV_FIELD_IN_CMD_PACKET);
-               rd_raise_interrupt(channels, channel);
-             } else {
-               rd_init_send_atapi_command(channels, channel, atapi_command, toc_length, alloc_length, false);
-               rd_ready_to_send_atapi(channels, channel);
-             }
-             break;
-             
-           case 1:
-             // multi session stuff. we ignore this and emulate a single session only
-             rd_init_send_atapi_command(channels, channel, atapi_command, 12, alloc_length, false);
-             
-             SELECTED_CONTROLLER(channel).buffer[0] = 0;
-             SELECTED_CONTROLLER(channel).buffer[1] = 0x0a;
-             SELECTED_CONTROLLER(channel).buffer[2] = 1;
-             SELECTED_CONTROLLER(channel).buffer[3] = 1;
-             for (i = 0; i < 8; i++)
-               SELECTED_CONTROLLER(channel).buffer[4+i] = 0;
-             
-             rd_ready_to_send_atapi(channels, channel);
-             break;
-             
-           case 2:
-           default:
-             RD_PANIC("(READ TOC) Format %d not supported\n", format);
-             break;
-           }
-         } else {
-           rd_atapi_cmd_error(channels, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-           rd_raise_interrupt(channels, channel);
-         }
+
+       // Product Revision level
+       const char* rev_level = "1.0 "; 
+       for (i = 0; i < 4; i++) {
+         controller->buffer[32 + i] = rev_level[i];
        }
-         break;
-      
-       case 0x28: // read (10)
-       case 0xa8: // read (12)
-         { 
+
+       rd_ready_to_send_atapi(dev, channel);
+       break;
+      }
+    case 0x25:  // read cd-rom capacity
+      {
+       // no allocation length???
+       rd_init_send_atapi_command(dev, channel, atapi_command, 8, 8, false);
        
-           uint32 transfer_length;
-           if (atapi_command == 0x28)
-             transfer_length = rd_read_16bit(SELECTED_CONTROLLER(channel).buffer + 7);
-           else
-             transfer_length = rd_read_32bit(SELECTED_CONTROLLER(channel).buffer + 6);
-           
-           uint32 lba = rd_read_32bit(SELECTED_CONTROLLER(channel).buffer + 2);
+       if (drive->cdrom.ready) {
+         uint32_t capacity = drive->cdrom.capacity;
+
+         PrintDebug("\t\tCapacity is %d sectors (%d bytes)\n", capacity, capacity * 2048);
+
+         controller->buffer[0] = (capacity >> 24) & 0xff;
+         controller->buffer[1] = (capacity >> 16) & 0xff;
+         controller->buffer[2] = (capacity >> 8) & 0xff;
+         controller->buffer[3] = (capacity >> 0) & 0xff;
+         controller->buffer[4] = (2048 >> 24) & 0xff;
+         controller->buffer[5] = (2048 >> 16) & 0xff;
+         controller->buffer[6] = (2048 >> 8) & 0xff;
+         controller->buffer[7] = (2048 >> 0) & 0xff;
+
+         rd_ready_to_send_atapi(dev, channel);
+       } else {
+         rd_atapi_cmd_error(dev, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+         rd_raise_interrupt(dev, channel);
+       }
+       break;
+      }
+      
+      
+    case 0xbe:  // read cd
+      {
+       if (drive->cdrom.ready) {
+         PrintError("Read CD with CD present not implemented\n");
+         rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+         rd_raise_interrupt(dev, channel);
+       } else {
+         rd_atapi_cmd_error(dev, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+         rd_raise_interrupt(dev, channel);
+       }
+       break;
+      }
+    case 0x43: // read toc
+      { 
+       if (drive->cdrom.ready) {
+         int toc_length;  
+         bool msf = (controller->buffer[1] >> 1) & 1;
+         uint8_t starting_track = controller->buffer[6];
+         
+         uint16_t alloc_length = rd_read_16bit(controller->buffer + 7);
+         
+         uint8_t format = (controller->buffer[9] >> 6);
+         int i;
+         switch (format) {
+         case 0:
            
-           if (!SELECTED_DRIVE(channel).cdrom.ready) {
-             rd_atapi_cmd_error(channels, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-             rd_raise_interrupt(channels, channel);
-             break;
+           if (!(drive->cdrom.cd->ops.read_toc(drive->cdrom.cd, controller->buffer,
+                                               &toc_length, msf, starting_track))) {
+             rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST,
+                                ASC_INV_FIELD_IN_CMD_PACKET);
+             rd_raise_interrupt(dev, channel);
+           } else {
+             rd_init_send_atapi_command(dev, channel, atapi_command, toc_length, alloc_length, false);
+             rd_ready_to_send_atapi(dev, channel);
            }
+           break;
            
-           if (transfer_length == 0) {
-             rd_atapi_cmd_nop(channels, channel);
-             rd_raise_interrupt(channels, channel);
-             Ramdisk_Print("\t\tREAD(%d) with transfer length 0, ok\n", atapi_command==0x28?10:12);
-             break;
-           }
+         case 1:
+           // multi session stuff. we ignore this and emulate a single session only
+           rd_init_send_atapi_command(dev, channel, atapi_command, 12, alloc_length, false);
            
-           if (lba + transfer_length > SELECTED_DRIVE(channel).cdrom.capacity) {
-             rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
-             rd_raise_interrupt(channels, channel);
-             break;
+           controller->buffer[0] = 0;
+           controller->buffer[1] = 0x0a;
+           controller->buffer[2] = 1;
+           controller->buffer[3] = 1;
+
+           for (i = 0; i < 8; i++) {
+             controller->buffer[4 + i] = 0;
            }
+
+           rd_ready_to_send_atapi(dev, channel);
+           break;
            
-           Ramdisk_Print("\t\tcdrom: READ (%d) LBA=%d LEN=%d\n", atapi_command==0x28?10:12, lba, transfer_length);
-           
-           // handle command
-           rd_init_send_atapi_command(channels, channel, atapi_command, transfer_length * 2048,
-                                      transfer_length * 2048, true);
-           SELECTED_DRIVE(channel).cdrom.remaining_blocks = transfer_length;
-           SELECTED_DRIVE(channel).cdrom.next_lba = lba;
-           rd_ready_to_send_atapi(channels, channel);
+         case 2:
+         default:
+           PrintError("(READ TOC) Format %d not supported\n", format);
+           return -1;
          }
+       } else {
+         rd_atapi_cmd_error(dev, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+         rd_raise_interrupt(dev, channel);
+       }
+       break;
+      }  
+    case 0x28: // read (10)
+    case 0xa8: // read (12)
+      { 
+       
+       uint32_t transfer_length;
+       if (atapi_command == 0x28) {
+         transfer_length = rd_read_16bit(controller->buffer + 7);
+       } else {
+         transfer_length = rd_read_32bit(controller->buffer + 6);
+       }
+
+       uint32_t lba = rd_read_32bit(controller->buffer + 2);
+       
+       if (!(drive->cdrom.ready)) {
+         rd_atapi_cmd_error(dev, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+         rd_raise_interrupt(dev, channel);
          break;
-         
-       case 0x2b: { // seek
-         uint32 lba = rd_read_32bit(SELECTED_CONTROLLER(channel).buffer + 2);
-         if (!SELECTED_DRIVE(channel).cdrom.ready) {
-           rd_atapi_cmd_error(channels, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-           rd_raise_interrupt(channels, channel);
-           break;
-         }
-         
-         if (lba > SELECTED_DRIVE(channel).cdrom.capacity) {
-           rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
-           rd_raise_interrupt(channels, channel);
-           break;
-         }
-         Ramdisk_Print("\t\tcdrom: SEEK (ignored)\n");
-         rd_atapi_cmd_nop(channels, channel);
-         rd_raise_interrupt(channels, channel);
        }
+       
+       if (transfer_length == 0) {
+         rd_atapi_cmd_nop(dev, channel);
+         rd_raise_interrupt(dev, channel);
+         Ramdisk_Print("\t\tREAD(%d) with transfer length 0, ok\n", atapi_command==0x28?10:12);
          break;
-      
-       case 0x1e: { // prevent/allow medium removal
-         if (SELECTED_DRIVE(channel).cdrom.ready) {
-           SELECTED_DRIVE(channel).cdrom.locked = SELECTED_CONTROLLER(channel).buffer[4] & 1;
-           rd_atapi_cmd_nop(channels, channel);
-         } else {
-           rd_atapi_cmd_error(channels, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-         }
-         rd_raise_interrupt(channels, channel);
        }
+       
+       if (lba + transfer_length > drive->cdrom.capacity) {
+         rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
+         rd_raise_interrupt(dev, channel);
          break;
-      
-       case 0x42: { // read sub-channel
-         bool msf = get_packet_field(channel,1, 1, 1);
-         bool sub_q = get_packet_field(channel,2, 6, 1);
-         uint8 data_format = get_packet_byte(channel,3);
-         uint8 track_number = get_packet_byte(channel,6);
-         uint16 alloc_length = get_packet_word(channel,7);
-         UNUSED(msf);
-         UNUSED(data_format);
-         UNUSED(track_number);
-         
-         if (!SELECTED_DRIVE(channel).cdrom.ready) {
-           rd_atapi_cmd_error(channels, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
-           rd_raise_interrupt(channels, channel);
-         } else {
-           SELECTED_CONTROLLER(channel).buffer[0] = 0;
-           SELECTED_CONTROLLER(channel).buffer[1] = 0; // audio not supported
-           SELECTED_CONTROLLER(channel).buffer[2] = 0;
-           SELECTED_CONTROLLER(channel).buffer[3] = 0;
+       }
        
-           int ret_len = 4; // header size
+       Ramdisk_Print("\t\tcdrom: READ (%d) LBA=%d LEN=%d\n", atapi_command==0x28?10:12, lba, transfer_length);
        
-           if (sub_q) { // !sub_q == header only
-             RD_ERROR("Read sub-channel with SubQ not implemented\n");
-             rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST,
-                                ASC_INV_FIELD_IN_CMD_PACKET);
-             rd_raise_interrupt(channels, channel);
-           }
-       
-           rd_init_send_atapi_command(channels, channel, atapi_command, ret_len, alloc_length, false);
-           rd_ready_to_send_atapi(channels, channel);
-         }
-       }
+       // handle command
+       rd_init_send_atapi_command(dev, channel, atapi_command, transfer_length * 2048,
+                                  transfer_length * 2048, true);
+       drive->cdrom.remaining_blocks = transfer_length;
+       drive->cdrom.next_lba = lba;
+       rd_ready_to_send_atapi(dev, channel);
+       break;
+      }
+    case 0x2b:  // seek
+      {
+       uint32_t lba = rd_read_32bit(controller->buffer + 2);
+
+       if (!(drive->cdrom.ready)) {
+         rd_atapi_cmd_error(dev, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+         rd_raise_interrupt(dev, channel);
          break;
-      
-       case 0x51: { // read disc info
-         // no-op to keep the Linux CD-ROM driver happy
-         rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
-         rd_raise_interrupt(channels, channel);
        }
-         break;
-      
-       case 0x55: // mode select
-       case 0xa6: // load/unload cd
-       case 0x4b: // pause/resume
-       case 0x45: // play audio
-       case 0x47: // play audio msf
-       case 0xbc: // play cd
-       case 0xb9: // read cd msf
-       case 0x44: // read header
-       case 0xba: // scan
-       case 0xbb: // set cd speed
-       case 0x4e: // stop play/scan
-       case 0x46: // ???
-       case 0x4a: // ???
-         RD_ERROR("ATAPI command 0x%x not implemented yet\n",
-                  atapi_command);
-         rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
-         rd_raise_interrupt(channels, channel);
-         break;
-       default:
-         RD_PANIC("Unknown ATAPI command 0x%x (%d)\n",
-                  atapi_command, atapi_command);
-         // We'd better signal the error if the user chose to continue
-         rd_atapi_cmd_error(channels, channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
-         rd_raise_interrupt(channels, channel);
+       
+       if (lba > drive->cdrom.capacity) {
+         rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
+         rd_raise_interrupt(dev, channel);
          break;
        }
-      }
-      
-      break;
-      
-      
-    default:
-      RD_PANIC("\t\tIO write(0x%x): current command is %02xh\n", address,
-              (unsigned) SELECTED_CONTROLLER(channel).current_command);
-    }
+       
+       PrintDebug("\t\tcdrom: SEEK (ignored)\n");
 
-/////////////////////////////////////////////////////////
-    break;
-  case 0x01: // hard disk write precompensation 0x1f1
-    WRITE_FEATURES(channel,value);
-    break;
+       rd_atapi_cmd_nop(dev, channel);
+       rd_raise_interrupt(dev, channel);
 
-  case 0x02: // hard disk sector count 0x1f2
-    WRITE_SECTOR_COUNT(channel,value);
-    break;
-    
-  case 0x03: // hard disk sector number 0x1f3
-    WRITE_SECTOR_NUMBER(channel,value);
-    break;
-    
-  case 0x04: // hard disk cylinder low 0x1f4
-    WRITE_CYLINDER_LOW(channel,value);
-    break;
-    
-  case 0x05: // hard disk cylinder high 0x1f5
-    WRITE_CYLINDER_HIGH(channel,value);
-    break;
-    
-  case 0x06: // hard disk drive and head register 0x1f6
-    // b7 Extended data field for ECC
-    // b6/b5: Used to be sector size.  00=256,01=512,10=1024,11=128
-    //   Since 512 was always used, bit 6 was taken to mean LBA mode:
-    //     b6 1=LBA mode, 0=CHS mode
-    //     b5 1
-    // b4: DRV
-    // b3..0 HD3..HD0
-    
-    if ( (value & 0xa0) != 0xa0 ) // 1x1xxxxx
-      Ramdisk_Print("\t\tIO write 0x%x (%02x): not 1x1xxxxxb\n", address, (unsigned) value);
-    
-    Bit32u drvsel = channels[channel].drive_select = (value >> 4) & 0x01;
-    WRITE_HEAD_NO(channel,value & 0xf);
-    if (SELECTED_CONTROLLER(channel).lba_mode == 0 && ((value >> 6) & 1) == 1){
-      Ramdisk_Print("\t\tenabling LBA mode\n");
+       break;
+      }
+    case 0x1e:  // prevent/allow medium removal
+      {
+
+       if (drive->cdrom.ready) {
+         drive->cdrom.locked = controller->buffer[4] & 1;
+         rd_atapi_cmd_nop(dev, channel);
+       } else {
+         rd_atapi_cmd_error(dev, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
        }
-    WRITE_LBA_MODE(channel,(value >> 6) & 1);
-    SELECTED_DRIVE(channel).cdrom.cd->lba = (value >> 6) & 1;
-    
-    
-    if (!SELECTED_IS_PRESENT(channel)) {
-      Ramdisk_Print ("\t\tError: device set to %d which does not exist! channel = %d\n",drvsel, channel);
-      SELECTED_CONTROLLER(channel).error_register = 0x04; // aborted
-      SELECTED_CONTROLLER(channel).status.err = 1;
-    }
-    
-    break;
-    
-    
-  case 0x07: // hard disk command 0x1f7
-
-      switch (value) {
-        // ATAPI commands
-        case 0xa1: // IDENTIFY PACKET DEVICE
-             if (SELECTED_IS_CD(channel)) {
-                   SELECTED_CONTROLLER(channel).current_command = value;
-                   SELECTED_CONTROLLER(channel).error_register = 0;
-
-                   SELECTED_CONTROLLER(channel).status.busy = 0;
-                   SELECTED_CONTROLLER(channel).status.drive_ready = 1;
-                   SELECTED_CONTROLLER(channel).status.write_fault = 0;
-                   SELECTED_CONTROLLER(channel).status.drq   = 1;
-                   SELECTED_CONTROLLER(channel).status.err   = 0;
-
-                   SELECTED_CONTROLLER(channel).status.seek_complete = 1;
-                   SELECTED_CONTROLLER(channel).status.corrected_data = 0;
-
-                   SELECTED_CONTROLLER(channel).buffer_index = 0;
-                   rd_raise_interrupt(channels, channel);
-                   rd_identify_ATAPI_drive(channels, channel);
-             } else {
-               rd_command_aborted(channels, channel, 0xa1);
-             }
-             break;
-
-        case 0xa0: // SEND PACKET (atapi)
-             if (SELECTED_IS_CD(channel)) {
-                   // PACKET
-                   if (SELECTED_CONTROLLER(channel).features & (1 << 0))
-                         RD_PANIC("\t\tPACKET-DMA not supported");
-                   if (SELECTED_CONTROLLER(channel).features & (1 << 1))
-                         RD_PANIC("\t\tPACKET-overlapped not supported");
-
-                   // We're already ready!
-                   SELECTED_CONTROLLER(channel).sector_count = 1;
-                   SELECTED_CONTROLLER(channel).status.busy = 0;
-                   SELECTED_CONTROLLER(channel).status.write_fault = 0;
-                   // serv bit??
-                   SELECTED_CONTROLLER(channel).status.drq = 1;
-                   SELECTED_CONTROLLER(channel).status.err = 0;
-
-                   // NOTE: no interrupt here
-                   SELECTED_CONTROLLER(channel).current_command = value;
-                   SELECTED_CONTROLLER(channel).buffer_index = 0;
-             } else {
-               rd_command_aborted (channels, channel, 0xa0);
-             }
-             break;
 
-      default:
-       Ramdisk_Print("\t\tneed translate command %2x\n", value);
-       break;
-      }//switch(value)
+       rd_raise_interrupt(dev, channel);
 
-    
-  case 0x16: // hard disk adapter control 0x3f6 
-    // (mch) Even if device 1 was selected, a write to this register
-    // goes to device 0 (if device 1 is absent)
-
-    prev_control_reset = SELECTED_CONTROLLER(channel).control.reset;
-    channels[channel].drives[0].controller.control.reset         = value & 0x04;
-    channels[channel].drives[1].controller.control.reset         = value & 0x04;
-    // CGS: was: SELECTED_CONTROLLER(channel).control.disable_irq    = value & 0x02;
-    channels[channel].drives[0].controller.control.disable_irq = value & 0x02;
-    channels[channel].drives[1].controller.control.disable_irq = value & 0x02;
-    
-    Ramdisk_Print("\t\tadpater control reg: reset controller = %d\n",
-                 (unsigned) (SELECTED_CONTROLLER(channel).control.reset) ? 1 : 0);
-    Ramdisk_Print("\t\tadpater control reg: disable_irq(X) = %d\n",
-                 (unsigned) (SELECTED_CONTROLLER(channel).control.disable_irq) ? 1 : 0);
-    
-    if (!prev_control_reset && SELECTED_CONTROLLER(channel).control.reset) {
-      // transition from 0 to 1 causes all drives to reset
-      Ramdisk_Print("\t\thard drive: RESET\n");
-      
-      // (mch) Set BSY, drive not ready
-      for (id = 0; id < 2; id++) {
-       CONTROLLER(channel,id).status.busy           = 1;
-       CONTROLLER(channel,id).status.drive_ready    = 0;
-       CONTROLLER(channel,id).reset_in_progress     = 1;
-       
-       CONTROLLER(channel,id).status.write_fault    = 0;
-       CONTROLLER(channel,id).status.seek_complete  = 1;
-       CONTROLLER(channel,id).status.drq            = 0;
-       CONTROLLER(channel,id).status.corrected_data = 0;
-       CONTROLLER(channel,id).status.err            = 0;
-       
-       CONTROLLER(channel,id).error_register = 0x01; // diagnostic code: no error
-       
-       CONTROLLER(channel,id).current_command = 0x00;
-       CONTROLLER(channel,id).buffer_index = 0;
-       
-       CONTROLLER(channel,id).sectors_per_block = 0x80;
-       CONTROLLER(channel,id).lba_mode          = 0;
-       
-       CONTROLLER(channel,id).control.disable_irq = 0;
-       rd_lower_irq((struct vm_device *)(ramdisk_state->private_data), channels[channel].irq);
+       break;
       }
-    } else if (SELECTED_CONTROLLER(channel).reset_in_progress &&
-              !SELECTED_CONTROLLER(channel).control.reset) {
-      // Clear BSY and DRDY
-      Ramdisk_Print("\t\tReset complete {%s}\n", SELECTED_TYPE_STRING(channel));
-      for (id = 0; id < 2; id++) {
-       CONTROLLER(channel,id).status.busy           = 0;
-       CONTROLLER(channel,id).status.drive_ready    = 1;
-       CONTROLLER(channel,id).reset_in_progress     = 0;
+    case 0x42:  // read sub-channel
+      {
+       //bool msf = get_packet_field(channel,1, 1, 1);
+       bool sub_q = get_packet_field(channel,2, 6, 1);
+       //uint8_t data_format = get_packet_byte(channel,3);
+       //uint8_t track_number = get_packet_byte(channel,6);
+       uint16_t alloc_length = get_packet_word(channel,7);
        
-       // Device signature
-       if (DRIVE_IS_HD(channel,id)) {
-         Ramdisk_Print("\t\tdrive %d/%d is harddrive\n", channel, id);
-         CONTROLLER(channel,id).head_no        = 0;
-         CONTROLLER(channel,id).sector_count   = 1;
-         CONTROLLER(channel,id).sector_no      = 1;
-         CONTROLLER(channel,id).cylinder_no    = 0;
+
+       /*
+         UNUSED(msf);
+         UNUSED(data_format);
+         UNUSED(track_number);
+       */
+       if (!(drive->cdrom.ready)) {
+         rd_atapi_cmd_error(dev, channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+         rd_raise_interrupt(dev, channel);
        } else {
-         CONTROLLER(channel,id).head_no        = 0;
-         CONTROLLER(channel,id).sector_count   = 1;
-         CONTROLLER(channel,id).sector_no      = 1;
-         CONTROLLER(channel,id).cylinder_no    = 0xeb14;
+         controller->buffer[0] = 0;
+         controller->buffer[1] = 0; // audio not supported
+         controller->buffer[2] = 0;
+         controller->buffer[3] = 0;
+         
+         int ret_len = 4; // header size
+         
+         if (sub_q) { // !sub_q == header only
+           PrintError("Read sub-channel with SubQ not implemented\n");
+           rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST,
+                              ASC_INV_FIELD_IN_CMD_PACKET);
+           rd_raise_interrupt(dev, channel);
+         }
+         
+         rd_init_send_atapi_command(dev, channel, atapi_command, ret_len, alloc_length, false);
+         rd_ready_to_send_atapi(dev, channel);
        }
+       break;
+      }
+    case 0x51:  // read disc info
+      {
+       // no-op to keep the Linux CD-ROM driver happy
+       rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+       rd_raise_interrupt(dev, channel);
+       break;
       }
+    case 0x55: // mode select
+    case 0xa6: // load/unload cd
+    case 0x4b: // pause/resume
+    case 0x45: // play audio
+    case 0x47: // play audio msf
+    case 0xbc: // play cd
+    case 0xb9: // read cd msf
+    case 0x44: // read header
+    case 0xba: // scan
+    case 0xbb: // set cd speed
+    case 0x4e: // stop play/scan
+    case 0x46: // ???
+    case 0x4a: // ???
+      PrintError("ATAPI command 0x%x not implemented yet\n",
+              atapi_command);
+      rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+      rd_raise_interrupt(dev, channel);
+      break;
+    default:
+      PrintError("Unknown ATAPI command 0x%x (%d)\n",
+                atapi_command, atapi_command);
+      // We'd better signal the error if the user chose to continue
+      rd_atapi_cmd_error(dev, channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+      rd_raise_interrupt(dev, channel);
+      break;
     }
-    Ramdisk_Print("\t\ts[0].controller.control.disable_irq = %02x\n", (channels[channel].drives[0]).controller.control.disable_irq);
-    Ramdisk_Print("\t\ts[1].controller.control.disable_irq = %02x\n", (channels[channel].drives[1]).controller.control.disable_irq);
-    break;
+  }
+  return 0;
+}
+
+
+
+
+void rd_init_send_atapi_command(struct vm_device * dev, struct channel_t * channel, Bit8u command, int req_length, int alloc_length, bool lazy)
+{
+  struct drive_t * drive = &(channel->drives[channel->drive_select]);
+  struct controller_t * controller = &(drive->controller);
+
+  // controller->byte_count is a union of controller->cylinder_no;
+  // lazy is used to force a data read in the buffer at the next read.
+  
+  Ramdisk_Print("[rd_init_send_atapi_cmd]\n");
+
+  if (controller->byte_count == 0xffff) {
+    controller->byte_count = 0xfffe;
+  }
+
+  if ((controller->byte_count & 1) && 
+      !(alloc_length <= controller->byte_count)) {
+      
+    Ramdisk_Print("\t\tOdd byte count (0x%04x) to ATAPI command 0x%02x, using 0x%x\n", 
+                 controller->byte_count, 
+                 command, 
+                 controller->byte_count - 1);
     
-  default:
-    RD_PANIC("\t\thard drive: io write to address %x = %02x\n",
-                 (unsigned) address, (unsigned) value);
+    controller->byte_count -= 1;
+  }
+  
+  if (controller->byte_count == 0) {
+    RD_PANIC("\t\tATAPI command with zero byte count\n");
+  }
+
+  if (alloc_length < 0) {
+    RD_PANIC("\t\tAllocation length < 0\n");
+  }
+
+  if (alloc_length == 0) {
+    alloc_length = controller->byte_count;
+  }
+  
+  controller->interrupt_reason.i_o = 1;
+  controller->interrupt_reason.c_d = 0;
+  controller->status.busy = 0;
+  controller->status.drq = 1;
+  controller->status.err = 0;
+  
+  // no bytes transfered yet
+  if (lazy) {
+    controller->buffer_index = 2048;
+  } else {
+    controller->buffer_index = 0;
+  }
+
+  controller->drq_index = 0;
+  
+  if (controller->byte_count > req_length) {
+    controller->byte_count = req_length;
+  }
+
+  if (controller->byte_count > alloc_length) {
+    controller->byte_count = alloc_length;
   }  
+
+  drive->atapi.command = command;
+  drive->atapi.drq_bytes = controller->byte_count;
+  drive->atapi.total_bytes_remaining = (req_length < alloc_length) ? req_length : alloc_length;
   
-  return;
+  // if (lazy) {
+  // // bias drq_bytes and total_bytes_remaining
+  // SELECTED_DRIVE(channel).atapi.drq_bytes += 2048;
+  // SELECTED_DRIVE(channel).atapi.total_bytes_remaining += 2048;
+  // }
 }
 
 
-static 
-void rd_identify_ATAPI_drive(struct channel_t *channels, Bit8u channel)
+
+ void rd_ready_to_send_atapi(struct vm_device * dev, struct channel_t * channel) {
+  Ramdisk_Print("[rd_ready_to_send_atapi]\n");
+
+  rd_raise_interrupt(dev, channel);
+}
+
+
+
+
+
+void rd_atapi_cmd_error(struct vm_device * dev, struct channel_t * channel, sense_t sense_key, asc_t asc)
 {
-  unsigned i;
+  struct drive_t * drive = &(channel->drives[channel->drive_select]);
+  struct controller_t * controller = &(drive->controller);
+
+  Ramdisk_Print("[rd_atapi_cmd_error]\n");
+  Ramdisk_Print("Error: atapi_cmd_error channel=%02x key=%02x asc=%02x\n", channel, sense_key, asc);
+
+  controller->error_register = sense_key << 4;
+  controller->interrupt_reason.i_o = 1;
+  controller->interrupt_reason.c_d = 1;
+  controller->interrupt_reason.rel = 0;
+  controller->status.busy = 0;
+  controller->status.drive_ready = 1;
+  controller->status.write_fault = 0;
+  controller->status.drq = 0;
+  controller->status.err = 1;
+  
+  drive->sense.sense_key = sense_key;
+  drive->sense.asc = asc;
+  drive->sense.ascq = 0;
+}
+
+
+
+void rd_atapi_cmd_nop(struct vm_device * dev, struct channel_t * channel)
+{
+  struct drive_t * drive = &(channel->drives[channel->drive_select]);
+  struct controller_t * controller = &(drive->controller);
+
+  Ramdisk_Print("[rd_atapi_cmd_nop]\n");
+  controller->interrupt_reason.i_o = 1;
+  controller->interrupt_reason.c_d = 1;
+  controller->interrupt_reason.rel = 0;
+  controller->status.busy = 0;
+  controller->status.drive_ready = 1;
+  controller->status.drq = 0;
+  controller->status.err = 0;
+}
+
+
+
+
+void rd_identify_ATAPI_drive(struct vm_device * dev, struct channel_t * channel)
+{
+  struct drive_t * drive = &(channel->drives[channel->drive_select]);
+  struct controller_t * controller = &(drive->controller);
+
+
+  uint_t i;
   const char* serial_number = " VT00001\0\0\0\0\0\0\0\0\0\0\0\0";
   const char* firmware = "ALPHA1  ";
 
-  SELECTED_DRIVE(channel).id_drive[0] = (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0); // Removable CDROM, 50us response, 12 byte packets
-
-  for (i = 1; i <= 9; i++)
-       SELECTED_DRIVE(channel).id_drive[i] = 0;
+  drive->id_drive[0] = (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0); // Removable CDROM, 50us response, 12 byte packets
 
+  for (i = 1; i <= 9; i++) {
+    drive->id_drive[i] = 0;
+  }
 
   for (i = 0; i < 10; i++) {
-       SELECTED_DRIVE(channel).id_drive[10+i] = (serial_number[i*2] << 8) |
-             serial_number[i*2 + 1];
+    drive->id_drive[10 + i] = ((serial_number[i * 2] << 8) |
+                              (serial_number[(i * 2) + 1]));
   }
 
-  for (i = 20; i <= 22; i++)
-       SELECTED_DRIVE(channel).id_drive[i] = 0;
-
+  for (i = 20; i <= 22; i++) {
+    drive->id_drive[i] = 0;
+  }
 
   for (i = 0; i < strlen(firmware)/2; i++) {
-       SELECTED_DRIVE(channel).id_drive[23+i] = (firmware[i*2] << 8) |
-             firmware[i*2 + 1];
+    drive->id_drive[23 + i] = ((firmware[i * 2] << 8) |
+                              (firmware[(i * 2) + 1]));
   }
-  V3_ASSERT((23+i) == 27);
+  V3_ASSERT((23 + i) == 27);
   
-  for (i = 0; i < strlen((char *) SELECTED_MODEL(channel))/2; i++) {
-       SELECTED_DRIVE(channel).id_drive[27+i] = (SELECTED_MODEL(channel)[i*2] << 8) |
-             SELECTED_MODEL(channel)[i*2 + 1];
+  for (i = 0; i < strlen((char *)(drive->model_no)) / 2; i++) {
+    drive->id_drive[27 + i] = ((drive->model_no[i * 2] << 8) |
+                              (drive->model_no[(i * 2) + 1]));
   }
-  V3_ASSERT((27+i) == 47);
+  V3_ASSERT((27 + i) == 47);
 
-  SELECTED_DRIVE(channel).id_drive[47] = 0;
-  SELECTED_DRIVE(channel).id_drive[48] = 1; // 32 bits access
+  drive->id_drive[47] = 0;
+  drive->id_drive[48] = 1; // 32 bits access
 
-  SELECTED_DRIVE(channel).id_drive[49] = (1 << 9); // LBA supported
+  drive->id_drive[49] = (1 << 9); // LBA supported
 
-  SELECTED_DRIVE(channel).id_drive[50] = 0;
-  SELECTED_DRIVE(channel).id_drive[51] = 0;
-  SELECTED_DRIVE(channel).id_drive[52] = 0;
+  drive->id_drive[50] = 0;
+  drive->id_drive[51] = 0;
+  drive->id_drive[52] = 0;
 
-  SELECTED_DRIVE(channel).id_drive[53] = 3; // words 64-70, 54-58 valid
+  drive->id_drive[53] = 3; // words 64-70, 54-58 valid
 
-  for (i = 54; i <= 62; i++)
-       SELECTED_DRIVE(channel).id_drive[i] = 0;
+  for (i = 54; i <= 62; i++) {
+    drive->id_drive[i] = 0;
+  }
 
   // copied from CFA540A
-  SELECTED_DRIVE(channel).id_drive[63] = 0x0103; // variable (DMA stuff)
-  SELECTED_DRIVE(channel).id_drive[64] = 0x0001; // PIO
-  SELECTED_DRIVE(channel).id_drive[65] = 0x00b4;
-  SELECTED_DRIVE(channel).id_drive[66] = 0x00b4;
-  SELECTED_DRIVE(channel).id_drive[67] = 0x012c;
-  SELECTED_DRIVE(channel).id_drive[68] = 0x00b4;
-
-  SELECTED_DRIVE(channel).id_drive[69] = 0;
-  SELECTED_DRIVE(channel).id_drive[70] = 0;
-  SELECTED_DRIVE(channel).id_drive[71] = 30; // faked
-  SELECTED_DRIVE(channel).id_drive[72] = 30; // faked
-  SELECTED_DRIVE(channel).id_drive[73] = 0;
-  SELECTED_DRIVE(channel).id_drive[74] = 0;
-
-  SELECTED_DRIVE(channel).id_drive[75] = 0;
-
-  for (i = 76; i <= 79; i++)
-       SELECTED_DRIVE(channel).id_drive[i] = 0;
-
-  SELECTED_DRIVE(channel).id_drive[80] = 0x1e; // supports up to ATA/ATAPI-4
-  SELECTED_DRIVE(channel).id_drive[81] = 0;
-  SELECTED_DRIVE(channel).id_drive[82] = 0;
-  SELECTED_DRIVE(channel).id_drive[83] = 0;
-  SELECTED_DRIVE(channel).id_drive[84] = 0;
-  SELECTED_DRIVE(channel).id_drive[85] = 0;
-  SELECTED_DRIVE(channel).id_drive[86] = 0;
-  SELECTED_DRIVE(channel).id_drive[87] = 0;
-  SELECTED_DRIVE(channel).id_drive[88] = 0;
-
-  for (i = 89; i <= 126; i++)
-       SELECTED_DRIVE(channel).id_drive[i] = 0;
-
-  SELECTED_DRIVE(channel).id_drive[127] = 0;
-  SELECTED_DRIVE(channel).id_drive[128] = 0;
-
-  for (i = 129; i <= 159; i++)
-       SELECTED_DRIVE(channel).id_drive[i] = 0;
-
-  for (i = 160; i <= 255; i++)
-       SELECTED_DRIVE(channel).id_drive[i] = 0;
+  drive->id_drive[63] = 0x0103; // variable (DMA stuff)
+  drive->id_drive[64] = 0x0001; // PIO
+  drive->id_drive[65] = 0x00b4;
+  drive->id_drive[66] = 0x00b4;
+  drive->id_drive[67] = 0x012c;
+  drive->id_drive[68] = 0x00b4;
+
+  drive->id_drive[69] = 0;
+  drive->id_drive[70] = 0;
+  drive->id_drive[71] = 30; // faked
+  drive->id_drive[72] = 30; // faked
+  drive->id_drive[73] = 0;
+  drive->id_drive[74] = 0;
+
+  drive->id_drive[75] = 0;
+
+  for (i = 76; i <= 79; i++) {
+    drive->id_drive[i] = 0;
+  }
+
+  drive->id_drive[80] = 0x1e; // supports up to ATA/ATAPI-4
+  drive->id_drive[81] = 0;
+  drive->id_drive[82] = 0;
+  drive->id_drive[83] = 0;
+  drive->id_drive[84] = 0;
+  drive->id_drive[85] = 0;
+  drive->id_drive[86] = 0;
+  drive->id_drive[87] = 0;
+  drive->id_drive[88] = 0;
+
+  for (i = 89; i <= 126; i++) {
+    drive->id_drive[i] = 0;
+  }
+
+  drive->id_drive[127] = 0;
+  drive->id_drive[128] = 0;
+
+  for (i = 129; i <= 159; i++) {
+    drive->id_drive[i] = 0;
+  }
+
+  for (i = 160; i <= 255; i++) {
+    drive->id_drive[i] = 0;
+  }
 
   // now convert the id_drive array (native 256 word format) to
   // the controller buffer (512 bytes)
   Bit16u temp16;
   for (i = 0; i <= 255; i++) {
-       temp16 = SELECTED_DRIVE(channel).id_drive[i];
-       SELECTED_CONTROLLER(channel).buffer[i*2] = temp16 & 0x00ff;
-       SELECTED_CONTROLLER(channel).buffer[i*2+1] = temp16 >> 8;
+    temp16 = drive->id_drive[i];
+    controller->buffer[i * 2] = temp16 & 0x00ff;
+    controller->buffer[i * 2 + 1] = temp16 >> 8;
   }
 
   return;
 
 
 
+
+
+
+
 static 
-void rd_raise_interrupt(struct channel_t *channels, Bit8u channel)
+void rd_init_mode_sense_single(struct vm_device * dev, 
+                              struct channel_t * channel, const void* src, int size)
 {
-  Bit32u irq;
-  struct vm_device *dev;
+  struct drive_t * drive = &(channel->drives[channel->drive_select]);
+  struct controller_t * controller = &(drive->controller);
 
-  Ramdisk_Print("[raise_interrupt] disable_irq = %02x\n", SELECTED_CONTROLLER(channel).control.disable_irq);
+  Ramdisk_Print("[rd_init_mode_sense_single]\n");
 
-  if (!SELECTED_CONTROLLER(channel).control.disable_irq) { 
-    Ramdisk_Print("\t\traising interrupt\n"); 
-  } else { 
-    Ramdisk_Print("\t\tNot raising interrupt\n"); 
-  }
-  if (!SELECTED_CONTROLLER(channel).control.disable_irq) {
-    irq = channels[channel].irq; 
-    Ramdisk_Print("\t\tRaising interrupt %d {%s}\n\n", irq, SELECTED_TYPE_STRING(channel));
-    //        DEV_pic_raise_irq(irq);
-    dev = (struct vm_device*) ramdisk_state->private_data;
-    Ramdisk_Print("\t\tdev = %x\n", dev);
-    dev->vm->vm_ops.raise_irq(dev->vm, irq);
-  } else {
-    Ramdisk_Print("\t\tirq is disabled\n");
-  }
+  // Header
+  controller->buffer[0] = (size + 6) >> 8;
+  controller->buffer[1] = (size + 6) & 0xff;
+  controller->buffer[2] = 0x70; // no media present
+  controller->buffer[3] = 0; // reserved
+  controller->buffer[4] = 0; // reserved
+  controller->buffer[5] = 0; // reserved
+  controller->buffer[6] = 0; // reserved
+  controller->buffer[7] = 0; // reserved
   
-  return;
+  // Data
+  memcpy(controller->buffer + 8, src, size);
 }
 
-static
-void rd_lower_irq(struct vm_device *dev, Bit32u irq)// __attribute__(regparm(1))
-{
-  Ramdisk_Print("[lower_irq] irq = %d\n", irq);
-  dev->vm->vm_ops.lower_irq(dev->vm, irq);
+
+
+static void rd_command_aborted(struct vm_device * dev, 
+                              struct channel_t * channel, unsigned value) {
+  struct drive_t * drive = &(channel->drives[channel->drive_select]);
+  struct controller_t * controller = &(drive->controller);
+
+  Ramdisk_Print("[rd_command_aborted]\n");
+  Ramdisk_Print("\t\taborting on command 0x%02x {%s}\n", value, SELECTED_TYPE_STRING(channel));
+
+  controller->current_command = 0;
+  controller->status.busy = 0;
+  controller->status.drive_ready = 1;
+  controller->status.err = 1;
+  controller->error_register = 0x04; // command ABORTED
+  controller->status.drq = 0;
+  controller->status.seek_complete = 0;
+  controller->status.corrected_data = 0;
+  controller->buffer_index = 0;
+
+  rd_raise_interrupt(dev, channel);
 }
 
 
-/*
- * Public Routines
- */
-static
-int ramdisk_read_port(ushort_t port,
-                        void *src,
-                        uint_t length,
-                        struct vm_device *dev)
-{
-  uint_t i;
-  //Ramdisk_Print("[ramdisk_read_port] port = %x, length = %d\n", port, length);
-    switch (length) {
-    case 1:
-      ((uchar_t*)src)[0] = rd_read_handler(ramdisk_state->channels, port, length);
-      break;
-    case 2:
-      ((ushort_t*)src)[0] = rd_read_handler(ramdisk_state->channels, port, length);
-      break;
-    case 4:
-      ((uint_t*)src)[0] = rd_read_handler(ramdisk_state->channels, port, length);
-      break;
-    default:
-      for (i = 0; i < length; i++) { 
-       ((uchar_t*)src)[i] = rd_read_handler(ramdisk_state->channels, port, 1);
-      }
-    }//switch length
+static int ramdisk_init_device(struct vm_device *dev) {
+  struct ramdisk_t *ramdisk_state = (struct ramdisk_t *)dev->private_data;
+
+  rd_init_hardware(ramdisk_state);
+
+
+  dev_hook_io(dev, PRI_CTRL_PORT, 
+             &read_status_port, &write_ctrl_port);
+
+  dev_hook_io(dev, PRI_DATA_PORT, 
+             &read_data_port, &write_data_port);
+  dev_hook_io(dev, PRI_FEATURES_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, PRI_SECT_CNT_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, PRI_SECT_ADDR1_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, PRI_SECT_ADDR2_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, PRI_SECT_ADDR3_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, PRI_DRV_SEL_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, PRI_CMD_PORT, 
+             &read_status_port, &write_cmd_port);
+
+
+  dev_hook_io(dev, SEC_CTRL_PORT, 
+             &read_status_port, &write_ctrl_port);
+
+  dev_hook_io(dev, SEC_DATA_PORT, 
+             &read_data_port, &write_data_port);
+  dev_hook_io(dev, SEC_FEATURES_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, SEC_SECT_CNT_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, SEC_SECT_ADDR1_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, SEC_SECT_ADDR2_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, SEC_SECT_ADDR3_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, SEC_DRV_SEL_PORT, 
+             &read_general_port, &write_general_port);
+  dev_hook_io(dev, SEC_CMD_PORT, 
+             &read_status_port, &write_cmd_port);
+
+  
+
+  dev_hook_io(dev, SEC_ADDR_REG_PORT, 
+             &read_general_port, &write_general_port);
+
+  dev_hook_io(dev, PRI_ADDR_REG_PORT, 
+             &read_general_port, &write_general_port);
+
+
+
+  return 0;
 
-  return length;
 }
 
 
-static
-int ramdisk_write_port(ushort_t port,
-                        void *src,
-                        uint_t length,
-                        struct vm_device *dev)
+static int ramdisk_deinit_device(struct vm_device *dev) {
+  struct ramdisk_t *ramdisk_state = (struct ramdisk_t *)(dev->private_data);
+  rd_close_harddrive(ramdisk_state);
+  return 0;
+}
+
+static struct vm_device_ops dev_ops = {
+  .init = ramdisk_init_device,
+  .deinit = ramdisk_deinit_device,
+  .reset = NULL,
+  .start = NULL,
+  .stop = NULL,
+};
+
+
+
+
+struct vm_device *create_ramdisk()
 {
-  //Ramdisk_Print("[ramdisk_write_port] port = %x, length = %d\n", port, length);
-  /*
-  uint_t i;
 
-  for (i = 0; i < length; i++)
-    Ramdisk_Print("\t\tsrc[%d] = 0x%02x\n", i, ((uchar_t*)src)[i]);
-  */
+  struct ramdisk_t *ramdisk;
+  ramdisk = (struct ramdisk_t *)V3_Malloc(sizeof(struct ramdisk_t));  
+  V3_ASSERT(ramdisk != NULL);  
+
+  Ramdisk_Print("[create_ramdisk]\n");
 
-  switch(length) {
-  case 1:
-    rd_write_handler(ramdisk_state->channels, port, *((uchar_t *)src), length);
-    break;
-  case 2:
-    rd_write_handler(ramdisk_state->channels, port, *((ushort_t *)src), length);
-    break;
-  case 4:
-    rd_write_handler(ramdisk_state->channels, port, *((uint_t *)src), length);
-    break;
-  default:
-    rd_write_handler(ramdisk_state->channels, port, *((uchar_t *)src), length);
-    break;
+  struct vm_device *device = create_device("RAMDISK", &dev_ops, ramdisk);
+
+  return device;
+}
+
+
+
+
+#ifdef RAMDISK_DEBUG
+static void rd_print_state(struct ramdisk_t * ramdisk,  struct vm_device *dev) {
+  uchar_t channel; 
+  uchar_t device;
+  struct channel_t * channels = (struct channel_t *)(&(ramdisk->channels));
+
+  for (channel = 0; channel < MAX_ATA_CHANNEL; channel++) {
+    memset((char *)(channels + channel), 0, sizeof(struct channel_t));
   }
 
-  return length;
+  Ramdisk_Print("sizeof(*channels) = %d\n", sizeof(*channels));
+  Ramdisk_Print("sizeof(channles->drives[0].controller) = %d\n", sizeof((channels->drives[0].controller)));
+  Ramdisk_Print("sizeof(channles->drives[0].cdrom) = %d\n", sizeof((channels->drives[0].cdrom)));
+  Ramdisk_Print("sizeof(channles->drives[0].sense) = %d\n", sizeof((channels->drives[0].sense)));
+  Ramdisk_Print("sizeof(channles->drives[0].atapi) = %d\n", sizeof((channels->drives[0].atapi)));
+
+
+  Ramdisk_Print("sizeof(channles->drives[0].controller.status) = %d\n", 
+               sizeof((channels->drives[0].controller.status)));
+  Ramdisk_Print("sizeof(channles->drives[0].controller.sector_count) = %d\n", 
+               sizeof((channels->drives[0].controller.sector_count)));
+  Ramdisk_Print("sizeof(channles->drives[0].controller.interrupt_reason) = %d\n", 
+               sizeof((channels->drives[0].controller.interrupt_reason)));
+
+  Ramdisk_Print("sizeof(channles->drives[0].controller.cylinder_no) = %d\n", 
+               sizeof((channels->drives[0].controller.cylinder_no)));
+  Ramdisk_Print("sizeof(channles->drives[0].controller.byte_count) = %d\n", 
+               sizeof((channels->drives[0].controller.byte_count)));
+
+
+  Ramdisk_Print("sizeof(channles->drives[0].controller.control) = %d\n", 
+               sizeof((channels->drives[0].controller.control)));
+
+
+  for (channel = 0; channel < MAX_ATA_CHANNEL; channel++){
+  
+    for (device = 0; device < 2; device++){
+                  
+      // Initialize controller state, even if device is not present
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.busy = %d\n",
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.busy);
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.drive_ready = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.drive_ready);
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.write_fault = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.write_fault);
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.seek_complete = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.seek_complete);
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.drq = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.drq);
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.corrected_data = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.corrected_data);
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.index_pulse = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.index_pulse);
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.index_pulse_count = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.index_pulse_count);
+      Ramdisk_Print("channels[%d].drives[%d].controller.status.err = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.status.err);
+
+
+      Ramdisk_Print("channels[%d].drives[%d].controller.error_register = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.error_register);
+      Ramdisk_Print("channels[%d].drives[%d].controller.head_no = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.head_no);
+      Ramdisk_Print("channels[%d].drives[%d].controller.sector_count = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.sector_count);
+      Ramdisk_Print("channels[%d].drives[%d].controller.sector_no = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.sector_no);
+      Ramdisk_Print("channels[%d].drives[%d].controller.cylinder_no = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.cylinder_no);
+      Ramdisk_Print("channels[%d].drives[%d].controller.current_command = %02x\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.current_command);
+      Ramdisk_Print("channels[%d].drives[%d].controller.buffer_index = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.buffer_index);
+
+
+      Ramdisk_Print("channels[%d].drives[%d].controller.control.reset = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.control.reset);
+      Ramdisk_Print("channels[%d].drives[%d].controller.control.disable_irq = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.control.disable_irq);
+
+
+      Ramdisk_Print("channels[%d].drives[%d].controller.reset_in_progress = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.reset_in_progress);
+      Ramdisk_Print("channels[%d].drives[%d].controller.sectors_per_block = %02x\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.sectors_per_block); 
+      Ramdisk_Print("channels[%d].drives[%d].controller.lba_mode = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.lba_mode); 
+      Ramdisk_Print("channels[%d].drives[%d].controller.features = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.features); 
+
+
+      Ramdisk_Print("channels[%d].drives[%d].model_no = %s\n", 
+                   channel, device, 
+                   channels[channel].drives[device].model_no); 
+      Ramdisk_Print("channels[%d].drives[%d].device_type = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].device_type); 
+      Ramdisk_Print("channels[%d].drives[%d].cdrom.locked = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].cdrom.locked); 
+      Ramdisk_Print("channels[%d].drives[%d].sense.sense_key = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].sense.sense_key); 
+      Ramdisk_Print("channels[%d].drives[%d].sense.asc = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].sense.asc); 
+      Ramdisk_Print("channels[%d].drives[%d].sense.ascq = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].sense.ascq); 
+
+
+
+      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.c_d = %02x\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.interrupt_reason.c_d);
+
+      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.i_o = %02x\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.interrupt_reason.i_o);
+
+      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.rel = %02x\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.interrupt_reason.rel);
+
+      Ramdisk_Print("channels[%d].drives[%d].controller.interrupt_reason.tag = %02x\n", 
+                   channel, device, 
+                   channels[channel].drives[device].controller.interrupt_reason.tag);
+
+      Ramdisk_Print("channels[%d].drives[%d].cdrom.ready = %d\n", 
+                   channel, device, 
+                   channels[channel].drives[device].cdrom.ready);
+      
+    }  //for device
+  }  //for channel
+  
+  return;
 }
 
 
-static void trace_info(ushort_t port, void *src, uint_t length)
-{
+
+static void trace_info(ushort_t port, void *src, uint_t length) {
+
   switch(port){
 
   case 0x3e8:
     if (length == 1 && *((uchar_t*) src) == ATA_DETECT)
-      Ramdisk_Print("ata_dectect()\n");
+      Ramdisk_Print("ata_detect()\n");
     break;
 
   case 0x3e9:
 }
 
 
-int ramdisk_read_port_ignore(ushort_t port,
-                        void *src,
-                        uint_t length,
-                        struct vm_device *dev)
-{
-  //  Ramdisk_Print("[ramdisk_read_port_ignore] port = %x, length = %d\n", port, length);
-  return length;
-}
-
-int ramdisk_write_port_ignore(ushort_t port,
-                        void *src,
-                        uint_t length,
-                        struct vm_device *dev)
-{
-
-  //  Ramdisk_Print("[ramdisk_write_port_ignore] port = %x, length = %d\n", port, length);
-
-  trace_info(port, src, length);
-  return length;
-}
 
-//////////////////////////////////////////////////////////////////////////
-
-/*
- * ATAPI subroutines
- */
-
-static 
-void rd_init_send_atapi_command(struct channel_t *channels, Bit8u channel, Bit8u command, int req_length, int alloc_length, bool lazy)
-{
-  // SELECTED_CONTROLLER(channel).byte_count is a union of SELECTED_CONTROLLER(channel).cylinder_no;
-  // lazy is used to force a data read in the buffer at the next read.
-  
-  Ramdisk_Print("[rd_init_send_atapi_cmd]\n");
-  if (SELECTED_CONTROLLER(channel).byte_count == 0xffff)
-    SELECTED_CONTROLLER(channel).byte_count = 0xfffe;
-  
-  if ((SELECTED_CONTROLLER(channel).byte_count & 1)
-      && !(alloc_length <= SELECTED_CONTROLLER(channel).byte_count)) {
-    Ramdisk_Print("\t\tOdd byte count (0x%04x) to ATAPI command 0x%02x, using 0x%x\n", 
-                 SELECTED_CONTROLLER(channel).byte_count, command, SELECTED_CONTROLLER(channel).byte_count - 1);
-    SELECTED_CONTROLLER(channel).byte_count -= 1;
+static  int check_bit_fields(struct controller_t * controller) {
+  //Check bit fields
+  controller->sector_count = 0;
+  controller->interrupt_reason.c_d = 1;
+  if (controller->sector_count != 0x01) {
+    return INTR_REASON_BIT_ERR;
   }
   
-  if (SELECTED_CONTROLLER(channel).byte_count == 0)
-    RD_PANIC("\t\tATAPI command with zero byte count\n");
-  
-  if (alloc_length < 0)
-    RD_PANIC("\t\tAllocation length < 0\n");
-  if (alloc_length == 0)
-    alloc_length = SELECTED_CONTROLLER(channel).byte_count;
-  
-  SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
-  SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 0;
-  SELECTED_CONTROLLER(channel).status.busy = 0;
-  SELECTED_CONTROLLER(channel).status.drq = 1;
-  SELECTED_CONTROLLER(channel).status.err = 0;
-  
-  // no bytes transfered yet
-  if (lazy)
-    SELECTED_CONTROLLER(channel).buffer_index = 2048;
-  else
-    SELECTED_CONTROLLER(channel).buffer_index = 0;
-  SELECTED_CONTROLLER(channel).drq_index = 0;
-  
-  if (SELECTED_CONTROLLER(channel).byte_count > req_length)
-    SELECTED_CONTROLLER(channel).byte_count = req_length;
-  
-  if (SELECTED_CONTROLLER(channel).byte_count > alloc_length)
-    SELECTED_CONTROLLER(channel).byte_count = alloc_length;
-  
-  SELECTED_DRIVE(channel).atapi.command = command;
-  SELECTED_DRIVE(channel).atapi.drq_bytes = SELECTED_CONTROLLER(channel).byte_count;
-  SELECTED_DRIVE(channel).atapi.total_bytes_remaining = (req_length < alloc_length) ? req_length : alloc_length;
+  controller->sector_count = 0;
+  controller->interrupt_reason.i_o = 1;
+  if (controller->sector_count != 0x02) {
+    return INTR_REASON_BIT_ERR;
+  }
   
-  // if (lazy) {
-  // // bias drq_bytes and total_bytes_remaining
-  // SELECTED_DRIVE(channel).atapi.drq_bytes += 2048;
-  // SELECTED_DRIVE(channel).atapi.total_bytes_remaining += 2048;
-  // }
-}
-
-
-static 
-void rd_atapi_cmd_error(struct channel_t *channels, Bit8u channel, sense_t sense_key, asc_t asc)
-{
-  Ramdisk_Print("[rd_atapi_cmd_error]\n");
-  Ramdisk_Print("Error: atapi_cmd_error channel=%02x key=%02x asc=%02x\n", channel, sense_key, asc);
-
-  SELECTED_CONTROLLER(channel).error_register = sense_key << 4;
-  SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
-  SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1;
-  SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0;
-  SELECTED_CONTROLLER(channel).status.busy = 0;
-  SELECTED_CONTROLLER(channel).status.drive_ready = 1;
-  SELECTED_CONTROLLER(channel).status.write_fault = 0;
-  SELECTED_CONTROLLER(channel).status.drq = 0;
-  SELECTED_CONTROLLER(channel).status.err = 1;
+  controller->sector_count = 0;
+  controller->interrupt_reason.rel = 1;
+  if (controller->sector_count != 0x04) {
+    return INTR_REASON_BIT_ERR;
+  }
   
-  SELECTED_DRIVE(channel).sense.sense_key = sense_key;
-  SELECTED_DRIVE(channel).sense.asc = asc;
-  SELECTED_DRIVE(channel).sense.ascq = 0;
-}
-
-
-static 
-void rd_atapi_cmd_nop(struct channel_t *channels, Bit8u channel)
-{
-  Ramdisk_Print("[rd_atapi_cmd_nop]\n");
-  SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
-  SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1;
-  SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0;
-  SELECTED_CONTROLLER(channel).status.busy = 0;
-  SELECTED_CONTROLLER(channel).status.drive_ready = 1;
-  SELECTED_CONTROLLER(channel).status.drq = 0;
-  SELECTED_CONTROLLER(channel).status.err = 0;
-}
-
-
-static 
-void rd_init_mode_sense_single(struct channel_t *channels, 
-                              Bit8u channel, const void* src, int size)
-{
-  Ramdisk_Print("[rd_init_mode_sense_single]\n");
-  // Header
-  SELECTED_CONTROLLER(channel).buffer[0] = (size+6) >> 8;
-  SELECTED_CONTROLLER(channel).buffer[1] = (size+6) & 0xff;
-  SELECTED_CONTROLLER(channel).buffer[2] = 0x70; // no media present
-  SELECTED_CONTROLLER(channel).buffer[3] = 0; // reserved
-  SELECTED_CONTROLLER(channel).buffer[4] = 0; // reserved
-  SELECTED_CONTROLLER(channel).buffer[5] = 0; // reserved
-  SELECTED_CONTROLLER(channel).buffer[6] = 0; // reserved
-  SELECTED_CONTROLLER(channel).buffer[7] = 0; // reserved
+  controller->sector_count = 0;
+  controller->interrupt_reason.tag = 3;
+  if (controller->sector_count != 0x18) {
+    return INTR_REASON_BIT_ERR;
+  }
   
-  // Data
-  memcpy(SELECTED_CONTROLLER(channel).buffer + 8, src, size);
-}
-
-
-static 
-void rd_ready_to_send_atapi(struct channel_t *channels, Bit8u channel)
-{
-  Ramdisk_Print("[rd_ready_to_send_atapi]\n");
-  rd_raise_interrupt(ramdisk_state->channels, channel);
-}
-
-
-static 
-void rd_command_aborted(struct channel_t *channels, 
-                       Bit8u channel, unsigned value)
-{
-  Ramdisk_Print("[rd_command_aborted]\n");
-  Ramdisk_Print("\t\taborting on command 0x%02x {%s}\n", value, SELECTED_TYPE_STRING(channel));
-  SELECTED_CONTROLLER(channel).current_command = 0;
-  SELECTED_CONTROLLER(channel).status.busy = 0;
-  SELECTED_CONTROLLER(channel).status.drive_ready = 1;
-  SELECTED_CONTROLLER(channel).status.err = 1;
-  SELECTED_CONTROLLER(channel).error_register = 0x04; // command ABORTED
-  SELECTED_CONTROLLER(channel).status.drq = 0;
-  SELECTED_CONTROLLER(channel).status.seek_complete = 0;
-  SELECTED_CONTROLLER(channel).status.corrected_data = 0;
-  SELECTED_CONTROLLER(channel).buffer_index = 0;
-  rd_raise_interrupt(ramdisk_state->channels, channel);
-}
-
-
-static int ramdisk_init_device(struct vm_device *dev)
-{
-  struct ramdisk_t *ramdisk_state = (struct ramdisk_t *)dev->private_data;
-
-  ramdisk_state->cops.init(ramdisk_state, dev);
-
-  //hook ports IDE 0x170-0x177, 0x376 & 0x377
-  dev_hook_io(dev, 0x170, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x171, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x172, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x173, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x174, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x175, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x176, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x177, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x376, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  dev_hook_io(dev, 0x377, 
-             (ramdisk_state->eops.read_port), 
-             (ramdisk_state->eops.write_port));
-
-  //Debug ports: 0x3e8-0x3ef & 0x2e8-0x2ef
-
-#ifdef DEBUG_RAMDISK
-
-  dev_hook_io(dev, 0x3e8, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x3e9, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x3ea, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x3eb, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x3ec, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x3ed, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x3ee, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x3ef, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x2e8, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x2e9, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x2ea, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x2eb, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x2ec, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x2ed, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x2ee, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-  dev_hook_io(dev, 0x2ef, 
-             (ramdisk_state->eops.read_port_ignore), 
-             (ramdisk_state->eops.write_port_ignore));
-
-#endif
-
-}
-
-
-static int ramdisk_deinit_device(struct vm_device *dev)
-{
-  struct ramdisk_t *ramdisk_state = (struct ramdisk_t *)(dev->private_data);
-  ramdisk_state->cops.close(ramdisk_state);
-
   return 0;
 }
-
-static struct vm_device_ops dev_ops = {
-  .init = ramdisk_init_device,
-  .deinit = ramdisk_deinit_device,
-  .reset = NULL,
-  .start = NULL,
-  .stop = NULL,
-};
-
-
-
-/*
- * Success: return 0; 
- * Failure: return integer greater than 0
- */
-
-/*
-struct ramdisk_t * create_ramdisk()
-{
-  struct ramdisk_t *ramdisk;
-  ramdisk = (struct ramdisk_t *)V3_Malloc(sizeof(struct ramdisk_t));  
-  V3_ASSERT(ramdisk != NULL);
-  
-  ramdisk->cops.init = &rd_init_harddrive;
-  ramdisk->cops.close = &rd_close_harddrive;
-  ramdisk->cops.reset = &rd_reset_harddrive;
-
-  ramdisk->eops.read_port = &ramdisk_read_port;
-  ramdisk->eops.write_port = &ramdisk_write_port;
-  ramdisk->eops.read_port_ignore = &ramdisk_read_port_ignore;
-  ramdisk->eops.write_port_ignore = &ramdisk_write_port_ignore;
- 
-  return ramdisk;
-}
-
-*/
-
-
-struct vm_device *create_ramdisk()
-{
-
-  struct ramdisk_t *ramdisk;
-  ramdisk = (struct ramdisk_t *)V3_Malloc(sizeof(struct ramdisk_t));  
-  V3_ASSERT(ramdisk != NULL);  
-
-  Ramdisk_Print("[create_ramdisk]\n");
-  ramdisk->cops.init = &rd_init_harddrive;
-  ramdisk->cops.close = &rd_close_harddrive;
-  ramdisk->cops.reset = &rd_reset_harddrive;
-
-  ramdisk->eops.read_port = &ramdisk_read_port;
-  ramdisk->eops.write_port = &ramdisk_write_port;
-  ramdisk->eops.read_port_ignore = &ramdisk_read_port_ignore;
-  ramdisk->eops.write_port_ignore = &ramdisk_write_port_ignore;
-
-  struct vm_device *device = create_device("RAMDISK", &dev_ops, ramdisk);
-
-  return device;
-}
+#endif