WiiRd forum

Wii & Gamecube Hacking => Wii Game hacking help => Topic started by: live2play on April 11, 2010, 09:29:39 PM

Title: IF THEN ELSE Help
Post by: live2play on April 11, 2010, 09:29:39 PM
How would I create ASM that only executes if the value of f1 equals a certain value?

Example:

If f1 = 40C00000 then .....

else .....
Title: Re: IF THEN ELSE Help
Post by: dcx2 on April 11, 2010, 10:10:05 PM
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.
Title: Re: IF THEN ELSE Help
Post by: live2play on April 11, 2010, 11:18:50 PM
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?
Title: Re: IF THEN ELSE Help
Post by: dcx2 on April 12, 2010, 12:05:39 AM
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"
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 03:49:10 AM
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)
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 04:32:10 AM
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?
Title: Re: IF THEN ELSE Help
Post by: dcx2 on April 12, 2010, 04:52:02 AM
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.
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 05:04:10 AM
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

Title: Re: IF THEN ELSE Help
Post by: dcx2 on April 12, 2010, 05:20:40 AM
 :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)
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 05:26:09 AM
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.
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 05:34:09 AM
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)
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 06:30:02 AM
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!
Title: Re: IF THEN ELSE Help
Post by: dcx2 on April 12, 2010, 12:46:14 PM
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"
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 05:52:22 PM
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.
Title: Re: IF THEN ELSE Help
Post by: dcx2 on April 12, 2010, 06:19:14 PM
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.
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 06:30:48 PM
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.
Title: Re: IF THEN ELSE Help
Post by: dcx2 on April 12, 2010, 06:52:56 PM
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.
Title: Re: IF THEN ELSE Help
Post by: live2play on April 12, 2010, 07:05:10 PM
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.