TL;DR

I have made my first ROM hack, fixing an issue on Master System’s Ninja Gaiden, where get infinite magic points once you collect 999 or more of them.

Here are the BPS patches:

A bit of my motivation

Growing up in Brazil, I owned a Sega Master System when I was kid. The SMS was far more available there, and probably more popular then the Nintendo Entertainment System (NES), as it was very well distributed by a local company, Tec Toy.

I’m a long time gamer, and a software developer by trade, with some knowledge on electronics and retro computers architecture, more theoretical than practical.

Putting all that together, it is not a surprise that I have a certain fascination for retro games and their inner workings.

For quite some time now I have been wanting to get something done in that area, and ROM hacking is a nice way to get acquainted with the topic.

In my spare time I am also an occasional podcaster. At Fliperama de Boteco, a Brazilian podcast, we talk mostly about retro games. Some time ago we published an episode on Ninja Gaiden for the Master System. One of things that got our attention was the fact that once you collected more than 999 magic points, you could use special weapons and magic without running out of points, specially with a fire magic that makes you invincible, and normally cost you 50 magic points. That made the game too easy, and to me it felt like a bug, as you could collect enough magic to do this early in the game.

Recently, someone posted about this game in a Brazilian Master System group on Facebook, and one of the replies was “I pray that someone will make a hack to fix the bug of the thousand magic points … it makes the game too easy when it reaches 999”. I took that as an interesting challenge to get myself initiated in ROM hacking, and went for it.

How I did it

The fist step was to find out where in the RAM (Random Access Memory) the information about the number of magic points was stored.

I started searching for it using the emulator BizHawk, which contains a RAM Search function.

Using this feature, I eventually found out that magic points were stored in the following addresses:

  • 0x1FB8 - first digit
  • 0x1FB9 - second digit
  • 0x1FBA - third digit
  • 0x1FBB - this one is normally 0, but it is set to 255 (0xFF in hexadecimal) when you collect more than 999 points

For example, if you have 150 magic points, these will be the values stored addresses above:

  • 0x1FB8 - 0
  • 0x1FB9 - 5
  • 0x1FBA - 1
  • 0x1FBB - 0

RAM Watch 290 magic points

RAM Watch 999 magic points

One important information, this addresses are relative to 0x0000, but RAM on the Master System is mapped to the address range from 0xC000-0xDFFF (and mirrored on the address range 0xE000-0xFFFF), so 0x1FB8 is actually mapped at 0xDFBB or 0xFFBB.

Now that I have identified these addresses, I started looking for the code that changed their content.

For that I used a different emulator, Emulicious, which has a very good disassembler and debugger.

I added breakpoints to stop the execution where this addresses were read or written, and started doing stuff like using magic and getting items that granted magic points, also reaching over 999, then checking the code around the breakpoints.

Ultimately I discovered that 0xDFBB was used to protect the magic counter from overflowing, but I also found out that it was checked at the beginning of routine that subtracted magic points when magic was used:

Emulicious debugging, routine at 0x50D9

_LABEL_50D9_:
    ld a, (_RAM_DFC3_)
    or a
    jr nz, _LABEL_513E_
    ld a, (_RAM_DFBB_)    ; load content of 0xDFBB into register a
    or a                  ; bitwise OR of a with a, store the result in a
    jr nz, _LABEL_513E_   ; jump to 0x513E if the result of last operation (a) is not zero
    ...                   ; continue to subtraction routine

_LABEL_513E_:
    scf
    ret

So I figured that setting the content of 0xDFBB back to zero, instead of checking it and jumping, should give me the result I wanted.

To do that, I used the Hex Editor wxHexEditor.

First thing was look at the address 0x50D9 (20697 in decimal) and locate the opcodes for the check:

HexEditor at 0x50D9

Now, without calling another routine, I had 6 bytes to do what I wanted, set the content of 0xDFBB with 0.

There are more sophisticated ways of doing this, like using a Z80 assembler like WLA-DX, but that would be like bringing a gun to a knife fight. I did not even had to look for the opcodes of the instructions I wanted to use to replace the check:

    ld a, $00
    ld ($DFBB), a

there was a very similar piece of code just above the one I wanted to change:

HexEditor at 0x50D0

It was only 5 bytes, I just had to adapt so that it was ld a, $00 instead of ld a, $FF, and fill the remaining byte with 0, which is the instruction nop (No Operation):

HexEditor at 0x50D0, hack

Now, I tried to load the game, and…

Patch with wrong checksum

Ooops, something is wrong…

The ROMs have a checksum stored at the address 0x7FFA, which is checked by the BIOS. After hacking the ROM we need to adjust the checksum. To calculate the checksum for the patch I used the command line tool SMS Check:

C:\Emuladores\Roms-SMS>smscheck.exe "Ninja Gaiden (Europe) - avoid infinite magic.sms"
SMS Check 0.24 - by Omar Cornut (Bock), Apr 19 2016
http://www.smspower.org
--

[./Ninja Gaiden (Europe) - patched with failure.sms]
Size ...... 262144
Header .... 54 4D 52 20 53 45 47 41 00 00 AD 4C 01 71 00 40
System .... SMS Export (4)
Size ...... 256k+
CheckSum .. 4BB3, Bad. Should be 4CAD
FullSum ... 1674F91

All, right, now I adjusted the checksum in the ROM, again using the hex editor:

HexEditor at 0x7FFA, hack

And, there we go:

Ninja Gaiden - no infinite magic

No more infinite fire magic!

Now I had my hack ready, so it is time to produce a BPS patch, I used a tool called Floating IPS.

Floating IPS

And there we have it, a patch file ready to be applied: Ninja-Gaiden-Europe-avoid-infinite-magic.bps

Later I was informed that there was also a prototype ROM for Ninja Gaiden on the Master System, so I produced a patch for that one too: Ninja-Gaiden-Europe-prototype-avoid-infinite-magic.bps

Closing words

It was a fun ride, getting acquainted with Z80 assembly takes a bit of patience, my last contact with assembly was around 20 years ago… managing to read it after sometime is quite satisfactory. I hope this blog post helps other people that would like to get started with ROM hacking.

A huge thanks go to the people maintaining the excellent content on the website smspower.org.

References