GBA SIO: Fix Normal mode being totally broken (fixes #1800)
@@ -27,6 +27,7 @@ - GBA Memory: Improve gamepak prefetch timing
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190) - GBA Savedata: Fix potential corruption when loading a 1Mbit flash save - GBA SIO: Fix copying Normal mode transfer values + - GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800) - GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319) - GBA Video: Fix Hblank timing - GBA Video: Fix invalid read in mode 4 mosaic
@@ -527,6 +527,8 @@ case REG_JOY_TRANS_LO:
case REG_JOY_TRANS_HI: gba->memory.io[REG_JOYSTAT >> 1] |= JOYSTAT_TRANS_BIT; // Fall through + case REG_SIODATA32_LO: + case REG_SIODATA32_HI: case REG_SIOMLT_SEND: case REG_JOYCNT: case REG_JOYSTAT:
@@ -30,6 +30,7 @@ lockstep->multiRecv[1] = 0xFFFF;
lockstep->multiRecv[2] = 0xFFFF; lockstep->multiRecv[3] = 0xFFFF; lockstep->attachedMulti = 0; + lockstep->attachedNormal = 0; } void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) {@@ -44,11 +45,14 @@ bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) {
if (lockstep->d.attached == MAX_GBAS) { return false; } + mLockstepLock(&lockstep->d); lockstep->players[lockstep->d.attached] = node; node->p = lockstep; node->id = lockstep->d.attached; + node->normalSO = true; node->transferFinished = true; ++lockstep->d.attached; + mLockstepUnlock(&lockstep->d); return true; }@@ -56,6 +60,7 @@ void GBASIOLockstepDetachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) {
if (lockstep->d.attached == 0) { return; } + mLockstepLock(&lockstep->d); int i; for (i = 0; i < lockstep->d.attached; ++i) { if (lockstep->players[i] != node) {@@ -66,8 +71,10 @@ lockstep->players[i - 1] = lockstep->players[i];
lockstep->players[i - 1]->id = i - 1; } --lockstep->d.attached; + lockstep->players[lockstep->d.attached] = NULL; break; } + mLockstepUnlock(&lockstep->d); } bool GBASIOLockstepNodeInit(struct GBASIODriver* driver) {@@ -107,6 +114,7 @@ node->d.p->siocnt = GBASIOMultiplayerFillSlave(node->d.p->siocnt);
} break; case SIO_NORMAL_32: + ATOMIC_ADD(node->p->attachedNormal, 1); node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister; break; default:@@ -132,6 +140,9 @@ switch (node->mode) {
case SIO_MULTI: ATOMIC_SUB(node->p->attachedMulti, 1); break; + case SIO_NORMAL_32: + ATOMIC_SUB(node->p->attachedNormal, 1); + break; default: break; }@@ -148,11 +159,6 @@ }
node->p->d.unload(&node->p->d, node->id); - node->p->multiRecv[0] = 0xFFFF; - node->p->multiRecv[1] = 0xFFFF; - node->p->multiRecv[2] = 0xFFFF; - node->p->multiRecv[3] = 0xFFFF; - _finishTransfer(node); if (!node->id) {@@ -173,7 +179,7 @@
mLockstepLock(&node->p->d); if (address == REG_SIOCNT) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); enum mLockstepPhase transferActive; ATOMIC_LOAD(transferActive, node->p->d.transferActive);@@ -200,7 +206,9 @@ }
value &= 0xFF83; value |= driver->p->siocnt & 0x00FC; } else if (address == REG_SIOMLT_SEND) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04X", node->id, value); + } else { + mLOG(GBA_SIO, STUB, "Lockstep %i: Unknown reg %03X <- %04X", node->id, address, value); } mLockstepUnlock(&node->p->d);@@ -246,7 +254,7 @@ sio->siocnt = GBASIONormalClearStart(sio->siocnt);
if (node->id) { sio->siocnt = GBASIONormalSetSi(sio->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); node->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = node->p->normalRecv[node->id - 1]; - node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] |= node->p->normalRecv[node->id - 1] >> 16; + node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = node->p->normalRecv[node->id - 1] >> 16; } else { node->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0xFFFF; node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0xFFFF;@@ -303,8 +311,8 @@ node->p->normalRecv[0] = node->d.p->p->memory.io[REG_SIODATA8 >> 1] & 0xFF;
break; case SIO_NORMAL_32: node->p->multiRecv[0] = 0xFFFF; - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04x", node->id, node->d.p->p->memory.io[REG_SIODATA32_LO >> 1]); - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04x", node->id, node->d.p->p->memory.io[REG_SIODATA32_HI >> 1]); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04X", node->id, node->d.p->p->memory.io[REG_SIODATA32_LO >> 1]); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04X", node->id, node->d.p->p->memory.io[REG_SIODATA32_HI >> 1]); node->p->normalRecv[0] = node->d.p->p->memory.io[REG_SIODATA32_LO >> 1]; node->p->normalRecv[0] |= node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] << 16; break;@@ -473,27 +481,31 @@
mLockstepLock(&node->p->d); if (address == REG_SIOCNT) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); value &= 0xFF8B; if (!node->id) { - value = GBASIONormalFillSi(value); + value = GBASIONormalClearSi(value); } - if (value & 0x0080 && !node->id) { - // Internal shift clock - if (value & 1) { - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); - } - // Frequency - if (value & 2) { - node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 1024; + if (value & 0x0080) { + if (!node->id) { + // Internal shift clock + if (value & 1) { + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); + } + // Frequency + if (value & 2) { + node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 1024; + } else { + node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 8192; + } } else { - node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 8192; + } } } else if (address == REG_SIODATA32_LO) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04X", node->id, value); } else if (address == REG_SIODATA32_HI) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04x", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04X", node->id, value); } mLockstepUnlock(&node->p->d);
@@ -221,6 +221,7 @@ GBASIOLockstepAttachNode(&m_gbaLockstep, node);
m_players.append({controller, node}); GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI); + GBASIOSetDriver(&gba->sio, &node->d, SIO_NORMAL_32); emit gameAttached(); return true;@@ -267,6 +268,7 @@ case 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_gbaLockstep, node); delete node;