GBA SIO: Fix Normal mode being totally broken (fixes #1800)

This commit is contained in:
Vicki Pfau 2020-08-03 17:55:44 -07:00
parent c6fb561465
commit c6ca0d25c0
4 changed files with 40 additions and 23 deletions

View File

@ -27,6 +27,7 @@ Emulation fixes:
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190) - 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 Savedata: Fix potential corruption when loading a 1Mbit flash save
- GBA SIO: Fix copying Normal mode transfer values - 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: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
- GBA Video: Fix Hblank timing - GBA Video: Fix Hblank timing
- GBA Video: Fix invalid read in mode 4 mosaic - GBA Video: Fix invalid read in mode 4 mosaic

View File

@ -527,6 +527,8 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
case REG_JOY_TRANS_HI: case REG_JOY_TRANS_HI:
gba->memory.io[REG_JOYSTAT >> 1] |= JOYSTAT_TRANS_BIT; gba->memory.io[REG_JOYSTAT >> 1] |= JOYSTAT_TRANS_BIT;
// Fall through // Fall through
case REG_SIODATA32_LO:
case REG_SIODATA32_HI:
case REG_SIOMLT_SEND: case REG_SIOMLT_SEND:
case REG_JOYCNT: case REG_JOYCNT:
case REG_JOYSTAT: case REG_JOYSTAT:

View File

@ -30,6 +30,7 @@ void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) {
lockstep->multiRecv[2] = 0xFFFF; lockstep->multiRecv[2] = 0xFFFF;
lockstep->multiRecv[3] = 0xFFFF; lockstep->multiRecv[3] = 0xFFFF;
lockstep->attachedMulti = 0; lockstep->attachedMulti = 0;
lockstep->attachedNormal = 0;
} }
void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) { void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) {
@ -44,11 +45,14 @@ bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLock
if (lockstep->d.attached == MAX_GBAS) { if (lockstep->d.attached == MAX_GBAS) {
return false; return false;
} }
mLockstepLock(&lockstep->d);
lockstep->players[lockstep->d.attached] = node; lockstep->players[lockstep->d.attached] = node;
node->p = lockstep; node->p = lockstep;
node->id = lockstep->d.attached; node->id = lockstep->d.attached;
node->normalSO = true;
node->transferFinished = true; node->transferFinished = true;
++lockstep->d.attached; ++lockstep->d.attached;
mLockstepUnlock(&lockstep->d);
return true; return true;
} }
@ -56,6 +60,7 @@ void GBASIOLockstepDetachNode(struct GBASIOLockstep* lockstep, struct GBASIOLock
if (lockstep->d.attached == 0) { if (lockstep->d.attached == 0) {
return; return;
} }
mLockstepLock(&lockstep->d);
int i; int i;
for (i = 0; i < lockstep->d.attached; ++i) { for (i = 0; i < lockstep->d.attached; ++i) {
if (lockstep->players[i] != node) { if (lockstep->players[i] != node) {
@ -66,8 +71,10 @@ void GBASIOLockstepDetachNode(struct GBASIOLockstep* lockstep, struct GBASIOLock
lockstep->players[i - 1]->id = i - 1; lockstep->players[i - 1]->id = i - 1;
} }
--lockstep->d.attached; --lockstep->d.attached;
lockstep->players[lockstep->d.attached] = NULL;
break; break;
} }
mLockstepUnlock(&lockstep->d);
} }
bool GBASIOLockstepNodeInit(struct GBASIODriver* driver) { bool GBASIOLockstepNodeInit(struct GBASIODriver* driver) {
@ -107,6 +114,7 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) {
} }
break; break;
case SIO_NORMAL_32: case SIO_NORMAL_32:
ATOMIC_ADD(node->p->attachedNormal, 1);
node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister; node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister;
break; break;
default: default:
@ -132,6 +140,9 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
case SIO_MULTI: case SIO_MULTI:
ATOMIC_SUB(node->p->attachedMulti, 1); ATOMIC_SUB(node->p->attachedMulti, 1);
break; break;
case SIO_NORMAL_32:
ATOMIC_SUB(node->p->attachedNormal, 1);
break;
default: default:
break; break;
} }
@ -148,11 +159,6 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) {
node->p->d.unload(&node->p->d, node->id); 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); _finishTransfer(node);
if (!node->id) { if (!node->id) {
@ -173,7 +179,7 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
mLockstepLock(&node->p->d); mLockstepLock(&node->p->d);
if (address == REG_SIOCNT) { 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; enum mLockstepPhase transferActive;
ATOMIC_LOAD(transferActive, node->p->d.transferActive); ATOMIC_LOAD(transferActive, node->p->d.transferActive);
@ -200,7 +206,9 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver
value &= 0xFF83; value &= 0xFF83;
value |= driver->p->siocnt & 0x00FC; value |= driver->p->siocnt & 0x00FC;
} else if (address == REG_SIOMLT_SEND) { } 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); mLockstepUnlock(&node->p->d);
@ -246,7 +254,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) {
if (node->id) { if (node->id) {
sio->siocnt = GBASIONormalSetSi(sio->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); 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_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 { } else {
node->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0xFFFF; node->d.p->p->memory.io[REG_SIODATA32_LO >> 1] = 0xFFFF;
node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0xFFFF; node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] = 0xFFFF;
@ -303,8 +311,8 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) {
break; break;
case SIO_NORMAL_32: case SIO_NORMAL_32:
node->p->multiRecv[0] = 0xFFFF; 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_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_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_LO >> 1];
node->p->normalRecv[0] |= node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] << 16; node->p->normalRecv[0] |= node->d.p->p->memory.io[REG_SIODATA32_HI >> 1] << 16;
break; break;
@ -473,12 +481,13 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
mLockstepLock(&node->p->d); mLockstepLock(&node->p->d);
if (address == REG_SIOCNT) { 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; value &= 0xFF8B;
if (!node->id) { if (!node->id) {
value = GBASIONormalFillSi(value); value = GBASIONormalClearSi(value);
} }
if (value & 0x0080 && !node->id) { if (value & 0x0080) {
if (!node->id) {
// Internal shift clock // Internal shift clock
if (value & 1) { if (value & 1) {
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
@ -489,11 +498,14 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive
} else { } else {
node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 8192; node->p->d.transferCycles = GBA_ARM7TDMI_FREQUENCY / 8192;
} }
} else {
}
} }
} else if (address == REG_SIODATA32_LO) { } 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) { } 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); mLockstepUnlock(&node->p->d);

View File

@ -221,6 +221,7 @@ bool MultiplayerController::attachGame(CoreController* controller) {
m_players.append({controller, node}); m_players.append({controller, node});
GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI); GBASIOSetDriver(&gba->sio, &node->d, SIO_MULTI);
GBASIOSetDriver(&gba->sio, &node->d, SIO_NORMAL_32);
emit gameAttached(); emit gameAttached();
return true; return true;
@ -267,6 +268,7 @@ void MultiplayerController::detachGame(CoreController* controller) {
GBA* gba = static_cast<GBA*>(thread->core->board); GBA* gba = static_cast<GBA*>(thread->core->board);
GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(gba->sio.drivers.multiplayer); GBASIOLockstepNode* node = reinterpret_cast<GBASIOLockstepNode*>(gba->sio.drivers.multiplayer);
GBASIOSetDriver(&gba->sio, nullptr, SIO_MULTI); GBASIOSetDriver(&gba->sio, nullptr, SIO_MULTI);
GBASIOSetDriver(&gba->sio, nullptr, SIO_NORMAL_32);
if (node) { if (node) {
GBASIOLockstepDetachNode(&m_gbaLockstep, node); GBASIOLockstepDetachNode(&m_gbaLockstep, node);
delete node; delete node;