mirror of
https://github.com/captain-amygdala/pistorm.git
synced 2026-02-25 00:07:44 +00:00
448 lines
13 KiB
C
448 lines
13 KiB
C
// SPDX-License-Identifier: MIT
|
|
|
|
//
|
|
// Gayle.c
|
|
// Originally based on Omega's Gayle emulation,
|
|
// created by Matt Parsons on 06/03/2019.
|
|
// Copyright © 2019 Matt Parsons. All rights reserved.
|
|
//
|
|
|
|
#include "Gayle.h"
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <endian.h>
|
|
|
|
#include "platforms/shared/rtc.h"
|
|
#include "config_file/config_file.h"
|
|
|
|
#include "amiga-registers.h"
|
|
|
|
//#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 ramsey_cfg = 0x08;
|
|
static uint8_t ramsey_id = RAMSEY_REV7;
|
|
|
|
int counter;
|
|
static uint8_t gayle_irq, gayle_cs, gayle_cs_mask, gayle_cfg;
|
|
int fd;
|
|
|
|
uint8_t rtc_type = RTC_TYPE_RICOH;
|
|
|
|
char *hdd_image_file[GAYLE_MAX_HARDFILES];
|
|
|
|
uint8_t cdtv_mode = 0;
|
|
unsigned char cdtv_sram[32 * SIZE_KILO];
|
|
|
|
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;
|
|
|
|
void adjust_gayle_4000() {
|
|
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) {
|
|
if (hdd_image_file[index] != NULL)
|
|
free(hdd_image_file[index]);
|
|
hdd_image_file[index] = calloc(1, strlen(filename) + 1);
|
|
strcpy(hdd_image_file[index], filename);
|
|
}
|
|
|
|
void InitGayle(void) {
|
|
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");
|
|
}
|
|
|
|
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]);
|
|
}
|
|
}
|
|
}
|
|
if (ide0)
|
|
ide_reset_begin(ide0);
|
|
|
|
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 (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;
|
|
skip_idewrite8:;
|
|
}
|
|
}
|
|
|
|
switch (address) {
|
|
/*case 0xDD203A:
|
|
printf("Write bye to A4000 Gayle: %.2X\n", value);
|
|
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:
|
|
ramsey_cfg = value & 0x0F;
|
|
return;
|
|
case GINT:
|
|
gayle_int = value;
|
|
return;
|
|
case GCS:
|
|
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;
|
|
}
|
|
|
|
if ((address & GAYLEMASK) == CLOCKBASE) {
|
|
if ((address & CLOCKMASK) >= 0x8000) {
|
|
if (cdtv_mode) {
|
|
//printf("[CDTV] BYTE write to SRAM @%.8X (%.8X): %.2X\n", (address & CLOCKMASK) - 0x8000, address, value);
|
|
cdtv_sram[(address & CLOCKMASK) - 0x8000] = value;
|
|
}
|
|
return;
|
|
}
|
|
//printf("Byte write to RTC.\n");
|
|
put_rtc_byte(address, value, rtc_type);
|
|
return;
|
|
}
|
|
|
|
DEBUG("Write Byte to Gayle Space 0x%06x (0x%06x)\n", address, value);
|
|
}
|
|
|
|
void writeGayle(unsigned int address, unsigned int value) {
|
|
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 & GAYLEMASK) == CLOCKBASE) {
|
|
if ((address & CLOCKMASK) >= 0x8000) {
|
|
if (cdtv_mode) {
|
|
//printf("[CDTV] WORD write to SRAM @%.8X (%.8X): %.4X\n", (address & CLOCKMASK) - 0x8000, address, htobe16(value));
|
|
((short *) ((size_t)(cdtv_sram + (address & CLOCKMASK) - 0x8000)))[0] = htobe16(value);
|
|
}
|
|
return;
|
|
}
|
|
//printf("Word write to RTC.\n");
|
|
put_rtc_byte(address + 1, (value & 0xFF), rtc_type);
|
|
put_rtc_byte(address, (value >> 8), rtc_type);
|
|
return;
|
|
}
|
|
|
|
DEBUG("Write Word to Gayle Space 0x%06x (0x%06x)\n", address, value);
|
|
}
|
|
|
|
void writeGayleL(unsigned int address, unsigned int value) {
|
|
if ((address & GAYLEMASK) == CLOCKBASE) {
|
|
if ((address & CLOCKMASK) >= 0x8000) {
|
|
if (cdtv_mode) {
|
|
//printf("[CDTV] LONGWORD write to SRAM @%.8X (%.8X): %.8X\n", (address & CLOCKMASK) - 0x8000, address, htobe32(value));
|
|
((int *) (size_t)(cdtv_sram + (address & CLOCKMASK) - 0x8000))[0] = htobe32(value);
|
|
}
|
|
return;
|
|
}
|
|
//printf("Longword write to RTC.\n");
|
|
put_rtc_byte(address + 3, (value & 0xFF), rtc_type);
|
|
put_rtc_byte(address + 2, ((value & 0x0000FF00) >> 8), rtc_type);
|
|
put_rtc_byte(address + 1, ((value & 0x00FF0000) >> 16), rtc_type);
|
|
put_rtc_byte(address, (value >> 24), rtc_type);
|
|
return;
|
|
}
|
|
|
|
DEBUG("Write Long to Gayle Space 0x%06x (0x%06x)\n", address, value);
|
|
}
|
|
|
|
uint8_t readGayleB(unsigned int address) {
|
|
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:;
|
|
ide_val = ide_read8(ide0, ide_action);
|
|
return ide_val;
|
|
skip_ideread8:;
|
|
}
|
|
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
//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;
|
|
}
|
|
}
|
|
|
|
if ((address & GAYLEMASK) == CLOCKBASE) {
|
|
if ((address & CLOCKMASK) >= 0x8000) {
|
|
if (cdtv_mode) {
|
|
//printf("[CDTV] BYTE read from SRAM @%.8X (%.8X): %.2X\n", (address & CLOCKMASK) - 0x8000, address, cdtv_sram[(address & CLOCKMASK) - 0x8000]);
|
|
return cdtv_sram[(address & CLOCKMASK) - 0x8000];
|
|
}
|
|
return 0;
|
|
}
|
|
//printf("Byte read from RTC.\n");
|
|
return get_rtc_byte(address, rtc_type);
|
|
}
|
|
|
|
DEBUG("Read Byte From Gayle Space 0x%06x\n", address);
|
|
return 0xFF;
|
|
}
|
|
|
|
uint16_t readGayle(unsigned int address) {
|
|
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 0x8000;
|
|
}
|
|
}
|
|
|
|
if ((address & GAYLEMASK) == CLOCKBASE) {
|
|
if ((address & CLOCKMASK) >= 0x8000) {
|
|
if (cdtv_mode) {
|
|
//printf("[CDTV] WORD read from SRAM @%.8X (%.8X): %.4X\n", (address & CLOCKMASK) - 0x8000, address, be16toh( (( unsigned short *) (size_t)(cdtv_sram + (address & CLOCKMASK) - 0x8000))[0]));
|
|
return be16toh( (( unsigned short *) (size_t)(cdtv_sram + (address & CLOCKMASK) - 0x8000))[0]);
|
|
}
|
|
return 0;
|
|
}
|
|
//printf("Word read from RTC.\n");
|
|
return ((get_rtc_byte(address, rtc_type) << 8) | (get_rtc_byte(address + 1, rtc_type)));
|
|
}
|
|
|
|
DEBUG("Read Word From Gayle Space 0x%06x\n", address);
|
|
return 0x8000;
|
|
}
|
|
|
|
uint32_t readGayleL(unsigned int address) {
|
|
if ((address & GAYLEMASK) == CLOCKBASE) {
|
|
if ((address & CLOCKMASK) >= 0x8000) {
|
|
if (cdtv_mode) {
|
|
//printf("[CDTV] LONGWORD read from SRAM @%.8X (%.8X): %.8X\n", (address & CLOCKMASK) - 0x8000, address, be32toh( (( unsigned short *) (size_t)(cdtv_sram + (address & CLOCKMASK) - 0x8000))[0]));
|
|
return be32toh( (( unsigned int *) (size_t)(cdtv_sram + (address & CLOCKMASK) - 0x8000))[0]);
|
|
}
|
|
return 0;
|
|
}
|
|
//printf("Longword read from RTC.\n");
|
|
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)));
|
|
}
|
|
|
|
DEBUG("Read Long From Gayle Space 0x%06x\n", address);
|
|
return 0x8000;
|
|
}
|