This is so cool; what a great read.
I'm working on a Gameboy emulator in Rust at the moment and I'm in the same boat as the author: I know it has been done before; _but I've never done it before!_
It's awesome how the Gameboy kept me entertained for hundreds and hundreds of hours as a kid. -- Here I am over a decade later, and debugging my Gameboy [emulator] has kept me up into the wee hours of the night. (On more than one occasion, I might add.)
---
Funny thing is I was actually going to write a NES emulator. On new-years day I decided I really wanted to write an emulator that could play the original MOTHER.
After a little bit of cursory research: the Gameboy hardware seemed a tad more approachable. To be honest that's not terribly surprising: obviously to fit a game console in such a small package (for the time) you had to make a lot of sacrifices.
I'm in the process of doing the same thing. Instead of using Rust I'm going to try to use Go. Would you mind sharing any resources that you found to help write the emulator?
This walkthrough[1] writing a Gameboy emulator in Javascript was very helpful in seeing how an emulator should be structured. -- Though the full source is available: the actual tutorial is more of a guideline that introduces you to the hardware and structure of the emulator. It often shows code-snippets instead of working code.
The Gameboy CPU Manual[2] and OP code chart[3] have been invaluable tools. In my experience: I spent most of my time flipping between the opcode chart and my CPU implementation.
As a tip: lots of the CPU instructions do the same thing w/ different register pairs. There is huge potential for code reuse here. In Rust I achieved it with macros, though.
Lastly: the "Pan Docs"[4] are more or less the same thing as the CPU Manual, but in a slightly more readable format.
Also there[5] are some test ROMs[6] that you can use for debugging.
I'm trying to find a good way to verify their results using Rust's unit testing.
`opus5.gb`[6] is a nice way to test the graphics subsystem; it's also a good "first ROM" to run because it has no additional ROM or RAM in the cartridge. In other-words: you don't need to implement the memory bank controllers to get it working.
---
And a little bit of friendly advice: _always_ keep debugging in the back of your mind. I usually resort to "printf debugging" but that really doesn't work inside a loop that's supposed to be ticking at 4.19MHz! :)
I accidentally filled up my disk once already; I forgot I had redirected STDERR to a file and it was pretty-printing registers after every tick. Yikes!
I have been reading through the emulator in Javascript walkthrough. It does a good job at explaining the basics. I'm not very far into development, still trying to understand the basics of the CPU. The manual and opcode chart you posted will help out a lot with that.
Thanks for the tip about the CPU instructions. I did notice that, but when looking through the Javascript code there was a one to one relationship with opcodes to instructions. Wasn't sure why the person went that way, maybe because it was easier? Either way I will keep that in mind and try to do code reuse!
Test ROMs are amazing! I have been playing around with a few ROMs I have and they all seem very complicated. This should make it easier to determine if I'm on the right track or not.
With the little code I have written, 90% of it is Printf to the screen to see what is going on :).
I do want to share with you this other tutorial that I found [1]. It is similar to the Javascript one, but it goes into way more detail on some parts.
Thanks very much for all the help! I have a lot of reading to do this weekend!
Seeing the Pokemon boot screen reminded me, I forgot the project which made me start down this road. I don't know if you've seen gomeboy-color[1]? It's an emulator written in Go. The picture of Pokemon Yellow on their homepage is what made me decide I wanted to emulate the Gameboy and not the NES.
It also inspired me to build an interactive debugger that understands the Gameboy memory map. It's _pretty sweet_ when you can dump the 32x32 tile-map as a nice grid of tile offsets.
@drbawb and @bpowell , if you guys can blog (or take notes) about your experiences building these emu's it would be a great.. It is a topic I would definitely read/follow, I grew up on Nintendo/Gameboy and now reading how you chaps are recreating my childhood addictions via emulation is just wonderful.
I have been keeping notes as I learn new things. Though they are more of a personal journal than anything.
I'll think about cleaning them up and publishing them once I get the source up. I just get uneasy about publishing stuff like this. It just never feels like my prose [or my code] is "good enough" for the world to see.
I want to keep it all tucked away: so no one will make fun of my primitive debugger, or how I use hardtabs for indentation![1]
[1]: The Rust style guide dictates the opposite. They can pry the tab key from my cold dead pinky!
I plan on blogging about it once I get more into it. I have just started doing some basic programming and reading up on how the GameBoy works.
I never grew up! I still play my old GameBoy Color when I go on long trips. I know there are many emulators out there for these old systems, but I want to know how they work and figured if I'm still playing with one, let's make one!
I haven't added audio support yet, and it isn't very go-gettable (yet). But! the code is reasonably well commented, and I have played through much of Super Mario with it.
They did use generics via Go's research into "explicit generics." Sort of like Dependency Injection, Go's Explicit Generics allow you to "wire up" types in "code files" for proper use later at run time.
Out of curiosity: can anyone w/ the relevant experience explain why older consoles like the NES and the GB have "mirrors" in their memory map? I haven't programmed embedded systems so I really don't understand what the advantage might be.
I thought the Gameboy was odd enough with it's 'echo' of WRAM. (Which isn't even a complete mirror; it stops partway through WRAM#1.)
Reading the NES memory map though it gets quite silly! There's a mirror ($2008-$3FFF) that repeats a set of 8 registers ($2000-2007) _every 8 bytes._
Was this simply a matter of having more address space than physical memory? Or were these mirrors actually useful when programming these consoles?
---
I thought it might have to do w/ different addressing modes; perhaps you could save some bytes by just storing offsets or something. But that makes no sense in the context of the Gameboy. Many of the z80's extended instructions are gone; the only "interesting" addressing mode is a handful of instructions which treat an 8-bit address as an offset from $FF00.
If you notice, that third most significant bit is the only difference - and this is true the entire way through the mirror. Because that address line wasn't needed to access WRAM, it was probably never even wired to it - so on access the value of it is simply ignored.
Came in here to make the same comment. I used to do some work with the author around the time he was writing it. There was much talk about playing Battle Toads in their break room.
Niels Widger, the author of this emulator, was the hardest working guy in the emulation community, and single-handedly kept Zophar's Domain alive for years. It's awesome that his love of emulation hasn't died more than a decade later.
Is the language really that important to the end product? I know we like to write in our favourite languages or the language we think in, but when you use software daily, do you only use software written in C, for example? Do you refuse to use anything written in C++?
Not directly, but very possibly indirectly because of the people a new language attracts: PG already touched on it: http://www.paulgraham.com/pypar.html
You can do one command, `go get -u github.com/nwidger/nintengo` and it will be installed. No Makefiles, no header files when developing, the code is nice and clean. Those are some off the top of my head.
True, you need to install some things. But these are just:
- OS X: Just Go, Git, and XCode (this project doesn't use freetype).
- Windows: Just Go, Git, and MinGW.
- Linux: Apt can install all dependencies (installation instructions list more than are actually needed -- sorry).
And then you get a _single binary_ that runs on the system without the installation of any additional libraries. All libraries used are included by default on any modern OS.
What's the use of anything? Seriously, you're on HackerNews where all sorts of one-off projects with no purpose except to exist get displayed and you're asking what the use of an NES emulator is? Maybe the author wanted to learn about emulation, or found it a challenge to do in Go. Does it matter? If you don't enjoy it, move on.