So I had what seemed like a pretty good
Golden Axe Warrior hack: my key problem, needing to grind to get back HP and MP, solved. Losing money on death, gone. Rockin'. I started playing the game to make sure it would perform ok, and got about halfway through (5 of 9 crystals recovered). Doing so reminded me of the need to use the earth magic spell to investigate rocks to find hidden rooms and clear pathways, and the fact that if you run out of MP, which you quickly do when shooting magic at every rock, you have to get more before you can look again. Getting MP back on death or staying at an inn helped a great deal with that, but I thought I could do better. Also, the fifth dungeon introduces enemies who inflict the rusty status, which can only be healed with the magic oil, which you can only carry one of at a time, meaning taking a hit from one of them after you've healed once requires either leaving the dungeon and finding a shop with oil for sale or just dealing with lowered defence for the rest of the dungeon. I took on the earth magic's MP cost first, but the rust thing was simpler so let's go through that:
I took this screenshot after figuring things out, but I'll try to explain my process. In the lower part of the Memory Editor are a bunch of RAM addresses that control various aspects of the player character's status. 18 20 80 80 1C for example are respectively a mystery variable, my max HP, my current MP, my max MP, and how much cash I have. The random 01s in the bottom two rows are for the items I have - the 00s in the same place are mostly items I don't have yet. I started off by just randomly changing 00s to 01s in the hope that one of them stored the rust on/off status. This got me lots of items and occasionally crashed the game or messed up the graphics, but I didn't find what I was looking for.
So I got a little cleverer - I looked up the cheat code for having the magic oil item which cures rust. This let me know that that variable is stored at C0E2, so I set a breakpoint in the Meka Debugger for when that address gets written to, set it to 01 so that I had some oil, and then set off for the dungeon with the rust monsters (scorpions, I think - I wonder if it was supposed to be a poison status?). I got inflicted with rust, used the oil, and the debugger stopped execution after C0E2 was dropped from 01 (I have oil) to 00 (oil gone). You can see the code for this in the debugger on the right of the screenshot, just above the highlighted line:
Code:
AF xor a
32 BF C0 ld (C0BFh),a
32 E2 C0 ld (C0E2h),a
What's happening here? Well, the first instruction, XOR A, takes the 8-bit value stored in register A and XORs it with itself bitwise. In other words, it compares each bit in A against the same bit in A. If the value of each is the same (which it always will be), the result of the eXclusive OR operation is zero, so it sets the bit in A to zero. To put it more simply, this instruction sets the value of A to 00. You could of course use a load instruction: LD A 00, but that would require two bytes and I think slightly more execution time (and, though this isn't immediately relevant, would have no effect on the Z80's flag register while XOR would). Anyways, A is set to zero. The next two lines then load that zero value into memory addresses C0BF and C0E2. We know that C0E2 is "do we have any oil?", but what does C0BF do? As it turns out, exactly what I want it to - I go into the memory editor and set C0BF to 01, and my guy is rusty again. I've found the value I want.
Now that I know the right memory address, I need to figure out what I want to do about it. One option would be to set a timer and have the rust go away after a certain time, but that would be more complicated than I want to be. Another would be to allow the player to accumulate more than one thing of oil, but again that would be complicated. I decided to keep it simple: I'll make the rust status revert to normal when you die or rest at an inn. And as it turns out, that's pretty easy for me to do, because I've already been fiddling with the code for death and resting.
If I look at my existing modification to the code:
3E 06 32 1D C0 3A DA C0 32 18 C3 3A DC C0 32 DB C0 C9 00 00 00 00 00 00 00 00 00
There's a whole bunch of zeroes at the end of the machine code. That's space where there used to be instructions but there aren't any more (because I removed the part that deducts your cash on death). So I can just stick a bit of code in there that'll fix the armour:
Code:
AF XOR A, i.e., set A=00
32 BF C0 LD (C0BF), A - load the value of A (00) into memory address C0BF
So I just stick AF 32 BF C0 in before the C9 (return) in my previous code, delete the excess 00s at the end, and rust will be cured on death/rest. Easy!
Now on to the tough stuff: making the earth spell free to use for breaking rocks and blocks. The simplest way to do this would be to make it always free, or to reduce the cost per cast as a middle ground move. To do that, I just need to find the place in the code that deducts the MP and change the value it deducts. In the above screenshot, I've started a new game, used the memory editor to give myself earth magic (set C0E5, the byte of RAM to the left of the selected byte, to 01), set a breakpoint for writes to the address where current MP is stored, and then cast the earth spell. As expected, the breakpoint has kicked in and stopped execution. Let's look at the code:
Code:
6E ld l,(hl) Load l with value stored at hex value of
register hl in memory
3A DB C0 ld a,(C0DBh) load a with current MP
95 sub l subtract l from a
D8 ret c if carry flag set (l bigger than a in previous), ret
32 DB C0 ld (C0DBh),a set current MP to value of a
C9 ret
This appears to be a generic subtract routine rather than one specific to this spell - instead of loading a specific value and subtracting that from current MP, it gets a value from the memory address in the HL register and subtracts that value from current MP. If this subtraction takes current MP below zero, then the routine stops early (that's the ret c instruction - it means return from this routine if the carry flag is set). Otherwise, it puts the subtracted value into current MP and the routine ends. So I have two problems: one is that I don't know where HL was last set, and the other is that if this routine is shared by other parts of the game program then fiddling with it to modify the earth spell might break other stuff. The first problem should be easy to resolve: MEKA, the emulator I'm using, has a trace function in the debugger that can give me a list of recently executed instructions (I could just look at what comes earlier in the file than this code, but because execution can jump around in the file what's earlier in the file may not be what was recently executed). Unfortunately, for reasons I haven't been able to figure out, the tracer is not currently working for me. It worked fine when I was hacking Wonder Boy in Monster Land, but now when I try to use it it says there are zero logged instructions. I must have changed a setting somewhere or something, but I can't figure it out. Still, there are other options: the next instruction due to be executed is RET, which will move execution back to wherever this routine was called from.
Here I've stepped forward one instruction and jumped to where the subtract routine was called. The instruction above the highlighted one is call 30BBh, which is slightly earlier in the code than the subtract routine I spelled out above (I looked at the code at 30BB, but without access to the trace function to see what's going on before it's hard to make out exactly what it's doing - I think it's setting the MP cost but it seems needlessly complicated for that). I tried out the thunder and fire spells and they also made calls to 30BBh, but from slightly earlier and slightly later in the file respectively. So, although I don't fully understand this code, I'm pretty sure that the instruction at 307C is specific to the earth spell and starts a routine that checks whether you have enough MP and deducts the appropriate MP if so. The next instruction is ret c - the carry flag was set during the subtract routine if you didn't have enough MP, so I think that this ret is here to abandon the spell cast if you don't have enough MP. Time to experiment! I replaced CALL 30BBh with 00 00 00 - three no operation instructions. The intent was to remove the MP check and MP subtraction. Firing up the game, it worked! I had infinite use of the earth magic.
Which is not bad for my purpose of stopping you running out of MP when you're testing rocks looking for hidden things, but the earth magic also has an effect on enemies - it freezes them in place temporarily. I still want that to cost MP, and my plan is to have MP deducted when the enemy gets hit rather than when the spell is cast. For that to happen, I need to identify the place in the code where the magic is striking the enemies. In the screenshot above, I've just frozen one of the enemies with the earth spell (the little cloud to the right of the enemy to the right of my character is the spell). The memory editor shows the data for the four enemies on screen, starting from C600, C630, C660, and C690. I found this data in memory by going to a screen with enemies on it, then scrolling through the memory editor looking for patterns of data that changed as the enemies moved around and did things, then confirmed that it was enemy data by killing them one at a time and watching sections of data disappear. Then I started firing off the earth spell and looking for the byte in memory that was changing when the spell hit. I've highlighted it in red in the memory editor - in the enemy I've just hit it's FC, in the others it's zero. It turns out that the spell sets that byte (C636 in this case) to FF, at which point the enemy freezes and the byte starts to count down to 00 and the enemy starts to move again. I assume it's going down one point per frame, giving about five seconds of freeze in my native PALonia or slightly over four seconds in NTSCerica. So it's back to the debugger. Let's set a breakpoint for writing the value FF to byte C636 and cast more earth magic:
OK, we don't have a lot of code to go on here - we know the last instruction executed was ld (ix+6=C636h),FFh, but not what came before that. I forget if I've mentioned this before, but the reason it's only showing one instruction is that we must have reached this point from either a call or a jump, where the program moves from one point in the file to another. This sort of thing is why the debugger's trace function, which is not currently working for me, is so useful. Anyway, the next instruction is a return, which will take me back to the place we called here from (technically, the last place anywhere was called from - it's possible there have been jumps since, which are one-way). Let's take a look:
I'll be honest: I don't know what's going on here. I could probably figure it out, but actually it's not relevant. I'm not looking to remove anything from what happens when the spell hits the enemy. What I want to do is add MP reduction to the existing behaviour. So let's go back to the code in the previous screenshot:
Code:
DD 36 06 FF ld (ix+6=C636),FFh
C9 ret
The first line is the one we're interested in. It loads the value FF into the memory address at ix+6, which is currently C636, the one that stores the freeze counter. ix is one of the Z80's registers, and it appears it currently is storing the starting point of the relevant enemy's data (C630). The next line returns to whatever else the code was doing. My problem now is that there's no free space. I still want the freezing code to happen, but I need to add the MP reduction code. So there's really only one thing to do: put the code somewhere else and replace this load instruction with a call to my new code. Calling to a different address requires three bytes, so I can use the first three bytes of the four-byte load instruction and replace the last one with 00 - no operation. The program will jump to my new code, execute it, then return to the NOP, then hit the existing return and go back to what it was doing before. So let's take a look at the code I'm going to insert, as I wrote it in my notes file while making the hack:
Code:
new code:
LD a,C0DB (set a to current MP)
SUB n (subtract 08 from a)
RET C (if current MP less than 08, do nothing)
LD C0DB,a (set new MP)
LD (IX+6),FF (freeze enemy)
RET
Which is:
3A DB C0
D6 08
D8
32 DB C0
DD 36 06 FF
C9
But then I noticed that the earth cloud continues through enemies, hitting them repeatedly. With the code as above, you'd have all your MP chewed up by repeated strikes on the same enemy, so I added some more code at the start:
Code:
LD a,(IX+6)
SUB n (F0)
RET C
This loads the current value of the relevant byte into A, then subtracts F0 from it, and quits the routine early if the result is less than zero (which would indicate the enemy was already frozen). On reflection now I should probably have made that number lower in case the timer ticked lower than that before the cloud cleared the enemy, but (spoiler warning) I'll be removing this bit of code before we finish so it doesn't matter anyway.
The complete sequence is about twenty bytes. I needed to find somewhere to put it, so I just scrolled through the ROM in MEKA's Memory Editor until I reached the above - a big field of 00s. The last bit of code before all those zeroes is C3 44 74, which translates to JP 7444, an unconditional one-way jump to earlier in the code. So unless something in the program is jumping into this field of nothingness for some reason, it should be safe for me to modify this part of the ROM. There is one problem: the ROM is too big to fit entirely in the Z80's RAM, so it gets loaded in in chunks (I think the usual term is banks). This part of the ROM falls within the second chunk of the ROM, and the Z80 can fit three chunks at a time. Apparently most games always keep the first two chunks loaded and change the third RAM chunk to whichever ROM chunk is needed. Hopefully this is what GAW does and this section of ROM will always be in RAM. Anyways, hoping for the best I just copy/pasted my code in. And it didn't work - can you spot the error?
Code:
LD a,(IX+6)
SUB n (F0)
RET C
It's the last line - this code loads the value of the counter, subtracts F0, and drops out if the result is negative. Which is the opposite of what I want: if the counter is above F0, I want it to do nothing. If below, I want it to freeze. I need to change RET C (return if carry) to RET NC (return if no carry). So I do that, and it works. But! Now you get charged MP seperately for each enemy the spell hits, meaning a single cast against four enemies in a line might cost you four full MP pots. Not quite what I had in mind.
So I had a think, and landed on setting a variable in the RAM somewhere when the magic is activated, and then unsetting it when the first hit connects. Subsequent hits wouldn't need to check if the enemy was already frozen or if there was enough MP or to do an MP reduction, because the variable being unset while a spell was active would indicate it had already landed a hit and therefore was valid to land more. The only problem would be identifying some RAM that was never used by the existing program - I don't want to accidentally break something else, and I also don't want another part of the program randomly breaking what I'm trying to do. So I had a browse through the RAM in MEKA's memory viewer and found:
Another field of zeroes. Actually I found a couple of these, but this one stayed full of zeros as I played the game, whereas the one at C320, for example, looks good but fills with data when you cast a spell. There are probably better ways to find unused memory - I believe the emulator Emulicious has a function that specifically tracks which memory addresses have not been used while you play the game - but for now this seems to be suitable territory. My plan is to use byte CE01 in my code to see if it works, and then play the game through (probably just from where I'm up to, though) with a breakpoint for read/write actions on CE00 - if nothing accesses it in a playthrough then I'll move over to using CE00 (I don't want to set the breakpoint on the byte I'm using in my test code because it'll stop execution every time my code runs which would be annoying). Might also set a breakpoint on the RAM byte that controls which chunk of ROM data is loaded in the second slot to make sure it doesn't change and my extra code is always where it should be. Speaking of extra code, here's the routine I came up with:
Code:
3A 01 CE load value of CE01 into A
A7 AND a - sets zero flag if a is zero (i.e., hit not yet landed)
C2 XX XX if zero flag is not set, jump to XX XX (after MP deduction)
3A DB C0 load value of C0DB into A (current MP)
D6 08 subtract 08 from A
D8 return if carry (i.e., not enough MP)
32 DB C0 load A into C0DB (set current MP to new value)
3E 01 ld a,01
32 01 CE load a (01) into CE01 (has it hit flag)
DD 36 06 FF load (ix+6), FF - freeze enemy - make jump above to here
C9 return
XX XX in the above is where the second last line landed in the ROM once I'd put this sequence in, 7B 48 (actually written 48 7B because of endianness). It probably would have been better to use a relative jump rather than absolute, but that would have been an opportunity to miscalculate how far to jump so I did it this way. Anyways, I've skipped to the final version here, there were a couple of revisions. My new variable at CE01 is now set to zero when the spell is launched and changed to 01 when a hit lands, the reverse of how I first did it, which I changed to make the spell launching code more efficient. So basically,
- I load my variable into register a and check if it's zero or not
- If it is not zero, that means a hit has landed already and we can skip ahead to where the enemy gets frozen.
- Otherwise, we load the current MP into register a, then subtract 8 from it.
- If the result is less than zero, the routine quits and nothing happens - the enemy is not frozen, MP is not reduced, and the has-it-hit variable remains zero.
- If the result is zero or higher, then that result is set as current MP, the has-it-hit variable is set to 01, and the enemy is frozen (this last is the point that gets jumped to if the has-it-hit variable is already 01).
- The routine exits back to where it was called from.
Along with this I also needed to change the code for when the spell is cast. Previously, I just replaced the call to the MP subtraction routine with three NOPs. Originally, I wrote another routine to set the has-it-hit variable to 01, put it after the new code above, and replaced the three NOPs with a call to it. But then I realised that if I were to set to has-it-hit to 00 at this point instead of 01 I could do it in four bytes, and I also realised that the next instruction after the original MP subtraction call was a conditional return that I never wanted to execute, so I had an extra byte to work with, meaning I could fit in the new code without calling. This makes no difference whatsoever to the player experience, but it's a bit neater, so the new code there is:
Code:
AF (XOR A)
32 01 CE (set has it hit flag to A (00))
I put it all together, and it seems to work. Whew! Hopefully I won't have to find a different spot in RAM for the variable, or have issues with bank switching breaking my code. Otherwise, I'm done. I'm gonna finish my playthrough, and if nothing goes wrong I'll submit it to romhacking.net afterwards.