m68k 0.3.2

A safe Rust M68000 family CPU emulator
Documentation
# m68k-rs

A safe, pure Rust implementation of the Motorola 68000 family CPU emulator.

One core for transaction-accurate hardware emulation and high-throughput
high-level emulation (HLE).

[![Rust CI](https://github.com/benletchford/m68k-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/benletchford/m68k-rs/actions/workflows/rust.yml)
[![Crates.io](https://img.shields.io/crates/v/m68k.svg)](https://crates.io/crates/m68k)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

## Features

- **Complete CPU family support**: M68000 through M68060, including EC/LC variants and the SCC68070
- **Two explicit execution contracts**: transaction-accurate cycle scheduling for hardware emulators, and an instruction-budgeted fast path for HLE
- **Bus-visible accuracy**: 68000/68010 two-word prefetch, model-specific access ordering, and internal clock synchronization through `AddressBus::sync`
- **Memory-safe core**: The interpreter — instruction semantics, decode, exceptions, MMU, FPU — is 100% safe Rust. The optional fast paths (fastmem batch execution and the trace JIT) use a small, contract-documented `unsafe` perimeter, fenced by step-vs-batch equivalence tests
- **FPU emulation**: Software 80-bit extended precision, packed decimal, and model-specific 68881/68882/68040/68060 behavior
- **MMU emulation**: 68030/68040/68060 translation, ATCs, transparent translation, `PTEST`, fault frames, and writeback
- **HLE-ready**: Built-in trap interception for High-Level Emulation
- **Save-state ready**: Optional `serde` support serializes architectural state while rebuilding runtime caches on load
- **Extensively tested**: Validated against multiple industry-standard test suites

## Quick Start

Add to your `Cargo.toml`:

```toml
[dependencies]
m68k = "0.3"
```

The default build has no JIT compiler dependency. Native applications that use
`run_batch()` can enable Cranelift compilation explicitly:

```toml
[dependencies]
m68k = { version = "0.3", features = ["jit"] }
```

### Basic Usage

```rust
use m68k::{CpuCore, CpuType, AddressBus, StepResult};

// Implement your memory bus
struct MyBus { memory: Vec<u8> }

impl AddressBus for MyBus {
    fn read_byte(&mut self, addr: u32) -> u8 {
        self.memory.get(addr as usize).copied().unwrap_or(0)
    }
    fn write_byte(&mut self, addr: u32, val: u8) {
        if let Some(m) = self.memory.get_mut(addr as usize) { *m = val; }
    }
    fn read_word(&mut self, addr: u32) -> u16 {
        u16::from_be_bytes([self.read_byte(addr), self.read_byte(addr + 1)])
    }
    fn write_word(&mut self, addr: u32, val: u16) {
        let bytes = val.to_be_bytes();
        self.write_byte(addr, bytes[0]);
        self.write_byte(addr + 1, bytes[1]);
    }
    fn read_long(&mut self, addr: u32) -> u32 {
        ((self.read_word(addr) as u32) << 16) | self.read_word(addr + 2) as u32
    }
    fn write_long(&mut self, addr: u32, val: u32) {
        self.write_word(addr, (val >> 16) as u16);
        self.write_word(addr + 2, val as u16);
    }
}

fn main() {
    let mut cpu = CpuCore::new();
    cpu.set_cpu_type(CpuType::M68000);

    let mut bus = MyBus { memory: vec![0; 0x10000] };

    // Set up vectors: SSP at 0x1000, PC at 0x400
    bus.write_long(0, 0x1000);
    bus.write_long(4, 0x400);

    // Write a NOP instruction at 0x400
    bus.write_word(0x400, 0x4E71);

    cpu.reset(&mut bus);

    loop {
        match cpu.step(&mut bus) {
            StepResult::Ok { cycles } => println!("Executed: {} cycles", cycles),
            StepResult::Stopped => break,
            StepResult::AlineTrap { opcode } => println!("A-line trap: {:04X}", opcode),
            StepResult::FlineTrap { opcode } => println!("F-line trap: {:04X}", opcode),
            StepResult::TrapInstruction { trap_num } => println!("TRAP #{}", trap_num),
            StepResult::Breakpoint { bp_num } => println!("BKPT #{}", bp_num),
            StepResult::IllegalInstruction { opcode } => println!("Illegal instruction: {:04X}", opcode),
        }
    }
}
```

### High-Level Emulation (HLE)

Intercept traps for OS emulation or debugger integration with CPU/bus access:

```rust
use m68k::{AddressBus, CpuCore, HleHandler};

struct MacToolbox;

// All methods in HleHandler are optional (default return is false).
// Return `true` to indicate the HLE handled the trap (suppressing the hardware exception).
// Return `false` to let the CPU take the standard hardware exception.
impl HleHandler for MacToolbox {
    // Optional: Intercept A-line traps (0xAxxx)
    fn handle_aline(
        &mut self,
        cpu: &mut CpuCore,
        bus: &mut dyn AddressBus,
        opcode: u16,
    ) -> bool {
        println!("A-line trap: {:04X} at PC=0x{:08X}", opcode, cpu.pc);
        // ... implement HLE logic ...
        true // Handled: do NOT take the standard Line-A exception
    }

    // Optional: Intercept TRAP #n instructions
    fn handle_trap(&mut self, _cpu: &mut CpuCore, _bus: &mut dyn AddressBus, trap: u8) -> bool {
        // Example: Only intercept TRAP #0, let #1-15 go to real hardware vectors
        if trap == 0 {
            println!("OS Call (TRAP #0)");
            true // Handled
        } else {
            false // Not handled: CPU will take exception vector 32+n
        }
    }

    // Optional: Intercept F-line traps (coprocessor instructions)
    fn handle_fline(&mut self, _cpu: &mut CpuCore, _bus: &mut dyn AddressBus, opcode: u16) -> bool {
        println!("Generic Coprocessor instruction: {:04X}", opcode);
        true // Handled
    }

    // Optional: Intercept BKPT #n instructions
    fn handle_breakpoint(&mut self, _cpu: &mut CpuCore, _bus: &mut dyn AddressBus, bp: u8) -> bool {
        println!("Breakpoint #{}", bp);
        true // Handled
    }

    // Optional: Intercept ILLEGAL instructions (0x4AFC)
    fn handle_illegal(&mut self, _cpu: &mut CpuCore, _bus: &mut dyn AddressBus, opcode: u16) -> bool {
        println!("Illegal instruction: {:04X}", opcode);
        false // Not handled: CPU will take illegal instruction exception
    }
}

fn emulate(cpu: &mut CpuCore, bus: &mut impl AddressBus) {
    let mut hle = MacToolbox;
    let result = cpu.step_with_hle_handler(bus, &mut hle);
}
```

### Choosing an Approach

| Method | Budget | Execution contract | Host-visible exit |
| :--- | :--- | :--- | :--- |
| **`step()`** | One instruction | Transaction-accurate `AddressBus` accesses and cycles | Surfaces A-line, F-line, `TRAP`, `BKPT`, and illegal instructions without taking their exception |
| **`step_with_hle_handler()`** | One instruction | Same precise path as `step()` | Offers traps to `HleHandler`; unhandled traps take the hardware exception |
| **`execute()`** | CPU cycles | Precise path; whole instructions may overshoot the requested cycles | Takes traps as hardware exceptions and returns consumed cycles |
| **`run_for_cycles()`** | CPU cycles | Precise path with actual cycle and instruction totals | Surfaces traps like `step()` and reports STOP separately |
| **`run_batch()`** | Instructions | Throughput path using decoded-op caching, optional direct RAM, and portable or `jit`-enabled native hot-loop traces | Surfaces traps, STOP, watched PCs, or budget exhaustion |

Use **`step()`** for debugger-style control. Use **`run_for_cycles()`** when a
machine scheduler needs to advance the CPU by a clock budget without losing
bus ordering or trap state:

```rust
use m68k::CycleBatchExit;

let result = cpu.run_for_cycles(&mut bus, 512);
match result.exit {
    CycleBatchExit::BudgetExhausted => {
        // result.cycles may be greater than 512: instructions are never split.
    }
    CycleBatchExit::Stopped => {
        // Wait for a serviceable interrupt.
    }
    event => {
        // Handle a surfaced trap. The trapping instruction is not included
        // in result.instructions and no exception-entry cycles were charged.
        println!("{event:?}");
    }
}
```

Use **`step_with_hle_handler()`** when patching selected guest OS calls while
allowing every unhandled trap to follow hardware behavior. Use
**`run_batch()`** for HLE workloads where host-call latency and instruction
throughput matter more than observing the physical prefetch bus.

## Supported CPU Types

| CPU        | Description                            |
| ---------- | -------------------------------------- |
| `M68000`   | Original 68000 (24-bit address bus)    |
| `M68010`   | 68010 with virtual memory support      |
| `M68EC020` | 68020 embedded controller (no MMU)     |
| `M68020`   | Full 68020 with 32-bit address bus     |
| `M68EC030` | 68030 embedded controller (no MMU)     |
| `M68030`   | Full 68030 with on-chip MMU            |
| `M68EC040` | 68040 embedded controller (no FPU/MMU) |
| `M68LC040` | 68040 lite (no FPU)                    |
| `M68040`   | Full 68040 with FPU and MMU            |
| `M68060`   | Superscalar 68060 with FPU and MMU     |
| `SCC68070` | Philips SCC68070 variant               |

## Validation & Testing

This emulator has been rigorously validated against multiple industry-standard test suites to ensure correctness:

### SingleStepTests (m68000)

The [SingleStepTests](https://github.com/SingleStepTests/m68000) project
provides exhaustive per-instruction fixtures from MAME's microcoded 68000 core.
The suite covers every supplied instruction file and 261,894 cases, including:

- All addressing modes and operand sizes
- Edge cases for condition codes (CCR/SR)
- BCD arithmetic (ABCD, SBCD, NBCD)
- Multiply/divide overflow handling
- Exception frame generation
- **Cycle and transaction auditing**: all 261,894 cases match their fixture cycle and bus-access totals, with separate ignored audit tools for access sequence and per-access timing analysis

### Musashi Reference Implementation

We also run binaries from [Musashi](https://github.com/kstenerud/Musashi), a
widely deployed M68000 emulator. The integration tests:

- Execute complete Musashi test binaries
- Verify register state, memory contents, and exception handling
- Cover 68000 through 68040 instruction sets
- Explicitly exclude legacy cases whose undefined BCD flags or illegal
  68000 encodings conflict with the hardware-oriented SingleStepTests model

### Cross-CPU Verification

Additional test suites verify behavior across CPU generations:

- **FPU tests**: 80-bit arithmetic, transcendental functions, packed decimal, memory operands, rounding modes, and save/restore
- **MMU translation tests**: 68030/68040/68060 table walks, ATCs, TTR matching, page crossings, fault frames, and writeback
- **Privilege tests**: User/supervisor mode transitions, TRAP behavior
- **Exception tests**: Per-model frames, resumable bus faults, double faults, and address errors
- **Execution-path differential tests**: Cold/warm cache state, self-modifying code, cycle batches, fast batches, and native traces

### Test Coverage

```
tests/
├── singlestep_m68000_v1_tests.rs  # Exhaustive 68000 fixture suite
├── musashi_tests.rs               # Musashi integration binaries
├── m68020_tests.rs ...            # Generation-specific behavior
├── m68060_tests.rs                # 68060 integer, FPU, and MMU behavior
├── fpu_accuracy.rs                # Extended-precision differential tests
├── run_for_cycles_tests.rs        # Precise cycle-batch boundary contract
├── run_batch_tests.rs             # HLE fast-path equivalence and exits
└── fixtures/
    ├── m68000/                    # External SingleStepTests checkout
    └── Musashi/                   # Musashi reference binaries
```

## Architecture

```
m68k/
├── core/           # CPU state, interpreter, timing, caches, and trace JIT
├── dasm/           # Disassembler
├── fpu/            # 80-bit FPU, packed decimal, and transcendental operations
└── mmu/            # 68030/68040/68060 translation and ATCs
```

### Key Types

| Type | Description |
| :--- | :--- |
| `CpuCore` | Main CPU state and execution APIs |
| `CpuType` | CPU model selection enum |
| `AddressBus` | Extensible memory, device, fetch-cache, timing, and fast-RAM contract |
| `LinearMemoryBus` / `FastMem` | Ready-made flat memory and optional direct-RAM window |
| `HleHandler` | Trap interception callbacks |
| `StepResult` | Single-instruction result |
| `CycleBatchResult` / `CycleBatchExit` | Precise cycle-scheduled execution result |
| `BatchResult` / `BatchExit` | High-throughput instruction-batch result |
| `CpuCore::is_stopped()` | STOP state check |
| `CpuCore::is_halted()` | Double-fault halt check |

## Performance

Accuracy and throughput are separate, deliberate contracts:

- **Precise execution**`step`, `step_with_hle_handler`, `execute`, and
  `run_for_cycles` use the interpreter and ordinary `AddressBus` calls. These
  paths preserve bus-visible prefetch, access ordering, internal-clock
  synchronization, fault state, and model-specific cycle accounting.
- **Throughput execution**`run_batch` reuses decoded operations and executes
  eligible hot backward-branch traces through a portable micro-op loop. The
  opt-in `jit` feature compiles those traces with Cranelift on native targets.
  A bus may expose a contiguous `FastMem` window to keep eligible RAM operands
  inside the trace. Guarded exits and self-modifying-code checks fall back
  without partially committing an instruction.

The two paths share instruction semantics and are continuously compared in
cold-cache, warm-cache, memory, control-flow, and fault tests. This keeps the
hardware contract simple for machine emulators while allowing HLE systems to
opt into lower dispatch overhead explicitly.

## License

MIT License - see [LICENSE](LICENSE) for details.

## Contributing

Contributions are welcome! Please ensure:

1. All tests pass: `cargo test`
2. No clippy warnings: `cargo clippy -- -D warnings`
3. Code is formatted: `cargo fmt`

## Acknowledgments

- [Musashi]https://github.com/kstenerud/Musashi - Reference implementation and test fixtures
- [SingleStepTests]https://github.com/SingleStepTests/m68000 - Exhaustive instruction test vectors
- The M68000 Programmer's Reference Manual