If you appreciate the work done within the wiki, please consider supporting The Cutting Room Floor on Patreon. Thanks for all your support!

Help:Contents/Finding Content/Debugger guide/BGB

From The Cutting Room Floor
Jump to navigation Jump to search

First understanding Game Boy programming and how GBZ80 works is important, however even if you have no experience it can be learned through testing with the methods in this guide and experimentation (as an example, ld a,xx stores a value into register a, while ld [$D000],a takes the value of register a and stores it into memory address $D000. A list of instructions with explanations can be found on the GCISheet, and can be used together with IIMarckus' opcode to instruction list). Here is another useful guide.

Using BGB emulator to debug Game Boy and Game Boy Color games

Hmmm...
To do:
Document more uses

Accessing the debugger (Windows)

  1. Run the BGB emulator and choose to load a ROM.
  2. Right-click on the game to bring up a menu.
  3. Move the mouse to "Other" to expand the sub-menu, then click "Debugger".

Understanding the menu

  • At the top of six options; File, Search, Run, Debug, Window and Execution profiler which can be explored to do a number of things within BGB's debugger.
  • The top-right corner box displays the hardware registers or hardware registers pairs; af, bc, de, hl, sp, pc, ime, ima, lcdc, stat, ly, cnt, ie, if, spd, rom.
  • The center box shows (left to right): The type of memory (with bank), the index number of the opcode or data, the instruction for the current view (note this is forced even if the bytes are data, as there is no way for BGB to know automatically), and (to-do): a pair of numbers on the right.
  • The right-center box displays the current stack pointer view. The current stack is indicated in blue, and some nearby pointers above or below it can also be seen. There is a scroll bar to see more.
  • The bottom box is a view of the Game Boy Memory Map (two byte pointers in the current bank from the Memory Bank Controller). There is a scroll bar to see more.

Running example code

In the debugger, right click at a location from the center box and choose "jump to cursor", "call cursor", or "run to cursor" (for simplicity jump to cursor) to run the GBZ80 code there. Note keep in mind the state of the stack, and the state of the game as very often this will freeze the game. Additionally, switching banks requires the relevant bank switch function (or running the game's own routine for switching banks).

The code can either be from an existing routine in ROM, offset from that routine, or in RAM (in this case modifying the RAM is useful if the user doesn't want to modify the ROM). Note changes to the ROM are lost after resetting the game, unless patched with the method below.

Patching the ROM

This can be used to make quick edits to the ROM without another tool such as an external hex editor. In the debugger, right click at a location from the center box and choose modify code/data to add your own instructions/bytes. Then from the top-left, select File>Save ROM as... to save the patched ROM. This can be used in combination with setting breakpoints to modify specific subroutines in the game. For example, one could set a breakpoint to code that causes the player to lose a life, and stub it out with ret for infinite lives.

Creating Game Genie codes for soft patches:

The Game Boy Game Genie (unlike the GameShark) works by effectively making temporary ROM patches to the game which are applied when the codes are enabled.

  1. Use the debugger and breakpoints to find an address in the ROM to patch (such as an opcode, operand or byte in the data).
  2. Right click on the game and select "cheat" to open the "bgb cheats" menu.
  3. Click "add" and where it says "internal values", specify the pointer to change (the bank doesn't need to be specified), and specify the "new value" as what byte you want to change it to (refer to the previously mentioned opcode list for opcode values) and the "old value" as what byte the opcode/operand/data originally was.
  4. After clicking "OK" the Game Genie code will be generated in the cheats list automatically.

Memory Bank Controller commands

This applies only to games which use the relevant Memory Bank Controller. Available commands may differ for the cartridge type (note some rarer games and memory bank controllers do not run properly or cannot be run in BGB; such as MBC6, MBC7).

Here are a couple of basic examples;

Change to the ROM bank specified by xx:

ld a,xx
ld (2000),a

Use di (disable interrupts) before this code in case there is an interrupt to banked ROM, as the incorrect bank for the interrupt could lead to a freeze.

Change to the SRAM bank specified by xx:

ld a,xx
ld (4000),a

Open and enable write access to SRAM:

ld a,0a
ld (0000),a

Remember to use ei (enable interrupts) to enable them again once you are finished with bank switching.

For more information, check this page on Gameboy Development Wiki

Checking and editing the state of the hardware registers

  1. Pause the game at a specific state by opening the debugger.
  2. From the debugger window, choose option "window" at the top, and select IO map.
  3. Many hardware registers (described and with their relevant HRAM address where applicable can be viewed and adjusted).

Finding unused content using breakpoints

  • Though not required, if the game being debugged has a .SYM file (a file labelling both the pointers to routines/subroutines and memory addresses in RAM), this will be useful for exploring the game in the box in the middle as the pointers (usually XXXX) will have the label applied. To enable the .SYM file, make sure it has the same name as the ROM and is in the same directory. "reload SYM file" is also an option from "File". Examples of Game Boy games with user created .SYM files are the Pokémon series. Additionally, if there is a disassembly project for the game (example), it is possible to use the linker in rgblink rgbds (Rednex Game Boy Development System) to create one after assembling the game. As a .SYM file is really just a plain text list with line breaks (except for comments marked with ;) the functions can also be added by hand manually e.g. two of the functions in Pokémon Gold (English) are:
00:0150 VBlank
00:0010 Bankswitch

Data Crystal is a good place to find ROM and RAM maps for Game Boy games to add to a handmade .SYM file.

  • Without testing individual routines one at a time, the user will require existing knowledge of how the game works. So it is a good idea to search the web if there are any existing hacking communities or Data Crystal pages for that game which may have already reverse engineered routines, or documented the data structures, RAM maps, etc. However, a lot of hacking can be learned from repeated trial and error, running random routines and using trace to find out how they work (see below), memory corruption, and so on.
  • To specify for the debugger later, RAM addresses can be found using the bgb cheat searcher. RAM addresses which might update in an intuitive way (such as the number of lives) can be found by clicking Start then doing a search in hexadecimal. So, if the character has 10 lives search for 0A to find RAM addresses equal to 0A, then if the player loses a life, narrow it down with another search for 09, and so on. If the user does not know how to cannot convert decimal to hexadecimal, Windows Calculator on programmer mode (type the number in the DEC section and the HEX value will be displayed above it) or an online tool is an option. After the RAM address is found, the user can set a breakpoint to it.
  • To set a breakpoint, from the top selection choose "Debug", then "breakpoints" or "access breakpoints".
    • "Breakpoints" is to be used if the user already knows the location they want to break at which is usually a pointer in ROM (force the debugger to open there when it is run in game). For example, adding breakpoint 0150 for the English Pokémon Gold example and playing the game makes the emulator pause and reveal the state of the game (such as the state of the stack) every time the program counter is running the start of VBlank. Also by viewing the addresses below the stack, the user may be able to find certain routines which were run before the current one. The user could repeat this to set up a chain of breakpoints to backtrace through the code. It is also possible to specify a bank (e.g. 0a:5a47). The condition can be specified (such as hl=(two byte hexadecimal value)) so that the break only occurs when the condition is true. The breakpoints stored on the menu can be enabled or disabled.
    • There is another way to use breakpoints on read by simply going to the middle box and double clicking at the desired pointer. If misaligned, the user can right click and choose "go to" then enter the address to bring the box there and interpret it as the beginning of code.
    • "Access breakpoints" is more powerful, and can also be thought of as a tool for finding the locations of subroutines. There are tick box options for "on read", "on write", "on execute", and "on jump". On read is to break whenever a certain address (or address range) is being read with an optional value. (For example, knowing the right address and values means the user could check to see if the title screen is searching for the player pressing a specific button sequence command as some games have hidden menus or cheats enabled this way). On write is for breaking whenever a RAM address is being written. On execute is for whenever the address(es) are being run by the program counter. On jump is like on execute but specifically for jumps (jp). Enter in the address range box, value (optional), then tick the relevant options (up to all four) to set up the breakpoint and have the game break on those conditions. Like before, it is also possible to enable/disable breakpoints stored on the menu.
    • It can help that there are I/O and HRAM addresses specific to the Game Boy/Color hardware rather than a game, such as $FF00 Joypad or $FF01/$FF02 which are used for the Link Cable and other serial devices. Here is a list.
  • A routine is likely to call or jump to another one, or under certain circumstances with conditional calls and jumps. Those related routines can also be studied/reverse engineered in the hope of finding unused code. Similarly, the locations to data structures are often stored in a register pair (typically stored in hl) for example, the user could experiment with different values for hl for code that loads a sprite or image at pointer hl in the current bank to try and find an unused sprite/image which isn't invalid.

Trace (F7) and Step Over (F3):

After either setting a breakpoint and playing until the breakpoint happens, or using run to cursor/jump to cursor/call cursor, the trace and step over options can be used to run through the routine one instruction at a time. This is useful for checking the state of the registers or register pairs on the right.

Trace is used to move to the next instruction, while Step Over can be used to avoid following the pointer in call and skip to when it returns to the instruction below it. If the shortcut keys F7 and F3 do not work, try typing Fn+F7 or Fn+F3 on the keyboard instead.

Additionally, the state of the registers for the instruction can be changed by double clicking on them and entering new ones. For example, if the player set a breakpoint to a routine that loads a menu, changing registers before clicking back into the game may change a behavior. Hypothetically (it will depend on the code in the routine), "a" might change the index number of the menu, "bc" might change another attribute of the menu, or "de"/"hl" might change the pointer to data or code about the menu, and so on (which could be used to find unused menus, modes, graphics, etc.).