From c6b96c6d7391efb654fade8dea079bc216928397 Mon Sep 17 00:00:00 2001 From: beeanyew Date: Fri, 15 Jan 2021 04:30:52 +0100 Subject: [PATCH] IDE updates, fix mouse hook hotkey Added support for headerless (RDSK) HDD images, and fixed the IDE controller emulation to not keep separate LBA1-4 values for each hard drive. This may look strange at first glance, but the Amiga would regularly write the CHS values for drive access ahead of actually switching to the drive it wanted, so I assume this is how it's actually supposed to work. The second drive still doesn't mount automatically on boot, but I believe that some currently unsupported register write or read is causing this. --- default.cfg | 7 +- emulator.c | 6 +- gpio/gpio.c | 2 +- gpio/gpio.h | 2 +- platforms/amiga/Gayle.c | 45 +++++++-- platforms/amiga/amiga-platform.c | 4 + platforms/amiga/gayle-ide/ide.c | 151 +++++++++++++++++++++++++------ platforms/amiga/gayle-ide/ide.h | 13 ++- 8 files changed, 180 insertions(+), 50 deletions(-) diff --git a/default.cfg b/default.cfg index aa07b9f..fe75e7a 100644 --- a/default.cfg +++ b/default.cfg @@ -26,8 +26,13 @@ loopcycles 300 platform amiga # Uncomment to let reads/writes through from/to the RTC memory range #setvar enable_rtc_emulation 0 -# Uncomment to set a custom HD image file for ide0 +# Uncomment to set a custom HD image file for ide0 drive 0/1 #setvar hdd0 snakes.img +#setvar hdd1 snakes2.img +# Uncomment to enable RTG +#setvar rtg +# Uncomment to enable CDTV mode (not working, requires Kickstart 1.3+CDTV extended ROM) +#setvar cdtv # Forward mouse events to host system, defaults to off unless toggle key is pressed on the Pi. # Syntax is mouse [device] [toggle key] diff --git a/emulator.c b/emulator.c index bb93c58..ce516f3 100644 --- a/emulator.c +++ b/emulator.c @@ -69,8 +69,8 @@ void *iplThread(void *args) { irq = 0; if (gayle_emulation_enabled) { - if (((gayle_int & 0x80) || gayle_a4k_int) && get_ide(0)->drive->intrq) { - //get_ide(0)->drive->intrq = 0; + if (((gayle_int & 0x80) || gayle_a4k_int) && (get_ide(0)->drive[0].intrq || get_ide(0)->drive[1].intrq)) { + //get_ide(0)->drive[0].intrq = 0; gayleirq = 1; m68k_end_timeslice(); } @@ -291,7 +291,7 @@ disasm_run:; } if (!kb_hook_enabled && c_type) { - if (c == cfg->mouse_toggle_key) { + if (c && c == cfg->mouse_toggle_key) { mouse_hook_enabled ^= 1; printf("Mouse hook %s.\n", mouse_hook_enabled ? "enabled" : "disabled"); mouse_dx = mouse_dy = mouse_buttons = 0; diff --git a/gpio/gpio.c b/gpio/gpio.c index 5eb11d9..ed388c4 100644 --- a/gpio/gpio.c +++ b/gpio/gpio.c @@ -377,7 +377,7 @@ inline void gpio_handle_irq() { srdata = read_reg(); m68k_set_irq((srdata >> 13) & 0xff); } else { - if ((gayle_int & 0x80) && get_ide(0)->drive->intrq) { + if ((gayle_int & 0x80) && (get_ide(0)->drive[0].intrq || get_ide(0)->drive[1].intrq)) { write16(0xdff09c, 0x8008); m68k_set_irq(2); } diff --git a/gpio/gpio.h b/gpio/gpio.h index 5ad470a..43521ce 100644 --- a/gpio/gpio.h +++ b/gpio/gpio.h @@ -76,7 +76,7 @@ srdata = read_reg(); \ m68k_set_irq((srdata >> 13) & 0xff); \ } else { \ - if ((gayle_int & 0x80) && get_ide(0)->drive->intrq) { \ + if ((gayle_int & 0x80) && (get_ide(0)->drive[0].intrq || get_ide(0)->drive[1].intrq)) { \ write16(0xdff09c, 0x8008); \ m68k_set_irq(2); \ } \ diff --git a/platforms/amiga/Gayle.c b/platforms/amiga/Gayle.c index 6e38ffa..044b27e 100644 --- a/platforms/amiga/Gayle.c +++ b/platforms/amiga/Gayle.c @@ -80,21 +80,34 @@ void InitGayle(void) { } ide0 = ide_allocate("cf"); - fd = open(hdd_image_file[0], O_RDWR); - if (fd == -1) { - printf("HDD Image %s failed open\n", hdd_image_file[0]); - } else { - ide_attach(ide0, 0, fd); - ide_reset_begin(ide0); - printf("HDD Image %s attached\n", hdd_image_file[0]); + + for (int i = 0; i < GAYLE_MAX_HARDFILES; i++) { + if (hdd_image_file[i]) { + fd = open(hdd_image_file[i], O_RDWR); + if (fd == -1) { + printf("[HDD%d] HDD Image %s failed open\n", i, hdd_image_file[i]); + } else { + printf("[HDD%d] Attaching HDD image %s.\n", i, hdd_image_file[i]); + if (strcmp(hdd_image_file[i] + (strlen(hdd_image_file[i]) - 3), "img") != 0) { + printf("No header present on HDD image %s.\n", hdd_image_file[i]); + ide_attach_hdf(ide0, i, fd); + } + else { + printf("Attaching HDD image with header.\n"); + ide_attach(ide0, i, fd); + } + printf("[HDD%d] HDD Image %s attached\n", i, hdd_image_file[i]); + } + } } + ide_reset_begin(ide0); } uint8_t CheckIrq(void) { uint8_t irq; if (gayle_int & (1 << 7)) { - irq = ide0->drive->intrq; + irq = ide0->drive[0].intrq || ide0->drive[1].intrq; // if (irq==0) // printf("IDE IRQ: %x\n",irq); return irq; @@ -108,9 +121,11 @@ void writeGayleB(unsigned int address, unsigned int value) { if (address >= gayle_ide_base) { switch ((address - gayle_ide_base) - gayle_ide_adj) { case GFEAT_OFFSET: + printf("Write to GFEAT: %.2X.\n", value); ide_action = ide_feature_w; goto idewrite8; case GCMD_OFFSET: + //printf("Write to GCMD: %.2X.\n", value); ide_action = ide_command_w; goto idewrite8; case GSECTCOUNT_OFFSET: @@ -126,9 +141,11 @@ void writeGayleB(unsigned int address, unsigned int value) { ide_action = ide_cyl_hi; goto idewrite8; case GDEVHEAD_OFFSET: + printf("Write to GDEVHEAD: %.2X.\n", value); ide_action = ide_dev_head; goto idewrite8; case GCTRL_OFFSET: + printf("Write to GCTRL: %.2X.\n", value); ide_action = ide_devctrl_w; goto idewrite8; case GIRQ_4000_OFFSET: @@ -150,9 +167,11 @@ skip_idewrite8:; gayle_a4k = value; return;*/ case GIDENT: + printf("Write to GIDENT: %d\n", value); counter = 0; return; case GCONF: + printf("Write to GCONF: %d\n", gayle_cfg); gayle_cfg = value; return; case RAMSEY_REG: @@ -165,6 +184,8 @@ skip_idewrite8:; gayle_cs_mask = value & ~3; gayle_cs &= ~3; gayle_cs |= value & 3; + printf("Write to GCS: %d\n", gayle_cs); + //ide0->selected = gayle_cs; return; } @@ -233,7 +254,7 @@ void writeGayleL(unsigned int address, unsigned int value) { } uint8_t readGayleB(unsigned int address) { - uint8_t ide_action = 0; + uint8_t ide_action = 0, ide_val = 0; if (address >= gayle_ide_base) { switch ((address - gayle_ide_base) - gayle_ide_adj) { @@ -268,6 +289,9 @@ uint8_t readGayleB(unsigned int address) { } goto skip_ideread8; ideread8:; + ide_val = ide_read8(ide0, ide_action); + if (((address - gayle_ide_base) - gayle_ide_adj) == GDEVHEAD_OFFSET) + printf("Read from GDEVHEAD: %.2X\n", ide_val); return ide_read8(ide0, ide_action); skip_ideread8:; } @@ -281,15 +305,18 @@ skip_ideread8:; val = 0x00; } counter++; + printf("Read from GIDENT: %.2X.\n", val); return val; } case GINT: return gayle_int; case GCONF: + printf("Read from GCONF: %d\n", gayle_cfg & 0x0F); return gayle_cfg & 0x0f; case GCS: { uint8_t v; v = gayle_cs_mask | gayle_cs; + printf("Read from GCS: %d\n", v); return v; } // This seems incorrect, GARY_REG3 is the same as GIDENT, and the A4000 diff --git a/platforms/amiga/amiga-platform.c b/platforms/amiga/amiga-platform.c index 292989d..264b1d9 100644 --- a/platforms/amiga/amiga-platform.c +++ b/platforms/amiga/amiga-platform.c @@ -278,6 +278,10 @@ void setvar_amiga(struct emulator_config *cfg, char *var, char *val) { if (val && strlen(val) != 0) set_hard_drive_image_file_amiga(0, val); } + if (strcmp(var, "hdd1") == 0) { + if (val && strlen(val) != 0) + set_hard_drive_image_file_amiga(1, val); + } if (strcmp(var, "cdtv") == 0) { printf("[AMIGA] CDTV mode enabled.\n"); cdtv_mode = 1; diff --git a/platforms/amiga/gayle-ide/ide.c b/platforms/amiga/gayle-ide/ide.c index f82dfb5..765bb13 100644 --- a/platforms/amiga/gayle-ide/ide.c +++ b/platforms/amiga/gayle-ide/ide.c @@ -25,6 +25,7 @@ #include #include #include +#include "../../../config_file/config_file.h" #include "ide.h" @@ -139,29 +140,34 @@ static off_t xlate_block(struct ide_taskfile *t) struct ide_drive *d = t->drive; uint16_t cyl; - if (t->lba4 & DEVH_LBA) { + if (d->controller->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); + return ((d->header_present) ? 2 : 0) + (((t->drive->controller->lba4 & DEVH_HEAD) << 24) | (t->drive->controller->lba3 << 16) | (t->drive->controller->lba2 << 8) | t->drive->controller->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) { + if (t->drive->controller->lba1 == 0) { fprintf(stderr, "[Bug: request for sector offset 0].\n"); - t->lba1 = 1; + t->drive->controller->lba1 = 1; } - cyl = (t->lba3 << 8) | t->lba2; + cyl = (t->drive->controller->lba3 << 8) | t->drive->controller->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) { + if (t->drive->controller->lba1 == 0 || t->drive->controller->lba1 > d->sectors || t->drive->controller->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; + //off_t ret = ((d->header_present) ? 1 : -1) + ((cyl * d->heads) + (t->drive->controller->lba4 & DEVH_HEAD)) * d->sectors + t->drive->controller->lba1; + //printf("Non-LBA xlate block %lX.\n", ret); + //printf("Cyl: %d Heads: %d Sectors: %d\n", cyl, d->heads, d->sectors); + //printf("LBA1: %.2X LBA2: %.2X LBA3: %.2X LBA4: %.2X\n", t->drive->controller->lba1, t->drive->controller->lba2, t->drive->controller->lba3, t->drive->controller->lba4); + + return ((d->header_present) ? 1 : -1) + ((cyl * d->heads) + (t->drive->controller->lba4 & DEVH_HEAD)) * d->sectors + t->drive->controller->lba1; } /* Indicate the drive is ready */ @@ -211,10 +217,10 @@ static void data_out_state(struct ide_taskfile *tf) 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->drive->controller->lba1 = 0x01; /* EDD always updates drive 0 */ + tf->drive->controller->lba2 = 0x00; + tf->drive->controller->lba3 = 0x00; + tf->drive->controller->lba4 = 0x00; tf->count = 0x01; ready(tf); } @@ -233,6 +239,8 @@ void ide_reset(struct ide_controller *c) c->drive[1].taskfile.status = ST_DRDY; c->drive[1].eightbit = 0; } + if (c->selected != 0) { + } c->selected = 0; } @@ -286,7 +294,7 @@ 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) { + if (tf->count != d->sectors || (tf->drive->controller->lba4 & DEVH_HEAD) + 1 != d->heads) { tf->status |= ST_ERR; tf->error |= ERR_ABRT; tf->drive->failed = 1; /* Report ID NF until fixed */ @@ -426,20 +434,20 @@ static void cmd_writesectors_complete(struct ide_taskfile *tf) static void ide_set_error(struct ide_drive *d) { - d->taskfile.lba4 &= ~DEVH_HEAD; + d->controller->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; + if (d->controller->lba4 & DEVH_LBA) { + d->controller->lba1 = d->offset & 0xFF; + d->controller->lba2 = (d->offset >> 8) & 0xFF; + d->controller->lba3 = (d->offset >> 16) & 0xFF; + d->controller->lba4 |= (d->offset >> 24) & DEVH_HEAD; } else { - d->taskfile.lba1 = d->offset % d->sectors + 1; + d->controller->lba1 = d->offset % d->sectors + 1; d->offset /= d->sectors; - d->taskfile.lba4 |= d->offset / (d->cylinders * d->sectors); + d->controller->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->controller->lba2 = d->offset & 0xFF; + d->controller->lba3 = (d->offset >> 8) & 0xFF; } d->taskfile.count = d->length; d->taskfile.status |= ST_ERR; @@ -607,13 +615,13 @@ uint8_t ide_read8(struct ide_controller *c, uint8_t r) case ide_sec_count: return t->count; case ide_lba_low: - return t->lba1; + return c->lba1; case ide_lba_mid: - return t->lba2; + return c->lba2; case ide_lba_hi: - return t->lba3; + return c->lba3; case ide_lba_top: - return t->lba4; + return c->lba4 | ((c->selected) ? 0x10 : 0x00); case ide_status_r: d->intrq = 0; /* Acked */ case ide_altst_r: @@ -641,6 +649,8 @@ void ide_write8(struct ide_controller *c, uint8_t r, uint8_t v) } } + uint8_t ve; + switch(r) { case ide_data: ide_data_out(d, v, 1); @@ -652,17 +662,17 @@ void ide_write8(struct ide_controller *c, uint8_t r, uint8_t v) t->count = v; break; case ide_lba_low: - t->lba1 = v; + c->lba1 = v; break; case ide_lba_mid: - t->lba2 = v; + c->lba2 = v; break; case ide_lba_hi: - t->lba3 = v; + c->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); + c->lba4 = v & (DEVH_HEAD|/*DEVH_DEV|*/DEVH_LBA); break; case ide_command_w: t->command = v; @@ -755,6 +765,7 @@ int ide_attach(struct ide_controller *c, int drive, int fd) d->heads = d->identify[3]; d->sectors = d->identify[6]; d->cylinders = le16(d->identify[1]); + d->header_present = 1; if (d->identify[49] & le16(1 << 9)) d->lba = 1; else @@ -762,6 +773,52 @@ int ide_attach(struct ide_controller *c, int drive, int fd) return 0; } +// Attach a headerless HDD image to the controller +int ide_attach_hdf(struct ide_controller *c, int drive, int fd) +{ + struct ide_drive *d = &c->drive[drive]; + if (d->present) { + printf("[IDE/HDL] Drive already attached.\n"); + return -1; + } + + d->fd = fd; + d->present = 1; + d->lba = 0; + + d->heads = 255; + d->sectors = 63; + d->header_present = 0; + + uint64_t file_size = lseek(fd, 0, SEEK_END); + lseek(fd, 1024, SEEK_SET); + + if (file_size < 500 * SIZE_MEGA) { + d->heads = 16; + } + else if (file_size < 1000 * SIZE_MEGA) { + d->heads = 32; + } + else if (file_size < 2000 * SIZE_MEGA) { + d->heads = 64; + } + else if (file_size < (uint64_t)4000 * SIZE_MEGA) { + d->heads = 128; + } + + d->cylinders = (file_size / 512) / (d->sectors * d->heads); + + printf("[IDE/HDL] Cylinders: %d Heads: %d Sectors: %d\n", d->cylinders, d->heads, d->sectors); + + if (file_size >= 4 * 1000 * 1000) { + d->lba = 1; + } + + ide_make_ident(d->cylinders, d->heads, d->sectors, "PISTORM HDD IMAGE v0.1", d->identify); + + return 0; +} + /* * Detach an IDE device from the interface (not hot pluggable) */ @@ -837,6 +894,40 @@ static void make_serial(uint16_t *p) make_ascii(p, buf, 20); } +int ide_make_ident(uint16_t c, uint8_t h, uint8_t s, char *name, uint16_t *target) +{ + uint16_t *ident = target; + uint32_t sectors; + + memset(ident, 0, 512); + memcpy(ident, ide_magic, 8); + + 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 */ + + make_ascii(ident + 23, "A001.001", 8); + make_ascii(ident + 27, name, 40); + ident[49] = le16(1 << 9); /* LBA */ + + 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]; + + return 0; +} + int ide_make_drive(uint8_t type, int fd) { uint8_t s, h; diff --git a/platforms/amiga/gayle-ide/ide.h b/platforms/amiga/gayle-ide/ide.h index 900c3cd..29598ec 100644 --- a/platforms/amiga/gayle-ide/ide.h +++ b/platforms/amiga/gayle-ide/ide.h @@ -19,7 +19,7 @@ #define ide_lba_mid 4 #define ide_cyl_hi 5 #define ide_lba_hi 5 -#define ide_dev_head 6 +#define ide_dev_head 6 #define ide_lba_top 6 #define ide_status_r 7 #define ide_command_w 7 @@ -32,10 +32,6 @@ struct ide_taskfile { 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; @@ -55,6 +51,7 @@ struct ide_drive { int fd; off_t offset; int length; + uint8_t header_present; }; struct ide_controller { @@ -62,6 +59,10 @@ struct ide_controller { int selected; const char *name; uint16_t data_latch; + uint8_t lba1; + uint8_t lba2; + uint8_t lba3; + uint8_t lba4; }; //extern ide_controller idectrl; @@ -77,6 +78,8 @@ 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); +int ide_attach_hdf(struct ide_controller *c, int drive, int fd); +int ide_make_ident(uint16_t c, uint8_t h, uint8_t s, char *name, uint16_t *target); void ide_detach(struct ide_drive *d); void ide_free(struct ide_controller *c); -- 2.39.2