An emulator for the Nintendo Entertainment System made using zig and raylib. The emulation is cycle accurate (as accurate as I could make it), and the code well commented. A secondary goal of this project is to be a learning resource for anyone wanting to write their own emulator.
NOTE: This repo is compatible with Zig v0.12.1. Should also work with Zig <= 0.14.0, but I haven't tested it.
Make sure you have a ROM file in the iNES format. Some people download ROMs from piracy websites such as this one. Of course, I would never recommend that!
To launch a ROM from the command line:
nez path/to/rom.nes
To see debug info while playing the games:
nez --debug path/to/rom.nes
Controls:
Controller | Keyboard |
---|---|
A | Q |
B | E |
Start | Enter |
Select | X |
Up/Down/Left/Right | Arrow keys |
An 8-bit CPU used in a wide variety of computers, the most prominent of them being the NES. It is a derivative of the MOS-6502 (almost an identical clone), without support for decimal mode.
Resources:
- Famicom party - This book helped me write my own homebrew ROMs to test.
- The Obelisk 6502 instruction set reference
- Masswerk - 6502 instruction set
- The ultimate 6502 reference
Things to watch out for:
- Zero page wrap around for instructions that use the zero page addressing mode.
- RMW (Read-modify-write) instructions like
ROR
will first write the unmodified byte to the memory location, and then write the modified byte. This can make a difference with some mappers. - The CPU and PPU must run in sync. For a single CPU cycle, the PPU executes (roughly) three clock cycles. A good way to emulate this is to use the delta time between two frames, and then figure out how many cycles each chip should execute based on their respective clockspeeds.
If you're feeling adventurous, and want to write a cycle stepped emulator, then this datasheet can prove to be useful.
Resources:
- PPU - Nesdev wiki
- You'll have to go over the documents dozens of times before things start making sense. There is a lot of information to take in.
- NES PPU Notes
- Austin Morlan - NES rendering overview
- Famicom party
Every game comes in a cartridge that contains the game code, assets, and sometimes extra hardware and battery.
A mapper is a blanket term that describes all kinds of extra hardware present in the circuit that extend the capabilities of the game console.
The Bus connects the CPU, PPU, and the Cart, allowing separate components to communicate with each other.
Clone the repository, then:
zig build run
This will open a custom ROM I wrote to debug the emulator.
To play games, make sure you have a .nes
file somewhere, and use:
zig build run -- <path-to-rom>
The CPU has about ~10k test cases for each instruction, coming from this awesome test repository.
To run the CPU tests, use zig test src/cpu.zig
.
- Support vertical scrolling
- Sprite zero hit
- Support horizontal scrolling
- Support split scrolling
- Add support for the controller
- Support more mappers
- CNROM
- UxROM
- MMC1 (Some minor bugs remain, many games playable)
- MMC3
- MMC5
- Add support for the APU.
- Sprite overflow detection (very few games use this feature so not that high priority)
I started out wanting to write a fantasy console, but didn't want to invent another programming language, compiler, assembler, and then write ROMs in those. Emulating an existing console sounded like an equally challenging, but more fruitful venture.