C0 Codetypes
#1
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 1.9.3.1 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. The instructions of a C0 code 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

Code:
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 Assemble 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 Gecko Code Assemblers have a C0 option while others do not.

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

Assembling with WiiRDGUI:
You MUST include the final blr instruction in your source. Use an address of '80000000' when assembling. Once your code has been assembled, change the 'C2' byte of the code (the very 1st byte that represents an Insert ASM code) it to 'C0'. If WiiRDGUI had to add a nop to your code (60000000), then remove the final assembled 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 assembled code that tells the Code Handler how many lines of assembled code is underneath).

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

Assembling with Waltress:
There is NO NEED to include the final blr instruction in your source. Just be sure the !C0 header is at the top of your source file.



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.

Code:
lis rX, 0x4E80
ori rX, rX, 0x0020
stw rX, 0 (r15)
dcbf 0, r15
icbi 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. The two instructions after the stw are for Cache purposes. It's to ensure the newly written executable instructions will actually take effect by the time the C0 code gets executed for its 2nd time. To learn more about Cache, here is a link - https://mariokartwii.com/showthread.php?tid=1958



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. There are 3 different methods for button activation/deactivation.

Method #1: When pressing activator/de-activator, no other buttons are allowed to be pressed. Button combos are allowed.

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).
cmplwi rX, 0xZZZZ  #ZZZZ is the button activator(s) you want to check for, logical comparison used for the rare case button activator/de-activator value is >= to 0x8000.
bne+ do_not_execute

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

do_not_execute:

Method #2: Allow other buttons being pressed when button activator/de-activator is pressed. However button combos (i.e. A + Z buttons) cannot be used.

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/de-activator you want to check for
beq+ do_not_execute

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

do_not_execute:

Method #3. It's Method #2 but button combos are now allowed to be used. Please be aware that Method #3's source can be shortened but then the end-User would be required to fill in the same button twice for the compiled version of your C0 code.

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).
li rY, 0xZZZZ #ZZZZ is the button activator/de-activator you want to check for
and rZ, rY, rX #Logically AND ZZZZ with loaded button value
cmpw rZ, rY #If button non-combo/combo was at least pressed, then rZ will equal rY
bne+ do_not_execute

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

do_not_execute:


IMPORTANT NOTE if writing this for GCN for Methods #2: Due to the way the source is written (to work on all controllers), you will have to use the Wavebird ZZZZ values!



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 C0 code takes too long to execute, regardless of whether or not it contains function calls, Dolphin will invoke its own 'master interrupt'. This 'master interrupt' will cause your C0 code to quit executing and force the emulated CPU to jump to the start of the Gecko Code Handler.

Nothing can disable this 'Master Interrupt'. Not even disabling interrupts on the Machine State Register.

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 that take much longer to execute (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.

You may discover that C0 codes with large function call(s) will botch your game on real Wii hardware. This is because of normal interrupts by Starlet or some other piece of hardware. You need to disable (mask) interrupts in the Machine State Register before your function call(s), then restore interrupts afterwards.

To mask interrupts, you must call the OSDisableInterrupts function (no args are required).

NTSC-U = 0x801A650C
PAL = 0x801A65AC
NTSC-J = 0x801A64CC
NTSC-K = 0x801A6908

r3 will return the arg that you need to call the next function with when you no longer need interrupts masked. Back it up somewhere safe.

When ready to unmask interrupts, call OSRestoreInterrupts~

NTSC-U = 0x801A6534
PAL =  0x801A65D4
NTSC-J =  0x801A64F4
NTSC-K = 0x801A6930

Be sure to supply the r3 arg that was the r3 return value from OSDisableInterrupts.

IMPORTANT NOTE (thank you Seeky!): Do *NOT* write out the Interrupt functions manually within your source even though they are tiny and have no stack push/pops. It's very rare, but a bug can occur if you do this.

Quote from Seeky-

(11-20-2022, 01:07 PM)Seeky Wrote: You can get weird bugs by manually disabling interrupts if a context switch happens after your mfmsr and before your mtmsr, and the msr is changed on another thread. OSLoadContext has special code to prevent this issue in OSDisableInterrupts (moves pc back to the start of the function if it was interrupted before the mtmsr), so really you should always call the actual function for this



Chapter 7: Stepping Thru C0 Codes (Debugging)

Stepping thru C0 Codes may require a bit more work compared to C2 Codes. Especially if you have multiple C0 codes. You will need to decide if you are OK with allowing the emulation to run a bit, or if you want to absolutely make sure you have all your debugging tools + Breakpoints set before the C0 code gets executed by the first initial execution of the Code Handler. This all depends on your C0 code you are debugging and what instructions are in the code. There's no way I can know personally which method applies to you and your code.

If you have a C0 code that's intended to execute all its instructions on its first initial execution, you may want to have everything prepared before the Code handler gets its first time execution. If you have a C0 Code that only executes on something like a button activator, then it would be okay to allow the game to run while setting up everything as long as said button activator isn't pressed.

How to debug a C0 code:
1. If you have multiple C0 codes on your Cheat-Manager, be sure the specific one you want to debug is the FIRST/TOP one.
2. At this point you need to decide if you are fine with allowing the emulation run a bit or you need everything setup immediately. If you are fine with the emulation running, skip ahead to Step 5
3. Before you boot the game, at the top of your Dolphin, click on Options.
4. A drop down menu should appear. Click on the box next to "Boot Game to Pause". Be sure the box is checkmarked/filled.
5. Boot your game! If you chose to allow the emulation to run, you can now Pause it.
6. In Code view go to 0x80001F9C. You should see a 'blrl' instruction. It may not be at that exact address, but still should be nearby. See picture below. If you did the boot-to-pause option, you will not see any instructions as the Code Handler hasn't been injected yet, proceed to Step 7.

[Image: C001.png]

7. Set an Instruction BP on that address that contains the blrl instruction. Like in this picture below (notice the Grey Dot next to the address indicating a BP has been activated). However if you did the boot-to-pause option, set an Instruction BP on 0x800018A8 instead, run the emulation, the BP will hit. Now you can scroll down to 0x80001F9C and set an Instruction BP there.

[Image: C002.png]

8. Resume emulation, the emulation will pause once your BP has been hit. At this point the blrl instruction in Code View should be highlighted in green, like the picture below...

[Image: C003.png]

9. Now Step just ONE time. You are now at the first instruction of your C0 code. Smile Now you can start debugging the C0 code, congratz.



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

I went ahead and updated the thread that explains Hooktypes if that helps.
Reply
#6
(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?
Reply
#7
A link is provided in Chapter 1.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)