diff --git a/CHANGES b/CHANGES index a0ce4f5b6..65f53ebb3 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ Features: - Debugger: Add range watchpoints Emulation fixes: - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) + - GB SIO: Further fix bidirectional transfer starting - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) - GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) diff --git a/src/gb/sio/lockstep.c b/src/gb/sio/lockstep.c index 4d1f1f549..b8dccf891 100644 --- a/src/gb/sio/lockstep.c +++ b/src/gb/sio/lockstep.c @@ -157,7 +157,7 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) { } } // Tell the other GBs they can continue up to where we were - node->p->d.addCycles(&node->p->d, node->id, node->eventDiff); + node->p->d.addCycles(&node->p->d, 0, node->eventDiff); #ifndef NDEBUG node->phase = node->p->d.transferActive; #endif @@ -169,26 +169,28 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) { static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) { enum mLockstepPhase transferActive; + int id; ATOMIC_LOAD(transferActive, node->p->d.transferActive); + ATOMIC_LOAD(id, node->id); bool signal = false; switch (transferActive) { case TRANSFER_IDLE: - node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT); + node->p->d.addCycles(&node->p->d, id, LOCKSTEP_INCREMENT); break; case TRANSFER_STARTING: case TRANSFER_FINISHING: break; case TRANSFER_STARTED: - if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { + if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) { break; } node->transferFinished = false; signal = true; break; case TRANSFER_FINISHED: - if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { + if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) { break; } _finishTransfer(node); @@ -199,7 +201,7 @@ static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) { node->phase = node->p->d.transferActive; #endif if (signal) { - node->p->d.signal(&node->p->d, 1 << node->id); + node->p->d.signal(&node->p->d, 1 << id); } return 0; } @@ -215,11 +217,13 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, int32_t cycles = 0; node->nextEvent -= cyclesLate; if (node->nextEvent <= 0) { - if (!node->id) { + int id; + ATOMIC_LOAD(id, node->id); + if (!id) { cycles = _masterUpdate(node); } else { cycles = _slaveUpdate(node); - cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff); + cycles += node->p->d.useCycles(&node->p->d, id, node->eventDiff); } node->eventDiff = 0; } else { @@ -240,7 +244,9 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, static void GBSIOLockstepNodeWriteSB(struct GBSIODriver* driver, uint8_t value) { struct GBSIOLockstepNode* node = (struct GBSIOLockstepNode*) driver; - node->p->pendingSB[node->id] = value; + int id; + ATOMIC_LOAD(id, node->id); + node->p->pendingSB[id] = value; } static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t value) { @@ -252,11 +258,17 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu mLockstepLock(&node->p->d); bool claimed = false; if (ATOMIC_CMPXCHG(node->p->masterClaimed, claimed, true)) { - if (node->id != 0) { + int id; + ATOMIC_LOAD(id, node->id); + if (id != 0) { + unsigned sb; node->p->players[0]->id = 1; - node->p->players[1] = node->p->players[0]; - node->p->players[0] = node->p->players[1]; node->id = 0; + node->p->players[1] = node->p->players[0]; + node->p->players[0] = node; + sb = node->p->pendingSB[0]; + node->p->pendingSB[0] = node->p->pendingSB[1]; + node->p->pendingSB[1] = sb; } ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); ATOMIC_STORE(node->p->d.transferCycles, GBSIOCyclesPerTransfer[(value >> 1) & 1]);