diff --git a/CHANGES b/CHANGES index fc9b82cda..6fe49fa21 100644 --- a/CHANGES +++ b/CHANGES @@ -66,6 +66,7 @@ Bugfixes: - LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735) - GB: Fix STAT blocking - GB MBC: Fix swapping carts not detect new MBC + - GB Timer: Fix DIV batching if TAC changes Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/gb/timer.c b/src/gb/timer.c index e5bda7113..48383f652 100644 --- a/src/gb/timer.c +++ b/src/gb/timer.c @@ -9,6 +9,8 @@ #include #include +static void _GBTimerUpdateDIV(struct GBTimer* timer, uint32_t cyclesLate); + void _GBTimerIRQ(struct mTiming* timing, void* context, uint32_t cyclesLate) { UNUSED(timing); UNUSED(cyclesLate); @@ -34,8 +36,12 @@ void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLat ++timer->internalDiv; timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4; } + _GBTimerUpdateDIV(timer, cyclesLate); +} + +void _GBTimerUpdateDIV(struct GBTimer* timer, uint32_t cyclesLate) { // Batch div increments - int divsToGo = 16 - (timer->internalDiv & 15); + int divsToGo = 16 - (timer->internalDiv & 15) + (timer->nextDiv / GB_DMG_DIV_PERIOD); int timaToGo = INT_MAX; if (timer->timaPeriod) { timaToGo = timer->timaPeriod - (timer->internalDiv & (timer->timaPeriod - 1)); @@ -43,8 +49,12 @@ void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLat if (timaToGo < divsToGo) { divsToGo = timaToGo; } - timer->nextDiv = GB_DMG_DIV_PERIOD * divsToGo; - mTimingSchedule(timing, &timer->event, timer->nextDiv - cyclesLate); + if (divsToGo > 16) { + divsToGo = 16; + } + timer->nextDiv &= GB_DMG_DIV_PERIOD - 1; + timer->nextDiv += GB_DMG_DIV_PERIOD * divsToGo; + mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv - cyclesLate); } void GBTimerReset(struct GBTimer* timer) { @@ -89,6 +99,8 @@ uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) { } else { timer->timaPeriod = 0; } + mTimingDeschedule(&timer->p->timing, &timer->event); + _GBTimerUpdateDIV(timer, 0); return tac; }