lc3_ensemble/sim/
debug.rs

1//! Utilities to debug simulation.
2//! 
3//! The key type here is [`Breakpoint`], which can be appended to the [`Simulator`]'s
4//! breakpoint field to cause the simulator to break.
5use std::fmt::Write;
6
7use crate::ast::Reg;
8
9use super::Simulator;
10
11/// Common breakpoints.
12#[derive(PartialEq, Eq, Hash)]
13pub enum Breakpoint {
14    /// Break when the PC is equal to the given value.
15    PC(u16),
16
17    /// Break when the provided register is set to a given value.
18    Reg {
19        /// Register to check.
20        reg: Reg,
21        /// Predicate to break against.
22        value: Comparator
23    },
24    /// Break when the provided memory address is written to with a given value.
25    Mem {
26        /// Address to check.
27        addr: u16,
28        /// Predicate to break against.
29        value: Comparator
30    },
31}
32
33impl Breakpoint where Breakpoint: Send + Sync { /* assert Breakpoint is send/sync */ }
34
35impl Breakpoint {
36    /// Checks if a break should occur.
37    pub fn check(&self, sim: &Simulator) -> bool {
38        match self {
39            Breakpoint::PC(expected) => expected == &sim.pc,
40            Breakpoint::Reg { reg, value: cmp } => cmp.check(sim.reg_file[*reg].get()),
41            Breakpoint::Mem { addr, value: cmp } => cmp.check(sim.mem[*addr].get()), // do not trigger IO devices
42        }
43    }
44
45    fn fmt_bp(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
46        match self {
47            Self::PC(expected) => {
48                write!(f, "PC == x{expected:04X}")?;
49            },
50            Self::Reg { reg, value } => {
51                write!(f, "{reg} ")?;
52                value.fmt_cmp(f)?;
53            },
54            Self::Mem { addr, value } => {
55                write!(f, "mem[x{addr:04X}] ")?;
56                value.fmt_cmp(f)?;
57            },
58        }
59        Ok(())
60    }
61}
62impl std::fmt::Debug for Breakpoint {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        f.write_str("Breakpoint(")?;
65        self.fmt_bp(f)?;
66        f.write_char(')')
67    }
68}
69/// Predicate checking whether the current value is equal to the value.
70#[derive(PartialEq, Eq, Hash, Debug)]
71pub enum Comparator {
72    /// Never breaks.
73    Never,
74    /// Break if the desired value is less than the provided value.
75    Lt(u16),
76    /// Break if the desired value is equal to the provided value.
77    Eq(u16),
78    /// Break if the desired value is less than or equal to the provided value.
79    Le(u16),
80    /// Break if the desired value is greater than the provided value.
81    Gt(u16),
82    /// Break if the desired value is not equal to the provided value.
83    Ne(u16),
84    /// Break if the desired value is greater than or equal to the provided value.
85    Ge(u16),
86    /// Always breaks.
87    Always
88}
89impl Comparator {
90    /// Checks if the operand passes the comparator.
91    pub fn check(&self, operand: u16) -> bool {
92        match *self {
93            Comparator::Never  => false,
94            Comparator::Lt(r)  => operand < r,
95            Comparator::Eq(r)  => operand == r,
96            Comparator::Le(r)  => operand <= r,
97            Comparator::Gt(r)  => operand > r,
98            Comparator::Ne(r)  => operand != r,
99            Comparator::Ge(r)  => operand >= r,
100            Comparator::Always => true,
101        }
102    }
103
104    fn fmt_cmp(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        match self {
106            Comparator::Never  => f.write_str("never"),
107            Comparator::Lt(r)  => write!(f, "< {r}"),
108            Comparator::Eq(r)  => write!(f, "== {r}"),
109            Comparator::Le(r)  => write!(f, "<= {r}"),
110            Comparator::Gt(r)  => write!(f, "> {r}"),
111            Comparator::Ne(r)  => write!(f, "!= {r}"),
112            Comparator::Ge(r)  => write!(f, ">= {r}"),
113            Comparator::Always => f.write_str("always"),
114        }
115    }
116}