Skip to content

A simple and clean NES emulator, written in Rust. Fast, lightweight, and with high compatibility.

License

Notifications You must be signed in to change notification settings

Comba92/nen-emulator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Nen Emulator

A simple and clean™️ cycle accurate NES emulator, written in Rust. Fast, lightweight, and with high compatibility.

nen_emu_preview.mp4

Download

Download is avaible in the release section. A SDL2 forntend for the emulator is avaible, with very basic user functionality, such as pausing, muting, single savestate save/load, and cartridge-ram savig to disk. A (WIP) WASM frontend is also avaible here: https://comba92.github.io/nen-emulator/frontend-wasm/index.html

Usage

Game ROMs can be loaded by dragging and dropping the files into the window.

Tip

Zip files are supported.

Note

ROMS with iNes or NES2.0 headers are supported.

The terminal window shows basic informations and warnings, such as the ROM information.

Controls

Tip

Both keyboards and controllers are supported.

Tip

A save/load state feature is avaible.

Note

By default, the 8 sprite limit per scanline is disabled, but can be enabled.

Keyboard Button
A A button
S B button
W Start
E Select
ArrowKeys You know what they do!
Space Pause/unpause the emulator
R Reset current game
M Mute/unmute sound
9 Save state
0 Load state
1 Toggle 8 sprites limit per scanline

Compatibility

The emulator supports mostly all the basic NES features you'd expect from a NES emulator.

  • The emulator is cycle accurate. A list of tests coverage is avaible here.

  • The 6502 CPU is emulated with most of its quirks.

  • The PPU is emulated as a pixel renderer, and emulates the LoopyRegister behaviour.

  • The pixel fethcer is emulated only for the backrounds. Object sprites are all fetched in one go, then mixed with the backround pixels one by one.

  • The APU channels are all fully emulated.

  • Both NTSC and PAL games are supported.

  • Games with tricky and obscure behaviour run correctly, except for one or two exceptions. For more information:

  • BATTLETOADS & BATTLETOADS 2 RUN!

  • All nametable mirrorings are supported.

  • iNes and NES2.0 headers are supported.

  • Zip files are supported.

  • Saving/loading of battery RAM when the game is changed or the emulator is closed.

  • Savestates

  • Resetting works, but some games require you to hold the down the reset button a few seconds

Warning

  • Headerless games are not supported.

Games compatibility list

I haven't kept track of a game compatibility list, but most of the development was driven by testing random games and beign sure they could boot, and run correctly for a minute or two. Right now, most games I've tried, popular and what not, are all running correctly. You are free to try some games and inform me about any issue!

Supported Mappers

The most popular

Other mappers

Building

The emulator is served as a stand-alone Rust library. It provides a basic API in src/nes.rs, which can be used by any frontend. (TODO: move the Nes struct to lib.rs)

Building requires, of course, Rust and it's development tools. To build the emulator library, simply use in the root folder:

cargo build -r

Two frontends are avaible. The SDL2 frontend, in frontend-native. To build, again, it's simply:

cargo build -r                   # Dynamically linked with SDL2
cargo build --features="static"  # Statically linked with SDL2

The WASM frontend is still WIP, but already avaible here: https://comba92.github.io/nen-emulator/frontend-wasm/index.html It is missing audio playback, savestates, and with a lackluster UI.

Architecture

Dependency tree

The emulator is organized as a tree (sort of) of dependencies. The root is the CPU. which contains the BUS, and which in turn contains all the peripherals:

  • PPU
  • DMA
  • APU
  • Cartridge/Mapper
  • Joypads

The cartridge is the only exception, as it is shared with multiple peripherals. Rust's typing system makes it hard to create circular dependencies of pointers, so the Cartride object has to be wrapped in a RefCell for interior mutability (giving us the ability to mutate it anywhere), and then in a Rc, for shared ownership.

This approach proved to be solid, safe, and reliable, but at the cost of flexibility. The architecure had to be changed from its roots multiple times, as most of the features and roadblocks weren't took into account since the beginning. This is the heart of software development though. You can never plan in advance how the system can be designed efficently.

An alternative solution would be having a global "god struct" (as the folks at emudev discord like to call), which contains a general emulation context, which is passed to every function. This increases flexibility, as there are fewer restrictions, but with a less cohesive design.

As the NES is a relatively simple machine, restricting the design to a dependency tree proved to be beneficial. Everything is tightly coupled, and the codebase is manageable. Adding more user features is a pain, tho.

TODO: detailed explanation of the architecture. ;)

What's missing

  • Famicon Disk System Support

  • RAM random initializing for games which uses it to seed RNG

  • Custom keybindings

  • Custom palettes

  • Headerless ROMs support

Various resources

This section contains some of the resources I've used during development. Sadly I didn't keep track of all of them. I did found a lot of interesting articles, blogs, and readings, but forgot to add them here.

CPU

Illegal Opcodes

Correct XAA

Memory

Ppu

Mappers

APU

Tests

About

A simple and clean NES emulator, written in Rust. Fast, lightweight, and with high compatibility.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages