diff options
Diffstat (limited to 'src/devices/clock_device.rs')
-rw-r--r-- | src/devices/clock_device.rs | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/devices/clock_device.rs b/src/devices/clock_device.rs new file mode 100644 index 0000000..494e0c7 --- /dev/null +++ b/src/devices/clock_device.rs @@ -0,0 +1,185 @@ +use bedrock_core::*; + +use std::time::{Duration, Instant}; + + +macro_rules! now { () => { + time::OffsetDateTime::now_local() + .unwrap_or_else(|_| time::OffsetDateTime::now_utc()) +}; } + +fn current_year() -> u8 { now!().year().saturating_sub(2000) as u8 } +fn current_month() -> u8 { now!().month() as u8 - 1 } +fn current_day() -> u8 { now!().day() as u8 - 1 } +fn current_hour() -> u8 { now!().hour() } +fn current_minute() -> u8 { now!().minute() } +fn current_second() -> u8 { now!().second() } + +macro_rules! fn_read_timer { + ($fn_name:ident($read:ident, $end:ident)) => { + pub fn $fn_name(&mut self) { + let uptime = self.uptime(); + if self.$end > uptime { + self.$read = (self.$end.saturating_sub(uptime)) as u16; + } else { + if self.$end > 0 { + self.$end = 0; + self.wake = true; + } + self.$read = 0; + } + } + }; +} + +macro_rules! fn_set_timer { + ($fn_name:ident($write:ident, $end:ident)) => { + pub fn $fn_name(&mut self) { + let uptime = self.uptime(); + if self.$write > 0 { + self.$end = uptime.saturating_add(self.$write as u32); + } else { + self.$end = 0; + } + } + }; +} + + +pub struct ClockDevice { + pub wake: bool, + + pub start: Instant, + pub uptime_read: u16, + + pub t1_end: u32, + pub t2_end: u32, + pub t3_end: u32, + pub t4_end: u32, + pub t1_read: u16, + pub t2_read: u16, + pub t3_read: u16, + pub t4_read: u16, + pub t1_write: u16, + pub t2_write: u16, + pub t3_write: u16, + pub t4_write: u16, +} + +impl ClockDevice { + pub fn new() -> Self { + Self { + start: Instant::now(), + uptime_read: 0, + wake: false, + + t1_end: 0, + t2_end: 0, + t3_end: 0, + t4_end: 0, + t1_read: 0, + t2_read: 0, + t3_read: 0, + t4_read: 0, + t1_write: 0, + t2_write: 0, + t3_write: 0, + t4_write: 0, + } + } + + pub fn uptime(&self) -> u32 { + (self.start.elapsed().as_millis() / 4) as u32 + } + + pub fn read_uptime(&mut self) { + self.uptime_read = self.uptime() as u16; + } + + fn_read_timer!{ read_t1(t1_read, t1_end) } + fn_read_timer!{ read_t2(t2_read, t2_end) } + fn_read_timer!{ read_t3(t3_read, t3_end) } + fn_read_timer!{ read_t4(t4_read, t4_end) } + + fn_set_timer!{ set_t1(t1_write, t1_end) } + fn_set_timer!{ set_t2(t2_write, t2_end) } + fn_set_timer!{ set_t3(t3_write, t3_end) } + fn_set_timer!{ set_t4(t4_write, t4_end) } + + + pub fn time_remaining(&mut self) -> Option<Duration> { + use std::cmp::max; + let uptime = self.uptime(); + + let end = max(self.t1_end, max(self.t2_end, max(self.t3_end, self.t4_end))); + let remaining = end.saturating_sub(uptime); + match remaining > 0 { + true => Some(Duration::from_millis(remaining as u64) * 4), + false => None, + } + } +} + +impl Device for ClockDevice { + fn read(&mut self, port: u8) -> u8 { + match port { + 0x0 => current_year(), + 0x1 => current_month(), + 0x2 => current_day(), + 0x3 => current_hour(), + 0x4 => current_minute(), + 0x5 => current_second(), + 0x6 => { self.read_uptime(); read_h!(self.uptime_read) }, + 0x7 => read_l!(self.uptime_read), + 0x8 => { self.read_t1(); read_h!(self.t1_read) }, + 0x9 => read_l!(self.t1_read), + 0xa => { self.read_t2(); read_h!(self.t2_read) }, + 0xb => read_l!(self.t2_read), + 0xc => { self.read_t3(); read_h!(self.t3_read) }, + 0xd => read_l!(self.t3_read), + 0xe => { self.read_t4(); read_h!(self.t4_read) }, + 0xf => read_l!(self.t4_read), + _ => unreachable!(), + } + } + + fn write(&mut self, port: u8, value: u8) -> Option<Signal> { + match port { + 0x0 => (), + 0x1 => (), + 0x2 => (), + 0x3 => (), + 0x4 => (), + 0x5 => (), + 0x6 => (), + 0x7 => (), + 0x8 => write_h!(self.t1_write, value), + 0x9 => { write_l!(self.t1_write, value); self.set_t1() }, + 0xa => write_h!(self.t2_write, value), + 0xb => { write_l!(self.t2_write, value); self.set_t2() }, + 0xc => write_h!(self.t3_write, value), + 0xd => { write_l!(self.t3_write, value); self.set_t3() }, + 0xe => write_h!(self.t4_write, value), + 0xf => { write_l!(self.t4_write, value); self.set_t4() }, + _ => unreachable!(), + }; + return None; + } + + fn wake(&mut self) -> bool { + let uptime = self.uptime(); + macro_rules! check_timer { + ($end:ident) => { + if self.$end > 0 && self.$end <= uptime { + self.$end = 0; + self.wake = true; + } + }; + } + check_timer!(t1_end); + check_timer!(t2_end); + check_timer!(t3_end); + check_timer!(t4_end); + return std::mem::take(&mut self.wake); + } +} |