DS Slot-1: Start implementing SPI

This commit is contained in:
Vicki Pfau 2017-02-22 21:03:03 -08:00
parent 5eba4f2b86
commit a557fb6ca6
5 changed files with 147 additions and 2 deletions

View File

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

View File

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

View File

@ -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);

View File

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

View File

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