-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtimer.rs
More file actions
191 lines (148 loc) · 5.34 KB
/
Copy pathtimer.rs
File metadata and controls
191 lines (148 loc) · 5.34 KB
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! CPU timer management.
use crate::bytes::ByteExt;
use crate::cpu::{MCycles, TCycles};
#[derive(Debug, Default)]
pub struct TimerRegisters {
pub counter: u8,
pub modulo: u8,
pub control: u8,
}
#[derive(Debug, Default)]
pub struct Timer {
/// Divider internal counter. The upper 8 bits are the DIV register.
div_counter: TCycles,
/// Timer internal counter.
timer_counter: MCycles,
/// The amount of time ticked since the last call to `reset_diff`.
diff: u32,
pub reg: TimerRegisters,
}
impl Timer {
pub fn divider(&self) -> u8 {
(self.div_counter.0 >> 8) as u8
}
/// Increment all timer-related registers, based on the M-time of the last instruction.
///
/// Requests the timer interrupt if necessary.
pub(super) fn tick(&mut self, mtime: MCycles, interrupt_requested: &mut bool) {
self.diff += mtime.0;
// The divider is always counting, regardless of whether the timer is enabled.
self.div_counter += TCycles::from(mtime);
if !self.is_enabled() {
return;
}
self.timer_counter += mtime;
// The timer will increment at a frequency determined by the control register.
let threshold = match self.reg.control & 0x3 {
0 => MCycles(256), // 4KHz
1 => MCycles(4), // 256KHz
2 => MCycles(16), // 64KHz
3 => MCycles(64), // 16KHz
_ => unreachable!(),
};
// NB: This is the source of a very common bug in timer implementations.
//
// Here, we need to increment the timer's internal counter relative to the tick size. The
// counter may have to be incremented multiple times for a given tick. While this
// technically could happen for the div internal counter, in practice it doesn't: no
// instruction takes longer to execute than it takes to increment DIV once. However, it
// _is_ possible to have the timer internal counter increment multiple times during a given
// instruction.
//
// Notably, getting this wrong will cause blargg's instr_timing test ROM to fail with
// the cryptic "Failure #255" message.
while self.timer_counter >= threshold {
self.timer_counter -= threshold;
let (counter, overflow) = match self.reg.counter.checked_add(1) {
Some(counter) => (counter, false),
None => (self.reg.modulo, true),
};
self.reg.counter = counter;
if overflow {
*interrupt_requested = true;
}
}
}
/// Returns the number of M-cycles that have passed since the last call of this method.
pub fn diff(&self) -> MCycles {
MCycles(self.diff)
}
pub fn reset_diff(&mut self) {
self.diff = 0;
}
pub fn reset_divider(&mut self) {
self.div_counter = TCycles(0);
self.timer_counter = MCycles(0);
}
pub fn is_enabled(&self) -> bool {
self.reg.control.has_bit_set(2)
}
}
#[cfg(test)]
mod tests {
use std::u8;
use super::{MCycles, Timer};
#[test]
fn div() {
let mut interrupt_requested = false;
let mut timer = Timer::default();
for _ in 0..64 {
timer.tick(MCycles(1), &mut interrupt_requested);
}
assert_eq!(timer.divider(), 1);
for _ in 0..128 {
timer.tick(MCycles(1), &mut interrupt_requested);
}
assert_eq!(timer.divider(), 3);
}
#[test]
fn reset_div() {
let mut interrupt_requested = false;
let mut timer = Timer::default();
for _ in 0..63 {
timer.tick(MCycles(1), &mut interrupt_requested);
}
assert_eq!(timer.divider(), 0);
timer.reset_divider();
assert_eq!(timer.divider(), 0);
for _ in 0..63 {
timer.tick(MCycles(1), &mut interrupt_requested);
}
assert_eq!(timer.divider(), 0);
timer.tick(MCycles(1), &mut interrupt_requested);
assert_eq!(timer.divider(), 1);
}
#[test]
fn tima() {
let mut interrupt_requested = false;
// Enable timer, increment every 64 M-cycles.
let mut timer = Timer::default();
timer.reg.control = 0x07;
for _ in 0..63 {
timer.tick(MCycles(1), &mut interrupt_requested);
}
assert_eq!(timer.reg.counter, 0);
timer.tick(MCycles(1), &mut interrupt_requested);
assert_eq!(timer.reg.counter, 1);
// Enable timer, increment every 4 M-cycles.
let mut timer = Timer::default();
timer.reg.control = 0x05;
timer.tick(MCycles(16), &mut interrupt_requested);
assert_eq!(timer.reg.counter, 4);
}
#[test]
fn tima_overflow() {
let mut interrupt_requested = false;
// Enable timer, increment every 4 M-cycles.
let mut timer = Timer::default();
timer.reg.control = 0x05;
// The number of M-cycles it will take to trigger an interrupt, divided by 8 iterations.
const INCREMENT: MCycles = MCycles(((u8::MAX as u16 * 4) / 8) as u32);
for _ in 0..8 {
timer.tick(INCREMENT, &mut interrupt_requested);
assert!(!interrupt_requested);
}
timer.tick(INCREMENT, &mut interrupt_requested);
assert!(interrupt_requested);
}
}