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 { 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 { 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); } }