How would I create ASM that only executes if the value of f1 equals a certain value?
Example:
If f1 = 40C00000 then .....
else .....
The float registers are very unhappy to work with. You can't load them like regular registers; you have to load them from memory, or with other float instructions.
I would follow the code back to see where f1 was being loaded from, and then use that pointer and offset to load/compare/branch using regular registers.
So, if I know that, for instance, f1 is loaded by lfs f1,76(r3), would I be able to do an lwz r0,76(r3) and then a check of r0 to see if it equals, for instance, 40C00000? If so, what would the ASM look like to do this?
Yup, that's what you'd do, assuming nothing depends on r0. You also need a second free register because you've gotta compare 32 bits; if it were only 16 bits you could use cmpwi, and if you can't find two, you'll have to push registers onto the stack and pop them off when you're done. I'm going to use r5 as the "second free register"
lwz r0,76(r3)
lis r5,0x40C0
ori r5,r5,0x0000
cmpw r0,r5
bne- NotEqual
# do "stuff", then set the above branch to jump over this "stuff"
Thanks! I almost have my code, but I get the following error in ASM->WiiRD for the bne- 0x80255754 line. Isn't that valid syntax?
Error: operand out of range (0x80255754 is not between 0xffff8000 and 0x00007fff)
There are two lines of code after my bne- that I need to skip if the bne- is true (i.e. r0 doesn't equal r5). Would I use bne- 0x12? If so, why the 0x12? How do you determine the hex value for skipping lines of code?
Instructions are 32-bits, so there's only room for 16-bits of immediate. This is why loading a 32 bit value into a register takes two steps - lis to load the upper 16-bits and ori to load the lower 16-bits.
Since branches can't take a 32-bit operand, they must be relative branches. That is, they will add their operand to the current instruction pointer (SRR0). Normally, for non-branches the instruction pointer changes by +4 (so a b 4 goes to the next instruction, b 8 skips the next instruction, b 12 skips the next two instructions, b -12 goes back three instructions, etc)
note that 0x12 = 18 decimal and that's a bad number to jump by because it will break the word-alignment of the instruction pointer. 0, 4, 8, C, 10, 14, 18, 1C (c-teen), 20, 24, 28, 2C (twenty-C), etc. That's how you count by four in hex.
...and now it seems like you figured out you need relative branches. ^_^ Okay, so...
I think what you're trying to do is skip over a chunk of code...but the C2 code isn't going to work like that. Your C2 assembly routine is actually located very low in memory, way more than 16-bits worth of operand can jump. The code handler has gone through the trouble of replacing the C2-address you specified with a branch pointing to where it wrote your little assembly routine. So a bne- 12 isn't going to do what you think it's going to do...it will probably crash your game.
I think what you really want to do is find the stfs f1,76(r3) that matches your lfs f1,76(r3). Keep in mind it might not be an f1, and there's even a chance it might not be r3, but the point is that I think you want to prevent the stfs from happening. Use a C2 code to replace that stfs. For your C2 code, use this.
lwz r0,76(r3) # load your 32-bit compare into r0
lis r5,0x40C0 # load upper 16 bits of r5
ori r5,r5,0x0000 # load lower 16 bits of r5
cmpw r0,r5 # compare r0 to r5
bne- 8 # if they are not equal, skip over the stfs to the nop
stfs f1,76(r3) # store f1 only if r0 == 40C00000
nop # don't actually need this, but I left it in for clarity regarding the bne-
Would you mind copy/pasting a few of the lines before the assembly you're trying to hook with C2? I want to see what you're trying to do.
My code is as follows. It basically is supposed to wait until f0=6.0 (40C00000) and, if so, multiply 6x6x6x6 and put it back into f0 which gets pushed to the stack at 80255754. May seem strange, but that's what it needs to do. Looks like the bne- is going to the right address (i.e. 800028EC) which is a branch to the next instruction in the assembly after my hook. However, the following code causes the game to freeze when I perform the action that triggers it. Maybe r2 and r5 are in use? I set a breakpoint and stepped through the program only to see that it goes to an unexpected address (i.e. 802C9DC0). Any ideas?
80255750: 4BDAD178 b 0x800028c8 -> branch to my new assembly routine (see below)
80255754: D0010124 stfs f0,292(r1) -> next instruction that will push my f0 to the stack. This is the original code.
800028C8: C004004C lfs f0,76(r4)
800028CC: 8064004C lwz r2,76(r4)
800028D0: 3CA040C0 lis r5,16576
800028D4: 60A50000 ori r5,r5,0
800028D8: 7C032800 cmpw r2,r5
800028DC: 40820010 bne- 0x800028ec
800028E0: EC000032 fmuls f0,f0,f0
800028E4: EC000032 fmuls f0,f0,f0 -> Program goes to 802C9DC0 after this instruction. It should go to 80255754, right?
800028E8: 60000000 nop
800028EC: 48252E68 b 0x80255754
:o OMFG R2 IS NEVER SAFE
Sorry, I probably should have said that earlier. r1 and r2 are never, ever safe, not even temporarily.
I don't know why your instruction pointer jumped, though. After it jumped to the unexpected address, did you try to hit Step again? If the instruction pointer doesn't change when you're hitting Step, it's because you're doing something illegal and the PowerPC is refusing to execute.
Other than that, your ASM code looks good. It's raising f0 to the fourth power if your value is not equal to 40C00000, yes?
By the way, you could also replace the stfs f0,292(r1) with an stw r5,292(r1), and load r5 with your arbitrary float in hex. And you should always assume that if you don't see a register used as a destination, that it is not safe either (i.e. I know you never saw anything changing r2, so you should never have assumed it was safe because it was never a destination register)
I thought that my code would be raising f0 to the fourth power if f0 DOES equal 40C00000. I could not continue with Step after hitting the unexpected address.
So, I could replace the stfs f0,292(r1) code with the following, right?
stfs f0,292(r1) -> original code
lwz r3,76(r4)
lis r5,16576
ori r5,r5,0
cmpw r3,r5
bne +0x12 -> this should skip the next three lines of code below if r3<>r5, right?
lis r5,0xHHHH -> the four high bytes of my desired float (e.g. 40C00000)
ori r5,r5,0xLLLL -> the four low bytes of my desired float (e.g. 40C00000)
stw r5,292(r1)
So, my ASM works, but not in all levels of the game. The game freezes in some levels. I'll have to track that down. Thanks for all of your help!
Yeah, I'm sorry, you're right. If f0 == 40C00000, f0 = f0^4.
You can't do bne- 0x12. 0x12 = 18 decimal, which would ruin the word alignment. 0x0C = 12 decimal = skip *two* instructions. Remember, b 0x04 = skip zero instructions, because the instruction pointer normally increments by 4 on non-branches, so b 0x04 is like a nop. b 0x08 skips one, b 0x0C skips two, b 0x10 skips three.
Also, your lis/ori to load r5 before pushing it onto the stack - I think you want to load the hex representation of 6^4, instead of 6.
Can you copy/paste some of the disassembly surrounding the stfs? Are you sure r3 and r5 are safe?
Besides for that, it looks like you've got the idea. Oh, btw, don't forget the '-' next to bne, it should be "bne-" for branches that jump forward and + for branches that jump backward. Note that this is separate from the sign of the branch target, so you could write "bne- +0xC"
Thanks for the bne explanation. I do appreciate it. It's strange that my bne +0x12 instruction showed as branching to the correct address when I used the disassembler in WiiRD. I do understand that I should not use 0x12, but 0x0C instead. Do you think that even though the disassembled code for bne +0x12 was branching to the right address that it could be causing other issues?
Also, I found that the issue with this hack not working in the other levels of the game is that those other levels use the value I'm pushing to the stack in other places that the working levels do not. So, I have to find a way to detect which level I'm in and add that to my ASM so I don't freeze in levels that don't support the hack.
Did you actually say "bne- 12", because the assembler would interpret the 12 as decimal and not hex.
Also, if there are other places that the float is used, you should try hooking those with C2 instead.
I did bne +0x12.
Yeah, I'll have to track down how/where that value is being used in the troublesome levels and deal with that using C2.
What was the 32-bit machine code that corresponded to the bne +0x12 in the WiiRD disassembly? The lower 16-bits will be the immediate.
Earlier I said that you can have problems if your ASM is being called for more than you intended. (i.e. RE4, the hurt-enemies function also affected the open-boxes) You might need to follow the float along the stack on a good level, and figure out what area is the one that reads the value the right way. Then, on a bad level, follow along again and the path should involve different branches.
That will help you find the place that's using the float in the way that you want to manipulate. Put the C2 there, so that it will be called only when needed, and not any more often than that.
I'll have to get back to you on the machine code as I'm not at my computer/Wii right now.
Thanks for the good advice on finding the "real" place to insert my C2 code. I'll have to track that down.