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;
283mod observer;
284
285use std::collections::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};
295
296use self::frame::{FrameStack, FrameType};
297use self::mem::{MemArray, RegFile, Word, MachineInitStrategy};
298
299/// Errors that can occur during simulation.
300#[derive(Debug)]
301pub enum SimErr {
302    /// Word was decoded, but the opcode was invalid.
303    IllegalOpcode,
304    /// Word was decoded, and the opcode is recognized,
305    /// but the instruction's format is invalid.
306    InvalidInstrFormat,
307    /// A privileged instruction was called in user mode.
308    PrivilegeViolation,
309    /// A supervisor region was accessed in user mode.
310    AccessViolation,
311    /// Object file contained unresolved external symbols.
312    UnresolvedExternal(String),
313    /// Interrupt raised.
314    Interrupt(ExternalInterrupt),
315    /// A register was loaded with a partially uninitialized value.
316    /// 
317    /// This will ignore loads from the stack (R6), because it is convention to push registers 
318    /// (including uninitialized registers).
319    /// This also ignores loads from allocated (`.blkw`) memory in case the program writer
320    /// uses those as register stores.
321    // IDEA: So currently, the way this is implemented is that LDR Rx, R6, OFF is accepted regardless of initialization.
322    // We could make this stricter by keeping track of how much is allocated on the stack.
323    StrictRegSetUninit,
324    /// Memory was loaded with a partially uninitialized value.
325    /// 
326    /// This will ignore loads from the stack (R6), because it is convention to push registers 
327    /// (including uninitialized registers).
328    /// This also ignores loads from allocated (`.blkw`) memory in case the program writer
329    /// uses those as register stores.
330    // IDEA: See StrictRegSetUninit.
331    StrictMemSetUninit,
332    /// Data was stored into MMIO with a partially uninitialized value.
333    StrictIOSetUninit,
334    /// Address to jump to is coming from an uninitialized value.
335    StrictJmpAddrUninit,
336    /// Address to jump to (which is a subroutine or trap call) is coming from an uninitialized value.
337    StrictSRAddrUninit,
338    /// Address to read from memory is coming from an uninitialized value.
339    StrictMemAddrUninit,
340    /// PC is pointing to an uninitialized value.
341    StrictPCCurrUninit,
342    /// PC was set to an address that has an uninitialized value and will read from it next cycle.
343    StrictPCNextUninit,
344    /// The PSR was loaded with a partially uninitialized value (by RTI).
345    StrictPSRSetUninit,
346}
347impl std::fmt::Display for SimErr {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        match self {
350            SimErr::IllegalOpcode         => f.write_str("simulator executed illegal opcode"),
351            SimErr::InvalidInstrFormat    => f.write_str("simulator executed invalid instruction"),
352            SimErr::PrivilegeViolation    => f.write_str("privilege violation"),
353            SimErr::AccessViolation       => f.write_str("access violation"),
354            SimErr::UnresolvedExternal(s) => write!(f, "unresolved external label {s} in object file"),
355            SimErr::Interrupt(e)          => write!(f, "unhandled interrupt: {e}"),
356            SimErr::StrictRegSetUninit    => f.write_str("register was set to uninitialized value (strict mode)"),
357            SimErr::StrictMemSetUninit    => f.write_str("tried to write an uninitialized value to memory (strict mode)"),
358            SimErr::StrictIOSetUninit     => f.write_str("tried to write an uninitialized value to memory-mapped IO (strict mode)"),
359            SimErr::StrictJmpAddrUninit   => f.write_str("PC address was set to uninitialized address (strict mode)"),
360            SimErr::StrictSRAddrUninit    => f.write_str("Subroutine starts at uninitialized address (strict mode)"),
361            SimErr::StrictMemAddrUninit   => f.write_str("tried to access memory with an uninitialized address (strict mode)"),
362            SimErr::StrictPCCurrUninit    => f.write_str("PC is pointing to uninitialized value (strict mode)"),
363            SimErr::StrictPCNextUninit    => f.write_str("PC will point to uninitialized value when this instruction executes (strict mode)"),
364            SimErr::StrictPSRSetUninit    => f.write_str("tried to set the PSR to an uninitialized value (strict mode)"),
365        }
366    }
367}
368impl std::error::Error for SimErr {}
369
370/// Anything that can cause a step to abruptly fail to finish.
371enum StepBreak {
372    /// A virtual halt was executed.
373    Halt,
374    /// A simulation error occurred.
375    Err(SimErr),
376}
377impl From<SimErr> for StepBreak {
378    fn from(value: SimErr) -> Self {
379        Self::Err(value)
380    }
381}
382
383macro_rules! int_vect {
384    ($Type:ident, {$($name:ident = $value:literal), +}) => {
385        enum $Type {
386            $($name = $value),+
387        }
388        impl TryFrom<u16> for $Type {
389            type Error = ();
390
391            fn try_from(value: u16) -> Result<Self, Self::Error> {
392                match value {
393                    $($value => Ok(Self::$name)),+,
394                    _ => Err(())
395                }
396            }
397        }
398    }
399}
400int_vect!(RealIntVect, {
401    Halt = 0x25,
402    PrivilegeViolation = 0x100,
403    IllegalOpcode = 0x101,
404    AccessViolation = 0x102
405});
406
407
408/// The OS object file with symbols.
409/// 
410/// Do not rely on this existing.
411#[doc(hidden)]
412#[allow(non_snake_case)]
413pub fn _os_obj_file() -> &'static ObjectFile {
414    // This is public because LC3Tools UI needs it;
415    // however, I don't think there's any particular other reason
416    // that a developer would need this, so it's #[doc(hidden)].
417    use crate::parse::parse_ast;
418    use crate::asm::assemble_debug;
419    use std::sync::OnceLock;
420
421    static OS_OBJ_FILE: OnceLock<ObjectFile> = OnceLock::new();
422    
423    OS_OBJ_FILE.get_or_init(|| {
424        let os_file = include_str!("os.asm");
425        let ast = parse_ast(os_file).unwrap();
426        assemble_debug(ast, os_file).unwrap()
427    })
428}
429
430/// Reason for why execution paused if it wasn't due to an error.
431#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
432enum PauseCondition {
433    /// Program reached a halt.
434    Halt,
435    /// Program set MCR to off.
436    MCROff,
437    /// Program hit a breakpoint.
438    Breakpoint,
439    /// Program hit a tripwire condition.
440    Tripwire,
441    /// Program hit an error and did not pause successfully.
442    #[default]
443    Unsuccessful
444}
445
446/// Configuration flags for [`Simulator`].
447/// 
448/// These can be modified after the `Simulator` is created with [`Simulator::new`]
449/// and their effects should still apply.
450/// 
451/// Read the field descriptions for more details.
452#[derive(Debug, PartialEq, Eq, Clone, Copy)]
453pub struct SimFlags {
454    /// Whether strict mode is enabled.
455    /// 
456    /// Strict mode adds additional integrity checks to the simulator,
457    /// such as verifying initialization state is normal for provided data.
458    /// 
459    /// By default, this flag is `false`.
460    pub strict: bool,
461
462    /// Whether to use emulated version of certain traps.
463    /// 
464    /// Certain traps and exceptions have two separate implementations within `Simulator`, namely:
465    /// - `HALT` or `TRAP x25`
466    /// - Privilege mode exception
467    /// - Illegal opcode exception
468    /// - Access violation exception
469    /// 
470    /// This flag allows us to configure between the two implementations:
471    /// - **virtual** (`false`): On execution of one of these interrupts, the simulator breaks
472    ///     and prints its own error.
473    /// - **real** (`true`): On execution of one of these interrupts, the simulator delegates
474    ///     the error to the machine's OS and continues through the OS.
475    /// 
476    /// Activating real traps is useful for maintaining integrity to the LC-3 ISA, whereas
477    /// virtual HALT preserves the state of the machine prior to calling the interrupt routines
478    /// and can provide slightly more helpful error messages.
479    /// 
480    /// By default, this flag is `false`.
481    pub use_real_traps: bool,
482    
483    /// The creation strategy for uninitialized Words.
484    /// 
485    /// This is used to initialize the `mem` and `reg_file` fields.
486    /// 
487    /// By default, this flag is [`MachineInitStrategy::default`].
488    pub machine_init: MachineInitStrategy,
489
490    /// Whether to store debugging information about call frames.
491    /// 
492    /// This flag only goes into effect after a `Simulator::new` or `Simulator::reset` call.
493    /// 
494    /// By default, this flag is `false`.
495    pub debug_frames: bool,
496
497    /// If true, privilege checks are ignored and the simulator runs as though
498    /// the executor has supervisor level privilege.
499    /// 
500    /// By default, this flag is `false`.
501    pub ignore_privilege: bool
502}
503
504#[allow(clippy::derivable_impls)]
505impl Default for SimFlags {
506    fn default() -> Self {
507        Self {
508            strict: false,
509            use_real_traps: false,
510            machine_init: Default::default(),
511            debug_frames: false,
512            ignore_privilege: false
513        }
514    }
515}
516
517const USER_START: u16 = 0x3000;
518const IO_START: u16 = 0xFE00;
519const PSR_ADDR: u16 = 0xFFFC;
520const MCR_ADDR: u16 = 0xFFFE;
521
522/// Context behind a memory access.
523/// 
524/// This struct is used by [`Simulator::read_mem`] and [`Simulator::write_mem`] to perform checks against memory accesses.
525/// A default memory access context for the given simulator can be constructed with [`Simulator::default_mem_ctx`].
526#[derive(Clone, Copy, Debug)]
527pub struct MemAccessCtx {
528    /// Whether this access is privileged (false = user, true = supervisor).
529    pub privileged: bool,
530
531    /// Whether writes to memory should follow strict rules 
532    /// (no writing partially or fully uninitialized data).
533    /// 
534    /// This does not affect [`Simulator::read_mem`].
535    pub strict: bool,
536
537    /// Whether a read to memory-mapped IO should cause side effects.
538    /// 
539    /// This can be set to false to observe the value of IO.
540    /// 
541    /// This does not affect [`Simulator::write_mem`].
542    pub io_effects: bool
543}
544impl MemAccessCtx {
545    /// Allows any access and allows access to (effectless) IO.
546    /// 
547    /// Useful for reading state.
548    pub fn omnipotent() -> Self {
549        MemAccessCtx { privileged: true, strict: false, io_effects: false }
550    }
551}
552/// Executes assembled code.
553#[derive(Debug)]
554pub struct Simulator {
555    // ------------------ SIMULATION STATE ------------------
556    // Calling [`Simulator::reset`] resets these values.
557
558    /// The simulator's memory.
559    /// 
560    /// Note that this is held in the heap, as it is too large for the stack.
561    pub mem: MemArray,
562
563    /// The simulator's register file.
564    pub reg_file: RegFile,
565
566    /// The program counter.
567    pub pc: u16,
568
569    /// The processor status register. See [`PSR`] for more details.
570    psr: PSR,
571
572    /// Saved stack pointer (the one currently not in use)
573    saved_sp: Word,
574
575    /// The frame stack.
576    pub frame_stack: FrameStack,
577
578    /// Allocated blocks in object file.
579    /// 
580    /// This field keeps track of "allocated" blocks 
581    /// (memory written to by instructions or directives like .blkw)
582    /// in the current object file.
583    /// 
584    /// Loading and storing uninitialized data in an allocated block
585    /// does not cause strictness errors because we're assuming
586    /// the programmer is using those as data stores.
587    /// 
588    /// This is technically a bit lax, because it lets them write
589    /// into instructions but oops.
590    alloca: Box<[(u16, u16)]>,
591
592    /// The number of instructions successfully run since this `Simulator` was initialized.
593    /// 
594    /// This can be set to 0 to reset the counter.
595    pub instructions_run: u64,
596
597    /// Indicates whether the PC has been incremented in the fetch stage yet.
598    /// 
599    /// This is just for error handling purposes. It's used to compute
600    /// the PC of the instruction that caused an error. See [`Simulator::prefetch_pc`].
601    prefetch: bool,
602
603    /// Indicates the reason why the last execution (via [`Simulator::run_while`] and adjacent)
604    /// had paused.
605    pause_condition: PauseCondition,
606
607    /// Tracks changes in simulator state.
608    pub observer: observer::ChangeObserver,
609
610    /// Indicates whether the OS has been loaded.
611    os_loaded: bool,
612
613    // ------------------ CONFIG/DEBUG STATE ------------------
614    // Calling [`Simulator::reset`] does not reset these values.
615
616    /// Machine control.
617    /// If unset, the program stops.
618    /// 
619    /// This is publicly accessible via a reference through [`Simulator::mcr`].
620    mcr: MCR,
621
622    /// Configuration settings for the simulator.
623    /// 
624    /// These are preserved between resets.
625    /// 
626    /// See [`SimFlags`] for more details on what configuration
627    /// settings are available.
628    pub flags: SimFlags,
629
630    /// Breakpoints for the simulator.
631    pub breakpoints: HashSet<Breakpoint>,
632
633    /// All external devices connected to the system (IO and interrupting devices).
634    pub device_handler: DeviceHandler
635
636}
637impl Simulator where Simulator: Send + Sync {}
638
639impl Simulator {
640    /// Creates a new simulator with the provided initializers
641    /// and with the OS loaded, but without a loaded object file.
642    /// 
643    /// This also allows providing an MCR atomic which is used by the Simulator.
644    fn new_with_mcr(flags: SimFlags, mcr: MCR) -> Self {
645        let mut filler = flags.machine_init.generator();
646
647        let mut sim = Self {
648            mem: MemArray::new(&mut filler),
649            reg_file: RegFile::new(&mut filler),
650            pc: 0x3000,
651            psr: PSR::new(),
652            saved_sp: Word::new_init(0x3000),
653            frame_stack: FrameStack::new(flags.debug_frames),
654            alloca: Box::new([]),
655            instructions_run: 0,
656            prefetch: false,
657            pause_condition: Default::default(),
658            observer: Default::default(),
659            os_loaded: false,
660
661            mcr,
662            flags,
663            breakpoints: Default::default(),
664            device_handler: Default::default()
665        };
666
667        sim.mem.as_slice_mut()[IO_START as usize..].fill(Word::new_init(0)); // clear IO section
668        sim.load_os();
669        sim
670    }
671
672    /// Creates a new simulator with the provided initializers
673    /// and with the OS loaded, but without a loaded object file.
674    pub fn new(flags: SimFlags) -> Self {
675        Self::new_with_mcr(flags, Arc::default())
676    }
677
678    /// Loads and initializes the operating system.
679    /// 
680    /// Note that this is done automatically with [`Simulator::new`].
681    /// 
682    /// This will initialize kernel space and create trap handlers,
683    /// however it will not load working IO. This can cause IO
684    /// traps such as `GETC` and `PUTC` to hang. The only trap that 
685    /// is assured to function without IO is `HALT`.
686    /// 
687    /// To initialize the IO, use [`Simulator::open_io`].
688    fn load_os(&mut self) {
689        if !self.os_loaded {
690            self.load_obj_file(_os_obj_file())
691                .unwrap_or_else(|_| unreachable!("OS object file should not have externals"));
692            self.os_loaded = true;
693        }
694    }
695
696    /// Resets the simulator.
697    /// 
698    /// This resets the state of the `Simulator` back to before any execution calls,
699    /// while preserving configuration and debug state.
700    /// 
701    /// Note that this function preserves:
702    /// - Flags
703    /// - Breakpoints
704    /// - External interrupts
705    /// - MCR reference (i.e., anything with access to the Simulator's MCR can still control it)
706    /// - IO (however, note that it does not reset IO state, which must be manually reset)
707    /// 
708    /// This also does not reload object files. Any object file data has to be reloaded into the Simulator.
709    pub fn reset(&mut self) {
710        let mcr = Arc::clone(&self.mcr);
711        let flags = self.flags;
712        let breakpoints = std::mem::take(&mut self.breakpoints);
713        let dev_handler = std::mem::take(&mut self.device_handler);
714
715        *self = Simulator::new_with_mcr(flags, mcr);
716        self.breakpoints = breakpoints;
717        self.device_handler = dev_handler;
718        self.device_handler.io_reset();
719    }
720    
721    /// Fallibly reads the word at the provided index, erroring if not possible.
722    /// 
723    /// This accepts a [`MemAccessCtx`], that describes the parameters of the memory access.
724    /// The simulator provides a default [`MemAccessCtx`] under [`Simulator::default_mem_ctx`].
725    /// 
726    /// The flags are used as follows:
727    /// - `privileged`: if false, this access errors if the address is a memory location outside of the user range.
728    /// - `strict`: not used for `read`
729    /// 
730    /// Note that this method is used for simulating a read to memory-mapped IO. 
731    /// If you would like to query the memory's state, consider using `index` on [`MemArray`].
732    pub fn read_mem(&mut self, addr: u16, ctx: MemAccessCtx) -> Result<Word, SimErr> {
733        use std::sync::atomic::Ordering;
734
735        if !ctx.privileged && !(USER_START..IO_START).contains(&addr) { return Err(SimErr::AccessViolation) };
736
737        // Apply read to IO and write to mem array:
738        match addr {
739            // Supervisor range
740            0..USER_START => { /* Non-IO read */ },
741            // User range
742            USER_START..IO_START => { /* Non-IO read */ },
743            // IO range
744            PSR_ADDR => self.mem[addr].set(self.psr.get()),
745            MCR_ADDR => self.mem[addr].set(u16::from(self.mcr.load(Ordering::Relaxed)) << 15),
746            IO_START.. => {
747                if let Some(data) = self.device_handler.io_read(addr, ctx.io_effects) {
748                    self.mem[addr].set(data);
749                }
750            }
751        }
752
753        // Load from mem array:
754        Ok(self.mem[addr])
755    }
756
757    /// Fallibly writes the word at the provided index, erroring if not possible.
758    /// 
759    /// This accepts a [`MemAccessCtx`], that describes the parameters of the memory access.
760    /// The simulator provides a default [`MemAccessCtx`] under [`Simulator::default_mem_ctx`].
761    /// 
762    /// The flags are used as follows:
763    /// - `privileged`: if false, this access errors if the address is a memory location outside of the user range.
764    /// - `strict`: If true, all accesses that would cause a memory location to be set with uninitialized data causes an error.
765    /// 
766    /// Note that this method is used for simulating a write to memory-mapped IO. 
767    /// If you would like to edit the memory's state, consider using `index_mut` on [`MemArray`].
768    pub fn write_mem(&mut self, addr: u16, data: Word, ctx: MemAccessCtx) -> Result<(), SimErr> {
769        use std::sync::atomic::Ordering;
770
771        if !ctx.privileged && !(USER_START..IO_START).contains(&addr) { return Err(SimErr::AccessViolation) };
772        
773        // Apply write to IO:
774        let success = match addr {
775            // Supervisor range (non-IO write)
776            0..USER_START => true,
777            // User range (non-IO write)
778            USER_START..IO_START => true,
779            // IO range
780            PSR_ADDR => {
781                let io_data = data.get_if_init(ctx.strict, SimErr::StrictIOSetUninit)?;
782                self.psr.set(io_data);
783                true
784            },
785            MCR_ADDR => {
786                let io_data = data.get_if_init(ctx.strict, SimErr::StrictIOSetUninit)?;
787                self.mcr.store((io_data as i16) < 0, Ordering::Relaxed);
788                true
789            },
790            IO_START.. => {
791                let io_data = data.get_if_init(ctx.strict, SimErr::StrictIOSetUninit)?;
792                self.device_handler.io_write(addr, io_data)
793            }
794        };
795
796        // Duplicate write in mem array:
797        if success {
798            if self.mem[addr] != data {
799                self.observer.set_mem_changed(addr);
800            }
801            self.mem[addr]
802                .set_if_init(data, ctx.strict, SimErr::StrictMemSetUninit)?;
803        }
804
805        Ok(())
806    }
807
808    /// Loads an object file into this simulator.
809    pub fn load_obj_file(&mut self, obj: &ObjectFile) -> Result<(), SimErr> {
810        use std::cmp::Ordering;
811
812        // Reject any object files with external symbols.
813        if let Some(ext) = obj.get_external_symbol() {
814            return Err(SimErr::UnresolvedExternal(ext.to_string()));
815        }
816
817        let mut alloca = vec![];
818
819        for (start, words) in obj.block_iter() {
820            self.mem.copy_obj_block(start, words);
821
822            // add this block to alloca
823            let len = words.len() as u16;
824            let end = start.wrapping_add(len);
825
826            match start.cmp(&end) {
827                Ordering::Less    => alloca.push((start, len)),
828                Ordering::Equal   => {},
829                Ordering::Greater => {
830                    // push (start..) and (0..end) as blocks
831                    alloca.push((start, start.wrapping_neg()));
832                    if end != 0 { alloca.push((0, end)) };
833                },
834            }
835        }
836
837        alloca.sort_by_key(|&(start, _)| start);
838        self.alloca = alloca.into_boxed_slice();
839
840        Ok(())
841    }
842
843    /// Sets the condition codes using the provided result.
844    fn set_cc(&mut self, result: u16) {
845        match (result as i16).cmp(&0) {
846            std::cmp::Ordering::Less    => self.psr.set_cc(0b100),
847            std::cmp::Ordering::Equal   => self.psr.set_cc(0b010),
848            std::cmp::Ordering::Greater => self.psr.set_cc(0b001),
849        }
850    }
851
852    /// Gets a reference to the PSR.
853    pub fn psr(&self) -> &PSR {
854        // This is not mutable because editing the PSR can cause crashes to occur if
855        // privilege is tampered with during an interrupt.
856        &self.psr
857    }
858
859    /// Gets a reference to the MCR.
860    pub fn mcr(&self) -> &MCR {
861        // The mcr field is not exposed because that allows someone to swap the MCR
862        // with another AtomicBool, which would cause the simulator's MCR
863        // to be inconsistent with any other component's 
864        &self.mcr
865    }
866
867    /// Sets the PC to the given address, raising any errors that occur.
868    /// 
869    /// The `st_check_mem` parameter indicates whether the data at the PC should be verified in strict mode.
870    /// This should be enabled when it is absolutely known that the PC will read from the provided address
871    /// on the next cycle.
872    /// 
873    /// This should be true when this function is used for instructions like `BR` and `JSR` 
874    /// and should be false when this function is used to increment PC during fetch.
875    fn set_pc(&mut self, addr_word: Word, st_check_mem: bool) -> Result<(), SimErr> {
876        let addr = addr_word.get_if_init(self.flags.strict, SimErr::StrictJmpAddrUninit)?;
877        if self.flags.strict && st_check_mem {
878            // Check next memory value is initialized:
879            if !self.read_mem(addr, self.default_mem_ctx())?.is_init() {
880                return Err(SimErr::StrictPCNextUninit);
881            }
882        }
883        self.pc = addr;
884        Ok(())
885    }
886    /// Adds an offset to the PC.
887    /// 
888    /// See [`Simulator::set_pc`] for details about `st_check_mem`.
889    fn offset_pc(&mut self, offset: i16, st_check_mem: bool) -> Result<(), SimErr> {
890        self.set_pc(Word::from(self.pc.wrapping_add_signed(offset)), st_check_mem)
891    }
892    /// Gets the value of the prefetch PC.
893    /// 
894    /// This function is useful as it returns the location of the currently
895    /// executing instruction in memory.
896    pub fn prefetch_pc(&self) -> u16 {
897        self.pc - (!self.prefetch) as u16
898    }
899
900    /// Checks whether the address points to a memory location that was allocated
901    /// in the currently loaded object file.
902    fn in_alloca(&self, addr: u16) -> bool {
903        let first_post = self.alloca.partition_point(|&(start, _)| start <= addr);
904        if first_post == 0 { return false };
905        
906        // This is the last block where start <= addr.
907        let (start, len) = self.alloca[first_post - 1];
908
909        // We must also check that addr < end.
910        // If start + len is None, that means end is greater than all possible lengths.
911        match start.checked_add(len) {
912            Some(e) => addr < e,
913            None    => true
914        }
915    }
916
917    /// Indicates whether the last execution of the simulator hit a breakpoint.
918    pub fn hit_breakpoint(&self) -> bool {
919        matches!(self.pause_condition, PauseCondition::Breakpoint)
920    }
921
922    /// Indicates whether the last execution of the simulator resulted in a HALT successfully occurring.
923    /// 
924    /// This is defined as:
925    /// - `HALT` being executed while virtual HALTs are enabled
926    /// - `MCR` being set to `x0000` during the execution of the program.
927    pub fn hit_halt(&self) -> bool {
928        matches!(self.pause_condition, PauseCondition::Halt | PauseCondition::MCROff)
929    }
930
931    /// Computes the default memory access context, 
932    /// which are the default flags to use (see [`Simulator::read_mem`] and [`Simulator::write_mem`]).
933    pub fn default_mem_ctx(&self) -> MemAccessCtx {
934        MemAccessCtx {
935            privileged: self.psr.privileged() || self.flags.ignore_privilege,
936            strict: self.flags.strict,
937            io_effects: true
938        }
939    }
940
941    /// Calls a subroutine.
942    /// 
943    /// This does all the steps for calling a subroutine, namely:
944    /// - Setting the PC to the subroutine's start address
945    /// - Setting R7 to the original PC (return address)
946    /// - Adding information to the frame stack
947    pub fn call_subroutine(&mut self, addr: u16) -> Result<(), SimErr> {
948        self.reg_file[R7].set(self.pc);
949        self.frame_stack.push_frame(self.prefetch_pc(), addr, FrameType::Subroutine, &self.reg_file, &self.mem);
950        self.set_pc(Word::new_init(addr), true)
951    }
952
953    /// Calls a trap or interrupt, adding information to the frame stack
954    /// and setting the PC to the start of the trap/interrupt handler.
955    /// 
956    /// `0x00-0xFF` represents a trap,
957    /// `0x100-0x1FF` represents an interrupt.
958    fn call_interrupt(&mut self, vect: u16, ft: FrameType) -> Result<(), SimErr> {
959        let addr = self.read_mem(vect, self.default_mem_ctx())?
960            .get_if_init(self.flags.strict, SimErr::StrictSRAddrUninit)?;
961
962        self.frame_stack.push_frame(self.prefetch_pc(), vect, ft, &self.reg_file, &self.mem);
963        self.set_pc(Word::new_init(addr), true)
964    }
965    /// Interrupt, trap, and exception handler.
966    /// 
967    /// If priority is none, this will unconditionally initialize the trap or exception handler.
968    /// If priority is not none, this will run the interrupt handler only if the interrupt's priority
969    /// is greater than the PSR's priority.
970    /// 
971    /// The address provided is the address into the jump table (either the trap or interrupt vector ones).
972    /// This function will always jump to `mem[vect]` at the end of this function.
973    fn handle_interrupt(&mut self, vect: u16, priority: Option<u8>) -> Result<(), StepBreak> {
974        if priority.is_some_and(|prio| prio <= self.psr.priority()) { return Ok(()) };
975        
976        // Virtual traps.
977        // See the flag for documentation.
978        // Virtual HALT
979        if !self.flags.use_real_traps {
980            if let Ok(intv) = RealIntVect::try_from(vect) {
981                if !self.prefetch {
982                    // decrement PC so that if play is pressed again, it goes back here
983                    self.offset_pc(-1, false)?;
984                    self.prefetch = true;
985                }
986                let break_value = match intv {
987                    RealIntVect::Halt => StepBreak::Halt,
988                    RealIntVect::PrivilegeViolation => StepBreak::Err(SimErr::PrivilegeViolation),
989                    RealIntVect::IllegalOpcode => StepBreak::Err(SimErr::IllegalOpcode),
990                    RealIntVect::AccessViolation => StepBreak::Err(SimErr::AccessViolation),
991                };
992                return Err(break_value);
993            }
994        };
995        
996        if !self.psr.privileged() {
997            std::mem::swap(&mut self.saved_sp, &mut self.reg_file[R6]);
998        }
999
1000        // Push PSR, PC to supervisor stack
1001        let old_psr = self.psr.get();
1002        let old_pc = self.pc;
1003        
1004        self.psr.set_privileged(true);
1005        let mctx = self.default_mem_ctx();
1006
1007        // push PSR and PC to stack
1008        let sp = self.reg_file[R6]
1009            .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
1010
1011        self.reg_file[R6] -= 2u16;
1012        self.write_mem(sp.wrapping_sub(1), Word::new_init(old_psr), mctx)?;
1013        self.write_mem(sp.wrapping_sub(2), Word::new_init(old_pc), mctx)?;
1014        
1015        // set PSR to z
1016        self.psr.set_cc_z();
1017
1018        // set interrupt priority
1019        if let Some(prio) = priority {
1020            self.psr.set_priority(prio);
1021        }
1022
1023        let ft = match priority.is_some() {
1024            true => FrameType::Interrupt,
1025            false => FrameType::Trap,
1026        };
1027        
1028        self.call_interrupt(vect, ft)
1029            .map_err(Into::into)
1030    }
1031
1032    /// Runs until the tripwire condition returns false (or any of the typical breaks occur).
1033    /// 
1034    /// The typical break conditions are:
1035    /// - `HALT` is executed
1036    /// - the MCR is set to false
1037    /// - A breakpoint matches
1038    pub fn run_while(&mut self, mut tripwire: impl FnMut(&mut Simulator) -> bool) -> Result<(), SimErr> {
1039        use std::sync::atomic::Ordering;
1040
1041        self.observer.clear();
1042        std::mem::take(&mut self.pause_condition);
1043        self.mcr.store(true, Ordering::Relaxed);
1044
1045        // event loop
1046        // run until:
1047        // 1. the MCR is set to false
1048        // 2. the tripwire condition returns false
1049        // 3. any of the breakpoints are hit
1050        let result = loop {
1051            // MCR turned off:
1052            if !self.mcr.load(Ordering::Relaxed) {
1053                break Ok(PauseCondition::MCROff);
1054            }
1055            // Tripwire turned off:
1056            if !tripwire(self) {
1057                break Ok(PauseCondition::Tripwire);
1058            }
1059            
1060            // Run a step:
1061            match self.step() {
1062                Ok(_) => {},
1063                Err(StepBreak::Halt) => break Ok(PauseCondition::Halt),
1064                Err(StepBreak::Err(e)) => break Err(e)
1065            }
1066
1067            // After executing, check that any breakpoints were hit.
1068            if self.breakpoints.iter().any(|bp| bp.check(self)) {
1069                break Ok(PauseCondition::Breakpoint);
1070            }
1071        };
1072    
1073        self.mcr.store(false, Ordering::Relaxed);
1074        self.pause_condition = result?;
1075        Ok(())
1076    }
1077
1078    /// Execute the program.
1079    /// 
1080    /// This blocks until the program ends. 
1081    /// If you would like to limit the maximum number of steps to execute, consider [`Simulator::run_with_limit`].
1082    pub fn run(&mut self) -> Result<(), SimErr> {
1083        self.run_while(|_| true)
1084    }
1085
1086    /// Execute the program with a limit on how many steps to execute.
1087    /// 
1088    /// This blocks until the program ends or until the number of steps to execute has been hit.
1089    pub fn run_with_limit(&mut self, max_steps: u64) -> Result<(), SimErr> {
1090        let i = self.instructions_run;
1091        self.run_while(|sim| sim.instructions_run.wrapping_sub(i) < max_steps)
1092    }
1093    
1094    /// Simulate one step, executing one instruction.
1095    /// 
1096    /// Unlike [`Simulator::step`], this function does not handle the `use_real_traps` flag.
1097    /// Both of these functions are not meant for general stepping use. That should be done
1098    /// with [`Simulator::step_in`].
1099    fn _step_inner(&mut self) -> Result<(), StepBreak> {
1100        self.prefetch = true;
1101
1102        if let Some(int) = self.device_handler.poll_interrupt() {
1103            match int.kind {
1104                // If priority passes, handle interrupt then skip FETCH:
1105                device::InterruptKind::Vectored { vect, priority } if priority > self.psr().priority() => {
1106                    return self.handle_interrupt(0x100 + u16::from(vect), Some(priority));
1107                },
1108                // If priority does not pass, move to FETCH:
1109                device::InterruptKind::Vectored { .. } => Ok(()),
1110
1111                // External interrupt.
1112                device::InterruptKind::External(int) => Err(StepBreak::Err(SimErr::Interrupt(int))),
1113            }?;
1114        }
1115
1116        let word = self.read_mem(self.pc, self.default_mem_ctx())?
1117            .get_if_init(self.flags.strict, SimErr::StrictPCCurrUninit)?;
1118
1119        let instr = SimInstr::decode(word)?;
1120
1121        self.offset_pc(1, false)?;
1122        self.prefetch = false;
1123
1124        match instr {
1125            SimInstr::BR(cc, off)  => {
1126                if cc & self.psr.cc() != 0 {
1127                    self.offset_pc(off.get(), true)?;
1128                }
1129            },
1130            SimInstr::ADD(dr, sr1, sr2) => {
1131                let val1 = self.reg_file[sr1];
1132                let val2 = match sr2 {
1133                    ImmOrReg::Imm(i2) => Word::from(i2.get()),
1134                    ImmOrReg::Reg(r2) => self.reg_file[r2],
1135                };
1136
1137                let result = val1 + val2;
1138                self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
1139                self.set_cc(result.get());
1140            },
1141            SimInstr::LD(dr, off) => {
1142                let ea = self.pc.wrapping_add_signed(off.get());
1143                let write_strict = self.flags.strict && !self.in_alloca(ea);
1144
1145                let val = self.read_mem(ea, self.default_mem_ctx())?;
1146                self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
1147                self.set_cc(val.get());
1148            },
1149            SimInstr::ST(sr, off) => {
1150                let ea = self.pc.wrapping_add_signed(off.get());
1151                let write_ctx = MemAccessCtx {
1152                    strict: self.flags.strict && !self.in_alloca(ea),
1153                    ..self.default_mem_ctx()
1154                };
1155
1156                let val = self.reg_file[sr];
1157                self.write_mem(ea, val, write_ctx)?;
1158            },
1159            SimInstr::JSR(op) => {
1160                // Note: JSRR R7 jumps to address at R7, then sets PC to R7.
1161                // Refer to: https://github.com/gt-cs2110/lc3tools/commit/fa9a23f62106eeee9fef7d2a278ba989356c9ee2
1162
1163                let addr = match op {
1164                    ImmOrReg::Imm(off) => Word::from(self.pc.wrapping_add_signed(off.get())),
1165                    ImmOrReg::Reg(br)  => self.reg_file[br],
1166                }.get_if_init(self.flags.strict, SimErr::StrictSRAddrUninit)?;
1167
1168                self.call_subroutine(addr)?;
1169            },
1170            SimInstr::AND(dr, sr1, sr2) => {
1171                let val1 = self.reg_file[sr1];
1172                let val2 = match sr2 {
1173                    ImmOrReg::Imm(i2) => Word::from(i2.get()),
1174                    ImmOrReg::Reg(r2) => self.reg_file[r2],
1175                };
1176
1177                let result = val1 & val2;
1178                self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
1179                self.set_cc(result.get());
1180            },
1181            SimInstr::LDR(dr, br, off) => {
1182                let ea = self.reg_file[br]
1183                    .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?
1184                    .wrapping_add_signed(off.get());
1185                let write_strict = self.flags.strict && br != R6 && !self.in_alloca(ea);
1186                
1187                let val = self.read_mem(ea, self.default_mem_ctx())?;
1188                self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
1189                self.set_cc(val.get());
1190            },
1191            SimInstr::STR(sr, br, off) => {
1192                let ea = self.reg_file[br]
1193                    .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?
1194                    .wrapping_add_signed(off.get());
1195                let write_ctx = MemAccessCtx {
1196                    strict: self.flags.strict && br != R6 && !self.in_alloca(ea),
1197                    ..self.default_mem_ctx()
1198                };
1199                
1200                let val = self.reg_file[sr];
1201                self.write_mem(ea, val, write_ctx)?;
1202            },
1203            SimInstr::RTI => {
1204                if self.psr.privileged() || self.flags.ignore_privilege {
1205                    let mctx = self.default_mem_ctx();
1206
1207                    // Pop PC and PSR from the stack
1208                    let sp = self.reg_file[R6]
1209                        .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
1210
1211                    let pc = self.read_mem(sp, mctx)?
1212                        .get_if_init(self.flags.strict, SimErr::StrictJmpAddrUninit)?;
1213                    let psr = self.read_mem(sp.wrapping_add(1), mctx)?
1214                        .get_if_init(self.flags.strict, SimErr::StrictPSRSetUninit)?;
1215                    self.reg_file[R6] += 2u16;
1216
1217                    self.set_pc(Word::new_init(pc), true)?;
1218                    self.psr = PSR(psr);
1219
1220                    if !self.psr.privileged() {
1221                        std::mem::swap(&mut self.saved_sp, &mut self.reg_file[R6]);
1222                    }
1223
1224                    self.frame_stack.pop_frame();
1225                } else {
1226                    return Err(SimErr::PrivilegeViolation.into());
1227                }
1228            },
1229            SimInstr::NOT(dr, sr) => {
1230                let val = self.reg_file[sr];
1231                
1232                let result = !val;
1233                self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
1234                self.set_cc(result.get());
1235            },
1236            SimInstr::LDI(dr, off) => {
1237                let shifted_pc = self.pc.wrapping_add_signed(off.get());
1238                let ea = self.read_mem(shifted_pc, self.default_mem_ctx())?
1239                    .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
1240                let write_strict = self.flags.strict && !self.in_alloca(ea);
1241
1242                let val = self.read_mem(ea, self.default_mem_ctx())?;
1243                self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
1244                self.set_cc(val.get());
1245            },
1246            SimInstr::STI(sr, off) => {
1247                let shifted_pc = self.pc.wrapping_add_signed(off.get());
1248                let ea = self.read_mem(shifted_pc, self.default_mem_ctx())?
1249                    .get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
1250                let write_ctx = MemAccessCtx {
1251                    strict: self.flags.strict && !self.in_alloca(ea),
1252                    ..self.default_mem_ctx()
1253                };
1254
1255                let val = self.reg_file[sr];
1256                self.write_mem(ea, val, write_ctx)?;
1257            },
1258            SimInstr::JMP(br) => {
1259                let addr = self.reg_file[br];
1260                self.set_pc(addr, true)?;
1261                
1262                // if this is RET,
1263                // we must also handle frame information:
1264                if br.reg_no() == 7 {
1265                    self.frame_stack.pop_frame();
1266                }
1267            },
1268            SimInstr::LEA(dr, off) => {
1269                let ea = self.pc.wrapping_add_signed(off.get());
1270                self.reg_file[dr].set(ea);
1271            },
1272            SimInstr::TRAP(vect) => {
1273                self.handle_interrupt(vect.get(), None)?;
1274            },
1275        }
1276
1277        self.instructions_run = self.instructions_run.wrapping_add(1);
1278        Ok(())
1279    }
1280
1281    /// Simulate one step, executing one instruction.
1282    ///
1283    /// This function properly handles the `use_real_traps` flag.
1284    /// 
1285    /// This function is a library function and should be used when one step is needed.
1286    /// The difference between this function and [`Simulator::step_in`] is that this
1287    /// function can return [`StepBreak::Halt`] as an error,
1288    /// whereas `step_in` will ignore that error.
1289    fn step(&mut self) -> Result<(), StepBreak> {
1290        match self._step_inner() {
1291            // Virtual traps don't need to go through handle_interrupt logic
1292            s if !self.flags.use_real_traps => s,
1293            // Real traps!
1294            Err(StepBreak::Halt) => self.handle_interrupt(RealIntVect::Halt as u16, None),
1295            Err(StepBreak::Err(SimErr::PrivilegeViolation)) => self.handle_interrupt(RealIntVect::PrivilegeViolation as u16, None),
1296            Err(StepBreak::Err(SimErr::IllegalOpcode)) => self.handle_interrupt(RealIntVect::IllegalOpcode as u16, None),
1297            Err(StepBreak::Err(SimErr::InvalidInstrFormat)) => self.handle_interrupt(RealIntVect::IllegalOpcode as u16, None),
1298            Err(StepBreak::Err(SimErr::AccessViolation)) => self.handle_interrupt(RealIntVect::AccessViolation as u16, None),
1299            s => s
1300        }
1301    }
1302    /// Simulate one step, executing one instruction.
1303    pub fn step_in(&mut self) -> Result<(), SimErr> {
1304        self.observer.clear();
1305        match self.step() {
1306            Ok(()) => Ok(()),
1307            Err(StepBreak::Halt) => Ok(()),
1308            Err(StepBreak::Err(e)) => Err(e)
1309        }
1310    }
1311
1312    /// Simulate one step, executing one instruction and running through entire subroutines as a single step.
1313    pub fn step_over(&mut self) -> Result<(), SimErr> {
1314        let curr_frame = self.frame_stack.len();
1315        let mut first = Some(()); // is Some if this is the first instruction executed in this call
1316
1317        // this function should do at least one step before checking its condition
1318        // condition: run until we have landed back in the same frame
1319        self.run_while(|sim| first.take().is_some() || curr_frame < sim.frame_stack.len())
1320    }
1321
1322    /// Run through the simulator's execution until the subroutine is exited.
1323    pub fn step_out(&mut self) -> Result<(), SimErr> {
1324        let curr_frame = self.frame_stack.len();
1325        let mut first = Some(()); // is Some if this is the first instruction executed in this call
1326        
1327        // this function should do at least one step before checking its condition
1328        // condition: run until we've landed in a smaller frame
1329        if curr_frame != 0 {
1330            self.run_while(|sim| first.take().is_some() || curr_frame <= sim.frame_stack.len())?;
1331        }
1332
1333        Ok(())
1334    }
1335}
1336impl Default for Simulator {
1337    fn default() -> Self {
1338        Self::new(Default::default())
1339    }
1340}
1341
1342/// A wrapper over `u16` in order to faciliate the PSR.
1343/// 
1344/// The word is encoded as the following:
1345/// - `PSR[15..16]`: Privilege mode (0 = supervisor, 1 = user)
1346/// - `PSR[8..11]`:  Interrupt priority
1347/// - `PSR[0..3]`:   Condition codes
1348/// 
1349/// ```text
1350///         privilege
1351///         |     interrupt priority
1352///         |     |         condition codes
1353///         |     |         |
1354///         V     V         V
1355/// 0x8002: 1000 0000 0000 0010
1356///         ~     ~~~       ~~~
1357/// ```
1358/// 
1359/// Each of these are exposed as the [`PSR::privileged`], [`PSR::priority`], and [`PSR::cc`] values.
1360#[allow(clippy::upper_case_acronyms)]
1361#[repr(transparent)]
1362pub struct PSR(u16);
1363
1364impl PSR {
1365    /// Creates a PSR with a default value (user mode, `z` condition code).
1366    pub fn new() -> Self {
1367        PSR(0x8002)
1368    }
1369
1370    /// Checks whether the simulator is in privileged mode.
1371    /// - `true` = supervisor mode
1372    /// - `false` = user mode
1373    pub fn privileged(&self) -> bool {
1374        (self.0 >> 15) == 0
1375    }
1376    /// Checks the current interrupt priority of the simulator.
1377    pub fn priority(&self) -> u8 {
1378        ((self.0 >> 8) & 0b111) as u8
1379    }
1380    /// Checks the condition code of the simulator.
1381    pub fn cc(&self) -> u8 {
1382        (self.0 & 0b111) as u8
1383    }
1384    /// Checks the condition code of the simulator is `n`.
1385    pub fn is_n(&self) -> bool {
1386        self.cc() & 0b100 != 0
1387    }
1388    /// Checks the condition code of the simulator is `z`.
1389    pub fn is_z(&self) -> bool {
1390        self.cc() & 0b010 != 0
1391    }
1392    /// Checks the condition code of the simulator is `p`.
1393    pub fn is_p(&self) -> bool {
1394        self.cc() & 0b001 != 0
1395    }
1396
1397    /// Gets the bit-representation of the PSR.
1398    pub fn get(&self) -> u16 {
1399        self.0
1400    }
1401    /// Sets the PSR to the provided data value.
1402    pub fn set(&mut self, data: u16) {
1403        const MASK: u16 = 0b1000_0111_0000_0111;
1404        
1405        self.0 = data & MASK;
1406        self.set_cc((data & 0b111) as u8);
1407    }
1408    /// Sets whether the simulator is in privileged mode.
1409    pub fn set_privileged(&mut self, privl: bool) {
1410        self.0 &= 0x7FFF;
1411        self.0 |= u16::from(!privl) << 15;
1412    }
1413    /// Sets the current interrupt priority of the simulator.
1414    pub fn set_priority(&mut self, prio: u8) {
1415        self.0 &= 0xF8FF;
1416        self.0 |= u16::from(prio & 0b111) << 8;
1417    }
1418    /// Sets the condition code of the simulator.
1419    pub fn set_cc(&mut self, mut cc: u8) {
1420        self.0 &= 0xFFF8;
1421
1422        // Guard from invalid CC.
1423        cc &= 0b111;
1424        if cc.count_ones() != 1 { cc = 0b010 };
1425        self.0 |= u16::from(cc);
1426    }
1427    /// Sets the condition code of the simulator to `n`.
1428    pub fn set_cc_n(&mut self) {
1429        self.set_cc(0b100)
1430    }
1431    /// Sets the condition code of the simulator to `z`.
1432    pub fn set_cc_z(&mut self) {
1433        self.set_cc(0b010)
1434    }
1435    /// Sets the condition code of the simulator to `p`.
1436    pub fn set_cc_p(&mut self) {
1437        self.set_cc(0b001)
1438    }
1439}
1440impl Default for PSR {
1441    fn default() -> Self {
1442        Self::new()
1443    }
1444}
1445impl std::fmt::Debug for PSR {
1446    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1447        use std::fmt::Write;
1448        struct CC(u8);
1449
1450        impl std::fmt::Debug for CC {
1451            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1452                if self.0 & 0b100 != 0 { f.write_char('N')?; };
1453                if self.0 & 0b010 != 0 { f.write_char('Z')?; };
1454                if self.0 & 0b001 != 0 { f.write_char('P')?; };
1455                Ok(())
1456            }
1457        }
1458
1459        f.debug_struct("PSR")
1460            .field("privileged", &self.privileged())
1461            .field("priority", &self.priority())
1462            .field("cc", &CC(self.cc()))
1463            .finish()
1464    }
1465}
1466
1467/// A type alias for MCR.
1468pub type MCR = Arc<AtomicBool>;