IF THEN ELSE Help

Started by live2play, April 11, 2010, 09:29:39 PM

Previous topic - Next topic

live2play

How would I create ASM that only executes if the value of f1 equals a certain value?

Example:

If f1 = 40C00000 then .....

else .....

dcx2

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.

live2play

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?

dcx2

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"

live2play

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)

live2play

#5
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?

dcx2

#6
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.

live2play

#7
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


dcx2

#8
 :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)

live2play

#9
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.

live2play

#10
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)

live2play

#11
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!

dcx2

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"

live2play

#13
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.

dcx2

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.