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. :)
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
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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.
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...
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.
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?
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.