+// SPDX-License-Identifier: MIT
+
//
// Gayle.c
-// Omega
-//
-// Created by Matt Parsons on 06/03/2019.
+// Originally based on Omega's Gayle emulation,
+// 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 "Gayle.h"
#include <fcntl.h>
#include <stdio.h>
#include <time.h>
#include <endian.h>
-#include "../shared/rtc.h"
-#include "../../config_file/config_file.h"
+#include "platforms/shared/rtc.h"
+#include "config_file/config_file.h"
-#include "gayle-ide/ide.h"
#include "amiga-registers.h"
-//#define GSTATUS 0xda201c
-//#define GCLOW 0xda2010
-//#define GDH 0xda2018
+//#define DEBUG_GAYLE
+#ifdef DEBUG_GAYLE
+#define DEBUG printf
+#else
+#define DEBUG(...)
+#endif
+
+#define IDE_DUMMY
+
+#ifdef IDE_DUMMY
+uint8_t *ide0 = NULL;
+
+uint8_t ide_feature_w = 0, ide_command_w = 0, ide_sec_count = 0, ide_sec_num = 0, idewrite8 = 0, ide_cyl_hi = 0, ide_dev_head = 0;
+uint8_t ide_devctrl_w = 0, ide_cyl_low = 0, ide_error_r = 0, ide_status_r = 0, ide_altst_r = 0, ide_data = 0;
+
+uint8_t ide_read8(uint8_t *dummy, uint8_t ide_action) { if (dummy || ide_action) {}; return 0; }
+uint16_t ide_read16(uint8_t *dummy, uint8_t ide_action) { if (dummy || ide_action) {}; return 0; }
+
+void ide_write8(uint8_t *dummy, uint8_t ide_action, uint8_t value) { if (dummy || ide_action || value) {}; }
+void ide_write16(uint8_t *dummy, uint8_t ide_action, uint16_t value) { if (dummy || ide_action || value) {}; }
+void ide_reset_begin(uint8_t *dummy) { if (dummy) {}; }
+
+uint8_t *ide_allocate(const char *name) { if (name) {}; return NULL; }
+
+void ide_attach_hdf(uint8_t *dummy, uint32_t idx, uint32_t fd) {
+ if (dummy || idx || fd) {};
+ printf("[!!!IDE] No IDE emulation layer available, HDF image not attached.\n");
+ return;
+}
+
+void ide_attach(uint8_t *dummy, uint32_t idx, uint32_t fd) {
+ if (dummy || idx || fd) {};
+ printf("[!!!IDE] No IDE emulation layer available, image not mounted.\n");
+ return;
+}
+#else
+static struct ide_controller *ide0 = NULL;
+#endif
uint8_t gary_cfg[8];
-uint8_t gayle_a4k = 0xA0;
-uint16_t gayle_a4k_irq;
uint8_t ramsey_cfg = 0x08;
static uint8_t ramsey_id = RAMSEY_REV7;
int counter;
static uint8_t gayle_irq, gayle_cs, gayle_cs_mask, gayle_cfg;
-static struct ide_controller *ide0;
int fd;
uint8_t rtc_type = RTC_TYPE_RICOH;
uint8_t cdtv_mode = 0;
unsigned char cdtv_sram[32 * SIZE_KILO];
-uint8_t gayle_int;
+uint8_t gayle_a4k = 0xA0;
+uint16_t gayle_a4k_irq = 0;
+uint8_t gayle_a4k_int = 0;
+uint8_t gayle_int = 0;
uint32_t gayle_ide_mask = ~GDATA;
uint32_t gayle_ide_base = GDATA;
+uint8_t gayle_ide_enabled = 1;
+uint8_t gayle_emulation_enabled = 1;
uint8_t gayle_ide_adj = 0;
-struct ide_controller *get_ide(int index) {
- //if (index) {}
- return ide0;
-}
-
void adjust_gayle_4000() {
- gayle_ide_base = GDATA_A4000;
+ gayle_ide_base = GAYLE_IDE_BASE_A4000;
gayle_ide_adj = 2;
+ gayle_a4k_int = 1;
}
void adjust_gayle_1200() {
-
}
void set_hard_drive_image_file_amiga(uint8_t index, char *filename) {
}
void InitGayle(void) {
- if (!hdd_image_file[0]) {
- hdd_image_file[0] = calloc(1, 64);
- sprintf(hdd_image_file[0], "hd0.img");
- }
+ uint8_t num_ide_drives = 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) {
+ if (!ide0)
+ ide0 = ide_allocate("cf");
+ }
- 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]);
+ 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);
+ num_ide_drives++;
+ }
+ else {
+ printf("Attaching HDD image with header.\n");
+ ide_attach(ide0, i, fd);
+ num_ide_drives++;
+ }
+ printf("[HDD%d] HDD Image %s attached\n", i, hdd_image_file[i]);
+ }
+ }
}
-}
-
-uint8_t CheckIrq(void) {
- uint8_t irq;
+ if (ide0)
+ ide_reset_begin(ide0);
- if (gayle_int & (1 << 7)) {
- irq = ide0->drive->intrq;
- // if (irq==0)
- // printf("IDE IRQ: %x\n",irq);
- return irq;
- };
- return 0;
+ if (num_ide_drives == 0) {
+ // No IDE drives mounted, disable IDE component of Gayle
+ printf("No IDE drives mounted, disabling Gayle IDE component.\n");
+ gayle_ide_enabled = 0;
+ }
}
static uint8_t ide_action = 0;
void writeGayleB(unsigned int address, unsigned int value) {
- if (address >= gayle_ide_base) {
- switch (address - gayle_ide_base + gayle_ide_adj) {
- case GFEAT_OFFSET:
- ide_action = ide_feature_w;
- goto idewrite8;
- case GCMD_OFFSET:
- ide_action = ide_command_w;
- goto idewrite8;
- case GSECTCOUNT_OFFSET:
- ide_action = ide_sec_count;
- goto idewrite8;
- case GSECTNUM_OFFSET:
- ide_action = ide_sec_num;
- goto idewrite8;
- case GCYLLOW_OFFSET:
- ide_action = ide_cyl_low;
- goto idewrite8;
- case GCYLHIGH_OFFSET:
- ide_action = ide_cyl_hi;
- goto idewrite8;
- case GDEVHEAD_OFFSET:
- ide_action = ide_dev_head;
- goto idewrite8;
- case GCTRL_OFFSET:
- ide_action = ide_devctrl_w;
- goto idewrite8;
- case GIRQ_4000_OFFSET:
- gayle_a4k_irq = value;
- case GIRQ_OFFSET:
- gayle_irq = (gayle_irq & value) | (value & (GAYLE_IRQ_RESET | GAYLE_IRQ_BERR));
- return;
- }
- goto skip_idewrite8;
+ if (ide0) {
+ 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:
+ ide_action = ide_sec_count;
+ goto idewrite8;
+ case GSECTNUM_OFFSET:
+ ide_action = ide_sec_num;
+ goto idewrite8;
+ case GCYLLOW_OFFSET:
+ ide_action = ide_cyl_low;
+ goto idewrite8;
+ case GCYLHIGH_OFFSET:
+ 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:
+ gayle_a4k_irq = value;
+ // Fallthrough
+ case GIRQ_OFFSET:
+ gayle_irq = (gayle_irq & value) | (value & (GAYLE_IRQ_RESET | GAYLE_IRQ_BERR));
+ return;
+ }
+ goto skip_idewrite8;
idewrite8:;
- ide_write8(ide0, ide_action, value);
- return;
+ ide_write8(ide0, ide_action, value);
+ return;
skip_idewrite8:;
+ }
}
switch (address) {
- case 0xDD203A:
+ /*case 0xDD203A:
+ printf("Write bye to A4000 Gayle: %.2X\n", value);
gayle_a4k = value;
- return;
+ 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:
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;
}
return;
}
- printf("Write Byte to Gayle Space 0x%06x (0x%06x)\n", address, value);
+ DEBUG("Write Byte to Gayle Space 0x%06x (0x%06x)\n", address, value);
}
void writeGayle(unsigned int address, unsigned int value) {
- if (address - gayle_ide_base == GDATA_OFFSET) {
- ide_write16(ide0, ide_data, value);
- return;
- }
+ if (ide0) {
+ if (address - gayle_ide_base == GDATA_OFFSET) {
+ ide_write16(ide0, ide_data, value);
+ return;
+ }
- if (address == GIRQ_A4000) {
- gayle_a4k_irq = value;
- return;
+ if (address == GIRQ_A4000) {
+ gayle_a4k_irq = value;
+ return;
+ }
}
if ((address & GAYLEMASK) == CLOCKBASE) {
return;
}
- printf("Write Word to Gayle Space 0x%06x (0x%06x)\n", address, value);
+ DEBUG("Write Word to Gayle Space 0x%06x (0x%06x)\n", address, value);
}
void writeGayleL(unsigned int address, unsigned int value) {
return;
}
- printf("Write Long to Gayle Space 0x%06x (0x%06x)\n", address, value);
+ DEBUG("Write Long to Gayle Space 0x%06x (0x%06x)\n", address, value);
}
uint8_t readGayleB(unsigned int address) {
- uint8_t ide_action = 0;
-
- if (address >= gayle_ide_base + gayle_ide_adj) {
- switch (address - gayle_ide_base) {
- case GERROR_OFFSET:
- ide_action = ide_error_r;
- goto ideread8;
- case GSTATUS_OFFSET:
- ide_action = ide_status_r;
- goto ideread8;
- case GSECTCOUNT_OFFSET:
- ide_action = ide_sec_count;
- goto ideread8;
- case GSECTNUM_OFFSET:
- ide_action = ide_sec_num;
- goto ideread8;
- case GCYLLOW_OFFSET:
- ide_action = ide_cyl_low;
- goto ideread8;
- case GCYLHIGH_OFFSET:
- ide_action = ide_cyl_hi;
- goto ideread8;
- case GDEVHEAD_OFFSET:
- ide_action = ide_dev_head;
- goto ideread8;
- case GCTRL_OFFSET:
- ide_action = ide_altst_r;
- goto ideread8;
- case GIRQ_4000_OFFSET:
- case GIRQ_OFFSET:
- return 0x80;
- //gayle_irq = (gayle_irq & value) | (value & (GAYLE_IRQ_RESET | GAYLE_IRQ_BERR));
- }
- goto skip_ideread8;
+ if (ide0) {
+ uint8_t ide_action = 0, ide_val = 0;
+
+ if (address >= gayle_ide_base) {
+ switch ((address - gayle_ide_base) - gayle_ide_adj) {
+ case GERROR_OFFSET:
+ ide_action = ide_error_r;
+ goto ideread8;
+ case GSTATUS_OFFSET:
+ ide_action = ide_status_r;
+ goto ideread8;
+ case GSECTCOUNT_OFFSET:
+ ide_action = ide_sec_count;
+ goto ideread8;
+ case GSECTNUM_OFFSET:
+ ide_action = ide_sec_num;
+ goto ideread8;
+ case GCYLLOW_OFFSET:
+ ide_action = ide_cyl_low;
+ goto ideread8;
+ case GCYLHIGH_OFFSET:
+ ide_action = ide_cyl_hi;
+ goto ideread8;
+ case GDEVHEAD_OFFSET:
+ ide_action = ide_dev_head;
+ goto ideread8;
+ case GCTRL_OFFSET:
+ ide_action = ide_altst_r;
+ goto ideread8;
+ case GIRQ_4000_OFFSET:
+ case GIRQ_OFFSET:
+ return 0x80;
+ //gayle_irq = (gayle_irq & value) | (value & (GAYLE_IRQ_RESET | GAYLE_IRQ_BERR));
+ }
+ goto skip_ideread8;
ideread8:;
- return ide_read8(ide0, ide_action);
+ ide_val = ide_read8(ide0, ide_action);
+ return ide_val;
skip_ideread8:;
- }
+ }
- switch (address) {
- case GIDENT: {
- uint8_t val;
- // printf("Read Byte from Gayle Ident 0x%06x (0x%06x)\n",address,counter);
- if (counter == 0 || counter == 1 || counter == 3) {
- val = 0x80; // 80; to enable gayle
- } else {
- val = 0x00;
+ switch (address) {
+ case GIDENT: {
+ uint8_t val;
+ if (counter == 0 || counter == 1 || counter == 3) {
+ val = 0x80; // 80; to enable gayle
+ } else {
+ val = 0x00;
+ }
+ counter++;
+ //printf("Read from GIDENT: %.2X.\n", val);
+ return val;
}
- counter++;
- return val;
- }
- case GINT:
- return gayle_int;
- case GCONF:
- return gayle_cfg & 0x0f;
- case GCS: {
- uint8_t v;
- v = gayle_cs_mask | gayle_cs;
- return v;
- }
- // This seems incorrect, GARY_REG3 is the same as GIDENT, and the A4000
- // service manual says that Gary is accessible in the address range $DFC000 to $DFFFFF.
- case GARY_REG0:
- case GARY_REG1:
- case GARY_REG2:
- return gary_cfg[address - GARY_REG0];
- break;
- //case GARY_REG3:
- case GARY_REG4:
- //case GARY_REG5:
- return gary_cfg[address - GARY_REG3];
- case RAMSEY_ID:
- return ramsey_id;
- case RAMSEY_REG:
- return ramsey_cfg;
- case GARY_REG5: { // This makes no sense.
- uint8_t val;
- printf("Read Byte from GARY Ident 0x%06x (0x%06x)\n",address,counter);
- if (counter == 0 || counter == 1 || counter == 3) {
- val = 0x80; // 80; to enable GARY
- } else {
- val = 0x00;
+ 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
+ // service manual says that Gary is accessible in the address range $DFC000 to $DFFFFF.
+ case GARY_REG0:
+ case GARY_REG1:
+ case GARY_REG2:
+ return gary_cfg[address - GARY_REG0];
+ break;
+ //case GARY_REG3:
+ case GARY_REG4:
+ //case GARY_REG5:
+ return gary_cfg[address - GARY_REG4];
+ case RAMSEY_ID:
+ return ramsey_id;
+ case RAMSEY_REG:
+ return ramsey_cfg;
+ case GARY_REG5: { // This makes no sense.
+ uint8_t val;
+ if (counter == 0 || counter == 1 || counter == 3) {
+ val = 0x80; // 80; to enable GARY
+ } else {
+ val = 0x00;
+ }
+ counter++;
+ return val;
}
- counter++;
- return val;
+ //case 0xDD203A:
+ // This can't be correct, as this is the same address as GDEVHEAD on the A4000 Gayle.
+ //printf("Read Byte from Gayle A4k: %.2X\n", gayle_a4k);
+ //return gayle_a4k;
}
- case 0xDD203A:
- return gayle_a4k;
}
if ((address & GAYLEMASK) == CLOCKBASE) {
return get_rtc_byte(address, rtc_type);
}
- printf("Read Byte From Gayle Space 0x%06x\n", address);
+ DEBUG("Read Byte From Gayle Space 0x%06x\n", address);
return 0xFF;
}
uint16_t readGayle(unsigned int address) {
- if (address - gayle_ide_base == GDATA_OFFSET) {
- uint16_t value;
- value = ide_read16(ide0, ide_data);
- // value = (value << 8) | (value >> 8);
- return value;
- }
+ if (ide0) {
+ if (address - gayle_ide_base == GDATA_OFFSET) {
+ uint16_t value;
+ value = ide_read16(ide0, ide_data);
+ // value = (value << 8) | (value >> 8);
+ return value;
+ }
- if (address == GIRQ_A4000) {
- gayle_a4k_irq = 0x8000;
- return 0x80FF;
+ if (address == GIRQ_A4000) {
+ gayle_a4k_irq = 0x8000;
+ return 0x8000;
+ }
}
if ((address & GAYLEMASK) == CLOCKBASE) {
return ((get_rtc_byte(address, rtc_type) << 8) | (get_rtc_byte(address + 1, rtc_type)));
}
- printf("Read Word From Gayle Space 0x%06x\n", address);
+ DEBUG("Read Word From Gayle Space 0x%06x\n", address);
return 0x8000;
}
return ((get_rtc_byte(address, rtc_type) << 24) | (get_rtc_byte(address + 1, rtc_type) << 16) | (get_rtc_byte(address + 2, rtc_type) << 8) | (get_rtc_byte(address + 3, rtc_type)));
}
- printf("Read Long From Gayle Space 0x%06x\n", address);
+ DEBUG("Read Long From Gayle Space 0x%06x\n", address);
return 0x8000;
}