summaryrefslogtreecommitdiff
path: root/src/devices/clock_device.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/clock_device.rs')
-rw-r--r--src/devices/clock_device.rs185
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);
+ }
+}