Hasty Briefsbeta

Bilingual

Cycle-accurate 6502 emulator as coroutine in Rust

5 months ago
  • #rust
  • #coroutines
  • #emulation
  • A simple CPU emulator typically uses a loop with a switch statement to interpret opcodes, abstracting away timing details.
  • Timing abstraction can cause synchronization issues in systems with timing-sensitive components, like the NES CPU and PPU.
  • The solution is to implement the CPU as a state machine that can be stepped one cycle at a time, ensuring accurate synchronization.
  • This project demonstrates using Rust coroutines to define CPU emulation semantics, compiling it into a state machine for lockstep execution with the PPU.
  • The CPU state includes registers and an I/O data bus, with coroutines yielding I/O events for reads and writes.
  • Macros like `fetch!` and `write!` handle I/O events, simplifying the implementation of instruction semantics.
  • Example code shows running the CPU and PPU together in lockstep, with the CPU stepping every third cycle to match the PPU's clock rate.
  • Performance is sufficient, with the emulator running at ~470 MHz on modern hardware, far exceeding the NES's 1.79 MHz requirement.
  • The project includes extensive testing with Harte and Klaus test suites to ensure correctness.
  • Built with Rust nightly, the project may be unstable due to compiler changes, but it works with version 1.93.0-nightly.