Sep 19, 2019 | 10 minute read
I decided several months ago that it'd be a neat idea to create my own NES emulator using my new favorite language: Rust. Little did I know how complicated it would be or how much of my soul would be required (and I'm not even finished!). My story starts shortly after I registered for my final few courses finishing my Bachelors of Computer Science. One of those courses was called "Rust Programming". In preparation for the course, I decided I should learn a bit about it before class started because I had only heard about it in passing. I immediately fell in love with it.
Now, don't get me wrong, there were certainly growing pains. There were some new ideas to get used to like borrow checking and lifetimes, but I slowly managed to get used to things and got really excited for class to begin. One of the course requirements was completing a decently sized code project that would span several weeks, and thus I began my search for the perfect Rust project. I've been into classic video games for years now and have played several emulators over that time, so I figured that might be a solid place to start. When I discovered there were barely any fully functional NES emulators written in Rust, I decided this would be the perfect project. The NES is an old platform so it should be simple to emulate right? Right. Little did I know...
This project has been a labor of love, with much pain and frustration mixed with the joy and satisfaction. I'd like to share some of my journey in, what will likely turn out to be, a many part series. My intention is to highlight the many benefits of embarking on such a large project and perhaps help caution against some of the pitfalls. I've learned so much from this endeavor and expanded my ability as a programmer. Also, while I understand why the NES Dev wiki discourages new emulator development, I highly encourage anyone interested to either start their own as just a toy project, or help collaborate on an existing project as it is highly informative and rewarding.
You can check out the code for this project on github and play it on the web!
Here are some of the things this series will cover:
There are currently 7 mappers completely implemented with 1 more in progress. I'll cover more on what mappers are later, but these 8 mappers will allow around 84% of NES games to be playable. Many of the remaining games are Japanese released games, obscure games, or homebrew games. These mappers include many popular titles including all of the Super Mario games, Zelda, Metroid, Final Fantasy, and Castlevania among many others.
Current Features:
Future Features:
Code Statistics
Overall, I'm very happy with the project and its performance, but I'm a perfectionist and there's still a lot to do. There are many crusty corners that I'd like to substantially refactor. Frustration got the better of me at times and in an attempt to just get things working, I left my "best practices" at the door. Rust was a fantastic choice for this project, especially once you get past the strict constraints Rust puts on you. I certainly struggled a lot early on in development because Rust limits variable ownership and mutable state and the prime thing this project is attempting to emulate is hardware where everything is globally mutable.
The NES hardware is composed of the following six components:
The architecture I've chosen groups the CPU, PPU, and APU components inside of a Console module which sits alongside of Input, Memory, and Mapper modules. The current UI module uses the Rust SDL2 libraries to handle the windows, input, graphics and audio. This will be updated to be swappable with a web-assembly implementation in a future version to allow playing the emulator in a modern browser.
In Part 2, I'll go into the details of my implementation and some of the major insights learned along the way.