From 5bb811a16e32a8061319af6c772a941aad9bfaa1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 12 Apr 2017 00:55:31 -0700 Subject: [PATCH] DS Slot-1: Improve savedata setup via IPC sniffing --- CHANGES | 1 + include/mgba/internal/ds/slot1.h | 3 +- src/ds/ipc.c | 13 ++++++ src/ds/slot1.c | 78 +++++++++++++++++++++++--------- 4 files changed, 72 insertions(+), 23 deletions(-) diff --git a/CHANGES b/CHANGES index 2f4241d6e..1021a6da4 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,7 @@ medusa alpha 2: (Future) Features: - DS Audio: Add PSG audio - DS Video: Add display capture blending + - DS Slot-1: Improve savedata setup via IPC sniffing Bugfixes: - DS Video: Fix VRAM mirroring in the renderer (fixes mgba.io/i/561) - DS Video: Fix extended modes 1.x screen base range (fixes mgba.io/i/568) diff --git a/include/mgba/internal/ds/slot1.h b/include/mgba/internal/ds/slot1.h index 4652ee9a4..ba54c317d 100644 --- a/include/mgba/internal/ds/slot1.h +++ b/include/mgba/internal/ds/slot1.h @@ -55,7 +55,7 @@ struct DSSlot1 { uint8_t statusReg; int spiAddressingRemaining; uint32_t spiAddress; - int32_t spiAddressingPc; + int spiAddressingBits; uint8_t* spiData; struct VFile* spiVf; @@ -70,6 +70,7 @@ void DSSlot1Reset(struct DS* ds); DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config); DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control); void DSSlot1WriteSPI(struct DSCommon* dscore, uint8_t datum); +void DSSlot1ConfigureSPI(struct DS* ds, uint32_t paramPtr); struct GBADMA; void DSSlot1ScheduleDMA(struct DSCommon* dscore, int number, struct GBADMA* info); diff --git a/src/ds/ipc.c b/src/ds/ipc.c index d830dc85c..506ff9a8b 100644 --- a/src/ds/ipc.c +++ b/src/ds/ipc.c @@ -10,6 +10,16 @@ mLOG_DEFINE_CATEGORY(DS_IPC, "DS IPC", "ds.ipc"); +static void _parseIPC(struct DS* ds, uint32_t value) { + switch (value & 0x1F) { + case 0x0B: // Savedata + if (value & ~0x3F && ds->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) { + DSSlot1ConfigureSPI(ds, value >> 6); + } + break; + } +} + void DSIPCWriteSYNC(struct ARMCore* remoteCpu, uint16_t* remoteIo, int16_t value) { remoteIo[DS_REG_IPCSYNC >> 1] &= 0xFFF0; remoteIo[DS_REG_IPCSYNC >> 1] |= (value >> 8) & 0x0F; @@ -41,6 +51,9 @@ void DSIPCWriteFIFO(struct DSCommon* dscore, int32_t value) { return; } mLOG(DS_IPC, DEBUG, "Written from ARM%c: %08X", (dscore == &dscore->p->ds7) ? '7' : '9', value); + if (!dscore->p->isHomebrew && dscore == &dscore->p->ds9) { + _parseIPC(dscore->p, value); + } CircleBufferWrite32(&dscore->ipc->fifo, value); size_t fullness = CircleBufferSize(&dscore->ipc->fifo); if (fullness == 4) { diff --git a/src/ds/slot1.c b/src/ds/slot1.c index 0680afd60..c77c8b5bf 100644 --- a/src/ds/slot1.c +++ b/src/ds/slot1.c @@ -37,6 +37,7 @@ void DSSlot1Reset(struct DS* ds) { ds->memory.slot1.spiCommand = 0; ds->memory.slot1.spiHoldEnabled = 0; ds->memory.slot1.dmaSource = -1; + ds->memory.slot1.spiAddressingBits = 16; } static void _scheduleTransfer(struct DS* ds, struct mTiming* timing, uint32_t cyclesLate) { @@ -223,20 +224,8 @@ static uint8_t _slot1SPIAutodetect(struct DSCommon* dscore, uint8_t datum) { dscore->p->memory.slot1.spiAddress <<= 8; dscore->p->memory.slot1.spiAddress |= datum; dscore->p->memory.slot1.spiAddressingRemaining -= 8; - if (dscore->p->memory.slot1.spiAddressingPc >= 0) { - dscore->p->memory.slot1.spiAddressingPc = dscore->cpu->gprs[ARM_PC]; - } return 0xFF; - } else if (dscore->cpu->gprs[ARM_PC] == dscore->p->memory.slot1.spiAddressingPc) { - dscore->p->memory.slot1.spiAddress <<= 8; - dscore->p->memory.slot1.spiAddress |= datum; - dscore->p->memory.slot1.savedataType = DS_SAVEDATA_FLASH; - return 0xFF; - } else { - if (dscore->p->memory.slot1.spiAddress) { - // Cease autodetection - dscore->p->memory.slot1.spiAddressingPc = -1; - } + } else if (dscore->p->isHomebrew) { if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) { return 0xFF; } @@ -253,6 +242,29 @@ static uint8_t _slot1SPIAutodetect(struct DSCommon* dscore, uint8_t datum) { return 0xFF; } +static uint8_t _slot1SPIEEPROM(struct DSCommon* dscore, uint8_t datum) { + DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1]; + + if (dscore->p->memory.slot1.spiAddressingRemaining) { + dscore->p->memory.slot1.spiAddress <<= 8; + dscore->p->memory.slot1.spiAddress |= datum; + dscore->p->memory.slot1.spiAddressingRemaining -= 8; + return 0xFF; + } + + switch (dscore->p->memory.slot1.spiCommand) { + case 0x03: // RDLO + case 0x0B: // RDHI + return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++]; + case 0x02: // WRLO + case 0x0A: // WRHI + dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum; + ++dscore->p->memory.slot1.spiAddress; + break; + } + return 0xFF; +} + static uint8_t _slot1SPIFlash(struct DSCommon* dscore, uint8_t datum) { DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1]; @@ -261,7 +273,7 @@ static uint8_t _slot1SPIFlash(struct DSCommon* dscore, uint8_t datum) { dscore->p->memory.slot1.spiAddress |= datum; dscore->p->memory.slot1.spiAddressingRemaining -= 8; return 0xFF; - } else { + } else if (dscore->p->isHomebrew) { if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) { return 0xFF; } @@ -303,15 +315,12 @@ static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate if (oldValue == 0x0B && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) { dscore->p->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512; } - dscore->p->memory.slot1.spiAddress = 0; - switch (dscore->p->memory.slot1.savedataType) { - case DS_SAVEDATA_FLASH: - dscore->p->memory.slot1.spiAddressingRemaining = 24; - break; - default: - dscore->p->memory.slot1.spiAddressingRemaining = 16; - break; + if ((oldValue & 0x08) && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_EEPROM512) { + dscore->p->memory.slot1.spiAddress = 1; + } else { + dscore->p->memory.slot1.spiAddress = 0; } + dscore->p->memory.slot1.spiAddressingRemaining = dscore->p->memory.slot1.spiAddressingBits; } else { switch (dscore->p->memory.slot1.spiCommand) { case 0x04: // WRDI @@ -331,6 +340,10 @@ static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate case DS_SAVEDATA_FLASH: newValue = _slot1SPIFlash(dscore, oldValue); break; + case DS_SAVEDATA_EEPROM: + case DS_SAVEDATA_EEPROM512: + newValue = _slot1SPIEEPROM(dscore, oldValue); + break; default: mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X", control, oldValue); break; @@ -380,6 +393,27 @@ static bool _slot1GuaranteeSize(struct DSSlot1* slot1) { return slot1->spiData; } +void DSSlot1ConfigureSPI(struct DS* ds, uint32_t paramPtr) { + struct ARMCore* cpu = ds->ds7.cpu; + uint32_t saveParams = cpu->memory.load32(cpu, paramPtr + 4, NULL); + uint32_t size = 1 << ((saveParams & 0xFF00) >> 8); + if ((saveParams & 0xFF) == 2) { + ds->memory.slot1.savedataType = DS_SAVEDATA_FLASH; + } else { + ds->memory.slot1.savedataType = DS_SAVEDATA_EEPROM; + } + if (size >= 0x10000) { + ds->memory.slot1.spiAddressingBits = 24; + } else if (size <= 0x200) { + ds->memory.slot1.spiAddressingBits = 8; + ds->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512; + } else { + ds->memory.slot1.spiAddressingBits = 16; + } + ds->memory.slot1.spiAddress = size; + _slot1GuaranteeSize(&ds->memory.slot1); +} + void DSSlot1ScheduleDMA(struct DSCommon* dscore, int number, struct GBADMA* info) { UNUSED(info); dscore->p->memory.slot1.dmaSource = number;