Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


reworked ramdisk now compiles, not tested
Jack Lange [Thu, 2 Oct 2008 05:51:05 +0000 (00:51 -0500)]
palacios/build/Makefile
palacios/include/devices/cdrom.h
palacios/include/devices/ramdisk.h
palacios/src/devices/cdrom.c
palacios/src/devices/ramdisk.c
palacios/src/geekos/mem.c
palacios/src/palacios/vmm_config.c

index 734062b..5c6cc51 100644 (file)
@@ -183,16 +183,6 @@ endif
 #
 TCPSTACK=UIP
 
-#
-#RAMDISK
-#
-RAMDISK_SRCS=
-BOOT_FLAGS=
-
-ifeq ($(RAMDISK_BOOT),1)
-BOOT_FLAGS := $(BOOT_FLAGS) -DRAMDISK_BOOT
-RAMDISK_SRCS := ramdisk.c cdrom.c
-endif
 
 
 
@@ -295,7 +285,7 @@ VMM_OBJS := $(VMM_C_OBJS) $(VMM_ASM_OBJS)
 
 
 
-DEVICE_C_SRCS := generic.c keyboard.c nvram.c timer.c simple_pic.c 8259a.c 8254.c serial.c $(RAMDISK_SRCS)
+DEVICE_C_SRCS := generic.c keyboard.c nvram.c timer.c simple_pic.c 8259a.c 8254.c serial.c ramdisk.c cdrom.c
 
 DEVICE_C_OBJS := $(DEVICE_C_SRCS:%.c=devices/%.o)
 
index 4546f4f..fb49ad9 100644 (file)
@@ -7,17 +7,12 @@
 #ifndef __DEVICES_CDROM_H_
 #define __DEVICES_CDROM_H_
 
-#include <geekos/ktypes.h>
+#include <devices/ramdisk.h>
+#include <devices/ide.h>
+#include <palacios/vmm_types.h>
+
 
-typedef unsigned int rd_bool;
-typedef uchar_t Bit8u;
-typedef ushort_t Bit16u;
-typedef uint_t Bit32u;
-typedef ullong_t Bit64u;
 
-#define uint8 Bit8u 
-#define uint16 Bit16u 
-#define uint32 Bit32u 
 
 struct cdrom_interface;
 
@@ -38,17 +33,17 @@ struct cdrom_ops {
   /* 
    * Read CD TOC. Returns false if start track is out of bounds.
    */
-  rd_bool (*read_toc)(struct cdrom_interface *cdrom, uint8* buf, int* length, rd_bool msf, int start_track);
+  rd_bool (*read_toc)(struct cdrom_interface * cdrom, uint8_t * buf, int* length, rd_bool msf, int start_track);
   
   /* 
    * Return CD-ROM capacity (in 2048 byte frames)
    */
-  uint32 (*capacity)(struct cdrom_interface *cdrom);
+  uint32_t (*capacity)(struct cdrom_interface *cdrom);
   
   /*
    * Read a single block from the CD
    */
-  void (*read_block)(struct cdrom_interface *cdrom, uint8* buf, int lba);
+  void (*read_block)(struct cdrom_interface *cdrom, uint8_t* buf, int lba);
   
   /*
    * Start (spin up) the CD.
index b5b7d66..77eb44d 100644 (file)
@@ -7,12 +7,14 @@
 #ifndef __DEVICES_RAMDISK_H_
 #define __DEVICES_RAMDISK_H_
 
-#include <stddef.h> //for off_t in C99
-#include <sys/types.h> //for size_t 
-#include <geekos/ktypes.h>
-#include <devices/cdrom.h>
+#include <palacios/vmm_types.h>
 #include <palacios/vm_dev.h>
 
+
+
+
+
+
 struct vm_device * create_ramdisk(void);
 
 #endif
index cfcd7bb..61d7f52 100644 (file)
@@ -56,7 +56,7 @@ void cdrom_eject(struct cdrom_interface *cdrom)
  * Read CD TOC. Returns false if start track is out of bounds.
  */
 static
-rd_bool cdrom_read_toc(struct cdrom_interface *cdrom, uint8* buf, int* length, rd_bool msf, int start_track)
+rd_bool cdrom_read_toc(struct cdrom_interface *cdrom, uint8_t* buf, int* length, rd_bool msf, int start_track)
 {
   Ramdisk_Print_CD("[cdrom_read_toc]\n");
   return 1;
@@ -66,7 +66,7 @@ rd_bool cdrom_read_toc(struct cdrom_interface *cdrom, uint8* buf, int* length, r
  * Return CD-ROM capacity (in 2048 byte frames)
  */
 static
-uint32 cdrom_capacity(struct cdrom_interface *cdrom)
+uint32_t cdrom_capacity(struct cdrom_interface *cdrom)
 {
   Ramdisk_Print_CD("[cdrom_capacity] s_ramdiskSize = %d\n", cdrom->capacity_B);
   if (cdrom->lba) {
@@ -87,13 +87,13 @@ uint32 cdrom_capacity(struct cdrom_interface *cdrom)
  * Read a single block from the CD
  */
 static
-void cdrom_read_block(struct cdrom_interface *cdrom, uint8* buf, int lba)// __attribute__(regparm(2));
+void cdrom_read_block(struct cdrom_interface *cdrom, uint8_t* buf, int lba)// __attribute__(regparm(2));
 {
 
   V3_ASSERT(lba != 0);
     
   Ramdisk_Print_CD("[cdrom_read_block] lba = %d\n", lba);
-  memcpy(buf, (uint8 *)(cdrom->fd + lba*2048), 2048);
+  memcpy(buf, (uint8_t *)(cdrom->fd + lba*2048), 2048);
     return;
 }
 
index 3e21f7c..4324c15 100644 (file)
@@ -6,7 +6,9 @@
 
 #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)
@@ -26,6 +28,9 @@
 
 
 
+
+
+
 /*
  * Data type definitions
  *
@@ -33,7 +38,6 @@
 #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
@@ -78,194 +100,9 @@ ssize_t rd_read (void* buf, size_t count);
 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;
 };
 
 
@@ -300,405 +137,318 @@ struct  ramdisk_t {
 #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
@@ -706,23 +456,18 @@ Bit32u rd_init_harddrive(struct ramdisk_t *ramdisk,
     }//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;
 }
 
@@ -730,1141 +475,1606 @@ void   rd_close_harddrive(struct ramdisk_t *ramdisk)
 ////////////////////////////////////////////////////////////////////
 
 
-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;
@@ -1872,112 +2082,322 @@ void rd_identify_ATAPI_drive(struct channel_t *channels, Bit8u channel)
 
 
 
+
+
+
+
 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:
@@ -2083,355 +2503,33 @@ static void trace_info(ushort_t port, void *src, uint_t length)
 }
 
 
-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
index 7bfb8aa..69acef5 100644 (file)
  */
 struct Page* g_pageList;
 
-#ifdef RAMDISK_BOOT
 ulong_t g_ramdiskImage;
 ulong_t s_ramdiskSize;
-#endif
+
 
 /*
  * Number of pages currently available on the freelist.
@@ -142,13 +141,12 @@ void Init_Mem(struct Boot_Info* bootInfo)
     ulong_t heapEnd;
     ulong_t vmmMemEnd;
 
-    /*Zheng 08/03/2008*/    
-#ifdef RAMDISK_BOOT
+
     g_ramdiskImage = bootInfo->ramdisk_image;
     s_ramdiskSize = bootInfo->ramdisk_size;
     ulong_t initrdAddr;
     ulong_t initrdEnd;
-#endif
+
     
 
     KASSERT(bootInfo->memSizeKB > 0);
@@ -204,7 +202,6 @@ void Init_Mem(struct Boot_Info* bootInfo)
     /* ** */
     vmmMemEnd = Round_Up_To_Page(pageListEnd + VMM_AVAIL_MEM_SIZE);
 
-#ifdef RAMDISK_BOOT
     /*
      * Zheng 08/03/2008
      * copy the ramdisk to this area 
@@ -216,23 +213,23 @@ void Init_Mem(struct Boot_Info* bootInfo)
     memcpy((ulong_t *)initrdAddr, (ulong_t *)g_ramdiskImage, s_ramdiskSize);
     PrintBoth(" done\n");
     PrintBoth("mem.c(%d) Set 0 to unused bytes in the last ramdisk page from %x to %x", __LINE__, initrdAddr+s_ramdiskSize, initrdEnd);
-    memset((ulong_t *)initrdAddr+s_ramdiskSize, 0, initrdEnd-(initrdAddr+s_ramdiskSize));
+    memset((ulong_t *)initrdAddr + s_ramdiskSize, 0, initrdEnd - (initrdAddr + s_ramdiskSize));
     PrintBoth(" done\n");
-    /*
-     * Zheng 08/03/2008
-     */
-    vm_range_start = initrdEnd;
-    vm_range_end = endOfMem;
-#else
+
+
     
     /* 
      *  the disgusting way to get at the memory assigned to a VM
      */
     
-    vm_range_start = vmmMemEnd;
-    vm_range_end = endOfMem;
-    
-#endif
+    //vm_range_start = vmmMemEnd;
+    //vm_range_end = endOfMem;
+    /*
+     * Zheng 08/03/2008
+     */
+    vm_range_start = initrdEnd;
+    vm_range_end = endOfMem;    
+
 
     Add_Page_Range(0, PAGE_SIZE, PAGE_UNUSED);                        // BIOS area
     Add_Page_Range(PAGE_SIZE, PAGE_SIZE * 3, PAGE_ALLOCATED);         // Intial kernel thread obj + stack
index 1dc5996..17ca9e4 100644 (file)
@@ -177,12 +177,15 @@ int config_guest(struct guest_info * info, void * config_ptr) {
     struct vm_device * generic = NULL;
     //Zheng 09/29/2008
 
+
     if (use_ramdisk) {
+      PrintDebug("Creating Ramdisk\n");
       ramdisk = create_ramdisk();
     }
     
     
     if (use_generic) {
+      PrintDebug("Creating Generic Device\n");
       generic = create_generic();
       
       // Make the DMA controller invisible