- Linux or macOS platform
- Some Game Boy assembly knowledge
- Game Boy emulator with debugger: BGB - works great with Wine (or Wineskin on macOS)
- Game Boy Assembler - WLA DX (wla-gb and wlalink expected to be found in your path)
- macOS: Install Homebrew and then run
brew install cmakeif you don't ready have it. Then follow the instructions in WLA DX's INSTALL file to create the make files and install in /usr/local/bin
- macOS: Install Homebrew and then run
- bsdiff command line patching tool (expected to be found in your path).
- macOS: Install Homebrew and then run
brew install bsdiffin the terminal
- macOS: Install Homebrew and then run
- Hex editor
- macOS: Hex Fiend is free and pretty good
The process below is for a standard game. Some games might be a bit more complicated if they use non-standard joypad read functions. If you run into one of these games, or have any other questions, create an issue and I'll try to help.
-
Go into the
/srcdirectory of the project and run the init patch script, passing in the ROM you want to patch. If the ROM is not already in the/original-romsdirectory then it will be copied there for you../init-patch ~/game.gbThe script will create a file in the
/srcdirectory with the name of the ROM file with.sappended. -
Open the original ROM in BGB emulator.
-
Next we need to check if the game resets the internal RAM when it starts up. In BGB add a write access breakpoint for
C300-C500and reset the game. Hopefully the breakpoint will be triggered inside a function that resets all of the internal RAM. If so, you can delete the reset ram section from the patch file, and delete the.INCLUDE "includes/reset_ram.s"directive.If the game doesn't reset the RAM itself, then the ROM patch needs to do it. This is so that the PackBits RLE will be able to compress the internal RAM efficiently. Check that the game jumps to
$0150from$0101. If not then update theRESET_RAM_DONEdefine to set the correct address. You will also need the.INCLUDE "includes/reset_ram.s"directive somewhere in bank 0. There is often space between$0080and$0100, so the template includes reset_ram.s in the "relocated read from joypad" section by default. -
Delete the write access breakpoint and create a new one for writes in the
2000-3FFFrange. This will cause the debugger to stop when a new ROM bank is selected.Games will generally write the bank that is being selected to an address in RAM and then write to
$2000to select the bank. If the selected ROM bank isn't saved in RAM anywhere then it might be at$4000or$7fffin the data of each ROM bank.Update the value in the config section of your patch file:
.DEFINE current_rom_bank $xxxx -
Delete the previous write access breakpoint and create a new one for writes with a value of
20toFF00. The debugger will stop during the joypad read routine. -
The
joypadandjoypad_2values are the addresses where the joypad data is stored. You can generally find it at the end of the joypad read function. Update the values in the config section of your patch file:.DEFINE joypad $tttt .DEFINE joypad_2 $ssss -
The joypad read routine usually writes
$20to$FF00then does aswap a(or sometimes a set of fourrrc aoperations) before writing$10to$FF00. If not, try adding this define to the config section:.DEFINE swap_joypad 1Some games don't do
cplon the joypad value, if so add this to the config section:.DEFINE cpl_joypad 1 -
Also in the joypad read routine it usually does
ld a, $30 | ldh ($00),a | ret. Locate the start address of these instructions, and set the .ORG directive in the "joypad read" section to the address of theld a, $30instruction. -
Locate about 64 ($40) bytes of free space in bank 0 of the ROM for the "relocated read from joypad" section. Update the .ORG directive (and SIZE if necessary). There is often space between $0080 and $0100, so the template locates the "relocated read from joypad" at $0080 by default.
If there isn't space available in bank 0 and the game uses a standard joypad read function, then you can overwrite the joypad function with a call to our own joypad read function in a different bank. The .ORG value from step 8 and SIZE will need to be updated to point to the start of the original joypad read function and the size of the original joypad read function. Change the joypad read section to do this:
.INCLUDE "includes/call_relocated_read_from_joypad_in_other_bank.s" retand then add the joypad read and check into save/load state section:
.INCLUDE "includes/joypad_read_and_check.s"See the Speedball 2 patch for an example.
-
Locate approx 544 ($220) bytes of free space in the ROM. Its usually easiest to do this in a hex editor. Then update the .BANK, .ORG, and .SECTION size defines in the save/load state section.
The .BANK value =
(the start address of the free space in the ROM file) / $4000The .ORG value =
(the start address of the free space in the ROM file) AND $3fffThe SIZE value =
The amount of free space availableIf there is not enough free space in the ROM then you will need to add a new bank at the end of the rom. This has the downside of doubling the ROM size, so you will also need to double the
.ROMBANKSdirective. If the current rom bank is stored in the ROM data then you will need to define that for your new ROM bank also. See the Pop 'N TwinBee patch for an example. -
If the game already supports games saves itself and uses 8KB of SRAM, then add this define in the config section:
.DEFINE game_uses_save_ram 1Games that already use 32 KB of SRAM are not currently supported.
-
If the game uses MBC5, then add this define in the config section:
.DEFINE uses_mbc5 1 -
Run the script to create the patch from the
/srcdirectory.e.g.
./create-bsdiff-patch game.gb.sThe patched ROM will be stored in the
/patched-romsdirectory and the .bsdiff patch file will be stored in the/patchesdirectory. -
Check if the game uses the wave pattern RAM by loading up the game in BGB and then save a game state during gameplay. Then reset the emulator and try to load up the save state as soon as possible after booting. If the sound/music sounds strange then it is likely due to the wave pattern RAM not being saved.
Making the wave pattern RAM readable requires disabling the third sound channel, so the third sound channel needs to be enabled again after saving/loading a save state. This is done through the NR 34 sound register located at
$FF1E.Set a write access breakpoint in BGB during gameplay for address
FF1Eand locate which address contains the data that is written to$FF1E. Add a define with the address to the config section:.DEFINE current_nr34_value $zzzzIf you cannot locate the address where the current value for the NR34 register is stored, you can try just adding this define to the config section, though results may vary:
.DEFINE restore_wave_ram 1Create the patch again and test the sound.
-
You're done! Create a pull request to add it to the repo so everyone else can enjoy it too.