pub struct Simulator {
pub mem: MemArray,
pub reg_file: RegFile,
pub pc: u16,
pub frame_stack: FrameStack,
pub instructions_run: u64,
pub observer: AccessObserver,
pub flags: SimFlags,
pub breakpoints: HashSet<Breakpoint>,
pub device_handler: DeviceHandler,
/* private fields */
}Expand description
Executes assembled code.
Fields§
§mem: MemArrayThe simulator’s memory.
Note that this is held in the heap, as it is too large for the stack.
reg_file: RegFileThe simulator’s register file.
pc: u16The program counter.
frame_stack: FrameStackThe frame stack.
instructions_run: u64The number of instructions successfully run since this Simulator was initialized.
This can be set to 0 to reset the counter.
observer: AccessObserverTracks accesses to simulator memory.
An access is defined as a read from or write to a memory location, which includes reading a memory location in order to decode it or to obtain an address for indirect accesses.
flags: SimFlagsConfiguration settings for the simulator.
These are preserved between resets.
See SimFlags for more details on what configuration
settings are available.
breakpoints: HashSet<Breakpoint>Breakpoints for the simulator.
device_handler: DeviceHandlerAll external devices connected to the system (IO and interrupting devices).
Implementations§
Source§impl Simulator
impl Simulator
Sourcepub fn new(flags: SimFlags) -> Self
pub fn new(flags: SimFlags) -> Self
Creates a new simulator with the provided initializers and with the OS loaded, but without a loaded object file.
Sourcepub fn reset(&mut self)
pub fn reset(&mut self)
Resets the simulator.
This resets the state of the Simulator back to before any execution calls,
while preserving configuration and debug state.
Note that this function preserves:
- Flags
- Breakpoints
- External interrupts
- MCR reference (i.e., anything with access to the Simulator’s MCR can still control it)
- IO (however, note that it does not reset IO state, which must be manually reset)
This also does not reload object files. Any object file data has to be reloaded into the Simulator.
Sourcepub fn read_mem(&mut self, addr: u16, ctx: MemAccessCtx) -> Result<Word, SimErr>
pub fn read_mem(&mut self, addr: u16, ctx: MemAccessCtx) -> Result<Word, SimErr>
Fallibly reads the word at the provided index, erroring if not possible.
This accepts a MemAccessCtx, that describes the parameters of the memory access.
The simulator provides a default MemAccessCtx under Simulator::default_mem_ctx.
The flags are used as follows:
privileged: if false, this access errors if the address is a memory location outside of the user range.strict: not used forread
Note that this method is used for simulating a read to memory-mapped IO.
If you would like to query the memory’s state, consider using index on MemArray.
Sourcepub fn write_mem(
&mut self,
addr: u16,
data: Word,
ctx: MemAccessCtx,
) -> Result<(), SimErr>
pub fn write_mem( &mut self, addr: u16, data: Word, ctx: MemAccessCtx, ) -> Result<(), SimErr>
Fallibly writes the word at the provided index, erroring if not possible.
This accepts a MemAccessCtx, that describes the parameters of the memory access.
The simulator provides a default MemAccessCtx under Simulator::default_mem_ctx.
The flags are used as follows:
privileged: if false, this access errors if the address is a memory location outside of the user range.strict: If true, all accesses that would cause a memory location to be set with uninitialized data causes an error.
Note that this method is used for simulating a write to memory-mapped IO.
If you would like to edit the memory’s state, consider using index_mut on MemArray.
Sourcepub fn mmap_internal(
&mut self,
addr: u16,
reg: InternalRegister,
) -> Result<(), MMapInternalErr>
pub fn mmap_internal( &mut self, addr: u16, reg: InternalRegister, ) -> Result<(), MMapInternalErr>
Exposes an internal register to memory-mapped IO.
The effect of this is that if the assigned IO address is accessed (loaded or stored), the internal register is accessed instead.
Returns an MMapInternalErr if unsuccessful.
§Example
use lc3_ensemble::sim::Simulator;
use lc3_ensemble::sim::SimFlags;
use lc3_ensemble::sim::InternalRegister;
use lc3_ensemble::sim::mem::Word;
let mut sim = Simulator::new(SimFlags { ignore_privilege: true, ..Default::default() });
sim.mmap_internal(0xFE05, InternalRegister::PC);
// Read from PC:
assert_eq!(sim.read_mem(0xFE05, sim.default_mem_ctx())?.get(), 0x3000);
// Write to PC:
sim.write_mem(0xFE05, Word::new_init(0x3939), sim.default_mem_ctx())?;
assert_eq!(sim.pc, 0x3939);Sourcepub fn munmap_internal(&mut self, addr: u16) -> bool
pub fn munmap_internal(&mut self, addr: u16) -> bool
Removes an internal register from memory-mapped IO
which was previously exposed by Simulator::mmap_internal.
Note that this only updates internal registers.
Any external devices need to be removed with DeviceHandler instead.
This returns whether a register was successfully removed.
Sourcepub fn load_obj_file(&mut self, obj: &ObjectFile) -> Result<(), SimErr>
pub fn load_obj_file(&mut self, obj: &ObjectFile) -> Result<(), SimErr>
Loads an object file into this simulator.
Sourcepub fn prefetch_pc(&self) -> u16
pub fn prefetch_pc(&self) -> u16
Gets the value of the prefetch PC.
This function is useful as it returns the location of the currently executing instruction in memory.
Sourcepub fn hit_breakpoint(&self) -> bool
pub fn hit_breakpoint(&self) -> bool
Indicates whether the last execution of the simulator hit a breakpoint.
Sourcepub fn hit_halt(&self) -> bool
pub fn hit_halt(&self) -> bool
Indicates whether the last execution of the simulator resulted in a HALT successfully occurring.
This is defined as:
HALTbeing executed while virtual HALTs are enabledMCRbeing set tox0000during the execution of the program.
Sourcepub fn default_mem_ctx(&self) -> MemAccessCtx
pub fn default_mem_ctx(&self) -> MemAccessCtx
Computes the default memory access context,
which are the default flags to use (see Simulator::read_mem and Simulator::write_mem).
Sourcepub fn call_subroutine(&mut self, addr: u16) -> Result<(), SimErr>
pub fn call_subroutine(&mut self, addr: u16) -> Result<(), SimErr>
Calls a subroutine.
This does all the steps for calling a subroutine, namely:
- Setting the PC to the subroutine’s start address
- Setting R7 to the original PC (return address)
- Adding information to the frame stack
Sourcepub fn run_while(
&mut self,
tripwire: impl FnMut(&mut Simulator) -> bool,
) -> Result<(), SimErr>
pub fn run_while( &mut self, tripwire: impl FnMut(&mut Simulator) -> bool, ) -> Result<(), SimErr>
Runs until the tripwire condition returns false (or any of the typical breaks occur).
The typical break conditions are:
HALTis executed- the MCR is set to false
- A breakpoint matches
Sourcepub fn run(&mut self) -> Result<(), SimErr>
pub fn run(&mut self) -> Result<(), SimErr>
Execute the program.
This blocks until the program ends.
If you would like to limit the maximum number of steps to execute, consider Simulator::run_with_limit.
Sourcepub fn run_with_limit(&mut self, max_steps: u64) -> Result<(), SimErr>
pub fn run_with_limit(&mut self, max_steps: u64) -> Result<(), SimErr>
Execute the program with a limit on how many steps to execute.
This blocks until the program ends or until the number of steps to execute has been hit.