DS SPI: Implement firmware access

This commit is contained in:
Vicki Pfau 2017-02-23 16:36:59 -08:00
parent b77716738a
commit 5c3fc0ac0d
2 changed files with 69 additions and 10 deletions

View File

@ -49,14 +49,18 @@ enum {
struct DSSPIBus {
bool holdEnabled;
uint8_t firmwareMode;
uint8_t firmCommand;
uint8_t firmStatusReg;
int firmAddressingRemaining;
uint32_t firmAddress;
struct mTimingEvent tscEvent;
uint8_t tscControlByte;
uint16_t tscRegister;
int tscOffset;
uint8_t powmgrByte;
struct mTimingEvent event;
};
struct DS;

View File

@ -6,23 +6,26 @@
#include <mgba/internal/ds/spi.h>
#include <mgba/internal/ds/ds.h>
#include <mgba-util/vfs.h>
mLOG_DEFINE_CATEGORY(DS_SPI, "DS SPI");
static void _tscEvent(struct mTiming*, void* context, uint32_t cyclesLate);
static void _firmwareEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
void DSSPIReset(struct DS* ds) {
memset(&ds->memory.spiBus, 0, sizeof(ds->memory.spiBus));
ds->memory.spiBus.tscEvent.name = "DS SPI TSC";
ds->memory.spiBus.tscEvent.context = ds;
ds->memory.spiBus.tscEvent.callback = _tscEvent;
ds->memory.spiBus.tscEvent.priority = 0x60;
ds->memory.spiBus.event.name = "DS SPI Event";
ds->memory.spiBus.event.context = ds;
ds->memory.spiBus.event.callback = _tscEvent;
ds->memory.spiBus.event.priority = 0x60;
}
DSSPICNT DSSPIWriteControl(struct DS* ds, uint16_t control) {
// TODO
if (!ds->memory.spiBus.holdEnabled) {
ds->memory.spiBus.tscControlByte = 0;
ds->memory.spiBus.firmCommand = 0;
}
ds->memory.spiBus.holdEnabled = DSSPICNTIsCSHold(control);
return control;
@ -37,16 +40,19 @@ void DSSPIWrite(struct DS* ds, uint8_t datum) {
baud = DS_ARM7TDMI_FREQUENCY >> baud;
switch (DSSPICNTGetChipSelect(control)) {
case DS_SPI_DEV_TSC:
control = DSSPICNTFillBusy(control);
mTimingDeschedule(&ds->ds7.timing, &ds->memory.spiBus.tscEvent);
mTimingSchedule(&ds->ds7.timing, &ds->memory.spiBus.tscEvent, baud);
ds->memory.spiBus.event.callback = _tscEvent;
break;
case DS_SPI_DEV_FIRMWARE:
ds->memory.spiBus.event.callback = _firmwareEvent;
break;
case DS_SPI_DEV_POWERMAN:
case DS_SPI_DEV_FIRMWARE:
default:
mLOG(DS_SPI, STUB, "Unimplemented data write: %04X:%02X", control, datum);
break;
}
control = DSSPICNTFillBusy(control);
mTimingDeschedule(&ds->ds7.timing, &ds->memory.spiBus.event);
mTimingSchedule(&ds->ds7.timing, &ds->memory.spiBus.event, baud);
ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
}
@ -102,3 +108,52 @@ static void _tscEvent(struct mTiming* timing, void* context, uint32_t cyclesLate
DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
}
}
static void _firmwareEvent(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];
DSSPICNT control = ds->memory.io7[DS7_REG_SPICNT >> 1];
uint8_t newValue = 0;
if (!ds->memory.spiBus.firmCommand) {
ds->memory.spiBus.firmCommand = oldValue;
ds->memory.spiBus.firmAddress = 0;
ds->memory.spiBus.firmAddressingRemaining = 24;
} else if (ds->memory.spiBus.firmAddressingRemaining) {
ds->memory.spiBus.firmAddress <<= 8;
ds->memory.spiBus.firmAddress |= oldValue;
ds->memory.spiBus.firmAddressingRemaining -= 8;
ds->firmwareVf->seek(ds->firmwareVf, ds->memory.spiBus.firmAddress, SEEK_SET);
} else {
switch (ds->memory.spiBus.firmCommand) {
case 0x02: // WR
ds->firmwareVf->write(ds->firmwareVf, &oldValue, 1);
++ds->memory.spiBus.firmAddress;
break;
case 0x03: // RD
ds->firmwareVf->read(ds->firmwareVf, &newValue, 1);
++ds->memory.spiBus.firmAddress;
case 0x04: // WRDI
ds->memory.spiBus.firmStatusReg &= ~2;
break;
case 0x05: // RDSR
newValue = ds->memory.spiBus.firmStatusReg;
break;
case 0x06: // WREN
ds->memory.spiBus.firmStatusReg |= 2;
break;
default:
mLOG(DS_SPI, STUB, "Unimplemented Firmware write: %04X:%02X:%02X", control, ds->memory.spiBus.firmCommand, newValue);
break;
}
}
control = DSSPICNTClearBusy(control);
ds->memory.io7[DS7_REG_SPIDATA >> 1] = newValue;
ds->memory.io7[DS7_REG_SPICNT >> 1] = control;
if (DSSPICNTIsDoIRQ(control)) {
DSRaiseIRQ(ds->ds7.cpu, ds->ds7.memory.io, DS_IRQ_SPI);
}
}