C0 Codetypes
C0 Codetypes

Thank you to Star for teaching me some details on the C0 Codes.

Fyi: This information is for the original release of the Gecko OS code handler which is used in many applications (such as USB Loader GX). Other code handlers (like the one for Dolphin) are different so the information here may not be accurate for those other code handlers.

Chapter 1. What Are C0 Codes?

C0 Codes are called Execute ASM Codes. They are a bit different than 'C2/Insert ASM' Codes. C0 Codes source-written assembly codes that are executed every time the Gecko Code Handler gets executed. The Gecko Code Handler gets executed whenever it's designated hooktype (that you chose in your Cheat/Ocarina settings of your Disc/ISO HBC loader app) gets executed. Every hooktype executes every frame (60 times per second). So in conclusion, your C0 Code executes every frame of the game.

For example, if your Hooktype for your Cheat/Ocarina settings is OSSleepThread, every time the OSSleepThread function is executed, the C0 Code will be executed as well. To understand more about Hooktypes, read this thread HERE.

For each C0 code you have in your GCT/Cheat-Manager, the Gecko Code Handler will create a subroutine for it, every C0 code you have gets executed via the following instructions of the Code Handler

mtlr r15 #Place address of current C0 code into r15
blrl #Execute C0 code and return back to Code Handler

Register safety differs in C0 Codes compared to how it is for C2 Codes, which will be covered in Chapter 3. C0 Codes have no base address, and can be region-free or even wii-game-universal (this all depends on the exact code itself ofc).

Chapter 2: How to Compile C0 Codes

Every C0 code must end in a blr instruction. This is needed so your can have code return back to the Code Handler once it has finished. Some compilers have a C0 option while others do not.

Compiling with PyiiASMH:
There is NO NEED to include the final blr instruction to your source. Just select the 'C0' option before compiling. That's it.

Compiling with WiiRDGUI:
You MUST include the final blr instruction in your source. Use an address of '80000000' when compiling. Once your code has been compiled change the 'C2' byte of the code (the very 1st byte that represents an Insert ASM code), and change it to 'C0'. if WiiRDGUI had to add a nop to your code (60000000), then remove the final compiled line of your code (60000000 00000000), and subtract one from the value of the line-amount-designator byte (this is the upper most far right byte of the compiled code that tells the Code Handler how many lines of compiled code is underneath).

Compiling with CodeWrite:
You MUST include the final blr instruction in your source. Leave the Insertion Address blank! Follow the same procedure in adjusting the compiled code that was explained for the WiiRDGUI compiler if a nop was auto added to your code.

Chapter 3: Register Safety

Certain registers and condition fields are already taken up by the Code Handler so we want to make sure we don't edit those specific values

r0 = Safe
r1 = Not Safe (Can push/pop stack like any other ASM code if needed)
r2 = Not Safe ofc...
r3 = Safe
r4 = Not Safe
r5 = Safe
r6 thru r8 = Not Safe
r9 thru r12 = Safe
r13 thru r31 = Not Safe
All Condition Fields = Safe (CR backed up by code handler, CR's get set with new values with new instructions after C0 subroutine)
CTR = Safe (CTR backed up by code handler, CTR gets set with new value with new instructions after C0 subroutine)
LR = Not Safe ofc...
f0 & f1 = Follow Standard Safety Protocol
f2 & f3 = Safe (backed up by code handler)
f4 thru f31 = Follow Standard Safety Protocol

As noted above, you can push/pop the stack for more registers if needed. As usual, try using the least amount of stack space possible. Only allocate what you need.

Chapter 4: Make C0 Code Only Execute Once

If desired, here is a list of instructions to add to your Source to enable a C0 Code to only execute once. Make sure these instructions come before all other instructions of your Source.

lis rX, 0x4E80
ori rX, rX, 0x0020
stw rX, 0 (r15)

rX = Any of the safe registers.

Since r15 points to the first instruction of your C0 code, we simply overwrite that instruction with a 'blr', thus causing that particular C0 code to not ever execute again.

Chapter 5: Button Activation/De-Activation

You cannot use gecko codetype 16-bit if statements (i.e. 2834/2833XXXX YYYYZZZZ) to make your C0 code(s) be activated and/or deactivated via a button value(s). You must write all of the button activation/deactivation in your source.

Here is an example template you can use for assistance, this isn't a 'plug and play' for use with every C0 code, you will need to make the proper adjustments for this to work in your specific C0 code ~~

lis rX, 0x803x #8033 for NTSC-K MKWii, 0x8034 for all other MKW regions, adjust this accordingly if used for a different Wii game. This instruction is setting the upper 16 bits of the memory address where your button values reside in memory.
lhz rX, 0xXXXX (rX) #XXXX is the lower 16 bits of the memory address (the XXXX in 2833/2834XXXX for MKW). If 0xZZZZ exceeds 0x7FFF, use the sign-extension trick (increase the lis instruction value by 0x0001, then pre-pend the 0xZZZZ value with 0xFFFF (0xFFFFZZZZ).
andi. rX, rX, 0xZZZZ #ZZZZ is the button activator(s) you want to check for
bne+ do_not_execute

#instructions here for when code gets executed (button(s) is pressed


IMPORTANT NOTE if writing this for GCN: Since standard wired GCNs have this stupid 0x0080 value that's always present, you will need to use the ZZZZ values that are for the Wavebird (wireless GCN). You could get around this by writing extra source, but I simply wanted to provide you the template that contains the least amount of instructions.

Chapter 6: Warning about Function Calls

Certain function calls will not work on C0 codes when ran on the Dolphin Emulator. This is not a fault of the code or C0 codetype, but it's the Dolphin Emulator forcing the Gecko Code Handler to execute in the middle of your C0 code. It appears that if a function call takes too long (whether it's amount of frames, instructions, or a combination thereof, I don't know), Dolphin will do its own interrupt (no, you cannot override this interrupt even with editing the Machine State Register) and force the execution of the emulated CPU to go to start of the Code Handler.

In fact, it's not the function call itself that causes Dolphin to do this, but if the C0 code simply takes too long to execute and/or executes too many instructions.

If you have function calls in a C0 code, be sure to test them many times on Dolphin. Game-specific function calls seem to work fine but larger Wii-based function calls (like file editing) will not work.

A solution is to drop the C0 codetype and rewrite your code using standard C2 with an address that executes every frame.

Yet another note: If a function call is botching itself on the Wii console (so you know it's not Dolphin itself using it's odd/mysterious interrupt), try disabling external interrupts in the Machine State Register. Sometimes IOS (Starlet) will interrupt the PPC, meaning your C0 code will stop prematurely and will eventually resume back, but this can cause your function calls to be called with invalid/junk values or your function calls will partially execute.

Here's a snippet of source disabling interrupts of the Machine State Register, place this at the start of your code~
mfmsr rX #rX must be a register that its value will be preserved throughout your code (until we need to restore interrupts)!!!; Copy value of MSR to rX
rlwinm rY, rX, 0, 17, 15 #Flip 'Allow Interrrupts' bit Low
mtmsr rY #rY's value can now be scrapped AFTER this instruction has executed; rY's value is copied to the MSR

And here's a snippet of source to restore interrupts, place this at the end of your code~
andi. rX, rX, 0x8000 #Check if 'Allow Interrupts' bit was already low before it was flipped low from earlier, we no longer need rX's preserved value
mfmsr rX  #We cannot simply reuse our old MSR value btw, your function calls may have modified other bits in the MSR
beq- disable_ints #Check if interrupts were already disabled beforehand. If so, ensure that they stay disabled

#Flip on 'Allow Interrupts' bit
ori rX, rX, 0x8000
b update_msr

rlwinm rX, rX, 0, 17, 15

mtmsr rX

Anybody who created/helped-on the Gecko Code Handler
dcx2 (for his comments about this topic on the old WiiRD forums)
the trick in chapter 5 does not update the code cache (this is not good)
should probably be
/* overwrite first instruction with blr */
lis rX, 0x4E80
ori rX, rX, 0x0020
stw rX, 0x0(r15)
/* update cache */
dcbf 0, r15
icbi 0, r15
In my experience I never needed to instantly update the cache. Though it wouldn't hurt.

According to the Broadway manual, the cache needs to be updated like this if you are modifying executable instructions

dcbst 0, rX #Update the block of data cache with the new contents without doing any checks on the old data (like flush does)
sync #Wait for all instructions to complete before continuing
icbi 0, rX #Invalidate the block of instruction cache
isync #Wait for the icbi instruction to take effect before continuing
I want to learn about Hooking.
There isn't much to learn.

I went ahead and updated the thread that explains Hooktypes if that helps.
(03-14-2021, 04:57 PM)Vega Wrote: There isn't much to learn.

I went ahead and updated the thread that explains Hooktypes if that helps.

Where can I find a thread that mentions hoking?
A link is provided in Chapter 1.

Forum Jump:

Users browsing this thread: 1 Guest(s)