Guide For Calling Functions in ASM Codes
#1
Guide For Calling Functions in ASM Codes



Chapter 1: Intro / What is a Function?

This guide will teach your how to call (execute) functions for use in Gecko ASM Cheat Codes. This is a hefty tutorial, so please take your time and read thoroughly.

What is a function? A function is a subroutine of code that accepts input(s), processes the inputs to preform a task(s), and returns back with output(s) depending on how the task(s) went. Of course, this is very general. Some functions don't take an input (task preformed is always the same), some functions don't give out an output(s), and some functions don't even return back at all.

To execute a function, you "call" it. This means you have to setup the proper inputs, if necessary, beforehand. Once the inputs have been set, you can call the function. In general, functions are very large and contain complex tasks that should not be "handwritten". The C/C++ library has many built-in functions such as printf, memcpy, memmove, etc. Since every Wii game is mostly coded in C++, these functions will be present in memory.

You can of course write your own functions for custom tasks, but when I speak of functions, it's for the very complex ones that should *not* be handwritten, and are already present in the Wii Game. Regarding Wii Games, they contain 3 different category of functions.
  • C/C++ Library (printf, scanf, memcpy, etc etc)
  • Wii Library (functions that are specific to the Wii that are found in most games, i.e. ISFS functions for file handling)
  • Game Specific Functions (this would be something like an Item Box related function in MKWii)

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



Chapter 2: Prologue, Epilogue, and understanding the Linking Mechanism

All functions reside in static memory (mem80 for MKWii). The majority of functions will start with a Prologue. A Prologue is simply a list of instructions that will create some stack space and backup some key registers onto that space.

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

Most prologues appear in this fashion. There are exceptions of course and some prologues may not look exactly like this. Also, some functions may not start with a prologue due to said functions being simple enough to not require backing up registers. Functions that do contain a prologue will always end in an Epilogue.

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

Of course there are exceptions, not all epilogues look like this, but most do. Take note of the 'blr' instruction at the very end. All functions end with a 'blr' instruction.

We won't go over all the instructions within the prologue and epilogue (some you should know already ofc) because calling/using functions within an ASM code is different than what you would find in a conventional PPC program/application. We do need to discuss the 'bl' and 'blr' instructions.

Branch & Link
bl label

This is just like a normal branch instruction, except the address of the instruction that's directly below the branch-link instruction is placed in the Link Register.

Example:
Code:
bl somewhere
add r3, r4, r5

In the above example, once the bl instruction executes, the CPU will jump to the location of "somewhere", and then the Link Register will be written with the address of the location of the add instruction. Let's say the "bl somewhere" instruction resides in memory at address 0x804200F0. When it gets executed, the address value of 0x804200F4 (where the add instruction resides at) will be placed into the Link Register.

This "linking mechanism" is important to understand because this is how functions can return back from where ever they were called from.  As you saw earlier, the Epilogue always ends in the blr instruction

Branch to Link Register:
blr

The blr instruction is just an unconditional branch, but it branches to the Memory Address in the Link Register. It's that simple.



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

Calling functions for use in ASM Codes differs a little bit than how a Wii game or a conventional PPC program does it. First, to call a function, you need to know its memory address (where does the function reside at in Memory). Once you have that address, you need a way to 'jump' to it. Let's go over a basic function that requires zero inputs and does **not** return back to us. This function is __OSShutdownToSBY. It does one job and that is to shut down the Wii. Now to call functions in your ASM Codes, there are two different methods.

1. Use the Link Register (LR) or..
2. Use the Count Register (CTR)

Calling a function via the LR:
Let's look at a snippet of code that will call the __OSShutdownToSBY function. It's important to note that the location of a function within a Wii Game will differ per region (most of the time).  The address of __OSShutdownToSBY for PAL MKWIi is 0x801AB960. By the way, this function is a Wii Library function and is present in all Wii Games, not just MKWii.

Code:
lis r12, 0x801A #Place address of _OSShutdownToSBY in r12
ori r12, r12, 0xB960
mtlr r12 #Copy address to Link Register
blr #Call it

You should be aware of how the blr instruction operates from reading the previous chapter. Once the blr instruction has executed, you will be sent to address 0x801AB960 and start executing the following ASM instructions that are located there. The instructions within this function will perform all the necessary tasks that is required to force the Wii to shutdown. In real time, the shutdown is instant.

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

The bctr instruction simply causes the execution of the CPU to jump to the address that's in the Count Register.

Unlike __OSShutdownToSBY, most functions will return back to where they were called from. Therefore, when we call these types of functions, we need to supply the function the proper address to return back to. If not, your code will cause a crash or undefined behavior in the Game will occur.

There are two instructions that you can choose from to make a function return back.,,
  • blrl #Branch to Link Register and Link
  • bctrl #Branch to Count Register and Link

You can think of blrl as the combination of bl + blr. The blrl instruction will jump to whatever address is currently in the Link Register. Afterwards (this is IMPORTANT), the address of the instruction directly below blrl is placed into the Link Register.

Example:
Code:
blrl
add r4, r4, r3


Pretend that the address's of the blrl and add instructions is 0x80501CC0 and 0x80501CC4 respectively. When the blrl gets executed, the CPU will jump to the address that's currently in the LR. Afterwards, the CPU is executing some function, and the address (value) in the Link Register gets replaced with 0x80501CC4. That way when the function needs to return back, it knows how to come back to us.

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

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

Example snippet of source (unfilled function address) using the CTR and r12

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



Chapter 4: Arguments

You may be wondering at this point, why r12 is shown in the above snippet of sources in Chapter 3. 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

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

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 integer (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 5: Return Values

Referencing the above snippet of source from Chapter 4, 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 4, but it now has new instructions to verify 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) for errors.



Chapter 6: Terminology, Caller vs Callee

Now would be a good time to go over some of the nicknames for various registers. You may have come across these names in some of my other tutorials before.
  • r0, r3 thru r12, and f0 thru f13 are part of the group that is known as the Volatile Registers.
  • r14 thru r31, and f14 thru f31 are part of the group that is known as the Non-Volatile Registers or also known as the Global Variable Registers.

The Volatile Registers are the Caller-Saved Registers. This means that if there is data in them that needs to be preserved/saved, it must be done *before* you call a function.

The Non-Volatile Registers are the Callee-Saved Registers. This means the Callee saves the data of these registers (not all, just the registers that will be utilized) during the Prologue of a function. The Caller can place data in these registers and know the data will be safe throughout a function call. Therefore, once the function call completes and return back, all the data is intact. THIS IS IMPORTANT. More on this in Chapter 8.

Caller? Callee? What the heck does the even mean?

Let's say you have a code and it calls a function. The portion of the code which sets the inputs, calls the function, and process the outputs, is known as the CALLER.

The CALLEE is the instructions within the function itself, which ofc includes the prologue and epilogue.

Keep in mind the Callee can become a new Caller. If a function contains a function within itself, this becomes the case. The function that calls the new function becomes the new Caller. The new function itself is the new Callee.

The Caller is responsible for...
  • Setting up any Input Values
  • Backing up data in the volatile registers before calling the function
  • Calling the function (i.e. blrl/bctrl)
  • Processing/Verifying the return values given back from the function

The Callee is responsible for...
  • Prologue
  • Save the inputs in the non-volatile registers; thus the older values in these registers must be saved before being used by the Callee, this occurs in the prologue
  • Epilogue
  • Return back to caller (blr; part of the Epilogue)

An alternative name for Caller is Parent. And for Callee is Child.




Chapter 7: Register Safety

Since we are calling functions within an ASM code, and not from a conventional program/application, we can use a modified version of the "Push/Pop the Stack" Method that will give us plenty of registers to freely use without over-complicating things. Here is the modified version~

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

As you can see, this will backup registers r3 thru r31 for us, and then retrieve them once our code and function call(s) are complete. Therefore, you are 100% free to use these registers. Now depending on your ASM code, you may need to backup the following...
  • r0
  • Link Register
  • Count Register


Which ones do I backup?
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 8: Non-Volatile Registers

Back in Chapter 6, I've mention that the Non-Volatile registers are preserved throughout function calls. Trying to save data to a place like the Exception Vector Area before a function call, and then loading the data back from said Area after a function call may not be necessary. You can throw the data into the non-volatile registers instead.

A good example use for the Non-Volatile Registers 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 arguments 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 Non-Volatile Register

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

Remember that since you are using the modified "Push/Pop the Stack" Method, you have r14 thru r31 at your disposal to backup data in. Even though the Push/Pop Stack Method backups r3 thru r13 as well, you *CANNOT* use these specific registers as a way to preserve data throughout a function call(s).



Chapter 9: 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!
Reply
#2
Thanks
Reply
#3
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.
Reply
#4
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.
Reply
#5
http://wiki.tockdom.com/wiki/Compiler has good info for this kind of thing
Reply
#6
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)
Reply
#7
Not sure where to move your post to. But since it deals with function calls, I placed it here.
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)