Dendy
Dendy is a NES/Famicom emulator written in Go and named after the soviet Famicom bootleg. It serves no practical purpose other than to be a toy project for me, so do not expect it to beat any of the existing emulators in terms of performance or accuracy. Yet, it is capable of running most of the games I tried, so itβs not completely useless, and it's still a great learning experience.
Try it
$ go install github.com/maxpoletaev/dendy/cmd/dendy@latest
$ dendy romfile.nes
You may need to install additional dependencies required by raylib. See https://github.com/gen2brain/raylib-go#requirements for more details.
Controls
Joystick
Player 1 joystick is emulated using the keyboard. The default mapping is as follows:
ββ
βββββββββββββββββββββββββββββββββββββββββ
β β
β [W] β
β [A] [D] β
β [S] [J] [K] β
β [Enter] [RShift] β
β β
βββββββββββββββββββββββββββββββββββββββββ
Zapper (Light Gun)
Zapper is emulated using the mouse and can be used in games like Duck Hunt. Point the mouse cursor at the right position on the screen and click to shoot.
Multiplayer
Dendy can be played over the network with another player. Run the emulator with
the -listen=<host>:<port>
flag to start a netplay server that will be waiting
for the second player to connect via the -connect=<host>:<port>
flag. Once the
connection is established, the game will start for both sides. The player who
started the server will be controlling the first joystick.
The feature works by synchronizing the state of the emulated NES and sending the controller input over the network to the other player. The algorithm allows slight drifts in the clock speed and network latency by generating fake input events for the other player if the state of the emulator is ahead of the other playerβs state. Theoretically, this should keep the game playable for both players even if the network connection is not very stable (e.g. over the Internet), but it is not very well tested.
Most of the ideas for the netplay implementation were borrowed from RetroArch.
Status
CPU
- Official opcodes
- Unofficial opcodes
- Runtime disassembly
- Cycle-accurate emulation
- Accurate clock speed
- Interrupts
Graphics
- Background rendering
- Sprite rendering
- 8Γ16 sprites
- Palettes
- Scrolling
- Color emphasis
- Cycle-accurate emulation
Input/Output
- Graphics output
- Controller 1
- Zapper
Sound
TODO
Cartridges
The goal is to support top 7 mappers covering the majority of games. The percentage indicates the number of games that use the mapper according to nescartdb.com.
- MMC1 (Mapper 1) - 28%
- MMC3 (Mapper 4) - 24%
- UxROM (Mapper 2) - 11%
- NROM (Mapper 0) - 10%
- CNROM (Mapper 3) - 6%
- AxROM (Mapper 7) - 3%
- MMC5 (Mapper 5) - 1%
Test ROMs
The checked items are the ones that pass the tests completely or with minor inaccuracies (that might be caused by the test ROMs themselves).
- Nestest CPU
- Blarggβs CPU tests
- Blarggβs PPU tests
- Blarggβs APU tests
Resources
Although NES emulation is a pretty well-covered topic, It is still a very interesting and challenging project to work on. Here are some of the resources that I found particularly useful while writing this emulator. Big thanks to everyone who made them!
Documentation
- MOS 6502 CPU Reference by Andrew Jabobs, 2009
- Extra Instructions of the 65xx Series CPU by Adam Vardy, 1996
- NES Rendering Overview by Austin Morlan, 2019
- Making NES Games in Assembly by Kevin Zurawel, 2021
- Retroarch Netplay README
Videos
- The NES Emulator from Scratch series covers most of the topics from the CPU to the sound, but I found the two videos about the PPU to be the most useful for understanding the obscure details of the NES rendering pipeline: [1], [2].
Code
During bad times, itβs always nice to look at other peopleβs code to see how they solved the same problems. Here are some of the emulators written by other people that I often referred to when I was stuck: