pub struct Cpu { /* private fields */ }Expand description
Motorola 6809 CPU emulator.
Implementations§
Source§impl Cpu
impl Cpu
Sourcepub fn reset(&mut self, mem: &mut impl Memory)
pub fn reset(&mut self, mem: &mut impl Memory)
Hardware reset: read PC from reset vector, set I+F, clear state.
Sourcepub fn registers_mut(&mut self) -> RegistersMut<'_>
pub fn registers_mut(&mut self) -> RegistersMut<'_>
Mutable access to the programmer-visible registers via an RAII guard.
The guard implements std::ops::Deref and std::ops::DerefMut for
Registers, giving transparent read/write access to all fields. On drop
it checks whether the hardware stack pointer (S) changed and, if so, arms
the NMI — matching the real 6809 behaviour where the first write to S
enables edge-triggered NMI.
Note: the guard detects S changes by comparing the value on entry with the
value on drop. Writing S to the value it already holds will not arm NMI, but
because nmi_armed is sticky (never cleared) this is inconsequential in
practice.
§Example
cpu.registers_mut().s = 0x8000; // arms NMI
{
let mut r = cpu.registers_mut();
r.s -= 2;
mem[r.s as usize] = lo;
} // NMI armed here via DropSourcepub fn cycles(&self) -> u64
pub fn cycles(&self) -> u64
Total elapsed cycles since the last Self::reset.
Sourcepub fn halted(&self) -> bool
pub fn halted(&self) -> bool
true if the CPU has been halted by a halt instruction.
Illegal opcodes do not set this flag; they only set Self::illegal
so the host can decide whether to keep running or stop.
Sourcepub fn set_halted(&mut self, active: bool)
pub fn set_halted(&mut self, active: bool)
Assert or de-assert the halted state.
Sourcepub fn illegal(&self) -> bool
pub fn illegal(&self) -> bool
Sticky flag set when an illegal opcode is executed.
The 6809 keeps running after undefined opcodes, so this flag does not
halt the CPU by itself. Hosts that want trap-like behaviour can check
this flag after each Self::step and stop on their own policy.
Sourcepub fn clear_illegal(&mut self)
pub fn clear_illegal(&mut self)
Clear the illegal opcode flag.
Sourcepub fn set_irq(&mut self, active: bool)
pub fn set_irq(&mut self, active: bool)
Assert or de-assert the IRQ line (level-triggered).
The CPU samples this each step. Only the peripheral should de-assert it
(by calling set_irq(false)); the CPU never clears it internally.
Sourcepub fn set_firq(&mut self, active: bool)
pub fn set_firq(&mut self, active: bool)
Assert or de-assert the FIRQ line (level-triggered).
The CPU samples this each step. Only the peripheral should de-assert it
(by calling set_firq(false)); the CPU never clears it internally.
Sourcepub fn trigger_nmi(&mut self)
pub fn trigger_nmi(&mut self)
Trigger an NMI (edge-triggered). Only effective if NMI is armed.
Sourcepub fn apply_signals(&mut self, signals: BusSignals, prev: BusSignals)
pub fn apply_signals(&mut self, signals: BusSignals, prev: BusSignals)
Apply a snapshot of bus signals to the CPU, handling NMI edge detection.
Call this from the host loop whenever BusSignals change. Passing the
previous snapshot allows the CPU to detect the NMI rising edge internally,
so the caller does not need to track edge transitions for NMI.
IRQ and FIRQ are level-triggered: their state is mirrored directly into
the CPU. The CPU will hold the line until the peripheral de-asserts it
(i.e. returns a snapshot without IRQ/FIRQ set on a subsequent tick).
RESET is not handled here; the host loop is responsible for calling
Cpu::reset when signals contains BusSignals::RESET.
§Host loop pattern
let mut prev_signals = BusSignals::default();
loop {
let cycles = cpu.step(&mut mem);
let signals = peripheral.tick(cycles);
if signals.contains(BusSignals::RESET) {
cpu.reset(&mut mem);
prev_signals = BusSignals::default();
continue;
}
if signals != prev_signals {
cpu.apply_signals(signals, prev_signals);
prev_signals = signals;
}
if cpu.halted() { break; }
}Sourcepub fn step(&mut self, mem: &mut impl Memory) -> u64
pub fn step(&mut self, mem: &mut impl Memory) -> u64
Execute a single instruction (or handle a pending interrupt). Returns the number of cycles consumed or ZERO if the CPU is halted.
Warning! Busy-loops in host code that doesn’t check Self::halted
can lead to high CPU usage.
If the decoded instruction is illegal, the CPU records that in
Self::illegal and continues execution unless the caller chooses to
stop.
Sourcepub fn run(&mut self, mem: &mut impl Memory, cycle_budget: u64) -> u64
pub fn run(&mut self, mem: &mut impl Memory, cycle_budget: u64) -> u64
Run until at least cycle_budget cycles have been consumed.
This method stops only when the cycle budget is exhausted or
Self::halted becomes true. Illegal opcodes do not stop run; check
Self::illegal in the host loop if that policy is desired.