use crate::*; use chrono::prelude::*; pub struct ClockDevice { pub epoch: Instant, pub uptime_read: u16, // End time for each timer as ticks since epoch, zero if not set. pub t1_end: u64, pub t2_end: u64, pub t3_end: u64, pub t4_end: u64, // Cached read value for each timer. pub t1_read: u16, pub t2_read: u16, pub t3_read: u16, pub t4_read: u16, // Cached write value for each timer. pub t1_write: u16, pub t2_write: u16, pub t3_write: u16, pub t4_write: u16, pub wake: bool, } impl Device for ClockDevice { fn read(&mut self, port: u8) -> u8 { match port { 0x0 => Local::now().year().saturating_sub(2000) as u8, 0x1 => Local::now().month().saturating_sub(1) as u8, 0x2 => Local::now().day().saturating_sub(1) as u8, 0x3 => Local::now().hour() as u8, 0x4 => Local::now().minute() as u8, 0x5 => Local::now().second() as u8, 0x6 => { self.uptime_read = self.uptime() as u16; 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(); if self.t1_end > 0 && self.t1_end <= uptime { self.t1_end = 0; self.wake = true; } if self.t2_end > 0 && self.t2_end <= uptime { self.t2_end = 0; self.wake = true; } if self.t3_end > 0 && self.t3_end <= uptime { self.t3_end = 0; self.wake = true; } if self.t4_end > 0 && self.t4_end <= uptime { self.t4_end = 0; self.wake = true; } return std::mem::take(&mut self.wake); } } impl ClockDevice { pub fn new() -> Self { Self { epoch: 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) -> u64 { (self.epoch.elapsed().as_nanos() * 256 / 1_000_000_000) as u64 } /// Duration remaining until the next timer expires, if any timer is set. pub fn duration_remaining(&mut self) -> Option { let mut end = u64::MAX; if self.t1_end > 0 { end = std::cmp::min(end, self.t1_end); } if self.t2_end > 0 { end = std::cmp::min(end, self.t2_end); } if self.t3_end > 0 { end = std::cmp::min(end, self.t3_end); } if self.t4_end > 0 { end = std::cmp::min(end, self.t4_end); } if end != u64::MAX { let remaining = end.saturating_sub(self.uptime()); Some(Duration::from_nanos(remaining * 1_000_000_000 / 256)) } else { None } } } 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) { match self.$write > 0 { true => self.$end = self.uptime().saturating_add(self.$write as u64), false => self.$end = 0, }; } }; } impl ClockDevice { 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) } }