Making Game Genie codes by reverse engineering amounts to a very simple and elementary form of ASM hacking. Prior understanding of ASM is not necessary to follow along, although this little tutorial won't teach much ASM itself. Here we're going to illustrate the process using FCEUXD SP 1.06 (or newer) and a ROM of Super Mario Bros. 3 (any ROM version should work). This code was first discovered by furrykef, as his first Game Genie code. Here is a reconstruction of the process he used.
Our goal is to make Starman invincibility time last forever. You still have to get a Starman first, but once you do, you will remain invincible until you die or the end of the level.
Finding the RAM location Edit
The first step is to find out where in memory the Starman timer is. This is a nice and easy variable to search for. The first thing we need to do is play the game until World 1-2 and get a Starman, then immediately pause the game. Go to Tools->Cheats... to see this window:
Click "Reset", and the window will look something like the picture. Close the window and return to the game. Unpause, wait a half-second, and pause again. Open the cheats window and click "Less Than" (don't click the checkbox or fill in a value, just click "Less Than"). We do this because we are assuming that the Starman timer variable starts with a high value, which gets decremented every frame until it runs out. That means that, each time we check, this particular byte should always be less than its previous value. After clicking Less Than, the number of candidates goes down from 10,240 to 95 in our case, although the number may differ for you depending on what RAM happens to contain at that moment. That means that any of 95 possible locations in RAM might be the Starman timer.
Repeat the process: close the window, unpause for a split second, pause again, go back to the cheat window, and click Less Than. Our number of possibilities went down to 24. Repeat the process again and we had 7 possibilities. Another repeat yielded 3 possibilities, which is quite easy to test individually, but we repeated it one more time anyway and got only one possibility:
That means that, if we did this right, the byte at 0553 contains the Starman variable. Type in a name for the cheat at the left of the window, put in 0553 for the address, and put in FF (the maximum possible value) for the value. This forces that value in RAM to always be FF, no matter what the code does to it. Click "add", return to the game, and unpause. Wait for Starman time to run out. It doesn't? Cool, it worked! It also turns out that this code actually makes you always invincible: you don't even have to grab a Starman first, although the Starman music won't play until you grab one.
If we just wanted a cheat code for FCE Ultra and other emulators, this would be good enough. But that's not our aim here: we wanted a Game Genie code which can be played on the real NES. It turns out that this code cannot be used on the real NES because the Game Genie hardware cannot modify RAM. It works by modifying values read from ROM. This is where ASM hacking is going to come in: we're going to use the information we found here to find the bit of code that decrements the Starman counter and remove it.
Finding the ROM location Edit
To find the location in ROM to modify, you must first be invincible. It doesn't matter if you still have the cheat code on or not. Pause the game and go to Tools->Debug... We're going to add a breakpoint. We know that our Starman variable is in 0553, so we're going to check for when the code writes to that location using a breakpoint. Click "Add..." For the address, type "0553" in the left box and leave the right box blank. (The right box is for when you want to put in a range of addresses). Click the "Write" checkbox, then click OK. The breakpoints combo box should say $0553: EC-W-, which means that it's enabled (E), operating on CPU memory (C), and checking for writes (W). Click "Run" to make sure that the game is still running, then close the debugger and unpause the game. The game should stop and the debugger will pop up again, because it reached some code that modifies 0553.
The top line, which is the line that is currently executing, should look like this (if it doesn't, click "Run" until it does):
CEE7: CE 53 05 DEC $0553 = #$FF
(It doesn't matter what the value after the equal sign is.) What does this mean? DEC $0553 is the 6502 assembly way of saying "decrement the variable at 0553"—that is, decrease it by one. This is the code we're looking for: if we replace it with code that does nothing, then the Starman value won't decrement anymore, meaning the Starman timer won't run out! The only way Starman time will ever end is if some other code modifies it (which does happen when you die or you complete the level).
OK, so how do we replace it with code that does nothing? First we need to note that this is a three-byte instruction: CE 53 05 is three bytes. We're going to replace each of those three bytes with a do-nothing instruction, NOP, which stands for "no operation". We'll replace those three bytes with the machine code for NOP. What is the machine code for NOP? A quick look at a 6502 instruction reference reveals it is EA, a one-byte instruction, so we'll repeat this instruction three times to replace the three bytes. So then we're going to carry out the following replacements:
CEE7: CE -> EA CEE8: 53 -> EA CEE9: 05 -> EA
The old values of CE, 53, and 05 are going to be used as "compare" values in our Game Genie code. What's a compare value? This is a little tricky to explain to somebody unfamiliar with NES mappers. You see, our instruction is located at CEE7, right? You might expect that the location CEE7 is going to always have the same code in it, but you would be wrong. Super Mario Bros. 3 uses a trick called "bank switching", which means that it can switch out the code at any location and replace it with different code. (The game does this because it is impossible to load all the game's ROM code at once. It's a limitation of NES hardware. The bank switching is handled by a special chip inside the cartridge called an "MMC" or "mapper".) At any given moment, CEE7 might contain our Starman decrement instruction, but at other times, another ROM bank is loaded there and it contains a different instruction. Modifying that other instruction is bad. How can we make sure that we modify only the Starman code? Simple: we check that the byte we're replacing is what we think it is. We want to replace the byte at CEE7 only when it is CE, the byte at CEE8 only when it is 53, and the byte at CEE9 only when it is 05. If they're something else, we're obviously in a different part of the code which should be left alone. That's what the compare values are for: making sure that you're modifying the code that you think you are.
Testing it out and making the Game Genie code Edit
If you haven't yet deleted your original RAM cheat of forcing 0553 to FF, delete or disable it now. Otherwise, we can't test the code.
Go to Tools->Game Genie Encoder/Decoder... and enter the following three cheat codes, clicking "Add To Cheat List" each time:
Address: CEE7 Compare: CE Value: EA
Address: CEE8 Compare: 53 Value: EA
Address: CEE9 Compare: 05 Value: EA
When you enter each code, FCEUXD will automatically convert it to Game Genie format for you. The codes you should have are XVVGNVVK, XVVKETLS, and XVVKOTIE. When you've got them all in, close the window and try it out! Play the game until you get a Starman. If you did it right, the Starman time won't run out!
By the way, are you wondering what happens if you leave out the compare values we talked about? Well, put the codes in without them and try it! What, you're too lazy? Fine, we'll tell you what happens...Mario will be unable to move on the map. Forgetting the compare values won't always yield such disastrous results...it might simply do something subtle, like cause the game to freeze toward the end of the game. Moral of the story: use compare values! (Note: compare values don't need to be used on old games that don't use a mapper. If you don't know whether the game uses a mapper or not, just use the compare values. They're harmless.)