GBA SIO: Begin implementing Normal mode in lockstep driver

This commit is contained in:
Jeffrey Pfau 2015-11-19 00:30:29 -08:00
parent 505157dca0
commit a89bb0b814
4 changed files with 130 additions and 16 deletions

View File

@ -32,6 +32,7 @@ struct GBASIO {
struct GBASIODriver* activeDriver;
uint16_t rcnt;
// TODO: Convert to bitfields
union {
struct {
unsigned sc : 1;

View File

@ -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 @@ void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
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 @@ void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) {
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 @@ void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver) {
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 GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
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 @@ static uint16_t GBASIOLockstepNodeWriteRegister(struct GBASIODriver* driver, uin
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 @@ static int32_t GBASIOLockstepNodeProcessEvents(struct GBASIODriver* driver, int3
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);
}

View File

@ -19,7 +19,8 @@ enum LockstepState {
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 GBASIOLockstepNode {
int32_t nextEvent;
uint16_t multiSend;
bool normalSO;
enum LockstepState state;
int id;
enum GBASIOMode mode;
};
void GBASIOLockstepInit(struct GBASIOLockstep*);

View File

@ -32,8 +32,10 @@ bool MultiplayerController::attachGame(GameController* controller) {
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 @@ void MultiplayerController::detachGame(GameController* controller) {
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;
}