[−][src]Crate tetanes
Summary
photo credit for background: Zsolt Palatinus on unsplash
TetaNES
is an emulator for the Nintendo Entertainment System (NES) released in 1983, written
using Rust, SDL2 and WASM.
It started as a personal curiosity that turned into a passion project. It is still a work-in-progress, but I hope to transform it into a fully-featured NES emulator that can play most games as accurately as possible. It is my hope to see a Rust emulator rise in popularity and compete with the more popular C and C++ versions.
TetaNES
is also meant to showcase how clean and readable low-level Rust programs can be in
addition to them having the type and memory-safety guarantees that Rust is known for. Many useful
features of Rust are leveraged in this project including traits, trait objects, generics, matching,
and iterators.
Try it out in your browser!
Screenshots
Mappers
Support for the following mappers is currently implemented or in development:
# | Name | Example Games | # of Games1 | % of Games1 |
---|---|---|---|---|
000 | NROM | Bomberman, Donkey Kong, Super Mario Bros. | ~247 | 10.14% |
001 | SxROM/MMC1 | Metroid, Legend of Zelda, Tetris | ~680 | 27.91% |
002 | UxROM | Castlevania, Contra, Mega Man | ~269 | 11.04% |
003 | CNROM | Arkanoid, Paperboy, Pipe Dream | ~155 | 6.36% |
004 | TxROM/MMC3 | Kirby's Adventure, Super Mario Bros. 2/3 | ~599 | 24.59% |
005 | ExROM/MMC5 | Castlevania 3, Laser Invasion | ~24 | 0.99% |
007 | AxROM | Battletoads, Marble Madness | ~75 | 3.08% |
009 | PxROM/MMC2 | Punch Out!! | 1 | <0.01% |
~2050 | 84.11% |
Dependencies
Installation
This should run on most platforms that supports Rust and SDL2, howeer, it's only being developed and tested on macOS at this time. So far, I've tested on macOS High Sierra, Mojave, Windows 7, Windows 10, Linux (Fedora and Ubuntu), and Raspberry Pi 4 (though performance lacking). When 1.0.0 is released, I'll make binaries available for all major platforms. Until then, follow the below instructions to build for your platform.
-
Install Rust (follow the link)
-
If Rust is already installed. Ensure it's up-to-date by running:
$ rustup update
-
-
Install SDL2 development libraries (follow the link)
-
Linux and macOS should be straightforward
-
Windows makes this a bit more complicated. Be sure to follow the above link instructions carefully. For the simple case of using
rustup
, all of the files inlib\
from the Visual C++ 32/64-bit development zip should go in:C:\Users\{Your Username}\.rustup\toolchains\{current toolchain}\lib\rustlib\{current toolchain}\lib
Where
{current toolchain}
will likely havex86_64-pc-windows
in its name. then a copy oflib\SDl2.dll
needs to go in:%USERPROFILE%\.cargo\bin
Next to the
tetanes.exe
binary.
-
-
Download & install
TetaNES
. Stable releases can be found on theReleases
tab at the top of the page. To build directly from a release tag, follow these steps:
$ git clone https://raw.githubusercontent.com/lukexor/tetanes.git
$ cd tetanes/
$ git checkout v0.6.0
$ cargo install --path ./
This will install the v0.6.0
tagged release of the TetaNES
binary to your cargo
bin directory
located at either $HOME/.cargo/bin/
on a Unix-like platform or %USERPROFILE%\.cargo\bin
on
Windows. Replace the release tag with the one you want to install. The latest is recommended. You
can see which release tags are available by clicking the Releases
tab at the top of this page or
by running the following command from the checked out git repository:
$ git tag -l
Usage
For each platform, the first cd
command may not be needed depending on the contents of your $PATH
environment variable. filename
should be replaced by the path to your game ROM ending in nes
.
At present, only the iNES format is fully supported,
but NES 2.0 support is coming.
Windows
$ cd %USERPROFILE%\.cargo\bin
$ tetanes.exe {filename}
macOS/Linux
$ cd $HOME/.cargo/bin/
$ tetanes {filename}
Additional Options
USAGE:
tetanes [FLAGS] [OPTIONS] [--] [path]
FLAGS:
-c, --clear-savestate Removes existing savestates for current save-slot
--concurrent-dpad Enables the ability to simulate concurrent L+R and U+D on the D-Pad.
-d, --debug Start with the CPU debugger enabled and emulation paused at first CPU instruction.
-f, --fullscreen Start fullscreen.
-h, --help Prints help information
-r, --record Record gameplay to a file for later action replay.
--rewind Enable savestate rewinding
--savestates-off Disable savestates
--sound-off Disable sound.
-V, --version Prints version information
--vsync-off Disable vsync.
OPTIONS:
-g, --genie-codes <genie-codes>... List of Game Genie Codes (space separated).
-p, --replay <replay> Replay a saved action replay file.
--savestate-slot <save-slot> Set savestate slot #. [default: 1] [possible values: 1, 2, 3, 4]
-s, --scale <scale> Window scale [default: 3]
--speed <speed> Increase/Decrease emulation speed. [default: 1.0]
ARGS:
<path> The NES ROM to load or a directory containing `.nes` ROM files. [default: current directory]
Controls
Button | Keyboard | Controller |
---|---|---|
A | Z | A |
B | X | B |
A (Turbo) | A | X |
B (Turbo) | S | Y |
Start | Enter | Start |
Select | Right Shift | Back |
Up, Down, Left, Right | Arrow Keys | Left Stick/D-Pad |
There are also some emulator actions:
Action | Keyboard | Controller |
---|---|---|
Pause | Escape | Guide Button |
Help Menu* | F1 | |
Configuration Menu* | Ctrl-C | |
Open ROM* | Ctrl-O | |
Quit | Ctrl-Q | |
Reset | Ctrl-R | |
Power Cycle | Ctrl-P | |
Increase Speed 25% | Ctrl-= | Right Shoulder |
Decrease Speed 25% | Ctrl-- | Left Shoulder |
Fast-Forward 2x (while held) | Space | |
Set Save State Slot # | Ctrl-(1-4) | |
Save State | Ctrl-S | |
Load State | Ctrl-L | |
Rewind 5 Seconds | R | |
Stop Action Replay Recording | Shift-V | |
Toggle Music/Sound | Ctrl-M | |
Toggle CPU Debugger | Ctrl-D | |
Toggle Fullscreen | Ctrl-Return | |
Toggle Vsync | Ctrl-V | |
Toggle NTSC Filter | Ctrl-N | |
Toggle PPU Viewer | Shift-P | |
Toggle Nametable Viewer | Shift-N | |
Take Screenshot | F10 |
While the CPU Debugger is open (these can also be held down):
Action | Keyboard |
---|---|
Step a single CPU instruction | C |
Step over a CPU instruction | O |
Step out of a CPU instruction | Ctrl-O |
Step a single scanline | S |
Step an entire frame | F |
Toggle Live CPU Debug Updating | D |
*: Not yet Implemented
Note on Controls
Ctrl-(1-4) may have conflicts in macOS with switching Desktops 1-4. You can disable this in the keyboard settings. I may consider changing them to something else or making macOS use the Option key in place of Ctrl, but I'm not bothering with OS-specific bindings just yet.
Directories & Screenshots
Battery-backed game data and save states are stored in $HOME/.tetanes
. Screenshots are saved
to the directory where TetaNES
was launched from. This may change in a future release.
Powerup State
The original NES hardware had semi-random contents located in RAM upon powerup and several games
made use of this to seed their Random Number Generators (RNGs). By default, the binaries and build
steps for TetaNES
emulate randomized powerup RAM state. This shows up in several games such as
Final Fantasy, River City Ransom, and Impossible Mission II, amongst others. Not emulating this
would make these games seem deterministic when they weren't intended to be.
If you would like TetaNES
to provide fully deterministic emulated powerup state, you'll need to
build the binary from source using the following command:
$ cargo build --release --features no-randomize-ram
Building/Testing
To build the project, ensure the dependencies are installed as outlined in the Installation
section and then run cargo build
or cargo build --release
(if you want better framerates).
Unit and integration tests can be run with cargo test --features no-randomize-ram
. RAM
randomization is enabled by default since this more accurately reflects the original NES, but many
tests fail under this condition so it needs to be disabled. There are also several test roms that
can be run to test various capabilities of the emulator. They are all located in the tests/
directory.
Run them in a similar way you would run a game. e.g.
$ cargo run --release --features no-randomize-ram tests/cpu/nestest.nes
Debugging
There are built-in debugging tools that allow you to monitor game state and step through CPU
instructions manually. See the Controls
section for more on keybindings.
The Default debugger screen provides CPU information such as the statis of the CPU register flags, Program Counter, Stack, PPU information, and the previous/upcoming CPU instructions.
The Nametable Viewer displays the current Nametables in PPU memory and allows you to scroll up/down to change the scanline at which the nametable is read. Some games swap out nametables mid-frame.
The PPU Viewer shows the current sprite and palettes loaded. You can also scroll up/down in a similar manner to the Nametable Viewer. Super Mario Bros 3 for example swaps out sprites mid-frame to render animations.
Logging can be set by setting the RUST_LOG
environment variable and setting it
to one of trace
, debug
, info
, warn
or error
.
Troubleshooting
If you get an error running a ROM that's using the supported Mapper list above, it could be a corrupted or incompatible ROM format. If you're unsure which games use which mappers, see here. Trying other versions of the same game from different sources sometimes resolves the issue.
If you get some sort of nasty error when trying to start a game you've played before, try passing
the --clear-savestate
option to ensure it's not an incompatible savestate file causing the issue.
When 1.0 releases, I'll be much more careful about backwards breaking changes with regards to
savestate files, but for now it's highly volatile and due to the nature of how I serialize data,
I can only catch certain sorts of data inconsistencies.
If you encounter any shortcuts not working, ensure your operating system does not have a binding
for it that is overriding it. macOs specifically has many things bound to Ctrl-*
. There are plans
to allow keybind customization, but it's not finished yet.
If an an issue is not already created, please use the github issue tracker to create it. A good guideline for what to include is:
- The game experiencing the issue (e.g. Super Mario Bros 3)
- Operating system and version (e.g. Windows 7, macOS Mojave 10.14.6, etc)
- What you were doing when the error happened
- A description of the error and what happeneed
- Any console output
- Any related errors or logs
When using the browser version (not yet available), also include:
- Web browser and version (e.g. Chrome 77.0.3865)
Known Issues
See the github issue tracker.
Roadmap
The following is a checklist of features and their progress:
- [x] Console
- [x] NTSC
- [ ] PAL
- [ ] Dendy
- [ ] Headless mode
- [x] Central Processing Unit (CPU)
- [x] Official Instructions
- [x] Unofficial Instructions (Some still incorrect)
- [x] Interrupts
- [x] Picture Processing Unit (PPU)
- [x] VRAM
- [x] Background
- [x] Sprites
- [x] NTSC TV Artifact Effects
- [x] Emphasize RGB/Grayscale
- [x] Audio Processing Unit (APU)
- [x] Pulse Channels
- [x] Triangle Channels
- [x] Noise Channels
- [x] Delta Mulation Channel (DMC)
- [x] Inputs
- [x] Keyboard
- [x] Standard Controller
- [x] Turbo
- [ ] Zapper (Light Gun)
- [x] Memory
- [x] Cartridge
- [x] Battery-backed Save RAM
- [x] iNES Format
- [x] NES 2.0 Format (Can read headers, but many features still unsupported)
- [x] Mappers
- [x] NROM (Mapper 0)
- [x] SxROM/MMC1 (Mapper 1)
- [x] UxROM (Mapper 2)
- [x] CNROM (Mapper 3)
- [x] TxROM/MMC3 (Mapper 4)
- [x] ExROM/MMC5 (Mapper 5) (Split screen and sound is unfinished)
- [x] AxROM (Mapper 7)
- [x] PxROM/MMC2 (Mapper 9)
- [x] User Interface (UI)
- [x] PixEngine (Custom graphics library for handling video and audio)
- [x] UI Notification messages
- [x] SDL2
- [ ] WebAssembly (WASM) - Run TetaNES in the browser! (in progress!)
- [x] Window
- [ ] Menus
- [ ] Help Menu
- [ ] Open/Run ROM with file browser
- [ ] Configuration options
- [ ] Custom Keybinds
- [ ] Recent Game Selection
- [x] Pause
- [x] Toggle Fullscreen
- [x] Reset
- [x] Power Cycle
- [x] Increase/Decrease Speed/Fast-forward
- [x] Instant Rewind (5 seconds)
- [ ] Visual Rewind (Holding R will time-travel backward)
- [x] Save/Load State
- [x] Take Screenshots
- [x] Toggle Action Recording
- [ ] Sound Recording (Save those memorable tunes!)
- [x] Toggle Sound
- [x] Toggle Debugger
- [x] Game Genie
- [ ] WideNES
- [ ] 4-Player support
- [ ] Network Multi-player
- [ ] Toggle individual sound channels
- [ ] Self Updater
- [x] Testing/Debugging/Documentation
- [x] CPU Debugger (Displays CPU status, registers, and disassembly)
- [X] Step Into/Out/Over
- [ ] Breakpoints
- [ ] Memory Hex Debugger
- [x] PPU Viewer (Displays PPU sprite patterns and color palettes)
- [x] Nametable Viewer (Displays all four PPU backgrounds)
- [X] Scanline Hit Configuration (For debugging IRQ Nametable changes)
- [ ] Scroll lines (Automatically adjusts the scanline, showing live nametable changes)
- [x] Unit/Integration tests (run with cargo test)
- [x] CPU integration testing (with nestest)
- [ ] Other tests (Missing a lot here)
- [x] Test ROMs (most pass, many still do not)
- [ ] Automated rom tests (in progress now that action recording is finished)
- [ ] Rust Docs
- [ ] Logging
- [x] Console
- [ ] File
- [x] CPU Debugger (Displays CPU status, registers, and disassembly)
Documentation
In addition to the wealth of information in the docs/
directory, I also referenced these websites
extensively during development:
License
TetaNES
is licensed under the GPLv3 license. See the LICENSE.md
file in the root for a copy.
Contact
For issue reporting, please use the github issue tracker. You can contact me directly here.
Contributing
While this is primarily a personal project, I welcome any contributions or suggestions. Feel free to submit a pull request if you want to help out!
Credits
Implementation was inspiried by several amazing NES projects, without which I would not have been able to understand or digest all the information on the NES wiki.
I also couldn't have gotten this far without the amazing people over on the NES Dev Forums:
- blargg for all his amazing test roms
- bisqwit for his test roms & integer NTSC video implementation
- Disch
- Quietust
- rainwarrior
- And many others who helped me understand the stickier bits of emulation
Also, a huge shout out to OneLoneCoder for his NES and olcPixelGameEngine series as those helped a ton in some recent refactorings.
Modules
apu | Audio Processing Unit |
bus | |
cartridge | Handles reading NES Cartridge headers and ROMs |
common | Utils and Traits shared among modules |
cpu | A 6502 Central Processing Unit |
filter | Contains the Filter Trait for both High Pass and Low Pass filters |
input | NES Controller Inputs |
mapper | NES Memory Mappers for Cartridges |
memory | Memory types for dealing with bytes |
nes | User Interface representing the the NES Control Deck |
ppu | Picture Processing Unit |
serialization | Serialization/Deserialization for internal game state |
Macros
hashmap | |
map_nes_err | |
nes_err |
Structs
NesErr |
Type Definitions
NesResult |