From 2aa4f43abaa4f5217783c12bf86b9687027297d0 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2020 19:21:15 +0100 Subject: [PATCH] added, not yet functioning, IDE emulation --- Gayle.c | 191 ++++++++++ Gayle.h | 26 ++ ide.c | 934 +++++++++++++++++++++++++++++++++++++++++++++++ ide.h | 83 +++++ ide/.hd0.img.swp | Bin 0 -> 1024 bytes ide/ide.c | 934 +++++++++++++++++++++++++++++++++++++++++++++++ ide/ide.h | 82 +++++ ide/makedisk | Bin 0 -> 23612 bytes ide/makedisk.c | 29 ++ 9 files changed, 2279 insertions(+) create mode 100644 Gayle.c create mode 100644 Gayle.h create mode 100644 ide.c create mode 100644 ide.h create mode 100644 ide/.hd0.img.swp create mode 100644 ide/ide.c create mode 100644 ide/ide.h create mode 100755 ide/makedisk create mode 100644 ide/makedisk.c diff --git a/Gayle.c b/Gayle.c new file mode 100644 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 +#include +#include +#include +#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 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 +#include + + + + +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 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..0d6549c --- /dev/null +++ b/ide.h @@ -0,0 +1,83 @@ +#include + +#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 index 0000000000000000000000000000000000000000..893a7774f6a42594ec1dbdb852a145c4c936fd47 GIT binary patch literal 1024 zcmYc?$V<%2S1{HyVn6}+%@`O8GEszZa*7g*3zAZciYf_c$Vf5J%gjy3Y0jv`Xb6mk I0DVIM07|wC`Tzg` literal 0 HcmV?d00001 diff --git a/ide/ide.c b/ide/ide.c new file mode 100644 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..7c8e32d --- /dev/null +++ b/ide/ide.h @@ -0,0 +1,82 @@ +#include + +#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 index 0000000000000000000000000000000000000000..a3d920f981250b3b96e936d2ae4345bc173ef44d GIT binary patch literal 23612 zcmeHveRx#Ib@$v|A%u{u1r%Era(rzIo5mm(kZlEHBOwW+#{y))4h>0GEA0wxkhIF~ zijO|jK|pNk;+oo|!3mBw3C%;dQY?iu=CQbz3)XQ#WzjUCB#ig~X)sXX{4& z{pLd&T?=d{e%gO>?P2E3oH=vmoHJ*>R`c1$<`s_P2=n>GTtVnafg`FBANv^o$rDqm zg)63^>8G}!TJ;*qKz;;>NhkwgZUWRgpVZaP5H$3zLzZY#YG$5bBl--d_ zMF76zWBmZaT9n)EpGO`8>yx(yU9q+WU6J`+v7Wwd^TUbm&W@^NylOFuBo1j1@3z%< zGD}b%en{(Rd zeyvEjp9}8vEp~RIIIk8+r7ORRf5+%khOEbLI>IFgGZAJXuw8y1Lbwc}9AP%Xl?Yq@ z=}S9;Cw3Kow6^H6d(QvSj+Q?=68PPp&M5i%mmaFP_3l5~kz9IU$AN8M-chysme-fP zHF~nLZ&z{6zyE%PxaO_He>iPw;Gw)_&!Px#JSkc;#;ESP;+=Yg;H z!2jfd4|?Dl58UX1DL3=6y)PR8F=44_ok#e%PBS9>Z4cb*f%gMGSnHFvFaw(SxTpM# z2mW0T-0Xq>)&tZ2%twB1H2`A5yFKuSJ@D5&aHR*{?15=N<|F;B20%>sQV)Ee2mXQw z78qac98oul=(bo&bf)4xNs;VL#ClR4BAE#HL_}{ik%%WmGL`6Q@7*p^vF@k{x5X1F zu_>DBjYU`z>FX8k;jXTDyXcN~C!;A53ZYt0JcO)pDi-e%iD)>&!uIZ7(b3zNY8PF} zXmqp80@*}3mW+Z_1l2^x)0&TCfM2I59m%=dw7(m_7)HH=aiI`98BY~r4`WP31-0{E{)`YmDcJesa9(D9 zE-y2XyCm~Krr`Wjx#w!Y=>L0QaMm;X2A_NLWX`0|Sm{$%`h=A}YNd}@>9mzTXr=dC z>AhBZrA6thBJwZ@w`u z!}C^p*h*)ZKAIj9nea6$J-!e?B_;lb<5& zgPsmmJ7ON2fsD_6=*O7Bhdw;I!1>eB(UGE?9I+SnUxxZ+z{Bj3Wm!O_ zCEl#j1=$0z4F-Odz^x1bH&BJbeSSt`%a0Wp@J)Lsa4s=OA$Q11Aj}3;~JrS>RyUlp$7r|J$L({*!$%0 zVdy7~J|98+O@yNe#}G~+1R#R~$e!E>2)UhvoSsI=J31d@M9GS6KNY+^dr9#2!2;ATKz+&}>3;Vt)9kyH zNp(7#41V`4I zXp7_!Y@_^Vzf>}yJf~fc8)iR^jTjyRumi)x4$2HVXC1Z? zIWwN7+?nqS)@SOkPG^18#X`orGyX-4>odD&rnBHbv)klfMSU&L?7D&Rt_=Gkg7?R|>-ecCwtHnWAZHl||9`xKXb znw`6xJc_khOs)43uR!OQ>3mGlA0giHh0kZ7a5rX&3n?xU!ORfH^Ao|1*#h*f41Igh z@5@LUs9OSgacr~fa#`lj(3UMZWNhE^B6LF?y=sk*B5gx#pEkUeIcBvtiO#-%M=+By9X+cq}OH1(RV^O>G%r3)+V5A=V^=1EtQH+L-qHe*V97{ z)qf%T%09KuRd#zZ+w<}aKP}rUMqhR>&1AoZV8)B&c@iE?om;@Ww~piw@_YQ<Td75Tc5kIq9sILFh5h)0-fAZ%4eoO$UX@~)3kbG)>sUNIi$MWfS+2_URKcpx7--7ge zv7d{{b3SM+)%3nJi9CN5`CuI)$Jv;CDwUk(L4I6=IN0mh@|teR3r7uO@|sL87n0+o z@`7%Ze8=S|?E!MkqOZTI>>z(>i~kB5M*q~c90Q=;L^(qLv@7&yY#hGd{!BJLFVvrJ zu}#d2ziywt0Xnp^GSaigmfg>Hv;Qyr=eYg%+GPI*7k+{h;c+#=pe=s zUHSN0LixZ-jt|t~SU3-#$8!W6f&Ea9SLB)Xrg!{$^#@sujMG;B5l3>8_QmYlONYLR zx~85MypMig%nw`-IyaLS(0rKV(bB!?r*|!DWb#4# LEyqo{L__+7@RrGBLeKY>^ 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 +#include +#include +#include +#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; +}