all repos — mgba @ a557fb6ca67b023be01e306a69a2f3e1c79ab79a

mGBA Game Boy Advance Emulator

DS Slot-1: Start implementing SPI
Vicki Pfau vi@endrift.com
Wed, 22 Feb 2017 21:03:03 -0800
commit

a557fb6ca67b023be01e306a69a2f3e1c79ab79a

parent

5eba4f2b869c0f3cfb436970f50291f8616dc846

5 files changed, 147 insertions(+), 2 deletions(-)

jump to
M include/mgba/internal/ds/slot1.hinclude/mgba/internal/ds/slot1.h

@@ -11,27 +11,51 @@

CXX_GUARD_START #include <mgba/core/log.h> +#include <mgba/core/timing.h> 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
M src/ds/io.csrc/ds/io.c

@@ -111,6 +111,15 @@ mLOG(DS_IO, GAME_ERROR, "Invalid cart access");

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 @@ case DS_REG_IF_HI:

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 @@ case DS9_REG_SQRT_RESULT_HI:

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); }
M src/ds/memory.csrc/ds/memory.c

@@ -195,10 +195,12 @@ ds->memory.wramBase9 = NULL;

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);
M src/ds/slot1.csrc/ds/slot1.c

@@ -11,8 +11,21 @@ #include <mgba-util/vfs.h>

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 @@ }

} 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 @@ ds->memory.slot1.transferSize = DSSlot1ROMCNTGetBlockSize(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 @@ LOAD_32(result, 0, ds->memory.slot1.readBuffer);

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); + } +}
M src/ds/spi.csrc/ds/spi.c

@@ -51,6 +51,7 @@ ds->memory.io7[DS7_REG_SPICNT >> 1] = control;

} 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];