use mc6809_core::{Cpu, Memory};
pub const PASS_REG: u16 = 0xFF00;
pub const FAIL_REG: u16 = 0xFF01;
const TRIGGER_IRQ: u16 = 0xFF02;
const TRIGGER_FIRQ: u16 = 0xFF03;
const TRIGGER_NMI: u16 = 0xFF04;
const MAX_CYCLES: u64 = 10_000_000;
#[derive(Debug, PartialEq)]
pub enum HaltReason {
Pass(u8),
Fail(u8),
CycleLimit,
}
pub struct TestHarness {
mem: Box<[u8; 65536]>,
halted: bool,
halt_reason: Option<HaltReason>,
irq_asserted: bool,
firq_asserted: bool,
nmi_pulse: bool,
}
impl TestHarness {
pub fn new() -> Self {
Self {
mem: Box::new([0u8; 65536]),
halted: false,
halt_reason: None,
irq_asserted: false,
firq_asserted: false,
nmi_pulse: false,
}
}
pub fn load(&mut self, data: &[u8], base: u16) {
let start = base as usize;
let end = start + data.len();
assert!(end <= 65536, "binary exceeds 64 KB address space");
self.mem[start..end].copy_from_slice(data);
}
}
impl Default for TestHarness {
fn default() -> Self {
Self::new()
}
}
impl Memory for TestHarness {
fn read(&mut self, addr: u16) -> u8 {
self.mem[addr as usize]
}
fn write(&mut self, addr: u16, val: u8) {
match addr {
PASS_REG => {
self.halt_reason = Some(HaltReason::Pass(val));
self.halted = true;
}
FAIL_REG => {
self.halt_reason = Some(HaltReason::Fail(val));
self.halted = true;
}
TRIGGER_IRQ => {
self.irq_asserted = val != 0;
}
TRIGGER_FIRQ => {
self.firq_asserted = val != 0;
}
TRIGGER_NMI => {
self.nmi_pulse = true;
}
_ => {
self.mem[addr as usize] = val;
}
}
}
}
pub fn run_to_halt(cpu: &mut Cpu, system: &mut TestHarness) -> HaltReason {
while cpu.cycles() < MAX_CYCLES {
cpu.set_irq(system.irq_asserted);
cpu.set_firq(system.firq_asserted);
if system.nmi_pulse {
system.nmi_pulse = false;
cpu.trigger_nmi();
}
cpu.step(system);
if system.halted {
return system.halt_reason.take().unwrap();
}
}
HaltReason::CycleLimit
}