mirror of https://github.com/mgba-emu/mgba.git
DS Slot-1: Start implementing SPI
This commit is contained in:
parent
5eba4f2b86
commit
a557fb6ca6
|
@ -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
|
||||
|
|
25
src/ds/io.c
25
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in New Issue