Here's the disassembly and notes for the code here http://wiird.l0nk.org/forum/index.php/topic,5791.msg55338.html#msg55338
There are a lot of ASM techniques I used here...it's pretty epic in scope. It's my hope that others can learn from these techniques so they may write better ASM hacks. Feel free to ask questions about it. It can be quite difficult to digest, though, so if there's enough interest I can show how the ASM code evolved from the first successful teleportation until this final code.
EDIT: Since posting this, I've managed to optimize two more instructions out, making it one line shorter.
Hook 803880AC = lwz r0,52(r1)
r3 has coordinates to read/write
r0,r30,r31 safe
controller at 80750A00
button offset = 2
Y axis offset = 100
xyz orientation offset = 12/16/20
b+z+[other] to store, b+[other] to restore
32.0 = 42000000
The following can be put straight into PyiiASMH. Use the hook address above.
# -------------- VARIABLES --------------
# to optimize an ori, we'll add the low hword of the controller address to all the offsets
.set CONTROLLER_ADDRESS_LOW,0xA00
.set BUTTON_OFFSET,2+CONTROLLER_ADDRESS_LOW
.set XYZ_ORIENT_OFFSET,12+CONTROLLER_ADDRESS_LOW
.set YAXIS_OFFSET,100+CONTROLLER_ADDRESS_LOW
.set FLOAT_OFFSET,FLOAT_POINTER_OFFSET-DATA_POINTER_BASE
.set B_MASK,0x400
.set Z_MASK,0x2000
# -------------- CODE --------------
lis r12,0x8075 # load the controller pointer 8075 into r12
# bl here so that the data area is 64-bit aligned close to the front of the C2 code
# bl will simultaneously skip over the data area
# *and* put a pointer to the data in LR
bl SKIP_DATA
DATA_POINTER_BASE:
# the four coordinate slots are stored here
# they are accessed by indexing off the data pointer
# note that each line is 96 bits; 32 bits each for X, Y, and Z
.long 0,0,0
.long 0,0,0
.long 0,0,0
.long 0,0,0
FLOAT_POINTER_OFFSET:
.long 0x42000000 # the max levitation speed, 32.0 in decimal
SKIP_DATA:
lhz r30,BUTTON_OFFSET(r12) # load button values into r30
andi. r0,r30,B_MASK # mask off the bit for the B button
beq- THE_END # eq means B is not being held down, so exit the code
rlwinm. r0,r30,28,0,3 # mask off the index buttons, rotate the interesting bits to the front
beq- THE_END # eq means no index buttons held, so exit the code
cntlzw r0,r0 # convert it to an index
mulli r0,r0,12 # multiply by 12 bytes per index to get an offset
mflr r31 # grab data pointer from LR
# r30 still has controller values, but those aren't needed after this test
# r31 has data pointer
# r0 has offset
andi. r30,r30,Z_MASK
add r30,r31,r0 # r30 now has pointer to indexed data coords
beq- RESTORE # eq means z not held, so load data coords
STORE:
# r3 is source
# r30 already has destination (data coords pointer)
# r12 is now free
mr r12,r3
b MEMCPY
RESTORE:
LEVITATE:
# r31 has pointer to data
# r12 still has controller pointer
# r30 has pointer to data coords
lfs f0,FLOAT_OFFSET(r31)
lfs f1,YAXIS_OFFSET(r12)
# f0 has max speed float stored in data area
# f1 has the value of the Y axis from p1's nunchuck
fmuls f0,f0,f1 # scale = max speed * Y axis
# set up a loop
# note that lfsu requires pointing 4 bytes _before_ the first value
li r0,3
mtctr r0
addi r31,r12,XYZ_ORIENT_OFFSET-4
subi r12,r30,4 # work on a cached copy of r30
# f0 has the scale value
# r31 has p1 wiimote orientation pointer
# r12 has a pointer to code's stored coordinates
TOP_OF_LOOP:
lfsu f1,4(r31)
lfsu f2,4(r12)
fnmsubs f1,f0,f1,f2 # f1 = coords â€" (scale * orientation)
stfs f1,0(r12)
bdnz+ TOP_OF_LOOP
# r30 is source, r3 is dest
mr r12,r30
mr r30,r3
MEMCPY:
# r12 is source, r30 is dest
psq_l f0,0(r12),0,0
psq_l f1,8(r12),1,0
psq_st f0,0(r30),0,0
psq_st f1,8(r30),1,0
THE_VERY_END:
THE_END:
lwz r0,52(r1)
wow that's crazy but very nice ASM instructionen :eek:
you included again a button activator with button address in your asm code.
how to do this? you said, blackwolf's tut fails because he forgot the alignment.
full code:
lis r12,-32651
ori r12,r12,2560
lhz r30,2(r12)
andi. r0,r30,1024
beq- 0x78
rlwinm. r0,r30,19,9,12
beq- 0x70
cntlzw r0,r0
mulli r0,r0,12
bl 0x04
mflr r31
andi. r30,r30,8192
add r30,r31,r0
beq- 0x0C
mr r12,r3
b 0x3C
lfs f0,104(r31)
lfs f1,100(r12)
fmuls f0,f0,f1
li r0,3
mtctr r0
addi r31,r12,8
subi r12,r30,4
lfsu f1,4(r31)
lfsu f2,4(r12)
fnmsubs f1,f0,f1,f2
stfs f1,0(r12)
bdnz+ 0xFFFFFFF0
mr r12,r30
mr r30,r3
psq_l f0,0(r12),0,0
psq_l f1,8(r12),1,0
psq_st f0,0(r30),0,0
psq_st f1,8(r30),1,0
lwz r0,52(r1)
b 0x38
bdnz- 0x00
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
Why do you use the 00000000 lines?
Aren´t they useless, that´s what I am wondering :eek:
Quote from: Deathwolf on July 25, 2010, 11:28:57 AM
you included again a button activator with button address in your asm code.
how to do this? you said, blackwolf's tut fails because he forgot the alignment.
If you look at the top, the line that says .set BUTTON_OFFSET,2 creates a variable and further down is another line that says lhz r30,BUTTON_OFFSET(r12). This is how I preserved alignment. I did this because I needed the controller pointer in order to get the Y axis and Wiimore orientations. You can see their offsets declared at the top, too, as YAXIS_OFFSET and XYZ_ORIENT_OFFSET.
---
Quote from: Bully@Wiiplaza on July 25, 2010, 12:26:56 PM
Why do you use the 00000000 lines?
Aren´t they useless, that´s what I am wondering :eek:
That is where the coordinates are stored. I call it a "Small Data Area" because the data's embedded in the C2 code. Each .long 0,0,0 is a set of XYZ coordinates, and there are four sets, one for each slot. I get a pointer to the SDA by using a technique I saw in brkirch's moon jump. blr 4/mflr.
yea but there is no example how to use beq- 0xXX, andi. rX,rXX,XXXX or something other instructionen...
then the alignment is confusing me. Sometimes I'm not sure, if my C2 codes are wrong!?
Yes for you it's maybe very easy and you can make alot of C2 codes with more than 20 instructionen,but for some other it's too hard without any examples...
I know it's maybe also hard for you to explain it right and you try to help everyone but some hackers haven't a chance and can't learn someting new without any examples.
BUT I hope you know that I'm very grateful to you....
I posted this so people can ask questions about how it works. There is a lot of reasoning behind every line, and sometimes there's lots of reasons behind a single line. Think of it as a "living tutorial"; rather than me telling you how it works, you should find a part that you want to learn about and ask why/what it does.
I can also start at the beginning, with the unoptimized single teleporter. That way you can see how the code "grows", like a flower, into something more complex.
---
Do you understand beq- at all, or why beq- is used with andi.?
andi. = AND with Immediate (http://pds.twi.tudelft.nl/vakken/in1200/labcourse/instruction-set/andi-dot.html). The AND operation is used to "mask" off the interesting bit.
At the top, I declared Z_MASK = 0x2000 and B_MASK = 0x400. These masks go over-top of the buttons and hide everything else. This allows me to see whether or not the Z button is pressed while ignoring the other buttons.
The AND operation tells me the result by setting or clearing bits in the Condition Register (CR). You can see the CR on the Breakpoint tab. The first bit (from the left) is equals, the second is greater than, and the third is less than.
If the result of an AND is equal to 0, then the equals bit of the CR is set to 1. If the result is not equal to 0, then the equals bit is cleared to 0.
When beq- is encountered, the processor will check the equals bit of the CR. If it is set, it will branch to the destination. If the equals bit of the CR is clear, then the code does NOT branch, but instead goes on to the next instruction. (Notice that I use branch labels instead of numbers; PyiiASMH will calculate offsets for you using the labels)
If you follow this post, you can see an example where I color-coded the instructions that get executed with a beq-. http://wiird.l0nk.org/forum/index.php/topic,5836.msg50772.html#msg50772
u mean andi is a if code?
andi. r0,r30,0x2000 <-- write value 2000 to the button address?
beq- 0x78 <-- check it if the button Z pressed and branch it?if not,code is disabled?
(EDIT: Yes, andi. and beq- are one way to do an if code)
andi. does a bit mask. Like holding up a piece of paper, with holes where there are 1's in the bit mask.
The mask will hide the blue squares that we aren't interested in, and only show us what is behind the red squares.
Let's say you're holding b + up arrow. This is 0x408. The B_MASK is 0x400. In binary, it would look like this
0000 0100 0000 1000 # button values (in r30)
0000 0100 0000 0000 # B_MASK (the Immediate)
__________________ # AND
0000 0100 0000 0000 # result (written to r0)
Compare to Z_MASK = 0x2000
0000 0100 0000 1000 # button values
0010 0000 0000 0000 # Z_MASK
__________________ # AND
0000 0000 0000 0000 # result
---
In the first case, the result written to r0 is not equal to 0. So the equals bit of the CR will be cleared to 0.
In the second case, the result written to r0 is equal to 0. So the equals bit of the CR will be set to 1.
makes no sense for me!?
maybe I don't understand it or wrong....
I'm searching for a universal instruction for including button activator...
There is no such thing as universal. Banish the thought from your mind. Even with regular WiiRD codes, but especially with ASM
There are two general types of button activators.
A) Is this the only button being pressed? For example, 28750A02 00000408 = "is B and up but no other buttons"
B) Is this any of the buttons being pressed? For example, 28750A02 FBF70408 = "is B and up, and whatever else"
---
In ASM, A will need cmpwi. Only exact matches (b and up but nothing else) B will use andi. because you don't care about other buttons.
Then you need to decide if you're doing bne- or beq-.
This code uses the second type, B, of activator. Let's take a closer look at the first section.
The intent of the code is to cause a teleportation when you do b button + any arrow, or to store coordinates if you do b + z + arrow. So if there is no b, then there should be no hook.
# load r12 with a pointer to the controller values, 80750A00
lis r12,0x8075
ori r12,r12,0x0A00
# Add the BUTTON_OFFSET = 2 to the pointer 80750A00 in r12 and put the value at that address into r30
# r30 = [r12+BUTTON_OFFSET]
lhz r30,BUTTON_OFFSET(r12)
# Apply the B_MASK to r30, and put the result into r0
andi. r0,r30,B_MASK
# if the result of the mask is 0, the equals bit will be set
# this means b is not pressed
# but we only want to hook when b is pressed...
# ...so we branch to the end if the mask = 0
beq- THE_END
---
Later, there's another button activator for Z. If we have B pressed, then we want to do something different depending on whether z is pressed or not.
# r30 still has the value of the buttons
andi. r30,r30,Z_MASK
...
# if the result was equal to 0, then z is not pressed, so load coordinates
beq- RESTORE
# otherwise, store coordinates
...etc...
you said anything about cmpwi.
for what?
so this is it?
lis r12,-32651
ori r12,r12,2560
lhz r30,2(r12)
andi. r0,r30,1024
beq- 0x78
lis r12,-32651->3D808075 618C0A00 <- ori r12,r12,2560
lhz r30,2(r12) ->A3CC0002 73C00400 <-andi. r0,r30,1024
beq- 0x78 ->41820078
lis r12,0x8075
ori r12,r12,0x0A00
lhz r30,2(r12)
andi. r0,r30,0x0400 <-- B MASK
beq- 0x78 <-- END
cmpwi is for different button activators. Like 28 codes with the MMMM = 0000 (http://geckocodes.org/index.php?arsenal=1&ct=28). andi. is used for codes where MMMM != 0.
Yes, PyiiASMH automatically calculated that the branch distance from the beq- to THE_END was 0x78. I didn't have to count that.
andi. was used to mask the buttons to see if B was pressed.
that's a really nice instrucion!!
so you mean PyiiASMH calculate for me the branch distance from the beq- to end?
where can I get this nice tool? at the moment I only use ASM to wiird.
thanks alot dcx2.
btw is the code right?
lis r12,0x8075
ori r12,r12,0x0A00
lhz r30,2(r12)
andi. r0,r30,0x0400 <-- B MASK
beq- 0x78 <-- END
http://wiird.l0nk.org/forum/index.php/topic,4845.msg55412.html#msg55412
ähm yea thanks.
which link is the right and which should I download?
This is the first teleporter that I wrote. It has only one slot, but it's a much less complex code, so it might be easier to understand. It uses cmpwi, so you must press C and up but no other buttons. If you press C and up and left, it won't work.
Hook 803880A8 = lwz r0,52(r1)
r3 has coordinates to read/write
r0,r30,r31 safe
buttons at 80750A02
c+up to store, c+down to restore
Everything below this line can be converted using PyiiASMH
--
# put the address of DATA_POINTER_BASE into the LR
bl 0x04
DATA_POINTER_BASE:
# move the LR into r31, so that r31 can be a data pointer
mflr r31
# add the offset from DATA_POINTER_BASE to DATA_POINTER_OFFSET to r31
addi r31,r31,DATA_POINTER_OFFSET-DATA_POINTER_BASE
# load r30 with the pointer to the button values
lis r30,0x8075
ori r30,r30,0x0A02
# load r30 with the button values
lhz r30,0(r30)
# compare the button values to 0x4001 = C and up and nothing else
cmpwi r30,0x4001
# if the button values are not equal to C and up, branch to the next compare
bne- TEST_RESTORE
# if we get here, r30 = 0x4001
# store the current coordinates (from r3 pointer)
# into the data area (to r31 pointer)
lwz r0,0(r3) # store
stw r0,0(r31)
lwz r0,4(r3)
stw r0,4(r31)
lwz r0,8(r3)
stw r0,8(r31)
# we're done now
b THE_END
# if we get here, then the C and up test failed
# so now we'll test C and down = 0x4008
TEST_RESTORE:
cmpwi r30,0x4008
# if it is not C and down either, then we're done
bne- THE_END
# otherwise, C and down are pressed,
# so let's restore the coordinates from the data area (from r31 pointer)
# to Mario's coordinates (to r3 pointer)
lwz r0,0(r31) # restore
stw r0,0(r3)
lwz r0,4(r31)
stw r0,4(r3)
lwz r0,8(r31)
stw r0,8(r3)
# skip over the data area
b THE_END
# this is the data area, where the coordinates are stored when you press C and up
DATA_POINTER_OFFSET:
.long 0,0,0
# this is the instruction that is being replaced by my hook
THE_END:
lwz r0,52(r1)
aha okay.
so that's your button activator:
lis r12,-32651
ori r12,r12,2560
lhz r30,2(r12)
andi. r0,r30,1024
beq- 0x78
and this your code:
rlwinm. r0,r30,19,9,12
beq- 0x70
cntlzw r0,r0
mulli r0,r0,12
bl 0x04
mflr r31
andi. r30,r30,8192
add r30,r31,r0
beq- 0x0C
mr r12,r3
b 0x3C
lfs f0,104(r31)
lfs f1,100(r12)
fmuls f0,f0,f1
li r0,3
mtctr r0
addi r31,r12,8
subi r12,r30,4
lfsu f1,4(r31)
lfsu f2,4(r12)
fnmsubs f1,f0,f1,f2
stfs f1,0(r12)
bdnz+ 0xFFFFFFF0
mr r12,r30
mr r30,r3
psq_l f0,0(r12),0,0
psq_l f1,8(r12),1,0
psq_st f0,0(r30),0,0
psq_st f1,8(r30),1,0
lwz r0,52(r1)
b 0x38
bdnz- 0x00
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
.word 0x00000000
That is one of the three button activators in the multi-teleporter. But...why do you keep converting the code to get rid of all the notes and stuff? You're making it more difficult. I posted the disassembly with comments and variables for a reason.
I'm trying to show others that there is a way to write ASM codes that makes them easier to read and modify. You can use variables like BUTTON_OFFSET and B_MASK instead of hard numbers. You can add comments so you know what registers have what values. You can use branch labels like THE_END instead of calculating branch offsets.
What's easier to read and understand?
# r12 is controller pointer
# r30 is button values
lis r12,0x8075
ori r12,r12,0x0A00
lhz r30,BUTTON_OFFSET(r12)
andi. r0,r30,B_MASK
beq- THE_END # no b means do nothing
or
lis r12,-32651
ori r12,r12,2560
lhz r30,2(r12)
andi. r0,r30,1024
beq- 0x78
---
Besides. If you're having trouble with button activators, the multi-teleporter is not what you want to learn from. Look at the single teleporter that I posted. It's a lot simpler.
When you do the branch and link, and it stores the return in the LR, is that the address of the very first coordinate data?
If not, how far away from that would it be? I try reading your stuff but, you're using count leading zeros and multiplying by 12 (I guess cause theres 12 bytes per line, and so checking which line to get the data from?) to get how much to add.
"return" is an awkward word to use, especially because this bl has no matching blr. bl will put PC + 4 into LR. It just so happens that my data is aligned with this PC + 4. I also use bl to branch over the data and prevent it from being executed as if it were code.
If you put the data anywhere besides immediately after bl, you can use labels to automatically generate offsets to the data. However, you'll need to branch over the data somehow.
Also, in my case, I want the data to be 12-byte aligned with the data pointer from the LR. This way, my index gets multiplied by 12 and I'm done; otherwise I have to add an offset to account for the alignment.
Another example would be reading the max speed.
lfs f0,FLOAT_OFFSET(r31)
After putting the data pointer into r31, I load the float from the data area into f0. FLOAT_OFFSET is a number that was calculated by the assembler based on labels; you can see that it's .set at the top. It's probably 48 or something like that, but that's the beauty of labels and variables...I don't know/care what it is.
I keep trying to access data in the code, but it doesn't seem to be working, or at least how i want it to...Can you tell me why this won't work?
(this is just a test)
.set Offset, Saved_Data - Next_Instruction
mflr r0
bl Next_Instruction
Next_Instruction:
mflr r5
mtlr r0
lwz r14, Offset(r5)
cmpwi r14, 0x3
bne Exit
#code should run if its reading correctly...
#next part of code here#
Exit:
blr
Saved_Data:
.long 0x3
You're using blr...is this a C0 code?
I wouldn't trust r14. Try r12 instead.
I also generally prefer putting the data after the bl. The bl then skips over the data. In the case of a C0 code, your blr is skipping over the data. However, for a C2 code, you will need an extra branch to jump over the data.
EDIT: putting the data after the bl also has the convenient side effect of making the LR point directly at the first piece of data, meaning at least one of the offsets will be 0.