GBA Timers: Fix toggling timer cascading while timer is active (fixes #2043)
Vicki Pfau vi@endrift.com
Tue, 09 Feb 2021 00:20:57 -0800
2 files changed,
24 insertions(+),
29 deletions(-)
M
CHANGES
→
CHANGES
@@ -51,6 +51,7 @@ - GBA SIO: Fix copying Normal mode transfer values
- 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)
M
src/gba/timer.c
→
src/gba/timer.c
@@ -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)) { - mTimingDeschedule(&gba->timing, ¤tTimer->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, ¤tTimer->event); - } else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(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, ¤tTimer->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); + } } }