mirror of https://github.com/stella-emu/stella.git
Added "How to hack a ROM" tutorial to Debugger.txt. Sometime soon I
expect the 'net will be flooded with hacked versions of Battlezone that give you infinite lives :) git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@702 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
169e43e16f
commit
803a9146be
|
@ -60,6 +60,8 @@ What the debugger can do:
|
||||||
breakpoints, traps, etc).
|
breakpoints, traps, etc).
|
||||||
- Built-in functions for use with "breakif", to support common conditions
|
- Built-in functions for use with "breakif", to support common conditions
|
||||||
(such as breaking when the user presses Game Select...)
|
(such as breaking when the user presses Game Select...)
|
||||||
|
- Save patched ROM ("saverom filename.bin"). This currently works for every
|
||||||
|
cart type except DPC (Pitfall II).
|
||||||
|
|
||||||
Planned features for Stella 2.0 release:
|
Planned features for Stella 2.0 release:
|
||||||
- Graphical TIA tab, with register names and GUI buttons for
|
- Graphical TIA tab, with register names and GUI buttons for
|
||||||
|
@ -70,12 +72,11 @@ Planned features for Stella 2.0 release:
|
||||||
code at once)
|
code at once)
|
||||||
- Bankswitch support in the debugger for the few remaining cart types
|
- Bankswitch support in the debugger for the few remaining cart types
|
||||||
that aren't supported.
|
that aren't supported.
|
||||||
- Patch ROM support for a few cart types doesn't work.
|
- Patch ROM support for a few cart types doesn't work. Must fix.
|
||||||
- Some way to control joystick/etc. input from within the debugger.
|
- Some way to control joystick/etc. input from within the debugger.
|
||||||
- Source-level debugging: if a DASM .lst file is available, we'll show
|
- Source-level debugging: if a DASM .lst file is available, we'll show
|
||||||
the listing in the ROM tab instead of a disassembly. This is already
|
the listing in the ROM tab instead of a disassembly. This is already
|
||||||
availabe in a very crude form ("loadlist" and "list" commands).
|
availabe in a very crude form ("loadlist" and "list" commands).
|
||||||
- Save patched ROM.
|
|
||||||
- More "special variables" for the expression parser. Currently, you can
|
- More "special variables" for the expression parser. Currently, you can
|
||||||
use all the CPU registers and flags in expressions (e.g. "print a+1" does
|
use all the CPU registers and flags in expressions (e.g. "print a+1" does
|
||||||
what you expect), and the pseudo-variables "_scan" and "_bank" (which
|
what you expect), and the pseudo-variables "_scan" and "_bank" (which
|
||||||
|
@ -609,3 +610,145 @@ can update them just by pressing Enter in the prompt.
|
||||||
|
|
||||||
You can also use the Step, Trace and Frame+1 buttons from anywhere in
|
You can also use the Step, Trace and Frame+1 buttons from anywhere in
|
||||||
the GUI via the keyboard, with Alt-S, Alt-T and Alt-F.
|
the GUI via the keyboard, with Alt-S, Alt-T and Alt-F.
|
||||||
|
|
||||||
|
|
||||||
|
Tutorial: How to hack a ROM
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Here is a step-by-step guide that shows you how to use the debugger to
|
||||||
|
actually do something useful. No experience with debuggers is necessary,
|
||||||
|
but it helps to know at least a little about 6502 programming.
|
||||||
|
|
||||||
|
Step 1: get the Atari Battlezone ROM image. Make sure you've got the
|
||||||
|
regular NTSC version. Load it up in Stella and press TAB to get to
|
||||||
|
the main menu. From there, click on "Game Information". For "Name", it
|
||||||
|
should say "Battlezone (1983) (Atari) [!]" and for MD5Sum it should say
|
||||||
|
"41f252a66c6301f1e8ab3612c19bc5d4". The rest of this tutorial assumes
|
||||||
|
you're using this version of the ROM; it may or may not work with the
|
||||||
|
PAL version, or with any of the various "hacked" versions floating around
|
||||||
|
on the 'net.
|
||||||
|
|
||||||
|
Step 2: Start the game. You begin the game with 5 lives (count the tank
|
||||||
|
symbols at the bottom of the screen).
|
||||||
|
|
||||||
|
Step 3: Enter the debugger by pressing the ` (backquote) key. Don't get
|
||||||
|
killed before you do this, though. You should still have all 5 lives.
|
||||||
|
|
||||||
|
Step 4: Go to the Cheat tab (click on Cheat). The Cheat tab will allow you
|
||||||
|
to search RAM for values such as the number of remaining lives.
|
||||||
|
|
||||||
|
Step 5: In the Cheat tab, enter "5" in the "Enter a value" box and click
|
||||||
|
on Search. This searches RAM for your value and displays the results in
|
||||||
|
the Address/Value list to the right. You should see two results: "00a5:
|
||||||
|
05" and "00ba: 05". These are the only two addresses that currently have
|
||||||
|
the value 5, so they're the most likely candidates for "number of lives"
|
||||||
|
counter. (However, some games might actually store one less than the real
|
||||||
|
number of lives, or one more, so you might have to experiment a bit.
|
||||||
|
Since this is a "rigged demo", I already know Battlezone stores the
|
||||||
|
actual number of lives. Most games do, actually).
|
||||||
|
|
||||||
|
Step 6: Exit the debugger by pressing ` (backquote) again. The game will
|
||||||
|
pick up where you left off.
|
||||||
|
|
||||||
|
Step 7: Get killed! Ram an enemy tank, or let him shoot you. Wait for
|
||||||
|
the explosion to finish. You will now have 4 lives.
|
||||||
|
|
||||||
|
Step 8: Enter the debugger again. The Cheat tab will still be showing.
|
||||||
|
In the "Enter a value" box, delete the 5 you entered earlier, and replace
|
||||||
|
it with a 4. Click "Compare". Now the Address/Value list should only
|
||||||
|
show one result: "00ba: 04". What we did was search within our previous
|
||||||
|
results (the ones that were 5 before) for the new value 4. Address $00ba
|
||||||
|
used to have the value 5, but now it has 4. This means that Battlezone
|
||||||
|
(almost certainly) stores the current number of lives at address $00ba.
|
||||||
|
|
||||||
|
Step 9: Test your theory. Go to the RAM tab and change address $ba to
|
||||||
|
some high number like $ff (you could use the Prompt instead: enter "ram
|
||||||
|
$ba $ff"). Exit the debugger again. You should now see lots of lives
|
||||||
|
at the bottom of the screen (of course, there isn't room to display $ff
|
||||||
|
(255) of them!)... play the game, get killed a few times, notice that
|
||||||
|
you have lots of lives.
|
||||||
|
|
||||||
|
Step 10: Now it's time to decide what sort of "ROM hack" we want to
|
||||||
|
accomplish. We've found the "lives" counter for the game, so we can
|
||||||
|
either have the game start with lots of lives, or change the game
|
||||||
|
code so we can't get killed (AKA immortality), or change the code
|
||||||
|
so we always have the same number of lives (so we never run out, AKA
|
||||||
|
infinite lives). Let's go for infinite lives: it's a little harder than
|
||||||
|
just starting with lots of lives, but not as difficult as immortality
|
||||||
|
(for that, we have to disable the collision checking code, which means
|
||||||
|
we have to find and understand it first!)
|
||||||
|
|
||||||
|
Step 11: Set a Write Trap on the lives counter address: "trapwrite $ba"
|
||||||
|
in the Prompt. Exit the debugger and play until you get killed. When
|
||||||
|
you die, the trap will cause the emulator to enter the debugger with the
|
||||||
|
Program Counter pointing to the instruction *after* the one that wrote
|
||||||
|
to location $ba.
|
||||||
|
|
||||||
|
Step 12: Once in the debugger, press Enter to refresh your prompt. The PC
|
||||||
|
should be at address $f238, instruction "LDA $e1". You want to disassemble
|
||||||
|
the ROM starting a few addresses before this to find the instruction
|
||||||
|
that actually caused the write trap, so enter the command "disasm pc-5"
|
||||||
|
(you may need to press Shift-PageUp afterwards to see the beginning of
|
||||||
|
the disassembly). Take a look at the first few instructions. Do you see
|
||||||
|
the one that affects the lives counter? That's right, it's the "DEC $ba"
|
||||||
|
at location $f236.
|
||||||
|
|
||||||
|
Step 13: Let's stop the DEC $ba from happening. We can't just delete the
|
||||||
|
instruction (it would mess up the addressing of everything afterwards,
|
||||||
|
if it were even possible), but we can replace it with some other
|
||||||
|
instruction(s).
|
||||||
|
|
||||||
|
Since we just want to get rid of the instruction, we can replace it with
|
||||||
|
NOP (no operation). From looking at the disassembly, you can see that
|
||||||
|
"DEC $ba" is a 2-byte long instruction, so we will need two one-byte
|
||||||
|
NOP instructions to replace it. From reading the prompt help (the "help"
|
||||||
|
command), you can see that the "rom" command is what we use to patch ROM.
|
||||||
|
|
||||||
|
Unfortunately, Stella doesn't contain an assembler, so we can't just
|
||||||
|
type NOP to put a NOP instruction in the code. We'll have to use the
|
||||||
|
hex opcode instead.
|
||||||
|
|
||||||
|
Now crack open your 6502 reference manual and look up the NOP
|
||||||
|
instruction's opcode... OK, OK, I'll just tell you what it is: it's $EA
|
||||||
|
(234 decimal). We need two of them, starting at address $f236, so our
|
||||||
|
prompt command looks like:
|
||||||
|
|
||||||
|
rom $f236 $ea $ea
|
||||||
|
|
||||||
|
The debugger should respond with "changed 02 locations". If you run
|
||||||
|
"disasm pc-5" again, you should see the two NOPs at $f236 and $f237.
|
||||||
|
|
||||||
|
Step 14: Test your patch. First, set location $ba to some number of
|
||||||
|
lives that can be displayed on the screen ("poke $ba 3" will do). Now
|
||||||
|
exit the debugger and play the game. You should see 3 lives on the screen.
|
||||||
|
|
||||||
|
Step 15: The crucial test: get killed again! After the explosion, you
|
||||||
|
will *still* see 3 lives: Success! We've hacked Battlezone to give us
|
||||||
|
infinite lives.
|
||||||
|
|
||||||
|
Step 16: Save your work. In the prompt: "saverom bzhack.bin". You now
|
||||||
|
have your very own infinite-lives version of Battlezone. The file will
|
||||||
|
be saved in the current directory (NOT your ROM directory), so you might
|
||||||
|
want to move it to your ROM directory if it isn't the current directory.
|
||||||
|
|
||||||
|
Step 17: Test the new ROM: exit Stella, and re-run it. Open your ROM
|
||||||
|
(or give its name on the command line) and play the game. You can play
|
||||||
|
forever! It worked.
|
||||||
|
|
||||||
|
Now, try the same techniques on some other ROM image (try Pac-Man). Some
|
||||||
|
games store (lives+1) or (lives-1) instead of the actual number,
|
||||||
|
so try searching for those if you can't seem to make it work. Also,
|
||||||
|
some cartridge types include their own RAM. The debugger doesn't (yet)
|
||||||
|
know how to access on-cartridge RAM, so you'll have to use the "bank" and
|
||||||
|
"ram" commands to manually search the address space for the value you're
|
||||||
|
looking for (future versions of the debugger will be smarter about this).
|
||||||
|
|
||||||
|
If you successfully patch a ROM in the debugger, but the saved version
|
||||||
|
won't work, or looks funny, you might need to add an entry to the
|
||||||
|
stella.pro file, to tell Stella what bankswitch and/or TV type to use.
|
||||||
|
That's outside the scope of this tutorial :)
|
||||||
|
|
||||||
|
Of course, the debugger is useful for a lot more than cheating and
|
||||||
|
hacking ROMs. Remember, with great power comes great responsibility,
|
||||||
|
so you have no excuse to avoid writing that game you've been thinking
|
||||||
|
about for so long now :)
|
||||||
|
|
Loading…
Reference in New Issue