diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index f83f9e717..3d52e7743 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -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; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 9bd21821a..c824e85aa 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -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; diff --git a/include/mgba/internal/gba/sio/gbp.h b/include/mgba/internal/gba/sio/gbp.h index 6713cdcaf..fe32cd8c1 100644 --- a/include/mgba/internal/gba/sio/gbp.h +++ b/include/mgba/internal/gba/sio/gbp.h @@ -21,7 +21,6 @@ struct GBASIOPlayer { struct GBA* p; unsigned inputsPosted; int txPosition; - struct mTimingEvent event; struct GBASIOPlayerKeyCallback callback; bool oldOpposingDirections; struct mKeyCallback* oldCallback; diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index fde4e3714..a04a5f6b3 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -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); } } diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index 4070626d8..a6eda3bdf 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -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; } diff --git a/src/gba/sio.c b/src/gba/sio.c index acc45e607..971eb9502 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -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 diff --git a/src/gba/sio/dolphin.c b/src/gba/sio/dolphin.c index f7aad747b..e36dfb8a5 100644 --- a/src/gba/sio/dolphin.c +++ b/src/gba/sio/dolphin.c @@ -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; diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index f7b3747a1..3335905a1 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -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,27 +101,26 @@ 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 - if (gbp->p->rumble) { - int32_t currentTime = mTimingCurrentTime(&gbp->p->timing); - gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble); - gbp->p->lastRumble = currentTime; - } + 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) { + // 0x00 = Stop + // 0x11 = Hard Stop + // 0x22 = Start + if (gbp->p->rumble) { + int32_t currentTime = mTimingCurrentTime(&gbp->p->timing); + gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble); + 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; } diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index f7b82f089..ed74ed6d4 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -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;