mirror of https://github.com/mgba-emu/mgba.git
GBA SIO: Incomplete draft of multiplayer rewrite
This commit is contained in:
parent
6519c4e465
commit
2ddc08e8b4
|
@ -47,7 +47,7 @@ static void _changeState(struct mCoreThread* threadContext, enum mCoreThreadStat
|
|||
}
|
||||
|
||||
static void _waitOnInterrupt(struct mCoreThread* threadContext) {
|
||||
while (threadContext->state == THREAD_INTERRUPTED) {
|
||||
while (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
|
||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
||||
}
|
||||
}
|
||||
|
@ -80,11 +80,9 @@ static void _waitUntilNotState(struct mCoreThread* threadContext, enum mCoreThre
|
|||
MutexUnlock(&threadContext->sync.videoFrameMutex);
|
||||
}
|
||||
|
||||
static void _pauseThread(struct mCoreThread* threadContext, bool onThread) {
|
||||
static void _pauseThread(struct mCoreThread* threadContext) {
|
||||
threadContext->state = THREAD_PAUSING;
|
||||
if (!onThread) {
|
||||
_waitUntilNotState(threadContext, THREAD_PAUSING);
|
||||
}
|
||||
_waitUntilNotState(threadContext, THREAD_PAUSING);
|
||||
}
|
||||
|
||||
static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||
|
@ -157,7 +155,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
threadContext->state = THREAD_RUNNING;
|
||||
resetScheduled = 1;
|
||||
}
|
||||
while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) {
|
||||
while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_WAITING) {
|
||||
ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +270,7 @@ void mCoreThreadEnd(struct mCoreThread* threadContext) {
|
|||
|
||||
void mCoreThreadReset(struct mCoreThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if (threadContext->state == THREAD_INTERRUPTED) {
|
||||
if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
|
||||
threadContext->savedState = THREAD_RESETING;
|
||||
} else {
|
||||
threadContext->state = THREAD_RESETING;
|
||||
|
@ -365,7 +363,7 @@ void mCoreThreadPause(struct mCoreThread* threadContext) {
|
|||
MutexLock(&threadContext->stateMutex);
|
||||
_waitOnInterrupt(threadContext);
|
||||
if (threadContext->state == THREAD_RUNNING) {
|
||||
_pauseThread(threadContext, false);
|
||||
_pauseThread(threadContext);
|
||||
threadContext->frameWasOn = frameOn;
|
||||
frameOn = false;
|
||||
}
|
||||
|
@ -391,7 +389,7 @@ void mCoreThreadUnpause(struct mCoreThread* threadContext) {
|
|||
bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
|
||||
bool isPaused;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if (threadContext->state == THREAD_INTERRUPTED) {
|
||||
if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
|
||||
isPaused = threadContext->savedState == THREAD_PAUSED;
|
||||
} else {
|
||||
isPaused = threadContext->state == THREAD_PAUSED;
|
||||
|
@ -409,7 +407,7 @@ void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
|
|||
ConditionWake(&threadContext->stateCond);
|
||||
frameOn = threadContext->frameWasOn;
|
||||
} else if (threadContext->state == THREAD_RUNNING) {
|
||||
_pauseThread(threadContext, false);
|
||||
_pauseThread(threadContext);
|
||||
threadContext->frameWasOn = frameOn;
|
||||
frameOn = false;
|
||||
}
|
||||
|
@ -421,8 +419,8 @@ void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
|
|||
void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
|
||||
bool frameOn = true;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if (threadContext->state == THREAD_RUNNING) {
|
||||
_pauseThread(threadContext, true);
|
||||
if (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING)) {
|
||||
threadContext->state = THREAD_PAUSING;
|
||||
frameOn = false;
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
|
@ -442,6 +440,27 @@ void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding)
|
|||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_RUNNING) {
|
||||
threadContext->savedState = THREAD_WAITING;
|
||||
} else if (threadContext->state == THREAD_RUNNING) {
|
||||
threadContext->state = THREAD_WAITING;
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_WAITING) {
|
||||
threadContext->savedState = THREAD_RUNNING;
|
||||
} else if (threadContext->state == THREAD_WAITING) {
|
||||
threadContext->state = THREAD_RUNNING;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
||||
#ifdef USE_PTHREADS
|
||||
struct mCoreThread* mCoreThreadGet(void) {
|
||||
pthread_once(&_contextOnce, _createTLS);
|
||||
|
|
|
@ -28,6 +28,7 @@ enum mCoreThreadState {
|
|||
THREAD_PAUSED,
|
||||
THREAD_PAUSING,
|
||||
THREAD_RUN_ON,
|
||||
THREAD_WAITING,
|
||||
THREAD_RESETING,
|
||||
THREAD_EXITING,
|
||||
THREAD_SHUTDOWN,
|
||||
|
@ -87,6 +88,8 @@ void mCoreThreadUnpause(struct mCoreThread* threadContext);
|
|||
bool mCoreThreadIsPaused(struct mCoreThread* threadContext);
|
||||
void mCoreThreadTogglePause(struct mCoreThread* threadContext);
|
||||
void mCoreThreadPauseFromThread(struct mCoreThread* threadContext);
|
||||
void mCoreThreadWaitFromThread(struct mCoreThread* threadContext);
|
||||
void mCoreThreadStopWaiting(struct mCoreThread* threadContext);
|
||||
|
||||
void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool);
|
||||
|
||||
|
|
|
@ -234,21 +234,33 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
|||
|
||||
testEvent = GBAVideoProcessEvents(&gba->video, cycles);
|
||||
if (testEvent < nextEvent) {
|
||||
if (testEvent == 0) {
|
||||
abort();
|
||||
}
|
||||
nextEvent = testEvent;
|
||||
}
|
||||
|
||||
testEvent = GBAAudioProcessEvents(&gba->audio, cycles);
|
||||
if (testEvent < nextEvent) {
|
||||
if (testEvent == 0) {
|
||||
abort();
|
||||
}
|
||||
nextEvent = testEvent;
|
||||
}
|
||||
|
||||
testEvent = GBATimersProcessEvents(gba, cycles);
|
||||
if (testEvent < nextEvent) {
|
||||
if (testEvent == 0) {
|
||||
abort();
|
||||
}
|
||||
nextEvent = testEvent;
|
||||
}
|
||||
|
||||
testEvent = GBAMemoryRunDMAs(gba, cycles);
|
||||
if (testEvent < nextEvent) {
|
||||
if (testEvent == 0) {
|
||||
abort();
|
||||
}
|
||||
nextEvent = testEvent;
|
||||
}
|
||||
|
||||
|
@ -263,6 +275,9 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
|||
if (cpu->halted) {
|
||||
cpu->cycles = cpu->nextEvent;
|
||||
}
|
||||
if (cpu->nextEvent == 0) {
|
||||
break;
|
||||
}
|
||||
} while (cpu->cycles >= cpu->nextEvent);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,16 +32,17 @@ static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mo
|
|||
|
||||
static void _switchMode(struct GBASIO* sio) {
|
||||
unsigned mode = ((sio->rcnt & 0xC000) | (sio->siocnt & 0x3000)) >> 12;
|
||||
enum GBASIOMode oldMode = sio->mode;
|
||||
enum GBASIOMode newMode;
|
||||
if (mode < 8) {
|
||||
sio->mode = (enum GBASIOMode) (mode & 0x3);
|
||||
newMode = (enum GBASIOMode) (mode & 0x3);
|
||||
} else {
|
||||
sio->mode = (enum GBASIOMode) (mode & 0xC);
|
||||
newMode = (enum GBASIOMode) (mode & 0xC);
|
||||
}
|
||||
if (oldMode != sio->mode) {
|
||||
if (newMode != sio->mode) {
|
||||
if (sio->activeDriver && sio->activeDriver->unload) {
|
||||
sio->activeDriver->unload(sio->activeDriver);
|
||||
}
|
||||
sio->mode = newMode;
|
||||
sio->activeDriver = _lookupDriver(sio, sio->mode);
|
||||
if (sio->activeDriver && sio->activeDriver->load) {
|
||||
sio->activeDriver->load(sio->activeDriver);
|
||||
|
@ -121,11 +122,11 @@ void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASI
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (sio->mode == mode) {
|
||||
sio->activeDriver = driver;
|
||||
if (driver->load) {
|
||||
driver->load(driver);
|
||||
}
|
||||
}
|
||||
if (sio->mode == mode) {
|
||||
sio->activeDriver = driver;
|
||||
if (driver && driver->load) {
|
||||
driver->load(driver);
|
||||
}
|
||||
}
|
||||
*driverLoc = driver;
|
||||
|
|
|
@ -7,17 +7,56 @@
|
|||
|
||||
#include "gba/gba.h"
|
||||
#include "gba/io.h"
|
||||
#include "gba/video.h"
|
||||
|
||||
#define LOCKSTEP_INCREMENT 3000
|
||||
|
||||
static bool _nodeWait(struct GBASIOLockstepNode* node, uint32_t mask) {
|
||||
uint32_t oldMask = 0;
|
||||
if (ATOMIC_CMPXCHG(node->p->waiting[node->id], oldMask, mask)) {
|
||||
node->p->wait(node->p, node->id);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
else if (oldMask != mask) {
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
else if ((node->p->waiting[node->id] & oldMask) == node->p->waiting[node->id]) {
|
||||
ATOMIC_AND(node->p->waitMask, ~mask);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _nodeSignal(struct GBASIOLockstepNode* node, uint32_t mask) {
|
||||
mask = ATOMIC_OR(node->p->waitMask, mask);
|
||||
bool eventTriggered = false;
|
||||
int i;
|
||||
for (i = 0; i < node->p->attached; ++i) {
|
||||
uint32_t waiting = node->p->waiting[i];
|
||||
if (waiting && (waiting & mask) == waiting && ATOMIC_CMPXCHG(node->p->waiting[i], waiting, 0)) {
|
||||
node->p->signal(node->p, i);
|
||||
eventTriggered = true;
|
||||
}
|
||||
}
|
||||
if (eventTriggered) {
|
||||
ATOMIC_STORE(node->p->waitMask, 0);
|
||||
} else {
|
||||
mLOG(GBA_SIO, WARN, "Nothing woke with mask %X", mask);
|
||||
}
|
||||
return eventTriggered;
|
||||
}
|
||||
|
||||
#define LOCKSTEP_INCREMENT 2048
|
||||
|
||||
static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver);
|
||||
static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver);
|
||||
static bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver);
|
||||
static bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver);
|
||||
static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||
static int32_t GBASIOLockstepNodeMultiProcessEvents(struct GBASIODriver* driver, int32_t cycles);
|
||||
static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||
static int32_t GBASIOLockstepNodeNormalProcessEvents(struct GBASIODriver* driver, int32_t cycles);
|
||||
static int32_t GBASIOLockstepNodeProcessEvents(struct GBASIODriver* driver, int32_t cycles);
|
||||
static int32_t GBASIOLockstepNodeProcessEvents(struct GBASIODriver* driver, int32_t cycles);
|
||||
|
||||
void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
|
||||
lockstep->players[0] = 0;
|
||||
|
@ -29,18 +68,17 @@ void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
|
|||
lockstep->multiRecv[2] = 0xFFFF;
|
||||
lockstep->multiRecv[3] = 0xFFFF;
|
||||
lockstep->attached = 0;
|
||||
lockstep->loadedMulti = 0;
|
||||
lockstep->loadedNormal = 0;
|
||||
lockstep->transferActive = false;
|
||||
lockstep->waiting = 0;
|
||||
lockstep->nextEvent = LOCKSTEP_INCREMENT;
|
||||
ConditionInit(&lockstep->barrier);
|
||||
MutexInit(&lockstep->mutex);
|
||||
lockstep->attachedMulti = 0;
|
||||
lockstep->transferActive = 0;
|
||||
memset(lockstep->waiting, 0, sizeof(lockstep->waiting));
|
||||
lockstep->waitMask = 0;
|
||||
#ifndef NDEBUG
|
||||
lockstep->transferId = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GBASIOLockstepDeinit(struct GBASIOLockstep* lockstep) {
|
||||
ConditionDeinit(&lockstep->barrier);
|
||||
MutexDeinit(&lockstep->mutex);
|
||||
UNUSED(lockstep);
|
||||
}
|
||||
|
||||
void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) {
|
||||
|
@ -49,7 +87,7 @@ void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) {
|
|||
node->d.load = GBASIOLockstepNodeLoad;
|
||||
node->d.unload = GBASIOLockstepNodeUnload;
|
||||
node->d.writeRegister = 0;
|
||||
node->d.processEvents = 0;
|
||||
node->d.processEvents = GBASIOLockstepNodeProcessEvents;
|
||||
}
|
||||
|
||||
bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) {
|
||||
|
@ -83,7 +121,7 @@ void GBASIOLockstepDetachNode(struct GBASIOLockstep* lockstep, struct GBASIOLock
|
|||
|
||||
bool GBASIOLockstepNodeInit(struct GBASIODriver* driver) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
node->nextEvent = LOCKSTEP_INCREMENT;
|
||||
node->needsToWait = false;
|
||||
node->d.p->multiplayerControl.slave = node->id > 0;
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Node init", node->id);
|
||||
return true;
|
||||
|
@ -95,15 +133,15 @@ void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver) {
|
|||
|
||||
bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
node->state = LOCKSTEP_IDLE;
|
||||
node->nextEvent = 0;
|
||||
node->eventDiff = 0;
|
||||
node->mode = driver->p->mode;
|
||||
MutexLock(&node->p->mutex);
|
||||
switch (node->mode) {
|
||||
case SIO_MULTI:
|
||||
node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister;
|
||||
node->d.processEvents = GBASIOLockstepNodeMultiProcessEvents;
|
||||
++node->p->loadedMulti;
|
||||
node->d.p->rcnt |= 3;
|
||||
++node->p->attachedMulti;
|
||||
node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->attached;
|
||||
if (node->id) {
|
||||
node->d.p->rcnt |= 4;
|
||||
node->d.p->multiplayerControl.slave = 1;
|
||||
|
@ -111,31 +149,28 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
|
|||
break;
|
||||
case SIO_NORMAL_32:
|
||||
node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister;
|
||||
node->d.processEvents = GBASIOLockstepNodeNormalProcessEvents;
|
||||
++node->p->loadedNormal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
MutexUnlock(&node->p->mutex);
|
||||
#ifndef NDEBUG
|
||||
node->phase = node->p->transferActive;
|
||||
node->transferId = node->p->transferId;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
MutexLock(&node->p->mutex);
|
||||
node->mode = driver->p->mode;
|
||||
switch (node->mode) {
|
||||
case SIO_MULTI:
|
||||
--node->p->loadedMulti;
|
||||
break;
|
||||
case SIO_NORMAL_32:
|
||||
--node->p->loadedNormal;
|
||||
--node->p->attachedMulti;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ConditionWake(&node->p->barrier);
|
||||
MutexUnlock(&node->p->mutex);
|
||||
_nodeSignal(node, (1 << node->id) ^ 0xF);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -143,14 +178,12 @@ 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) {
|
||||
if (!node->id) {
|
||||
if (value & 0x0080 && node->p->transferActive == TRANSFER_IDLE) {
|
||||
if (!node->id && node->d.p->multiplayerControl.ready) {
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id);
|
||||
MutexLock(&node->p->mutex);
|
||||
node->p->transferActive = true;
|
||||
node->p->transferActive = TRANSFER_STARTING;
|
||||
node->p->transferCycles = GBASIOCyclesPerTransfer[node->d.p->multiplayerControl.baud][node->p->attached - 1];
|
||||
node->multiSend = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
|
||||
MutexUnlock(&node->p->mutex);
|
||||
node->nextEvent = 0;
|
||||
} else {
|
||||
value &= ~0x0080;
|
||||
}
|
||||
|
@ -163,67 +196,202 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
|
|||
return value;
|
||||
}
|
||||
|
||||
static int32_t GBASIOLockstepNodeMultiProcessEvents(struct GBASIODriver* driver, int32_t cycles) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
node->nextEvent -= cycles;
|
||||
while (node->nextEvent <= 0) {
|
||||
MutexLock(&node->p->mutex);
|
||||
++node->p->waiting;
|
||||
if (node->p->waiting < node->p->loadedMulti) {
|
||||
ConditionWait(&node->p->barrier, &node->p->mutex);
|
||||
} else {
|
||||
if (node->p->transferActive) {
|
||||
node->p->transferCycles -= node->p->nextEvent;
|
||||
if (node->p->transferCycles > 0) {
|
||||
if (node->p->transferCycles < LOCKSTEP_INCREMENT) {
|
||||
node->p->nextEvent = node->p->transferCycles;
|
||||
}
|
||||
} else {
|
||||
node->p->nextEvent = LOCKSTEP_INCREMENT;
|
||||
node->p->transferActive = false;
|
||||
int i;
|
||||
for (i = 0; i < node->p->attached; ++i) {
|
||||
node->p->multiRecv[i] = node->p->players[i]->multiSend;
|
||||
node->p->players[i]->state = LOCKSTEP_FINISHED;
|
||||
}
|
||||
for (; i < MAX_GBAS; ++i) {
|
||||
node->p->multiRecv[i] = 0xFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
node->p->waiting = 0;
|
||||
ConditionWake(&node->p->barrier);
|
||||
static void _finishTransfer(struct GBASIOLockstepNode* node) {
|
||||
if (node->transferFinished) {
|
||||
return;
|
||||
}
|
||||
struct GBASIO* sio = node->d.p;
|
||||
switch (node->mode) {
|
||||
case SIO_MULTI:
|
||||
sio->p->memory.io[REG_SIOMULTI0 >> 1] = node->p->multiRecv[0];
|
||||
sio->p->memory.io[REG_SIOMULTI1 >> 1] = node->p->multiRecv[1];
|
||||
sio->p->memory.io[REG_SIOMULTI2 >> 1] = node->p->multiRecv[2];
|
||||
sio->p->memory.io[REG_SIOMULTI3 >> 1] = node->p->multiRecv[3];
|
||||
sio->rcnt |= 1;
|
||||
sio->multiplayerControl.busy = 0;
|
||||
sio->multiplayerControl.id = node->id;
|
||||
if (sio->multiplayerControl.irq) {
|
||||
GBARaiseIRQ(sio->p, IRQ_SIO);
|
||||
}
|
||||
if (node->state == LOCKSTEP_FINISHED) {
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: Finishing transfer: %04x %04x %04x %04x", node->id, node->p->multiRecv[0], node->p->multiRecv[1], node->p->multiRecv[2], node->p->multiRecv[3]);
|
||||
node->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = node->p->multiRecv[0];
|
||||
node->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = node->p->multiRecv[1];
|
||||
node->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = node->p->multiRecv[2];
|
||||
node->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = node->p->multiRecv[3];
|
||||
node->d.p->rcnt |= 1;
|
||||
node->state = LOCKSTEP_IDLE;
|
||||
if (node->d.p->multiplayerControl.irq) {
|
||||
GBARaiseIRQ(node->d.p->p, IRQ_SIO);
|
||||
break;
|
||||
case SIO_NORMAL_8:
|
||||
case SIO_NORMAL_32:
|
||||
// TODO
|
||||
sio->normalControl.start = 0;
|
||||
if (sio->multiplayerControl.irq) {
|
||||
GBARaiseIRQ(sio->p, IRQ_SIO);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
node->transferFinished = true;
|
||||
#ifndef NDEBUG
|
||||
++node->transferId;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _masterUpdate(struct GBASIOLockstepNode* node) {
|
||||
ATOMIC_STORE(node->needsToWait, false);
|
||||
int i;
|
||||
switch (node->p->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->attached;
|
||||
break;
|
||||
case TRANSFER_STARTING:
|
||||
// Start the transfer, but wait for the other GBAs to catch up
|
||||
node->transferFinished = false;
|
||||
node->p->multiRecv[0] = 0xFFFF;
|
||||
node->p->multiRecv[1] = 0xFFFF;
|
||||
node->p->multiRecv[2] = 0xFFFF;
|
||||
node->p->multiRecv[3] = 0xFFFF;
|
||||
ATOMIC_STORE(node->needsToWait, true);
|
||||
ATOMIC_STORE(node->p->transferActive, TRANSFER_STARTED);
|
||||
node->nextEvent += 512;
|
||||
break;
|
||||
case TRANSFER_STARTED:
|
||||
// All the other GBAs have caught up and are sleeping, we can all continue now
|
||||
#ifndef NDEBUG
|
||||
/*for (i = 1; i < node->p->attached; ++i) {
|
||||
enum GBASIOLockstepPhase phase;
|
||||
ATOMIC_LOAD(phase, node->p->players[i]->phase);
|
||||
if (node->p->players[i]->mode == node->mode && phase != TRANSFER_STARTED) {
|
||||
abort();
|
||||
}
|
||||
node->d.p->multiplayerControl.id = node->id;
|
||||
node->d.p->multiplayerControl.busy = 0;
|
||||
} else if (node->state == LOCKSTEP_IDLE && node->p->transferActive) {
|
||||
node->state = LOCKSTEP_STARTED;
|
||||
}*/
|
||||
#endif
|
||||
node->p->multiRecv[0] = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
|
||||
node->nextEvent += 512;
|
||||
ATOMIC_STORE(node->p->transferActive, TRANSFER_FINISHING);
|
||||
break;
|
||||
case TRANSFER_FINISHING:
|
||||
// Finish the transfer
|
||||
// We need to make sure the other GBAs catch up so they don't get behind
|
||||
node->nextEvent += node->p->transferCycles - 1024; // Split the cycles to avoid waiting too long
|
||||
#ifndef NDEBUG
|
||||
ATOMIC_ADD(node->p->transferId, 1);
|
||||
#endif
|
||||
ATOMIC_STORE(node->needsToWait, true);
|
||||
ATOMIC_STORE(node->p->transferActive, TRANSFER_FINISHED);
|
||||
break;
|
||||
case TRANSFER_FINISHED:
|
||||
// Everything's settled. We're done.
|
||||
_finishTransfer(node);
|
||||
node->nextEvent += LOCKSTEP_INCREMENT;
|
||||
ATOMIC_STORE(node->p->transferActive, TRANSFER_IDLE);
|
||||
break;
|
||||
}
|
||||
if (node->needsToWait) {
|
||||
int mask = 0;
|
||||
for (i = 1; i < node->p->attached; ++i) {
|
||||
if (node->p->players[i]->mode == node->mode) {
|
||||
mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
if (mask) {
|
||||
_nodeWait(node, mask);
|
||||
}
|
||||
}
|
||||
// Tell the other GBAs they can continue up to where we were
|
||||
for (i = 1; i < node->p->attached; ++i) {
|
||||
ATOMIC_ADD(node->p->players[i]->nextEvent, node->eventDiff);
|
||||
ATOMIC_STORE(node->p->players[i]->needsToWait, false);
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
node->phase = node->p->transferActive;
|
||||
#endif
|
||||
_nodeSignal(node, 1);
|
||||
}
|
||||
|
||||
static void _slaveUpdate(struct GBASIOLockstepNode* node) {
|
||||
ATOMIC_STORE(node->needsToWait, true);
|
||||
node->d.p->multiplayerControl.ready = node->p->attachedMulti == node->p->attached;
|
||||
#ifndef NDEBUG
|
||||
if (node->phase >= TRANSFER_STARTED && node->phase != TRANSFER_FINISHED && node->phase != node->p->transferActive && node->p->transferActive < TRANSFER_FINISHING) {
|
||||
//abort();
|
||||
}
|
||||
if (node->phase < TRANSFER_FINISHED && node->phase != TRANSFER_IDLE && node->p->transferActive == TRANSFER_IDLE) {
|
||||
//abort();
|
||||
}
|
||||
#endif
|
||||
bool signal = false;
|
||||
switch (node->p->transferActive) {
|
||||
case TRANSFER_IDLE:
|
||||
if (!node->d.p->multiplayerControl.ready) {
|
||||
node->nextEvent += LOCKSTEP_INCREMENT;
|
||||
ATOMIC_STORE(node->needsToWait, false);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case TRANSFER_STARTING:
|
||||
case TRANSFER_FINISHING:
|
||||
break;
|
||||
case TRANSFER_STARTED:
|
||||
#ifndef NDEBUG
|
||||
if (node->transferId != node->p->transferId) {
|
||||
//abort();
|
||||
}
|
||||
#endif
|
||||
node->transferFinished = false;
|
||||
switch (node->mode) {
|
||||
case SIO_MULTI:
|
||||
node->d.p->rcnt &= ~1;
|
||||
node->p->multiRecv[node->id] = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
|
||||
node->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = 0xFFFF;
|
||||
node->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = 0xFFFF;
|
||||
node->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
|
||||
node->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
|
||||
node->d.p->rcnt &= ~1;
|
||||
if (node->id) {
|
||||
node->multiSend = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
|
||||
node->d.p->multiplayerControl.busy = 1;
|
||||
}
|
||||
node->d.p->multiplayerControl.busy = 1;
|
||||
break;
|
||||
default:
|
||||
node->p->multiRecv[node->id]= 0xFFFF;
|
||||
break;
|
||||
}
|
||||
node->d.p->multiplayerControl.ready = node->p->loadedMulti == node->p->attached;
|
||||
node->nextEvent += node->p->nextEvent;
|
||||
MutexUnlock(&node->p->mutex);
|
||||
signal = true;
|
||||
break;
|
||||
case TRANSFER_FINISHED:
|
||||
_finishTransfer(node);
|
||||
signal = true;
|
||||
break;
|
||||
}
|
||||
return node->nextEvent;
|
||||
_nodeWait(node, 1);
|
||||
#ifndef NDEBUG
|
||||
node->phase = node->p->transferActive;
|
||||
#endif
|
||||
if (signal) {
|
||||
_nodeSignal(node, 1 << node->id);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t GBASIOLockstepNodeProcessEvents(struct GBASIODriver* driver, int32_t cycles) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
if (node->p->attached < 2) {
|
||||
return INT_MAX;
|
||||
}
|
||||
node->eventDiff += cycles;
|
||||
cycles = ATOMIC_ADD(node->nextEvent, -cycles);
|
||||
if (cycles <= 0) {
|
||||
if (!node->id) {
|
||||
_masterUpdate(node);
|
||||
} else {
|
||||
_slaveUpdate(node);
|
||||
}
|
||||
node->eventDiff = 0;
|
||||
if (node->needsToWait) {
|
||||
return 0;
|
||||
}
|
||||
ATOMIC_LOAD(cycles, node->nextEvent);
|
||||
#ifndef NDEBUG
|
||||
if (cycles <= 0 && !node->needsToWait) {
|
||||
abort();
|
||||
mLOG(GBA_SIO, WARN, "Sleeping needlessly");
|
||||
}
|
||||
#endif
|
||||
return cycles;
|
||||
}
|
||||
return cycles;
|
||||
}
|
||||
|
||||
static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
||||
|
@ -231,11 +399,10 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
|||
if (address == REG_SIOCNT) {
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value);
|
||||
value &= 0xFF8B;
|
||||
MutexLock(&node->p->mutex);
|
||||
if (value & 0x0080) {
|
||||
// Internal shift clock
|
||||
if (value & 1) {
|
||||
node->p->transferActive = true;
|
||||
node->p->transferActive = TRANSFER_STARTING;
|
||||
}
|
||||
// Frequency
|
||||
if (value & 2) {
|
||||
|
@ -249,7 +416,6 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
|||
value |= node->p->players[node->id - 1]->normalSO << 2;
|
||||
}
|
||||
}
|
||||
MutexUnlock(&node->p->mutex);
|
||||
} else if (address == REG_SIODATA32_LO) {
|
||||
mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04x", node->id, value);
|
||||
} else if (address == REG_SIODATA32_HI) {
|
||||
|
@ -257,51 +423,3 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
|
|||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static int32_t GBASIOLockstepNodeNormalProcessEvents(struct GBASIODriver* driver, int32_t cycles) {
|
||||
struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver;
|
||||
node->nextEvent -= cycles;
|
||||
while (node->nextEvent <= 0) {
|
||||
MutexLock(&node->p->mutex);
|
||||
++node->p->waiting;
|
||||
if (node->p->waiting < node->p->loadedNormal) {
|
||||
ConditionWait(&node->p->barrier, &node->p->mutex);
|
||||
} else {
|
||||
if (node->p->transferActive) {
|
||||
node->p->transferCycles -= node->p->nextEvent;
|
||||
if (node->p->transferCycles > 0) {
|
||||
if (node->p->transferCycles < LOCKSTEP_INCREMENT) {
|
||||
node->p->nextEvent = node->p->transferCycles;
|
||||
}
|
||||
} else {
|
||||
node->p->nextEvent = LOCKSTEP_INCREMENT;
|
||||
node->p->transferActive = false;
|
||||
int i;
|
||||
for (i = 0; i < node->p->attached; ++i) {
|
||||
node->p->players[i]->state = LOCKSTEP_FINISHED;
|
||||
}
|
||||
}
|
||||
}
|
||||
node->p->waiting = 0;
|
||||
ConditionWake(&node->p->barrier);
|
||||
}
|
||||
if (node->state == LOCKSTEP_FINISHED) {
|
||||
int i;
|
||||
for (i = 1; i < node->p->loadedNormal; ++i) {
|
||||
node->p->players[i]->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = node->p->players[i - 1]->d.p->p->memory.io[REG_SIODATA32_LO >> 1];
|
||||
node->p->players[i]->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = node->p->players[i - 1]->d.p->p->memory.io[REG_SIODATA32_HI >> 1];
|
||||
}
|
||||
node->state = LOCKSTEP_IDLE;
|
||||
if (node->d.p->normalControl.irq) {
|
||||
GBARaiseIRQ(node->d.p->p, IRQ_SIO);
|
||||
}
|
||||
node->d.p->multiplayerControl.id = node->id;
|
||||
node->d.p->normalControl.start = 0;
|
||||
} else if (node->state == LOCKSTEP_IDLE && node->p->transferActive) {
|
||||
node->state = LOCKSTEP_STARTED;
|
||||
}
|
||||
node->nextEvent += node->p->nextEvent;
|
||||
MutexUnlock(&node->p->mutex);
|
||||
}
|
||||
return node->nextEvent;
|
||||
}
|
||||
|
|
|
@ -8,28 +8,32 @@
|
|||
|
||||
#include "gba/sio.h"
|
||||
|
||||
#include "util/threading.h"
|
||||
|
||||
enum LockstepState {
|
||||
LOCKSTEP_IDLE = 0,
|
||||
LOCKSTEP_STARTED = 1,
|
||||
LOCKSTEP_FINISHED = 2
|
||||
enum GBASIOLockstepPhase {
|
||||
TRANSFER_IDLE = 0,
|
||||
TRANSFER_STARTING,
|
||||
TRANSFER_STARTED,
|
||||
TRANSFER_FINISHING,
|
||||
TRANSFER_FINISHED
|
||||
};
|
||||
|
||||
struct GBASIOLockstep {
|
||||
struct GBASIOLockstepNode* players[MAX_GBAS];
|
||||
int attached;
|
||||
int loadedMulti;
|
||||
int loadedNormal;
|
||||
int attachedMulti;
|
||||
|
||||
uint16_t multiRecv[MAX_GBAS];
|
||||
bool transferActive;
|
||||
enum GBASIOLockstepPhase transferActive;
|
||||
int32_t transferCycles;
|
||||
int32_t nextEvent;
|
||||
|
||||
int waiting;
|
||||
Mutex mutex;
|
||||
Condition barrier;
|
||||
uint32_t waitMask;
|
||||
uint32_t waiting[MAX_GBAS];
|
||||
|
||||
void (*signal)(struct GBASIOLockstep*, int id);
|
||||
void (*wait)(struct GBASIOLockstep*, int id);
|
||||
void* context;
|
||||
#ifndef NDEBUG
|
||||
int transferId;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct GBASIOLockstepNode {
|
||||
|
@ -37,11 +41,16 @@ struct GBASIOLockstepNode {
|
|||
struct GBASIOLockstep* p;
|
||||
|
||||
int32_t nextEvent;
|
||||
uint16_t multiSend;
|
||||
int32_t eventDiff;
|
||||
bool normalSO;
|
||||
enum LockstepState state;
|
||||
bool needsToWait;
|
||||
int id;
|
||||
enum GBASIOMode mode;
|
||||
bool transferFinished;
|
||||
#ifndef NDEBUG
|
||||
int transferId;
|
||||
enum GBASIOLockstepPhase phase;
|
||||
#endif
|
||||
};
|
||||
|
||||
void GBASIOLockstepInit(struct GBASIOLockstep*);
|
||||
|
|
|
@ -130,6 +130,10 @@ GameController::GameController(QObject* parent)
|
|||
}
|
||||
|
||||
controller->m_gameOpen = true;
|
||||
if (controller->m_multiplayer) {
|
||||
controller->m_multiplayer->attachGame(controller);
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(mCoreThread*, context), Q_ARG(const QString&, controller->m_fname));
|
||||
QMetaObject::invokeMethod(controller, "startAudio");
|
||||
};
|
||||
|
@ -256,7 +260,12 @@ void GameController::setMultiplayerController(MultiplayerController* controller)
|
|||
}
|
||||
clearMultiplayerController();
|
||||
m_multiplayer = controller;
|
||||
controller->attachGame(this);
|
||||
if (isLoaded()) {
|
||||
mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* thread) {
|
||||
GameController* controller = static_cast<GameController*>(thread->userData);
|
||||
controller->m_multiplayer->attachGame(controller);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GameController::clearMultiplayerController() {
|
||||
|
@ -522,6 +531,9 @@ void GameController::closeGame() {
|
|||
if (!m_gameOpen) {
|
||||
return;
|
||||
}
|
||||
if (m_multiplayer) {
|
||||
m_multiplayer->detachGame(this);
|
||||
}
|
||||
|
||||
if (mCoreThreadIsPaused(&m_threadContext)) {
|
||||
mCoreThreadUnpause(&m_threadContext);
|
||||
|
|
|
@ -7,10 +7,41 @@
|
|||
|
||||
#include "GameController.h"
|
||||
|
||||
extern "C" {
|
||||
#ifdef M_CORE_GBA
|
||||
#include "gba/gba.h"
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
MultiplayerController::MultiplayerController() {
|
||||
GBASIOLockstepInit(&m_lockstep);
|
||||
m_lockstep.context = this;
|
||||
m_lockstep.signal = [](GBASIOLockstep* lockstep, int id) {
|
||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||
GameController* game = controller->m_players[id];
|
||||
controller->m_lock.lock();
|
||||
if (--controller->m_asleep[id] == 0) {
|
||||
mCoreThreadStopWaiting(game->thread());
|
||||
}
|
||||
controller->m_lock.unlock();
|
||||
};
|
||||
m_lockstep.wait = [](GBASIOLockstep* lockstep, int id) {
|
||||
MultiplayerController* controller = static_cast<MultiplayerController*>(lockstep->context);
|
||||
controller->m_lock.lock();
|
||||
GameController* game = controller->m_players[id];
|
||||
if (++controller->m_asleep[id] == 1) {
|
||||
mCoreThreadWaitFromThread(game->thread());
|
||||
} else if (controller->m_asleep[id] == 0) {
|
||||
mCoreThreadStopWaiting(game->thread());
|
||||
}
|
||||
if (controller->m_asleep[id] > 1) {
|
||||
//abort();
|
||||
}
|
||||
controller->m_lock.unlock();
|
||||
};
|
||||
}
|
||||
|
||||
MultiplayerController::~MultiplayerController() {
|
||||
|
@ -18,74 +49,68 @@ MultiplayerController::~MultiplayerController() {
|
|||
}
|
||||
|
||||
bool MultiplayerController::attachGame(GameController* controller) {
|
||||
MutexLock(&m_lockstep.mutex);
|
||||
if (m_lockstep.attached == MAX_GBAS) {
|
||||
MutexUnlock(&m_lockstep.mutex);
|
||||
return false;
|
||||
}
|
||||
GBASIOLockstepNode* node = new GBASIOLockstepNode;
|
||||
GBASIOLockstepNodeCreate(node);
|
||||
GBASIOLockstepAttachNode(&m_lockstep, node);
|
||||
MutexUnlock(&m_lockstep.mutex);
|
||||
|
||||
controller->threadInterrupt();
|
||||
mCoreThread* thread = controller->thread();
|
||||
/*if (controller->isLoaded()) {
|
||||
GBASIOSetDriver(&thread->gba->sio, &node->d, SIO_MULTI);
|
||||
GBASIOSetDriver(&thread->gba->sio, &node->d, SIO_NORMAL_32);
|
||||
if (!thread) {
|
||||
return false;
|
||||
}
|
||||
thread->sioDrivers.multiplayer = &node->d;
|
||||
thread->sioDrivers.normal = &node->d;*/
|
||||
controller->threadContinue();
|
||||
emit gameAttached();
|
||||
return true;
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
if (controller->platform() == PLATFORM_GBA) {
|
||||
GBA* gba = static_cast<GBA*>(thread->core->board);
|
||||
|
||||
GBASIOLockstepNode* node = new GBASIOLockstepNode;
|
||||
GBASIOLockstepNodeCreate(node);
|
||||
GBASIOLockstepAttachNode(&m_lockstep, node);
|
||||
m_players.append(controller);
|
||||
m_asleep.append(0);
|
||||
|
||||
GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
|
||||
GBASIOSetDriver(&gba->sio, &node->d, SIO_NORMAL_32);
|
||||
|
||||
emit gameAttached();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MultiplayerController::detachGame(GameController* controller) {
|
||||
controller->threadInterrupt();
|
||||
MutexLock(&m_lockstep.mutex);
|
||||
mCoreThread* thread = nullptr;
|
||||
/*for (int i = 0; i < m_lockstep.attached; ++i) {
|
||||
thread = controller->thread();
|
||||
if (thread->sioDrivers.multiplayer == &m_lockstep.players[i]->d) {
|
||||
break;
|
||||
}
|
||||
thread = nullptr;
|
||||
if (!m_players.contains(controller)) {
|
||||
return;
|
||||
}
|
||||
if (thread) {
|
||||
GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(thread->sioDrivers.multiplayer);
|
||||
if (controller->isLoaded()) {
|
||||
GBASIOSetDriver(&thread->gba->sio, nullptr, SIO_MULTI);
|
||||
GBASIOSetDriver(&thread->gba->sio, nullptr, SIO_NORMAL_32);
|
||||
mCoreThread* thread = controller->thread();
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
#ifdef M_CORE_GBA
|
||||
if (controller->platform() == PLATFORM_GBA) {
|
||||
GBA* gba = static_cast<GBA*>(thread->core->board);
|
||||
GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(gba->sio.drivers.multiplayer);
|
||||
GBASIOSetDriver(&gba->sio, nullptr, SIO_MULTI);
|
||||
GBASIOSetDriver(&gba->sio, nullptr, SIO_NORMAL_32);
|
||||
if (node) {
|
||||
GBASIOLockstepDetachNode(&m_lockstep, node);
|
||||
delete node;
|
||||
}
|
||||
thread->sioDrivers.multiplayer = nullptr;
|
||||
thread->sioDrivers.normal = nullptr;
|
||||
GBASIOLockstepDetachNode(&m_lockstep, node);
|
||||
delete node;
|
||||
}*/
|
||||
MutexUnlock(&m_lockstep.mutex);
|
||||
controller->threadContinue();
|
||||
}
|
||||
#endif
|
||||
int i = m_players.indexOf(controller);
|
||||
m_players.removeAt(i);
|
||||
m_players.removeAt(i);
|
||||
emit gameDetached();
|
||||
}
|
||||
|
||||
int MultiplayerController::playerId(GameController* controller) {
|
||||
MutexLock(&m_lockstep.mutex);
|
||||
int id = -1;
|
||||
for (int i = 0; i < m_lockstep.attached; ++i) {
|
||||
mCoreThread* thread = controller->thread();
|
||||
/*if (thread->sioDrivers.multiplayer == &m_lockstep.players[i]->d) {
|
||||
id = i;
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
MutexUnlock(&m_lockstep.mutex);
|
||||
return id;
|
||||
return m_players.indexOf(controller);
|
||||
}
|
||||
|
||||
int MultiplayerController::attached() {
|
||||
int num;
|
||||
MutexLock(&m_lockstep.mutex);
|
||||
num = m_lockstep.attached;
|
||||
MutexUnlock(&m_lockstep.mutex);
|
||||
return num;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef QGBA_MULTIPLAYER_CONTROLLER
|
||||
#define QGBA_MULTIPLAYER_CONTROLLER
|
||||
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
extern "C" {
|
||||
|
@ -35,6 +37,9 @@ signals:
|
|||
|
||||
private:
|
||||
GBASIOLockstep m_lockstep;
|
||||
QList<GameController*> m_players;
|
||||
QList<int> m_asleep;
|
||||
QMutex m_lock;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -52,6 +52,23 @@ typedef intptr_t ssize_t;
|
|||
#define M_PI 3.141592654f
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#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_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)
|
||||
#else
|
||||
// TODO
|
||||
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
||||
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
||||
#define ATOMIC_ADD(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)
|
||||
#endif
|
||||
|
||||
#if defined(__PPC__) || defined(__POWERPC__)
|
||||
#define LOAD_32LE(DEST, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
|
|
|
@ -7,15 +7,6 @@
|
|||
|
||||
#include "util/memory.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE)
|
||||
#define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE)
|
||||
#else
|
||||
// TODO
|
||||
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
||||
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
||||
#endif
|
||||
|
||||
void RingFIFOInit(struct RingFIFO* buffer, size_t capacity) {
|
||||
buffer->data = anonymousMemoryMap(capacity);
|
||||
buffer->capacity = capacity;
|
||||
|
|
Loading…
Reference in New Issue