уторак, 28. април 2015.

Debugging Nintendo games

Have you ever played a video game and wondered what rules you could bend? What's behind the flagpole in Super Mario Bros, can you skip a dungeon in Legend of Zelda or beat the BubbleMan with his own gun?

Sometimes the game authors themselves leave cheat codes that implement interesting game rules like flying, all weapons etc. Game genie codes and glitches like cartrigde tilting can also provide a ton of fun. But what if the game you like has no exotic codes, and the only game genie codes you can find online give you infinite ammo? You break the game yourself, of course!

Felix, where did you steal that balloon in the first level?!

Read on to see how to create new rules yourself in classic 8-bit Nintendo games.

Game modding has many faces. You can alter game assets to chage models, sounds, add new levels etc. You could change game scripts to alter it's behavior. You can decompile the executables to fix bugs or add new features. In case of classic NES and Famicom games your only option is to modify the game ROM on the cartridge. But thanks to emulation it does not require complicated hardware anymore.

Hacking NES ROM is not very difficult. What you need is a bit of computer hardware background, a basic knowledge of (any) assembly and some free time.

First, you need to think of a game you want to challenge. Pick one that you played many times and know the rules well. It's important - you'll easier understand the mechanics later on.

Next, you need to set a goal. What would you like to change? Infinite lives is an easy but boring target. Think of some rule that makes sense game-wise, but is not implemented. It's important to have a clear goal - it'll help you focus.

I wanted to have all weapons in the game Whomp Em available straight from the beginning. WhompEm is a bit like Megaman - you defeat bosses and gain their superpowers (kinda). I was wondering what would happen if I would to attack a boss with his own weapon. Would it do any damage at all? Would the game crash? Would it be super effective? Hence the idea to fire up a debugger and see what can I do about it.

Game, tell me your secrets!

So, you've picked the game, and have a clear goal in sight. Great! Now, open the game in an emulator and check what debug tools you have at your disposal. I had no prior experience in ROM hacking and started with Nindendulator. I've heard (a couple of years ago) that it has quite precise emulation and solid debug tools so it seemed like a good start. Unfortunately, (probably due to no fault of Nintendulator and more probably because of my inexperience) I did not went far with the tools available in it, and so I switched to FCEUX. This provided better results, which I'm about to present. I'm sure it can be done with many other tools, but I did not investigate much further. FCEUX it is, this time.

First, a bit of background. Roughly speaking, NES has a CPU, PPU and 2KB of RAM. CPU is a processor that receives the input, updates the game logic and tells PPU what to draw. PPU handles the drawing. RAM contains the code, game data (at least parts of it) and variables.

Thankfully, 2KB of RAM is miniscule capacity by todays standards and will make our job easy. Thats 2048 bytes. You could easily fit that all on screen at once :).

CPU is a 6502. It's an 8-bit CPU, so one instruction takes one byte + up to two bytes as instruction parameters.

Our first goal is to familiarize with the memory map. You can easily look it up online. RAM is avaiable through CPU addresses $0000 to $800 (hexadecimal), split into 8 pages, 256 bytes each, First page is called "zero page" since it can be addessed using a single byte (instead of usual two), so the instruction takes less space and addressing is faster than other pages. This immediately raises a flag - it's quite possible the game is going to store fast-changing variables there.

Enough preparation, lets peek inside. FCEUX has a fantastic tool called "RAM search". It allows you to filter though the memory in real time, looking for variables that control parts of the game you're interested in. You can refine your searches until you find what you want. So, in my case, I was looking for a way to have all weapons available from the start of the game. That meant I need to figure out where the game keeps infomation on what is my current weapon, and can I switch to another one or not. So I've beaten a single boss (to be able to switch weapons), and used a RAM serach to find single bytes that change when I switch weapons. And quickly enough I found out that every time I switch weapons - bytes at $0084 and $00e6 change value.

When you use the RAM search function for the first time you'll always get more results than you want, but you'll quickly learn how to narrow down your results - you can repeat the searches one after another, change the search criteria (number of changes, values that increase/decrease etc) and when you're sure which bytes you don't want - you can remove them by clicking the "Eliminate" button.
Repeat your ingame action (either maually, using save states or recorded demos) until you find some interesting memory locations.

To change a value of the memory location, right click on it and it'll open in the Hex Editor - a nifty tool for editing values on all the memory locations available to the CPU. I changed the value at $0084 to 02 and voila! I had a fire stick weapon. Take that, game!

It's only the first level and I've already mastered all the elements! Fire benders, unite!

As fun as being able to dictate currently held weapon through live RAM editing sounds like, it's not very practical. Once you switch the weapon ingame, you can't get it back, until you poke the memory again. Game knows you've not earned it yet. So we need to change that.

This part is a bit tricky. Bytes that indicate whether you can switch to the next weapon or not do not change when you attempt to switch it. They probably get updated once you beat the level, but comparing all the changed bytes at the beginning of each level could be a tedious task. Time to put some real effort into our case.

Right clicking the memory location in Hex Editor reveals a context menu item - "Add Write Breakpoint For Address". This will make the game pause when the code tries to write something to a given location. Since we want to find out where the game stores the list of weapons I've got so far, surely it'll compare $0084 to it at some point before changing the weapon (or not), so the verification will have to be somewhere before $0084 gets written. By making a write breakpoint I can see the code that executes.

Here is a snippet of dissasembled code (I've numbered the lines for convinience):

  1.  02:B71F:8C 42 06  STY $0642 = #$00
  2.  02:B722:8D 43 06  STA $0643 = #$79
  3.  02:B725:60        RTS -----------------------------------------
  4.  02:B726:A9 07     LDA #$07
  5.  02:B728:20 9F F5  JSR $F59F
  6.  02:B72B:A9 05     LDA #$05
  7.  02:B72D:20 9F F5  JSR $F59F
  8.  02:B730:A9 23     LDA #$23
  9.  02:B732:20 9F F5  JSR $F59F
  10. >02:B735:E6 84     INC $0084= #$00
  11.  02:B737:A5 84     LDA $0084= #$00
  12.  02:B739:29 07     AND #$07
  13.  02:B73B:85 84     STA $0084= #$00
  14.  02:B73D:AA        TAX
  15.  02:B73E:BD C5 F1  LDA $F1C5,X @ $F1CA = #$20
  16.  02:B741:25 33     AND $0033= #$03
  17.  02:B743:F0 F0     BEQ $B735

Let me explain the disassembly of 6502 debugger. The first two digits represent a number of 16k bank the code resides in. This is not of much use to us currently. Next is a physical addess of the executed code. It'll come handy when we edit the ROM later, Next comes the instruction that is executed, followed by up to 2 bytes of instruction parameters. Lastly we have a description of the instruction, followed by the value at the memory location, if applicable. So, for example,

 02:B71F:8C 42 06  STY $0642 = #$00

means the instruction that is being executed is 8c, with 2 parameters, 42 and 06. Instruction 8c is called STY, Store Register Y which stores the contents of the register at the memory location provided as a parameter. In this case it's $0642, since low byte goes first, followed by a high byte. =#$00 indicates the current value of the memory location.

6502 is a very straightforward CPU, it has 8-bit arithmetic logic, 16-bit addressing, 2 general purpose registers (X and Y) and one accumulator (A). Most of the code will revolve around moving values from registers to memory and vice versa, arithmetic logic and jumps.

So, back to our code. I've included the first two lines just for the reference. They are followed by a RTS (Return from subroutine). RTS indicates an end of the subroutine, and at the same time announces the beginning of the next one (not alwyas the case, but a good starting assumption). When you stop at the breakpoint and see a RTS couple of lines before it's generally a good sign - it means you're at the beginning of a function, which probably does something that you're looking for. I can safely assume in my case it's the routine for switching weapons (yay!).

So what it does? a9 07 - LDA #07 loads literal 7 into an accumulator. In the next line (5) we see JSR $F59F which is a subroutine call. We can see the code in the routine by stepping into it with the debugger, provided we created a code execution breakpoint in the line before. For the sake of simplicity let's skip it for now. Next lines are similar - load 5 into accumulator, call a routine, load 23, call a routine. We can assume that is some kind of preparation or pre-calculation.

At line 10 interesting things happen. This is also a line the CPU has paused in (indicated by the right bracket), since it writes to $0084, as requested by an original breakpoint we made before. So, it says INC $0084. Great, it's increasing my current weapon. Next it loads it into accumulator and performs logical AND over a value 7. The result is written back to $0084. Knowing that there are only 7 weapons, AND-ing with 7 gives us the original number, so no clear change there. But look ahead, line 14 - TAX - moves A to X, LDA $f1c5 - reads the value at f1c5 into accumulator. AND $0033, BEQ $B735. Aha!

Whenever you see a branch instruction, pause for a second and look around. BEQ means "branch if equal". We compared something and conditionally jumped to a different code path. It's the equivalent of the "if" statement in the higher level programming languages. This could be it. BEQ branches if zero flag is set. Zero flag would be up if whatever was at $f1c5 AND-ed with $0033 at logical false.

Both of those locations are suspicious. Let's check them up. First, $0033. It has a value of 3. That is binary 11, Lets load a save when I've beaten more levels. A conclusion: beaten fist level - 1, first and second 3, first second and third - 7. Each bit in a byte represents a level. Given there are 8 levels in total, it makes sense. Let's try and write #FF (all ones) to $0033. Bingo! I can switch between all available weapons, even the dragon, which you get for beating first 7 levels. Sweet!

But there is one issue. When  you beat a level, you cannot choose any other! Game thinks you've beaten them all, and just sits there at the level select screen. Apparently the level you've beaten and the available weapons are stored at the same location, or more precisely, there is no distinction between given weapons and beaten levels. If you finshed a level you got an appropriate weapon and thats it.

So, clearly, forcing #FF to $0033 is not a way to go. Thankfully, the real solution is not far. We just need to alter the game code not to look at $0033 when switching weapons, but somewhere else instead. Since the exact instruction is "25 33", 25 being the "AND" and "33" being the address, we need to find another location in zero-page (we have only one byte for the address) that stores "FF" every time we try to change a weapon. Luckily, a quick glance at Hex Editor reveal that $00F4 pretty consistently (at least in FCEUX) holds #FF. Alterenatively, instead of relying on memory location, we could use the immediate version of the AND (29) command, and compare to #FF directly.

In order to change the game data we need to edit the ROM. Hovering over the AND instruction in the 6502 Debugger we can see in the status bar that the physical location in the file is 0x00B751, as indicated in the status bar.

We can open the .nes file with any binary editor, like XVI32, and navigate to 0x00B751. Sure enough, we clearly see 25 33. We can change it to 25 F4 or 29 FF and save the file. Voila! Load the file back in the emulator and try it out. We cannot be stopped anymore! :)

"So, in the end, what happens when you beat the boss with his own weapon?", I hear you say. Well, check it out for yourself:

Quite interesting, isn't it?

This blog post turned to be quite lengthy, and I haven't even talked about another WhompEm hack I've discovered. I have also some interesting findings in Felix The Cat for the NES. Might write a follow up in the future if I find enough time.

So, what are the conclusions? ROM hacking for the NES is quite fun. 6502 is pretty simple, and given enough effort a lot of things could be discovered. This particular hack ended up changing only a single byte!

So, pick your game, set a goal, and start debugging! If you have an interesing idea about a hack I could try debugging it myself - this turned to be an enjoyable challenge. So leave your comment below, and I'll take a look what can be done.


NES memory map: http://www.fceux.com/web/help/fceux.html?NESRAMMappingFindingValues.html

6502 instruction set: http://www.obelisk.demon.co.uk/6502/reference.html

6502 addressing modes: http://www.emulator101.com.s3-website-us-east-1.amazonaws.com/6502-addressing-modes/

FCEUX 6502 debugger documentation: http://www.fceux.com/web/help/fceux.html?Debugger.html

3 коментара:

  1. Hey I have a challenge for you. Make it so the hidden items in super pitfall nes are always visible, or visible simply by walking under it.

  2. I made a romhack after reading this guide, спасибо!