Purpose: This guide is written as a tutorial for someone who would like to learn how to hack the game code (ROMS) themselves.
Ikari warriors has no documented free play option (interestingly enough, the ROM has a string "FREE PLAY" embedded into it) and I wanted to add a free play feature to it so I didn't have to coin up on my home Ikari machine.
To do this we have to hack the ROM itself. Here's how I've done it using Mame and Hex Workshop.
Tools Used: MAME and Hex Workshop
MAME is a free arcade system emulatator with support for all the old CPUs.It also has a nice debugging feature that has a built in disassembler (no assembler though :( ) and it allows you to set breakpoints, watchpoints, and view memory and alter RAM. It's also very nice as Mame itself emulates the actual system so you can run your code on your PC and see the effects immediately... MAME ROCKS. When you start mame make sure to give it the debug flag
mamed ikari -debug
It's also important to note that I unzipped the ikari warriors files into the "roms" directory in MAME, and DELETED the zip file. This way I can work on individual ROM files. The version of the ikari roms used in this write up are the US JAMMA version ROMS. (mame refers to them as "ikari")
Hex Workshop is a very nice Hex Editor, I don't have a z80 assembler, so for this hack I assembled the machine code by hand and used Hex Workshop to insert it into the ROM image.
Hacking the game:
The way I attacked this was the following steps
* Note: my initial approach (which is probably cleaner) was to try to locate the code that checked if youd had enough credits to play when you hit start and simply return "true". However due to issues I could not do this as I could not set a watchpoint and hit coin up fast enough before another polling cycle hit the watchpoint and stopped execution before my credit was accepted.
- Disable the ROM checks
- Set the credits to 99 after game initialization *
- Change the code that decrements the coin counter on game start so
rather than decrementing by 1 it decrements by 0, therefore you always have 99 credits. *
- Replace the text "Credits" with "Free Play"
Step 1 - Disable the ROM checks
The JAMMA version of Ikari Warriors game code logic (not including graphics etc) is contained on a single ROM, so this is the only ROM that needs to be hacked. In the MAME romsets this rom is labeled 1.rom. Unforunately the JAMMA version of Ikari warriors performs a ROM check upon boot, if the ROMS are altered on purpose or corrupted by accident the code will detect that and stop the game from booting. If you try to alter the ROMS you will get a screen that looks like this.
To edit the game ROM you must alter the ROM checking code, but first we have to find it! The way I did this is to alter the ROM in a "safe" place, then setup mame debuger to set a watchpoint for when this memory address is read. Since the ROM checking code has to read every byte to compute it's checksum, we know we can catch the code like this. Once that memory address is read the watchpoint will stop code execution.
First use Hex Workshop to open the ROM and then to search for a string (INSERT). I noted the address of INSERT (0xB58) and then altered INSERT to CHANGE and saved the ROM.
Then in mame debug mode, set a watchpoint for a read on the address of the first character you changed (0xb58) the command in mame debugger is
Hit f5 to start mame running and it will soon stop. You should see this image in MAME.
Your debugger will be set to the instruction AFTER the one that accessed your memroy.
Now pay close attention to what you see. The line above the highlighted line accessed your memory. the instruction is "add a,(hl)" which takes the value of the address pointed to by the register HL (your address 0xb58) and adds it to register A. You can see on the left the HL register has your value 0xb58. What this code is doing is adding up each byte of the ROM in a loop. hit F11 a few times and see it keeps looping through instruction at 0x1184 - 0x118B over and over again. Note also during this loop, register BC is decremented by 1. DC is actually counting BACKWARDS. once it hits zero, the loop will end and you'll be at instruction 0x118E (ld (ix+$00),d). This will take some time, so let's speed the process by entering
Then hit f5 to resume, it will then stop almost instantly. You should see the following
Notice that your BC counter is definitely 0000!
you are just a few instructions away from that ret instruction which will return you to the calling function. Let's hit F11 3 times.
You should now be back to the calling function and at instruction 0xE3F
Notice that you are only a few instructions from a call instruction which calls a subroutine.
Hit F11 3 times to get to this point.
What that call does is copy blocks of memory around. You can actually see the results if you step into it, but for now let's step OVER it by hitting F10 (stepping over means that the debugger will enter the subroutine and run all the code in the subroutine and then stop after the subroutine ends. In this case that is instruction at address 0xE4A (ld h1, $1709).)
At this point you should see your MAME output changed.
and your debugger looks like this
Now we are on instruction 0xE4A (ld hl,$1709)
Hit F11 one time so you are at instruction 0xE4D (call $19A0), then hit F10 to step over it. and Voila! look at your mame screen.
Wow.. look at that... it printed out CPUA RAM OK, and CPUA ROM...
You should currently be stopped in your debugger on line 0xE50 (call $1194). I wonder what this function call does? I bet if you step over it this is where you lock up.
Let's find out, hit F10 to step over it and let's see what happens.
You guessed it, that's the code that checks the ROM and locks us up. So what can we do? Well all we have to do is NOT call that function when we get to 0xE50.
let's just replace that call with the previous call that succeeded so we can bypass that ROM check!.
What we need to do is replace the instructions at 0xE50 (CD 94 11) with the previous successful call from 0xE4D (cd A0 19). Fire up your Hex Workshop, find memory location 0xE50 and change the next 3 bytes to go from
CD 91 11 -> CD A0 19
Then save your changes.
Quit Mame and then restart mame with the debugger and see if your game passes the ROM checks (remember to hit f5 to start mame running)
Whoo whoo it worked! We can now make changes to our ROMs! On to Step 2
Step 2 - Set the credits to 99 after game initialization
Step two in this process is to give ourself some credits, it really doesn't matter how many, since later we will alter the code so the game never deducts credits when we start a game so we'll never run out of credits, but for this example I choose 99.
The first thing we need to do is to find where the number of credit is stored in RAM. The way I actually found it initially was rather round about trying to track code flow, and that would be too difficult to write up in a clear manner. So for this example I'm going to instead describe a more straigh forward method that I've used before.
The first thing we need to do is add some quarters to the system. Let's coin up (hit 5) seven times so we have 7 credits. Verify your Credit counter at the bottom right had of the Ikari warriors game says "CREDIT 7". Now let's use MAME debugger to search for the number 7. The command to do this is
this means look through address 0 - 10000 searching for the number 7.
Unfortunately that returned alot of entries. Write them all down on a sheet of paper. (too bad you cannot cut and paste them to a file :(
What we are going to do now is add another coin (hit 5) and do the search again, this time for 8 instead of 7 (since we now have 8 credits). take the list of returned values and find the memory address (or possibly a few addresses) that was returned by both searches! this probalby will only return one or a few addresses and let's us figure out which one is probably the real address we are looking for.
Whoa!!! did you notice anything, the LAST line in both of these as 0xFC5C! That's convient! Whats the chances of that happening (really... that's really lucky... otherwise we'd have to write down all these addresses and find the addresses that were common to both).
So now we THINK 0xFC5C is the address that stores our credits. let's test our theory. MAME allows you to directly edit addresses that correspond to RAM while the system is running (you CANNOT do this with ROM addresses). Let's pull up the memory window (Debug->New Memory Window) and enter the address we are interested (0xFC5C) in in the top bar. then hit return to update the memory window.
Now click in the 8 of "08" and type 9,
The memory window will update from 08 to 09 AND note your CREDIT counter now also says 9! Congratuations you found the memory address of the credit counter.
Interestingly this number is stored in Binary Coded Decimal, NOT in normal "hex "representation.
OK now we just need to write 99 to this location when the game starts. Most games will actually initialize their important memory locations to 0 before allowing anyone to use the system. This is done to ensure that there is no "garbage" in memory like having some postive number of credits in the credits counter, which would give people free games when the system turned on. let's try to find the place where the game initializes that memory location and change it to initialize to 99 credits rather than 0.
To do this, restart mame (hit shift-F3) but do NOT start the game running yet.
Open your MAME memory viewer (Debug->New Memory Windows) and type in 0xFC5C and hit return and make sure you can see the correct memory address.
Now let's set a watchpoint so MAME will stop whenever the system WRITES to memory address 0xFC5C the command do do this is
Now hit F5 to start mame running
MAME will initially stop, and memory location 0xFC5C will be at value 55, this is not the initialization stage, hit F5 to continue.
MAME will stop again, this time memory location 0xFC5C will be value AA, this is still not what we want, hit F5 again to continue.
The third time is the charm here, look memory location is 0xFC5C is 00, this MIGHT be what we want!
Now let's look at the debugging windows for that step."ld ($F5C5),a" and the corresponding machine code is "32 5C FC"
The code has stopped at the instruction immediately after our memory was altered. Our memory was actually altered with the instruction at address 0x00D0 which is "ld ($FC5C),a" which means "load the value of register A into memory address 0xFC5C". We can see that the value of A is 0 if you look at the register section of the debugger window. What more you can see that immediately above the instruction that altered us, register A was set to 0 with the instruction "xor a" which is an optimized way for setting register A to the value of 0. What more... look at the next couple instructions, they all are loading register A (0) to HARD CODED memory addresses.(these are probably important memory locations!) I'm fairly certain this is the initialization code. Let's first write down the address of the instruction that actually initialized the credit counter, it's address 0x00D0 and the command is
the machine code refences the memory location as 5C FC (reversed) because the z80 is little endian procesor (wikipedia this if you are unsure what this means :).
anyway let's verify this was the initialzation section by hitting F5 to continue. You will notice the game continues on and does not stop again. So that must be our initialization routine!
Now all that's left is to replace the code at 0x00D0 with something that initializes the code to 99 rather than zero. Unfortunately we cannot just put the code directly at memory location 0x00D0 as that code requires more space than we have. (the code we are replaces is only 12 byes long). however you know what is also 12 bytes long? A call instruction. (a call instruction let's you jump to a new memory address and execute code there, then the ret instruction will let you get back when your done!
So we are going to replace the machine code at 0x00D0 with a call instruction. Now we need to find somewhere in the code that is free for us to insert code into. Since the size of the ROM is fixed there is probably some leftover space at the end of the code. usually this space will show up as a series of "FF"s as that's how a ROM programmer puts in non-used locations. Get your MAME memory veiwer window and set the memory location to 0x0000 (the begining) then scroll down you hit a bunch of FF's.
You should notice at 0xBEC0 the memory turns into a bunch of FF's.
We can choose any address in there to put our code. I'm going to skip a few bytse just incase one of those first FF's is not really "blank" I wouldn't want to overwrite code that NEEDS to be FF.. so for safe measure I'll start putting my code at memory location 0xBEF7.
Now this is the tricky part. I want to write code that LOADS the value 99 (BCD) into memory location 0xFC5C, then returns back to the calling code. Since I don't have an z80 assembler I"m going to do this by hand using the z80 reference guides.
The guide I used is at http://nemesis.lonestar.org/computers/tandy/software/apps/m4/qd/opcodes.html
Here's what I came up with.
|PUSH AF||F5||Save the contents of registers A&F to the stack|
|LD A 99||3E 99||Load register A with the value 99 (BCD)|
|LD ($FC5C),A||32 5C FC||Store register A to memory locations FC5C (Z80 is little endian)|
|F1||F1||restore the initial values of register A & F|
|ret||C9||Return back from the subroutine call
SO let's open that hex editor, find the locations for 0xBEF7 and insert the machine code above.
Wait though we are not done yet... we also have to edit the code at 0x00D0 (32 5C FC) with the code to "call $BEF7" which in machine code is "CD F7 BE"
Save your code and restart MAME.
You did it! Great job!
Step 3 - Change the code that decrements the credits when a game is played.
Now we have 99 credits to start with but if we play more than 99 games... we would have to credit up again :( so for step 3 we are going to ensure we don't lose credits for each game we play :)
Normally when a player presses 1 or 2 player start, the game checks to make sure they have enough credits and if they do it decrements the credit counter by 1 and starts the game. Note do do this the game has to WRITE to the memory address where the credits are stored. From the last step we know this is memory address 0xFC5C. So let's set a watchpoint on 0xFC5C that will fire when this address is WRITEN to, and we should be able to find the code that does the decrementing.
The command to set a write watchpoint at address 0xFC5C in MAME debuger is.
Let's restart the mame debugger from scratch, and BEFORE you start running mame enter the command to set the watch point
Now that the watch point is set, let's start mame by hitting F5. You notice the game will almost immediately freeze. This is the system initializing. Hit F5 to continue. The game will actually freeze 2 more times as the system does testing and initialization, hit F5 3 more times to skip these screens. (We saw this in the last step)
The game should now be running as normal. After initialization the system doesn't write to the credit counter at 0xFC5C unless someone enters a coin or starts a game.
Now theoritically if we start a game, the system will decrement the credit counter and we should find that code that we are looking for. Let's try it! We already have 99 credits so let's just hit 1 to start a game. and bam the system stops.
Let's look in our debugger where we are. The system should have stopped at the line AFTER your memory was written too. we stop at instruction 0xA56 (ret), and look above it the instruction "ld (hl),a", which loads the value of register A into the memory address pointed to by register HL, if you look at the value of register HL it's 0xFC5C which is where the credit counter is in memory.. awesome. and it was loading the value of register A which is 98 (in the register listing it shows register AF (9846) register A is the left most part of that (98). I bet if we look above a few lines... something probably just decremented a, and in fact, there it is just a few instructions above at 0xA52 is the command "sub $01" which means subtract 1 from the value in register A (sub on Z80 uses register as the source and the destination). Note the actual machine code on the right side of the debugger for that command "D6 01" here D6 is the command for "SUB" and "01" is the 1 that you want to subtract. OK so we need to rembmer at address 0xA52 is the bytes D6 01.
Let's get our handy hex editor find address 0xA52 and change the data stored there from "D6 01" to "D6 00". If we are right, now when the code runs it will always subtract 0 instead of 1 and we'll never lose credits.
Now let's test our theory. save the changes in the hex editor, and restart MAME. This time don't add any watchpoints. Instead
- notice you started with 99 credits (from the hack in the last step)
- start a new game (hit 1)
- wait for the game to end
- and notice that you still have 99 credits!
Additional Things to Do
OK you've learned alot and hopefully have the basic skills. Heres some additiona
l things you can do to polish things up to make an even nicer ROM hacks. These additional items will help you apply your skills in different ways and learn more. I will leave you some hints on how to accomplish them... but they are up to you to figure out. Feel free to contact me if you want help.
SPOILER ALERT!!! SPOILER ALERT!!! SPOILER ALERT!!!
- Remove the "CREDIT" text and add a "FREE PLAY" banner.
If you've followed along so far and understand what went on the previous steps you should have NO problem with this.
Use your hex editor or mame debuger to search for the string "CREDIT", and just play around with altering the text to see what happens.
- Remove the "99"s on the right and the left of the bottom of the screen
HINT: This is similar to the step above... however with a twist, this display is not statically displayed, it's derived from the coin counter. Figure out a way to make the resulting text the character code for an ASCII space "0x20")
- Rather than disabiling the checksum verification, figure out how the checksum test actually work... then re-encode the data and patch the code so that the game checksum stored in the ROM matches your actual checksum
HINT: The checksum verification code initialized register A to 0, then adds the contents of register A to each byte in the ROM up to byte 0x4000. Since register A is only 8 bits if the number surpasses 255, it starts over at 0 again. (that is if A=200 and the next rom value = 57, after adding A + rom value, A will equal 2. To solve this problem you will need
- To find where the ROM chksum value is encoded in the ROM.
- write a program to compute your own ROM value
- find an byte that's not being used, and add a certain number to that byte such that the rom checksum after everything is patched = the value incoded in the ROM. This is not as easy as you'd think, but it's not terribly hard either.
If you are interested in the answers to the "Additional things to do"
here in an excellent write up of one persons answers. You REALLY should try yourself though before looking at the answers, also like in most things in life, there are more then one way to solve the problem. Thanks to Mike for letting me link to his most excellent solution write up!
Here is also a video I as part of my arcade hacking video series for another simple way of bypassing the checksum tests.
The hacked ROMS
JAMMA VERSION (1 rom)
Click here for the final ROM image for the jamma version of ikari (altered from 1.rom in the MAME set "ikari")
NON-JAMMA VERSION (2 roms)
Click here for ROM p1.bin final ROM image for the non-JAMMA version of ikari (with continues allowed) (altered from p1.bin in the MAME set "ikaria")
Click here for ROM p2.bin final ROM image for the non-JAMMA version of ikari (with continues allowed) (altered from p2.bin in the MAME set "ikaria")
a picture of the non-JAMMA version (with continues) freeplay ROMS above that have been futher modified for the personalization and to remove the 99 credit counter running on the real hardware.
Victory Road (Ikari Warriors II) Freeplay hack (complete with correct checksums and "prety freeplay" title).
Click here for ROM p1.bin