use std::fmt::Write;
use crate::ast::Reg;
use super::Simulator;
macro_rules! fmt_cmp {
($f:expr, $cmp:expr) => { fmt_cmp!($f, $cmp, "{}") };
($f:expr, $cmp:expr, $lit:literal) => {
match $cmp.flag {
0b000 => write!($f, "never"),
0b100 => write!($f, concat!("< ", $lit), $cmp.value),
0b010 => write!($f, concat!("== ", $lit), $cmp.value),
0b110 => write!($f, concat!("<= ", $lit), $cmp.value),
0b001 => write!($f, concat!("> ", $lit), $cmp.value),
0b101 => write!($f, concat!("!= ", $lit), $cmp.value),
0b011 => write!($f, concat!(">= ", $lit), $cmp.value),
0b111 => write!($f, "always"),
_ => unreachable!("comparator flag should have been less than 8")
}
}
}
pub enum Breakpoint {
PC(Comparator),
Reg {
reg: Reg,
value: Comparator
},
Mem {
addr: u16,
value: Comparator
},
Generic(BreakpointFn),
And([Box<Breakpoint>; 2]),
Or([Box<Breakpoint>; 2]),
}
impl Breakpoint where Breakpoint: Send + Sync { }
type BreakpointFn = Box<dyn Fn(&Simulator) -> bool + Send + Sync + 'static>;
impl Breakpoint {
pub fn generic(f: impl Fn(&Simulator) -> bool + Send + Sync + 'static) -> Breakpoint {
Breakpoint::Generic(Box::new(f))
}
pub fn check(&self, sim: &Simulator) -> bool {
match self {
Breakpoint::PC(cmp) => cmp.check(sim.pc),
Breakpoint::Reg { reg, value: cmp } => cmp.check(sim.reg_file[*reg].get()),
Breakpoint::Mem { addr, value: cmp } => cmp.check(sim.mem.data[*addr as usize].get()), Breakpoint::Generic(pred) => (pred)(sim),
Breakpoint::And([l, r]) => l.check(sim) && r.check(sim),
Breakpoint::Or([l, r]) => l.check(sim) || r.check(sim),
}
}
fn fmt_bp(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::PC(cmp) => {
f.write_str("PC ")?;
fmt_cmp!(f, cmp, "x{:04X}")?;
},
Self::Reg { reg, value } => {
write!(f, "{reg} ")?;
fmt_cmp!(f, value)?;
},
Self::Mem { addr, value } => {
write!(f, "mem[x{addr:04X}] ")?;
fmt_cmp!(f, value)?;
},
Self::Generic(_) => f.debug_struct("Generic").finish_non_exhaustive()?,
Self::And([l, r]) => {
f.write_char('(')?;
l.fmt_bp(f)?;
f.write_str(") && (")?;
r.fmt_bp(f)?;
f.write_char(')')?;
},
Self::Or([l, r]) => {
f.write_char('(')?;
l.fmt_bp(f)?;
f.write_str(") || (")?;
r.fmt_bp(f)?;
f.write_char(')')?;
},
}
Ok(())
}
}
impl std::fmt::Debug for Breakpoint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Breakpoint(")?;
self.fmt_bp(f)?;
f.write_char(')')
}
}
impl std::ops::BitAnd for Breakpoint {
type Output = Breakpoint;
fn bitand(self, rhs: Self) -> Self::Output {
Breakpoint::And([Box::new(self), Box::new(rhs)])
}
}
impl std::ops::BitOr for Breakpoint {
type Output = Breakpoint;
fn bitor(self, rhs: Self) -> Self::Output {
Breakpoint::Or([Box::new(self), Box::new(rhs)])
}
}
impl PartialEq for Breakpoint {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::PC(l0), Self::PC(r0)) => l0 == r0,
(Self::Reg { reg: l_reg, value: l_value }, Self::Reg { reg: r_reg, value: r_value }) => l_reg == r_reg && l_value == r_value,
(Self::Mem { addr: l_addr, value: l_value }, Self::Mem { addr: r_addr, value: r_value }) => l_addr == r_addr && l_value == r_value,
(Self::Generic(_), Self::Generic(_)) => false, (Self::And(l0), Self::And(r0)) => l0 == r0,
(Self::Or(l0), Self::Or(r0)) => l0 == r0,
_ => false,
}
}
}
#[derive(PartialEq, Eq)]
pub struct Comparator {
flag: u8,
pub value: u16
}
impl Comparator {
pub fn never() -> Self {
Self { flag: 0b000, value: 0 }
}
pub fn lt(value: u16) -> Self {
Self { flag: 0b100, value }
}
pub fn eq(value: u16) -> Self {
Self { flag: 0b010, value }
}
pub fn le(value: u16) -> Self {
Self { flag: 0b110, value }
}
pub fn gt(value: u16) -> Self {
Self { flag: 0b001, value }
}
pub fn ne(value: u16) -> Self {
Self { flag: 0b101, value }
}
pub fn ge(value: u16) -> Self {
Self { flag: 0b011, value }
}
pub fn always() -> Self {
Self { flag: 0b111, value: 0 }
}
pub fn check(&self, operand: u16) -> bool {
let cmp_flags = match operand.cmp(&self.value) {
std::cmp::Ordering::Less => 0b100,
std::cmp::Ordering::Equal => 0b010,
std::cmp::Ordering::Greater => 0b001,
};
(cmp_flags & self.flag) != 0
}
}
#[cfg(test)]
mod test {
use crate::ast::reg_consts::R7;
use crate::sim::debug::{Breakpoint, Comparator};
#[test]
fn print() {
println!("{:?}", Breakpoint::Reg { reg: R7, value: Comparator::lt(14) } & Breakpoint::Reg { reg: R7, value: Comparator::ge(10) });
println!("{:?}", Breakpoint::Mem { addr: 0x4000, value: Comparator::lt(14) } | Breakpoint::Mem { addr: 0x5000, value: Comparator::ge(10) } & Breakpoint::PC(Comparator::eq(0x3000)));
}
}