Jumping out of a function, injecting new code, and jumping back to modify in-game variables tutorial
Introduction
I will explain below how to jump out of a certain function, add some new code to a blank area in the ROM, and jump back. This sort of thing is useful for many different hacks, but especially in cases where there are no NOPs to add new opcodes into. Even if there are some NOPs in the function you’re interested in, it’s sometimes useful to jump out in order to add more code than there are NOPs. I’m going to be using the following programs:
1. NEmu64 – command, register, and memory debuggers for finding opcodes and hex values
2. LemAsm – for modifying actual ROM and saving the changes
3. Cygnus hex editor – for finding the opcodes in LemAsm originally found in Nemu
This tutorial is written using Zoinkity’s hacked 2000 version. Other versions can probably similarly be hacked, but the addresses are most likely different. The hack that will be discussed here is related to the tackle speed. Blitz works by having multiple functions control tackle speeds. Thus far, I have found 4 total:
1. Lateral (left – right) tackle speed, not locked onto target
2. Vertical (up – down) tackle speed, not locked onto target
3. Lateral tackle speed, locked on
4. Vertical speed, locked on
By “locked on” I refer to how the game mechanics handles tackling. It works as follows: if the tackler is within a certain distance to the opponent (player to be tackled), the game adjusts the course of the player to hit the opponent. It’s similar to an auto-aim feature in a FPS. It’s usually a very small adjustment, but it is there. But more importantly, I found that being within this “zone of locking on” writes the speed of the tackler from a different function than if you are not. Thus, if you are tackling nowhere near anyone, the player tackle speed is being written to by (1) and (2) above; if the you are tackling very near an opponent, then (3) and (4) are. I think there are some other subtleties I haven’t worked out yet regarding how these functions work, but for the sake of the tutorial, this is about all you need to know about the mechanics of the game. We will be focusing on (1) above – modifying the lateral tackle speed when not near an opponent.
A few things to mention before getting started: Thus far from the various hacks I have done regarding the physics of the game (player speed, tackling, gravity, etc), they are usually heavily arithmetic – lots of math working out where the player will be next, speed, etc. This is convenient because it’s usually quite easy to read the assembly and follow what exactly is happening. Usually there are constants that will be set throughout the function, in our case the lateral speed of the tackler that can be very easily modified either by changing the value, or adding more arithmetic operations to the value to increase/decrease whatever that value is.
Another thing to mention is that these functions are undoubtedly using floating point values for each operation, meaning that whatever value you are modifying is not going to be a whole number or integer such as 1, 20, 255, etc. It will usually be an irrational number of some sort such as 1.333, 1.43546, etc. This is important because the N64 has specific registers (registers = slots to stick temporary values needed for the function you are in) for floating point operations, located in COP 1 (Figure 1 shows an annotated screenshot of the COP 1 register window in nemu). There are 31 floating point registers that can be accessed – most of these are being used throughout a given function, but there are sometimes some that aren’t, and you can write your own values to those to apply arithmetic operations on the registers you want. The nice thing about Nemu64 (beyond all the other awesome debugging features it has) is you can monitor these registers very easily to see what values are being stored.
The lateral tackle speed function was found by following a similar procedure found in a previous tutorial I wrote regarding finding variables. The address corresponding to the players lateral tackle speed is written to by 8027F1CC, which has the operation SWC1 F4, 0x0014(S0). This can be seen, along with the previous and subsequent operations, in Figure 2. It should be noted that the game processes these operations in-line, meaning that they go sequentially through the operations, unless there are jumps, branches, etc.
This operation code (opcode) stores the value into the floating point register F4 (seen in Figure 1). This means that we need to modify whatever F4 is prior to this operation. If you look above two lines at 8027F1C4, the opcode is SUB.S F4,F4,F2. This operation is doing the following: F4 = F4 – F2. So it takes whatever value was in F4, takes away whatever was in F2, and stores that value in F4. If one wanted to, this opcode could be replaced with NOP (no operation; does nothing) and this would in itself modify the lateral tackle speed. However, we don’t want to simply delete this, we may want to increase the speed of the tackle. Unfortunately looking at the list of operations in the command dialogue box shows no NOPs where we could stuff an extra bit of code to modify F4. Therefore, we must jump out of this function, go to a new area in the ROM where there is no pre-existing code, add our code, and then jump back. These changes can be made directly to the Nemu command window and memory viewer, but any changes made here will be temporary and will not be permanent. Therefore, we must modify the actual ROM itself in order to make it permanent (The reason we can modify the operations of the game in the RAM but it is not permanent is the 64 will load the whole ROM into memory, which we are modifying in Nemu – therefore, we are not modifying the actual ROM itself, just the “RAM image” of the ROM).
To view the ROM that also has the opcodes as well (rather than just the hex, which is essentially indecipherable), download LemAsm. This program allows you to load a ROM, add/edit opcodes, write and save to the ROM itself. However, the addresses in RAM (in nemu) and the ROM (in lemAsm/hex editor) are not the same. There is an offset between them that must be determined. How that is done is beyond this part of the tutorial, so I will say the easiest way to find the function you want is to do a search match of the hexadecimal of the function you want to find and the instructions nearest it in a hex editor, such as Cygnus. So if you are looking for the hex corresponding to the SWC1 F4, 0x0014(S0), which is E6040014, you can search for this value and a few more hex codes following this. Don’t simply search for E6040014 as there are probably at least 50 of these codes throughout the ROM. Once you find the correct address in the ROM in the hex editor and LemAsm, which is at 00065EEC, we can start modifying the actual ROM (Figure 3).
As mentioned previously, there are no NOPs that can be replaced with an operation to modify our value. Therefore, we must jump out of the function to change it. We can use the opcode “J $XXXXXXXX” where J is the jump command, to some address, XXXXXXXX, to execute some new code. This is where things may get complicated: we cannot simply search the ROM for some empty space and jump to it. So for instance, 00071800 has a bunch NOPs, so we could stick some new code here. But we can’t simply Jump to 00071800 – we must jump to its RAM value. This value is calculated as follows: RAM = ROM - 1000 + 80000400. This is all in hex, by the way, not decimal. So to calculate the RAM address for the ROM address we have, we do: RAM = 00071800 – 1000 + 80000400, which is equal to 80070C00. Therefore, our jump command that we type into LemAsm will be “J $80070C00” (no quotes). Now I have not mentioned where we will add this. So the question then is, where should we add this jump to inject some new code? So far, I have found that it is best to modify the register of interest as near to the address that writes your value (8027F1CC in ram, 00065EEC in ROM). This means find the last operation to modify your value (F4), execute this operation, and then jump to your code.
Important!!!!!!
LemAsm has a bug where when you type the SUB.S command, it will actually be a DIV.S command. Therefore, when you change the ADDU command to the SUB.S F4,F4,F2 command, you will have to go into a hex editor to change the hex value of that line.
The error erroneously changes the last value from 01 to 03, which calls the DIV.S command. This will ALWAYS happen when modifying the SUB.S command, regardless of location or registers, so be careful!
So the last operation on F4 prior to writing it is 2 lines above the SWC1 command. It is the SUB.S F4,F4,F2 opcode. After this is an ADDU V0,R0,R0 operation. This one doesn’t actually affect our value, so modifying when this occurs shouldn’t change our function too much. To add our jump and then add our code, we do the following: 1) replace the SUB.S F4,F4,F2 opcode with our Jump, 2) change the following ADDU V0,R0,R0 opcode to the SUB.S F4,F4,F2. Why do we need to do (2)? This is because the Jump command (as well as other jump opcodes such as JR RA or JAL) always execute the command immediately after it. Therefore, if we put our jump where the SUB.S command is, we should move the SUB.S command where the ADDU command is so that it still is executed at the right time. So what happens to the ADDU opcode? Well now we go to the location of where our new code goes, which we found in the ROM to be 00071800. There is nothing but NOPs here, so we know we’re not replacing actual code or other functions. The first thing we should add here is the command we wrote over in the previous function: ADDU V0,R0,R0. We can now add whatever operations we want to modify F4 (or really any other registers if we wanted)!
Let’s say you want your lateral tackle speed to be very fast. Well currently F4 is ~1.6 = 3FCCCCD. We could double this value by adding the line ADD.S F4, F4,F4. This will do the function F4 = F4+F4, which is equal to 1.6+1.6 = 3.2. So now your lateral speed is 2x as fast! You can do other arithmetic operations here however you want. After you add your new code, you need to jump back to your original function. This is easy – just figure out the address 2 down from your original Jump (our original jump was at 65EE4, so 2 addresses down is 65EEC). Compute the RAM value for this ROM address as explained above (it is = 80065EC). So simply type J $800652EC after your code, and you will jump right back into the original function, now with your modified F4 value! It should be noted that LemAsm will automatically change your 8 at the beginning of the RAM address to a 0, so your 800652EC will look like 000652EC.
This is a simple example of how you can jump out of a function, add new code, and then jump back like nothing happened. This is very useful when there are no NOPs to add new code, or if your new code takes up too much space. Just remember that the registers you are modifying shouldn’t be over written. If you want to really get good at ROM hacking the assembly, you need to familiarize yourself with the MIPS assembly opcodes. As explained in this tutorial, SUB.S is a subtraction operation, ADD.S is an addition, but there are many more beyond just simple arithmetic operations.