This thread is a… I guess it’s like a blog of sorts. It is not in and of itself a journal of the emulator project’s progress, but rather a set of mini-articles detailing individual concepts. I’m posting here in the hopes that I can shed some light on what goes on behind the scenes to make an emulator do what it does.
Discussion is welcome. Any questions, comments or better ideas would make this thread a happenin’ place!
The emulator project isn’t only an emulator. I want to give a source of high-quality Virtual Boy emulation to the development community as a whole, and the best way to do that is to put together a package that can be used by other developers in other projects. So the project’s scope, in the most general sense, is that it is both an emulator package and a development library.
Without going into full detail for the umpteenth time, the decision was made to use C for the emulation core library and Java for the GUI application. This of course raises the self-imposed obstacle of getting the Java application to use the C core library, but that problem has been solved and the basic structure of the final product has been well researched and verified.
In any of my posts, I will use the terms “the core” and “the application” to refer specifically to these components.
With that out of the way, there are three main categories of features I’d like the emulator to have: System, Development and TAS. System features would be the usual point of an emulator, where you can run programs and play games in a simulated environment. Development features would be useful for hackers and programmers, giving full control over the CPU and hardware components. TAS features would be useful for speedrunners, giving fine control over input and program state, as well as some way to go back in time.
The emulation core isn’t responsible for implementing any particular features. Its job is just to pretend to be a Virtual Boy and nothing else. With the right built-in hooks, the core library can allow some GUI program to attach to it and get all the fancy features set up.
Abstract: Emulation Core
As a pure C code library, the emulation core is included in some larger software project. It makes no system calls, and in fact does not make use of the C runtime either. It is only program code that can be used absolutely anywhere that C can be used.
The library operates on instances of emulated Virtual Boys. These instances are known as “contexts” and include all of the state information needed to represent a Virtual Boy properly. The application is responsible for allocating memory for these contexts, which are then manipulated by the core. This means no static memory is required, and there is no limit to the number of Virtual Boys that can be emulated.
Within the VB context, certain fields can be configured to extend core functionality and incorporate additional code supplied by the application. For example, memory read accesses could pass through an application function before executing, allowing the application to implement a cheat engine. Various such situations are available, and the core needs only provide a mechanism for the application to hook into.
The application supplies ROM data and controller input, but otherwise all emulation tasks are handled by the core. Likewise, the core provides output buffers for audio and video.
Since the CPU is the fastest clock in the Virtual Boy, all emulation timing is performed relative to it. For each CPU instruction executed, all hardware components are emulated for the amount of time determined by the number of CPU cycles spent.
Generally speaking, the first task when implementing an emulator is to implement the CPU. After all, the CPU is the most central part of an emulated system–it processes the program instructions and configures the hardware components. This emulator is no different: the CPU will be implemented first and serve as the basis for all other emulation processes.
Likewise, the first task when implementing a CPU is to implement the bus. Fortunately, Virtual Boy’s internals are simple and straightforward, so implementing the CPU bus isn’t all that involved. In its most general form, the bus maps hardware connections to numeric CPU addresses. Since program code is loaded from the bus, it needs to be emulated before any individual CPU instruction can be. Some hardware components like system memory have many addresses mapped to them, while other components only have a handful of addresses used as communication channels.
The V810, which the Virtual Boy’s CPU is based on, technically defines two CPU buses: memory and I/O. Fortunately for us, Virtual Boy maps the I/O bus to the memory bus, so they behave identically from the emulated program’s point of view. The only two bus operations are read and write, so that’s what the emulated bus needs to handle.
On modern systems, the bus controller is responsible for some pretty sophisticated things like mapping memory to specific CPU address ranges and the like, but Virtual Boy isn’t that fancy. Every address has a fixed significance that never changes, so the emulated bus is only required to make sure data comes from and goes to the correct places.
Different hardware components process reads and writes at different speeds. For instance, system RAM is faster than video RAM. Whenever a memory access is performed, the emulated bus needs to account for the number of CPU cycles required by each operation.
Lastly, memory accesses are a very useful place for an application hook. If a function pointer is supplied by the application, the bus controller can call that function just before performing any reads or writes. Most emulators seem to trap accesses after they occur, but sometimes it’s more useful to know what data is being overwritten. Rather than provide two access hooks–one for before and one after–it’s simpler to merely require the application to tell the core if an access needs to be performed.
Once the CPU is initialized on reset, it can start processing program instructions. If you’ve ever taken a computer science course, you’ll be told that the most general form of CPU operations involves four stages: fetch, decode, execute and store. This general approach is also valid when implementing an emulator.
I don’t have solid data on certain parts of the CPU’s internals, so timing will not be 100% accurate. In particular, specifics of the pipeline, instruction cache, read/write buffers and exact operation of certain instructions aren’t available. All of these things will have to be emulated in a fairly rudimentary way until such a time that more information becomes available.
Some instruction sets can be a real pain to decode, but Virtual Boy’s is fairly simple and consistent. An instruction’s opcode can be determined by reading the first 6 bits, for both 16- and 32-bit instructions. Decoding and executing can subsequently be handled very efficiently by using that opcode as an index into an array of function pointers.
Just prior to executing an instruction is another good place for an application hook. If the decoded instruction is also passed to the application, intelligent handling of runtime situations becomes possible in addition to simple execute breakpoints. One of the things I really want to see is a mechanism that automatically records function addresses every time the emulated program calls a function…