WiiRd forum

Wii & Gamecube Hacking => Wii Game hacking help => Topic started by: Nutmeg on August 02, 2010, 01:50:20 AM

Title: Freeze Time Assembly Language
Post by: Nutmeg on August 02, 2010, 01:50:20 AM
I am hacking NTSC-US Mario Strikers Charged.  I searched for the address that held the seconds on the "game clock."  The time is held as a regular 32-bit hexa-decimal.  I thought, "Easy enough, I'll just fill in a new value, and the timer should freeze."  That was not the case.  The timer ran perfectly.  As if nothing had been changed.

I know this is the right address, because I can see it counting down in memory viewer.  Also, by chance, I found the minutes at an address 32 bits away.  So, here is what I have so far (to help you vizualize what I see).

81000000  //address for seconds
81000004  //address for minutes
Note: Those are not the actual addresses.

I tried doing direct RAM fills on these addresses.  However, that did not achieve anything.

So... I thought maybe changing the code type from a 'direct value fill' to a 'direct ASM fill.'  To clear things up, I just set a breakpoint on the address that holds the seconds.  I chose "write" because I was hoping to find an 'addi' or a 'subi'.

I did not find any 'addi's or 'subi's.  I found 'stw's.  Here are the instructions that I found:

stw r28, 0(r26)
stw r29, 4(r26)

at r26, there is a pointer (81000000) to the address that holds the seconds on the "game clock."
at r28, the seconds on the "game clock."
at r29, the minutes on the "game clock."

Now remember, the address that holds the minutes is 32-bits away from the address that holds the seconds.  That is why there is the "4" in "stw r29, 4(r26)."

Here is what I understand from those instructions.  The seconds are stored at r28 and the minutes are stored at r29.  The way the game knows how many seconds/minutes there are on the game clock, is by loading r26 into r28 and r29.  However, there is a pointer at r26, so the value at the pointer is loaded into r28.  And the value at the pointer+4 is loaded into r29.

Here is where I am stuck.  I don't know how I should change these instuctions to freeze the "game clock." How should I compile instructions so that the "game clock" is frozen?

Help is appreciated. :)
Title: Re: Freeze Time Assembly Language
Post by: Deathwolf on August 02, 2010, 01:56:39 AM
there are 2 instruction!
make 2 assembly codes.
one with read and one with write.

change by breaking write the instruction to nop (60000000)
and change by read your instruction to:

lis r12,0xXXXX
ori r12,r12,0xXXXX
stw r12, -------- <-- your original instruction
------------------- <--- your original instruction

so write will stop your timer and with read you can set your own time value
Title: Re: Freeze Time Assembly Language
Post by: dcx2 on August 02, 2010, 04:31:30 AM
You have stw and lwz backwards.  stw rA,d(rB) takes the value in rA and writes it the address rB+d.  The stw that your breakpoint landed on is writing the new values to memory.

Without the disassembly, I can't offer you much help.  If you can, try to show about 10 instructions before the stw that your breakpoint hit.
Title: Re: Freeze Time Assembly Language
Post by: Nutmeg on August 03, 2010, 11:50:57 PM
Hmm, now I am getting different results, but I have no clue what I changed.   ???  But this is what I have now.

lwz r3, 92(r26)
li r4, 0
li r0, 1
stb r4, 142(r3)
lwz r3, 96(r26)
stb r4, 142(r3)
lwz r3, 12(r26)
stb r0, 142(r3)
lwz r3, 16(r26)
stb r0, 142(r3)
lwz r0, 0(r26)  //Broke here

-Thanks for the help.
Title: Re: Freeze Time Assembly Language
Post by: dcx2 on August 04, 2010, 12:00:19 AM
Without the disassembly, I can't offer you much help.  If you can, try to show about 10 instructions before the stw that your breakpoint hit.

---

I'm not sure what you're trying to do.  It looks like you're still confused about what lwz and stw do...

Take the instruction stb r4, 142(r3).  142(r3) will be the Effective Address EA; it will be the contents of r3 plus a displacement of 142.  r4 is the source register.  This instruction will take the contents of r4 and write those contents to the Effective Address.  It writes from registers to memory.

lwz r3, 96(r26).  This will calculate the EA for the contents as r26 + 96.  r3 will be the destination register.  I'm assuming this is your timer, because you said the pointer is in r26.  This instruction will load the value from the EA into the destination register.  It writes to registers from memory.

Then, your next instruction uses r3 as part of the EA.  But....your timer is not going to make a good pointer.  So the EA won't be a valid memory address, and you will crash.
Title: Re: Freeze Time Assembly Language
Post by: Nutmeg on August 04, 2010, 02:57:28 AM
Oops... I failed, I set a break-point on the wrong address...  :rolleyes:  I had it correct the first time.  :-\

Here is the correct dissassembly:

801A0004  lwz r0, 4(r26)
7C1D0040  cmplw r29, r0
40820010  bne- 0xYYYYYYYY
801A0008  lwz r0, 8(r26)
7C1B0040  cmplw r27, r0
41820594  beq- 0xYYYYYYYY
38000000  li r0, 0
2C1D0000  cmpwi r29, 0
90010024  stw r0, 36(r1)
90010020  stw r0, 32(r1)
939A0000  stw r28, 0(r26)  //Break-point is here

Specs are the same as the first post.

How should I write a code that prevents r28 from counting down?  Because whatever is in r28, gets loaded into the pointer at r26 which is displayed on the screen.  (I'm still not sure why a direct RAM fill did not work  ???) 

To answer your question on what I am trying to do:
To stop the value at 81YYYYYY from counting down.  (the address that holds the seconds on the "game clock")  For some reason doing a direct fill doesn't work.  (Any ideas there would be interesting to hear)

Bottom Line:  I am trying to stop the timer from counting down.
Sorry if that didn't answer your question. 

Thank you for the explanation on lwz and stw.  It's a lot more clear to me now, but I'm still not sure how to change stw or lwz to do what I want to do.

Title: Re: Freeze Time Assembly Language
Post by: dcx2 on August 04, 2010, 03:19:40 AM
I need more disassembly.  Are you using Gecko.NET?  If you right-click on the disassembly list view and click Copy, it will get everything.  I need to know what the branch destinations are, and the addresses of the instructions, etc.

I don't see anything working on r28.  You're right, there should be an addi or subi or something nearby.

If there isn't one of those, you may have found an intermediate step.  Maybe it's being prepared for display on the screen  That would explain why poke wouldn't work; the poked value is over-written.  It's too far down the food chain.
Title: Re: Freeze Time Assembly Language
Post by: Nutmeg on August 04, 2010, 08:08:15 PM
801EA220:  9803008E   stb   r0,142(r3)
801EA224:  807A0010   lwz   r3,16(r26)
801EA228:  9803008E   stb   r0,142(r3)
801EA22C:  801A0000   lwz   r0,0(r26)
801EA230:  7C1C0040   cmplw   r28,r0
801EA234:  4082001C   bne-   0x801ea250
801EA238:  801A0004   lwz   r0,4(r26)
801EA23C:  7C1D0040   cmplw   r29,r0
801EA240:  40820010   bne-   0x801ea250
801EA244:  801A0008   lwz   r0,8(r26)
801EA248:  7C1B0040   cmplw   r27,r0
801EA24C:  41820594   beq-   0x801ea7e0
801EA250:  38000000   li   r0,0
801EA254:  2C1D0000   cmpwi   r29,0
801EA258:  90010024   stw   r0,36(r1)
801EA25C:  90010020   stw   r0,32(r1)
801EA260:  939A0000   stw   r28,0(r26)  //break-point is here
801EA264:  93BA0004   stw   r29,4(r26)
801EA268:  937A0008   stw   r27,8(r26)
801EA26C:  40820244   bne-   0x801ea4b0
801EA270:  C002A694   lfs   f0,-22892(r2)
801EA274:  FC1E0040   fcmpo   cr0,f30,f0
801EA278:  40800238   bge-   0x801ea4b0
801EA27C:  2C1E0000   cmpwi   r30,0
801EA280:  40820230   bne-   0x801ea4b0
801EA284:  7F86E378   mr   r6,r28
801EA288:  38610040   addi   r3,r1,64
801EA28C:  38800008   li   r4,8
801EA290:  38ADA378   subi   r5,r13,23688
801EA294:  4CC63182   crclr   6,6
801EA298:  480C0685   bl   0x802aa91c
801EA29C:  7F66DB78   mr   r6,r27
801EA2A0:  38610030   addi   r3,r1,48
801EA2A4:  38800008   li   r4,8
801EA2A8:  38ADA378   subi   r5,r13,23688
801EA2AC:  4CC63182   crclr   6,6
801EA2B0:  480C066D   bl   0x802aa91c
801EA2B4:  836DECC8   lwz   r27,-4920(r13)
801EA2B8:  386DA380   subi   r3,r13,23680
801EA2BC:  480C0841   bl   0x802aaafc
Title: Re: Freeze Time Assembly Language
Post by: dcx2 on August 04, 2010, 08:32:57 PM
I might need more from before 801EA220 (I still can't tell where r28 comes from), but we might be able to work something out here...

It looks like 0(r26), 4(r26), and even 8(r26) contain the timer (seconds, minutes, and...hours?).  Line 22C loads the seconds value into r0, and 230 compares the current r28 against the value we just loaded from memory.  if r28 != r0 (in other words, if r28 is different from the timer value in memory), we branch to 250, which results in writing the new values from r27-r29 to memory (and some stuff on the stack...?  Not sure why this is here).  

238-240 is a minutes-based duplicate of 22C-234.  244-24C is probably an hours-based version of the same

254 tests the hours value before 26C potentially branches (probably to display the hours).  Not sure what's going on after this, but it's a pretty epic function call with four parameters!  r3 is an index into the stack (probably related to 258/25C).  r4 is 8...not sure what this signifies.  r5 is a pointer to something in the read/write small data area...perhaps a global variable?  Importantly, r6 is the seconds left on the clock.

I'll bet that bl is doing something interesting...but I don't think we'll need it.

---

Try this test.  Recall how poking does not happen at the right time, and hence the intermediate value is being over-written.  Well, when your breakpoint hits on 801EA260, you are at the right time.

Wait for the seconds value to become small...something less than 10 seconds.  Then set your breakpoint.  While at the breakpoint, modify the contents of r28.  Make it something big, like 50 seconds.  Then hit run.  If it works, then we can use this area for a hook.  If it doesn't work, you'll have to follow the disassembly backwards and figure out where r28 came from.

---

Alternatively, if you're daring, you could try replacing the bne- 0x801ea250 @ address 0x801EA234 with a b 0x801ea7e0.  This would effectively say "nothing to see here, move along", bypassing the comparison between r27-r29 and the values in memory.
Title: Re: Freeze Time Assembly Language
Post by: Nutmeg on August 04, 2010, 09:00:08 PM
The hours is actually milliseconds.  See where it says cmpwi r29, 0.  If r29 is 0 (if the minutes are 0) the milliseconds are displayed, but I haven't messed with that.  I really don't understand cmpwi, but I thought that that line might have something to do with that.  Thanks for clarifying what was happening there.

During the break-point, I set r28 (from 4) to 53 (34 in hex).  It momentarily worked (53 was displayed for less than a second) but then it continued counting down to 3... 2... etc.

Hook type maybe possible here... ??? It did more than the direct RAM fill.
Title: Re: Freeze Time Assembly Language
Post by: dcx2 on August 04, 2010, 09:08:40 PM
Nah, if it blipped momentarily and then went back to counting down, you found a duplicate copy of the timer that's being prepared for display.  You could try my suggestion about replacing the bne- with a different b, but that probably won't work, either.  It will look like it's working...because the display is frozen.  But the actual timer is still counting in memory.

This happened to me a lot with Ghost Squad...lots of timers in that game.

You're going to have to back track and see where r28 came from.  You might have to follow a few more breakpoints too.

BTW, cmpwi will CoMPare a Word to an Immediate.  Word = 32 bits.  So it tests the 32-bit value in r29 against 0.  The result of the comparison (less than, equal to, or greater than) is stored in the Condition Register CR.  Then, the CPU can branch differently based on the result of the comparison.  For instance, bne- will branch only if the equal bit of the CR is NOT set.  ble- would branch if either the less-than or equals bits are set.
Title: Re: Freeze Time Assembly Language
Post by: Nutmeg on August 05, 2010, 12:27:06 AM
so I did what you told me to do wiht the branch.  It seems like it freezes the timer.  But after 5 minutes, the games still ends.  That probably means that I have the wrong address.  Like you said, I probably have the address that displays what is on the screen, but another address somewhere else actually holds the time.
Title: Re: Freeze Time Assembly Language
Post by: dcx2 on August 05, 2010, 02:41:01 AM
If you follow the function backwards from your breakpoint, you should be able to figure out where r28 comes from.  That's probably the real timer.
Title: Re: Freeze Time Assembly Language
Post by: Nutmeg on August 05, 2010, 07:17:30 PM
Well, I found some other places that deal with r28:

801EA058:  FC20F890   fmr   f1,f31
801EA05C:  4800001C   b   0x801ea078
801EA060:  3C004330   lis   r0,17200
801EA064:  90610054   stw   r3,84(r1)
801EA068:  C822A6A0   lfd   f1,-22880(r2)
801EA06C:  90010050   stw   r0,80(r1)
801EA070:  C8010050   lfd   f0,80(r1)
801EA074:  EC200828   fsubs   f1,f0,f1
801EA078:  481A36FD   bl   0x8038d774
801EA07C:  3C808889   lis   r4,-30583
801EA080:  7C7F1B78   mr   r31,r3
801EA084:  38048889   subi   r0,r4,30583
801EA088:  3B600000   li   r27,0
801EA08C:  7C001816   mulhwu   r0,r0,r3
801EA090:  541DD97E   rlwinm   r29,r0,27,5,31
801EA094:  1C1D003C   mulli   r0,r29,60
801EA098:  7F801850   sub   r28,r3,r0              //right here
801EA09C:  4BF275D1   bl   0x8011166c
801EA0A0:  C002A694   lfs   f0,-22892(r2)
801EA0A4:  FC1E0040   fcmpo   cr0,f30,f0
801EA0A8:  4C401382   cror   2,0,2
801EA0AC:  4182000C   beq-   0x801ea0b8
801EA0B0:  2C1E0000   cmpwi   r30,0
801EA0B4:  4182004C   beq-   0x801ea100
801EA0B8:  881A0054   lbz   r0,84(r26)
801EA0BC:  2C000000   cmpwi   r0,0
801EA0C0:  40820040   bne-   0x801ea100
801EA0C4:  38000001   li   r0,1
801EA0C8:  38A000CC   li   r5,204
801EA0CC:  981A0054   stb   r0,84(r26)
801EA0D0:  38600033   li   r3,51
801EA0D4:  380000FF   li   r0,255
801EA0D8:  38810028   addi   r4,r1,40
801EA0DC:  98A10028   stb   r5,40(r1)
801EA0E0:  98610029   stb   r3,41(r1)
801EA0E4:  9861002A   stb   r3,42(r1)
801EA0E8:  9801002B   stb   r0,43(r1)
801EA0EC:  807A000C   lwz   r3,12(r26)
801EA0F0:  48118295   bl   0x80302384
801EA0F4:  807A0010   lwz   r3,16(r26)
801EA0F8:  38810028   addi   r4,r1,40
801EA0FC:  48118289   bl   0x80302384
801EA100:  2C1D0000   cmpwi   r29,0
801EA104:  40820044   bne-   0x801ea148
801EA108:  C002A694   lfs   f0,-22892(r2)
801EA10C:  FC1E0040   fcmpo   cr0,f30,f0
801EA110:  40800038   bge-   0x801ea148
801EA114:  2C1E0000   cmpwi   r30,0
801EA118:  40820030   bne-   0x801ea148
801EA11C:  3C004330   lis   r0,17200
801EA120:  93810054   stw   r28,84(r1)         //and right here
801EA124:  C842A6A0   lfd   f2,-22880(r2)
801EA128:  90010050   stw   r0,80(r1)
801EA12C:  C002A698   lfs   f0,-22888(r2)
801EA130:  C8210050   lfd   f1,80(r1)
801EA134:  EC211028   fsubs   f1,f1,f2
801EA138:  EC3E0828   fsubs   f1,f30,f1
801EA13C:  EC200072   fmuls   f1,f0,f1
801EA140:  481A3635   bl   0x8038d774
801EA144:  7C7B1B78   mr   r27,r3
801EA148:  2C1E0000   cmpwi   r30,0
801EA14C:  40820070   bne-   0x801ea1bc
801EA150:  3C004330   lis   r0,17200
801EA154:  806DD8D4   lwz   r3,-10028(r13)
801EA158:  93E10054   stw   r31,84(r1)
801EA15C:  C822A6A0   lfd   f1,-22880(r2)
801EA160:  90010050   stw   r0,80(r1)
801EA164:  C043001C   lfs   f2,28(r3)
801EA168:  C8010050   lfd   f0,80(r1)
801EA16C:  EC000828   fsubs   f0,f0,f1
801EA170:  FC001000   fcmpu   cr0,f0,f2
801EA174:  40820048   bne-   0x801ea1bc
801EA178:  881A0054   lbz   r0,84(r26)
801EA17C:  2C000000   cmpwi   r0,0
801EA180:  4182003C   beq-   0x801ea1bc
801EA184:  38000000   li   r0,0
801EA188:  807A000C   lwz   r3,12(r26)
801EA18C:  981A0054   stb   r0,84(r26)
801EA190:  389A0056   addi   r4,r26,86
801EA194:  981A0055   stb   r0,85(r26)
801EA198:  481181ED   bl   0x80302384
801EA19C:  807A0010   lwz   r3,16(r26)
801EA1A0:  389A0056   addi   r4,r26,86
801EA1A4:  481181E1   bl   0x80302384
801EA1A8:  807A000C   lwz   r3,12(r26)
801EA1AC:  38000001   li   r0,1
801EA1B0:  9803008E   stb   r0,142(r3)
801EA1B4:  807A0010   lwz   r3,16(r26)
801EA1B8:  9803008E   stb   r0,142(r3)
801EA1BC:  2C1E0000   cmpwi   r30,0
801EA1C0:  41820044   beq-   0x801ea204
801EA1C4:  881A0055   lbz   r0,85(r26)
801EA1C8:  2C000000   cmpwi   r0,0
801EA1CC:  4082000C   bne-   0x801ea1d8
801EA1D0:  38000001   li   r0,1
801EA1D4:  981A0055   stb   r0,85(r26)
801EA1D8:  807A005C   lwz   r3,92(r26)
801EA1DC:  38800001   li   r4,1
801EA1E0:  38000000   li   r0,0
801EA1E4:  9883008E   stb   r4,142(r3)
801EA1E8:  807A0060   lwz   r3,96(r26)
801EA1EC:  9883008E   stb   r4,142(r3)
801EA1F0:  807A000C   lwz   r3,12(r26)
801EA1F4:  9803008E   stb   r0,142(r3)
801EA1F8:  807A0010   lwz   r3,16(r26)
801EA1FC:  9803008E   stb   r0,142(r3)
801EA200:  4800002C   b   0x801ea22c
801EA204:  807A005C   lwz   r3,92(r26)
801EA208:  38800000   li   r4,0
801EA20C:  38000001   li   r0,1
801EA210:  9883008E   stb   r4,142(r3)
801EA214:  807A0060   lwz   r3,96(r26)
801EA218:  9883008E   stb   r4,142(r3)
801EA21C:  807A000C   lwz   r3,12(r26)
801EA220:  9803008E   stb   r0,142(r3)
801EA224:  807A0010   lwz   r3,16(r26)
801EA228:  9803008E   stb   r0,142(r3)
801EA22C:  801A0000   lwz   r0,0(r26)
801EA230:  7C1C0040   cmplw   r28,r0
801EA234:  4082001C   bne-   0x801ea250
801EA238:  801A0004   lwz   r0,4(r26)
801EA23C:  7C1D0040   cmplw   r29,r0
801EA240:  40820010   bne-   0x801ea250
801EA244:  801A0008   lwz   r0,8(r26)
801EA248:  7C1B0040   cmplw   r27,r0
801EA24C:  41820594   beq-   0x801ea7e0
801EA250:  38000000   li   r0,0
801EA254:  2C1D0000   cmpwi   r29,0
801EA258:  90010024   stw   r0,36(r1)
801EA25C:  90010020   stw   r0,32(r1)
801EA260:  939A0000   stw   r28,0(r26)  //This is where the duplicate time is stored
801EA264:  93BA0004   stw   r29,4(r26)
801EA268:  937A0008   stw   r27,8(r26)
801EA26C:  40820244   bne-   0x801ea4b0
801EA270:  C002A694   lfs   f0,-22892(r2)
801EA274:  FC1E0040   fcmpo   cr0,f30,f0
801EA278:  40800238   bge-   0x801ea4b0
801EA27C:  2C1E0000   cmpwi   r30,0
801EA280:  40820230   bne-   0x801ea4b0
801EA284:  7F86E378   mr   r6,r28
801EA288:  38610040   addi   r3,r1,64
801EA28C:  38800008   li   r4,8
801EA290:  38ADA378   subi   r5,r13,23688
801EA294:  4CC63182   crclr   6,6
801EA298:  480C0685   bl   0x802aa91c
801EA29C:  7F66DB78   mr   r6,r27
801EA2A0:  38610030   addi   r3,r1,48
801EA2A4:  38800008   li   r4,8
801EA2A8:  38ADA378   subi   r5,r13,23688
801EA2AC:  4CC63182   crclr   6,6
801EA2B0:  480C066D   bl   0x802aa91c
801EA2B4:  836DECC8   lwz   r27,-4920(r13)
801EA2B8:  386DA380   subi   r3,r13,23680
801EA2BC:  480C0841   bl   0x802aaafc

-Sorry for the lengthy post...
Title: Re: Freeze Time Assembly Language
Post by: dcx2 on August 05, 2010, 09:13:23 PM
If you're concerned about long posts, you can wrap the disassembly in a spoiler.  But that disasm is quite yummy....although I feel like a hungry Luma, because I think I need some more!  This could potentially be a very long function...you will need to scroll up and look for a series of instructions that look something like

stwu r1,-xyz(r1)
mflr r0

That's the beginning of the function.  This is an important area, because at this point r3, up to r10, can have incoming parameters.  One of these parameters might be the timer.

---

This is what I've got so far.  Using this portion...

801EA060:  3C004330   lis   r0,17200
801EA064:  90610054   stw   r3,84(r1)
801EA068:  C822A6A0   lfd   f1,-22880(r2)
801EA06C:  90010050   stw   r0,80(r1)
801EA070:  C8010050   lfd   f0,80(r1)
801EA074:  EC200828   fsubs   f1,f0,f1
801EA078:  481A36FD   bl   0x8038d774
801EA07C:  3C808889   lis   r4,-30583
801EA080:  7C7F1B78   mr   r31,r3
801EA084:  38048889   subi   r0,r4,30583
801EA088:  3B600000   li   r27,0
801EA08C:  7C001816   mulhwu   r0,r0,r3
801EA090:  541DD97E   rlwinm   r29,r0,27,5,31
801EA094:  1C1D003C   mulli   r0,r29,60
801EA098:  7F801850   sub   r28,r3,r0              //right here

058 through 074 are preparing f1 as an input parameter for the bl @078.  I don't know what the stw r3 is doing @064, though...it looks like it might be participating in the construction of the float that ends up in f1?

Then we get to the good stuff.  The bl @078 is returning something interesting in r3.  This interesting value is cached in r31 @080.  07C/084 put 88887777 into r0.  Then there's an interesting mix that I can't quite grok...I don't follow what the mul/rotate/mul is doing.  But it's converting the 88887777 into something that can be subtracted from our interesting value in r3.  This is then used as the new r28 value.  r3 still has the interesting value returned from @078 by the time we get to the bl @09C.

098 might be the line that's calculating r28.  You could try the modify-r28-during-breakpoint trick on line 09C (NOTE: that must be *after* the sub @098 has loaded r28).

---

I'm interested in the bl's @078, 09C, and 140.  They look like nice candidates.
Title: Re: Freeze Time Assembly Language
Post by: Nutmeg on August 06, 2010, 02:14:11 AM
Ok.  r31 stores the time!  not the seconds, but the overall time.  e.x. for 1 min and 1 sec to go, it is stored as 3D.  But it stores what is about to be loaded... so 3C if 1:01 is displayed on the game clock.

801E9FD4:  9BE3008E   stb   r31,142(r3)
801E9FD8:  BB61002C   lmw   r27,44(r1)
801E9FDC:  80010044   lwz   r0,68(r1)
801E9FE0:  7C0803A6   mtlr   r0
801E9FE4:  38210040   addi   r1,r1,64
801E9FE8:  4E800020   blr   
801E9FEC:  9421FF70   stwu   r1,-144(r1)
801E9FF0:  7C0802A6   mflr   r0
801E9FF4:  90010094   stw   r0,148(r1)
801E9FF8:  DBE10080   stfd   f31,128(r1)
801E9FFC:  F3E10088   psq_st   f31,136(r1),0,0
801EA000:  DBC10070   stfd   f30,112(r1)
801EA004:  F3C10078   psq_st   f30,120(r1),0,0
801EA008:  39610070   addi   r11,r1,112
801EA00C:  481A388D   bl   0x8038d898
801EA010:  808DDB98   lwz   r4,-9320(r13)
801EA014:  7C7A1B78   mr   r26,r3          //dupicate time is stored at r26
801EA018:  806DD8D4   lwz   r3,-10028(r13)
801EA01C:  8BC40EC5   lbz   r30,3781(r4)
801EA020:  4BE6E695   bl   0x800586b4
801EA024:  806DD8D4   lwz   r3,-10028(r13)
801EA028:  C3E2A690   lfs   f31,-22896(r2)
801EA02C:  C003001C   lfs   f0,28(r3)
801EA030:  EC410028   fsubs   f2,f1,f0
801EA034:  EFC00828   fsubs   f30,f0,f1
801EA038:  FC02F840   fcmpo   cr0,f2,f31
801EA03C:  40810008   ble-   0x801ea044
801EA040:  48000008   b   0x801ea048
801EA044:  FFE01090   fmr   f31,f2
801EA048:  FC20F090   fmr   f1,f30
801EA04C:  481A3729   bl   0x8038d774
801EA050:  2C1E0000   cmpwi   r30,0
801EA054:  4182000C   beq-   0x801ea060
801EA058:  FC20F890   fmr   f1,f31
801EA05C:  4800001C   b   0x801ea078
801EA060:  3C004330   lis   r0,17200
801EA064:  90610054   stw   r3,84(r1)  //r3 is stored at 806f7a90 + 84
801EA068:  C822A6A0   lfd   f1,-22880(r2)
801EA06C:  90010050   stw   r0,80(r1)
801EA070:  C8010050   lfd   f0,80(r1)
801EA074:  EC200828   fsubs   f1,f0,f1
801EA078:  481A36FD   bl   0x8038d774
801EA07C:  3C808889   lis   r4,-30583
801EA080:  7C7F1B78   mr   r31,r3
801EA084:  38048889   subi   r0,r4,30583
801EA088:  3B600000   li   r27,0
801EA08C:  7C001816   mulhwu   r0,r0,r3
801EA090:  541DD97E   rlwinm   r29,r0,27,5,31
801EA094:  1C1D003C   mulli   r0,r29,60
801EA098:  7F801850   sub   r28,r3,r0
801EA09C:  4BF275D1   bl   0x8011166c
801EA0A0:  C002A694   lfs   f0,-22892(r2)
801EA0A4:  FC1E0040   fcmpo   cr0,f30,f0
801EA0A8:  4C401382   cror   2,0,2
801EA0AC:  4182000C   beq-   0x801ea0b8
801EA0B0:  2C1E0000   cmpwi   r30,0
801EA0B4:  4182004C   beq-   0x801ea100
801EA0B8:  881A0054   lbz   r0,84(r26)
801EA0BC:  2C000000   cmpwi   r0,0
801EA0C0:  40820040   bne-   0x801ea100
801EA0C4:  38000001   li   r0,1
801EA0C8:  38A000CC   li   r5,204
801EA0CC:  981A0054   stb   r0,84(r26)
801EA0D0:  38600033   li   r3,51
801EA0D4:  380000FF   li   r0,255
801EA0D8:  38810028   addi   r4,r1,40
801EA0DC:  98A10028   stb   r5,40(r1)
801EA0E0:  98610029   stb   r3,41(r1)
801EA0E4:  9861002A   stb   r3,42(r1)
801EA0E8:  9801002B   stb   r0,43(r1)
801EA0EC:  807A000C   lwz   r3,12(r26)
801EA0F0:  48118295   bl   0x80302384
801EA0F4:  807A0010   lwz   r3,16(r26)
801EA0F8:  38810028   addi   r4,r1,40
801EA0FC:  48118289   bl   0x80302384
801EA100:  2C1D0000   cmpwi   r29,0
801EA104:  40820044   bne-   0x801ea148


The line above where r3 is stored at an address isn’t really stored at an address.  The time just blips in that address for a split second after the breakpoint.  After that, there is a pointer that leads to a bunch of other pointers…

How should I stop r31 from decreasing?
Title: Re: Freeze Time Assembly Language
Post by: dcx2 on August 06, 2010, 06:31:29 AM
Seeing the value in r31 does not mean that's where it really is.  r31 is probably another copy.

@014, what is in r3?  You say "duplicate time", but r3 is being cached in r26, and r26 is being used as a pointer @0CC.

It may help you to know that bl is how the CPU calls functions, and that values are returned in r3 and r4.  So the value in r3 @04C is going to be different from the value in r3 @050.  The value in r3 that we get after calling the bl @04C will be the time you want.  This value is put into r31 @080.  It's also put on the stack @064, as part of building a float that ends up in f0 @070.

---

I like the fsubs @030/034.  The fcmpo @038 and the stuff after it through 048 are making sure that the smaller of f31 and f2 is loaded into f31.  But then f30 is put into f1...probably as part of getting ready to call that bl we like @04C.  It feels like that bl is taking the float in f1 and turning it into an integer in r3.  f30 came from the fsubs @034.  It looks like f0 has the time, stored as a float, @030/034.  f0 is loaded @02C, by a pointer in r3.

That pointer in r3 comes from a special area.  r13 is the small read/write data area.  Constants and globals and stuff like that are stored here.  So I bet the pointer to the timer object is stored as a global variable, and the pointer is loaded into r3 @024.

---

In summary, this is my guess.  r3 is loaded with the timer pointer @024.  Then the timer, as a float, is loaded @02C.  The time then ticks down once @034.  The new time is then converted to an integer by the call to 04C.  This integer is then used @064 and @080.  I think it's the value in r31 that you see.

---

Suggestion: You can test this, but it's more difficult with a float; you can't modify a float register, you need to modify the memory it reads instead.  Set a breakpoint @02C, right before f0 is loaded with the timer-as-float.  Then click Show Mem to go to Memory Viewer.  Go to the Poke box, right click, to hex->float, then increase the value you get...like, double it or something obnoxiously large, then float->hex, and click Poke.  Cross your fingers and hit Run Game.

---

In case you like the tid bits about what the code is doing...FEC through 00C is function prologue.  Make room on the stack (...144 bytes?  That's a LOT of room!), back up the LR on the stack, back up a few floats on the stack, and the bl @00C is probably backing up registers r31 through...r26?...on the stack.

You expressed interest in the stw @064.  Note that the pointer being used for that stw is coming from r1.  r1 is the stack pointer; anything that lives on the stack is short-lived and temporary.  Each function has a "stack frame"; this stack frame is created in the function prologue, and destroyed in the function epilogue at the end (when it executes a mflr/blr).  Once you go through that blr, everything on the stack is lost forever.  The next time this function is called, the stack could be different, which means that value could go to a different address each time this function is called.