mirror of https://github.com/mgba-emu/mgba.git
GBA SIO: Begin implementing Normal mode in lockstep driver
This commit is contained in:
parent
505157dca0
commit
a89bb0b814
|
@ -32,6 +32,7 @@ struct GBASIO {
|
|||
struct GBASIODriver* activeDriver;
|
||||
|
||||
uint16_t rcnt;
|
||||
// TODO: Convert to bitfields
|
||||
union {
|
||||
struct {
|
||||
unsigned sc : 1;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue