12-21-2019, 06:17 PM
How to make Codes Execute on Set Intervals
This has been a requested tutorial for quite some time, so here ya guys go.
Requirements:
Already an intermediate-level (beyond pure beginner) ASM Coder
This thread will teach you how to take a basic code and make the code execute on set timed intervals. This will imitate an automated off-on switch or vice versa.
Method using the Race Timer:
This is by far the easiest method for the beginner ASM Coder. For this method, we will want to use Hamster's Invisible Racing code (NTSC-U) located HERE. We will utilize the seconds value of the Race Timer to determine whether or not the Invisible Racing code will be off or on. We want the code to execute whenever the seconds of the race timer is an odd number.
NTSC-U Invisible Racing
04559228 38000001
We will need to modify Hamster's code into a short ASM write. Two ASM Codes are required. One for the Invisible Racing Code and the other ASM will need to use an address that contains a register which tracks the race timer seconds. We will have the turn off for 1 full second, then turn on for 1 second, and repeat for the entirety of the race.
Bully's Millisecond Modifier code https://mkwii.com/showthread.php?tid=102 is a good code for this. r28 at this address contains the seconds value of the race timer.
Let's first write our ASM for Bully's Millisecond Modifier (NTSC-U). We will simply take r28's value and store it to the Exception Vectors. Before we write any ASM for this, launch Dolphin, set an Instruction BP on the address, and take a look at the code view. We do this just in case you have any weird scenario in regards to Register Safety.
We see in the Code View this is our default instruction...
Code:
lhz r5, 0x0024 (sp)
We can set the default instruction at the end of the source and use r5 safely. Let's write our ASM...
Code:
lis r5, 0x8000 #Setup upper bits of Exception Vector Address
stw r28, 0x1398 (r5) #Store seconds value to 0x80001398
lhz r5, 0x0024 (sp) #Default Instruction
Our first ASM is completed. Now we need to make an ASM out of the Invisible Racing Code that will load this value and execute accordingly.
You can go ahead and also set an Instruction BP on this address to check out the Register Safety. After a quick glance, it appears it will be easier just to use r12 instead of searching for safe registers to use. r0 cannot be used due to being read by the CPU as a pure 0 in certain instructions that we will be using for our ASM write. We will have the code only execute when the seconds value is an even number. Let's write it...
Code:
lbz r0, 0x0020 (r5) #Default Instruction
lis r12, 0x8000 #Set upper bits of Exception Vector Address
lwz r12, 0x1398 (r12) #Load seconds value back into r12 as r12's first value of 0x8000 isn't needed anymore
Okay at this point, we have our seconds value in r12. Now we need a proper efficient way to know if the current seconds value is odd or even when being read. To do this, we need to go over binary just a quick bit. What is binary? Hex numbers are actually compiled readable form of binary numbers. Binary numbers are 0 or 1. As usual a word is 32 bits in length aka it has 32 binary numbers (0/1's). Thus each Hex number digit has 4 bits.
When a Hex number is even, it's far-right most bit is 0. If it's odd that bit will be 1. So here's an easy instruction that will check if value in a register is even or odd.
Code:
clrlwi rX, rY, 31
The above instruction (Clear Left Word Immediate) will clear out all the bits except the last bit (which can referred to as the least significant bit). What's great about the clrlwi instruction is that you can use the 'record' feature for a free use of cmpwi rX, 0. Let's use the record feature and add in our branch instruction.
Code:
clrlwi. r12, r12, 31
beq- number_was_even #If equal to 0, number was even. Skip execution of code.
Going back to Hamster's code, he was able to make the code by replacing the default instruction with li r0, 1. Thus we will have that Load Immediate instruction be executed if our seconds value is even. Let's write in the rest of the source and view it as completed.
Code:
lbz r0, 0x0020 (r5) #Default Instruction
lis r12, 0x8000 #Set upper bits of Exception Vector Address
lwz r12, 0x1398 (r12) #Load seconds value back into r12 as r12's first value of 0x8000 isn't needed anymore
clrlwi. r12, r12, 31 #Clear all bits of r12 except final bit, then execute a cmpwi r12, 0
beq- number_was_even #If equal to 0, number was even. Skip execution of code.
li r0, 1 #Execute the code, replace r0's value with 1
number_was_even: #li r0, 1 will be skipped if the beq branch was taken thus not executing the code
After compiling both of your own ASM's. Add them to your GCT/Cheat-Manager. If they fail, double check your work for errors and also remember to check Register Safety. Here are the two compiled ASMs of what we did wrote in this tutorial (NTSC-U). The code below has already been tested and works.
C25310A0 00000002
3CA08000 93851398
A0A10024 00000000
C2559228 00000004
88050020 3D808000
818C1398 558C07FF
41820008 38000001
60000000 00000000
NOTE: For codes that you want to modify are 32 bit RAM Writes (that aren't replacing branch routes), you can actually do this with one ASM via just using Bully's Code. However, learning it the way explained in the tutorial is easier for the beginner.
Conclusion:
If the code you are wanting to utilize for set intervals is already an ASM code, then just simply add in the extra instructions for the seconds load-and-check. If you need to code to execute quicker, keep in mind that r5 in Bully's code contains the millisecond value. You can utilize that value to make codes go off/on in less than a second. For codes you need to execute off/on all the time regardless of whether or not you are in a live race, you will need to find an address that keeps track of some sort of timer value that can set an Instruction Breakpoint anywhere in game.