GBA SIO: Move starting/end timing logic out of drivers

This commit is contained in:
Vicki Pfau 2024-05-27 23:11:22 -07:00
parent 435c4aa243
commit 621eb4d425
9 changed files with 104 additions and 103 deletions

View File

@ -119,6 +119,10 @@ struct GBASIODriver {
int (*deviceId)(struct GBASIODriver* driver);
uint16_t (*writeSIOCNT)(struct GBASIODriver* driver, uint16_t value);
uint16_t (*writeRCNT)(struct GBASIODriver* driver, uint16_t value);
bool (*start)(struct GBASIODriver* driver);
void (*finishMultiplayer)(struct GBASIODriver* driver, uint16_t data[4]);
uint8_t (*finishNormal8)(struct GBASIODriver* driver);
uint32_t (*finishNormal32)(struct GBASIODriver* driver);
};
enum GBASIOBattleChipGateFlavor {
@ -130,7 +134,6 @@ enum GBASIOBattleChipGateFlavor {
struct GBASIOBattlechipGate {
struct GBASIODriver d;
struct mTimingEvent event;
uint16_t chipId;
uint16_t data[2];
int state;

View File

@ -191,7 +191,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bits 2 - 3: GB Player inputs posted
* | bits 4 - 8: GB Player transmit position
* | bits 9 - 23: Reserved
* 0x002C4 - 0x002C7: Game Boy Player next event
* 0x002C4 - 0x002C7: SIO next event
* 0x002C8 - 0x002CB: Current DMA transfer word
* 0x002CC - 0x002CF: Last DMA transfer PC
* 0x002D0 - 0x002DF: Matrix memory command buffer
@ -370,7 +370,7 @@ struct GBASerializedState {
uint8_t lightSample;
GBASerializedHWFlags2 flags2;
GBASerializedHWFlags3 flags3;
uint32_t gbpNextEvent;
uint32_t sioNextEvent;
} hw;
uint32_t dmaTransferRegister;

View File

@ -21,7 +21,6 @@ struct GBASIOPlayer {
struct GBA* p;
unsigned inputsPosted;
int txPosition;
struct mTimingEvent event;
struct GBASIOPlayerKeyCallback callback;
bool oldOpposingDirections;
struct mKeyCallback* oldCallback;

View File

@ -486,10 +486,10 @@ void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASeria
flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState);
flags2 = GBASerializedHWFlags1SetLightCounter(flags2, hw->lightCounter);
// GBP stuff is only here for legacy reasons
// GBP/SIO stuff is only here for legacy reasons
flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->p->sio.gbp.inputsPosted);
flags2 = GBASerializedHWFlags2SetGbpTxPosition(flags2, hw->p->sio.gbp.txPosition);
STORE_32(hw->p->sio.gbp.event.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.gbpNextEvent);
STORE_32(hw->p->sio.completeEvent.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.sioNextEvent);
state->hw.flags2 = flags2;
}
@ -520,16 +520,16 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
hw->lightSample = state->hw.lightSample;
hw->lightEdge = GBASerializedHWFlags1GetLightEdge(flags1);
// GBP stuff is only here for legacy reasons
// GBP/SIO stuff is only here for legacy reasons
hw->p->sio.gbp.inputsPosted = GBASerializedHWFlags2GetGbpInputsPosted(state->hw.flags2);
hw->p->sio.gbp.txPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2);
uint32_t when;
LOAD_32(when, 0, &state->hw.gbpNextEvent);
LOAD_32(when, 0, &state->hw.sioNextEvent);
if (hw->devices & HW_GB_PLAYER) {
GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d, GBA_SIO_NORMAL_32);
if (hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) {
mTimingSchedule(&hw->p->timing, &hw->p->sio.gbp.event, when);
}
}
if ((hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) && when < 0x20000) {
mTimingSchedule(&hw->p->timing, &hw->p->sio.completeEvent, when);
}
}

View File

@ -37,25 +37,15 @@ static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
static uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value);
static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode);
static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver);
static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]);
void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
gate->d.init = NULL;
gate->d.deinit = NULL;
memset(&gate->d, 0, sizeof(gate->d));
gate->d.load = GBASIOBattlechipGateLoad;
gate->d.unload = NULL;
gate->d.writeSIOCNT = GBASIOBattlechipGateWriteSIOCNT;
gate->d.setMode = NULL;
gate->d.handlesMode = GBASIOBattlechipGateHandlesMode;
gate->d.connectedDevices = GBASIOBattlechipGateConnectedDevices;
gate->d.deviceId = NULL;
gate->d.writeRCNT = NULL;
gate->event.context = gate;
gate->event.callback = _battlechipTransferEvent;
gate->event.priority = 0x80;
gate->d.finishMultiplayer = GBASIOBattlechipGateFinishMultiplayer;
gate->chipId = 0;
gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE;
@ -70,12 +60,9 @@ bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
}
uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) {
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
UNUSED(driver);
value &= ~0xC;
value |= 0x8;
if (value & 0x80) {
_battlechipTransfer(gate);
}
return value;
}
@ -95,20 +82,8 @@ static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver) {
return 1;
}
void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
int32_t cycles = GBASIOTransferCycles(gate->d.p);
mTimingDeschedule(&gate->d.p->p->timing, &gate->event);
mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
}
void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
struct GBASIOBattlechipGate* gate = user;
if (gate->d.p->mode == GBA_SIO_NORMAL_32) {
GBASIONormal32FinishTransfer(gate->d.p, 0, cyclesLate);
return;
}
static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]) {
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
uint16_t cmd = gate->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)];
uint16_t reply = 0xFFFF;
@ -189,8 +164,8 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state);
++gate->state;
uint16_t data[4] = {
cmd, reply, 0xFFFF, 0xFFFF
};
GBASIOMultiplayerFinishTransfer(gate->d.p, data, cyclesLate);
data[0] = cmd;
data[1] = reply;
data[2] = 0xFFFF;
data[3] = 0xFFFF;
}

View File

@ -221,6 +221,17 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) {
}
}
static void _startTransfer(struct GBASIO* sio) {
if (sio->activeDriver && sio->activeDriver->start) {
if (!sio->activeDriver->start(sio->activeDriver)) {
// Transfer completion is handled internally to the driver
return;
}
}
mTimingDeschedule(&sio->p->timing, &sio->completeEvent);
mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio));
}
void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
if ((value ^ sio->siocnt) & 0x3000) {
sio->siocnt = value & 0x3000;
@ -256,6 +267,18 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
// investigation than I managed, apparently.
sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt);
if (GBASIOMultiplayerIsBusy(value) && !GBASIOMultiplayerIsBusy(sio->siocnt)) {
if (!id) {
sio->p->memory.io[GBA_REG(SIOMULTI0)] = 0xFFFF;
sio->p->memory.io[GBA_REG(SIOMULTI1)] = 0xFFFF;
sio->p->memory.io[GBA_REG(SIOMULTI2)] = 0xFFFF;
sio->p->memory.io[GBA_REG(SIOMULTI3)] = 0xFFFF;
sio->rcnt = GBASIORegisterRCNTClearSc(sio->rcnt);
_startTransfer(sio);
} else {
// TODO
}
}
break;
case GBA_SIO_NORMAL_8:
case GBA_SIO_NORMAL_32:
@ -264,6 +287,13 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
if (GBASIONormalGetSc(value)) {
sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt);
}
if (GBASIONormalIsStart(value) && !GBASIONormalIsStart(sio->siocnt)) {
if (GBASIONormalIsSc(value)) {
_startTransfer(sio);
} else {
// TODO
}
}
break;
default:
// TODO
@ -277,15 +307,6 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
case GBA_SIO_NORMAL_8:
case GBA_SIO_NORMAL_32:
value = GBASIONormalFillSi(value);
if ((value & 0x0081) == 0x0081) {
if (GBASIONormalIsIrq(value)) {
mTimingDeschedule(&sio->p->timing, &sio->completeEvent);
mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio));
} else {
// TODO: Test this on hardware to see if this is correct
value = GBASIONormalClearStart(value);
}
}
break;
case GBA_SIO_MULTI:
value = GBASIOMultiplayerFillReady(value);
@ -473,16 +494,29 @@ void GBASIONormal32FinishTransfer(struct GBASIO* sio, uint32_t data, uint32_t cy
static void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
struct GBASIO* sio = user;
uint16_t data[4] = {0, 0, 0, 0};
union {
uint16_t multi[4];
uint8_t normal8;
uint32_t normal32;
} data = {0};
switch (sio->mode) {
case GBA_SIO_MULTI:
GBASIOMultiplayerFinishTransfer(sio, data, cyclesLate);
if (sio->activeDriver && sio->activeDriver->finishMultiplayer) {
sio->activeDriver->finishMultiplayer(sio->activeDriver, data.multi);
}
GBASIOMultiplayerFinishTransfer(sio, data.multi, cyclesLate);
break;
case GBA_SIO_NORMAL_8:
GBASIONormal8FinishTransfer(sio, 0, cyclesLate);
if (sio->activeDriver && sio->activeDriver->finishNormal8) {
data.normal8 = sio->activeDriver->finishNormal8(sio->activeDriver);
}
GBASIONormal8FinishTransfer(sio, data.normal8, cyclesLate);
break;
case GBA_SIO_NORMAL_32:
GBASIONormal32FinishTransfer(sio, 0, cyclesLate);
if (sio->activeDriver && sio->activeDriver->finishNormal32) {
data.normal32 = sio->activeDriver->finishNormal32(sio->activeDriver);
}
GBASIONormal32FinishTransfer(sio, data.normal32, cyclesLate);
break;
default:
// TODO

View File

@ -33,15 +33,12 @@ static int32_t _processCommand(struct GBASIODolphin* dol, uint32_t cyclesLate);
static void _flush(struct GBASIODolphin* dol);
void GBASIODolphinCreate(struct GBASIODolphin* dol) {
memset(&dol->d, 0, sizeof(dol->d));
dol->d.init = GBASIODolphinInit;
dol->d.load = GBASIODolphinLoad;
dol->d.unload = GBASIODolphinUnload;
dol->d.writeSIOCNT = NULL;
dol->d.setMode = NULL;
dol->d.handlesMode = GBASIODolphinHandlesMode;
dol->d.connectedDevices = GBASIODolphinConnectedDevices;
dol->d.deviceId = NULL;
dol->d.writeSIOCNT = NULL;
dol->event.context = dol;
dol->event.name = "GB SIO Lockstep";
dol->event.callback = GBASIODolphinProcessEvents;

View File

@ -16,7 +16,8 @@ static uint16_t _gbpRead(struct mKeyCallback*);
static uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value);
static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode);
static int _gbpSioConnectedDevices(struct GBASIODriver* driver);
static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate);
static bool _gbpSioStart(struct GBASIODriver* driver);
static uint32_t _gbpSioFinishNormal32(struct GBASIODriver* driver);
static const uint8_t _logoPalette[] = {
0xDF, 0xFF, 0x0C, 0x64, 0x0C, 0xE4, 0x2D, 0xE4, 0x4E, 0x64, 0x4E, 0xE4, 0x6E, 0xE4, 0xAF, 0x68,
@ -45,20 +46,12 @@ void GBASIOPlayerInit(struct GBASIOPlayer* gbp) {
gbp->callback.d.readKeys = _gbpRead;
gbp->callback.d.requireOpposingDirections = true;
gbp->callback.p = gbp;
gbp->d.init = NULL;
gbp->d.deinit = NULL;
gbp->d.load = NULL;
gbp->d.unload = NULL;
memset(&gbp->d, 0, sizeof(gbp->d));
gbp->d.writeSIOCNT = _gbpSioWriteSIOCNT;
gbp->d.setMode = NULL;
gbp->d.handlesMode = _gbpSioHandlesMode;
gbp->d.connectedDevices = _gbpSioConnectedDevices;
gbp->d.deviceId = NULL;
gbp->d.writeRCNT = NULL;
gbp->event.context = gbp;
gbp->event.name = "GBA SIO Game Boy Player";
gbp->event.callback = _gbpSioProcessEvents;
gbp->event.priority = 0x80;
gbp->d.start = _gbpSioStart;
gbp->d.finishNormal32 = _gbpSioFinishNormal32;
}
void GBASIOPlayerReset(struct GBASIOPlayer* gbp) {
@ -108,13 +101,16 @@ uint16_t _gbpRead(struct mKeyCallback* callback) {
}
uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) {
UNUSED(driver);
return value & 0x78FB;
}
bool _gbpSioStart(struct GBASIODriver* driver) {
struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver;
if (value & 0x0080) {
uint32_t rx = gbp->p->memory.io[GBA_REG(SIODATA32_LO)] | (gbp->p->memory.io[GBA_REG(SIODATA32_HI)] << 16);
if (gbp->txPosition < 12 && gbp->txPosition > 0) {
// TODO: Check expected
} else if (gbp->txPosition >= 12) {
uint32_t mask = 0x33;
// 0x00 = Stop
// 0x11 = Hard Stop
// 0x22 = Start
@ -124,11 +120,7 @@ uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) {
gbp->p->lastRumble = currentTime;
}
}
mTimingDeschedule(&gbp->p->timing, &gbp->event);
mTimingSchedule(&gbp->p->timing, &gbp->event, 2048);
}
value &= 0x78FB;
return value;
return true;
}
static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) {
@ -141,9 +133,8 @@ static int _gbpSioConnectedDevices(struct GBASIODriver* driver) {
return 1;
}
void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
struct GBASIOPlayer* gbp = user;
uint32_t _gbpSioFinishNormal32(struct GBASIODriver* driver) {
struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver;
uint32_t tx = 0;
int txPosition = gbp->txPosition;
if (txPosition > 16) {
@ -154,5 +145,5 @@ void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLat
}
tx = _gbpTxData[txPosition];
++gbp->txPosition;
GBASIONormal32FinishTransfer(gbp->d.p, tx, cyclesLate);
return tx;
}

View File

@ -18,6 +18,7 @@ static bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver);
static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode);
static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver);
static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver);
static bool GBASIOLockstepNodeStart(struct GBASIODriver* driver);
static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, uint16_t value);
static uint16_t GBASIOLockstepNodeNormalWriteSIOCNT(struct GBASIODriver* driver, uint16_t value);
static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate);
@ -37,16 +38,16 @@ void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
}
void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) {
memset(&node->d, 0, sizeof(node->d));
node->d.init = GBASIOLockstepNodeInit;
node->d.deinit = GBASIOLockstepNodeDeinit;
node->d.load = GBASIOLockstepNodeLoad;
node->d.unload = GBASIOLockstepNodeUnload;
node->d.setMode = NULL;
node->d.handlesMode = GBASIOLockstepNodeHandlesMode;
node->d.connectedDevices = GBASIOLockstepNodeConnectedDevices;
node->d.deviceId = GBASIOLockstepNodeDeviceId;
node->d.start = GBASIOLockstepNodeStart;
node->d.writeSIOCNT = NULL;
node->d.writeRCNT = NULL;
}
bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) {
@ -224,6 +225,11 @@ static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver) {
return node->id;
}
static bool GBASIOLockstepNodeStart(struct GBASIODriver* driver) {
UNUSED(driver);
return false;
}
static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) {
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
@ -319,10 +325,6 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
switch (node->mode) {
case GBA_SIO_MULTI:
node->p->multiRecv[0] = node->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)];
node->d.p->p->memory.io[GBA_REG(SIOMULTI0)] = 0xFFFF;
node->d.p->p->memory.io[GBA_REG(SIOMULTI1)] = 0xFFFF;
node->d.p->p->memory.io[GBA_REG(SIOMULTI2)] = 0xFFFF;
node->d.p->p->memory.io[GBA_REG(SIOMULTI3)] = 0xFFFF;
node->p->multiRecv[1] = 0xFFFF;
node->p->multiRecv[2] = 0xFFFF;
node->p->multiRecv[3] = 0xFFFF;