blob: d4ee69f9ac26a136901d2b8322e366d3317b0b63 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
#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;
}
}
}
|