I have a problem regarding finding codes. For example in Wii Play, I want to get the Tanks Coordinates (and later write an ASM code which uses the Cursors coordinates to calculate where the tank can be ported to, using the wiimote). But If I try to get them, I receive some strange behaving variables, which increase and decrease with their X AND Y coordinate on top of that, freezing them does not freeze the Tank itself. Can you suggest me something, (I already tried to set a bp on that variable but I couldn't find anything useful)
Greets,
Fighter19
EDIT:I already tried searching different memory sections.
If you look at the address in Memory Viewer with auto-update on, you should see that the numbers "react" properly.
There's a chance the coordinates may be floats. So you could set Gecko.NET to use Single display mode if you don't naturally have a feel for what 32-bit float hex patterns look like.
Poking may not work like you expect. There may be several copies of the coordinates in memory that are being passed along from one to the next. The key is finding where one of these transitions happens and overwriting the source, not the destination. If you overwrite the destination, chances are the source will be used next frame and your frozen value will be over-written.
Thank you for answering in first place, so If i understand properly those are mirrors I found, since they are changing according to the position. I'll take a look at it asap, but somehow I couldn't find the source. I tried freezing all addresses I found (around 12, afaik) but no one worked. But shouldn't setting a write breakpoint to the mirrors bring me to the source, anyway? (e.g it outputs mov eax,ecx ... then I look at ecx and this should be the source eventually this happens more often)
Hmm, so you know some x86. Not sure what else you already know.
It's not always as easy as just following registers. You have to know the calling convention, in case it's an argument, so you can follow it back to the caller. Check out these tutorials.
http://wiird.l0nk.org/forum/index.php/topic,5080 - Function calls, arguments, and walking the stack
http://wiird.l0nk.org/forum/index.php/topic,5249.0.html - introduction to CPU architecture and ASM
http://wiird.l0nk.org/forum/index.php/topic,6555.0.html - On the safety of registers
Proper application of write breakpoints can help you follow the chain, but there's not necessarily a "source" this way. You can follow it back over and over and eventually land at the same place as your first breakpoint.
Thank you! This looks very useful, seems that it can help me understanding how this with push and pop works on an applied example (e.g I tried figuring out how to deactivate a menu item, at some point a register got incremented by one, when you move the cursor, but disabling this function would lead into not moving it at all, I wasn't able to find a solution to tell the program that he should just skip THIS item and go to the next)
If you're on the Gecko.NET disassembly tab for your breakpoint, you can try right-clicking and then "Copy Function" into a spoiler here (use the spoiler tag in case it's huge, so that it doesn't distort the thread), and I will read it and try to explain what's going on. Sounds like you might want to use a C2 code to optionally avoid execution.
Also, one thing to watch out for is whether the instruction you find with a write breakpoint also writes to other addresses. You can set an execute breakpoint once you find the write instruction's address. If the execute breakpoint hits exactly once per frame, it only writes to that address. If it doesn't hit every frame, that's probably still okay, but check the address. If it writes more than once per frame, it's probably writing to different addresses.
Well, thank you for your help. I wouldn't have been able to search for it without your help since I didn't know how float would act like. I managed to build the code I wanted using gRNs so there was no need to overwrite any functions. It probably will not work on other systems since it seems that the address is changing due to some other factors (probably any private data that will push the values), since the Teleporter code available on the Geckocodes page wasn't working (Tanks: Multi Teleporter by Bully@Wiiplaza)
2837A640 EFFF1000 //If "-" Button is pressed
82200001 8037A548 //Store the X-position of the cursor into gr0
86A00001 43E6A000 //Multiply the value by 461.25 to get an equivalent Tank position
84200001 91D30624 //Write that new position
82200001 8037A54C //Same for Y-position
86A00001 442FF000 //Just with other value
84200001 91D3062C
E0000000 80008000
Now the tank can be set wherever my cursor points to, yay! :cool:
Well the only problem is the pointer, I have no clue how to begin to search for it.
It's not working because there are different versions of Wii Play! how it seems.
ASM or F6 is needed then. :)
In fact, your code won't work for me now since I could never get other people's codes to work on this game either. :P
Lol, awesome code. It must be lots of fun to use!
Hmm I'll probably try to take a look at F6 codes then, though I need to get my hands (the one or the other way) on another version of Wii Play.
So meanwhile using assembler would be a good option. Do I have to break the function which writes to it directly and insert an ASM (of course with Branching etc.)? Well this will take way more time... How to create an ASM code? I mean I can't simply create it using the debugger, is there some sort of mini-linker which parses for example nop -> 60 (I think it is this byte in PPC assembly) ?
I think I used one, but I don't know the name anymore. So lets assume the value is being stored in r3, I have to copy that function, then I have to do that whole stuff again using assembler and on top If all registers are in use I have to write one down somewhere in memory so I can restore him later, after the operation is done?
You either do a breakpoint read or write to get a good hook address to inject your assembly code. Use ASMWiiRd to convert from source to gecko code and vice versa. You only need to type what your code should do, like loading values into a register and storing them again using the destination given by the default instruction for example. Unfortunately your teleporting code is quite advanced to code already :/ but you should be able to get the hang of it quickly. Two breaks may be needed since I wouldn't say relying on 8037A548 to be correct for all regions is a good idea.
Idea:
An assembly hook to retrieve the value from 8037A548 and store it at some free memory location
Another assembly hook to access coordinates, load value retrieved from 1st code, do stuff like multiplying and writing to the coordinates (conditionally?)
You can see examples where it says C2 at the beginning or even better F6 for this game also.
So I overwrite the function that writes to 8037A548 and let it write to a new memory location AND at the original, then use the new address in the other injected code (that overwrites writing to the position address)? Then I have to paste the original code in again, since we don't want to disable moving completely adding a cmp (probably another function in PPC) and check whether the button is "-" (put it simply, probably there it's needed to modify this and copy it to a free location as well) then the rest of the code gets executed (storing it in registers, multiplying, writing it back), Well this sounds a little dirty but should work fine, shouldn't it?
EDIT: Wow that I need so many words in extra, than you to describe the same thing...
So yeah, I'll try this tomorrow, sounds quite fun and ambitious :) . Thank you for providing me that idea.
EDIT2: There is no EASY way to dynamically allocate free space, is there?
Yes, that's how it would work.
I think there's no easy way to allocate free space dynamically but go check around 80001500 somewhere, it's unused for every game.
Man, I tried around replacing this function but somehow it freezes.
stfs f3,4(r3) //Sets position
lis r27,0x8000 //HiByte
addi r27,r27,0x159C //LoByte for free memory
stfs f3,0(r27) //Store f3 (position) to address of r27
lis r22,0x8037 //HiByte
addi r22,r22,0x7FFF //
addi r22,r22,0x2641 //Add together to achieve 8037A640
lhz r23,0(r22) //Load value from 8037A640 (Pressed button) (address of r22) to r23
cmpi 3,0,r23,0x1000 //Is r23 = 0x1000 (So is the "-" Button pressed), using CR3
beq 3,SET //If value in CR3 return true, jump to SET
lis r27,0
lis r23,0
lis r22,0 //If not clean everything up
b END //Jump to a location after SET so it doesn't get executed
SET:
addi r27,r27,0x0020 //Increment by 0x0020
stw r23,0(r27) //Store the value from r23 (In this case it HAS to be 0x1000) to address of r27
lis r27,0
lis r23,0
lis r22,0 //clean everything up
END:
nop
(now using the disassembler you can see it now returns to the normal function)
In ASMWIIRD ; doesn't work as comment anyway...
EDIT:Insertion address is 8024239C
EDTI2:Ok, appearantly it DOES work, maybe some bug in geckodotnet.
Pfeeeew, finished coding that now, hopefully it works on other PAL versions now aswell:
C22423A4 00000012
D003000C 7E2802A6
3E408024 3A5243FC
7E119000 40920070
3F608000 3B7B159C
3EC08037 3AD67FFF
3AD62641 A2F60000
2D971000 418E0024
3B7B0020 3AC00000
92DB0000 3F600000
3EE00000 3EC00000
3E200000 48000030
823B0000 92230004
3B7B0004 823B0000
9223000C 3B7B0020
B2FBFFFC 3F600000
3EE00000 3EC00000
3E200000 60000000
60000000 00000000
C20A4280 00000004
D05E0020 3F608000
3B7B15AC D05B0000
3B7B0004 D01B0000
3F600000 00000000
280015BC EFFF1000
82200001 800015AC
86A00001 43E6A000
84200001 8000159C
82200001 800015B0
86A00001 442FF000
84200001 800015A0
E0000000 80008000
I hope double posting is allowed.
Wow, fantastic work man. It should be perfectly fine!
Tips:
To load a 32 bit value you should use lis and ori to get rid of ugly double addi's.
"Brainlessly" overwrite register 12 and work your way down, restoring them again is not needed.
Comments work like this: #This is a comment, it has an opening and closing tag# Be aware not to make it line-wrap in ASMWiiRd!
Gecko.Net may have bugs but codes do the right thing.
So clearing all the registers I used is not needed?
Means I can simply leave out all
lis rXX,0 ?
Yes, but use r12, r11, r10 to be safe
Are you sure, because there are already values in those registers?
EDIT:I'll trim the code by removing some unnecessary ADDI instructions.
Yes, I told you.
There's a tutorial about the safety of registers if you're thirsty for details, lol.
Quote from: Fighter19 on December 03, 2013, 07:45:43 PMHmm I'll probably try to take a look at F6 codes then, though I need to get my hands (the one or the other way) on another version of Wii Play.
So meanwhile using assembler would be a good option. Do I have to break the function which writes to it directly and insert an ASM (of course with Branching etc.)? Well this will take way more time... How to create an ASM code? I mean I can't simply create it using the debugger, is there some sort of mini-linker which parses for example nop -> 60 (I think it is this byte in PPC assembly) ?
I would wait until you have a better handle on C2 codes before trying an F6 code. They are quite a unique beast.
You do not need a new copy of Wii Play to make an F6 code work. You only need it to verify that your F6 code works for all versions/regions, but to get it working you only need one copy of the game.
You do not need to handle branching. When the code handler processes a C2 code, it automatically handles the branches for you. However, it is your C2 code's responsibility to ensure that the original instruction is executed (if necessary), since the code handler will overwrite the original instruction with a branch when it processes the C2 code.
Note that C2 codes run in the context of whatever function they hook.
In addition to ASMWiiRD, PyiiASMH can also assemble ASM codes. It has a little bit more flexibility, in that it can also do raw codes (which I used once, for a multi-C2 code), but it requires Python.
While a nop is 60000000, that's actually just a mnemonic for ori r0,r0,0. A bitwise OR with 0 always results in the original value, which is why this instruction is essentially a nop.
Quote from: Fighter19 on December 03, 2013, 08:54:57 PM
So I overwrite the function that writes to 8037A548 and let it write to a new memory location AND at the original, then use the new address in the other injected code (that overwrites writing to the position address)? Then I have to paste the original code in again, since we don't want to disable moving completely adding a cmp (probably another function in PPC) and check whether the button is "-" (put it simply, probably there it's needed to modify this and copy it to a free location as well) then the rest of the code gets executed (storing it in registers, multiplying, writing it back), Well this sounds a little dirty but should work fine, shouldn't it?
EDIT2: There is no EASY way to dynamically allocate free space, is there?
I would hook the first function and instead of writing values, I would cache the pointer instead. Then your other hook can read the cached pointer and write to the address if it wants to.
Yes, you would use a cmp to check whether - has been pressed. You would use a cmpwi (CoMPare Word to Immediate) if you wanted to check whether ONLY - is held, and then beq- to your hack or bne- over your hack. If you wanted to mask the - so that you could press it while holding other buttons, you would have to use andi. (AND with Immediate and the . means update the Condition Register flags) to mask off just the bit, and then bne- to your hack or beq- over your hack (note: this is the REVERSE of the cmp)
There is no "easy" way to dynamically allocate space. One way (that I personally never use) is to use the 80001500 section. But myself, I prefer to use the "bl trick". You may need to create a stack frame if you need to preserve LR (the Link Register) before doing the bl trick, but you can use it to create small data areas for your codes. The trick is that bl puts the address of the next instruction into LR, and then branches. Set the branch destination to be after your data section, and when you get there, LR holds a pointer to your data. mflr (Move From Link Register) to get the pointer. But make sure that LR can be clobbered safely (if your function has mflr at the start and mtlr at the end, you should be safe). Several of my codes use the bl trick, you can see them because I use .float or .long to initialize the data.
Quote from: Fighter19 on December 04, 2013, 07:13:34 PM
Man, I tried around replacing this function but somehow it freezes.
[spoiler]
stfs f3,4(r3) //Sets position
lis r27,0x8000 //HiByte
addi r27,r27,0x159C //LoByte for free memory
stfs f3,0(r27) //Store f3 (position) to address of r27
lis r22,0x8037 //HiByte
addi r22,r22,0x7FFF //
addi r22,r22,0x2641 //Add together to achieve 8037A640
lhz r23,0(r22) //Load value from 8037A640 (Pressed button) (address of r22) to r23
cmpi 3,0,r23,0x1000 //Is r23 = 0x1000 (So is the "-" Button pressed), using CR3
beq 3,SET //If value in CR3 return true, jump to SET
lis r27,0
lis r23,0
lis r22,0 //If not clean everything up
b END //Jump to a location after SET so it doesn't get executed
SET:
addi r27,r27,0x0020 //Increment by 0x0020
stw r23,0(r27) //Store the value from r23 (In this case it HAS to be 0x1000) to address of r27
lis r27,0
lis r23,0
lis r22,0 //clean everything up
END:
nop
(now using the disassembler you can see it now returns to the normal function)
[/spoiler]
In ASMWIIRD ; doesn't work as comment anyway...
Tip: Use the spoiler tag instead of the code tag. Bigger text and it can be collapsed so that it doesn't interfere with the flow of the thread.
Line comments use # instead of ; or //.
There are many things that need fixed with this code. You should really re-read the "On the Safety of Registers" link I provided earlier. I know the CPU architecture link might have been below you, given your knowledge of x86 assembly, but I truly encourage you to read the whole Safety of Registers thread in its entirety. You need to write your ASM codes while following the PowerPC Application Binary Interface. You seem to think that register safety depends on the contents of the register, in other words, a register with the value 0 is safe to use, but
this is a big no-no. Register safety is related to the dependency chain of the register in the flow of instructions. r12 will almost always have non-zero values in it, but that's okay, because the vast majority of instructions never depend on the contents of r12. For those instructions that do, r12 is loaded right before it is used, so it will be obvious when you should not clobber the value. In contrast, a register could contain 0, but during some other execution (for example, a different caller), that register might not contain 0, or perhaps the code depends on 0 being in that register. Remember,
0 does not mean safe.
Your code probably crashed because you clobbered the non-volatile registers r22, r23, and r27 without saving their value.
You should avoid addi when loading addresses. I think you noticed why. You had to split the creation of 8037A640 into three instructions. The reason is because addi's 16-bit Immediate value is signed! So when you went to add 0xA640 to 80370000, you were actually
subtracting 0x59C0, so you got 803
6A640. As Bully said, use ori instead of addi, because ori uses an unsigned Immediate.
You also should NOT use CR3 for your cmp! That could also cause your code to crash, because CR2-4 are non-volatile, and their contents must be backed up if you intend to use them. Don't specify a CR, just use the implicit CR0, it's volatile and you're allowed to clobber it. Note that float compares will use CR1 implicitly, but it's still volatile, so you're allowed to clobber it.
You don't need a nop at the end. The assemblers will add the nop if it is needed.
You use a lot of addi's when you could just use the displacement operand. Instead of
addi r27,r27,0x0020 //Increment by 0x0020
stw r23,0(r27) //Store the value from r23 (In this case it HAS to be 0x1000) to address of r27
Just do
stw r23,32(r27) # [r27 + 32] = r23
If you would like to see some of the source assembly that I've used to write my codes, here are several examples. Some are very complex, but well commented. However, I very much flaunt my expertise with PPC ASM, so some may be very confusing, especially the SMG2 Televitation code, which is so highly optimized I even have trouble following it.
http://wiird.l0nk.org/forum/index.php/topic,8138.0.html
http://wiird.l0nk.org/forum/index.php/topic,8497.msg70986.html#msg70986
http://wiird.l0nk.org/forum/index.php/topic,8455.msg71990.html#msg71990
http://wiird.l0nk.org/forum/index.php/topic,8757.msg73781.html#msg73781
http://wiird.l0nk.org/forum/index.php/topic,8742.msg72940.html#msg72940
http://wiird.l0nk.org/forum/index.php/topic,8591.msg71402.html#msg71402
http://wiird.l0nk.org/forum/index.php/topic,6517.0.html
http://wiird.l0nk.org/forum/index.php/topic,9557.msg80797.html#msg80797 (this one is Bully, but it covers the u-suffixed instructions nicely in the thread)
I've already finished doing the most. So here's a cleanup:
[spoiler]stfs f0,12(r3)
mflr 10
lis r12,0x8024
addi r12,r12,0x43FC
cmp 0,0,r10,r12
bne 0,END
lis r10,0x8000
addi r10,r10,0x159C
lis r12,0x8037
ori r12,r12,0xA640
lhz r11,0(r12)
cmpi 0,0,r11,0x1000
beq 0,SET
li r12,0x0000
stw r12,0x20(r10)
b END
SET:
lwz r12,0(r10)
stw r12,4(r3)
lwz r12,4(r10)
stw r12,0xC(r3)
sth r11,0x20(r10)
END:
[/spoiler]
or in Code form with everything else included:
[spoiler]
C22423A4 0000000B
D003000C 7D4802A6
3D808024 398C43FC
7C0A6000 40820040
3D408000 394A159C
3D808037 618CA640
A16C0000 2C0B1000
41820010 39800000
918A0020 48000018
818A0000 91830004
818A0004 9183000C
B16A0020 00000000
C20A4280 00000004
D05E0020 3F608000
3B7B15AC D05B0000
3B7B0004 D01B0000
3F600000 00000000
280015BC EFFF1000
82200001 800015AC
86A00001 43E6A000
84200001 8000159C
82200001 800015B0
86A00001 442FF000
84200001 800015A0
E0000000 80008000
[/spoiler]
Is this acceptable now, or is it still too dirty?
P.S.: Referring to register safety:I even thought about backing the registers up to restore them later, but since I'm new to assembler I forgot that there is a stack (whooops).
It's perfectly fine to mix-n-match Gecko codes and ASM codes. The only thing to keep in mind is that the C2 code executes when the game calls it, and the Gecko codes execute when the code handler runs.
I've rewritten your example a bit.
[spoiler].set TEST_LR,0x802443FC
.set STORAGE0,0x8000159C
.set STORAGE4,STORAGE0+4
.set STORAGE32,STORAGE0+32
.set BTN_ADDR,0x8037A640
.set BTN_MINUS,0x1000
stfs f0,12(r3)
mflr 10
lis r12,TEST_LR@h
ori r12,r12,TEST_LR@l
cmpw r10,r12
bne- _END
lis r10,STORAGE0@ha
lis r12,BTN_ADDR@ha
lhz r11,BTN_ADDR@l(r12)
cmpwi r11,BTN_MINUS
beq- _SET
lis r12,0x0000
stw r12,STORAGE32@l(r10)
b _END
_SET:
lwz r12,STORAGE0@l(r10)
stw r12,4(r3)
lwz r12,STORAGE4@l(r10)
stw r12,0xC(r3)
sth r11,STORAGE32@l(r10)
_END:[/spoiler]
Some questions. Why are you testing LR? Have you verified that there are multiple callers to this piece of code, and that the other callers interfere?
You're still using addi for addresses. You should kick that habit, it will bite you.
You're explicitly specifying registers for comps and branches. Avoid doing so unless you need to.
You should use macros for holding addresses. They're easier to read.
I tend to prefix my jump labels with an _ so that they stand out against the other macros. This isn't required, it's just a convention I use personally.
While not necessary, I tend to add the branch hints to conditional branches. I mimic the compiler; a - suffix for branches that go forward and a + suffix for branches that go backwards.
You cannot use li to clear a register, li is a mnemonic for addi, which does not clear the upper 16-bits. You should use lis to clear a register, because it will clear the bottom 16 bits.
You use both sth and stw to the same address. Why? You probably meant one or the other.
EDIT:
I haven't looked at the function, but the chances are r10 is also safe. r11 and r12 are almost always safe and if they aren't you should be able to tell quickly from reading the ASM. r10 down to r3 get gradually less safe. The only way to know for sure is to analyze the hook, but you never pasted the output of Gecko.NET's disassembly tab's Copy Function on your hook address, so I can't tell you for sure.
To LR, yeah I added the check of LR to make sure that it's getting called from only that function. I think the other caller sets the spawn position, I'm not sure though, maybe also just some other player, but it has the same value as if you start the level from beginning. And I have to say your version's really way more clean, good to see how it's solved accurately. Because I never saw really "handwritten" ASM code I only saw how it looks like in it's disassembled form and I have to say, I would probably have finished way faster if I knew this stuff. >.< But now that I have a comparison on how it should look like and how not, it will be probably easier next time.
Thank you guys.
EDIT:Hmm? I only see sth gets executed where the button is begin stored to it's address, and this only once, so I don't understand what you're referring to.
EDIT2: And also on
lis r12,BTN_ADDR@ha //
lhz r11,BTN_ADDR@l(r12)
Where is low being loaded? It only stores the High address to r12 doesn't it, how can you load the value from the whole address then?
I'm not sure you need the LR check. Does it fail without it? Generally, LR isn't terribly reliable for that purpose. For example, other players would probably use the same call chain. It's just a bit odd, not necessarily wrong.
The mixture of sth and stw can be seen in your original, because you 0 it out if you don't execute your hack.
stw r12,0x20(r10)
[...]
sth r11,0x20(r10)
Be careful with @ha and @h. @ha is used if you will be adding, @h should be used if you will be oring. The lower half of the address is provided by the displacement operand, in this case BTN_ADDR@l, which takes the low half of the address as a signed 16-bit integer. Since the displacement operand is an addition, I use @ha to load the upper half of the address. @ha accounts for the fact that 0x8000 <= x <= 0xFFFF will be interpreted as negative, and adjusts the upper half as necessary (by adding 1 to the upper half). The a probably means "arithmetic", aka signed. @h would be the "logical" version, aka unsigned.
lwz rD,d(rS) # rD = [rS + d]
rS = Source Register, d = Displacement Operand, rD = Destination Register.
EDIT: I made a small adjustment to my code above. I had the assembler calculate the addresses for me instead. Slightly safer - computers are much better at math than I am lol
Ah now I see, derp. Thank you, man.
Uhm, you use the .set instruction to assign text to values, then use them below in your actual assembly. It seems it's just for readability which helps a lot not getting confused quickly. Any documentation on all @ arguments though?
Yep, it's just for readability- like labels for branching.
The assembler sees them like so:
[var]@h = higher (first) 16 bits of the variable
[var]@ha = higher adjusted (first) 16 bits of the variable + 1 (for subsequent adding, as dcx2 mentioned)
[var]@l = lower (last) 16 bits of the variable
Quote from: dcx2 on December 06, 2013, 03:58:01 PM
You cannot use li to clear a register, li is a mnemonic for addi, which does not clear the upper 16-bits. You should use lis to clear a register, because it will clear the bottom 16 bits.
Sorry to prove you wrong, sir. :)
Li does clear the upper 16 bits just like lis the lower. It's not a mnemonic for addi.
addi r12, r12, 0x2876
lis r12, 0x1234
ori r12, r12, 0x5678
li r12, 0
Debugger step into:
1) r12 = 0x80000000
2) r12 = 0x80002876
3) r12 = 0x12340000
4) r12 = 0x12345678
5) r12 = 0x00000000
li == addi
lis == addis
the rA is a literal 0 and not register r0, so it does set the whole value
note: SIMM is signed though, so you can't load any value using this method. it must be either 0xFFFFFXXX or 0x00000XXX with li as it'll subtract if SIMM >= 0x8000
Ah yes, it slipped my mind that r0 would be treated as a whole 32-bit 0. It is still true that addi does not clear the upper 16 bits ;) But it appears li rD,0 is safe after all - although megazig's explanation shows why we lis/ori instead of li/oris.
And li is very much the mnemonic for addi. Go ahead, type in the assembly command "addi r3,r0,3" into ASMWiiRd, convert to your Gecko code, then convert back to ASM. It will disassemble to li r3,3.
My source of the fixed teleporter:
[spoiler]Hook: 80246A88
.set STORAGE, 0x80001650
.set BUTTON_MINUS, 0x1000
.set BUTTON_ONE, 0x200
.set BUTTON_ADDRESS, 0x8037A640
lis r12, STORAGE@h
lis r10, BUTTON_ADDRESS@h
ori r10, r10, BUTTON_ADDRESS@l
lhz r10, 0 (r10)
andi. r9, r10, BUTTON_MINUS
beq- _NOSTORE
lwz r11, 0 (r5)
stw r11, STORAGE@l (r12)
lwz r11, 8 (r5)
stw r11, STORAGE + 4@l (r12)
_NOSTORE:
andi. r9, r10, BUTTON_ONE
beq- _NOLOAD
lwz r11, STORAGE@l (r12)
stw r11, 0 (r5)
lwz r11, STORAGE + 4@l (r12)
stw r11, 8 (r5)
_NOLOAD:
lfs f0,8(r5)[/spoiler]
Note that there are x y z coordinates but only x and z are actually used.
The height is always 0.
http://geckocodes.org/?c=RHAP01
Quote from: Bully@Wiiplaza on December 13, 2013, 05:29:39 PM
My source of the fixed teleporter:
[spoiler]Hook: 80246A88
.set STORAGE, 0x80001650
.set BUTTON_MINUS, 0x1000
.set BUTTON_ONE, 0x200
.set BUTTON_ADDRESS, 0x8037A640
lis r12, STORAGE@ha
lis r10, BUTTON_ADDRESS@ha
lhz r10, BUTTON_ADDRESS@l (r10)
andi. r9, r10, BUTTON_MINUS
beq- _NOSTORE
lwz r11, 0 (r5)
stw r11, STORAGE@l (r12)
lwz r11, 8 (r5)
stw r11, STORAGE + 4@l (r12)
_NOSTORE:
andi. r9, r10, BUTTON_ONE
beq- _NOLOAD
lwz r11, STORAGE@l (r12)
stw r11, 0 (r5)
lwz r11, STORAGE + 4@l (r12)
stw r11, 8 (r5)
_NOLOAD:
lfs f0,8(r5)[/spoiler]
Note that there are x y z coordinates but only x and z are actually used.
The height is always 0.
http://geckocodes.org/?c=RHAP01
now with less code and more safety. lwz/stw use SIMM so offset needs @ha for initial lis
I'm also not too keen on using the 0x80000000-0x80001800 area for storage as it's not defined for such things. setting up variables in your .s would be better