So it looks like the time is drawing near where any ol’ shmoe will be able to run homebrew on his Nintendo 3DS. And if that’s the case, then it’s only a logical next-step to be able to run Virtual Boy games on the system. But, of course, before that can happen, an emulator needs to exist. And really, what’s in an emulator?
To “emulate” something is to copy it. An apprentice will emulate the techniques of his master. A child will emulate the behaviors of his role models. Pasty-white-skinned girls will emulate their favorite Japanese comic or video game characters’ appearances while at media conventions. You get the idea. As it pertains to computer science, an “emulator” plays the role of another computer system.
In its simplest form, an emulator is software that pretends to be a computing platform, so that software designed for that platform can be used elsewhere. Okay, so what’s a platform? A platform (oversimplifying things a bit) is some combination of hardware and operating system. Sometimes there’s hardware with no OS, such as older video game systems (Virtual Boy included). Other times, you might have what amounts to an OS with no hardware, which is the idea behind Java. Either way, these are distinct platforms, and any software that pretends to be one such platform is an emulator.
The platform that runs the emulator is called the host system, and the platform being emulated is the guest system. It’s important to note that the host and guest platforms don’t have to be different: it’s quite common to run “virtual machines” to have Windows running inside Windows, for instance. But since Windows is designed to hog all of the platform’s resources for itself, running more than one at a time on the same physical computer requires an emulator.
And that brings us to this thread. In this case, the Nintendo 3DS will be the host system, and the Virtual Boy will be the guest system. 3DS homebrew is the link that will allow all of this to be possible.
The exact means by which an emulator can do its thing are many and varied. Sometimes you can take a direct approach, while other times you can abstract things away. Either way, most emulators have two particular goals in common: accuracy and speed. If you can get both of those things down, you’ll be a success in the emulation world.
Two methods in particular are what I’ll be discussing here: interpretation and recompilation. These each have their strengths and weaknesses, and both have been used to great success out in the wild. So let’s take a moment to learn about them…
When referring to an emulator as an “interpreter”, it means that the emulator is running the guest software directly without doing any more with it than is necessary to take the desired input and produce the desired output. It’s not unlike taking a book written in Spanish and reading it out loud in English. The book itself never changes, and you never write anything down or anything. But every time the interpreter wants to read from the book, he has to do all the translating in his head.
On the lowest level, a computer program is a list of instructions. When one instruction is completed, the next one is processed. Multiple-processor systems can technically run two simultaneous “threads” of program, but they still work the same way: one instruction at a time. Since Virtual Boy has only one CPU, this makes the job a lot simpler. An interpreter simply reads the bytes for the next instruction, figures out what it means, then carries it out. Even someone not overly obsessed with computer science will notice that this is… well, an obvious approach. I mean, how else is it supposed to work?
And that’s the point. The interpreter approach to emulation is by far the most intuitive and easy to understand, and since it operates with such strict control over the entire process, it’s also very accurate. This is the key benefit to interpretation. Debuggers written for interpreting emulators are likewise very easy to use, since programs can be inspected one CPU instruction at a time, even if the instructions themselves were designed for a CPU other than the one running the host system. I really can’t say enough good things about interpretation, so I’ll leave it at that for now.
That all said, interpretation has an ironic insufficiency: it’s complex. I know, it seems weird, but the act of parsing every single instruction and figuring out what to do with it each time takes a lot of work on behalf of the host CPU. Although it’s a simple technique, the actual work involved turns out to make the whole process somewhat inefficient. So that’s going to be the main drawback to interpretation: it’s about as slow as any approach to emulation can be.
This leads us to…
I had this awesome story in mind regarding UltraHLE, but then I remembered that its success wasn’t due to recompilation, but due to something even more make-believe. Rather than pretend to be the Nintendo 64 exactly, it would delegate processes to pre-compiled libraries or graphics adapters, meaning it wasn’t a true emulation so much as a reasonable facsimile. Either way, that has nothing to do with recompilation, but at least it’s worth noting!
Most programs nowadays are written in C or something like it, which holds true for Virtual Boy games. In fact, I’m pretty sure the GNU C++ compiler basically converts source code into C before the assembly step. Compiled C code has three extraordinarily useful characteristics that can really speed up the process of emulation: 1) all code exists inside functions, 2) all bytes between the first and last instruction of a function belong to and are code for that same function, and 3) all branches within a function will never jump outside of the function. There are some exceptions, such as self-modifying code in RAM or what-not, but bear with me here…
Recall the Spanish book/English reader example. If each paragraph on the pages of the book were analogous to a function, then we could simplify the process of reading from the book if we did a little processing on those paragraphs. What if, the first time the reader came across a Spanish paragraph, he would translate it into English, write it down on a sticky note, and put the note over the Spanish paragraph on the page? Then, the next time he comes across that paragraph, it’s already in his target language, right?
That’s the idea behind a recompiler. Functions are converted wholesale into code that honest-to-goodness really does run on the host CPU for real, which is then stored for later so it doesn’t have to be translated more than once. There are two main categories of recompiler: ahead-of-time (AOT) and just-in-time (JIT). AOT compilation basically converts code into an executable compatible with the host platform (like turning a Virtual Boy ROM into a Windows .exe file). JIT compilation, which is more common, only translates functions as the program is being run.
JIT compilation is more common, and in many ways more useful. Since C programs have tell-tale behavior when calling functions (it’s usually with a jump-and-link instruction, or just any jump by register), it’s easy to keep track of where in the binary file each function is based on the address used during the function call. This address can then be checked against a list of all known functions and, if the function hasn’t been encountered yet, it can be translated into native code and stashed away for later. That way, subsequent calls to that same function don’t have to be re-translated. From the memory-management side of things, the function cache can be periodically pruned according to how frequently each function is used, which can save memory without sacrificing speed. JIT recompilation has a lot of potential to… knee the bees… or… something like that. You know what I’m trying to say.
Of course, implementing a JIT recompiler is quite a bit more specialized and complicated than writing an interpreter. But the very significant trade-off is speed. Delicious, glorious speed. I don’t know exactly how powerful the Nintendo 3DS is, but it’s not outside the realm of possibility that an interpreter might be too much of a burden for it to keep up. Or maybe I’m not giving the system enough credit, who knows. No way to tell for sure without giving it a try.
What I can say with certainty is that a Virtual Boy emulator for Nintendo 3DS is totally possible. And we can make it happen.
That was an interesting read. I was kinda hoping for some ARM-specific details, like possible hurdles because of the difference in the different CPU instruction sets, or nice speed-ups because of matches in the instruction sets.
But, well, now I understand what recompilation really does just a little bit better. And heck, the Wii can do Virtual Boy emulation at a good speed, and as I understand it the 3DS is more powerful than the Wii, so that leaves some leeway to be able to handle two screens too.