rgy/
timer.rs

1use crate::device::IoHandler;
2use crate::ic::Irq;
3use crate::mmu::{MemRead, MemWrite, Mmu};
4use log::*;
5
6pub struct Timer {
7    irq: Irq,
8    div: u8,
9    div_clocks: usize,
10    tim: u8,
11    tim_clocks: usize,
12    tim_load: u8,
13    ctrl: u8,
14}
15
16impl Timer {
17    pub fn new(irq: Irq) -> Self {
18        Self {
19            irq,
20            div: 0,
21            div_clocks: 0,
22            tim: 0,
23            tim_clocks: 0,
24            tim_load: 0,
25            ctrl: 0,
26        }
27    }
28
29    fn tim_clock_reset(&mut self) {
30        self.tim_clocks = match self.ctrl & 0x3 {
31            0x0 => 1024, // 4096Hz = 1024 cpu clocks
32            0x1 => 16,   // 262144Hz = 16 cpu clocks
33            0x2 => 64,   // 65536Hz = 64 cpu clocks
34            0x3 => 256,  // 16384Hz = 256 cpu clocks
35            _ => unreachable!(),
36        };
37    }
38
39    fn div_clock_reset(&mut self) {
40        self.div_clocks = 256; // 16384Hz = 256 cpu clocks
41    }
42
43    pub fn step(&mut self, time: usize) {
44        if self.div_clocks < time {
45            self.div = self.div.wrapping_add(1);
46            let rem = time - self.div_clocks;
47            self.div_clock_reset();
48            self.div_clocks -= rem;
49        } else {
50            self.div_clocks -= time;
51        }
52
53        if self.ctrl & 0x04 == 0 {
54            return;
55        }
56
57        if self.tim_clocks < time {
58            let mut rem = time - self.tim_clocks;
59
60            loop {
61                let (tim, of) = self.tim.overflowing_add(1);
62                self.tim = tim;
63                if of {
64                    self.tim = self.tim_load;
65                    self.irq.timer(true);
66                }
67                self.tim_clock_reset();
68                if rem <= self.tim_clocks {
69                    self.tim_clocks -= rem;
70                    break;
71                }
72                rem -= self.tim_clocks;
73            }
74        } else {
75            self.tim_clocks -= time;
76        }
77    }
78}
79
80impl IoHandler for Timer {
81    fn on_read(&mut self, _mmu: &Mmu, addr: u16) -> MemRead {
82        info!("Timer read: {:04x}", addr);
83        match addr {
84            0xff04 => MemRead::Replace(self.div),
85            0xff05 => MemRead::Replace(self.tim),
86            0xff06 => MemRead::Replace(self.tim_load),
87            0xff07 => MemRead::Replace(self.ctrl),
88            _ => MemRead::PassThrough,
89        }
90    }
91
92    fn on_write(&mut self, _mmu: &Mmu, addr: u16, value: u8) -> MemWrite {
93        info!("Timer write: {:04x} {:02x}", addr, value);
94        match addr {
95            0xff04 => self.div = 0,
96            0xff05 => self.tim = value,
97            0xff06 => self.tim_load = value,
98            0xff07 => {
99                let old_ctrl = self.ctrl;
100                self.ctrl = value;
101
102                if old_ctrl & 4 == 0 && value & 4 != 0 {
103                    debug!("Timer started");
104                    self.tim_clock_reset();
105                }
106            }
107            _ => {}
108        }
109        MemWrite::PassThrough
110    }
111}