use bedrock_core::*;
use chrono::prelude::*;
use std::time::{Duration, Instant};
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 => 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.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);
}
}