One special feature presented by RealBoy is an interactive debugger called GDDB. This debugger lets you execute a particular code in a controller manner; it effectively lets you debug it. With GDDB you can, among other things, set breakpoints at specific addresses; print very useful information at any point, including the value of the various Game Boy registers, or even the value of an arbitrary memory address, etc. We will use these facilities to help us with a small exercise: We will see one way in which games wait for the appropriate moment to access the Video RAM (VRAM). This is extremely important because accessing VRAM can only be done at precise timings, and Nintendo even rejected games that did not respect this rule.
A CASE STUDY: POKEMON RED
Before we actually get to the point where the game waits before accessing VRAM, we will show how to load a game in RealBoy.
Right now the only user interface implemented by RealBoy is a CLI (Command-Line Interface).
We have just compiled and generated the executable binary for RealBoy; the src directory looks something like this:
Directory contents after compiling RealBoy
The executable binary is named realboy and showed in green. Let’s execute it. We will be presented with the typical Unix-like format when executing programs in the terminal:
Execution format and options
Note that the format for executing RealBoy is a list of options, followed by the game’s file path. For this example, we want RealBoy to drop to the GDDB debugger as soon as it starts executing the game, so we use the -d option. Also, we would like to execute Pokemon Red in DMG mode, so we use the option -D. This is because RealBoy automatically detects if the cartridge supports Color Game Boy or Super Game Boy, and defaults to that (Pokemon Red supports Super Game Boy). So, make sure to place your Pokemon Red ROM in the src directory and execute the following line:
- ./realboy -d -D <file name>
The session continues as follows:
Loaded game; enabled debugging and DMG mode, and started emulation.
Also note that the instruction at address 0x100 is a nop instruction. Every CPU presents this instruction (nop is for no-operation); nop does absolutely nothing, but can be useful in a variety of situations. The instruction executed at address 0x100 is the first instruction executed that actually belongs to the game. Every game presents a nop instruction at this address, at a jump instruction at address 0x101. Let’s see:
The instruction at address 0x101 is a jump to address 0x150.
Before taking this jump, let’s take a look at some of the current state of our machine. Let’s type the help command to see what GDDB has to offer:
Commands available: step, show, print, break, disasm
Let’s take a look at these commands:
step: If no breakpoint is set, execute the following instruction and show GDDB again. If a breakpoint is set, execution continues until the breakpoint is hit, and then GDDB is shown again.
show: Used to print values of the various Game Boy registers.
print: Used to print the value of an arbitrary memory address.
break: Used to set breakpoints.
disasm: Used to disassemble the memory addresses following the current instruction.
Let’s see an example:
Examples of the ‘show’ command.
Here we show the result of show regs and show ioregs; the first one outputs the current values of the CPU’s internal registers, while the second outputs the values of some of the IO registers. Note that it is possible to execute show lcdregs and show sndregs, which outputs the LCD display registers and the sound registers, respectively.
The code we want to study begins at address 0x006B; let us set a breakpoint to it and continue execution:
Breakpoint at address 0x006B.
The instruction at address 0x006B loads the value at address 0xFF44 to register A. However, address 0xFF44 is mapped to a special register of the LCD display device. This display is actually 160×144 pixels, which means it consists of 160 columns and 144 rows. The LCD register mapped at 0xFF44 holds the value of the row currently being scanned by the display. This way, any game can know which row the display is updating simply by reading the value at address 0xFF44; this is precisely what the instruction at address 0x006B is doing. Hopefully you can now suspect where all this is going.
When the LCD display device is scanning (updating) the screen, the CPU can only access restricted areas of Video RAM. This arises because when the display is in this process, it is actually accessing Video RAM and, if the CPU were allowed to access the same memory as well, the hardware could be permanently damaged as noted by Nintendo. This way, the safest way to have free access to all of Video RAM during a long period of time is waiting until the LCD display finishes updating the current frame. This is done when the register at address 0xFF44 is greater or equal to 144 (0x90); when the LCD finishes scanning the last row of the display. When the device gets to row 144, a special period know as the Vertical Blank is started. This is the time required for the display to begin scanning the next frame. It is lengthy, lasting 4560 cycles to complete.
The register at address 0xFF44 was read to register A.
Notice that register A holds the value read from address 0xFF44; this is, holds the row currently being scanned by the LCD display. Because A is 1, the device is just beginning to update the current frame; the scanner is somewhere in the second row (the upper part) of the screen.
The next instruction compares the value at register A with 0x91 (145 decimal), and sets the F register flags accordingly:
The value of the F register (the flags) changed after executing the compare instruction.
Now, if the value at register A was no equal to 0x91, a jump back to our starting address 0x6B is performed:
Loop back to address 0x006B.
Let’s perform a disassembling of the following instructions to get a picture:
Disassemble of the instructions at, and following, address 0x006B
By default GDDB disassembles 10 instructions starting from the address of the current instruction. Hitting any key disassembles another set of 10 instructions; hitting ‘q’ stops disassembling.
So, we see the three instructions we just executed that reads the value from 0xFF44, compares it to 0x91 and jumps back to 0x006B if not equal. The instruction that follows, at address 0x0071, must be executed only in case the jump fails; that is, when the value at 0xFF44 equals 0x91 (145 decimal). Let’s set a breakpoint at 0x0071 and then look at the value at 0xFF44 to see if this is true:
Continue execution at address 0x0071 because value at 0xFF44 was finally 0x91 (145).
Notice that the value at address 0xFF44 is indeed 0x91; also, recall that this address was permanently being written to register A, so A, as the image shows, is also 0x91. Now the LCD display has finished writing the current frame, and the game is free to access all of Video RAM during a long shot.
The important thing to notice here is that games need to know the current state of various devices. In this case, the game needed to know when the LCD display had finished writing the current frame to the screen so it is free to access the Video RAM. This way, updating the contents of the Video RAM is done in a secure manner. Another possibility for accomplishing this is through the interrupt system; a possible source of interruption comes from the LCD display, and the interrupt is set as soon as the Vertical Blank period starts (that is, as soon as the value at register 0xFF44 is equal or greater to 0x90). This is a more efficient solution, because the CPU may execute the halt instruction instead of constantly reading address 0xFF44 and comparing it to the desired value, which saves energy preserving the life of the battery. Actually, the code just studied happens at the beginning of Pokemon Red; surely, after the game ‘settles a bit’ it starts using the more efficient solution. In any case, the result is the same.
HELP US IMPROVE!
Did you like this post? Do you have any suggestions? Please rate this post or leave us a comment so we can improve the quality of our work!