Mario Kart Wii Gecko Codes, Cheats, & Hacks

Full Version: Guide For Calling Functions in ASM Codes
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Guide For Calling Functions in ASM Codes



Chapter 1: Intro / Fundamentals

This guide will teach your how to call (execute) functions for use in Gecko ASM Cheat Codes. In non-coding terms, a function is simply a subroutine of code that takes in input(s), processes them to preform a specific task(s), and spits out output(s) depending on what occurred during those task(s). Like almost all other video games, Wii games are written in a high-level computer programming language (mostly C++) instead of Assembly. Thus, Wii games contain many generic C/C++ type functions (such as memcpy, memset, printf, sqrt, etc). Obviously, Wii games have their own unique Wii specific functions such as DWC_Login, ISFS_Open, GXDraw, etc.

The purpose of calling functions in your codes is for 2 main reasons:
  • The function(s) you want to use are very complex and writing a similar function from scratch in your source is essentially impossible
  • Writing similar function(s) from scratch in your source would result in your code being way too massive

All functions reside in static memory (mem80). The majority of functions will have a Prologue. A Prologue is simply a list of instructions that will make some stack space and backup some key registers.

Template of a Prologue
Code:
stwu sp, -0xZZZZ (sp)
mflr r0
stw r0, 0xZZZZ (sp)
stmw/stw rX, 0xZZZZ (sp)

As mentioned earlier, some functions may not have a prologue due to said functions being simple enough to not require backing up registers. Video Game Developers use what is called a symbol map as a reference of where all the functions are located in memory. Most Wii games will have their symbol maps scrubbed before being released, meaning there is no file/information in the game that will tell us the names of the functions and where they are at in memory. Luckily various devs/coders in the MKWii community have worked on a custom symbol map which you can find HERE

Another symbol map that has is very detailed (but only has a small list of commonly used functions), can also be found HERE

Most functions are called via a bl (branch-then-link) instruction. Let's say you are viewing a random spot in static memory, you see some instructions that lead up to a bl instruction. Chances are that bl is going to jump to the start of some function. Execution of the CPU is jumped to the prologue and then the function executes. Most functions end via what's known as an Epilogue.

Template of an Epilogue
Code:
lmw/lwz rX, 0xZZZZ (sp)
lwz r0, 0xZZZZ (sp)
mtlr r0
addi sp, sp, 0xZZZZ
blr

Once the blr instruction (branch to link register) has been executed, the function has fully completed, and the CPU returns to its original routine of instructions that it was executing before the function was called upon.

Blr instructions are very simple to understand. All a blr instruction does is jump to the address that's in the Link Register. There is also the bctr instruction, it simply jumps to the address that's in the CTR (Count Register).

While most functions will return back from where they were executed from, some functions are meant not to return back. Thus they are called with a non-linking branch instruction (i.e. blr/bctr). Functions such as __OSShutdownToSBY (PAL address 0x801AB960) are not meant to return back. _OSShutdownToSBY is a function call that shuts down the Wii.




Chapter 2: How to Call a Function in your ASM Codes

Calling functions for use in Gecko ASM Cheat Codes differs a little bit than how a Wii game does it. First, to call a function, you need to know its address. Once you have that address, you need a way to 'jump' to it. There are two different registers that you can choose from to accomplish this.

Link Register (LR) and Count Register (CTR)

Calling a function via the LR:
Let's say you are using the PAL game and you want to call the function __OSShutdownToSBY (PAL address 0x801AB960), here's how you can call the function.

Code:
lis r12, 0x801A #Place address of _OSShutdownToSBY in r12
ori r12, r12, 0xB960
mtlr r12 #Place address in Link Register
blr #Call it, __OSShutdownToSBY is a non-returning function, simply branch without re-linking

Once the blr instruction has executed, you will be sent to address 0x801AB960 and start executing the following ASM instructions that are there in memory. Shortly after this has happened, the Wii will shutdown.

Calling a function via the CTR:
As mentioned earlier, you also 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.

Code:
lis r12, 0x801A #Place address of _OSShutdownToSBY in r12
ori r12, r12, 0xB960
mtctr r12 #Copy address to Count Register
bctr #Call it, __OSShutdownToSBY is a non-returning function, simply branch without re-linking

If you wanted to call a different function that you needed to return back from, you will need a linking branch instruction.
You can use blrl (Branch to Link Register and Link) or bctrl (Branch to Count Register and Link).

Example snippet of source (unfilled function address) using the LR

Code:
lis r12, 0xXXXX
ori r12, r12, 0xXXXX
mtlr r12
blrl #Call the function and have it return back once it's done



Chapter 3: Arguments

You are probably wondering at this point, why r12 is shown in the above snippet of sources in Chapter 2. This is because certain functions need 'arguments' (aka inputs) established before being called and certain specific registers must be used for these 'arguments'. If these arguments are not set in the correct registers and/or the registers are filled with invalid values, then the function will fail or cause your game to halt.

Here's the structure of how arguments are set in the registers

Code:
r3 = 1st arg
r4 = 2nd arg
r5 = 3rd arg
...
r10 = 8th arg

f1 = 1st arg that's a float value
f2 = 2nd arg
...
f8 = 8th arg

GPR's r0, sp (r1), rtoc (r2), and r11 thru r31 are never used for args. FPR's f0, and f9 thru f31 are also never used for float-value args. Most coders prefer to use r12 as the register to place the function's address in the LR/CTR before calling it. That is why r12 was used in the sources shown in Chapter 2.

Let's take a look at the function NETCalcCRC (PAL address 0x801D1CA0). It's a function that is used to generate a 32-bit checksum on a desired block of data. NETCalcCRC requires two non-float args...
  • r3 (1st arg) - Memory Address that points to the start of Data that will be checksummed
  • r4 (2nd arg) - Size of the Data (in bytes)

Here's an example of a snippet of source setting up the two args and then calling NETCalcCRC

Code:
lis r3, 0x8056
ori r3, r3, 0xEE00 #Let's pretend this is where our data starts at
li r4, 100 #Let's pretend that data is 100 bytes in size
lis r12, 0x801D
ori r12, r12, 0x1CA0 #Place NETCalcCRC (PAL) address in r12
mtlr r12 #Copy r12 to the Link Register
blrl #Call the function via the LR and have it return back to us



Chapter 4: Return Values

Referencing the above snippet of source from Chapter 3, NETCalcCRC will return to us with our 32-bit checksum value. Almost all functions (that are designed to be returned back from) will return a value in r3. A few functions will return values in both r3 and r4, and/or return a value in f1.

Once NETCalcCRC has completed, r3 will now contain the 32-bit checksum value. NETCalcCRC returns 0 in r3 if a checksum could not be generated.

Here's our source from Chapter 3, but it now has new instructions to check what's in r3.

Code:
lis r3, 0x8056
ori r3, r3, 0xEE00 #Let's pretend this is where our data starts at
li r4, 100 #Let's pretend that data is 100 bytes in size
lis r12, 0x801D
ori r12, r12, 0x1CA0 #Place NETCalcCRC (PAL) address in r12
mtlr r12 #Copy r12 to the Link Register
blrl #Call the function via the LR and have it return back to us
cmpwi r3, 0 #Check if r3 is 0
beq- error_occurred #If r0 was zero, the checksum could not be generated, take the branch to a spot in our source that will handle errors

Please note that while NetCalcCRC returns 0 for an error, most functions will return negative values (i.e. 0xFFFFFFFC).



Chapter 5: Register Safety

Calling functions in your ASM code(s) will require to do the push/pop the stack method that you may have used in the past to backup r14 thru r31. However, you will use a modified version to backup more registers.

Push/pop stack method for codes with function call(s)~

Code:
##Default instruction could reside here##

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

##Contents of your ASM code and your function call(s) will reside here##

lmw r3, 0x8 (sp)
addi sp, sp, 0x80 #Pop stack

##Default instruction could reside here##

Now depending on your ASM code's address, you may also need to backup the following...

r0
Link Register
Count Register


Which ones do I backup?
Once again, this all depends on your code. At your Code's Address in Dolphin's Code View, take a look at some instructions that are further ahead, if you see any of the 3 registers being used, you will need to back them up. The Count Register rarely needs to be backed up.

Here's a snippet of code to put before pushing the stack if you need to backup the LR and r0...

Code:
mr r11, r0 #Copy r0's value to r11
mflr r12 #Copy LR's value to r12


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

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



Chapter 6: Non-Volatile Registers

When calling functions that will return back, you don't have to store values to some place in memory such as the Exception Vector Area for later use in your code. The values in r14 thru r31 are for global variables. They are known as the Global Variable Registers (GVR). Their values are preserved through function calls. With 18 total GVR's, you will have plenty of space to backup values.

A good example use for the GVR's is for the ISFS based function calls. The ISFS family of function calls are responsible for reading and/or modifying of files on the Wii's NAND. To open a file you use the ISFS_Open function. It has argumentss like most functions do, but what's important here is that ISFS_Open returns what is known as a File Descriptor (fd) as a byte value in r3. You need this File Descriptor value for all other ISFS based functions. All other ISFS based function calls require this fd as part of their arguments. So it would make sense to do something such as...

Code:
mr r31, r3 #Backup fd to a GVR

At some point after calling ISFS_Open and before calling any other ISFS based function. Since r31 is a GVR, it's value will be safe throughout other function calls.



Chapter 7: Stepping Thru Functions on Dolphin

Most functions simply have too many instructions for you to step thru when debugging. So to get around this problem you can use Instruction Breakpoints. When stepping through your code, right before your function gets called (via execution of blrl/bctrl), set an Instruction Breakpoint on the instruction that's immediately after the blrl/bctrl. You can set Instruction BPs very quickly by just left clicking on the address of the desired instruction in Code View. A grey dot will appear next to the address indicating an Instruction BP has been set. Like so...

[Image: functionBP.png]

Once that Instruction BP has been set, simply press the play button or Dolphin (or use your Hotkey) to let the emulation play normally. After a split moment, you will then be on the instruction immediately after your blrl/bctrl. You can then take a look at r3 to verify your return value if desired. Also remember to check your arguments before the function was called. Happy coding!
Thanks
Since this thread was bumped, I thought I’d share some tips I’ve found useful.

It probably goes without saying, but in my opinion the best hook addresses for function calls are those right after a ‘bl.’ Picking such an address makes things easier because there’s no backing up of registers to worry about (except possibly r3 or f1).

Also, in dealing with functions used by an actual game, most of these will be methods belonging to a particular class. This implies there will be an inherent “this” pointer as the argument in r3, with the actual arguments beginning in r4. Just something I realized which is helpful to know, especially for reverse engineering.
I've known the r3 thing for a while, the idea of using the address right after a bl is something I haven't really thought about. It's a smart idea, although it's good practice that r3 and f1 should always be considered unsafe since they are going to potentially be used as return values of the said function.
http://wiki.tockdom.com/wiki/Compiler has good info for this kind of thing
when i see https://mariokartwii.com/showthread.php?tid=922 and all other code why you can create a game or mod with the cheat code ? (for example your xyz swapper with this you can get all ia pos and make a minigame)
Not sure where to move your post to. But since it deals with function calls, I placed it here.