lc3_ensemble/sim/
device.rs

1//! Handlers for external devices connected to the Simulator.
2//! 
3//! This handles IO devices (such as the keyboard and display)
4//! and handles interrupts.
5//! 
6//! The core types here are:
7//! - [`ExternalDevice`]: A device which can be connected to the Simulator.
8//! - [`DeviceHandler`]: The handler for the Simulator's IO ports & interrupts.
9//! 
10//! This module also provides some IO devices:
11//! - [`NullDevice`]: Does nothing.
12//! - [`BufferedKeyboard`]: Keyboard device that reads off of an input buffer.
13//! - [`BufferedDisplay`]: Display device that writes to an output buffer.
14//! - [`TimerDevice`]: Timer that triggers an interrupt after a specified number of interrupts.
15
16mod keyboard;
17mod display;
18mod timer;
19
20use super::IO_START;
21pub use keyboard::BufferedKeyboard;
22pub use display::BufferedDisplay;
23pub use timer::TimerDevice;
24
25const KBSR: u16 = 0xFE00;
26const KBDR: u16 = 0xFE02;
27const DSR:  u16 = 0xFE04;
28const DDR:  u16 = 0xFE06;
29const KB_INTV: u8 = 0x80;
30const KB_INTP: u8 = 0b100;
31const DEVICE_SLOTS: usize = IO_START.wrapping_neg() as usize;
32
33/// An external device, which can be accessed via memory-mapped IO or via interrupts.
34pub trait ExternalDevice: Send + Sync + 'static {
35    /// Reads the data at the given memory-mapped address.
36    /// 
37    /// If successful, this returns the value returned from that address.
38    /// If unsuccessful, this returns `None`.
39    fn io_read(&mut self, addr: u16, effectful: bool) -> Option<u16>;
40
41    /// Writes the data to the given memory-mapped address.
42    /// 
43    /// This returns whether the write was successful or not.
44    fn io_write(&mut self, addr: u16, data: u16) -> bool;
45
46    /// Resets device.
47    fn io_reset(&mut self);
48
49    /// During each instruction cycle, this function is called once to see whether
50    /// to trigger an interrupt.
51    /// 
52    /// Of course, in the real world, the devices would send an interrupt signal
53    /// which would be detected, but that can't really be done here.
54    fn poll_interrupt(&mut self) -> Option<Interrupt>;
55
56    /// Hacky specialization.
57    /// 
58    /// This allows [`super::Simulator::open_io`]'s signature to just require an [`IODevice`]
59    /// and does not require that we expose IO internals.
60    #[doc(hidden)]
61    fn _to_sim_device(self, _: internals::ToSimDeviceToken) -> internals::SimDevice
62        where Self: Sized
63    {
64        internals::SimDevice::Custom(Box::new(self))
65    }
66}
67
68fn _get_dev_id(ports: &[u16], port: u16) -> Option<u16> {
69    ports.get(port.checked_sub(IO_START)? as usize).copied()
70}
71
72/// The central hub for all external devices for the Simulator.
73#[derive(Debug)]
74pub struct DeviceHandler {
75    devices: Vec<internals::SimDevice>,
76    io_ports: Box<[u16; DEVICE_SLOTS]>
77}
78
79impl DeviceHandler {
80    const NULL_DEV: u16 = 0;
81    const KB_DEV: u16 = 1;
82    const DS_DEV: u16 = 2;
83    const FIXED_DEVS: &'static [u16] = &[Self::NULL_DEV, Self::KB_DEV, Self::DS_DEV];
84
85    /// Creates a new device handler.
86    pub fn new() -> Self {
87        use internals::SimDevice::Null;
88
89        let mut handler = Self {
90            devices: vec![Null, Null, Null],
91            io_ports: vec![0; DEVICE_SLOTS]
92                .try_into()
93                .expect("array should have the correct number of elements")
94        };
95
96        handler.set_port(KBSR, Self::KB_DEV);
97        handler.set_port(KBDR, Self::KB_DEV);
98        handler.set_port(DSR,  Self::DS_DEV);
99        handler.set_port(DDR,  Self::DS_DEV);
100
101        handler
102    }
103
104    /// Gets the device ID bound to a given address.
105    fn get_dev_id(&self, port: u16) -> Option<u16> {
106        _get_dev_id(&*self.io_ports, port)
107    }
108    // Sets port, failing if not possible.
109    fn set_port(&mut self, port: u16, dev_id: u16) -> bool {
110        fn get_dev_id_mut(ports: &mut [u16], port: u16) -> Option<&mut u16> {
111            ports.get_mut(port.checked_sub(IO_START)? as usize)
112        }
113
114        let dev_len = self.devices.len();
115
116        if let Some(d @ 0) = get_dev_id_mut(&mut *self.io_ports, port) {
117            if (dev_id as usize) < dev_len {
118                *d = dev_id;
119            }
120        }
121
122        false
123    }
124
125    /// Set the keyboard device.
126    pub fn set_keyboard(&mut self, kb: impl ExternalDevice) {
127        self.devices[Self::KB_DEV as usize] = kb._to_sim_device(internals::ToSimDeviceToken(()));
128    }
129    /// Set the display device.
130    pub fn set_display(&mut self, ds: impl ExternalDevice) {
131        self.devices[Self::DS_DEV as usize] = ds._to_sim_device(internals::ToSimDeviceToken(()));
132    }
133    /// Add a new device (which is not a keyboard or a display).
134    /// 
135    /// This accepts an external device and the addresses which the device should act on.
136    /// If successful, the ID of the device is returned.
137    /// 
138    /// # Errors
139    /// If the device cannot be added, it will be returned back to the user.
140    /// 
141    /// The cases where the device cannot be added include:
142    /// - The number of devices ever added exceeds [`u16::MAX`].
143    /// - At least one of the addresses provided are already occupied by another device or not an IO port.
144    pub fn add_device<D: ExternalDevice>(&mut self, dev: D, addrs: &[u16]) -> Result<u16, D> {
145        // Fail if too many devices
146        let Ok(dev_id) = u16::try_from(self.devices.len()) else { return Err(dev) };
147        // Fail if port is preoccupied or non-existence
148        let all_valid_ports = addrs.iter()
149            .copied()
150            .map(|p| self.get_dev_id(p))
151            .all(|m_dev_id| m_dev_id.is_some_and(|d| d == 0));
152        if !all_valid_ports { return Err(dev) };
153
154        // Success:
155        self.devices.push(dev._to_sim_device(internals::ToSimDeviceToken(())));
156
157        for &p in addrs {
158            self.set_port(p, dev_id);
159        }
160
161        Ok(dev_id)
162    }
163    /// Removes the device at the given device ID.
164    pub fn remove_device(&mut self, dev_id: u16) {
165        if let Some(dev_ref) = self.devices.get_mut(dev_id as usize) {
166            std::mem::take(dev_ref);
167
168            // Only reset ports if they're not keyboard/display.
169            if !Self::FIXED_DEVS.contains(&dev_id) {
170                self.io_ports.iter_mut()
171                    .filter(|p| **p == dev_id)
172                    .for_each(|p| *p = Self::NULL_DEV);
173            }
174        }
175    }
176}
177impl Default for DeviceHandler {
178    fn default() -> Self {
179        Self::new()
180    }
181}
182impl ExternalDevice for DeviceHandler {
183    /// Accesses the IO device mapped to the given address and tries [`ExternalDevice::io_read`] on it.
184    fn io_read(&mut self, addr: u16, effectful: bool) -> Option<u16> {
185        let dev_id = self.get_dev_id(addr)?;
186        self.devices[dev_id as usize].io_read(addr, effectful)
187    }
188    
189    /// Accesses the IO device mapped to the given address and tries [`ExternalDevice::io_write`] on it.
190    fn io_write(&mut self, addr: u16, data: u16) -> bool {
191        let Some(dev_id) = self.get_dev_id(addr) else { return false };
192        self.devices[dev_id as usize].io_write(addr, data)
193    }
194    
195    /// Resets all the devices connected to this handler.
196    fn io_reset(&mut self) {
197        self.devices.iter_mut().for_each(ExternalDevice::io_reset)
198    }
199
200    /// Checks for interrupts on all devices.
201    fn poll_interrupt(&mut self) -> Option<Interrupt> {
202        self.devices.iter_mut()
203            .filter_map(|s| s.poll_interrupt())
204            .max_by_key(|i| i.priority().unwrap_or(0b1000))
205    }
206}
207
208/// An interrupt.
209/// 
210/// This is output by an implementation of [`ExternalDevice::poll_interrupt`] if an interrupt should occur.
211#[derive(Debug)]
212pub struct Interrupt {
213    pub(super) kind: InterruptKind
214}
215
216#[derive(Debug)]
217pub(super) enum InterruptKind {
218    /// A vectored interrupt (one handled by delegating to a corresponding interrupt vector).
219    Vectored {
220        /// Interrupt vector
221        vect: u8,
222        /// Priority value from 0-7
223        priority: u8
224    },
225
226    /// An external interrupt (one handled by some force outside of the Simulator).
227    External(ExternalInterrupt)
228}
229impl Interrupt {
230    /// Creates a new vectored interrupt.
231    /// 
232    /// Note that the priority is truncated to 3 bits.
233    pub fn vectored(vect: u8, priority: u8) -> Self {
234        Self { kind: InterruptKind::Vectored { vect, priority: priority.clamp(0, 7) } }
235    }
236    /// Creates a new external interrupt (one not handled by the OS).
237    /// 
238    /// When this type of interrupt is raised, the simulator raises [`SimErr::Interrupt`]
239    /// which can be used to handle the resulting `InterruptErr`.
240    /// 
241    /// One example where this is used is in Python bindings. 
242    /// In that case, we want to be able to halt the Simulator on a `KeyboardInterrupt`.
243    /// However, by default, Python cannot signal to the Rust library that a `KeyboardInterrupt`
244    /// has occurred. Thus, we can add a signal handler as an external interrupt to allow the `KeyboardInterrupt`
245    /// to be handled properly.
246    /// 
247    /// [`SimErr::Interrupt`]: super::SimErr::Interrupt
248    pub fn external(e: impl std::error::Error + Send + Sync + 'static) -> Self {
249        Self { kind: InterruptKind::External(ExternalInterrupt::new(e)) }
250    }
251
252    /// Calculates the priority for a given interrupt.
253    /// 
254    /// - For all vectored interrupts, this is the three least-significant bits of the priority value.
255    /// - For external interrupts, this is None
256    pub fn priority(&self) -> Option<u8> {
257        match self.kind {
258            InterruptKind::Vectored { vect: _, priority } => Some(priority & 0b111),
259            InterruptKind::External(_) => None,
260        }
261    }
262}
263
264/// An interrupt not handled by the simulator occurred.
265/// 
266/// See [`Interrupt::external`].
267#[derive(Debug)]
268pub struct ExternalInterrupt(Box<dyn std::error::Error + Send + Sync + 'static>);
269impl ExternalInterrupt {
270    /// Creates a new [`ExternalInterrupt`].
271    fn new(e: impl std::error::Error + Send + Sync + 'static) -> Self {
272        ExternalInterrupt(Box::new(e))
273    }
274
275    /// Get the internal error from this interrupt.
276    /// 
277    /// This can be downcast by the typical methods on `dyn Error`.
278    pub fn into_inner(self) -> Box<dyn std::error::Error + Send + Sync + 'static> {
279        self.0
280    }
281}
282impl std::fmt::Display for ExternalInterrupt {
283    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284        self.0.fmt(f)
285    }
286}
287impl std::error::Error for ExternalInterrupt {}
288
289
290/// Small wrapper that handles the base impls for keyboard and display devices.
291/// 
292/// A little HACKy.
293/// See `keyboard.rs` and `display.rs` for actual use.
294#[repr(transparent)]
295struct DevWrapper<D, T: ?Sized>(D, std::marker::PhantomData<T>);
296impl<D, T: ?Sized> DevWrapper<D, T> {
297    pub fn wrap(d: &mut D) -> &mut DevWrapper<D, T> {
298        // SAFETY: DevWrapper<D, T> is transparent with D
299        unsafe { &mut *(d as *mut D).cast::<DevWrapper<D, T>>() }
300    }
301}
302impl<D, T: ?Sized> std::ops::Deref for DevWrapper<D, T> {
303    type Target = D;
304
305    fn deref(&self) -> &Self::Target {
306        &self.0
307    }
308}
309impl<D, T: ?Sized> std::ops::DerefMut for DevWrapper<D, T> {
310    fn deref_mut(&mut self) -> &mut Self::Target {
311        &mut self.0
312    }
313}
314//
315
316mod internals {
317    use super::{BufferedDisplay, BufferedKeyboard, ExternalDevice, NullDevice};
318
319    #[derive(Default)]
320    pub enum SimDevice {
321        #[default]
322        Null,
323        Keyboard(BufferedKeyboard),
324        Display(BufferedDisplay),
325        Custom(Box<dyn ExternalDevice + Send + Sync + 'static>)
326    }
327
328    impl ExternalDevice for SimDevice {
329        fn io_read(&mut self, addr: u16, effectful: bool) -> Option<u16> {
330            match self {
331                SimDevice::Null => NullDevice.io_read(addr, effectful),
332                SimDevice::Keyboard(dev) => dev.io_read(addr, effectful),
333                SimDevice::Display(dev) => dev.io_read(addr, effectful),
334                SimDevice::Custom(dev) => dev.io_read(addr, effectful),
335            }
336        }
337    
338        fn io_write(&mut self, addr: u16, data: u16) -> bool {
339            match self {
340                SimDevice::Null => NullDevice.io_write(addr, data),
341                SimDevice::Keyboard(dev) => dev.io_write(addr, data),
342                SimDevice::Display(dev) => dev.io_write(addr, data),
343                SimDevice::Custom(dev) => dev.io_write(addr, data),
344            }
345        }
346    
347        fn io_reset(&mut self) {
348            match self {
349                SimDevice::Null => NullDevice.io_reset(),
350                SimDevice::Keyboard(dev) => dev.io_reset(),
351                SimDevice::Display(dev) => dev.io_reset(),
352                SimDevice::Custom(dev) => dev.io_reset(),
353            }
354        }
355
356        fn poll_interrupt(&mut self) -> Option<super::Interrupt> {
357            match self {
358                SimDevice::Null => NullDevice.poll_interrupt(),
359                SimDevice::Keyboard(dev) => dev.poll_interrupt(),
360                SimDevice::Display(dev) => dev.poll_interrupt(),
361                SimDevice::Custom(dev) => dev.poll_interrupt(),
362            }
363        }
364    }
365    impl std::fmt::Debug for SimDevice {
366        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367            match self {
368                Self::Null => write!(f, "Null"),
369                Self::Keyboard(_) => f.debug_struct("Keyboard").finish_non_exhaustive(),
370                Self::Display(_) => f.debug_struct("Display").finish_non_exhaustive(),
371                Self::Custom(_) => f.debug_struct("Custom").finish_non_exhaustive(),
372            }
373        }
374    }
375
376    /// Allows `_to_sim_device` to be private to this file only.
377    pub struct ToSimDeviceToken(pub ());
378}
379
380// Implementations of devices are found in the submodules. Here's one:
381
382/// Does nothing.
383/// 
384/// Does not accept any reads nor writes and never interrupts.
385#[derive(Default, Clone, Copy, PartialEq, Eq)]
386pub struct NullDevice;
387impl ExternalDevice for NullDevice {
388    fn io_read(&mut self, _addr: u16, _effectful: bool) -> Option<u16> {
389        None
390    }
391
392    fn io_write(&mut self, _addr: u16, _data: u16) -> bool {
393        false
394    }
395
396    fn io_reset(&mut self) {}
397
398    fn poll_interrupt(&mut self) -> Option<Interrupt> {
399        None
400    }
401    
402    fn _to_sim_device(self, _: internals::ToSimDeviceToken) -> internals::SimDevice
403        where Self: Sized
404    {
405        internals::SimDevice::Null
406    }
407}
408
409/// A device that handles interrupts with a function.
410#[allow(clippy::type_complexity)]
411pub struct InterruptFromFn(Box<dyn FnMut() -> Option<Interrupt> + Send + Sync + 'static>);
412impl InterruptFromFn {
413    /// Creates a new interrupt from a function.
414    pub fn new(f: impl FnMut() -> Option<Interrupt> + Send + Sync + 'static) -> Self {
415        Self(Box::new(f))
416    }
417}
418impl std::fmt::Debug for InterruptFromFn {
419    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
420        f.debug_struct("InterruptFromFn").finish_non_exhaustive()
421    }
422}
423impl ExternalDevice for InterruptFromFn {
424    fn io_read(&mut self, _addr: u16, _effectful: bool) -> Option<u16> {
425        None
426    }
427
428    fn io_write(&mut self, _addr: u16, _data: u16) -> bool {
429        false
430    }
431
432    fn io_reset(&mut self) {}
433
434    fn poll_interrupt(&mut self) -> Option<Interrupt> {
435        (self.0)()
436    }
437}
438
439fn resolve_lock<G>(e: std::sync::TryLockResult<G>) -> Option<G> {
440    use std::sync::TryLockError;
441
442    match e {
443        Ok(guard) => Some(guard),
444        Err(TryLockError::WouldBlock) => None,
445        Err(TryLockError::Poisoned(e)) => Some(e.into_inner())
446    }
447}
448impl<D: ExternalDevice> ExternalDevice for std::sync::Arc<std::sync::RwLock<D>> {
449    fn io_read(&mut self, addr: u16, effectful: bool) -> Option<u16> {
450        resolve_lock(self.try_write())?
451            .io_read(addr, effectful)
452    }
453
454    fn io_write(&mut self, addr: u16, data: u16) -> bool {
455        resolve_lock(self.try_write())
456            .is_some_and(|mut g| g.io_write(addr, data))
457    }
458
459    fn io_reset(&mut self) {
460        if let Some(mut guard) = resolve_lock(self.try_write()) {
461            guard.io_reset();
462        }
463    }
464
465    fn poll_interrupt(&mut self) -> Option<Interrupt> {
466        resolve_lock(self.try_write())
467            .and_then(|mut g| g.poll_interrupt())
468    }
469}
470impl<D: ExternalDevice> ExternalDevice for std::sync::Arc<std::sync::Mutex<D>> {
471    fn io_read(&mut self, addr: u16, effectful: bool) -> Option<u16> {
472        resolve_lock(self.try_lock())?
473            .io_read(addr, effectful)
474    }
475
476    fn io_write(&mut self, addr: u16, data: u16) -> bool {
477        resolve_lock(self.try_lock())
478            .is_some_and(|mut g| g.io_write(addr, data))
479    }
480
481    fn io_reset(&mut self) {
482        if let Some(mut guard) = resolve_lock(self.try_lock()) {
483            guard.io_reset();
484        }
485    }
486
487    fn poll_interrupt(&mut self) -> Option<Interrupt> {
488        resolve_lock(self.try_lock())
489            .and_then(|mut g| g.poll_interrupt())
490    }
491}