diff --git a/include/mgba/internal/ds/ds.h b/include/mgba/internal/ds/ds.h index 363c8350b..1b4211019 100644 --- a/include/mgba/internal/ds/ds.h +++ b/include/mgba/internal/ds/ds.h @@ -155,6 +155,7 @@ void DSAttachDebugger(struct DS* ds, struct mDebugger* debugger); void DSDetachDebugger(struct DS* ds); bool DSLoadROM(struct DS* ds, struct VFile* vf); +bool DSLoadSave(struct DS* ds, struct VFile* vf); void DSUnloadROM(struct DS* ds); void DSApplyPatch(struct DS* ds, struct Patch* patch); diff --git a/include/mgba/internal/ds/slot1.h b/include/mgba/internal/ds/slot1.h index c1e45795f..d3fa1f472 100644 --- a/include/mgba/internal/ds/slot1.h +++ b/include/mgba/internal/ds/slot1.h @@ -36,6 +36,7 @@ enum DSSavedataType { DS_SAVEDATA_FLASH = 3 }; +struct VFile; struct DSSlot1 { uint8_t command[8]; uint32_t address; @@ -48,10 +49,17 @@ struct DSSlot1 { bool spiHoldEnabled; uint8_t spiCommand; uint8_t statusReg; + int spiAddressingRemaining; + uint32_t spiAddress; + + uint8_t* spiData; + struct VFile* spiVf; + struct VFile* spiRealVf; }; struct DS; struct DSCommon; +void DSSlot1SPIInit(struct DS* ds, struct VFile* vf); void DSSlot1Reset(struct DS* ds); DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config); DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control); diff --git a/src/ds/core.c b/src/ds/core.c index c8d7569d6..b298c7dfe 100644 --- a/src/ds/core.c +++ b/src/ds/core.c @@ -163,7 +163,7 @@ static bool _DSCoreLoadBIOS(struct mCore* core, struct VFile* vf, int type) { } static bool _DSCoreLoadSave(struct mCore* core, struct VFile* vf) { - return false; + return DSLoadSave(core->board, vf); } static bool _DSCoreLoadPatch(struct mCore* core, struct VFile* vf) { diff --git a/src/ds/ds.c b/src/ds/ds.c index 09bf87426..e1af5c613 100644 --- a/src/ds/ds.c +++ b/src/ds/ds.c @@ -201,6 +201,7 @@ static void DSInit(void* cpu, struct mCPUComponent* component) { ds->rumble = NULL; ds->romVf = NULL; + DSSlot1SPIInit(ds, NULL); ds->keyCallback = NULL; @@ -444,6 +445,11 @@ bool DSLoadROM(struct DS* ds, struct VFile* vf) { return true; } +bool DSLoadSave(struct DS* ds, struct VFile* sav) { + DSSlot1SPIInit(ds, sav); + return true; +} + bool DSIsROM(struct VFile* vf) { if (vf->seek(vf, DS_ROM_MAGIC_OFFSET, SEEK_SET) < 0) { return false; diff --git a/src/ds/slot1.c b/src/ds/slot1.c index 21a3b3cad..024195f4b 100644 --- a/src/ds/slot1.c +++ b/src/ds/slot1.c @@ -7,17 +7,26 @@ #include #include +#include #include mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1"); static void _slot1SPI(struct mTiming*, void* context, uint32_t cyclesLate); +static bool _slot1GuaranteeSize(struct DSSlot1*); -void DSSlot1Reset(struct DS* ds) { +void DSSlot1SPIInit(struct DS* ds, struct VFile* vf) { ds->memory.slot1.spiEvent.name = "DS Slot-1 SPI"; ds->memory.slot1.spiEvent.priority = 0x70; ds->memory.slot1.spiEvent.context = NULL; ds->memory.slot1.spiEvent.callback = _slot1SPI; + ds->memory.slot1.savedataType = DS_SAVEDATA_AUTODETECT; + ds->memory.slot1.spiVf = vf; + ds->memory.slot1.spiRealVf = vf; + ds->memory.slot1.spiData = NULL; +} + +void DSSlot1Reset(struct DS* ds) { ds->memory.slot1.statusReg = 0; ds->memory.slot1.spiCommand = 0; ds->memory.slot1.spiHoldEnabled = 0; @@ -132,6 +141,26 @@ void DSSlot1WriteSPI(struct DSCommon* dscore, uint8_t datum) { static uint8_t _slot1SPIAutodetect(struct DSCommon* dscore, uint8_t datum) { DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1]; mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X:%02X", control, dscore->p->memory.slot1.spiCommand, datum); + + 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; + } else { + if (!_slot1GuaranteeSize(&dscore->p->memory.slot1)) { + return 0xFF; + } + } + + switch (dscore->p->memory.slot1.spiCommand) { + case 0x03: // RD + return dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress++]; + case 0x02: // WR + dscore->p->memory.slot1.spiData[dscore->p->memory.slot1.spiAddress] = datum; + ++dscore->p->memory.slot1.spiAddress; + break; + } return 0xFF; } @@ -141,7 +170,7 @@ static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate struct DSCommon* dscore = context; DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1]; uint8_t oldValue = dscore->memory.io[DS_REG_AUXSPIDATA >> 1]; - uint8_t newValue = 0; + uint8_t newValue = 0xFF; if (!dscore->p->memory.slot1.spiCommand) { dscore->p->memory.slot1.spiCommand = oldValue; @@ -149,6 +178,8 @@ 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; + dscore->p->memory.slot1.spiAddressingRemaining = 16; } else { switch (dscore->p->memory.slot1.spiCommand) { case 0x04: // WRDI @@ -177,7 +208,23 @@ static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate dscore->ipc->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue; dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control; dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control; - if (DSSlot1AUXSPICNTIsDoIRQ(control)) { - DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_SLOT1); - } +} + +static bool _slot1GuaranteeSize(struct DSSlot1* slot1) { + if (!slot1->spiVf) { + return false; + } + if (slot1->spiAddress >= slot1->spiVf->size(slot1->spiVf)) { + size_t size = toPow2(slot1->spiAddress + 1); + if (slot1->spiData) { + slot1->spiVf->unmap(slot1->spiVf, slot1->spiData, slot1->spiVf->size(slot1->spiVf)); + slot1->spiData = NULL; + } + slot1->spiVf->truncate(slot1->spiVf, size); + // TODO: Write FFs + } + if (!slot1->spiData) { + slot1->spiData = slot1->spiVf->map(slot1->spiVf, slot1->spiVf->size(slot1->spiVf), MAP_WRITE); + } + return slot1->spiData; }