lc3_ensemble/
sim.rs

1//! Simulating and execution for LC-3 assembly.
2//! 
3//! This module is focused on executing fully assembled code (i.e., [`ObjectFile`]).
4//! 
5//! This module consists of:
6//! - [`Simulator`]: The struct that simulates assembled code.
7//! - [`mem`]: The module handling memory relating to the registers.
8//! - [`device`]: The module handling simulator IO, interrupts, and general handling of external devices.
9//! - [`debug`]: The module handling types of breakpoints for the simulator.
10//! - [`frame`]: The module handling the frame stack and call frame management.
11//! 
12//! # Usage
13//! 
14//! To simulate some code, you need to instantiate a Simulator and load an object file to it:
15//! 
16//! ```no_run
17//! use lc3_ensemble::sim::Simulator;
18//! 
19//! # let obj_file = panic!("don't actually make an object file");
20//! let mut simulator = Simulator::new(Default::default());
21//! simulator.load_obj_file(&obj_file);
22//! simulator.run().unwrap();
23//! ```
24//! 
25//! ## Flags
26//! 
27//! Here, we define `simulator` to have the default flags. 
28//! We could also configure the simulator by editing the flags. For example,
29//! if we wish to enable real traps, we can edit the flags like so:
30//! 
31//! ```no_run
32//! # use lc3_ensemble::sim::{Simulator, SimFlags};
33//! let mut simulator = Simulator::new(SimFlags { use_real_traps: true, ..Default::default() });
34//! ```
35//! 
36//! All of the available flags can be found in [`SimFlags`].
37//! 
38//! ## Execution
39//! 
40//! Beyond the basic [`Simulator::run`] (which runs until halting),
41//! there are also: 
42//! - [`Simulator::step_in`], [`Simulator::step_out`], [`Simulator::step_over`]: manual step-by-step simulation
43//! - [`Simulator::run_while`], [`Simulator::run_with_limit`]: more advanced programmatic execution
44//! 
45//! ```
46//! use lc3_ensemble::parse::parse_ast;
47//! use lc3_ensemble::asm::assemble;
48//! use lc3_ensemble::sim::Simulator;
49//! use lc3_ensemble::ast::Reg::R0;
50//! 
51//! let src = "
52//!     .orig x3000
53//!     AND R0, R0, #0
54//!     ADD R0, R0, #1
55//!     ADD R0, R0, #1
56//!     ADD R0, R0, #1
57//!     HALT
58//!     .end
59//! ";
60//! let ast = parse_ast(src).unwrap();
61//! let obj_file = assemble(ast).unwrap();
62//! 
63//! let mut sim = Simulator::new(Default::default());
64//! sim.load_obj_file(&obj_file);
65//! 
66//! // Running step by step:
67//! sim.step_in().unwrap();
68//! assert_eq!(sim.reg_file[R0].get(), 0);
69//! sim.step_in().unwrap();
70//! assert_eq!(sim.reg_file[R0].get(), 1);
71//! sim.step_in().unwrap();
72//! assert_eq!(sim.reg_file[R0].get(), 2);
73//! sim.step_in().unwrap();
74//! assert_eq!(sim.reg_file[R0].get(), 3);
75//! ```
76//! 
77//! ## Querying State
78//! 
79//! You can query (or set) a variety of different state values from the simulator.
80//! 
81//! - If you wish to access the PC, it can simply be done through the `sim.pc` field.
82//! - If you wish to access the PSR or MCR, the [`Simulator::psr`] and [`Simulator::mcr`] methods are present to query those values.
83//! - If you wish to access the register file, you can access it through the `sim.reg_file` field.
84//! 
85//! [`RegFile`] holds its values in a [`Word`], which is a memory cell which keeps track of initialization state.
86//! Accessing can simply be done with [`Word::get`] and [`Word::set`]:
87//! ```
88//! use lc3_ensemble::sim::Simulator;
89//! use lc3_ensemble::ast::Reg::R0;
90//! 
91//! let mut sim = Simulator::new(Default::default());
92//! 
93//! sim.reg_file[R0].set(0x1234);
94//! assert_eq!(sim.reg_file[R0].get(), 0x1234);
95//! ```
96//! 
97//! - If you wish to access the memory, the simulator provides two pairs of memory access:
98//!     - Direct access to the memory array (via the `mem`) field, which does not trigger access violations or IO.
99//!     - [`Simulator::read_mem`] and [`Simulator::write_mem`], which are used for accesses which do trigger access violations and IO.
100//! ```
101//! use lc3_ensemble::sim::Simulator;
102//! 
103//! let mut sim = Simulator::new(Default::default());
104//! 
105//! // Raw memory access:
106//! sim.mem[0x3000].set(0x5678);
107//! assert_eq!(sim.mem[0x3000].get(), 0x5678);
108//! 
109//! // Through read/write:
110//! use lc3_ensemble::sim::mem::Word;
111//! 
112//! assert!(sim.write_mem(0x0000, Word::new_init(0x9ABC), sim.default_mem_ctx()).is_err());
113//! assert!(sim.write_mem(0x3000, Word::new_init(0x9ABC), sim.default_mem_ctx()).is_ok());
114//! assert!(sim.read_mem(0x0000, sim.default_mem_ctx()).is_err());
115//! assert!(sim.read_mem(0x3000, sim.default_mem_ctx()).is_ok());
116//! ```
117//! 
118//! - Other state can be accessed. Consult the [`Simulator`] docs for more information.
119//! 
120//! ### Frames
121//! 
122//! The simulator also keeps track of subroutine frame information, accessible on the `frame_stack` field of [`Simulator`].
123//! 
124//! **If `debug_frames` is not enabled in [`SimFlags`]**, the only information the [`Simulator`] keeps track of
125//! is the number of subroutine frames deep the simulator is (via [`FrameStack::len`]):
126//! - During a JSR instruction, the frame count increases by 1.
127//! - During a RET instruction, the frame count decreases by 1.
128//! 
129//! **If `debug_frames` is enabled in [`SimFlags`]**, the frame information is significantly extended.
130//! The simulator then keeps track of several frame values (such as caller and callee address).
131//! These are accessible via the [`FrameStack::frames`] method.
132//! 
133//! Debug frame information by default includes caller and callee addresses, but can be
134//! configured to also include frame pointer and argument information. See the [`frame`]
135//! module for details.
136//! 
137//! ## Debugging with breakpoints
138//! 
139//! Breakpoints are accessible through the `breakpoints` field on [`Simulator`].
140//! 
141//! To add a `breakpoint`, simply insert a [`Breakpoint`] and 
142//! it will break if its condition is met during all execution functions (except [`Simulator::step_in`]).
143//! 
144//! ```
145//! use lc3_ensemble::parse::parse_ast;
146//! use lc3_ensemble::asm::assemble;
147//! use lc3_ensemble::sim::Simulator;
148//! use lc3_ensemble::sim::debug::Breakpoint;
149//! 
150//! let src = "
151//!     .orig x3000
152//!     ADD R0, R0, #0
153//!     ADD R0, R0, #1
154//!     ADD R0, R0, #2
155//!     ADD R0, R0, #3
156//!     HALT
157//!     .end
158//! ";
159//! let ast = parse_ast(src).unwrap();
160//! let obj_file = assemble(ast).unwrap();
161//! 
162//! let mut sim = Simulator::new(Default::default());
163//! sim.load_obj_file(&obj_file);
164//! 
165//! // Without breakpoint
166//! sim.run().unwrap();
167//! assert_eq!(sim.pc, 0x3004);
168//! 
169//! // With breakpoint
170//! sim.reset();
171//! sim.load_obj_file(&obj_file);
172//! sim.breakpoints.insert(Breakpoint::PC(0x3002));
173//! sim.run().unwrap();
174//! assert_eq!(sim.pc, 0x3002);
175//! ```
176//! 
177//! ## IO, interrupts, and external devices
178//! 
179//! IO and interrupts are handled by "external devices" (the trait [`ExternalDevice`]).
180//! 
181//! These can be added by registering the device in the Simulator's device handler 
182//! ([`DeviceHandler::add_device`] of the `device_handler` field).
183//! 
184//! When a load or store to a memory-mapped address (0xFE00-0xFFFF) occurs,
185//! the device handler sends the corresponding load/store to the device for it to handle.
186//! 
187//! The best IO for programmatic uses is [`device::BufferedKeyboard`] and [`device::BufferedDisplay`],
188//! which exposes the IO to memory buffers that can be modified.
189//! 
190//! ```
191//! use lc3_ensemble::parse::parse_ast;
192//! use lc3_ensemble::asm::assemble;
193//! use lc3_ensemble::sim::Simulator;
194//! use lc3_ensemble::sim::device::{BufferedKeyboard, BufferedDisplay};
195//! use std::sync::Arc;
196//! 
197//! let src = "
198//!     .orig x3000
199//!     LOOP:
200//!     GETC
201//!     PUTC
202//!     ADD R0, R0, #0
203//!     BRnp LOOP
204//!     HALT
205//!     .end
206//! ";
207//! let ast = parse_ast(src).unwrap();
208//! let obj_file = assemble(ast).unwrap();
209//! 
210//! let mut sim = Simulator::new(Default::default());
211//! sim.load_obj_file(&obj_file);
212//! 
213//! let input = BufferedKeyboard::default();
214//! let output = BufferedDisplay::default();
215//! sim.device_handler.set_keyboard(input.clone());
216//! sim.device_handler.set_display(output.clone());
217//! 
218//! input.get_buffer().write().unwrap().extend(b"Hello, World!\0");
219//! sim.run().unwrap();
220//! 
221//! assert_eq!(&*input.get_buffer().read().unwrap(), b"");
222//! assert_eq!(&**output.get_buffer().read().unwrap(), b"Hello, World!\0");
223//! ```
224//! 
225//! These external devices also support interrupt-based IO.
226//! 
227//! If the [`device::BufferedKeyboard`] device is enabled, interrupts can be enabled by setting `KBSR[14]`.
228//! Once enabled, the keyboard can interrupt the simulator and run its interrupt service routine.
229//! 
230//! ## Strictness (experimental)
231//! 
232//! This simulator also features uninitialized memory access checks (via the `strict` flag).
233//! 
234//! These strict memory checks verify that unintialized data is not written to the register files, memory, 
235//! or other areas that do not expect uninitialized data. Uninitialized data here is defined as
236//! data that is unknown as it was never fully set and is dependent on the values the machine was initialized with.
237//! 
238//! The `strict` flag can currently detect:
239//! - Loads of uninitialized data into a register (excluding uninitialized reads from `mem[R6 + offset]`).
240//! - Stores of uninitialized data into memory (excluding uninitialized stores to `mem[R6 + offset]` and `.blkw`'d memory).
241//! - Stores of uninitialized data into memory-mapped IO
242//! - Loads and stores through an uninitialized memory address
243//! - Jumping to an uninitialized address (e.g., via `JSRR` or `JMP`)
244//! - Jumping to a memory location that is uninitialized
245//! - Decoding an instruction from uninitialized data
246//! - Setting the PSR to an uninitialized value
247//! 
248//! Note that this is considered *experimental* as false positives can still occur.
249//! Also note that the exceptions for loads and stores of uninitialized data
250//! are present to prevent typical value manipulation on the stack or in stored memory
251//! from triggering a strictness error.
252//! 
253//! ```
254//! use lc3_ensemble::parse::parse_ast;
255//! use lc3_ensemble::asm::assemble;
256//! use lc3_ensemble::sim::{Simulator, SimErr};
257//! 
258//! let src = "
259//!     .orig x3000
260//!     ADD R0, R0, #0
261//!     ADD R0, R0, #15 ;; R0 = 15
262//!     HALT
263//!     .end
264//! ";
265//! let ast = parse_ast(src).unwrap();
266//! let obj_file = assemble(ast).unwrap();
267//! 
268//! let mut sim = Simulator::new(Default::default());
269//! sim.load_obj_file(&obj_file);
270//! sim.flags.strict = true;
271//! 
272//! // Strictness check detects `R0` was set without first being cleared.
273//! assert!(matches!(sim.run(), Err(SimErr::StrictRegSetUninit)));
274//! assert_eq!(sim.prefetch_pc(), 0x3000);
275//! ```
276//! 
277//! [`VecDeque`]: std::collections::VecDeque
278//! [`Breakpoint`]: self::debug::Breakpoint
279pub mod mem;
280pub mod debug;
281pub mod frame;
282pub mod device;
283pub mod observer;
284
285use std::collections::{HashMap, HashSet};
286use std::sync::atomic::AtomicBool;
287use std::sync::Arc;
288
289use crate::asm::ObjectFile;
290use crate::ast::Reg::{R6, R7};
291use crate::ast::sim::SimInstr;
292use crate::ast::ImmOrReg;
293use debug::Breakpoint;
294use device::{DeviceHandler, ExternalDevice, ExternalInterrupt};
295use observer::AccessSet;
296
297use self::frame::{FrameStack, FrameType};
298use self::mem::{MemArray, RegFile, Word, MachineInitStrategy};
299
300/// Errors that can occur during simulation.
301#[derive(Debug)]
302pub enum SimErr {
303    /// Word was decoded, but the opcode was invalid.
304    IllegalOpcode,
305    /// Word was decoded, and the opcode is recognized,
306    /// but the instruction's format is invalid.
307    InvalidInstrFormat,
308    /// A privileged instruction was called in user mode.
309    PrivilegeViolation,
310    /// A supervisor region was accessed in user mode.
311    AccessViolation,
312    /// Object file contained unresolved external symbols.
313    UnresolvedExternal(String),
314    /// Interrupt raised.
315    Interrupt(ExternalInterrupt),
316    /// A register was loaded with a partially uninitialized value.
317    /// 
318    /// This will ignore loads from the stack (R6), because it is convention to push registers 
319    /// (including uninitialized registers).
320    /// This also ignores loads from allocated (`.blkw`) memory in case the program writer
321    /// uses those as register stores.
322    // IDEA: So currently, the way this is implemented is that LDR Rx, R6, OFF is accepted regardless of initialization.
323    // We could make this stricter by keeping track of how much is allocated on the stack.
324    StrictRegSetUninit,
325    /// Memory was loaded with a partially uninitialized value.
326    /// 
327    /// This will ignore loads from the stack (R6), because it is convention to push registers 
328    /// (including uninitialized registers).
329    /// This also ignores loads from allocated (`.blkw`) memory in case the program writer
330    /// uses those as register stores.
331    // IDEA: See StrictRegSetUninit.
332    StrictMemSetUninit,
333    /// Data was stored into MMIO with a partially uninitialized value.
334    StrictIOSetUninit,
335    /// Address to jump to is coming from an uninitialized value.
336    StrictJmpAddrUninit,
337    /// Address to jump to (which is a subroutine or trap call) is coming from an uninitialized value.
338    StrictSRAddrUninit,
339    /// Address to read from memory is coming from an uninitialized value.
340    StrictMemAddrUninit,
341    /// PC is pointing to an uninitialized value.
342    StrictPCCurrUninit,
343    /// PC was set to an address that has an uninitialized value and will read from it next cycle.
344    StrictPCNextUninit,
345    /// The PSR was loaded with a partially uninitialized value (by RTI).
346    StrictPSRSetUninit,
347}
348impl std::fmt::Display for SimErr {
349    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350        match self {
351            SimErr::IllegalOpcode         => f.write_str("simulator executed illegal opcode"),
352            SimErr::InvalidInstrFormat    => f.write_str("simulator executed invalid instruction"),
353            SimErr::PrivilegeViolation    => f.write_str("privilege violation"),
354            SimErr::AccessViolation       => f.write_str("access violation"),
355            SimErr::UnresolvedExternal(s) => write!(f, "unresolved external label {s} in object file"),
356            SimErr::Interrupt(e)          => write!(f, "unhandled interrupt: {e}"),
357            SimErr::StrictRegSetUninit    => f.write_str("register was set to uninitialized value (strict mode)"),
358            SimErr::StrictMemSetUninit    => f.write_str("tried to write an uninitialized value to memory (strict mode)"),
359            SimErr::StrictIOSetUninit     => f.write_str("tried to write an uninitialized value to memory-mapped IO (strict mode)"),
360            SimErr::StrictJmpAddrUninit   => f.write_str("PC address was set to uninitialized address (strict mode)"),
361            SimErr::StrictSRAddrUninit    => f.write_str("Subroutine starts at uninitialized address (strict mode)"),
362            SimErr::StrictMemAddrUninit   => f.write_str("tried to access memory with an uninitialized address (strict mode)"),
363            SimErr::StrictPCCurrUninit    => f.write_str("PC is pointing to uninitialized value (strict mode)"),
364            SimErr::StrictPCNextUninit    => f.write_str("PC will point to uninitialized value when this instruction executes (strict mode)"),
365            SimErr::StrictPSRSetUninit    => f.write_str("tried to set the PSR to an uninitialized value (strict mode)"),
366        }
367    }
368}
369impl std::error::Error for SimErr {}
370
371/// Anything that can cause a step to abruptly fail to finish.
372enum StepBreak {
373    /// A virtual halt was executed.
374    Halt,
375    /// A simulation error occurred.
376    Err(SimErr),
377}
378impl From<SimErr> for StepBreak {
379    fn from(value: SimErr) -> Self {
380        Self::Err(value)
381    }
382}
383
384macro_rules! int_vect {
385    ($Type:ident, {$($name:ident = $value:literal), +}) => {
386        enum $Type {
387            $($name = $value),+
388        }
389        impl TryFrom<u16> for $Type {
390            type Error = ();
391
392            fn try_from(value: u16) -> Result<Self, Self::Error> {
393                match value {
394                    $($value => Ok(Self::$name)),+,
395                    _ => Err(())
396                }
397            }
398        }
399    }
400}
401int_vect!(RealIntVect, {
402    Halt = 0x25,
403    PrivilegeViolation = 0x100,
404    IllegalOpcode = 0x101,
405    AccessViolation = 0x102
406});
407
408
409/// The OS object file with symbols.
410/// 
411/// Do not rely on this existing.
412#[doc(hidden)]
413#[allow(non_snake_case)]
414pub fn _os_obj_file() -> &'static ObjectFile {
415    // This is public because LC3Tools UI needs it;
416    // however, I don't think there's any particular other reason
417    // that a developer would need this, so it's #[doc(hidden)].
418    use crate::parse::parse_ast;
419    use crate::asm::assemble_debug;
420    use std::sync::OnceLock;
421
422    static OS_OBJ_FILE: OnceLock<ObjectFile> = OnceLock::new();
423    
424    OS_OBJ_FILE.get_or_init(|| {
425        let os_file = include_str!("os.asm");
426        let ast = parse_ast(os_file).unwrap();
427        assemble_debug(ast, os_file).unwrap()
428    })
429}
430
431/// Reason for why execution paused if it wasn't due to an error.
432#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
433enum PauseCondition {
434    /// Program reached a halt.
435    Halt,
436    /// Program set MCR to off.
437    MCROff,
438    /// Program hit a breakpoint.
439    Breakpoint,
440    /// Program hit a tripwire condition.
441    Tripwire,
442    /// Program hit an error and did not pause successfully.
443    #[default]
444    Unsuccessful
445}
446
447/// Configuration flags for [`Simulator`].
448/// 
449/// These can be modified after the `Simulator` is created with [`Simulator::new`]
450/// and their effects should still apply.
451/// 
452/// Read the field descriptions for more details.
453#[derive(Debug, PartialEq, Eq, Clone, Copy)]
454pub struct SimFlags {
455    /// Whether strict mode is enabled.
456    /// 
457    /// Strict mode adds additional integrity checks to the simulator,
458    /// such as verifying initialization state is normal for provided data.
459    /// 
460    /// By default, this flag is `false`.
461    pub strict: bool,
462
463    /// Whether to use emulated version of certain traps.
464    /// 
465    /// Certain traps and exceptions have two separate implementations within `Simulator`, namely:
466    /// - `HALT` or `TRAP x25`
467    /// - Privilege mode exception
468    /// - Illegal opcode exception
469    /// - Access violation exception
470    /// 
471    /// This flag allows us to configure between the two implementations:
472    /// - **virtual** (`false`): On execution of one of these interrupts, the simulator breaks
473    ///     and prints its own error.
474    /// - **real** (`true`): On execution of one of these interrupts, the simulator delegates
475    ///     the error to the machine's OS and continues through the OS.
476    /// 
477    /// Activating real traps is useful for maintaining integrity to the LC-3 ISA, whereas
478    /// virtual HALT preserves the state of the machine prior to calling the interrupt routines
479    /// and can provide slightly more helpful error messages.
480    /// 
481    /// By default, this flag is `false`.
482    pub use_real_traps: bool,
483    
484    /// The creation strategy for uninitialized Words.
485    /// 
486    /// This is used to initialize the `mem` and `reg_file` fields.
487    /// 
488    /// By default, this flag is [`MachineInitStrategy::default`].
489    pub machine_init: MachineInitStrategy,
490
491    /// Whether to store debugging information about call frames.
492    /// 
493    /// This flag only goes into effect after a `Simulator::new` or `Simulator::reset` call.
494    /// 
495    /// By default, this flag is `false`.
496    pub debug_frames: bool,
497
498    /// If true, privilege checks are ignored and the simulator runs as though
499    /// the executor has supervisor level privilege.
500    /// 
501    /// By default, this flag is `false`.
502    pub ignore_privilege: bool
503}
504
505#[allow(clippy::derivable_impls)]
506impl Default for SimFlags {
507    fn default() -> Self {
508        Self {
509            strict: false,
510            use_real_traps: false,
511            machine_init: Default::default(),
512            debug_frames: false,
513            ignore_privilege: false
514        }
515    }
516}
517
518const USER_START: u16 = 0x3000;
519const IO_START: u16 = 0xFE00;
520const PSR_ADDR: u16 = 0xFFFC;
521const MCR_ADDR: u16 = 0xFFFE;
522
523/// A register which stores internal state in [`Simulator`].
524/// 
525/// An internal register can be memory-mapped using [`Simulator::mmap_internal`]
526/// and unmapped using [`Simulator::munmap_internal`].
527#[derive(Debug, PartialEq, Eq, Clone, Copy)]
528pub enum InternalRegister {
529    /// The program counter.
530    PC,
531    /// The processor status register.
532    PSR,
533    /// Machine control register.
534    MCR,
535    /// Saved stack pointer (USP in kernel, SSP in user).
536    SavedSP,
537}
538impl InternalRegister {
539    fn default_mmap() -> HashMap<u16, Self> {
540        HashMap::from_iter([
541            (PSR_ADDR, Self::PSR),
542            (MCR_ADDR, Self::MCR),
543        ])
544    }
545
546    /// Reads from an internal register.
547    fn read(self, sim: &mut Simulator) -> u16 {
548        use std::sync::atomic::Ordering;
549
550        match self {
551            InternalRegister::PC => sim.pc,
552            InternalRegister::PSR => sim.psr.get(),
553            InternalRegister::MCR => u16::from(sim.mcr.load(Ordering::Relaxed)) << 15,
554            InternalRegister::SavedSP => sim.saved_sp.get(),
555        }
556    }
557    /// Writes to an internal register.
558    fn write(self, sim: &mut Simulator, data: u16) {
559        use std::sync::atomic::Ordering;
560
561        match self {
562            InternalRegister::PC => sim.pc = data,
563            InternalRegister::PSR => sim.psr.set(data),
564            InternalRegister::MCR => sim.mcr.store((data as i16) < 0, Ordering::Relaxed),
565            InternalRegister::SavedSP => sim.saved_sp.set(data)
566        }
567    }
568}
569
570/// Error in [`Simulator::mmap_internal`].
571#[derive(Debug)]
572pub enum MMapInternalErr {
573    /// Address specified is not in IO range.
574    NotInIORange,
575    /// Address specified is already mapped to another internal register or device.
576    AddrAlreadyMapped
577}
578impl std::fmt::Display for MMapInternalErr {
579    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
580        match self {
581            MMapInternalErr::NotInIORange => f.write_str("address not in IO range"),
582            MMapInternalErr::AddrAlreadyMapped => f.write_str("address already mapped"),
583        }
584    }
585}
586impl std::error::Error for MMapInternalErr {}
587
588/// Context behind a memory access.
589/// 
590/// This struct is used by [`Simulator::read_mem`] and [`Simulator::write_mem`] to perform checks against memory accesses.
591/// A default memory access context for the given simulator can be constructed with [`Simulator::default_mem_ctx`].
592#[derive(Clone, Copy, Debug)]
593pub struct MemAccessCtx {
594    /// Whether this access is privileged (false = user, true = supervisor).
595    pub privileged: bool,
596
597    /// Whether writes to memory should follow strict rules 
598    /// (no writing partially or fully uninitialized data).
599    /// 
600    /// This does not affect [`Simulator::read_mem`].
601    pub strict: bool,
602
603    /// Whether a read to memory-mapped IO should cause side effects.
604    /// 
605    /// This can be set to false to observe the value of IO.
606    /// 
607    /// This does not affect [`Simulator::write_mem`].
608    pub io_effects: bool,
609
610    /// Whether an access should be tracked in the Simulator's [`observer::AccessObserver`].
611    /// 
612    /// This typically should be disabled if some sort of system (e.g., autograder or program setup)
613    /// is reading/writing and enabled during program execution.
614    pub track_access: bool
615}
616impl MemAccessCtx {
617    /// Allows accessing memory and (effectless) IO
618    /// without causing any IO updates or access observer updates.
619    /// 
620    /// Useful for accessing memory + IO states.
621    pub fn omnipotent() -> Self {
622        MemAccessCtx {
623            privileged: true, strict: false, io_effects: false, track_access: false }
624    }
625}
626/// Executes assembled code.
627#[derive(Debug)]
628pub struct Simulator {
629    // ------------------ SIMULATION STATE ------------------
630    // Calling [`Simulator::reset`] resets these values.
631
632    /// The simulator's memory.
633    /// 
634    /// Note that this is held in the heap, as it is too large for the stack.
635    pub mem: MemArray,
636
637    /// The simulator's register file.
638    pub reg_file: RegFile,
639
640    /// The program counter.
641    pub pc: u16,
642
643    /// The processor status register. See [`PSR`] for more details.
644    psr: PSR,
645
646    /// Saved stack pointer (the one currently not in use)
647    saved_sp: Word,
648
649    /// The frame stack.
650    pub frame_stack: FrameStack,
651
652    /// Allocated blocks in object file.
653    /// 
654    /// This field keeps track of "allocated" blocks 
655    /// (memory written to by instructions or directives like .blkw)
656    /// in the current object file.
657    /// 
658    /// Loading and storing uninitialized data in an allocated block
659    /// does not cause strictness errors because we're assuming
660    /// the programmer is using those as data stores.
661    /// 
662    /// This is technically a bit lax, because it lets them write
663    /// into instructions but oops.
664    alloca: Box<[(u16, u16)]>,
665
666    /// The number of instructions successfully run since this `Simulator` was initialized.
667    /// 
668    /// This can be set to 0 to reset the counter.
669    pub instructions_run: u64,
670
671    /// Indicates whether the PC has been incremented in the fetch stage yet.
672    /// 
673    /// This is just for error handling purposes. It's used to compute
674    /// the PC of the instruction that caused an error. See [`Simulator::prefetch_pc`].
675    prefetch: bool,
676
677    /// Indicates the reason why the last execution (via [`Simulator::run_while`] and adjacent)
678    /// had paused.
679    pause_condition: PauseCondition,
680
681    /// Tracks accesses to simulator memory.
682    /// 
683    /// An access is defined as a read from or write to a memory location,
684    /// which includes reading a memory location in order to decode it
685    /// or to obtain an address for indirect accesses.
686    pub observer: observer::AccessObserver,
687
688    /// Indicates whether the OS has been loaded.
689    os_loaded: bool,
690
691    // ------------------ CONFIG/DEBUG STATE ------------------
692    // Calling [`Simulator::reset`] does not reset these values.
693
694    /// Machine control.
695    /// If unset, the program stops.
696    /// 
697    /// This is publicly accessible via a reference through [`Simulator::mcr`].
698    mcr: MCR,
699
700    /// Configuration settings for the simulator.
701    /// 
702    /// These are preserved between resets.
703    /// 
704    /// See [`SimFlags`] for more details on what configuration
705    /// settings are available.
706    pub flags: SimFlags,
707
708    /// Breakpoints for the simulator.
709    pub breakpoints: HashSet<Breakpoint>,
710
711    // Any addresses memory-mapped to internal registers (e.g., PSR, MCR).
712    ireg_mmap: HashMap<u16, InternalRegister>,
713    /// All external devices connected to the system (IO and interrupting devices).
714    pub device_handler: DeviceHandler
715
716}
717impl Simulator where Simulator: Send + Sync {}
718
719impl Simulator {
720    /// Creates a new simulator with the provided initializers
721    /// and with the OS loaded, but without a loaded object file.
722    /// 
723    /// This also allows providing an MCR atomic which is used by the Simulator.
724    fn new_with_mcr(flags: SimFlags, mcr: MCR) -> Self {
725        let mut filler = flags.machine_init.generator();
726
727        let mut sim = Self {
728            mem: MemArray::new(&mut filler),
729            reg_file: RegFile::new(&mut filler),
730            pc: 0x3000,
731            psr: PSR::new(),
732            saved_sp: Word::new_init(0x3000),
733            frame_stack: FrameStack::new(flags.debug_frames),
734            alloca: Box::new([]),
735            instructions_run: 0,
736            prefetch: false,
737            pause_condition: Default::default(),
738            observer: Default::default(),
739            os_loaded: false,
740
741            mcr,
742            flags,
743            breakpoints: Default::default(),
744            ireg_mmap: InternalRegister::default_mmap(),
745            device_handler: Default::default()
746        };
747
748        sim.mem.as_slice_mut()[IO_START as usize..].fill(Word::new_init(0)); // clear IO section
749        sim.load_os();
750        sim
751    }
752
753    /// Creates a new simulator with the provided initializers
754    /// and with the OS loaded, but without a loaded object file.
755    pub fn new(flags: SimFlags) -> Self {
756        Self::new_with_mcr(flags, Arc::default())
757    }
758
759    /// Loads and initializes the operating system.
760    /// 
761    /// Note that this is done automatically with [`Simulator::new`].
762    /// 
763    /// This will initialize kernel space and create trap handlers,
764    /// however it will not load working IO. This can cause IO
765    /// traps such as `GETC` and `PUTC` to hang. The only trap that 
766    /// is assured to function without IO is `HALT`.
767    /// 
768    /// To initialize the IO, use [`Simulator::open_io`].
769    fn load_os(&mut self) {
770        if !self.os_loaded {
771            self.load_obj_file(_os_obj_file())
772                .unwrap_or_else(|_| unreachable!("OS object file should not have externals"));
773            self.os_loaded = true;
774        }
775    }
776
777    /// Resets the simulator.
778    /// 
779    /// This resets the state of the `Simulator` back to before any execution calls,
780    /// while preserving configuration and debug state.
781    /// 
782    /// Note that this function preserves:
783    /// - Flags
784    /// - Breakpoints
785    /// - External interrupts
786    /// - MCR reference (i.e., anything with access to the Simulator's MCR can still control it)
787    /// - IO (however, note that it does not reset IO state, which must be manually reset)
788    /// 
789    /// This also does not reload object files. Any object file data has to be reloaded into the Simulator.
790    pub fn reset(&mut self) {
791        let mcr = Arc::clone(&self.mcr);
792        let flags = self.flags;
793        let breakpoints = std::mem::take(&mut self.breakpoints);
794        let ireg_map = std::mem::take(&mut self.ireg_mmap);
795        let dev_handler = std::mem::take(&mut self.device_handler);
796
797        *self = Simulator::new_with_mcr(flags, mcr);
798        self.breakpoints = breakpoints;
799        self.ireg_mmap = ireg_map;
800        self.device_handler = dev_handler;
801        self.device_handler.io_reset();
802    }
803    
804    /// Fallibly reads the word at the provided index, erroring if not possible.
805    /// 
806    /// This accepts a [`MemAccessCtx`], that describes the parameters of the memory access.
807    /// The simulator provides a default [`MemAccessCtx`] under [`Simulator::default_mem_ctx`].
808    /// 
809    /// The flags are used as follows:
810    /// - `privileged`: if false, this access errors if the address is a memory location outside of the user range.
811    /// - `strict`: not used for `read`
812    /// 
813    /// Note that this method is used for simulating a read to memory-mapped IO. 
814    /// If you would like to query the memory's state, consider using `index` on [`MemArray`].
815    pub fn read_mem(&mut self, addr: u16, ctx: MemAccessCtx) -> Result<Word, SimErr> {
816        if !ctx.privileged && !(USER_START..IO_START).contains(&addr) { return Err(SimErr::AccessViolation) };
817
818        // Apply read to IO and write to mem array:
819        match addr {
820            // Supervisor range
821            0..USER_START => { /* Non-IO read */ },
822            // User range
823            USER_START..IO_START => { /* Non-IO read */ },
824            // IO range
825            IO_START.. => {
826                if let Some(ireg) = self.ireg_mmap.get(&addr) {
827                    let data = ireg.read(self);
828                    self.mem[addr].set(data);
829                } else if let Some(data) = self.device_handler.io_read(addr, ctx.io_effects) {
830                    self.mem[addr].set(data);
831                }
832            }
833        }
834
835        if ctx.track_access {
836            // Update memory observer:
837            self.observer.update_mem_accesses(addr, AccessSet::READ);
838        }
839        // Load from mem array:
840        Ok(self.mem[addr])
841    }
842
843    /// Fallibly writes the word at the provided index, erroring if not possible.
844    /// 
845    /// This accepts a [`MemAccessCtx`], that describes the parameters of the memory access.
846    /// The simulator provides a default [`MemAccessCtx`] under [`Simulator::default_mem_ctx`].
847    /// 
848    /// The flags are used as follows:
849    /// - `privileged`: if false, this access errors if the address is a memory location outside of the user range.
850    /// - `strict`: If true, all accesses that would cause a memory location to be set with uninitialized data causes an error.
851    /// 
852    /// Note that this method is used for simulating a write to memory-mapped IO. 
853    /// If you would like to edit the memory's state, consider using `index_mut` on [`MemArray`].
854    pub fn write_mem(&mut self, addr: u16, data: Word, ctx: MemAccessCtx) -> Result<(), SimErr> {
855        if !ctx.privileged && !(USER_START..IO_START).contains(&addr) { return Err(SimErr::AccessViolation) };
856        
857        // Apply write to IO:
858        let success = match addr {
859            // Supervisor range (non-IO write)
860            0..USER_START => true,
861            // User range (non-IO write)
862            USER_START..IO_START => true,
863            // IO range
864            IO_START.. => {
865                let io_data = data.get_if_init(ctx.strict, SimErr::StrictIOSetUninit)?;
866
867                match self.ireg_mmap.get(&addr) {
868                    Some(ir) => {
869                        ir.write(self, io_data);
870                        true
871                    },
872                    None => self.device_handler.io_write(addr, io_data),
873                }
874            }
875        };
876
877        // Duplicate write in mem array:
878        if success {
879            if ctx.track_access {
880                // Update memory observer:
881                self.observer.update_mem_accesses(addr, AccessSet::WRITTEN);
882                if self.mem[addr] != data {
883                    self.observer.update_mem_accesses(addr, AccessSet::MODIFIED);
884                }
885            }
886
887            self.mem[addr]
888                .set_if_init(data, ctx.strict, SimErr::StrictMemSetUninit)?;
889        }
890
891        Ok(())
892    }
893
894    /// Exposes an internal register to memory-mapped IO.
895    /// 
896    /// The effect of this is that if the assigned IO address is accessed (loaded or stored),
897    /// the internal register is accessed instead.
898    /// 
899    /// Returns an [`MMapInternalErr`] if unsuccessful.
900    /// 
901    /// # Example
902    /// 
903    /// ```
904    /// use lc3_ensemble::sim::Simulator;
905    /// use lc3_ensemble::sim::SimFlags;
906    /// use lc3_ensemble::sim::InternalRegister;
907    /// use lc3_ensemble::sim::mem::Word;
908    /// # use lc3_ensemble::err::SimErr;
909    /// 
910    /// # fn main() -> Result<(), SimErr> {
911    /// let mut sim = Simulator::new(SimFlags { ignore_privilege: true, ..Default::default() });
912    /// sim.mmap_internal(0xFE05, InternalRegister::PC);
913    /// 
914    /// // Read from PC:
915    /// assert_eq!(sim.read_mem(0xFE05, sim.default_mem_ctx())?.get(), 0x3000);
916    /// // Write to PC:
917    /// sim.write_mem(0xFE05, Word::new_init(0x3939), sim.default_mem_ctx())?;
918    /// assert_eq!(sim.pc, 0x3939);
919    /// # Ok(())
920    /// # }
921    /// ```
922    /// 
923    pub fn mmap_internal(&mut self, addr: u16, reg: InternalRegister) -> Result<(), MMapInternalErr> {
924        use std::collections::hash_map::Entry;
925
926        if !(IO_START..).contains(&addr) { return Err(MMapInternalErr::NotInIORange); }
927        // Note: Since `mmap_internal` is disconnected from `device_handler`,
928        // this doesn't properly handle the case when a port is mapped onto `device_handler`
929        // and `mmap_internal` at the same time.
930        //
931        // Solution to this could be merging internal registers + device handler.
932        let Entry::Vacant(e) = self.ireg_mmap.entry(addr) else {
933            return Err(MMapInternalErr::AddrAlreadyMapped);
934        };
935
936        e.insert(reg);
937        Ok(())
938    }
939
940    /// Removes an internal register from memory-mapped IO
941    /// which was previously exposed by [`Simulator::mmap_internal`].
942    /// 
943    /// Note that this only updates internal registers. 
944    /// Any external devices need to be removed with [`DeviceHandler`] instead.
945    /// 
946    /// This returns whether a register was successfully removed.
947    pub fn munmap_internal(&mut self, addr: u16) -> bool {
948        self.ireg_mmap.remove(&addr).is_some()
949    }
950
951    /// Loads an object file into this simulator.
952    pub fn load_obj_file(&mut self, obj: &ObjectFile) -> Result<(), SimErr> {
953        use std::cmp::Ordering;
954
955        // Reject any object files with external symbols.
956        if let Some(ext) = obj.get_external_symbol() {
957            return Err(SimErr::UnresolvedExternal(ext.to_string()));
958        }
959
960        let mut alloca = vec![];
961
962        for (start, words) in obj.block_iter() {
963            self.mem.copy_obj_block(start, words);
964
965            // add this block to alloca
966            let len = words.len() as u16;
967            let end = start.wrapping_add(len);
968
969            match start.cmp(&end) {
970                Ordering::Less    => alloca.push((start, len)),
971                Ordering::Equal   => {},
972                Ordering::Greater => {
973                    // push (start..) and (0..end) as blocks
974                    alloca.push((start, start.wrapping_neg()));
975                    if end != 0 { alloca.push((0, end)) };
976                },
977            }
978        }
979
980        alloca.sort_by_key(|&(start, _)| start);
981        self.alloca = alloca.into_boxed_slice();
982
983        Ok(())
984    }
985
986    /// Sets the condition codes using the provided result.
987    fn set_cc(&mut self, result: u16) {
988        match (result as i16).cmp(&0) {
989            std::cmp::Ordering::Less    => self.psr.set_cc(0b100),
990            std::cmp::Ordering::Equal   => self.psr.set_cc(0b010),
991            std::cmp::Ordering::Greater => self.psr.set_cc(0b001),
992        }
993    }
994
995    /// Gets a reference to the PSR.
996    pub fn psr(&self) -> &PSR {
997        // This is not mutable because editing the PSR can cause crashes to occur if
998        // privilege is tampered with during an interrupt.
999        &self.psr
1000    }
1001
1002    /// Gets a reference to the MCR.
1003    pub fn mcr(&self) -> &MCR {
1004        // The mcr field is not exposed because that allows someone to swap the MCR
1005        // with another AtomicBool, which would cause the simulator's MCR
1006        // to be inconsistent with any other component's 
1007        &self.mcr
1008    }
1009
1010    /// Sets the PC to the given address, raising any errors that occur.
1011    /// 
1012    /// The `st_check_mem` parameter indicates whether the data at the PC should be verified in strict mode.
1013    /// This should be enabled when it is absolutely known that the PC will read from the provided address
1014    /// on the next cycle.
1015    /// 
1016    /// This should be true when this function is used for instructions like `BR` and `JSR` 
1017    /// and should be false when this function is used to increment PC during fetch.
1018    fn set_pc(&mut self, addr_word: Word, st_check_mem: bool) -> Result<(), SimErr> {
1019        let addr = addr_word.get_if_init(self.flags.strict, SimErr::StrictJmpAddrUninit)?;
1020        if self.flags.strict && st_check_mem {
1021            // Check next memory value is initialized:
1022            if !self.read_mem(addr, self.default_mem_ctx())?.is_init() {
1023                return Err(SimErr::StrictPCNextUninit);
1024            }
1025        }
1026        self.pc = addr;
1027        Ok(())
1028    }
1029    /// Adds an offset to the PC.
1030    /// 
1031    /// See [`Simulator::set_pc`] for details about `st_check_mem`.
1032    fn offset_pc(&mut self, offset: i16, st_check_mem: bool) -> Result<(), SimErr> {
1033        self.set_pc(Word::from(self.pc.wrapping_add_signed(offset)), st_check_mem)
1034    }
1035    /// Gets the value of the prefetch PC.
1036    /// 
1037    /// This function is useful as it returns the location of the currently
1038    /// executing instruction in memory.
1039    pub fn prefetch_pc(&self) -> u16 {
1040        self.pc - (!self.prefetch) as u16
1041    }
1042
1043    /// Checks whether the address points to a memory location that was allocated
1044    /// in the currently loaded object file.
1045    fn in_alloca(&self, addr: u16) -> bool {
1046        let first_post = self.alloca.partition_point(|&(start, _)| start <= addr);
1047        if first_post == 0 { return false };
1048        
1049        // This is the last block where start <= addr.
1050        let (start, len) = self.alloca[first_post - 1];
1051
1052        // We must also check that addr < end.
1053        // If start + len is None, that means end is greater than all possible lengths.
1054        match start.checked_add(len) {
1055            Some(e) => addr < e,
1056            None    => true
1057        }
1058    }
1059
1060    /// Indicates whether the last execution of the simulator hit a breakpoint.
1061    pub fn hit_breakpoint(&self) -> bool {
1062        matches!(self.pause_condition, PauseCondition::Breakpoint)
1063    }
1064
1065    /// Indicates whether the last execution of the simulator resulted in a HALT successfully occurring.
1066    /// 
1067    /// This is defined as:
1068    /// - `HALT` being executed while virtual HALTs are enabled
1069    /// - `MCR` being set to `x0000` during the execution of the program.
1070    pub fn hit_halt(&self) -> bool {
1071        matches!(self.pause_condition, PauseCondition::Halt | PauseCondition::MCROff)
1072    }
1073
1074    /// Computes the default memory access context, 
1075    /// which are the default flags to use (see [`Simulator::read_mem`] and [`Simulator::write_mem`]).
1076    pub fn default_mem_ctx(&self) -> MemAccessCtx {
1077        MemAccessCtx {
1078            privileged: self.psr.privileged() || self.flags.ignore_privilege,
1079            strict: self.flags.strict,
1080            io_effects: true,
1081            track_access: true
1082        }
1083    }
1084
1085    /// Calls a subroutine.
1086    /// 
1087    /// This does all the steps for calling a subroutine, namely:
1088    /// - Setting the PC to the subroutine's start address
1089    /// - Setting R7 to the original PC (return address)
1090    /// - Adding information to the frame stack
1091    pub fn call_subroutine(&mut self, addr: u16) -> Result<(), SimErr> {
1092        self.reg_file[R7].set(self.pc);
1093        self.frame_stack.push_frame(self.prefetch_pc(), addr, FrameType::Subroutine, &self.reg_file, &self.mem);
1094        self.set_pc(Word::new_init(addr), true)
1095    }
1096
1097    /// Calls a trap or interrupt, adding information to the frame stack
1098    /// and setting the PC to the start of the trap/interrupt handler.
1099    /// 
1100    /// `0x00-0xFF` represents a trap,
1101    /// `0x100-0x1FF` represents an interrupt.
1102    fn call_interrupt(&mut self, vect: u16, ft: FrameType) -> Result<(), SimErr> {
1103        let addr = self.read_mem(vect, self.default_mem_ctx())?
1104            .get_if_init(self.flags.strict, SimErr::StrictSRAddrUninit)?;
1105
1106        self.frame_stack.push_frame(self.prefetch_pc(), vect, ft, &self.reg_file, &self.mem);
1107        self.set_pc(Word::new_init(addr), true)
1108    }
1109    /// Interrupt, trap, and exception handler.
1110    /// 
1111    /// If priority is none, this will unconditionally initialize the trap or exception handler.
1112    /// If priority is not none, this will run the interrupt handler only if the interrupt's priority
1113    /// is greater than the PSR's priority.
1114    /// 
1115    /// The address provided is the address into the jump table (either the trap or interrupt vector ones).
1116    /// This function will always jump to `mem[vect]` at the end of this function.
1117    fn handle_interrupt(&mut self, vect: u16, priority: Option<u8>) -> Result<(), StepBreak> {
1118        if priority.is_some_and(|prio| prio <= self.psr.priority()) { return Ok(()) };
1119        
1120        // Virtual traps.
1121        // See the flag for documentation.
1122        // Virtual HALT
1123        if !self.flags.use_real_traps {
1124            if let Ok(intv) = RealIntVect::try_from(vect) {
1125                if !self.prefetch {
1126                    // decrement PC so that if play is pressed again, it goes back here
1127                    self.offset_pc(-1, false)?;
1128                    self.prefetch = true;
1129                }
1130                let break_value = match intv {
1131                    RealIntVect::Halt => StepBreak::Halt,
1132                    RealIntVect::PrivilegeViolation => StepBreak::Err(SimErr::PrivilegeViolation),
1133                    RealIntVect::IllegalOpcode => StepBreak::Err(SimErr::IllegalOpcode),
1134                    RealIntVect::AccessViolation => StepBreak::Err(SimErr::AccessViolation),
1135                };
1136                return Err(break_value);
1137            }
1138        };
1139        
1140        if !self.psr.privileged() {
1141            std::mem::swap(&mut self.saved_sp, &mut self.reg_file[R6]);
1142        }
1143
1144        // Push PSR, PC to supervisor stack
1145        let old_psr = self.psr.get();
1146        let old_pc = self.pc;
1147        
1148        self.psr.set_privileged(true);
1149        let mctx = self.default_mem_ctx();
1150
1151        // push PSR and PC to stack
1152        let sp = self.reg_file[R6]
1153            .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
1154
1155        self.reg_file[R6] -= 2u16;
1156        self.write_mem(sp.wrapping_sub(1), Word::new_init(old_psr), mctx)?;
1157        self.write_mem(sp.wrapping_sub(2), Word::new_init(old_pc), mctx)?;
1158        
1159        // set PSR to z
1160        self.psr.set_cc_z();
1161
1162        // set interrupt priority
1163        if let Some(prio) = priority {
1164            self.psr.set_priority(prio);
1165        }
1166
1167        let ft = match priority.is_some() {
1168            true => FrameType::Interrupt,
1169            false => FrameType::Trap,
1170        };
1171        
1172        self.call_interrupt(vect, ft)
1173            .map_err(Into::into)
1174    }
1175
1176    /// Runs until the tripwire condition returns false (or any of the typical breaks occur).
1177    /// 
1178    /// The typical break conditions are:
1179    /// - `HALT` is executed
1180    /// - the MCR is set to false
1181    /// - A breakpoint matches
1182    pub fn run_while(&mut self, mut tripwire: impl FnMut(&mut Simulator) -> bool) -> Result<(), SimErr> {
1183        use std::sync::atomic::Ordering;
1184
1185        self.observer.clear();
1186        std::mem::take(&mut self.pause_condition);
1187        self.mcr.store(true, Ordering::Relaxed);
1188
1189        // event loop
1190        // run until:
1191        // 1. the MCR is set to false
1192        // 2. the tripwire condition returns false
1193        // 3. any of the breakpoints are hit
1194        let result = loop {
1195            // MCR turned off:
1196            if !self.mcr.load(Ordering::Relaxed) {
1197                break Ok(PauseCondition::MCROff);
1198            }
1199            // Tripwire turned off:
1200            if !tripwire(self) {
1201                break Ok(PauseCondition::Tripwire);
1202            }
1203            
1204            // Run a step:
1205            match self.step() {
1206                Ok(_) => {},
1207                Err(StepBreak::Halt) => break Ok(PauseCondition::Halt),
1208                Err(StepBreak::Err(e)) => break Err(e)
1209            }
1210
1211            // After executing, check that any breakpoints were hit.
1212            if self.breakpoints.iter().any(|bp| bp.check(self)) {
1213                break Ok(PauseCondition::Breakpoint);
1214            }
1215        };
1216    
1217        self.mcr.store(false, Ordering::Relaxed);
1218        self.pause_condition = result?;
1219        Ok(())
1220    }
1221
1222    /// Execute the program.
1223    /// 
1224    /// This blocks until the program ends. 
1225    /// If you would like to limit the maximum number of steps to execute, consider [`Simulator::run_with_limit`].
1226    pub fn run(&mut self) -> Result<(), SimErr> {
1227        self.run_while(|_| true)
1228    }
1229
1230    /// Execute the program with a limit on how many steps to execute.
1231    /// 
1232    /// This blocks until the program ends or until the number of steps to execute has been hit.
1233    pub fn run_with_limit(&mut self, max_steps: u64) -> Result<(), SimErr> {
1234        let i = self.instructions_run;
1235        self.run_while(|sim| sim.instructions_run.wrapping_sub(i) < max_steps)
1236    }
1237    
1238    /// Simulate one step, executing one instruction.
1239    /// 
1240    /// Unlike [`Simulator::step`], this function does not handle the `use_real_traps` flag.
1241    /// Both of these functions are not meant for general stepping use. That should be done
1242    /// with [`Simulator::step_in`].
1243    fn _step_inner(&mut self) -> Result<(), StepBreak> {
1244        self.prefetch = true;
1245
1246        if let Some(int) = self.device_handler.poll_interrupt() {
1247            match int.kind {
1248                // If priority passes, handle interrupt then skip FETCH:
1249                device::InterruptKind::Vectored { vect, priority } if priority > self.psr().priority() => {
1250                    return self.handle_interrupt(0x100 + u16::from(vect), Some(priority));
1251                },
1252                // If priority does not pass, move to FETCH:
1253                device::InterruptKind::Vectored { .. } => Ok(()),
1254
1255                // External interrupt.
1256                device::InterruptKind::External(int) => Err(StepBreak::Err(SimErr::Interrupt(int))),
1257            }?;
1258        }
1259
1260        let word = self.read_mem(self.pc, self.default_mem_ctx())?
1261            .get_if_init(self.flags.strict, SimErr::StrictPCCurrUninit)?;
1262
1263        let instr = SimInstr::decode(word)?;
1264
1265        self.offset_pc(1, false)?;
1266        self.prefetch = false;
1267
1268        match instr {
1269            SimInstr::BR(cc, off)  => {
1270                if cc & self.psr.cc() != 0 {
1271                    self.offset_pc(off.get(), true)?;
1272                }
1273            },
1274            SimInstr::ADD(dr, sr1, sr2) => {
1275                let val1 = self.reg_file[sr1];
1276                let val2 = match sr2 {
1277                    ImmOrReg::Imm(i2) => Word::from(i2.get()),
1278                    ImmOrReg::Reg(r2) => self.reg_file[r2],
1279                };
1280
1281                let result = val1 + val2;
1282                self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
1283                self.set_cc(result.get());
1284            },
1285            SimInstr::LD(dr, off) => {
1286                let ea = self.pc.wrapping_add_signed(off.get());
1287                let write_strict = self.flags.strict && !self.in_alloca(ea);
1288
1289                let val = self.read_mem(ea, self.default_mem_ctx())?;
1290                self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
1291                self.set_cc(val.get());
1292            },
1293            SimInstr::ST(sr, off) => {
1294                let ea = self.pc.wrapping_add_signed(off.get());
1295                let write_ctx = MemAccessCtx {
1296                    strict: self.flags.strict && !self.in_alloca(ea),
1297                    ..self.default_mem_ctx()
1298                };
1299
1300                let val = self.reg_file[sr];
1301                self.write_mem(ea, val, write_ctx)?;
1302            },
1303            SimInstr::JSR(op) => {
1304                // Note: JSRR R7 jumps to address at R7, then sets PC to R7.
1305                // Refer to: https://github.com/gt-cs2110/lc3tools/commit/fa9a23f62106eeee9fef7d2a278ba989356c9ee2
1306
1307                let addr = match op {
1308                    ImmOrReg::Imm(off) => Word::from(self.pc.wrapping_add_signed(off.get())),
1309                    ImmOrReg::Reg(br)  => self.reg_file[br],
1310                }.get_if_init(self.flags.strict, SimErr::StrictSRAddrUninit)?;
1311
1312                self.call_subroutine(addr)?;
1313            },
1314            SimInstr::AND(dr, sr1, sr2) => {
1315                let val1 = self.reg_file[sr1];
1316                let val2 = match sr2 {
1317                    ImmOrReg::Imm(i2) => Word::from(i2.get()),
1318                    ImmOrReg::Reg(r2) => self.reg_file[r2],
1319                };
1320
1321                let result = val1 & val2;
1322                self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
1323                self.set_cc(result.get());
1324            },
1325            SimInstr::LDR(dr, br, off) => {
1326                let ea = self.reg_file[br]
1327                    .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?
1328                    .wrapping_add_signed(off.get());
1329                let write_strict = self.flags.strict && br != R6 && !self.in_alloca(ea);
1330                
1331                let val = self.read_mem(ea, self.default_mem_ctx())?;
1332                self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
1333                self.set_cc(val.get());
1334            },
1335            SimInstr::STR(sr, br, off) => {
1336                let ea = self.reg_file[br]
1337                    .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?
1338                    .wrapping_add_signed(off.get());
1339                let write_ctx = MemAccessCtx {
1340                    strict: self.flags.strict && br != R6 && !self.in_alloca(ea),
1341                    ..self.default_mem_ctx()
1342                };
1343                
1344                let val = self.reg_file[sr];
1345                self.write_mem(ea, val, write_ctx)?;
1346            },
1347            SimInstr::RTI => {
1348                if self.psr.privileged() || self.flags.ignore_privilege {
1349                    let mctx = self.default_mem_ctx();
1350
1351                    // Pop PC and PSR from the stack
1352                    let sp = self.reg_file[R6]
1353                        .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
1354
1355                    let pc = self.read_mem(sp, mctx)?
1356                        .get_if_init(self.flags.strict, SimErr::StrictJmpAddrUninit)?;
1357                    let psr = self.read_mem(sp.wrapping_add(1), mctx)?
1358                        .get_if_init(self.flags.strict, SimErr::StrictPSRSetUninit)?;
1359                    self.reg_file[R6] += 2u16;
1360
1361                    self.set_pc(Word::new_init(pc), true)?;
1362                    self.psr = PSR(psr);
1363
1364                    if !self.psr.privileged() {
1365                        std::mem::swap(&mut self.saved_sp, &mut self.reg_file[R6]);
1366                    }
1367
1368                    self.frame_stack.pop_frame();
1369                } else {
1370                    return Err(SimErr::PrivilegeViolation.into());
1371                }
1372            },
1373            SimInstr::NOT(dr, sr) => {
1374                let val = self.reg_file[sr];
1375                
1376                let result = !val;
1377                self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
1378                self.set_cc(result.get());
1379            },
1380            SimInstr::LDI(dr, off) => {
1381                let shifted_pc = self.pc.wrapping_add_signed(off.get());
1382                let ea = self.read_mem(shifted_pc, self.default_mem_ctx())?
1383                    .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
1384                let write_strict = self.flags.strict && !self.in_alloca(ea);
1385
1386                let val = self.read_mem(ea, self.default_mem_ctx())?;
1387                self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
1388                self.set_cc(val.get());
1389            },
1390            SimInstr::STI(sr, off) => {
1391                let shifted_pc = self.pc.wrapping_add_signed(off.get());
1392                let ea = self.read_mem(shifted_pc, self.default_mem_ctx())?
1393                    .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
1394                let write_ctx = MemAccessCtx {
1395                    strict: self.flags.strict && !self.in_alloca(ea),
1396                    ..self.default_mem_ctx()
1397                };
1398
1399                let val = self.reg_file[sr];
1400                self.write_mem(ea, val, write_ctx)?;
1401            },
1402            SimInstr::JMP(br) => {
1403                let addr = self.reg_file[br];
1404                self.set_pc(addr, true)?;
1405                
1406                // if this is RET,
1407                // we must also handle frame information:
1408                if br.reg_no() == 7 {
1409                    self.frame_stack.pop_frame();
1410                }
1411            },
1412            SimInstr::LEA(dr, off) => {
1413                let ea = self.pc.wrapping_add_signed(off.get());
1414                self.reg_file[dr].set(ea);
1415            },
1416            SimInstr::TRAP(vect) => {
1417                self.handle_interrupt(vect.get(), None)?;
1418            },
1419        }
1420
1421        self.instructions_run = self.instructions_run.wrapping_add(1);
1422        Ok(())
1423    }
1424
1425    /// Simulate one step, executing one instruction.
1426    ///
1427    /// This function properly handles the `use_real_traps` flag.
1428    /// 
1429    /// This function is a library function and should be used when one step is needed.
1430    /// The difference between this function and [`Simulator::step_in`] is that this
1431    /// function can return [`StepBreak::Halt`] as an error,
1432    /// whereas `step_in` will ignore that error.
1433    fn step(&mut self) -> Result<(), StepBreak> {
1434        match self._step_inner() {
1435            // Virtual traps don't need to go through handle_interrupt logic
1436            s if !self.flags.use_real_traps => s,
1437            // Real traps!
1438            Err(StepBreak::Halt) => self.handle_interrupt(RealIntVect::Halt as u16, None),
1439            Err(StepBreak::Err(SimErr::PrivilegeViolation)) => self.handle_interrupt(RealIntVect::PrivilegeViolation as u16, None),
1440            Err(StepBreak::Err(SimErr::IllegalOpcode)) => self.handle_interrupt(RealIntVect::IllegalOpcode as u16, None),
1441            Err(StepBreak::Err(SimErr::InvalidInstrFormat)) => self.handle_interrupt(RealIntVect::IllegalOpcode as u16, None),
1442            Err(StepBreak::Err(SimErr::AccessViolation)) => self.handle_interrupt(RealIntVect::AccessViolation as u16, None),
1443            s => s
1444        }
1445    }
1446    /// Simulate one step, executing one instruction.
1447    pub fn step_in(&mut self) -> Result<(), SimErr> {
1448        self.observer.clear();
1449        match self.step() {
1450            Ok(()) => Ok(()),
1451            Err(StepBreak::Halt) => Ok(()),
1452            Err(StepBreak::Err(e)) => Err(e)
1453        }
1454    }
1455
1456    /// Simulate one step, executing one instruction and running through entire subroutines as a single step.
1457    pub fn step_over(&mut self) -> Result<(), SimErr> {
1458        let curr_frame = self.frame_stack.len();
1459        let mut first = Some(()); // is Some if this is the first instruction executed in this call
1460
1461        // this function should do at least one step before checking its condition
1462        // condition: run until we have landed back in the same frame
1463        self.run_while(|sim| first.take().is_some() || curr_frame < sim.frame_stack.len())
1464    }
1465
1466    /// Run through the simulator's execution until the subroutine is exited.
1467    pub fn step_out(&mut self) -> Result<(), SimErr> {
1468        let curr_frame = self.frame_stack.len();
1469        let mut first = Some(()); // is Some if this is the first instruction executed in this call
1470        
1471        // this function should do at least one step before checking its condition
1472        // condition: run until we've landed in a smaller frame
1473        if curr_frame != 0 {
1474            self.run_while(|sim| first.take().is_some() || curr_frame <= sim.frame_stack.len())?;
1475        }
1476
1477        Ok(())
1478    }
1479}
1480impl Default for Simulator {
1481    fn default() -> Self {
1482        Self::new(Default::default())
1483    }
1484}
1485
1486/// A wrapper over `u16` in order to faciliate the PSR.
1487/// 
1488/// The word is encoded as the following:
1489/// - `PSR[15..16]`: Privilege mode (0 = supervisor, 1 = user)
1490/// - `PSR[8..11]`:  Interrupt priority
1491/// - `PSR[0..3]`:   Condition codes
1492/// 
1493/// ```text
1494///         privilege
1495///         |     interrupt priority
1496///         |     |         condition codes
1497///         |     |         |
1498///         V     V         V
1499/// 0x8002: 1000 0000 0000 0010
1500///         ~     ~~~       ~~~
1501/// ```
1502/// 
1503/// Each of these are exposed as the [`PSR::privileged`], [`PSR::priority`], and [`PSR::cc`] values.
1504#[allow(clippy::upper_case_acronyms)]
1505#[repr(transparent)]
1506pub struct PSR(u16);
1507
1508impl PSR {
1509    /// Creates a PSR with a default value (user mode, `z` condition code).
1510    pub fn new() -> Self {
1511        PSR(0x8002)
1512    }
1513
1514    /// Checks whether the simulator is in privileged mode.
1515    /// - `true` = supervisor mode
1516    /// - `false` = user mode
1517    pub fn privileged(&self) -> bool {
1518        (self.0 >> 15) == 0
1519    }
1520    /// Checks the current interrupt priority of the simulator.
1521    pub fn priority(&self) -> u8 {
1522        ((self.0 >> 8) & 0b111) as u8
1523    }
1524    /// Checks the condition code of the simulator.
1525    pub fn cc(&self) -> u8 {
1526        (self.0 & 0b111) as u8
1527    }
1528    /// Checks the condition code of the simulator is `n`.
1529    pub fn is_n(&self) -> bool {
1530        self.cc() & 0b100 != 0
1531    }
1532    /// Checks the condition code of the simulator is `z`.
1533    pub fn is_z(&self) -> bool {
1534        self.cc() & 0b010 != 0
1535    }
1536    /// Checks the condition code of the simulator is `p`.
1537    pub fn is_p(&self) -> bool {
1538        self.cc() & 0b001 != 0
1539    }
1540
1541    /// Gets the bit-representation of the PSR.
1542    pub fn get(&self) -> u16 {
1543        self.0
1544    }
1545    /// Sets the PSR to the provided data value.
1546    pub fn set(&mut self, data: u16) {
1547        const MASK: u16 = 0b1000_0111_0000_0111;
1548        
1549        self.0 = data & MASK;
1550        self.set_cc((data & 0b111) as u8);
1551    }
1552    /// Sets whether the simulator is in privileged mode.
1553    pub fn set_privileged(&mut self, privl: bool) {
1554        self.0 &= 0x7FFF;
1555        self.0 |= u16::from(!privl) << 15;
1556    }
1557    /// Sets the current interrupt priority of the simulator.
1558    pub fn set_priority(&mut self, prio: u8) {
1559        self.0 &= 0xF8FF;
1560        self.0 |= u16::from(prio & 0b111) << 8;
1561    }
1562    /// Sets the condition code of the simulator.
1563    pub fn set_cc(&mut self, mut cc: u8) {
1564        self.0 &= 0xFFF8;
1565
1566        // Guard from invalid CC.
1567        cc &= 0b111;
1568        if cc.count_ones() != 1 { cc = 0b010 };
1569        self.0 |= u16::from(cc);
1570    }
1571    /// Sets the condition code of the simulator to `n`.
1572    pub fn set_cc_n(&mut self) {
1573        self.set_cc(0b100)
1574    }
1575    /// Sets the condition code of the simulator to `z`.
1576    pub fn set_cc_z(&mut self) {
1577        self.set_cc(0b010)
1578    }
1579    /// Sets the condition code of the simulator to `p`.
1580    pub fn set_cc_p(&mut self) {
1581        self.set_cc(0b001)
1582    }
1583}
1584impl Default for PSR {
1585    fn default() -> Self {
1586        Self::new()
1587    }
1588}
1589impl std::fmt::Debug for PSR {
1590    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1591        use std::fmt::Write;
1592        struct CC(u8);
1593
1594        impl std::fmt::Debug for CC {
1595            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1596                if self.0 & 0b100 != 0 { f.write_char('N')?; };
1597                if self.0 & 0b010 != 0 { f.write_char('Z')?; };
1598                if self.0 & 0b001 != 0 { f.write_char('P')?; };
1599                Ok(())
1600            }
1601        }
1602
1603        f.debug_struct("PSR")
1604            .field("privileged", &self.privileged())
1605            .field("priority", &self.priority())
1606            .field("cc", &CC(self.cc()))
1607            .finish()
1608    }
1609}
1610
1611/// A type alias for MCR.
1612pub type MCR = Arc<AtomicBool>;