Guide For Calling Functions (ASM)
Guide For Calling Functions (ASM)

NOTE: This is for real code creators who already know how to read/write ASM.

Chapter 1: Intro

Mario Kart Wii was written in C++. Thus, it contains many generic C++ type functions (such as memcpy, memset, printf, sqrt, etc). Obviously, the game has its own unique functions as well such as DWC_Auth, DWC_Login, etc.

You can create a cheat code by including the calling of one of these functions or just calling the function by itself. The majority of the time, these functions will start off with an ASM instruction that uses the stack frame (aka sp, aka r1) in some way.

During a subroutine, certain instructions will occur, then a 'branch then link' (bl) instruction will follow. After the 'bl' instruction, you will be at the beginning of some sort of function call. The end of a function call results in a Branch to Link Register (blr).

I won't get into all the endless technical details of subroutines in Power PC ASM, this guide will simply demo how to call some very basic functions.

A list of many functions for MKW can be found HERE (PAL); some Wii & MKW specific functions are also included. Credits to Megazig for the original list. Credits to Star for finding the list and posting the thread plus additional new functions. Credits to RiiDefi for also the addition of new functions.

Also, here's a way smaller list (more detailed for arguments, return values, etc) for functions most commonly found in MKW codes -

Chapter 2: Manipulating the Link Register to Call a Function

You should already know what the Link Register is since you know some ASM. An easy way to call a function, is to have the address of that function in the Link Register, then branch to the Link Register. The function called OSRestart (responsible for restarting the game/disc) starts at address 0x801A8688 (PAL). Let's look at a set of ASM instructions below to see how we would call that function:

lis r12, 0x801A #Set 1st half address of PAL OS Restart
ori r12, r12, 0x8688 #Set 2nd half address
mtlr r12 #Copy value of r12 (0x801A8688) to the Link Register
blr #Branch to Link Register (Call/Start the function)

Once the blr instruction is executed, you will be sent to address 0x801A8688 and start executing the following ASM instructions that are there in memory. If one were to make a cheat code out of the above list of ASM instructions, this will cause the game/disc to reboot whenever that cheat code is used/activated.

Instead of using the Link Register, you have the option to use the Count Register. Here's that same source from above but using the Count Register instead of the Link Register.

lis r12, 0x801A #Set 1st half address of PAL OS Restart
ori r12, r12, 0x8688 #Set 2nd half address
mtctr r12 #Copy value of r12 (0x801A8688) to the Count Register
bctr #Branch to Count Register (Call/Start the function)

A completed activate-able code of the first source listed above is located HERE

Chapter 3: Arguments

Certain functions need 'arguments' established before being called or else the calling of said function will cause the game to freeze or black-screen. Usually the game prefers starting off with r3 as the first argument. Then proceeding to r4, then r5 etc etc for any other added arguments if required.

If a certain function you are calling is not working or you need to figure out what the arguments are, it is best the take the address of said function and set an instruction breakpoint on Dolphin. Do whatever is necessary in the game to make the game call the function. Then the game will 'break'. Take a look at r3, r4, r5, etc. by the way, r11 and above are never used as arguments for function calls. Sometimes the register values are actual integers values, or they are memory addresses pointing to the location of necessary integer values. They can also be memory addresses that hold another address which points to the location of a desired integer value.

The function known as SCGetSimpleAddr has 1 argument. This function is responsible for grabbing your country code (byte), residence code (byte), and globe location (word) from your SYSCONF file and storing it to memory. The argument (r3) acts as a 'reference pointer/navigator' of where the country code and globe location will be stored at in memory. r3's exact value the memory address to where your country code will be stored. r3's value plus 0x1 is where your region residence code will be stored. r3's value plus 0x1004 is where your globe location will be stored.

Chapter 4: Re-Linking

If we were to make a code to call the SCGetSimpleAddr function by utilizing a mtlr/blr method. The game will freeze. Most functions need a 're-link'. This re-link allows you to call the function and return back from where you started.

If using the Link Register to call a function, the instruction blrl (Branch to LR and Link) is what you need. If using the Count Register to call a function, the instruction bctrl (Branch to CTR and Link) will work.

Obviously function calls that are responsible for actions such as shutting down the Wii do not need a re-link. Anyway, let's look at a snippet of source (PAL) that would call the SCGetSimpleAddr with a re-link.

lis r3, 0x8042 #Set only argument for SCGetSimpleAddr
ori r3, r3, 0x1000
lis r12, 0x801B #Set Function Address for SCGetSimpleAddr
ori r12, r12, 0x2424
mtlr r12
blrl #Call the function and re-link

Once the function has been completed, address 0x80421000 will hold your country code value. 0x80421001 will hold the region residence value. 0x80422004 will hold the globe location.

Chapter 5: Return Values

Some functions will return values after being called (with a re-link ofc). It can be important to know these return values, as they usually indicate whether or not an error has occurred.

For example, let's take a look at the function ISFS_CreateFile. This function creates a new file in your NAND. First let's go over its arguments.

r3 = Address that points to File Path w/File that will be created
r4 = u8 attributes
r5 = Owner permissions
r6 = Group permissions
r7 = Other permissions

Side Note: For generic use of ISFS_CreateFile r4 is always 0x0, and r5 thru r7 is always 0x3.

Regarding r3: This is a memory address that will point to the beginning of the ASCII string that represents the file path and name. For example let's say our file path for the new file is /shared2/newfile.txt. The string of Hex byte code representing that ASCII data will start at memory address 0x80001600.

So the argument for r3 would be 0x80001600. At this point, you would call the function utilizing a re-link. Once the function is over and you are returned where you started from, r3 will contain a return value. For most functions there is usually just one return value (in r3). However some functions will contain multiple return values utilizing r4 then r5, etc etc up til r10.

For ISFS_CreateFile, a return value of 0x00000000 means the function call was a success and the file was created. A value of 0xFFFFFF97 (-105) indicates the file already exists so it was not created. A return value of 0xFFFFFF9B (-101) indicates an invalid register argument.

When you get a return value indicating a failure occurred, you need to re-evaluate your values for the arguments. Sometimes the arguments themselves follow special rules. For example the function ISFS_Write (make edits to a file in NAND) has the argument r4 for the memory address that points to the string of data that will be used for the writes. r4 must be 32-[byte] aligned no matter what. So even if r4 is pointing to the data to write that you want, and the other arguments are correct, the function will fail if r4's value is not 32 byte aligned.

What is 32 byte aligned? 
This means whatever value in the register must be divisible by 0x20 (or 32 in decimal).

So keep that in mind when setting up the arguments.

Chapter 6: Backing up Registers; Register Safety

Due to r3 thru r10 being used for arguments, it's pretty important that you will need to backup these registers somehow beforehand. Especially r3, r4, and r5. A good way to do this is called pushing/popping the stack. Normally, coders push the stack for the use of the Global Variable registers (r14 thru r31), but with function calls, this method of pushing/popping the stack will be slightly different. For pushing/popping the stack using the follow snippet of code...

##Default instruction could reside here##

stwu r1,-0x80(r1) #Push stack, make space for 29 registers
stmw r3,8(r1)

##Contents of your ASM code will reside here##

lmw r3,8(r1)
addi r1,r1,0x80 #Pop stack

##Default instruction could also reside here##

Now depending on your ASM code, you may also need to backup the following...
Link Register
Count Register

Which ones do I backup?
Once again, this all depends on your code. If you code address is coming up to a BL instruction, you obviously don't need to backup the Link Register as its value is about to replaced anyway. If r0 is getting a value loaded into it before your code address and there is some sort of compare instruction using r0 after your code address, you obviously need to backup r0. Just set a breakpoint on your code address, and take a good look at the instructions before and after your code address. The Count Register rarely needs to be backed up.

Here's a snippet of code to put before pushing the stack...

mr r11, r0 #Copy r0's value to r11
mftr r12 #Copy LR's value to r12

And a snippet of code to put after popping the stack...

mtlr r12 #Restore LR's value
mr r0, r11 #Restore r0's value

You should already be familiar with the fact that r11 and r12 is safe to use 99% of the time without backing up their values. For a refresher on generic register safety view this thread -

Chapter 7: Non-Volatile Registers

When calling functions with a re-link, you don't have to store values to some place in memory such as the Exception Vector Area for later use for another function. The values in r14 thru r31 are for global variables, thus their values are saved (returned) after the re-link is done on a function call. Say we call the function ISFS_Open with a re-link. Once the function is completely over and we are back to next instruction after the blrl, the values in r14 thru r31 are the same as what they were before the call. Knowing this can help chop down on a codes length. Need to store some values throughout a function call? Throw them in r31 then going downward as necessary.

Chapter 8: Conclusion

As you can see, this sort of use of ASM is not that complicated. As long as you have the arguments correct, you shouldn't have much trouble.

Forum Jump:

Users browsing this thread: 1 Guest(s)