#include #include #include static timer_context ctx = {0}; bool selected_bit() { switch(ctx.tac & 0b11) { case 0b00: return (ctx.div & (1 << 9)); break; case 0b01: return (ctx.div & (1 << 3)); break; case 0b10: return (ctx.div & (1 << 5)); break; case 0b11: return (ctx.div & (1 << 7)); break; } } void inc_tima() { ctx.tima++; if(ctx.tima == 0x00){ ctx.tima_needs_reset = true; } } void timer_init() { ctx.div = 0XAC00; } void timer_tick() { u16 prev_div = ctx.div; bool old = selected_bit(); ctx.div++; bool new = selected_bit(); if((prev_div & (1 << 1)) && (!(ctx.div & (1 << 1)))){ if(ctx.tima_just_reset) ctx.tima_just_reset = false; if(ctx.tima_needs_reset){ ctx.tima = ctx.tma; ctx.tima_needs_reset = false; ctx.tima_just_reset = true; cpu_request_interrupt(IT_TIMER); } } if(ctx.tac & 0b100 && old && !new) { inc_tima(); } audio_sample_tick(); if((prev_div & (1 << 12)) && (!(ctx.div & (1 << 12)))){ audio_tick(); } if((prev_div & (1 << 0)) && (!(ctx.div & (1 << 0)))){ audio_period_tick(); } } void timer_write(u16 address, u8 value){ switch(address) { case 0xFF04: //DIV if(selected_bit()) { inc_tima(); } ctx.div = 0; break; case 0xFF05: //TIMA if(ctx.tima_just_reset) return; ctx.tima_needs_reset = false; ctx.tima = value; break; case 0xFF06: //TMA if(ctx.tima_just_reset) ctx.tima = value; ctx.tma = value; break; case 0xFF07: //TAC bool current = selected_bit(); bool old_enable = ctx.tac & 0b100; ctx.tac = value; bool new = selected_bit(); if(((current && !new) && (ctx.tac & 0b100)) || (current && old_enable && !(ctx.tac & 0b100))) { inc_tima(); } break; } } u8 timer_read(u16 address) { switch(address) { case 0xFF04: //DIV return ctx.div >> 8; case 0xFF05: //TIMA return ctx.tima; case 0xFF06: //TMA return ctx.tma; case 0xFF07: //TAC return ctx.tac; } } timer_context *timer_get_context() { return &ctx; } void timer_save_state(timer_state* state) { state->ctx = ctx; } void timer_load_state(const timer_state* state) { ctx = state->ctx; }