From a557fb6ca67b023be01e306a69a2f3e1c79ab79a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 22 Feb 2017 21:03:03 -0800 Subject: [PATCH] DS Slot-1: Start implementing SPI --- include/mgba/internal/ds/slot1.h | 24 ++++++++ src/ds/io.c | 25 ++++++++ src/ds/memory.c | 2 + src/ds/slot1.c | 97 +++++++++++++++++++++++++++++++- src/ds/spi.c | 1 + 5 files changed, 147 insertions(+), 2 deletions(-) diff --git a/include/mgba/internal/ds/slot1.h b/include/mgba/internal/ds/slot1.h index b52fb918d..c1e45795f 100644 --- a/include/mgba/internal/ds/slot1.h +++ b/include/mgba/internal/ds/slot1.h @@ -11,27 +11,51 @@ CXX_GUARD_START #include +#include mLOG_DECLARE_CATEGORY(DS_SLOT1); DECL_BITFIELD(DSSlot1AUXSPICNT, uint16_t); +DECL_BITS(DSSlot1AUXSPICNT, Baud, 0, 2); +DECL_BIT(DSSlot1AUXSPICNT, CSHold, 6); +DECL_BIT(DSSlot1AUXSPICNT, Busy, 7); +DECL_BIT(DSSlot1AUXSPICNT, SPIMode, 13); +DECL_BIT(DSSlot1AUXSPICNT, DoIRQ, 14); +DECL_BIT(DSSlot1AUXSPICNT, Enable, 15); DECL_BITFIELD(DSSlot1ROMCNT, uint32_t); DECL_BIT(DSSlot1ROMCNT, WordReady, 23); DECL_BITS(DSSlot1ROMCNT, BlockSize, 24, 3); DECL_BIT(DSSlot1ROMCNT, BlockBusy, 31); +enum DSSavedataType { + DS_SAVEDATA_AUTODETECT = -1, + DS_SAVEDATA_FORCE_NONE = 0, + DS_SAVEDATA_EEPROM512 = 1, + DS_SAVEDATA_EEPROM = 2, + DS_SAVEDATA_FLASH = 3 +}; + struct DSSlot1 { uint8_t command[8]; uint32_t address; uint32_t transferSize; uint32_t transferRemaining; uint8_t readBuffer[4]; + + enum DSSavedataType savedataType; + struct mTimingEvent spiEvent; + bool spiHoldEnabled; + uint8_t spiCommand; + uint8_t statusReg; }; struct DS; +struct DSCommon; +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); uint32_t DSSlot1Read(struct DS* ds); CXX_GUARD_END diff --git a/src/ds/io.c b/src/ds/io.c index 9ad0959e4..972c6b792 100644 --- a/src/ds/io.c +++ b/src/ds/io.c @@ -111,6 +111,15 @@ static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t va return 0; } break; + case DS_REG_AUXSPIDATA: + if (dscore->memory.slot1Access) { + DSSlot1WriteSPI(dscore, value); + dscore->ipc->memory.io[address >> 1] = value; + } else { + mLOG(DS_IO, GAME_ERROR, "Invalid cart access"); + return 0; + } + break; case DS_REG_ROMCNT_HI: if (dscore->memory.slot1Access) { DSSlot1ROMCNT cnt = value << 16; @@ -369,6 +378,14 @@ uint16_t DS7IORead(struct DS* ds, uint32_t address) { case DS_REG_POSTFLG: // Handled transparently by the registers break; + case DS_REG_AUXSPICNT: + case DS_REG_AUXSPIDATA: + if (ds->ds7.memory.slot1Access) { + break; + } else { + mLOG(DS_IO, GAME_ERROR, "Invalid cart access"); + return 0; + } default: mLOG(DS_IO, STUB, "Stub DS7 I/O register read: %06X", address); } @@ -589,6 +606,14 @@ uint16_t DS9IORead(struct DS* ds, uint32_t address) { case DS_REG_POSTFLG: // Handled transparently by the registers break; + case DS_REG_AUXSPICNT: + case DS_REG_AUXSPIDATA: + if (ds->ds9.memory.slot1Access) { + break; + } else { + mLOG(DS_IO, GAME_ERROR, "Invalid cart access"); + return 0; + } default: mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address); } diff --git a/src/ds/memory.c b/src/ds/memory.c index 3c65a85cf..ad24818ea 100644 --- a/src/ds/memory.c +++ b/src/ds/memory.c @@ -195,10 +195,12 @@ void DSMemoryReset(struct DS* ds) { ds->memory.slot1Owner = true; ds->memory.slot2Owner = true; + ds->memory.slot1.savedataType = DS_SAVEDATA_AUTODETECT; ds->ds7.memory.slot1Access = true; ds->ds9.memory.slot1Access = false; DSSPIReset(ds); + DSSlot1Reset(ds); DSVideoConfigureVRAM(ds, 0, 0); DSVideoConfigureVRAM(ds, 1, 0); diff --git a/src/ds/slot1.c b/src/ds/slot1.c index 17660e363..21a3b3cad 100644 --- a/src/ds/slot1.c +++ b/src/ds/slot1.c @@ -11,8 +11,21 @@ mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1"); +static void _slot1SPI(struct mTiming*, void* context, uint32_t cyclesLate); + +void DSSlot1Reset(struct DS* ds) { + 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.statusReg = 0; + ds->memory.slot1.spiCommand = 0; + ds->memory.slot1.spiHoldEnabled = 0; +} + static void DSSlot1StepTransfer(struct DS* ds) { DSSlot1ROMCNT romcnt; + // TODO: Big endian LOAD_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7); if (ds->memory.slot1.transferRemaining) { ds->romVf->read(ds->romVf, ds->memory.slot1.readBuffer, 4); @@ -65,7 +78,12 @@ static void DSSlot1StartTransfer(struct DS* ds) { } DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config) { - mLOG(DS_SLOT1, STUB, "Unimplemented SPI AUX config: %04X", config); + if (DSSlot1AUXSPICNTIsSPIMode(config)) { + if (!ds->memory.slot1.spiHoldEnabled) { + ds->memory.slot1.spiCommand = 0; + } + ds->memory.slot1.spiHoldEnabled = DSSlot1AUXSPICNTIsCSHold(config); + } return config; } @@ -74,9 +92,15 @@ DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control) { if (ds->memory.slot1.transferSize != 0 && ds->memory.slot1.transferSize != 7) { ds->memory.slot1.transferSize = 0x100 << ds->memory.slot1.transferSize; } + + DSSlot1AUXSPICNT config = ds->memory.io7[DS_REG_AUXSPICNT >> 1]; + if (DSSlot1AUXSPICNTIsSPIMode(config)) { + mLOG(DS_SLOT1, STUB, "Bad ROMCNT?"); + return control; + } if (DSSlot1ROMCNTIsBlockBusy(control)) { DSSlot1StartTransfer(ds); - // TODO timing + // TODO: timing control = DSSlot1ROMCNTFillWordReady(control); } return control; @@ -88,3 +112,72 @@ uint32_t DSSlot1Read(struct DS* ds) { DSSlot1StepTransfer(ds); return result; } + +void DSSlot1WriteSPI(struct DSCommon* dscore, uint8_t datum) { + UNUSED(datum); + DSSlot1AUXSPICNT control = dscore->memory.io[DS_REG_AUXSPICNT >> 1]; + if (!DSSlot1AUXSPICNTIsSPIMode(control) || !DSSlot1AUXSPICNTIsEnable(control)) { + return; + } + uint32_t baud = 19 - DSSlot1AUXSPICNTGetBaud(control); + baud = DS_ARM7TDMI_FREQUENCY >> baud; // TODO: Right frequency for ARM9 + control = DSSlot1AUXSPICNTFillBusy(control); + mTimingDeschedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent); + mTimingSchedule(&dscore->timing, &dscore->p->memory.slot1.spiEvent, baud); + dscore->p->memory.slot1.spiEvent.context = dscore; + dscore->memory.io[DS_REG_AUXSPICNT >> 1] = control; + dscore->ipc->memory.io[DS_REG_AUXSPICNT >> 1] = control; +} + +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); + return 0xFF; +} + +static void _slot1SPI(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(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; + + if (!dscore->p->memory.slot1.spiCommand) { + dscore->p->memory.slot1.spiCommand = oldValue; + // Probably RDHI + if (oldValue == 0x0B && dscore->p->memory.slot1.savedataType == DS_SAVEDATA_AUTODETECT) { + dscore->p->memory.slot1.savedataType = DS_SAVEDATA_EEPROM512; + } + } else { + switch (dscore->p->memory.slot1.spiCommand) { + case 0x04: // WRDI + dscore->p->memory.slot1.statusReg &= ~2; + break; + case 0x05: // RDSR + newValue = dscore->p->memory.slot1.statusReg; + break; + case 0x06: // WREN + dscore->p->memory.slot1.statusReg |= 2; + break; + default: + switch (dscore->p->memory.slot1.savedataType) { + case DS_SAVEDATA_AUTODETECT: + newValue = _slot1SPIAutodetect(dscore, oldValue); + break; + default: + mLOG(DS_SLOT1, STUB, "Unimplemented SPI write: %04X:%02X", control, oldValue); + break; + } + } + } + + control = DSSlot1AUXSPICNTClearBusy(control); + dscore->memory.io[DS_REG_AUXSPIDATA >> 1] = newValue; + 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); + } +} diff --git a/src/ds/spi.c b/src/ds/spi.c index 31e43847a..06b3814ed 100644 --- a/src/ds/spi.c +++ b/src/ds/spi.c @@ -51,6 +51,7 @@ void DSSPIWrite(struct DS* ds, uint8_t datum) { } static void _tscEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); UNUSED(cyclesLate); struct DS* ds = context; uint8_t oldValue = ds->memory.io7[DS7_REG_SPIDATA >> 1];