diff --git a/include/mgba-util/common.h b/include/mgba-util/common.h index c3ce5be12..d8ef11029 100644 --- a/include/mgba-util/common.h +++ b/include/mgba-util/common.h @@ -81,6 +81,7 @@ typedef intptr_t ssize_t; #define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE) #define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE) #define ATOMIC_ADD(DST, OP) __atomic_add_fetch(&DST, OP, __ATOMIC_RELEASE) +#define ATOMIC_SUB(DST, OP) __atomic_sub_fetch(&DST, OP, __ATOMIC_RELEASE) #define ATOMIC_OR(DST, OP) __atomic_or_fetch(&DST, OP, __ATOMIC_RELEASE) #define ATOMIC_AND(DST, OP) __atomic_and_fetch(&DST, OP, __ATOMIC_RELEASE) #define ATOMIC_CMPXCHG(DST, EXPECTED, SRC) __atomic_compare_exchange_n(&DST, &EXPECTED, SRC, true,__ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE) @@ -89,6 +90,7 @@ typedef intptr_t ssize_t; #define ATOMIC_STORE(DST, SRC) DST = SRC #define ATOMIC_LOAD(DST, SRC) DST = SRC #define ATOMIC_ADD(DST, OP) DST += OP +#define ATOMIC_SUB(DST, OP) DST -= OP #define ATOMIC_OR(DST, OP) DST |= OP #define ATOMIC_AND(DST, OP) DST &= OP #define ATOMIC_CMPXCHG(DST, EXPECTED, OP) ((DST == EXPECTED) ? ((DST = OP), true) : false) diff --git a/src/gb/sio/lockstep.c b/src/gb/sio/lockstep.c index c60978ffe..2d4ec7546 100644 --- a/src/gb/sio/lockstep.c +++ b/src/gb/sio/lockstep.c @@ -236,6 +236,8 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu mTimingDeschedule(&driver->p->p->timing, &driver->p->event); mTimingDeschedule(&driver->p->p->timing, &node->event); mTimingSchedule(&driver->p->p->timing, &node->event, 0); + } else { + mLOG(GB_SIO, FATAL, "GBSIOLockstepNodeWriteSC() failed to write to masterClaimed\n"); } } return value; diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 848dc9b4d..237eff493 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -93,7 +93,7 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { case SIO_MULTI: node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister; node->d.p->rcnt |= 3; - ++node->p->attachedMulti; + ATOMIC_ADD(node->p->attachedMulti, 1); node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached; if (node->id) { node->d.p->rcnt |= 4; @@ -118,7 +118,7 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) { node->mode = driver->p->mode; switch (node->mode) { case SIO_MULTI: - --node->p->attachedMulti; + ATOMIC_SUB(node->p->attachedMulti, 1); break; default: break; @@ -132,11 +132,15 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; if (address == REG_SIOCNT) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value); - if (value & 0x0080 && node->p->d.transferActive == TRANSFER_IDLE) { + + enum mLockstepPhase transferActive; + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + + if (value & 0x0080 && transferActive == TRANSFER_IDLE) { if (!node->id && node->d.p->multiplayerControl.ready) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); - node->p->d.transferActive = TRANSFER_STARTING; - node->p->d.transferCycles = GBASIOCyclesPerTransfer[node->d.p->multiplayerControl.baud][node->p->d.attached - 1]; + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); + ATOMIC_STORE(node->p->d.transferCycles, GBASIOCyclesPerTransfer[node->d.p->multiplayerControl.baud][node->p->d.attached - 1]); mTimingDeschedule(&driver->p->p->timing, &node->event); mTimingSchedule(&driver->p->p->timing, &node->event, 0); } else { @@ -209,11 +213,19 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) { static int32_t _masterUpdate(struct GBASIOLockstepNode* node) { bool needsToWait = false; int i; - switch (node->p->d.transferActive) { + + enum mLockstepPhase transferActive; + int attachedMulti, attached; + + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + ATOMIC_LOAD(attachedMulti, node->p->attachedMulti); + ATOMIC_LOAD(attached, node->p->d.attached); + + switch (transferActive) { case TRANSFER_IDLE: // If the master hasn't initiated a transfer, it can keep going. node->nextEvent += LOCKSTEP_INCREMENT; - node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached; + node->d.p->multiplayerControl.ready = attachedMulti == attached; break; case TRANSFER_STARTING: // Start the transfer, but wait for the other GBAs to catch up @@ -276,9 +288,16 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) { } static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) { - node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->d.attached; + enum mLockstepPhase transferActive; + int attachedMulti, attached; + + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + ATOMIC_LOAD(attachedMulti, node->p->attachedMulti); + ATOMIC_LOAD(attached, node->p->d.attached); + + node->d.p->multiplayerControl.ready = attachedMulti == attached; bool signal = false; - switch (node->p->d.transferActive) { + switch (transferActive) { case TRANSFER_IDLE: if (!node->d.p->multiplayerControl.ready) { node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT); @@ -368,7 +387,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive if (value & 0x0080 && !node->id) { // Internal shift clock if (value & 1) { - node->p->d.transferActive = TRANSFER_STARTING; + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); } // Frequency if (value & 2) {