mc6809-core 0.3.1

Motorola 6809 CPU emulator core
Documentation

mc6809-core

mc6809-core is a small, focused Rust library implementing the Motorola 6809 CPU for use in emulators, tools, and testing harnesses. It provides a Cpu implementation capable of executing 6809 instructions against any memory system that implements the Memory trait.

Features

  • Accurate 6809 instruction execution and addressing modes
  • A Memory trait for pluggable memory and I/O backends
  • A Clocked trait for peripheral timing and interrupt signal delivery, kept separate from memory access
  • Lightweight API suitable for embedding in emulators, disassemblers, and debuggers

Quick example

use mc6809_core::{Cpu, Memory};

struct FlatRam([u8; 65536]);

impl Memory for FlatRam {
    fn read(&mut self, addr: u16) -> u8 { self.0[addr as usize] }
    fn write(&mut self, addr: u16, val: u8) { self.0[addr as usize] = val; }
}

let mut mem = FlatRam([0; 65536]);
// Place a reset vector pointing to 0x0400
mem.0[0xFFFE] = 0x04;
mem.0[0xFFFF] = 0x00;
// Place a NOP at 0x0400
mem.0[0x0400] = 0x12;

let mut cpu = Cpu::new();
cpu.reset(&mut mem);
assert_eq!(cpu.registers().pc, 0x0400);
cpu.step(&mut mem);
assert_eq!(cpu.registers().pc, 0x0401);

Systems with peripherals implement both traits on the same type. The Memory trait is passed to the CPU, while Clocked::tick is called separately by the host loop. The preferred way to feed signals into the CPU is Cpu::apply_signals, which handles NMI edge detection internally:

let mut prev_signals = BusSignals::default();

loop {
    let cycles = cpu.step(&mut system);
    let signals = system.tick(cycles);

    // RESET is handled before apply_signals so a held-RESET pin keeps the
    // CPU quiescent and is not confused with a regular interrupt transition.
    if signals.contains(BusSignals::RESET) {
        cpu.reset(&mut system);
        prev_signals = BusSignals::default();
        continue;
    }

    // Only call into the CPU when something actually changed on the bus.
    if signals != prev_signals {
        cpu.apply_signals(signals, prev_signals);
        prev_signals = signals;
    }

    if cpu.halted() { break; }
    if cpu.illegal() {
        // Optional host policy: stop, log, or ignore.
    }
}

The individual cpu.set_irq(), cpu.set_firq(), and cpu.trigger_nmi() methods are still available for simpler setups where NMI edge detection is handled by the caller.

Behavior notes

  • Illegal opcodes set Cpu::illegal() but do not halt the CPU. This matches the default 6809-style execution model and leaves trap/stop policy to the host.
  • Repeated page-prefix chaining (0x10/0x11 after an initial page prefix) is intentionally not implemented. Only a single leading page prefix is recognised.

Building and testing

  • Build: cargo build (run in the workspace or this crate)
  • Test: cargo test

Contributing

  • Contributions, bug reports and improvements are welcome — open an issue or pull request in the main repository.