GBA Timers: Fix toggling timer cascading while timer is active (fixes #2043)

This commit is contained in:
Vicki Pfau 2021-02-09 00:20:57 -08:00
parent 2a35f0689a
commit e12ca74d1e
2 changed files with 24 additions and 29 deletions

View File

@ -51,6 +51,7 @@ Emulation fixes:
- GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800)
- GBA SIO: Fix deseralizing SIO registers
- GBA SIO: Fix hanging on starting a second multiplayer window (fixes mgba.io/i/854)
- GBA Timers: Fix toggling timer cascading while timer is active (fixes mgba.io/i/2043)
- GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
- GBA Video: Fix Hblank timing
- GBA Video: Implement green swap (fixes mgba.io/i/1609)

View File

@ -121,39 +121,33 @@ void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
struct GBATimer* currentTimer = &gba->timers[timer];
GBATimerUpdateRegister(gba, timer, 0);
unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
unsigned prescaleBits;
switch (control & 0x0003) {
case 0x0000:
prescaleBits = 0;
break;
case 0x0001:
prescaleBits = 6;
break;
case 0x0002:
prescaleBits = 8;
break;
case 0x0003:
prescaleBits = 10;
break;
}
const unsigned prescaleTable[4] = { 0, 6, 8, 10 };
unsigned prescaleBits = prescaleTable[control & 0x0003];
GBATimerFlags oldFlags = currentTimer->flags;
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, prescaleBits);
currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004));
currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040);
bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags);
currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080);
if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) {
bool reschedule = false;
if (GBATimerFlagsIsEnable(oldFlags) != GBATimerFlagsIsEnable(currentTimer->flags)) {
reschedule = true;
if (GBATimerFlagsIsEnable(currentTimer->flags)) {
gba->memory.io[REG_TMCNT_LO(timer) >> 1] = currentTimer->reload;
}
} else if (GBATimerFlagsIsCountUp(oldFlags) != GBATimerFlagsIsCountUp(currentTimer->flags)) {
reschedule = true;
} else if (GBATimerFlagsGetPrescaleBits(currentTimer->flags) != GBATimerFlagsGetPrescaleBits(oldFlags)) {
reschedule = true;
}
if (reschedule) {
mTimingDeschedule(&gba->timing, &currentTimer->event);
gba->memory.io[REG_TMCNT_LO(timer) >> 1] = currentTimer->reload;
int32_t tickMask = (1 << prescaleBits) - 1;
currentTimer->lastEvent = mTimingCurrentTime(&gba->timing) & ~tickMask;
GBATimerUpdateRegister(gba, timer, 0);
} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event);
} else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event);
int32_t tickMask = (1 << prescaleBits) - 1;
currentTimer->lastEvent = mTimingCurrentTime(&gba->timing) & ~tickMask;
GBATimerUpdateRegister(gba, timer, 0);
if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
int32_t tickMask = (1 << prescaleBits) - 1;
currentTimer->lastEvent = mTimingCurrentTime(&gba->timing) & ~tickMask;
GBATimerUpdateRegister(gba, timer, 0);
}
}
}