Guide For Calling Functions in ASM Codes
Guide For Calling Functions in ASM Codes

Chapter 1: Intro / Fundamentals

This guide will teach your how to call functions for use in Gecko ASM Cheat Codes. 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.

You can create a cheat code by utilizing these functions. All functions reside in static memory (mem80). You can spot most functions by looking for its prologue.

Example of a prologue
stwu sp, -0xZZZZ (sp)
mflr r0
stw r0, 0xZZZZ (sp)
stmw/stw rX, 0xZZZZ (sp)

Most Wii games have their symbol maps scrubbed before being released, meaning there is no file 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. Execution of the CPU is jumped to the prologue and then the function executes. Most functions end via an epilogue.

Example of an epilogue
lwm/lwz rX, 0xZZZZ (sp)
lwz r0, 0xZZZZ (sp)
mtlr r0
addi sp, sp, 0xZZZZ

Once the blr insruction (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.

Fyi: All a blr instruction does is jump to the address that's in the Link Register. Also, the bctr instruction is similar but instead you jump to the address that's in the Count Register.

Some functions are meant not to return back. Thus they are called with a non-linking branch instruction (i.e. b, 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 (call 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 have the function __OSShutdownToSBY (PAL address 0x801AB960), here's how you can call the function.

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.

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

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' 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

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

f1 = 1st arg that's a float value
f2 = 2nd arg
f13 = 13th arg

GPR's r0, sp (r1), rtoc (r2), and r11 thru r31 are never used for args. FPR's f0 and f14 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. Thus you would have to use r3 for it's 1st arg, and r4 for it's 2nd arg. The 1st arg for NETCalcCRC is the memory address that points to the start of the block of data that will checksummed. The second arg will be the size of that data (in bytes).

Here's a snippet of source demo-ing setting up and calling NETCalcCRC

lis r3, 0x8056
ori r3, r3, 0xEE00 #Let's pretend this is where our data starts at
li r4, 100 #Amount for our checksum is 100 bytes
lis r12, 0x801D
ori r12, r12, 0x1CA0 #Place NETCalc (PAL) address in r12
mtlr r12 #Place it in the LR
blrl #Call the function and have it return back to us

Chapter 4: Return Values

With the above snippet of source from Chapter 3, NETCalcCRC will return us 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 need to return values in r3 and r4.

Thus, once NETCalcCRC has returned back, r3 will now have the 32-bit checksum value. NETCalcCRC returns 0 in r3 if a checksum could not be generated.

Please note that most functions will return negative values in r3 (i.e. 0xFFFFFFFC) to notify you that an error occur (most likely due to an invalid/missing arg). It's advised to write some sort of source to check for these errors...

Example of checking for negative error values from a function

bctrl #Your function was called and returns back to you
cmpwi r3, 0 #Compare return value to 0
blt- skip_to_end #If less than 0 (negative value), our function failed, skip rest of code

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)~

##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...

Link Register
Count Register

Which ones do I backup?
Once again, this all depends on your code. If your 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. 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 if you need to backup the LR and r0...

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...

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

Chapter 6: 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 in your code. The values in r14 thru r31 are for global variables. They are called the Global Variable Registers (GVR). Their values are preserved through function calls. Need to backup something like r12? Just move it to r31 (i.e. mr r31, r12) before you call your function. Once your function call has ended and you need that preserved value back in r12, just move it back (i.e. r12, r31). With 18 total GVR's, you will have plenty of space to backup values.

Chapter 7: Stepping Thru Functions on Dolphin

Most functions simply have too many instructions for you to step thru when debugging. You will be at your computer for hours, maybe days lol. 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.

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 (usually how long a function takes), 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!
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.
Super Mario Eclipse, what Super Mario Sunshine could've been.
#5 has good info for this kind of thing

Forum Jump:

Users browsing this thread: 1 Guest(s)