GBA SIO: Begin implementing Normal mode in lockstep driver
Jeffrey Pfau jeffrey@endrift.com
Thu, 19 Nov 2015 00:30:29 -0800
4 files changed,
130 insertions(+),
16 deletions(-)
M
src/gba/sio.h
→
src/gba/sio.h
@@ -32,6 +32,7 @@ struct GBASIODriverSet drivers;
struct GBASIODriver* activeDriver; uint16_t rcnt; + // TODO: Convert to bitfields union { struct { unsigned sc : 1;
M
src/gba/sio/lockstep.c
→
src/gba/sio/lockstep.c
@@ -14,8 +14,10 @@ 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 GBASIOLockstepNodeWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); -static int32_t GBASIOLockstepNodeProcessEvents(struct GBASIODriver* driver, int32_t cycles); +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); void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) { lockstep->players[0] = 0;@@ -27,7 +29,8 @@ lockstep->multiRecv[1] = 0xFFFF;
lockstep->multiRecv[2] = 0xFFFF; lockstep->multiRecv[3] = 0xFFFF; lockstep->attached = 0; - lockstep->loaded = 0; + lockstep->loadedMulti = 0; + lockstep->loadedNormal = 0; lockstep->transferActive = false; lockstep->waiting = 0; lockstep->nextEvent = LOCKSTEP_INCREMENT;@@ -45,8 +48,8 @@ node->d.init = GBASIOLockstepNodeInit;
node->d.deinit = GBASIOLockstepNodeDeinit; node->d.load = GBASIOLockstepNodeLoad; node->d.unload = GBASIOLockstepNodeUnload; - node->d.writeRegister = GBASIOLockstepNodeWriteRegister; - node->d.processEvents = GBASIOLockstepNodeProcessEvents; + node->d.writeRegister = 0; + node->d.processEvents = 0; } bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) {@@ -93,12 +96,26 @@
bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; node->state = LOCKSTEP_IDLE; + node->mode = driver->p->mode; MutexLock(&node->p->mutex); - ++node->p->loaded; - node->d.p->rcnt |= 3; - if (node->id) { - node->d.p->rcnt |= 4; - node->d.p->multiplayerControl.slave = 1; + switch (node->mode) { + case SIO_MULTI: + node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister; + node->d.processEvents = GBASIOLockstepNodeMultiProcessEvents; + ++node->p->loadedMulti; + node->d.p->rcnt |= 3; + if (node->id) { + node->d.p->rcnt |= 4; + node->d.p->multiplayerControl.slave = 1; + } + break; + case SIO_NORMAL_32: + node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister; + node->d.processEvents = GBASIOLockstepNodeNormalProcessEvents; + ++node->p->loadedNormal; + break; + default: + break; } MutexUnlock(&node->p->mutex); return true;@@ -107,13 +124,22 @@
bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; MutexLock(&node->p->mutex); - --node->p->loaded; + switch (node->mode) { + case SIO_MULTI: + --node->p->loadedMulti; + break; + case SIO_NORMAL_32: + --node->p->loadedNormal; + break; + default: + break; + } ConditionWake(&node->p->barrier); MutexUnlock(&node->p->mutex); return true; } -static uint16_t GBASIOLockstepNodeWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { +static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; if (address == REG_SIOCNT) { GBALog(node->d.p->p, GBA_LOG_SIO, "Lockstep %i: SIOCNT <- %04x", node->id, value);@@ -137,13 +163,13 @@ }
return value; } -static int32_t GBASIOLockstepNodeProcessEvents(struct GBASIODriver* driver, int32_t cycles) { +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->loaded) { + if (node->p->waiting < node->p->loadedMulti) { ConditionWait(&node->p->barrier, &node->p->mutex); } else { if (node->p->transferActive) {@@ -193,7 +219,87 @@ node->multiSend = node->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
node->d.p->multiplayerControl.busy = 1; } } - node->d.p->multiplayerControl.ready = node->p->loaded == node->p->attached; + node->d.p->multiplayerControl.ready = node->p->loadedMulti == node->p->attached; + node->nextEvent += node->p->nextEvent; + MutexUnlock(&node->p->mutex); + } + return node->nextEvent; +} + +static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { + struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; + if (address == REG_SIOCNT) { + GBALog(node->d.p->p, GBA_LOG_SIO, "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; + } + // Frequency + if (value & 2) { + node->p->transferCycles = GBA_ARM7TDMI_FREQUENCY / 1024; + } else { + node->p->transferCycles = GBA_ARM7TDMI_FREQUENCY / 8192; + } + node->normalSO = !!(value & 8); + // Opponent's SO + if (node->id) { + value |= node->p->players[node->id - 1]->normalSO << 2; + } + } + MutexUnlock(&node->p->mutex); + } else if (address == REG_SIODATA32_LO) { + GBALog(node->d.p->p, GBA_LOG_SIO, "Lockstep %i: SIODATA32_LO <- %04x", node->id, value); + } else if (address == REG_SIODATA32_HI) { + GBALog(node->d.p->p, GBA_LOG_SIO, "Lockstep %i: SIODATA32_HI <- %04x", node->id, value); + } + 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); }
M
src/gba/sio/lockstep.h
→
src/gba/sio/lockstep.h
@@ -19,7 +19,8 @@
struct GBASIOLockstep { struct GBASIOLockstepNode* players[MAX_GBAS]; int attached; - int loaded; + int loadedMulti; + int loadedNormal; uint16_t multiRecv[MAX_GBAS]; bool transferActive;@@ -37,8 +38,10 @@ struct GBASIOLockstep* p;
int32_t nextEvent; uint16_t multiSend; + bool normalSO; enum LockstepState state; int id; + enum GBASIOMode mode; }; void GBASIOLockstepInit(struct GBASIOLockstep*);
M
src/platform/qt/MultiplayerController.cpp
→
src/platform/qt/MultiplayerController.cpp
@@ -32,8 +32,10 @@ controller->threadInterrupt();
GBAThread* thread = controller->thread(); if (controller->isLoaded()) { GBASIOSetDriver(&thread->gba->sio, &node->d, SIO_MULTI); + GBASIOSetDriver(&thread->gba->sio, &node->d, SIO_NORMAL_32); } thread->sioDrivers.multiplayer = &node->d; + thread->sioDrivers.normal = &node->d; controller->threadContinue(); emit gameAttached(); return true;@@ -54,8 +56,10 @@ 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); } thread->sioDrivers.multiplayer = nullptr; + thread->sioDrivers.normal = nullptr; GBASIOLockstepDetachNode(&m_lockstep, node); delete node; }