lc3_ensemble/sim/device/
timer.rs

1use std::ops::{Bound, RangeBounds};
2
3use rand::Rng;
4
5use super::ExternalDevice;
6
7#[derive(Clone, Copy)]
8struct SampleRange {
9    start: u32,
10    end: u32,
11    end_incl: bool
12}
13impl SampleRange {
14    fn new(r: impl RangeBounds<u32>) -> Self {
15        let start = match r.start_bound() {
16            Bound::Included(&s) => s,
17            Bound::Excluded(&s) => s.checked_add(1).expect("(Bound::Excluded(u32::MAX), _) is not an acceptable range"),
18            Bound::Unbounded    => 0,
19        };
20        let (end, end_incl) = match r.end_bound() {
21            Bound::Included(&s) => (s, true),
22            Bound::Excluded(&s) => (s, false),
23            Bound::Unbounded => (u32::MAX, true),
24        };
25
26        Self { start, end, end_incl }
27    }
28}
29impl std::fmt::Debug for SampleRange {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            SampleRange { start, end, end_incl: true } => (start..=end).fmt(f),
33            SampleRange { start, end, end_incl: false } => (start..end).fmt(f),
34        }
35    }
36}
37impl RangeBounds<u32> for SampleRange {
38    fn start_bound(&self) -> Bound<&u32> {
39        Bound::Included(&self.start)
40    }
41
42    fn end_bound(&self) -> Bound<&u32> {
43        match self.end_incl {
44            true => Bound::Included(&self.end),
45            false => Bound::Excluded(&self.end),
46        }
47    }
48}
49
50/// A timer device that triggers an interrupt after a configured number (or range) of instructions.
51/// 
52/// This is not part of LC-3 specification, so the design decision made 
53/// (such as default interrupt vector, priority level) are not reflective of LC-3's choices.
54#[derive(Debug)]
55pub struct TimerDevice {
56    generator: Box<rand::rngs::StdRng>,
57    range: SampleRange,
58    time: u32,
59
60    /// The interrupt vector.
61    pub vect: u8,
62    /// The priority. 
63    /// 
64    /// Note that if this exceeds 7, it will be treated as though it is 7.
65    pub priority: u8,
66
67    /// Whether this timer can trigger an interrupt.
68    pub enabled: bool,
69}
70impl TimerDevice {
71    /// Creates a new timer device.
72    /// - `seed`: Sets the seed for the timer's RNG. This can be `None` 
73    ///     if RNG does not need to be deterministic or if range can only be exactly one value.
74    /// - `range`: Sets the range of possible number of instructions before interrupt trigger.
75    /// - `vect` and `priority`: Initializes the interrupt vector and priority values.
76    pub fn new(seed: Option<u64>, range: impl RangeBounds<u32>, vect: u8, priority: u8) -> Self {
77        use rand::SeedableRng;
78        use rand::rngs::StdRng;
79
80        let generator = match seed {
81            Some(s) => StdRng::seed_from_u64(s),
82            None => StdRng::from_os_rng(),
83        };
84
85        let mut timer = Self {
86            generator: Box::new(generator),
87            range: SampleRange::new(range),
88            time: 0,
89            vect,
90            priority,
91            enabled: false
92        };
93        timer.reset_remaining();
94
95        timer
96    }
97
98    /// Gets the range of possible number of instructions before the interrupt is triggered.
99    pub fn get_range(&self) -> impl RangeBounds<u32> {
100        self.range
101    }
102    /// Sets the number of instructions before the interrupt is triggered to a range of values.
103    pub fn set_range(&mut self, r: impl RangeBounds<u32>) -> &mut Self {
104        self.range = SampleRange::new(r);
105        self
106    }
107    /// Sets the number of instructions before the interrupt is triggered to an exact number.
108    pub fn set_exact(&mut self, n: u32) -> &mut Self {
109        self.set_range(n..=n)
110    }
111
112    /// Gets the number of instructions remaining until the interrupt triggers.
113    pub fn get_remaining(&self) -> u32 {
114        self.time
115    }
116    /// Resets the remaining time.
117    pub fn reset_remaining(&mut self) {
118        self.time = self.try_generate_time();
119    }
120    /// Generates a new random time.
121    fn try_generate_time(&mut self) -> u32 {
122        match self.range {
123            SampleRange { start, end, end_incl: true } => self.generator.random_range(start..=end),
124            SampleRange { start, end, end_incl: false } => self.generator.random_range(start..end),
125        }
126    }
127}
128impl Default for TimerDevice {
129    /// Creates a timer with default parameters.
130    /// 
131    /// The default parameters here are:
132    /// - non-deterministic RNG
133    /// - Triggers a timer interrupt every 50 instructions
134    /// - Binds to interrupt vector `0x81`
135    /// - Interrupt priority 4
136    /// - Disabled
137    /// 
138    /// (These are arbitrary.)
139    fn default() -> Self {
140        Self::new(None, 50..=50, 0x81, 0b100)
141    }
142}
143impl ExternalDevice for TimerDevice {
144    fn io_read(&mut self, _addr: u16, _effectful: bool) -> Option<u16> {
145        None
146    }
147    
148    fn io_write(&mut self, _addr: u16, _data: u16) -> bool {
149        false
150    }
151    
152    fn io_reset(&mut self) {
153        self.time = self.try_generate_time();
154    }
155    fn poll_interrupt(&mut self) -> Option<super::Interrupt> {
156        if !self.enabled { return None };
157        
158        match self.time {
159            0 => {
160                self.reset_remaining();
161                None
162            },
163            1 => {
164                self.time = 0;
165                Some(super::Interrupt::vectored(self.vect, self.priority))
166            },
167            _ => {
168                self.time -= 1;
169                None
170            }
171        }
172    }
173}