all repos — mgba @ e12ca74d1e891349a01be852e0ff555529868e50

mGBA Game Boy Advance Emulator

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
commit

e12ca74d1e891349a01be852e0ff555529868e50

parent

2a35f0689aaf74b03a0e02de19643275c9822e6d

2 files changed, 24 insertions(+), 29 deletions(-)

jump to
M CHANGESCHANGES

@@ -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.csrc/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, &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)) { + + 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); - 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); + } } }