pub mod mem;
pub mod debug;
pub mod frame;
pub mod device;
pub mod observer;
use std::collections::{HashMap, HashSet};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use crate::asm::ObjectFile;
use crate::ast::Reg::{R6, R7};
use crate::ast::sim::SimInstr;
use crate::ast::ImmOrReg;
use debug::Breakpoint;
use device::{DeviceHandler, ExternalDevice, ExternalInterrupt};
use observer::AccessSet;
use self::frame::{FrameStack, FrameType};
use self::mem::{MemArray, RegFile, Word, MachineInitStrategy};
#[derive(Debug)]
pub enum SimErr {
IllegalOpcode,
InvalidInstrFormat,
PrivilegeViolation,
AccessViolation,
UnresolvedExternal(String),
Interrupt(ExternalInterrupt),
StrictRegSetUninit,
StrictMemSetUninit,
StrictIOSetUninit,
StrictJmpAddrUninit,
StrictSRAddrUninit,
StrictMemAddrUninit,
StrictPCCurrUninit,
StrictPCNextUninit,
StrictPSRSetUninit,
}
impl std::fmt::Display for SimErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SimErr::IllegalOpcode => f.write_str("simulator executed illegal opcode"),
SimErr::InvalidInstrFormat => f.write_str("simulator executed invalid instruction"),
SimErr::PrivilegeViolation => f.write_str("privilege violation"),
SimErr::AccessViolation => f.write_str("access violation"),
SimErr::UnresolvedExternal(s) => write!(f, "unresolved external label {s} in object file"),
SimErr::Interrupt(e) => write!(f, "unhandled interrupt: {e}"),
SimErr::StrictRegSetUninit => f.write_str("register was set to uninitialized value (strict mode)"),
SimErr::StrictMemSetUninit => f.write_str("tried to write an uninitialized value to memory (strict mode)"),
SimErr::StrictIOSetUninit => f.write_str("tried to write an uninitialized value to memory-mapped IO (strict mode)"),
SimErr::StrictJmpAddrUninit => f.write_str("PC address was set to uninitialized address (strict mode)"),
SimErr::StrictSRAddrUninit => f.write_str("Subroutine starts at uninitialized address (strict mode)"),
SimErr::StrictMemAddrUninit => f.write_str("tried to access memory with an uninitialized address (strict mode)"),
SimErr::StrictPCCurrUninit => f.write_str("PC is pointing to uninitialized value (strict mode)"),
SimErr::StrictPCNextUninit => f.write_str("PC will point to uninitialized value when this instruction executes (strict mode)"),
SimErr::StrictPSRSetUninit => f.write_str("tried to set the PSR to an uninitialized value (strict mode)"),
}
}
}
impl std::error::Error for SimErr {}
enum StepBreak {
Halt,
Err(SimErr),
}
impl From<SimErr> for StepBreak {
fn from(value: SimErr) -> Self {
Self::Err(value)
}
}
macro_rules! int_vect {
($Type:ident, {$($name:ident = $value:literal), +}) => {
enum $Type {
$($name = $value),+
}
impl TryFrom<u16> for $Type {
type Error = ();
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
$($value => Ok(Self::$name)),+,
_ => Err(())
}
}
}
}
}
int_vect!(RealIntVect, {
Halt = 0x25,
PrivilegeViolation = 0x100,
IllegalOpcode = 0x101,
AccessViolation = 0x102
});
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn _os_obj_file() -> &'static ObjectFile {
use crate::parse::parse_ast;
use crate::asm::assemble_debug;
use std::sync::OnceLock;
static OS_OBJ_FILE: OnceLock<ObjectFile> = OnceLock::new();
OS_OBJ_FILE.get_or_init(|| {
let os_file = include_str!("os.asm");
let ast = parse_ast(os_file).unwrap();
assemble_debug(ast, os_file).unwrap()
})
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
enum PauseCondition {
Halt,
MCROff,
Breakpoint,
Tripwire,
#[default]
Unsuccessful
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct SimFlags {
pub strict: bool,
pub use_real_traps: bool,
pub machine_init: MachineInitStrategy,
pub debug_frames: bool,
pub ignore_privilege: bool
}
#[allow(clippy::derivable_impls)]
impl Default for SimFlags {
fn default() -> Self {
Self {
strict: false,
use_real_traps: false,
machine_init: Default::default(),
debug_frames: false,
ignore_privilege: false
}
}
}
const USER_START: u16 = 0x3000;
const IO_START: u16 = 0xFE00;
const PSR_ADDR: u16 = 0xFFFC;
const MCR_ADDR: u16 = 0xFFFE;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum InternalRegister {
PC,
PSR,
MCR,
SavedSP,
}
impl InternalRegister {
fn default_mmap() -> HashMap<u16, Self> {
HashMap::from_iter([
(PSR_ADDR, Self::PSR),
(MCR_ADDR, Self::MCR),
])
}
fn read(self, sim: &mut Simulator) -> u16 {
use std::sync::atomic::Ordering;
match self {
InternalRegister::PC => sim.pc,
InternalRegister::PSR => sim.psr.get(),
InternalRegister::MCR => u16::from(sim.mcr.load(Ordering::Relaxed)) << 15,
InternalRegister::SavedSP => sim.saved_sp.get(),
}
}
fn write(self, sim: &mut Simulator, data: u16) {
use std::sync::atomic::Ordering;
match self {
InternalRegister::PC => sim.pc = data,
InternalRegister::PSR => sim.psr.set(data),
InternalRegister::MCR => sim.mcr.store((data as i16) < 0, Ordering::Relaxed),
InternalRegister::SavedSP => sim.saved_sp.set(data)
}
}
}
#[derive(Debug)]
pub enum MMapInternalErr {
NotInIORange,
AddrAlreadyMapped
}
impl std::fmt::Display for MMapInternalErr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MMapInternalErr::NotInIORange => f.write_str("address not in IO range"),
MMapInternalErr::AddrAlreadyMapped => f.write_str("address already mapped"),
}
}
}
impl std::error::Error for MMapInternalErr {}
#[derive(Clone, Copy, Debug)]
pub struct MemAccessCtx {
pub privileged: bool,
pub strict: bool,
pub io_effects: bool,
pub track_access: bool
}
impl MemAccessCtx {
pub fn omnipotent() -> Self {
MemAccessCtx {
privileged: true, strict: false, io_effects: false, track_access: false }
}
}
#[derive(Debug)]
pub struct Simulator {
pub mem: MemArray,
pub reg_file: RegFile,
pub pc: u16,
psr: PSR,
saved_sp: Word,
pub frame_stack: FrameStack,
alloca: Box<[(u16, u16)]>,
pub instructions_run: u64,
prefetch: bool,
pause_condition: PauseCondition,
pub observer: observer::AccessObserver,
os_loaded: bool,
mcr: MCR,
pub flags: SimFlags,
pub breakpoints: HashSet<Breakpoint>,
ireg_mmap: HashMap<u16, InternalRegister>,
pub device_handler: DeviceHandler
}
impl Simulator where Simulator: Send + Sync {}
impl Simulator {
fn new_with_mcr(flags: SimFlags, mcr: MCR) -> Self {
let mut filler = flags.machine_init.generator();
let mut sim = Self {
mem: MemArray::new(&mut filler),
reg_file: RegFile::new(&mut filler),
pc: 0x3000,
psr: PSR::new(),
saved_sp: Word::new_init(0x3000),
frame_stack: FrameStack::new(flags.debug_frames),
alloca: Box::new([]),
instructions_run: 0,
prefetch: false,
pause_condition: Default::default(),
observer: Default::default(),
os_loaded: false,
mcr,
flags,
breakpoints: Default::default(),
ireg_mmap: InternalRegister::default_mmap(),
device_handler: Default::default()
};
sim.mem.as_slice_mut()[IO_START as usize..].fill(Word::new_init(0)); sim.load_os();
sim
}
pub fn new(flags: SimFlags) -> Self {
Self::new_with_mcr(flags, Arc::default())
}
fn load_os(&mut self) {
if !self.os_loaded {
self.load_obj_file(_os_obj_file())
.unwrap_or_else(|_| unreachable!("OS object file should not have externals"));
self.os_loaded = true;
}
}
pub fn reset(&mut self) {
let mcr = Arc::clone(&self.mcr);
let flags = self.flags;
let breakpoints = std::mem::take(&mut self.breakpoints);
let ireg_map = std::mem::take(&mut self.ireg_mmap);
let dev_handler = std::mem::take(&mut self.device_handler);
*self = Simulator::new_with_mcr(flags, mcr);
self.breakpoints = breakpoints;
self.ireg_mmap = ireg_map;
self.device_handler = dev_handler;
self.device_handler.io_reset();
}
pub fn read_mem(&mut self, addr: u16, ctx: MemAccessCtx) -> Result<Word, SimErr> {
if !ctx.privileged && !(USER_START..IO_START).contains(&addr) { return Err(SimErr::AccessViolation) };
match addr {
0..USER_START => { },
USER_START..IO_START => { },
IO_START.. => {
if let Some(ireg) = self.ireg_mmap.get(&addr) {
let data = ireg.read(self);
self.mem[addr].set(data);
} else if let Some(data) = self.device_handler.io_read(addr, ctx.io_effects) {
self.mem[addr].set(data);
}
}
}
if ctx.track_access {
self.observer.update_mem_accesses(addr, AccessSet::READ);
}
Ok(self.mem[addr])
}
pub fn write_mem(&mut self, addr: u16, data: Word, ctx: MemAccessCtx) -> Result<(), SimErr> {
if !ctx.privileged && !(USER_START..IO_START).contains(&addr) { return Err(SimErr::AccessViolation) };
let success = match addr {
0..USER_START => true,
USER_START..IO_START => true,
IO_START.. => {
let io_data = data.get_if_init(ctx.strict, SimErr::StrictIOSetUninit)?;
match self.ireg_mmap.get(&addr) {
Some(ir) => {
ir.write(self, io_data);
true
},
None => self.device_handler.io_write(addr, io_data),
}
}
};
if success {
if ctx.track_access {
self.observer.update_mem_accesses(addr, AccessSet::WRITTEN);
if self.mem[addr] != data {
self.observer.update_mem_accesses(addr, AccessSet::MODIFIED);
}
}
self.mem[addr]
.set_if_init(data, ctx.strict, SimErr::StrictMemSetUninit)?;
}
Ok(())
}
pub fn mmap_internal(&mut self, addr: u16, reg: InternalRegister) -> Result<(), MMapInternalErr> {
use std::collections::hash_map::Entry;
if !(IO_START..).contains(&addr) { return Err(MMapInternalErr::NotInIORange); }
let Entry::Vacant(e) = self.ireg_mmap.entry(addr) else {
return Err(MMapInternalErr::AddrAlreadyMapped);
};
e.insert(reg);
Ok(())
}
pub fn munmap_internal(&mut self, addr: u16) -> bool {
self.ireg_mmap.remove(&addr).is_some()
}
pub fn load_obj_file(&mut self, obj: &ObjectFile) -> Result<(), SimErr> {
use std::cmp::Ordering;
if let Some(ext) = obj.get_external_symbol() {
return Err(SimErr::UnresolvedExternal(ext.to_string()));
}
let mut alloca = vec![];
for (start, words) in obj.block_iter() {
self.mem.copy_obj_block(start, words);
let len = words.len() as u16;
let end = start.wrapping_add(len);
match start.cmp(&end) {
Ordering::Less => alloca.push((start, len)),
Ordering::Equal => {},
Ordering::Greater => {
alloca.push((start, start.wrapping_neg()));
if end != 0 { alloca.push((0, end)) };
},
}
}
alloca.sort_by_key(|&(start, _)| start);
self.alloca = alloca.into_boxed_slice();
Ok(())
}
fn set_cc(&mut self, result: u16) {
match (result as i16).cmp(&0) {
std::cmp::Ordering::Less => self.psr.set_cc(0b100),
std::cmp::Ordering::Equal => self.psr.set_cc(0b010),
std::cmp::Ordering::Greater => self.psr.set_cc(0b001),
}
}
pub fn psr(&self) -> &PSR {
&self.psr
}
pub fn mcr(&self) -> &MCR {
&self.mcr
}
fn set_pc(&mut self, addr_word: Word, st_check_mem: bool) -> Result<(), SimErr> {
let addr = addr_word.get_if_init(self.flags.strict, SimErr::StrictJmpAddrUninit)?;
if self.flags.strict && st_check_mem {
if !self.read_mem(addr, self.default_mem_ctx())?.is_init() {
return Err(SimErr::StrictPCNextUninit);
}
}
self.pc = addr;
Ok(())
}
fn offset_pc(&mut self, offset: i16, st_check_mem: bool) -> Result<(), SimErr> {
self.set_pc(Word::from(self.pc.wrapping_add_signed(offset)), st_check_mem)
}
pub fn prefetch_pc(&self) -> u16 {
self.pc - (!self.prefetch) as u16
}
fn in_alloca(&self, addr: u16) -> bool {
let first_post = self.alloca.partition_point(|&(start, _)| start <= addr);
if first_post == 0 { return false };
let (start, len) = self.alloca[first_post - 1];
match start.checked_add(len) {
Some(e) => addr < e,
None => true
}
}
pub fn hit_breakpoint(&self) -> bool {
matches!(self.pause_condition, PauseCondition::Breakpoint)
}
pub fn hit_halt(&self) -> bool {
matches!(self.pause_condition, PauseCondition::Halt | PauseCondition::MCROff)
}
pub fn default_mem_ctx(&self) -> MemAccessCtx {
MemAccessCtx {
privileged: self.psr.privileged() || self.flags.ignore_privilege,
strict: self.flags.strict,
io_effects: true,
track_access: true
}
}
pub fn call_subroutine(&mut self, addr: u16) -> Result<(), SimErr> {
self.reg_file[R7].set(self.pc);
self.frame_stack.push_frame(self.prefetch_pc(), addr, FrameType::Subroutine, &self.reg_file, &self.mem);
self.set_pc(Word::new_init(addr), true)
}
fn call_interrupt(&mut self, vect: u16, ft: FrameType) -> Result<(), SimErr> {
let addr = self.read_mem(vect, self.default_mem_ctx())?
.get_if_init(self.flags.strict, SimErr::StrictSRAddrUninit)?;
self.frame_stack.push_frame(self.prefetch_pc(), vect, ft, &self.reg_file, &self.mem);
self.set_pc(Word::new_init(addr), true)
}
fn handle_interrupt(&mut self, vect: u16, priority: Option<u8>) -> Result<(), StepBreak> {
if priority.is_some_and(|prio| prio <= self.psr.priority()) { return Ok(()) };
if !self.flags.use_real_traps {
if let Ok(intv) = RealIntVect::try_from(vect) {
if !self.prefetch {
self.offset_pc(-1, false)?;
self.prefetch = true;
}
let break_value = match intv {
RealIntVect::Halt => StepBreak::Halt,
RealIntVect::PrivilegeViolation => StepBreak::Err(SimErr::PrivilegeViolation),
RealIntVect::IllegalOpcode => StepBreak::Err(SimErr::IllegalOpcode),
RealIntVect::AccessViolation => StepBreak::Err(SimErr::AccessViolation),
};
return Err(break_value);
}
};
if !self.psr.privileged() {
std::mem::swap(&mut self.saved_sp, &mut self.reg_file[R6]);
}
let old_psr = self.psr.get();
let old_pc = self.pc;
self.psr.set_privileged(true);
let mctx = self.default_mem_ctx();
let sp = self.reg_file[R6]
.get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
self.reg_file[R6] -= 2u16;
self.write_mem(sp.wrapping_sub(1), Word::new_init(old_psr), mctx)?;
self.write_mem(sp.wrapping_sub(2), Word::new_init(old_pc), mctx)?;
self.psr.set_cc_z();
if let Some(prio) = priority {
self.psr.set_priority(prio);
}
let ft = match priority.is_some() {
true => FrameType::Interrupt,
false => FrameType::Trap,
};
self.call_interrupt(vect, ft)
.map_err(Into::into)
}
pub fn run_while(&mut self, mut tripwire: impl FnMut(&mut Simulator) -> bool) -> Result<(), SimErr> {
use std::sync::atomic::Ordering;
self.observer.clear();
std::mem::take(&mut self.pause_condition);
self.mcr.store(true, Ordering::Relaxed);
let result = loop {
if !self.mcr.load(Ordering::Relaxed) {
break Ok(PauseCondition::MCROff);
}
if !tripwire(self) {
break Ok(PauseCondition::Tripwire);
}
match self.step() {
Ok(_) => {},
Err(StepBreak::Halt) => break Ok(PauseCondition::Halt),
Err(StepBreak::Err(e)) => break Err(e)
}
if self.breakpoints.iter().any(|bp| bp.check(self)) {
break Ok(PauseCondition::Breakpoint);
}
};
self.mcr.store(false, Ordering::Relaxed);
self.pause_condition = result?;
Ok(())
}
pub fn run(&mut self) -> Result<(), SimErr> {
self.run_while(|_| true)
}
pub fn run_with_limit(&mut self, max_steps: u64) -> Result<(), SimErr> {
let i = self.instructions_run;
self.run_while(|sim| sim.instructions_run.wrapping_sub(i) < max_steps)
}
fn _step_inner(&mut self) -> Result<(), StepBreak> {
self.prefetch = true;
if let Some(int) = self.device_handler.poll_interrupt() {
match int.kind {
device::InterruptKind::Vectored { vect, priority } if priority > self.psr().priority() => {
return self.handle_interrupt(0x100 + u16::from(vect), Some(priority));
},
device::InterruptKind::Vectored { .. } => Ok(()),
device::InterruptKind::External(int) => Err(StepBreak::Err(SimErr::Interrupt(int))),
}?;
}
let word = self.read_mem(self.pc, self.default_mem_ctx())?
.get_if_init(self.flags.strict, SimErr::StrictPCCurrUninit)?;
let instr = SimInstr::decode(word)?;
self.offset_pc(1, false)?;
self.prefetch = false;
match instr {
SimInstr::BR(cc, off) => {
if cc & self.psr.cc() != 0 {
self.offset_pc(off.get(), true)?;
}
},
SimInstr::ADD(dr, sr1, sr2) => {
let val1 = self.reg_file[sr1];
let val2 = match sr2 {
ImmOrReg::Imm(i2) => Word::from(i2.get()),
ImmOrReg::Reg(r2) => self.reg_file[r2],
};
let result = val1 + val2;
self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
self.set_cc(result.get());
},
SimInstr::LD(dr, off) => {
let ea = self.pc.wrapping_add_signed(off.get());
let write_strict = self.flags.strict && !self.in_alloca(ea);
let val = self.read_mem(ea, self.default_mem_ctx())?;
self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
self.set_cc(val.get());
},
SimInstr::ST(sr, off) => {
let ea = self.pc.wrapping_add_signed(off.get());
let write_ctx = MemAccessCtx {
strict: self.flags.strict && !self.in_alloca(ea),
..self.default_mem_ctx()
};
let val = self.reg_file[sr];
self.write_mem(ea, val, write_ctx)?;
},
SimInstr::JSR(op) => {
let addr = match op {
ImmOrReg::Imm(off) => Word::from(self.pc.wrapping_add_signed(off.get())),
ImmOrReg::Reg(br) => self.reg_file[br],
}.get_if_init(self.flags.strict, SimErr::StrictSRAddrUninit)?;
self.call_subroutine(addr)?;
},
SimInstr::AND(dr, sr1, sr2) => {
let val1 = self.reg_file[sr1];
let val2 = match sr2 {
ImmOrReg::Imm(i2) => Word::from(i2.get()),
ImmOrReg::Reg(r2) => self.reg_file[r2],
};
let result = val1 & val2;
self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
self.set_cc(result.get());
},
SimInstr::LDR(dr, br, off) => {
let ea = self.reg_file[br]
.get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?
.wrapping_add_signed(off.get());
let write_strict = self.flags.strict && br != R6 && !self.in_alloca(ea);
let val = self.read_mem(ea, self.default_mem_ctx())?;
self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
self.set_cc(val.get());
},
SimInstr::STR(sr, br, off) => {
let ea = self.reg_file[br]
.get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?
.wrapping_add_signed(off.get());
let write_ctx = MemAccessCtx {
strict: self.flags.strict && br != R6 && !self.in_alloca(ea),
..self.default_mem_ctx()
};
let val = self.reg_file[sr];
self.write_mem(ea, val, write_ctx)?;
},
SimInstr::RTI => {
if self.psr.privileged() || self.flags.ignore_privilege {
let mctx = self.default_mem_ctx();
let sp = self.reg_file[R6]
.get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
let pc = self.read_mem(sp, mctx)?
.get_if_init(self.flags.strict, SimErr::StrictJmpAddrUninit)?;
let psr = self.read_mem(sp.wrapping_add(1), mctx)?
.get_if_init(self.flags.strict, SimErr::StrictPSRSetUninit)?;
self.reg_file[R6] += 2u16;
self.set_pc(Word::new_init(pc), true)?;
self.psr = PSR(psr);
if !self.psr.privileged() {
std::mem::swap(&mut self.saved_sp, &mut self.reg_file[R6]);
}
self.frame_stack.pop_frame();
} else {
return Err(SimErr::PrivilegeViolation.into());
}
},
SimInstr::NOT(dr, sr) => {
let val = self.reg_file[sr];
let result = !val;
self.reg_file[dr].set_if_init(result, self.flags.strict, SimErr::StrictRegSetUninit)?;
self.set_cc(result.get());
},
SimInstr::LDI(dr, off) => {
let shifted_pc = self.pc.wrapping_add_signed(off.get());
let ea = self.read_mem(shifted_pc, self.default_mem_ctx())?
.get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
let write_strict = self.flags.strict && !self.in_alloca(ea);
let val = self.read_mem(ea, self.default_mem_ctx())?;
self.reg_file[dr].set_if_init(val, write_strict, SimErr::StrictRegSetUninit)?;
self.set_cc(val.get());
},
SimInstr::STI(sr, off) => {
let shifted_pc = self.pc.wrapping_add_signed(off.get());
let ea = self.read_mem(shifted_pc, self.default_mem_ctx())?
.get_if_init(self.flags.strict, SimErr::StrictMemAddrUninit)?;
let write_ctx = MemAccessCtx {
strict: self.flags.strict && !self.in_alloca(ea),
..self.default_mem_ctx()
};
let val = self.reg_file[sr];
self.write_mem(ea, val, write_ctx)?;
},
SimInstr::JMP(br) => {
let addr = self.reg_file[br];
self.set_pc(addr, true)?;
if br.reg_no() == 7 {
self.frame_stack.pop_frame();
}
},
SimInstr::LEA(dr, off) => {
let ea = self.pc.wrapping_add_signed(off.get());
self.reg_file[dr].set(ea);
},
SimInstr::TRAP(vect) => {
self.handle_interrupt(vect.get(), None)?;
},
}
self.instructions_run = self.instructions_run.wrapping_add(1);
Ok(())
}
fn step(&mut self) -> Result<(), StepBreak> {
match self._step_inner() {
s if !self.flags.use_real_traps => s,
Err(StepBreak::Halt) => self.handle_interrupt(RealIntVect::Halt as u16, None),
Err(StepBreak::Err(SimErr::PrivilegeViolation)) => self.handle_interrupt(RealIntVect::PrivilegeViolation as u16, None),
Err(StepBreak::Err(SimErr::IllegalOpcode)) => self.handle_interrupt(RealIntVect::IllegalOpcode as u16, None),
Err(StepBreak::Err(SimErr::InvalidInstrFormat)) => self.handle_interrupt(RealIntVect::IllegalOpcode as u16, None),
Err(StepBreak::Err(SimErr::AccessViolation)) => self.handle_interrupt(RealIntVect::AccessViolation as u16, None),
s => s
}
}
pub fn step_in(&mut self) -> Result<(), SimErr> {
self.observer.clear();
match self.step() {
Ok(()) => Ok(()),
Err(StepBreak::Halt) => Ok(()),
Err(StepBreak::Err(e)) => Err(e)
}
}
pub fn step_over(&mut self) -> Result<(), SimErr> {
let curr_frame = self.frame_stack.len();
let mut first = Some(());
self.run_while(|sim| first.take().is_some() || curr_frame < sim.frame_stack.len())
}
pub fn step_out(&mut self) -> Result<(), SimErr> {
let curr_frame = self.frame_stack.len();
let mut first = Some(());
if curr_frame != 0 {
self.run_while(|sim| first.take().is_some() || curr_frame <= sim.frame_stack.len())?;
}
Ok(())
}
}
impl Default for Simulator {
fn default() -> Self {
Self::new(Default::default())
}
}
#[allow(clippy::upper_case_acronyms)]
#[repr(transparent)]
pub struct PSR(u16);
impl PSR {
pub fn new() -> Self {
PSR(0x8002)
}
pub fn privileged(&self) -> bool {
(self.0 >> 15) == 0
}
pub fn priority(&self) -> u8 {
((self.0 >> 8) & 0b111) as u8
}
pub fn cc(&self) -> u8 {
(self.0 & 0b111) as u8
}
pub fn is_n(&self) -> bool {
self.cc() & 0b100 != 0
}
pub fn is_z(&self) -> bool {
self.cc() & 0b010 != 0
}
pub fn is_p(&self) -> bool {
self.cc() & 0b001 != 0
}
pub fn get(&self) -> u16 {
self.0
}
pub fn set(&mut self, data: u16) {
const MASK: u16 = 0b1000_0111_0000_0111;
self.0 = data & MASK;
self.set_cc((data & 0b111) as u8);
}
pub fn set_privileged(&mut self, privl: bool) {
self.0 &= 0x7FFF;
self.0 |= u16::from(!privl) << 15;
}
pub fn set_priority(&mut self, prio: u8) {
self.0 &= 0xF8FF;
self.0 |= u16::from(prio & 0b111) << 8;
}
pub fn set_cc(&mut self, mut cc: u8) {
self.0 &= 0xFFF8;
cc &= 0b111;
if cc.count_ones() != 1 { cc = 0b010 };
self.0 |= u16::from(cc);
}
pub fn set_cc_n(&mut self) {
self.set_cc(0b100)
}
pub fn set_cc_z(&mut self) {
self.set_cc(0b010)
}
pub fn set_cc_p(&mut self) {
self.set_cc(0b001)
}
}
impl Default for PSR {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for PSR {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
struct CC(u8);
impl std::fmt::Debug for CC {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0 & 0b100 != 0 { f.write_char('N')?; };
if self.0 & 0b010 != 0 { f.write_char('Z')?; };
if self.0 & 0b001 != 0 { f.write_char('P')?; };
Ok(())
}
}
f.debug_struct("PSR")
.field("privileged", &self.privileged())
.field("priority", &self.priority())
.field("cc", &CC(self.cc()))
.finish()
}
}
pub type MCR = Arc<AtomicBool>;