#include "timer.h" // Which DIV bit feeds the TIMA increment for each TAC clock-select. static const int tac_bit[4] = { 9, 3, 5, 7 }; static bool timer_signal(Timer *t) { if (!(t->tac & 0x04)) return false; return (t->div >> tac_bit[t->tac & 3]) & 1; } static void set_div(GB *gb, u16 newdiv) { Timer *t = &gb->timer; bool old = timer_signal(t); t->div = newdiv; bool now = timer_signal(t); // falling edge -> increment TIMA if (old && !now) { if (t->tima == 0xFF) { t->tima = 0; t->overflow_pending = true; t->reload_delay = 4; // reload TMA after 4 T-cycles } else { t->tima++; } } } void timer_tick(GB *gb, int tcycles) { Timer *t = &gb->timer; for (int i = 0; i < tcycles; i++) { if (t->overflow_pending) { t->reload_delay--; if (t->reload_delay == 0) { t->tima = t->tma; t->overflow_pending = false; gb_request_interrupt(gb, INT_TIMER); } } set_div(gb, t->div + 1); } } u8 timer_read(GB *gb, u16 addr) { Timer *t = &gb->timer; switch (addr) { case 0xFF04: return t->div >> 8; case 0xFF05: return t->tima; case 0xFF06: return t->tma; case 0xFF07: return t->tac | 0xF8; } return 0xFF; } void timer_write(GB *gb, u16 addr, u8 val) { Timer *t = &gb->timer; switch (addr) { case 0xFF04: set_div(gb, 0); // writing DIV resets it (may cause edge) break; case 0xFF05: // writing during reload delay cancels the reload if (t->reload_delay != 0) t->overflow_pending = false; if (!(t->overflow_pending && t->reload_delay == 0)) t->tima = val; break; case 0xFF06: t->tma = val; break; case 0xFF07: { bool old = timer_signal(t); t->tac = val & 0x07; bool now = timer_signal(t); if (old && !now) { // disabling can also cause an increment (glitch) if (t->tima == 0xFF) { t->tima = 0; t->overflow_pending = true; t->reload_delay = 4; } else t->tima++; } break; } } }