]> git.sesse.net Git - pistorm/commitdiff
added, not yet functioning, IDE emulation
authorClaude <claude.schwarz@gmail.com>
Sun, 1 Nov 2020 18:21:15 +0000 (19:21 +0100)
committerClaude <claude.schwarz@gmail.com>
Sun, 1 Nov 2020 18:21:15 +0000 (19:21 +0100)
Gayle.c [new file with mode: 0644]
Gayle.h [new file with mode: 0644]
ide.c [new file with mode: 0644]
ide.h [new file with mode: 0644]
ide/.hd0.img.swp [new file with mode: 0644]
ide/ide.c [new file with mode: 0644]
ide/ide.h [new file with mode: 0644]
ide/makedisk [new file with mode: 0755]
ide/makedisk.c [new file with mode: 0644]

diff --git a/Gayle.c b/Gayle.c
new file mode 100644 (file)
index 0000000..5c16da3
--- /dev/null
+++ b/Gayle.c
@@ -0,0 +1,191 @@
+//
+//  Gayle.c
+//  Omega
+//
+//  Created by Matt Parsons on 06/03/2019.
+//  Copyright © 2019 Matt Parsons. All rights reserved.
+//
+
+//Write Byte to Gayle Space 0xda9000 (0x0000c3)
+//Read Byte From Gayle Space 0xda9000
+//Read Byte From Gayle Space 0xdaa000
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "Gayle.h"
+#include "ide.h"
+
+#define CLOCKBASE 0xDC0000
+
+#define GSTATUS 0xda201c 
+#define GCLOW   0xda2010
+#define GDH    0xda2018
+
+
+
+//Write Byte to Gayle Space 0xda2018 (0x000000)
+//Read Byte From Gayle Space 0xda2010
+//Read Byte From Gayle Space 0xda201c
+//Write Byte to Gayle Space 0xdaa000 (0x00002c)
+//Write Byte to Gayle Space 0xda8000 (0x000000)
+//Write Byte to Gayle Space 0xda2018 (0x000000)
+//Write Byte to Gayle Space 0xda2010 (0x000012)
+
+
+
+/*
+Write Byte to Gayle Space 0xda3018 (0x000000)
+
+Read Byte from Gayle Ident 0xde1000 (0x000004)
+Write ide_dev_head: 0x0000a0
+Write Byte to Gayle Space 0xda2018 (0x0000a0)
+Write ide_cyl_low: 0x000012
+Write Byte to Gayle Space 0xda2010 (0x000012)
+Write ide_cyl_low: 0x000034
+Write Byte to Gayle Space 0xda2010 (0x000034)
+Write Byte to Gayle Space 0xda3018 (0x000000)
+Write ide_status_r: 0x000010
+Write Byte to Gayle Space 0xda201c (0x000010)
+*/
+
+
+int counter;
+static struct ide_controller *ide0;
+int fd;
+
+void InitGayle(void){
+   ide0 = ide_allocate("cf");
+   fd = open("hd0.img", O_RDWR);
+   if (fd == -1){
+        printf("HDD Image hd0.image failed open\n");
+   }else{
+        ide_attach(ide0, 0, fd);
+        ide_reset_begin(ide0);
+        printf("HDD Image hd0.image attached\n");
+   }
+}
+
+uint8_t CheckIrq(void){
+uint8_t irq;
+
+       irq = ide0->drive->intrq;
+//     if (irq==0)
+//     printf("IDE IRQ: 0\n");
+
+return irq;
+}
+
+void writeGayleB(unsigned int address, unsigned int value){
+
+
+    if (address == GSTATUS) {
+       ide_write8(ide0, ide_status_r, value);
+//     printf("Write ide_status_r: 0x%06x IRQ:0x%06x\n",value, ide0->drive->intrq);
+       return;
+       }
+
+   if (address == GDH) {
+        ide_write8(ide0, ide_dev_head, value);
+//     printf("Write ide_dev_head: 0x%06x\n",value);
+        return;
+       }
+
+    if (address == GCLOW) {
+        ide_write8(ide0, ide_cyl_low, value);
+//        printf("Write ide_cyl_low: 0x%06x\n",value);
+       return;
+        }
+
+
+    if (address == 0xDE1000){
+        counter = 0;
+//      printf("Write Byte to Gayle Ident 0x%06x (0x%06x)\n",address,value);
+       return;
+       }
+
+    if (address == 0xda9000){
+       return;
+       }
+
+      if (address == 0xda8000){
+        return;
+        }
+
+      if (address == 0xdaa000){
+        return;
+        }
+
+      if (address == 0xdab000){
+        return;
+        }
+
+
+
+
+
+    printf("Write Byte to Gayle Space 0x%06x (0x%06x)\n",address,value);
+}
+
+void writeGayle(unsigned int address, unsigned int value){
+//    printf("Write to Gayle Space 0x%06x (0x%06x)\n",address,value);
+}
+
+void writeGayleL(unsigned int address, unsigned int value){
+//    printf("Write Long to Gayle Space 0x%06x (0x%06x)\n",address,value);
+}
+
+uint8_t readGayleB(unsigned int address){
+
+       if (address == GSTATUS) {
+//     printf("Read ide_status_r\n");
+        return ide_read8(ide0, ide_status_r);
+        }
+
+       if (address == GCLOW) {
+//     printf("Read ide_cyl_low\n");
+        return ide_read8(ide0, ide_cyl_low);
+        }
+
+       if (address == GDH) {
+//     printf("Read ide_dev_head\n");
+        return ide_read8(ide0, ide_dev_head);
+        }
+
+
+    if (address == 0xDE1000){
+        counter++;
+//     printf("Read Byte from Gayle Ident 0x%06x (0x%06x)\n",address,counter);
+
+       if (counter == 3){ 
+//             printf("Gayle Ident cycle\n");
+               return 0xFF;//7F; to enable gayle
+       }else{
+               return 0xFF;
+       }
+
+        }
+
+      if (address == 0xda9000){
+        return 0;
+        }
+
+      if (address == 0xdaa000){
+        return 0;
+       }
+
+    printf("Read Byte From Gayle Space 0x%06x\n",address);
+    return 0xFF;
+}
+
+uint16_t readGayle(unsigned int address){
+    printf("Read From Gayle Space 0x%06x\n",address);
+    return 0x8000;
+}
+
+uint32_t readGayleL(unsigned int address){
+    printf("Read Long From Gayle Space 0x%06x\n",address);
+    return 0x8000;
+}
diff --git a/Gayle.h b/Gayle.h
new file mode 100644 (file)
index 0000000..ffcab20
--- /dev/null
+++ b/Gayle.h
@@ -0,0 +1,26 @@
+//
+//  Gayle.h
+//  Omega
+//
+//  Created by Matt Parsons on 06/03/2019.
+//  Copyright © 2019 Matt Parsons. All rights reserved.
+//
+
+#ifndef Gayle_h
+#define Gayle_h
+
+#include <stdio.h>
+#include <stdint.h>
+
+
+
+
+uint8_t CheckIrq(void);
+void InitGayle(void);
+void writeGayleB(unsigned int address, unsigned value);
+void writeGayle(unsigned int address, unsigned value);
+void writeGayleL(unsigned int address, unsigned value);
+uint8_t readGayleB(unsigned int address);
+uint16_t readGayle(unsigned int address);
+uint32_t readGayleL(unsigned int address);
+#endif /* Gayle_h */
diff --git a/ide.c b/ide.c
new file mode 100644 (file)
index 0000000..36b7d0a
--- /dev/null
+++ b/ide.c
@@ -0,0 +1,934 @@
+/*
+ *     IDE Emulation Layer for retro-style PIO interfaces
+ *
+ *     (c) Copyright Alan Cox, 2015-2019
+ *
+ *     IDE-emu is free software: you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation, either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     IDE-emu is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with IDE-emu.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include "ide.h"
+
+#define IDE_IDLE       0
+#define IDE_CMD                1
+#define IDE_DATA_IN    2
+#define IDE_DATA_OUT   3
+#define DCR_NIEN       2
+#define DCR_SRST       4
+
+#define DEVH_HEAD      15
+#define DEVH_DEV       16
+#define DEVH_LBA       64
+
+#define ERR_AMNF       1
+#define ERR_TKNONF     2
+#define ERR_ABRT       4
+#define ERR_MCR                8
+#define ERR_IDNF       16
+#define        ERR_MC          32
+#define ERR_UNC                64
+
+#define ST_ERR         1
+#define ST_IDX         2
+#define ST_CORR                4
+#define ST_DRQ         8
+#define ST_DSC         16
+#define ST_DF          32
+#define ST_DRDY                64
+#define ST_BSY         128
+
+#define DCL_SRST       4
+#define DCL_NIEN       2
+
+#define IDE_CMD_CALIB          0x10
+#define IDE_CMD_READ           0x20
+#define IDE_CMD_READ_NR                0x21
+#define IDE_CMD_WRITE          0x30
+#define IDE_CMD_WRITE_NR       0x31
+#define IDE_CMD_VERIFY         0x40
+#define IDE_CMD_VERIFY_NR      0x41
+#define IDE_CMD_SEEK           0x70
+#define IDE_CMD_EDD            0x90
+#define IDE_CMD_INTPARAMS      0x91
+#define IDE_CMD_IDENTIFY       0xEC
+#define IDE_CMD_SETFEATURES    0xEF
+
+const uint8_t ide_magic[8] = {
+  '1','D','E','D','1','5','C','0'
+};
+
+static char *charmap(uint8_t v)
+{
+  static char cbuf[3];
+  if (v < 32)
+    sprintf(cbuf, "^%c", '@'+v);
+  else if (v < 127)
+    sprintf(cbuf, " %c", v);
+  else if (v == 127)
+    sprintf(cbuf, "DL");
+  else if (v < 160)
+    sprintf(cbuf, ":%c", '@' + v - 128);
+  else if (v < 255)
+    sprintf(cbuf, "~%c", v - 128);
+  else
+    sprintf(cbuf, "!D");
+  return cbuf;
+}
+
+static void hexdump(uint8_t *bp)
+{
+  int i,j;
+  for (i = 0; i < 512; i+= 16) {
+    for(j = 0; j < 16; j++)
+      fprintf(stderr, "%02X ", bp[i+j]);
+    fprintf(stderr, "|");
+    for(j = 0; j < 16; j++)
+      fprintf(stderr, "%2s", charmap(bp[i+j]));
+    fprintf(stderr, "\n");
+  }
+}
+
+/* FIXME: use proper endian convertors! */
+static uint16_t le16(uint16_t v)
+{
+  uint8_t *p = (uint8_t *)&v;
+  return p[0] | (p[1] << 8);
+}
+
+static void ide_xlate_errno(struct ide_taskfile *t, int len)
+{
+  t->status |= ST_ERR;
+  if (len == -1) {
+    if (errno == EIO)
+      t->error = ERR_UNC;
+    else
+      t->error = ERR_AMNF;
+  } else
+    t->error = ERR_AMNF;
+}
+
+static void ide_fault(struct ide_drive *d, const char *p)
+{
+  fprintf(stderr, "ide: %s: %d: %s\n", d->controller->name,
+                       (int)(d - d->controller->drive), p);
+}
+
+/* Disk translation */
+static off_t xlate_block(struct ide_taskfile *t)
+{
+  struct ide_drive *d = t->drive;
+  uint16_t cyl;
+
+  if (t->lba4 & DEVH_LBA) {
+/*    fprintf(stderr, "XLATE LBA %02X:%02X:%02X:%02X\n", 
+      t->lba4, t->lba3, t->lba2, t->lba1);*/
+    if (d->lba)
+      return 2 + (((t->lba4 & DEVH_HEAD) << 24) | (t->lba3 << 16) | (t->lba2 << 8) | t->lba1);
+    ide_fault(d, "LBA on non LBA drive");
+  }
+
+  /* Some well known software asks for 0/0/0 when it means 0/0/1. Drives appear
+     to interpret sector 0 as sector 1 */
+  if (t->lba1 == 0) {
+    fprintf(stderr, "[Bug: request for sector offset 0].\n");
+    t->lba1 = 1;
+  }
+  cyl = (t->lba3 << 8) | t->lba2;
+  /* fprintf(stderr, "(H %d C %d S %d)\n", t->lba4 & DEVH_HEAD, cyl, t->lba1); */
+  if (t->lba1 == 0 || t->lba1 > d->sectors || t->lba4 >= d->heads || cyl >= d->cylinders) {
+    return -1;
+  }
+  /* Sector 1 is first */
+  /* Images generally go cylinder/head/sector. This also matters if we ever
+     implement more advanced geometry setting */
+  return 1 + ((cyl * d->heads) + (t->lba4 & DEVH_HEAD)) * d->sectors + t->lba1;
+}
+
+/* Indicate the drive is ready */
+static void ready(struct ide_taskfile *tf)
+{
+  tf->status &= ~(ST_BSY|ST_DRQ);
+  tf->status |= ST_DRDY;
+  tf->drive->state = IDE_IDLE;
+}
+
+/* Return to idle state, completing a command */
+static void completed(struct ide_taskfile *tf)
+{
+  ready(tf);
+  tf->drive->intrq = 1;
+}
+
+static void drive_failed(struct ide_taskfile *tf)
+{
+  tf->status |= ST_ERR;
+  tf->error = ERR_IDNF;
+  ready(tf);
+}
+
+static void data_in_state(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  d->state = IDE_DATA_IN;
+  d->dptr = d->data + 512;
+  /* We don't clear DRDY here, drives may well accept a command at this
+     point and at least one firmware for RC2014 assumes this */
+  tf->status &= ~ST_BSY;
+  tf->status |= ST_DRQ;
+  d->intrq = 1;                        /* Double check */
+}
+
+static void data_out_state(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  d->state = IDE_DATA_OUT;
+  d->dptr = d->data;
+  tf->status &= ~ (ST_BSY|ST_DRDY);
+  tf->status |= ST_DRQ;
+  d->intrq = 1;                        /* Double check */
+}
+
+static void edd_setup(struct ide_taskfile *tf)
+{
+  tf->error = 0x01;            /* All good */
+  tf->lba1 = 0x01;             /* EDD always updates drive 0 */
+  tf->lba2 = 0x00;
+  tf->lba3 = 0x00;
+  tf->lba4 = 0x00;
+  tf->count = 0x01;
+  ready(tf);
+}
+
+void ide_reset(struct ide_controller *c)
+{
+  if (c->drive[0].present) {
+    edd_setup(&c->drive[0].taskfile);
+    /* A drive could clear busy then set DRDY up to 2 minutes later if its
+       mindnumbingly slow to start up ! We don't emulate any of that */
+    c->drive[0].taskfile.status = ST_DRDY;
+    c->drive[0].eightbit = 0;
+  }
+  if (c->drive[1].present) {
+    edd_setup(&c->drive[1].taskfile);
+    c->drive[1].taskfile.status = ST_DRDY;
+    c->drive[1].eightbit = 0;
+  }
+  c->selected = 0;
+}
+
+void ide_reset_begin(struct ide_controller *c)
+{
+  if (c->drive[0].present)
+    c->drive[0].taskfile.status |= ST_BSY;
+  if (c->drive[1].present)
+    c->drive[1].taskfile.status |= ST_BSY;
+  /* Ought to be a time delay relative to reset or power on */
+  ide_reset(c);
+}
+
+static void ide_srst_begin(struct ide_controller *c)
+{
+  ide_reset(c);
+  if (c->drive[0].present)
+    c->drive[0].taskfile.status |= ST_BSY;
+  if (c->drive[1].present)
+    c->drive[1].taskfile.status |= ST_BSY;
+}  
+
+static void ide_srst_end(struct ide_controller *c)
+{
+  /* Could be time delays here */
+  ready(&c->drive[0].taskfile);
+  ready(&c->drive[1].taskfile);
+}
+
+static void cmd_edd_complete(struct ide_taskfile *tf)
+{
+  struct ide_controller *c = tf->drive->controller;
+  if (c->drive[0].present)
+    edd_setup(&c->drive[0].taskfile);
+  if (c->drive[1].present)
+    edd_setup(&c->drive[1].taskfile);
+  c->selected = 0;
+}
+
+static void cmd_identify_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  memcpy(d->data, d->identify, 512);
+  data_in_state(tf);
+  /* Arrange to copy just the identify buffer */
+  d->dptr = d->data;
+  d->length = 1;
+}
+
+static void cmd_initparam_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  /* We only support the current mapping */
+  if (tf->count != d->sectors || (tf->lba4 & DEVH_HEAD) + 1 != d->heads) {
+    tf->status |= ST_ERR;
+    tf->error |= ERR_ABRT;
+    tf->drive->failed = 1;             /* Report ID NF until fixed */
+/*    fprintf(stderr, "geo is %d %d, asked for %d %d\n",
+      d->sectors, d->heads, tf->count, (tf->lba4 & DEVH_HEAD) + 1); */
+    ide_fault(d, "invalid geometry");
+  } else if (tf->drive->failed == 1)
+    tf->drive->failed = 0;             /* Valid translation */
+  completed(tf);
+}
+
+static void cmd_readsectors_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  /* Move to data xfer */
+  if (d->failed) {
+    drive_failed(tf);
+    return;
+  }
+  d->offset = xlate_block(tf);
+  /* DRDY is not guaranteed here but at least one buggy RC2014 firmware
+     expects it */
+  tf->status |= ST_DRQ | ST_DSC | ST_DRDY;
+  tf->status &= ~ST_BSY;
+  /* 0 = 256 sectors */
+  d->length = tf->count ? tf->count : 256;
+  /* fprintf(stderr, "READ %d SECTORS @ %ld\n", d->length, d->offset); */
+  if (d->offset == -1 ||  lseek(d->fd, 512 * d->offset, SEEK_SET) == -1) {
+    tf->status |= ST_ERR;
+    tf->status &= ~ST_DSC;
+    tf->error |= ERR_IDNF;
+    /* return null data */
+    completed(tf);
+    return;
+  }
+  /* do the xfer */
+  data_in_state(tf);
+}
+
+static void cmd_verifysectors_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  /* Move to data xfer */
+  if (d->failed) {
+    drive_failed(tf);
+    return;
+  }
+  d->offset = xlate_block(tf);
+  /* 0 = 256 sectors */
+  d->length = tf->count ? tf->count : 256;
+  if (d->offset == -1 || lseek(d->fd, 512 * (d->offset + d->length - 1), SEEK_SET) == -1) {
+    tf->status &= ~ST_DSC;
+    tf->status |= ST_ERR;
+    tf->error |= ERR_IDNF;
+  }
+  tf->status |= ST_DSC;
+  completed(tf);
+}
+
+static void cmd_recalibrate_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  if (d->failed)
+    drive_failed(tf);
+  if (d->offset == -1 || xlate_block(tf) != 0L) {
+    tf->status &= ~ST_DSC;
+    tf->status |= ST_ERR;
+    tf->error |= ERR_ABRT;
+  }
+  tf->status |= ST_DSC;
+  completed(tf);
+}
+
+static void cmd_seek_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  if (d->failed)
+    drive_failed(tf);
+  d->offset = xlate_block(tf);
+  if (d->offset == -1 || lseek(d->fd, 512 * d->offset, SEEK_SET) == -1) {
+    tf->status &= ~ST_DSC;
+    tf->status |= ST_ERR;
+    tf->error |= ERR_IDNF;
+  }
+  tf->status |= ST_DSC;
+  completed(tf);
+}
+
+static void cmd_setfeatures_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  switch(tf->feature) {
+    case 0x01:
+      d->eightbit = 1;
+      break;
+    case 0x03:
+      if ((tf->count & 0xF0) >= 0x20) {
+        tf->status |= ST_ERR;
+        tf->error |= ERR_ABRT;
+      }
+      /* Silently accept PIO mode settings */
+      break;
+    case 0x81:
+      d->eightbit = 0;
+      break;
+    default:
+      tf->status |= ST_ERR;
+      tf->error |= ERR_ABRT;
+  }
+  completed(tf);
+}
+
+static void cmd_writesectors_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  /* Move to data xfer */
+  if (d->failed) {
+    drive_failed(tf);
+    return;
+  }
+  d->offset = xlate_block(tf);
+  tf->status |= ST_DRQ;
+  /* 0 = 256 sectors */
+  d->length = tf->count ? tf->count : 256;
+/*  fprintf(stderr, "WRITE %d SECTORS @ %ld\n", d->length, d->offset); */
+  if (d->offset == -1 ||  lseek(d->fd, 512 * d->offset, SEEK_SET) == -1) {
+    tf->status |= ST_ERR;
+    tf->error |= ERR_IDNF;
+    tf->status &= ~ST_DSC;
+    /* return null data */
+    completed(tf);
+    return;
+  }
+  /* do the xfer */
+  data_out_state(tf);
+}
+
+static void ide_set_error(struct ide_drive *d)
+{
+  d->taskfile.lba4 &= ~DEVH_HEAD;
+
+  if (d->taskfile.lba4 & DEVH_LBA) {
+    d->taskfile.lba1 = d->offset & 0xFF;
+    d->taskfile.lba2 = (d->offset >> 8) & 0xFF;
+    d->taskfile.lba3 = (d->offset >> 16) & 0xFF;
+    d->taskfile.lba4 |= (d->offset >> 24) & DEVH_HEAD;
+  } else {
+    d->taskfile.lba1 = d->offset % d->sectors + 1;
+    d->offset /= d->sectors;
+    d->taskfile.lba4 |= d->offset / (d->cylinders * d->sectors);
+    d->offset %= (d->cylinders * d->sectors);
+    d->taskfile.lba2 = d->offset & 0xFF;
+    d->taskfile.lba3 = (d->offset >> 8) & 0xFF;
+  }
+  d->taskfile.count = d->length;
+  d->taskfile.status |= ST_ERR;
+  d->state = IDE_IDLE;
+  completed(&d->taskfile);
+}
+
+static int ide_read_sector(struct ide_drive *d)
+{
+  int len;
+
+  d->dptr = d->data;
+  if ((len = read(d->fd, d->data, 512)) != 512) {
+    perror("ide_read_sector");
+    d->taskfile.status |= ST_ERR;
+    d->taskfile.status &= ~ST_DSC;
+    ide_xlate_errno(&d->taskfile, len);
+    return -1;
+  }
+//  hexdump(d->data);
+  d->offset += 512;
+  return 0;
+}
+
+static int ide_write_sector(struct ide_drive *d)
+{
+  int len;
+
+  d->dptr = d->data;
+  if ((len = write(d->fd, d->data, 512)) != 512) {
+    d->taskfile.status |= ST_ERR;
+    d->taskfile.status &= ~ST_DSC;
+    ide_xlate_errno(&d->taskfile, len);
+    return -1;
+  }
+//  hexdump(d->data);
+  d->offset += 512;
+  return 0;
+}
+
+static uint16_t ide_data_in(struct ide_drive *d, int len)
+{
+  uint16_t v;
+  if (d->state == IDE_DATA_IN) {
+    if (d->dptr == d->data + 512) {
+      if (ide_read_sector(d) < 0) {
+        ide_set_error(d);      /* Set the LBA or CHS etc */
+        return 0xFFFF;         /* and error bits set by read_sector */
+      }
+    }
+    v = *d->dptr;
+    if (!d->eightbit) {
+      if (len == 2)
+        v |= (d->dptr[1] << 8);
+      d->dptr+=2;
+    } else
+      d->dptr++;
+    d->taskfile.data = v;
+    if (d->dptr == d->data + 512) {
+      d->length--;
+      d->intrq = 1;            /* we don't yet emulate multimode */
+      if (d->length == 0) {
+        d->state = IDE_IDLE;
+        completed(&d->taskfile);
+      }
+    }
+  } else
+    ide_fault(d, "bad data read");
+
+  if (len == 1)
+    return d->taskfile.data & 0xFF;
+  return d->taskfile.data;
+}
+
+static void ide_data_out(struct ide_drive *d, uint16_t v, int len)
+{
+  if (d->state != IDE_DATA_OUT) {
+    ide_fault(d, "bad data write");
+    d->taskfile.data = v;
+  } else {
+    if (d->eightbit)
+      v &= 0xFF;
+    *d->dptr++ = v;
+    d->taskfile.data = v;
+    if (!d->eightbit) {
+      *d->dptr++ = v >> 8;
+      d->taskfile.data = v >> 8;
+    }
+    if (d->dptr == d->data + 512) {
+      if (ide_write_sector(d) < 0) {
+        ide_set_error(d);
+        return;        
+      }
+      d->length--;
+      d->intrq = 1;
+      if (d->length == 0) {
+        d->state = IDE_IDLE;
+        d->taskfile.status |= ST_DSC;
+        completed(&d->taskfile);
+      }
+    }
+  }
+}
+
+static void ide_issue_command(struct ide_taskfile *t)
+{
+  t->status &= ~(ST_ERR|ST_DRDY);
+  t->status |= ST_BSY;
+  t->error = 0;
+  t->drive->state = IDE_CMD;
+  
+  /* We could complete with delays but don't do so yet */
+  switch(t->command) {
+    case IDE_CMD_EDD:  /* 0x90 */
+      cmd_edd_complete(t);
+      break;
+    case IDE_CMD_IDENTIFY:     /* 0xEC */
+      cmd_identify_complete(t);
+      break;
+    case IDE_CMD_INTPARAMS:    /* 0x91 */
+      cmd_initparam_complete(t);
+      break;
+    case IDE_CMD_READ:         /* 0x20 */
+    case IDE_CMD_READ_NR:      /* 0x21 */
+      cmd_readsectors_complete(t);
+      break;
+    case IDE_CMD_SETFEATURES:  /* 0xEF */
+      cmd_setfeatures_complete(t);
+      break;
+    case IDE_CMD_VERIFY:       /* 0x40 */
+    case IDE_CMD_VERIFY_NR:    /* 0x41 */
+      cmd_verifysectors_complete(t);
+      break;
+    case IDE_CMD_WRITE:                /* 0x30 */
+    case IDE_CMD_WRITE_NR:     /* 0x31 */
+      cmd_writesectors_complete(t);
+      break;
+    default:
+      if ((t->command & 0xF0) == IDE_CMD_CALIB)        /* 1x */
+        cmd_recalibrate_complete(t);
+      else if ((t->command & 0xF0) == IDE_CMD_SEEK) /* 7x */
+        cmd_seek_complete(t);
+      else {
+        /* Unknown */
+        t->status |= ST_ERR;
+        t->error |= ERR_ABRT;
+        completed(t);
+      }
+  }
+}
+
+/*
+ *     8bit IDE controller emulation
+ */
+
+uint8_t ide_read8(struct ide_controller *c, uint8_t r)
+{
+  struct ide_drive *d = &c->drive[c->selected];
+  struct ide_taskfile *t = &d->taskfile;
+  switch(r) {
+    case ide_data:
+      return ide_data_in(d, 1);
+    case ide_error_r:
+      return t->error;
+    case ide_sec_count:
+      return t->count;
+    case ide_lba_low:
+      return t->lba1;
+    case ide_lba_mid:
+      return t->lba2;
+    case ide_lba_hi:
+      return t->lba3;
+    case ide_lba_top:
+      return t->lba4;
+    case ide_status_r:
+      d->intrq = 0;            /* Acked */
+    case ide_altst_r:
+      return t->status;
+    default:
+      ide_fault(d, "bogus register");
+      return 0xFF;
+  }
+}
+
+void ide_write8(struct ide_controller *c, uint8_t r, uint8_t v)
+{
+  struct ide_drive *d = &c->drive[c->selected];
+  struct ide_taskfile *t = &d->taskfile;
+
+  if (r != ide_devctrl_w) {
+    if (t->status & ST_BSY) {
+      ide_fault(d, "command written while busy");
+      return;
+    }
+    /* Not clear this is the right emulation */
+    if (d->present == 0 && r != ide_lba_top) {
+      ide_fault(d, "not present");
+      return;
+    }
+  }
+
+  switch(r) {
+    case ide_data:
+      ide_data_out(d, v, 1);
+      break;
+    case ide_feature_w:
+      t->feature = v;
+      break;
+    case ide_sec_count:
+      t->count = v;
+      break;
+    case ide_lba_low:
+      t->lba1 = v;
+      break;
+    case ide_lba_mid:
+      t->lba2 = v;
+      break;
+    case ide_lba_hi:
+      t->lba3 = v;
+      break;
+    case ide_lba_top:
+      c->selected = (v & DEVH_DEV) ? 1 : 0;
+      c->drive[c->selected].taskfile.lba4 = v & (DEVH_HEAD|DEVH_DEV|DEVH_LBA);
+      break;
+    case ide_command_w:
+      t->command = v; 
+      ide_issue_command(t);
+      break;
+    case ide_devctrl_w:
+      /* ATA: "When the Device Control register is written, both devices
+         respond to the write regardless of which device is selected" */
+      if ((v ^ t->devctrl) & DCL_SRST) {
+        if (v & DCL_SRST)
+          ide_srst_begin(c);
+        else
+          ide_srst_end(c);
+      }
+      c->drive[0].taskfile.devctrl = v;        /* Check versus real h/w does this end up cleared */
+      c->drive[1].taskfile.devctrl = v;
+      break;
+  }
+}
+
+/*
+ *     16bit IDE controller emulation
+ */
+
+uint16_t ide_read16(struct ide_controller *c, uint8_t r)
+{
+  struct ide_drive *d = &c->drive[c->selected];
+  if (r == ide_data)
+    return htons(ide_data_in(d,2));
+  return ide_read8(c, r);
+}
+
+void ide_write16(struct ide_controller *c, uint8_t r, uint16_t v)
+{
+  struct ide_drive *d = &c->drive[c->selected];
+  struct ide_taskfile *t = &d->taskfile;
+
+  if (r != ide_devctrl_w && (t->status & ST_BSY)) {
+    ide_fault(d, "command written while busy");
+    return;
+  }
+  if (r == ide_data)
+    ide_data_out(d, ntohs(v), 2);
+  else
+    ide_write8(c, r, v);
+}
+
+/*
+ *     Allocate a new IDE controller emulation
+ */
+struct ide_controller *ide_allocate(const char *name)
+{
+  struct ide_controller *c = calloc(1, sizeof(*c));
+  if (c == NULL)
+    return NULL;
+  c->name = strdup(name);
+  if (c->name == NULL) {
+    free(c);
+    return NULL;
+  }
+  c->drive[0].controller = c;
+  c->drive[1].controller = c;
+  c->drive[0].taskfile.drive = &c->drive[0];
+  c->drive[1].taskfile.drive = &c->drive[1];
+  return c;
+}
+
+/*
+ *     Attach a file to a device on the controller
+ */
+int ide_attach(struct ide_controller *c, int drive, int fd)
+{
+  struct ide_drive *d = &c->drive[drive];
+  if (d->present) {
+    ide_fault(d, "double attach");
+    return -1;
+  }
+  d->fd = fd;
+  if (read(d->fd, d->data, 512) != 512 ||
+      read(d->fd, d->identify, 512) != 512) {
+    ide_fault(d, "i/o error on attach");
+    return -1;
+  }
+  if (memcmp(d->data, ide_magic, 8)) {
+    ide_fault(d, "bad magic");
+    return -1;
+  }
+  d->fd = fd;
+  d->present = 1;
+  d->heads = d->identify[3];
+  d->sectors = d->identify[6];
+  d->cylinders = le16(d->identify[1]);
+  if (d->identify[49] & le16(1 << 9))
+    d->lba = 1;
+  else
+    d->lba = 0;
+  return 0;
+}
+
+/*
+ *     Detach an IDE device from the interface (not hot pluggable)
+ */
+void ide_detach(struct ide_drive *d)
+{
+  close(d->fd);
+  d->fd = -1;
+  d->present = 0;
+}
+
+/*
+ *     Free up and release and IDE controller
+ */  
+void ide_free(struct ide_controller *c)
+{
+  if (c->drive[0].present)
+    ide_detach(&c->drive[0]);
+  if (c->drive[1].present)
+    ide_detach(&c->drive[1]);
+  free((void *)c->name);
+  free(c);
+}
+
+/*
+ *     Emulation interface for an 8bit controller using latches on the
+ *     data register
+ */
+uint8_t ide_read_latched(struct ide_controller *c, uint8_t reg)
+{
+  uint16_t v;
+  if (reg == ide_data_latch)
+    return c->data_latch;
+  v = ide_read16(c, reg);
+  if (reg == ide_data) {
+    c->data_latch = v >> 8;
+    v &= 0xFF;
+  }
+  return v;
+}
+
+void ide_write_latched(struct ide_controller *c, uint8_t reg, uint8_t v)
+{
+  uint16_t d = v;
+
+  if (reg == ide_data_latch) {
+    c->data_latch = v;
+    return;
+  }
+  if (reg == ide_data)
+    d |=  (c->data_latch << 8);
+  ide_write16(c, reg, d);  
+}
+
+static void make_ascii(uint16_t *p, const char *t, int len)
+{
+  int i;
+  char *d = (char *)p;
+  strncpy(d, t, len);
+
+  for (i = 0; i < len; i += 2) {
+    char c = *d;
+    *d = d[1];
+    d[1] = c;
+    d += 2;
+  }  
+}
+
+static void make_serial(uint16_t *p)
+{
+  char buf[21];
+  srand(getpid()^time(NULL));
+  snprintf(buf, 21, "%08d%08d%04d", rand(), rand(), rand());
+  make_ascii(p, buf, 20);
+}
+
+int ide_make_drive(uint8_t type, int fd)
+{
+  uint8_t s, h;
+  uint16_t c;
+  uint32_t sectors;
+  uint16_t ident[256];
+
+  if (type < 1 || type > MAX_DRIVE_TYPE)
+    return -2;
+  
+  memset(ident, 0, 512);
+  memcpy(ident, ide_magic, 8);
+  if (write(fd, ident, 512) != 512)
+    return -1;
+
+  memset(ident, 0, 8);
+  ident[0] = le16((1 << 15) | (1 << 6));       /* Non removable */
+  make_serial(ident + 10);
+  ident[47] = 0; /* no read multi for now */
+  ident[51] = le16(240 /* PIO2 */ << 8);       /* PIO cycle time */
+  ident[53] = le16(1);         /* Geometry words are valid */
+  
+  switch(type) {
+    case ACME_ROADRUNNER:
+      /* 504MB drive with LBA support */
+      c = 1024;
+      h = 16;
+      s = 63;
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME ROADRUNNER v0.1", 40);
+      ident[49] = le16(1 << 9); /* LBA */
+      break;  
+    case ACME_ULTRASONICUS:
+      /* 40MB drive with LBA support */
+      c = 977;
+      h = 5;
+      s = 16;
+      ident[49] = le16(1 << 9); /* LBA */
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME ULTRASONICUS AD INFINITUM v0.1", 40);
+      break;
+    case ACME_NEMESIS:
+      /* 20MB drive with LBA support */
+      c = 615;
+      h = 4;
+      s = 16;
+      ident[49] = le16(1 << 9); /* LBA */
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME NEMESIS RIDICULII v0.1", 40);
+      break;
+    case ACME_COYOTE:
+      /* 20MB drive without LBA support */
+      c = 615;
+      h = 4;
+      s = 16;
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME COYOTE v0.1", 40);
+      break;  
+    case ACME_ACCELLERATTI:
+      c = 1024;
+      h = 16;
+      s = 16;
+      ident[49] = le16(1 << 9); /* LBA */
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME ACCELLERATTI INCREDIBILUS v0.1", 40);
+      break;
+    case ACME_ZIPPIBUS:
+      c = 1024;
+      h = 16;
+      s = 32;
+      ident[49] = le16(1 << 9); /* LBA */
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME ZIPPIBUS v0.1", 40);
+      break;
+  }
+  ident[1] = le16(c);
+  ident[3] = le16(h);
+  ident[6] = le16(s);
+  ident[54] = ident[1];
+  ident[55] = ident[3];
+  ident[56] = ident[6];
+  sectors = c * h * s;
+  ident[57] = le16(sectors & 0xFFFF);
+  ident[58] = le16(sectors >> 16);
+  ident[60] = ident[57];
+  ident[61] = ident[58];
+  if (write(fd, ident, 512) != 512)
+    return -1;
+  
+  memset(ident, 0xE5, 512);
+  while(sectors--)
+    if (write(fd, ident, 512) != 512)
+      return -1;  
+  return 0;
+}
diff --git a/ide.h b/ide.h
new file mode 100644 (file)
index 0000000..0d6549c
--- /dev/null
+++ b/ide.h
@@ -0,0 +1,83 @@
+#include <stdint.h>
+
+#define ACME_ROADRUNNER                1       /* 504MB classic IDE drive */
+#define ACME_COYOTE            2       /* 20MB early IDE drive */
+#define ACME_NEMESIS           3       /* 20MB LBA capable drive */
+#define ACME_ULTRASONICUS      4       /* 40MB LBA capable drive */
+#define ACME_ACCELLERATTI      5       /* 128MB LBA capable drive */
+#define ACME_ZIPPIBUS          6       /* 256MB LBA capable drive */
+
+#define MAX_DRIVE_TYPE         6
+
+#define                ide_data        0
+#define                ide_error_r     1
+#define                ide_feature_w   1
+#define                ide_sec_count   2
+#define                ide_sec_num     3
+#define                ide_lba_low     3
+#define                ide_cyl_low     4
+#define                ide_lba_mid     4
+#define                ide_cyl_hi      5
+#define                ide_lba_hi      5
+#define                ide_dev_head    6
+#define                ide_lba_top     6
+#define                ide_status_r    7
+#define                ide_command_w   7
+#define                ide_altst_r     8
+#define                ide_devctrl_w   8
+#define                ide_data_latch  9
+
+struct ide_taskfile {
+  uint16_t data;
+  uint8_t error;
+  uint8_t feature;
+  uint8_t count;
+  uint8_t lba1;
+  uint8_t lba2;
+  uint8_t lba3;
+  uint8_t lba4;
+  uint8_t status;
+  uint8_t command;
+  uint8_t devctrl;
+  struct ide_drive *drive;
+};
+
+struct ide_drive {
+  struct ide_controller *controller;
+  struct ide_taskfile taskfile;
+  unsigned int present:1, intrq:1, failed:1, lba:1, eightbit:1;
+  uint16_t cylinders;
+  uint8_t heads, sectors;
+  uint8_t data[512];
+  uint16_t identify[256];
+  uint8_t *dptr;
+  int state;
+  int fd;
+  off_t offset;
+  int length;
+};
+
+struct ide_controller {
+  struct ide_drive drive[2];
+  int selected;
+  const char *name;
+  uint16_t data_latch;
+};
+
+//extern ide_controller idectrl;
+extern const uint8_t ide_magic[8];
+
+void ide_reset_begin(struct ide_controller *c);
+uint8_t ide_read8(struct ide_controller *c, uint8_t r);
+void ide_write8(struct ide_controller *c, uint8_t r, uint8_t v);
+uint16_t ide_read16(struct ide_controller *c, uint8_t r);
+void ide_write16(struct ide_controller *c, uint8_t r, uint16_t v);
+uint8_t ide_read_latched(struct ide_controller *c, uint8_t r);
+void ide_write_latched(struct ide_controller *c, uint8_t r, uint8_t v);
+
+struct ide_controller *ide_allocate(const char *name);
+int ide_attach(struct ide_controller *c, int drive, int fd);
+void ide_detach(struct ide_drive *d);
+void ide_free(struct ide_controller *c);
+
+int ide_make_drive(uint8_t type, int fd);
diff --git a/ide/.hd0.img.swp b/ide/.hd0.img.swp
new file mode 100644 (file)
index 0000000..893a777
Binary files /dev/null and b/ide/.hd0.img.swp differ
diff --git a/ide/ide.c b/ide/ide.c
new file mode 100644 (file)
index 0000000..148eb04
--- /dev/null
+++ b/ide/ide.c
@@ -0,0 +1,934 @@
+/*
+ *     IDE Emulation Layer for retro-style PIO interfaces
+ *
+ *     (c) Copyright Alan Cox, 2015-2019
+ *
+ *     IDE-emu is free software: you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation, either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     IDE-emu is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with IDE-emu.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <arpa/inet.h>
+
+#include "ide.h"
+
+#define IDE_IDLE       0
+#define IDE_CMD                1
+#define IDE_DATA_IN    2
+#define IDE_DATA_OUT   3
+  
+#define DCR_NIEN       2
+#define DCR_SRST       4
+
+#define DEVH_HEAD      15
+#define DEVH_DEV       16
+#define DEVH_LBA       64
+
+#define ERR_AMNF       1
+#define ERR_TKNONF     2
+#define ERR_ABRT       4
+#define ERR_MCR                8
+#define ERR_IDNF       16
+#define        ERR_MC          32
+#define ERR_UNC                64
+
+#define ST_ERR         1
+#define ST_IDX         2
+#define ST_CORR                4
+#define ST_DRQ         8
+#define ST_DSC         16
+#define ST_DF          32
+#define ST_DRDY                64
+#define ST_BSY         128
+
+#define DCL_SRST       4
+#define DCL_NIEN       2
+
+#define IDE_CMD_CALIB          0x10
+#define IDE_CMD_READ           0x20
+#define IDE_CMD_READ_NR                0x21
+#define IDE_CMD_WRITE          0x30
+#define IDE_CMD_WRITE_NR       0x31
+#define IDE_CMD_VERIFY         0x40
+#define IDE_CMD_VERIFY_NR      0x41
+#define IDE_CMD_SEEK           0x70
+#define IDE_CMD_EDD            0x90
+#define IDE_CMD_INTPARAMS      0x91
+#define IDE_CMD_IDENTIFY       0xEC
+#define IDE_CMD_SETFEATURES    0xEF
+
+const uint8_t ide_magic[8] = {
+  '1','D','E','D','1','5','C','0'
+};
+
+static char *charmap(uint8_t v)
+{
+  static char cbuf[3];
+  if (v < 32)
+    sprintf(cbuf, "^%c", '@'+v);
+  else if (v < 127)
+    sprintf(cbuf, " %c", v);
+  else if (v == 127)
+    sprintf(cbuf, "DL");
+  else if (v < 160)
+    sprintf(cbuf, ":%c", '@' + v - 128);
+  else if (v < 255)
+    sprintf(cbuf, "~%c", v - 128);
+  else
+    sprintf(cbuf, "!D");
+  return cbuf;
+}
+
+static void hexdump(uint8_t *bp)
+{
+  int i,j;
+  for (i = 0; i < 512; i+= 16) {
+    for(j = 0; j < 16; j++)
+      fprintf(stderr, "%02X ", bp[i+j]);
+    fprintf(stderr, "|");
+    for(j = 0; j < 16; j++)
+      fprintf(stderr, "%2s", charmap(bp[i+j]));
+    fprintf(stderr, "\n");
+  }
+}
+
+/* FIXME: use proper endian convertors! */
+static uint16_t le16(uint16_t v)
+{
+  uint8_t *p = (uint8_t *)&v;
+  return p[0] | (p[1] << 8);
+}
+
+static void ide_xlate_errno(struct ide_taskfile *t, int len)
+{
+  t->status |= ST_ERR;
+  if (len == -1) {
+    if (errno == EIO)
+      t->error = ERR_UNC;
+    else
+      t->error = ERR_AMNF;
+  } else
+    t->error = ERR_AMNF;
+}
+
+static void ide_fault(struct ide_drive *d, const char *p)
+{
+  fprintf(stderr, "ide: %s: %d: %s\n", d->controller->name,
+                       (int)(d - d->controller->drive), p);
+}
+
+/* Disk translation */
+static off_t xlate_block(struct ide_taskfile *t)
+{
+  struct ide_drive *d = t->drive;
+  uint16_t cyl;
+
+  if (t->lba4 & DEVH_LBA) {
+/*    fprintf(stderr, "XLATE LBA %02X:%02X:%02X:%02X\n", 
+      t->lba4, t->lba3, t->lba2, t->lba1);*/
+    if (d->lba)
+      return 2 + (((t->lba4 & DEVH_HEAD) << 24) | (t->lba3 << 16) | (t->lba2 << 8) | t->lba1);
+    ide_fault(d, "LBA on non LBA drive");
+  }
+
+  /* Some well known software asks for 0/0/0 when it means 0/0/1. Drives appear
+     to interpret sector 0 as sector 1 */
+  if (t->lba1 == 0) {
+    fprintf(stderr, "[Bug: request for sector offset 0].\n");
+    t->lba1 = 1;
+  }
+  cyl = (t->lba3 << 8) | t->lba2;
+  /* fprintf(stderr, "(H %d C %d S %d)\n", t->lba4 & DEVH_HEAD, cyl, t->lba1); */
+  if (t->lba1 == 0 || t->lba1 > d->sectors || t->lba4 >= d->heads || cyl >= d->cylinders) {
+    return -1;
+  }
+  /* Sector 1 is first */
+  /* Images generally go cylinder/head/sector. This also matters if we ever
+     implement more advanced geometry setting */
+  return 1 + ((cyl * d->heads) + (t->lba4 & DEVH_HEAD)) * d->sectors + t->lba1;
+}
+
+/* Indicate the drive is ready */
+static void ready(struct ide_taskfile *tf)
+{
+  tf->status &= ~(ST_BSY|ST_DRQ);
+  tf->status |= ST_DRDY;
+  tf->drive->state = IDE_IDLE;
+}
+
+/* Return to idle state, completing a command */
+static void completed(struct ide_taskfile *tf)
+{
+  ready(tf);
+  tf->drive->intrq = 1;
+}
+
+static void drive_failed(struct ide_taskfile *tf)
+{
+  tf->status |= ST_ERR;
+  tf->error = ERR_IDNF;
+  ready(tf);
+}
+
+static void data_in_state(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  d->state = IDE_DATA_IN;
+  d->dptr = d->data + 512;
+  /* We don't clear DRDY here, drives may well accept a command at this
+     point and at least one firmware for RC2014 assumes this */
+  tf->status &= ~ST_BSY;
+  tf->status |= ST_DRQ;
+  d->intrq = 1;                        /* Double check */
+}
+
+static void data_out_state(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  d->state = IDE_DATA_OUT;
+  d->dptr = d->data;
+  tf->status &= ~ (ST_BSY|ST_DRDY);
+  tf->status |= ST_DRQ;
+  d->intrq = 1;                        /* Double check */
+}
+
+static void edd_setup(struct ide_taskfile *tf)
+{
+  tf->error = 0x01;            /* All good */
+  tf->lba1 = 0x01;             /* EDD always updates drive 0 */
+  tf->lba2 = 0x00;
+  tf->lba3 = 0x00;
+  tf->lba4 = 0x00;
+  tf->count = 0x01;
+  ready(tf);
+}
+
+void ide_reset(struct ide_controller *c)
+{
+  if (c->drive[0].present) {
+    edd_setup(&c->drive[0].taskfile);
+    /* A drive could clear busy then set DRDY up to 2 minutes later if its
+       mindnumbingly slow to start up ! We don't emulate any of that */
+    c->drive[0].taskfile.status = ST_DRDY;
+    c->drive[0].eightbit = 0;
+  }
+  if (c->drive[1].present) {
+    edd_setup(&c->drive[1].taskfile);
+    c->drive[1].taskfile.status = ST_DRDY;
+    c->drive[1].eightbit = 0;
+  }
+  c->selected = 0;
+}
+
+void ide_reset_begin(struct ide_controller *c)
+{
+  if (c->drive[0].present)
+    c->drive[0].taskfile.status |= ST_BSY;
+  if (c->drive[1].present)
+    c->drive[1].taskfile.status |= ST_BSY;
+  /* Ought to be a time delay relative to reset or power on */
+  ide_reset(c);
+}
+
+static void ide_srst_begin(struct ide_controller *c)
+{
+  ide_reset(c);
+  if (c->drive[0].present)
+    c->drive[0].taskfile.status |= ST_BSY;
+  if (c->drive[1].present)
+    c->drive[1].taskfile.status |= ST_BSY;
+}  
+
+static void ide_srst_end(struct ide_controller *c)
+{
+  /* Could be time delays here */
+  ready(&c->drive[0].taskfile);
+  ready(&c->drive[1].taskfile);
+}
+
+static void cmd_edd_complete(struct ide_taskfile *tf)
+{
+  struct ide_controller *c = tf->drive->controller;
+  if (c->drive[0].present)
+    edd_setup(&c->drive[0].taskfile);
+  if (c->drive[1].present)
+    edd_setup(&c->drive[1].taskfile);
+  c->selected = 0;
+}
+
+static void cmd_identify_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  memcpy(d->data, d->identify, 512);
+  data_in_state(tf);
+  /* Arrange to copy just the identify buffer */
+  d->dptr = d->data;
+  d->length = 1;
+}
+
+static void cmd_initparam_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  /* We only support the current mapping */
+  if (tf->count != d->sectors || (tf->lba4 & DEVH_HEAD) + 1 != d->heads) {
+    tf->status |= ST_ERR;
+    tf->error |= ERR_ABRT;
+    tf->drive->failed = 1;             /* Report ID NF until fixed */
+/*    fprintf(stderr, "geo is %d %d, asked for %d %d\n",
+      d->sectors, d->heads, tf->count, (tf->lba4 & DEVH_HEAD) + 1); */
+    ide_fault(d, "invalid geometry");
+  } else if (tf->drive->failed == 1)
+    tf->drive->failed = 0;             /* Valid translation */
+  completed(tf);
+}
+
+static void cmd_readsectors_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  /* Move to data xfer */
+  if (d->failed) {
+    drive_failed(tf);
+    return;
+  }
+  d->offset = xlate_block(tf);
+  /* DRDY is not guaranteed here but at least one buggy RC2014 firmware
+     expects it */
+  tf->status |= ST_DRQ | ST_DSC | ST_DRDY;
+  tf->status &= ~ST_BSY;
+  /* 0 = 256 sectors */
+  d->length = tf->count ? tf->count : 256;
+  /* fprintf(stderr, "READ %d SECTORS @ %ld\n", d->length, d->offset); */
+  if (d->offset == -1 ||  lseek(d->fd, 512 * d->offset, SEEK_SET) == -1) {
+    tf->status |= ST_ERR;
+    tf->status &= ~ST_DSC;
+    tf->error |= ERR_IDNF;
+    /* return null data */
+    completed(tf);
+    return;
+  }
+  /* do the xfer */
+  data_in_state(tf);
+}
+
+static void cmd_verifysectors_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  /* Move to data xfer */
+  if (d->failed) {
+    drive_failed(tf);
+    return;
+  }
+  d->offset = xlate_block(tf);
+  /* 0 = 256 sectors */
+  d->length = tf->count ? tf->count : 256;
+  if (d->offset == -1 || lseek(d->fd, 512 * (d->offset + d->length - 1), SEEK_SET) == -1) {
+    tf->status &= ~ST_DSC;
+    tf->status |= ST_ERR;
+    tf->error |= ERR_IDNF;
+  }
+  tf->status |= ST_DSC;
+  completed(tf);
+}
+
+static void cmd_recalibrate_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  if (d->failed)
+    drive_failed(tf);
+  if (d->offset == -1 || xlate_block(tf) != 0L) {
+    tf->status &= ~ST_DSC;
+    tf->status |= ST_ERR;
+    tf->error |= ERR_ABRT;
+  }
+  tf->status |= ST_DSC;
+  completed(tf);
+}
+
+static void cmd_seek_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  if (d->failed)
+    drive_failed(tf);
+  d->offset = xlate_block(tf);
+  if (d->offset == -1 || lseek(d->fd, 512 * d->offset, SEEK_SET) == -1) {
+    tf->status &= ~ST_DSC;
+    tf->status |= ST_ERR;
+    tf->error |= ERR_IDNF;
+  }
+  tf->status |= ST_DSC;
+  completed(tf);
+}
+
+static void cmd_setfeatures_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  switch(tf->feature) {
+    case 0x01:
+      d->eightbit = 1;
+      break;
+    case 0x03:
+      if ((tf->count & 0xF0) >= 0x20) {
+        tf->status |= ST_ERR;
+        tf->error |= ERR_ABRT;
+      }
+      /* Silently accept PIO mode settings */
+      break;
+    case 0x81:
+      d->eightbit = 0;
+      break;
+    default:
+      tf->status |= ST_ERR;
+      tf->error |= ERR_ABRT;
+  }
+  completed(tf);
+}
+
+static void cmd_writesectors_complete(struct ide_taskfile *tf)
+{
+  struct ide_drive *d = tf->drive;
+  /* Move to data xfer */
+  if (d->failed) {
+    drive_failed(tf);
+    return;
+  }
+  d->offset = xlate_block(tf);
+  tf->status |= ST_DRQ;
+  /* 0 = 256 sectors */
+  d->length = tf->count ? tf->count : 256;
+/*  fprintf(stderr, "WRITE %d SECTORS @ %ld\n", d->length, d->offset); */
+  if (d->offset == -1 ||  lseek(d->fd, 512 * d->offset, SEEK_SET) == -1) {
+    tf->status |= ST_ERR;
+    tf->error |= ERR_IDNF;
+    tf->status &= ~ST_DSC;
+    /* return null data */
+    completed(tf);
+    return;
+  }
+  /* do the xfer */
+  data_out_state(tf);
+}
+
+static void ide_set_error(struct ide_drive *d)
+{
+  d->taskfile.lba4 &= ~DEVH_HEAD;
+
+  if (d->taskfile.lba4 & DEVH_LBA) {
+    d->taskfile.lba1 = d->offset & 0xFF;
+    d->taskfile.lba2 = (d->offset >> 8) & 0xFF;
+    d->taskfile.lba3 = (d->offset >> 16) & 0xFF;
+    d->taskfile.lba4 |= (d->offset >> 24) & DEVH_HEAD;
+  } else {
+    d->taskfile.lba1 = d->offset % d->sectors + 1;
+    d->offset /= d->sectors;
+    d->taskfile.lba4 |= d->offset / (d->cylinders * d->sectors);
+    d->offset %= (d->cylinders * d->sectors);
+    d->taskfile.lba2 = d->offset & 0xFF;
+    d->taskfile.lba3 = (d->offset >> 8) & 0xFF;
+  }
+  d->taskfile.count = d->length;
+  d->taskfile.status |= ST_ERR;
+  d->state = IDE_IDLE;
+  completed(&d->taskfile);
+}
+
+static int ide_read_sector(struct ide_drive *d)
+{
+  int len;
+
+  d->dptr = d->data;
+  if ((len = read(d->fd, d->data, 512)) != 512) {
+    perror("ide_read_sector");
+    d->taskfile.status |= ST_ERR;
+    d->taskfile.status &= ~ST_DSC;
+    ide_xlate_errno(&d->taskfile, len);
+    return -1;
+  }
+//  hexdump(d->data);
+  d->offset += 512;
+  return 0;
+}
+
+static int ide_write_sector(struct ide_drive *d)
+{
+  int len;
+
+  d->dptr = d->data;
+  if ((len = write(d->fd, d->data, 512)) != 512) {
+    d->taskfile.status |= ST_ERR;
+    d->taskfile.status &= ~ST_DSC;
+    ide_xlate_errno(&d->taskfile, len);
+    return -1;
+  }
+//  hexdump(d->data);
+  d->offset += 512;
+  return 0;
+}
+
+static uint16_t ide_data_in(struct ide_drive *d, int len)
+{
+  uint16_t v;
+  if (d->state == IDE_DATA_IN) {
+    if (d->dptr == d->data + 512) {
+      if (ide_read_sector(d) < 0) {
+        ide_set_error(d);      /* Set the LBA or CHS etc */
+        return 0xFFFF;         /* and error bits set by read_sector */
+      }
+    }
+    v = *d->dptr;
+    if (!d->eightbit) {
+      if (len == 2)
+        v |= (d->dptr[1] << 8);
+      d->dptr+=2;
+    } else
+      d->dptr++;
+    d->taskfile.data = v;
+    if (d->dptr == d->data + 512) {
+      d->length--;
+      d->intrq = 1;            /* we don't yet emulate multimode */
+      if (d->length == 0) {
+        d->state = IDE_IDLE;
+        completed(&d->taskfile);
+      }
+    }
+  } else
+    ide_fault(d, "bad data read");
+
+  if (len == 1)
+    return d->taskfile.data & 0xFF;
+  return d->taskfile.data;
+}
+
+static void ide_data_out(struct ide_drive *d, uint16_t v, int len)
+{
+  if (d->state != IDE_DATA_OUT) {
+    ide_fault(d, "bad data write");
+    d->taskfile.data = v;
+  } else {
+    if (d->eightbit)
+      v &= 0xFF;
+    *d->dptr++ = v;
+    d->taskfile.data = v;
+    if (!d->eightbit) {
+      *d->dptr++ = v >> 8;
+      d->taskfile.data = v >> 8;
+    }
+    if (d->dptr == d->data + 512) {
+      if (ide_write_sector(d) < 0) {
+        ide_set_error(d);
+        return;        
+      }
+      d->length--;
+      d->intrq = 1;
+      if (d->length == 0) {
+        d->state = IDE_IDLE;
+        d->taskfile.status |= ST_DSC;
+        completed(&d->taskfile);
+      }
+    }
+  }
+}
+
+static void ide_issue_command(struct ide_taskfile *t)
+{
+  t->status &= ~(ST_ERR|ST_DRDY);
+  t->status |= ST_BSY;
+  t->error = 0;
+  t->drive->state = IDE_CMD;
+  
+  /* We could complete with delays but don't do so yet */
+  switch(t->command) {
+    case IDE_CMD_EDD:  /* 0x90 */
+      cmd_edd_complete(t);
+      break;
+    case IDE_CMD_IDENTIFY:     /* 0xEC */
+      cmd_identify_complete(t);
+      break;
+    case IDE_CMD_INTPARAMS:    /* 0x91 */
+      cmd_initparam_complete(t);
+      break;
+    case IDE_CMD_READ:         /* 0x20 */
+    case IDE_CMD_READ_NR:      /* 0x21 */
+      cmd_readsectors_complete(t);
+      break;
+    case IDE_CMD_SETFEATURES:  /* 0xEF */
+      cmd_setfeatures_complete(t);
+      break;
+    case IDE_CMD_VERIFY:       /* 0x40 */
+    case IDE_CMD_VERIFY_NR:    /* 0x41 */
+      cmd_verifysectors_complete(t);
+      break;
+    case IDE_CMD_WRITE:                /* 0x30 */
+    case IDE_CMD_WRITE_NR:     /* 0x31 */
+      cmd_writesectors_complete(t);
+      break;
+    default:
+      if ((t->command & 0xF0) == IDE_CMD_CALIB)        /* 1x */
+        cmd_recalibrate_complete(t);
+      else if ((t->command & 0xF0) == IDE_CMD_SEEK) /* 7x */
+        cmd_seek_complete(t);
+      else {
+        /* Unknown */
+        t->status |= ST_ERR;
+        t->error |= ERR_ABRT;
+        completed(t);
+      }
+  }
+}
+
+/*
+ *     8bit IDE controller emulation
+ */
+
+uint8_t ide_read8(struct ide_controller *c, uint8_t r)
+{
+  struct ide_drive *d = &c->drive[c->selected];
+  struct ide_taskfile *t = &d->taskfile;
+  switch(r) {
+    case ide_data:
+      return ide_data_in(d, 1);
+    case ide_error_r:
+      return t->error;
+    case ide_sec_count:
+      return t->count;
+    case ide_lba_low:
+      return t->lba1;
+    case ide_lba_mid:
+      return t->lba2;
+    case ide_lba_hi:
+      return t->lba3;
+    case ide_lba_top:
+      return t->lba4;
+    case ide_status_r:
+      d->intrq = 0;            /* Acked */
+    case ide_altst_r:
+      return t->status;
+    default:
+      ide_fault(d, "bogus register");
+      return 0xFF;
+  }
+}
+
+void ide_write8(struct ide_controller *c, uint8_t r, uint8_t v)
+{
+  struct ide_drive *d = &c->drive[c->selected];
+  struct ide_taskfile *t = &d->taskfile;
+
+  if (r != ide_devctrl_w) {
+    if (t->status & ST_BSY) {
+      ide_fault(d, "command written while busy");
+      return;
+    }
+    /* Not clear this is the right emulation */
+    if (d->present == 0 && r != ide_lba_top) {
+      ide_fault(d, "not present");
+      return;
+    }
+  }
+
+  switch(r) {
+    case ide_data:
+      ide_data_out(d, v, 1);
+      break;
+    case ide_feature_w:
+      t->feature = v;
+      break;
+    case ide_sec_count:
+      t->count = v;
+      break;
+    case ide_lba_low:
+      t->lba1 = v;
+      break;
+    case ide_lba_mid:
+      t->lba2 = v;
+      break;
+    case ide_lba_hi:
+      t->lba3 = v;
+      break;
+    case ide_lba_top:
+      c->selected = (v & DEVH_DEV) ? 1 : 0;
+      c->drive[c->selected].taskfile.lba4 = v & (DEVH_HEAD|DEVH_DEV|DEVH_LBA);
+      break;
+    case ide_command_w:
+      t->command = v; 
+      ide_issue_command(t);
+      break;
+    case ide_devctrl_w:
+      /* ATA: "When the Device Control register is written, both devices
+         respond to the write regardless of which device is selected" */
+      if ((v ^ t->devctrl) & DCL_SRST) {
+        if (v & DCL_SRST)
+          ide_srst_begin(c);
+        else
+          ide_srst_end(c);
+      }
+      c->drive[0].taskfile.devctrl = v;        /* Check versus real h/w does this end up cleared */
+      c->drive[1].taskfile.devctrl = v;
+      break;
+  }
+}
+
+/*
+ *     16bit IDE controller emulation
+ */
+
+uint16_t ide_read16(struct ide_controller *c, uint8_t r)
+{
+  struct ide_drive *d = &c->drive[c->selected];
+  if (r == ide_data)
+    return htons(ide_data_in(d,2));
+  return ide_read8(c, r);
+}
+
+void ide_write16(struct ide_controller *c, uint8_t r, uint16_t v)
+{
+  struct ide_drive *d = &c->drive[c->selected];
+  struct ide_taskfile *t = &d->taskfile;
+
+  if (r != ide_devctrl_w && (t->status & ST_BSY)) {
+    ide_fault(d, "command written while busy");
+    return;
+  }
+  if (r == ide_data)
+    ide_data_out(d, ntohs(v), 2);
+  else
+    ide_write8(c, r, v);
+}
+
+/*
+ *     Allocate a new IDE controller emulation
+ */
+struct ide_controller *ide_allocate(const char *name)
+{
+  struct ide_controller *c = calloc(1, sizeof(*c));
+  if (c == NULL)
+    return NULL;
+  c->name = strdup(name);
+  if (c->name == NULL) {
+    free(c);
+    return NULL;
+  }
+  c->drive[0].controller = c;
+  c->drive[1].controller = c;
+  c->drive[0].taskfile.drive = &c->drive[0];
+  c->drive[1].taskfile.drive = &c->drive[1];
+  return c;
+}
+
+/*
+ *     Attach a file to a device on the controller
+ */
+int ide_attach(struct ide_controller *c, int drive, int fd)
+{
+  struct ide_drive *d = &c->drive[drive];
+  if (d->present) {
+    ide_fault(d, "double attach");
+    return -1;
+  }
+  d->fd = fd;
+  if (read(d->fd, d->data, 512) != 512 ||
+      read(d->fd, d->identify, 512) != 512) {
+    ide_fault(d, "i/o error on attach");
+    return -1;
+  }
+  if (memcmp(d->data, ide_magic, 8)) {
+    ide_fault(d, "bad magic");
+    return -1;
+  }
+  d->fd = fd;
+  d->present = 1;
+  d->heads = d->identify[3];
+  d->sectors = d->identify[6];
+  d->cylinders = le16(d->identify[1]);
+  if (d->identify[49] & le16(1 << 9))
+    d->lba = 1;
+  else
+    d->lba = 0;
+  return 0;
+}
+
+/*
+ *     Detach an IDE device from the interface (not hot pluggable)
+ */
+void ide_detach(struct ide_drive *d)
+{
+  close(d->fd);
+  d->fd = -1;
+  d->present = 0;
+}
+
+/*
+ *     Free up and release and IDE controller
+ */  
+void ide_free(struct ide_controller *c)
+{
+  if (c->drive[0].present)
+    ide_detach(&c->drive[0]);
+  if (c->drive[1].present)
+    ide_detach(&c->drive[1]);
+  free((void *)c->name);
+  free(c);
+}
+
+/*
+ *     Emulation interface for an 8bit controller using latches on the
+ *     data register
+ */
+uint8_t ide_read_latched(struct ide_controller *c, uint8_t reg)
+{
+  uint16_t v;
+  if (reg == ide_data_latch)
+    return c->data_latch;
+  v = ide_read16(c, reg);
+  if (reg == ide_data) {
+    c->data_latch = v >> 8;
+    v &= 0xFF;
+  }
+  return v;
+}
+
+void ide_write_latched(struct ide_controller *c, uint8_t reg, uint8_t v)
+{
+  uint16_t d = v;
+
+  if (reg == ide_data_latch) {
+    c->data_latch = v;
+    return;
+  }
+  if (reg == ide_data)
+    d |=  (c->data_latch << 8);
+  ide_write16(c, reg, d);  
+}
+
+static void make_ascii(uint16_t *p, const char *t, int len)
+{
+  int i;
+  char *d = (char *)p;
+  strncpy(d, t, len);
+
+  for (i = 0; i < len; i += 2) {
+    char c = *d;
+    *d = d[1];
+    d[1] = c;
+    d += 2;
+  }  
+}
+
+static void make_serial(uint16_t *p)
+{
+  char buf[21];
+  srand(getpid()^time(NULL));
+  snprintf(buf, 21, "%08d%08d%04d", rand(), rand(), rand());
+  make_ascii(p, buf, 20);
+}
+
+int ide_make_drive(uint8_t type, int fd)
+{
+  uint8_t s, h;
+  uint16_t c;
+  uint32_t sectors;
+  uint16_t ident[256];
+
+  if (type < 1 || type > MAX_DRIVE_TYPE)
+    return -2;
+  
+  memset(ident, 0, 512);
+  memcpy(ident, ide_magic, 8);
+  if (write(fd, ident, 512) != 512)
+    return -1;
+
+  memset(ident, 0, 8);
+  ident[0] = le16((1 << 15) | (1 << 6));       /* Non removable */
+  make_serial(ident + 10);
+  ident[47] = 0; /* no read multi for now */
+  ident[51] = le16(240 /* PIO2 */ << 8);       /* PIO cycle time */
+  ident[53] = le16(1);         /* Geometry words are valid */
+  
+  switch(type) {
+    case ACME_ROADRUNNER:
+      /* 504MB drive with LBA support */
+      c = 1024;
+      h = 16;
+      s = 63;
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME ROADRUNNER v0.1", 40);
+      ident[49] = le16(1 << 9); /* LBA */
+      break;  
+    case ACME_ULTRASONICUS:
+      /* 40MB drive with LBA support */
+      c = 977;
+      h = 5;
+      s = 16;
+      ident[49] = le16(1 << 9); /* LBA */
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME ULTRASONICUS AD INFINITUM v0.1", 40);
+      break;
+    case ACME_NEMESIS:
+      /* 20MB drive with LBA support */
+      c = 615;
+      h = 4;
+      s = 16;
+      ident[49] = le16(1 << 9); /* LBA */
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME NEMESIS RIDICULII v0.1", 40);
+      break;
+    case ACME_COYOTE:
+      /* 20MB drive without LBA support */
+      c = 615;
+      h = 4;
+      s = 16;
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME COYOTE v0.1", 40);
+      break;  
+    case ACME_ACCELLERATTI:
+      c = 1024;
+      h = 16;
+      s = 16;
+      ident[49] = le16(1 << 9); /* LBA */
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME ACCELLERATTI INCREDIBILUS v0.1", 40);
+      break;
+    case ACME_ZIPPIBUS:
+      c = 1024;
+      h = 16;
+      s = 32;
+      ident[49] = le16(1 << 9); /* LBA */
+      make_ascii(ident + 23, "A001.001", 8);
+      make_ascii(ident + 27, "ACME ZIPPIBUS v0.1", 40);
+      break;
+  }
+  ident[1] = le16(c);
+  ident[3] = le16(h);
+  ident[6] = le16(s);
+  ident[54] = ident[1];
+  ident[55] = ident[3];
+  ident[56] = ident[6];
+  sectors = c * h * s;
+  ident[57] = le16(sectors & 0xFFFF);
+  ident[58] = le16(sectors >> 16);
+  ident[60] = ident[57];
+  ident[61] = ident[58];
+  if (write(fd, ident, 512) != 512)
+    return -1;
+  
+  memset(ident, 0xE5, 512);
+  while(sectors--)
+    if (write(fd, ident, 512) != 512)
+      return -1;  
+  return 0;
+}
diff --git a/ide/ide.h b/ide/ide.h
new file mode 100644 (file)
index 0000000..7c8e32d
--- /dev/null
+++ b/ide/ide.h
@@ -0,0 +1,82 @@
+#include <stdint.h>
+
+#define ACME_ROADRUNNER                1       /* 504MB classic IDE drive */
+#define ACME_COYOTE            2       /* 20MB early IDE drive */
+#define ACME_NEMESIS           3       /* 20MB LBA capable drive */
+#define ACME_ULTRASONICUS      4       /* 40MB LBA capable drive */
+#define ACME_ACCELLERATTI      5       /* 128MB LBA capable drive */
+#define ACME_ZIPPIBUS          6       /* 256MB LBA capable drive */
+
+#define MAX_DRIVE_TYPE         6
+
+#define                ide_data        0
+#define                ide_error_r     1
+#define                ide_feature_w   1
+#define                ide_sec_count   2
+#define                ide_sec_num     3
+#define                ide_lba_low     3
+#define                ide_cyl_low     4
+#define                ide_lba_mid     4
+#define                ide_cyl_hi      5
+#define                ide_lba_hi      5
+#define                ide_dev_head    6
+#define                ide_lba_top     6
+#define                ide_status_r    7
+#define                ide_command_w   7
+#define                ide_altst_r     8
+#define                ide_devctrl_w   8
+#define                ide_data_latch  9
+
+struct ide_taskfile {
+  uint16_t data;
+  uint8_t error;
+  uint8_t feature;
+  uint8_t count;
+  uint8_t lba1;
+  uint8_t lba2;
+  uint8_t lba3;
+  uint8_t lba4;
+  uint8_t status;
+  uint8_t command;
+  uint8_t devctrl;
+  struct ide_drive *drive;
+};
+
+struct ide_drive {
+  struct ide_controller *controller;
+  struct ide_taskfile taskfile;
+  unsigned int present:1, intrq:1, failed:1, lba:1, eightbit:1;
+  uint16_t cylinders;
+  uint8_t heads, sectors;
+  uint8_t data[512];
+  uint16_t identify[256];
+  uint8_t *dptr;
+  int state;
+  int fd;
+  off_t offset;
+  int length;
+};
+
+struct ide_controller {
+  struct ide_drive drive[2];
+  int selected;
+  const char *name;
+  uint16_t data_latch;
+};
+
+extern const uint8_t ide_magic[8];
+
+void ide_reset_begin(struct ide_controller *c);
+uint8_t ide_read8(struct ide_controller *c, uint8_t r);
+void ide_write8(struct ide_controller *c, uint8_t r, uint8_t v);
+uint16_t ide_read16(struct ide_controller *c, uint8_t r);
+void ide_write16(struct ide_controller *c, uint8_t r, uint16_t v);
+uint8_t ide_read_latched(struct ide_controller *c, uint8_t r);
+void ide_write_latched(struct ide_controller *c, uint8_t r, uint8_t v);
+
+struct ide_controller *ide_allocate(const char *name);
+int ide_attach(struct ide_controller *c, int drive, int fd);
+void ide_detach(struct ide_drive *d);
+void ide_free(struct ide_controller *c);
+
+int ide_make_drive(uint8_t type, int fd);
diff --git a/ide/makedisk b/ide/makedisk
new file mode 100755 (executable)
index 0000000..a3d920f
Binary files /dev/null and b/ide/makedisk differ
diff --git a/ide/makedisk.c b/ide/makedisk.c
new file mode 100644 (file)
index 0000000..9e4defb
--- /dev/null
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "ide.h"
+
+int main(int argc, const char *argv[])
+{
+  int t, fd;
+  if (argc != 3) {
+    fprintf(stderr, "%s [type] [path]\n", argv[0]);
+    exit(1);
+  }
+  t = atoi(argv[1]);
+  if (t < 1 || t > MAX_DRIVE_TYPE) {
+    fprintf(stderr, "%s: unknown drive type.\n", argv[0]);
+    exit(1);
+  }
+  fd = open(argv[2], O_WRONLY|O_TRUNC|O_CREAT|O_EXCL, 0666);
+  if (fd == -1) {
+    perror(argv[2]);
+    exit(1);
+  }
+  if (ide_make_drive(t, fd) < 0) {
+    perror(argv[2]);
+    exit(1);
+  }
+  return 0;
+}