lha RAM Write Template

Started by Bully@Wiiplaza, October 08, 2012, 09:58:49 PM

Previous topic - Next topic

Bully@Wiiplaza

lha means "load half algebraic"
http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.aix.aixassem/doc/alangref/alangref.pdf

I was wondering if it works to "replace" an lha instruction with *just* li and sth. Is it fool-proof or may there occur negative effects after doing so? I want to be sure. Weird thing: There´s no stha!
My Wii hacking site...
http://bullywiihacks.com/

My youtube account with a lot of hacking videos...
http://www.youtube.com/user/BullyWiiPlaza

~Bully

dcx2

#1
lha is a counterpart to lhz.

Basically, when computing with different size integers, we have a problem.

Consider the 16-bit value of -1, that is, 0xFFFF.  Let's say that you want to load that into a 32-bit register.  Well, if you just put 0xFFFF into the reg, you will have 65535 instead of -1 (32-bit -1 would be 0xFFFFFFFF).

So we use a concept called "Sign Extension".  You may have seen instructions before, extsh and extsb.  These are EXTend Sign Half-word and EXTend Sign Byte.  These instructions "extend" the sign bit so that it fills up the remainder of the integer.  In this case, if you extsh on 0x0000FFFF (or extsb 0x000000FF), you will get 0xFFFFFFFF, because the sign bit is extended.  Using sign extension, we can make 8-bit and 16-bit signed integers into their 32-bit counterparts, allowing us to perform 32-bit arithmetic on 8- or 16-bit signed numbers.

You also see this problem with bit shifts.  Let's say you have a 32-bit -2 (0xFFFFFFFE).  You want to right-shift by 1-bit, which should "halve" the value, meaning you'll get -1.  However, if you do a plain old right-shift, you'll get 0x7FFFFFFF, which is actually 2147483647 (2^31 - 1, the maximum signed integer).  That's why we have an "arithmetic" (or "algebraic") shift right, which preserves the sign bit during a shift.  An arithmetic shift right will turn 0xFFFFFFFE into 0xFFFFFFFF.

It's important to note that if you are NOT working with signed integers (i.e. you declared your integer as an unsigned byte instead of a signed byte), then you DO NOT want sign extension.  An unsigned 0xFF is actually 255, and if you sign extend that you will get 4294967295 (2^32 - 1, the max unsigned integer).

So in summary, for signed integers, you want to do sign extension.  For unsigned integers, you do NOT want sign extension.  This all hinges 100% on what the source code told the compiler to do.

With all this in mind, lha is basically lhz and extsh all wrapped up in one package.  lhz is for unsigned, lha is for signed.

It's also kinda important to know that li already handles sign extension.  http://pds.twi.tudelft.nl/vakken/in101/labcourse/instruction-set/addi.html  (li is actually a mnemonic for addi rX,r0,imm)  Do you see the "EXTS(SIMM)"?  EXTS means EXTend Sign, and SIMM means Signed IMMediate.  That means the immediate part of the instruction is considered signed, and that this instruction extends the sign of the immediate value before it is used.  So li r0, -1 will actually load 0xFFFFFFFF into r0 (which is why we always lis/ori, because lis and ori do not use sign extension; notice the UIMM [Unsigned IMMediate] in the description of ori http://pds.twi.tudelft.nl/vakken/in101/labcourse/instruction-set/ori.html )

The reason there is no stha is because it doesn't make sense in this context.  With sign extension, we are "filling up" the missing bits when the value is loaded so that we have the correct 32-bit value.  However, when we are storing a value, there are no "missing bits"; instead, we have extra bits that we want to get rid of, which sth does by virtue of only writing the lower 16-bits to memory.  The "replicated" sign bits are not written to memory.

Consider this sequence of instructions

lhz r0, foo  # r0 = [foo], [foo] = 0xFFFF, r0 = 0x0000FFFF
extsh r0, r0 # r0 = 0xFFFFFFFF
sth r0,bar  # [bar] = 0xFFFF (NOT 0x0000FFFF, NOT 0xFFFFFFFF)

In this example, it doesn't matter that the upper 16-bits of r0 are all 1s, because those bits are not written to memory.  In terms of a 16-bit integer (signed OR unsigned), 0x0000FFFF, 0xFFFFFFFF, and 0xFFFF are all the same.

Bully@Wiiplaza

#2
I think I got it right:

Assume the value is 00000012.

lha rY, ? (rX) # rY is now 00120012

If I just do li and sth I would miss the EXTend Sign Half-word. It would cause rY to hold 00000012 instead.

What I can do is this:

li rY, 0xTTTT
sth rY, ? (rX)
extsh rY, rY

I remember how I´ve seen some assembly like this, or just for using an extsh/extsb instruction. Great :smileyface:
My Wii hacking site...
http://bullywiihacks.com/

My youtube account with a lot of hacking videos...
http://www.youtube.com/user/BullyWiiPlaza

~Bully

dcx2

#3
Quote from: Bully@Wiiplaza on October 09, 2012, 03:17:33 PM
Assume the value is 00000012.

lha rY, ? (rX) # rY is now 00120012

No, you got it all wrong.

The sign bit is copied.  The sign bit is the most significant bit of that size integer.  You're just wholesale copying and pasting the lower halfword into the upper halfword.

Let's take a 16-bit signed integer in binary (by signed, I mean it can be positive or negative, as opposed to unsigned, which can only be positive).  (binary is denoted 0b instead of 0x)

   v--- sign bit
0b1111 1111 0000 0000  # 0xFF00 (1 = negative; sign extension copies 1's into upper halfword)
0b0000 0000 1111 1111  # 0x00FF (0 = positive; sign extension copies 0's into upper halfword)
0b0000 0000 0001 0010  # 0x0012 (0 = positive; sign extension copies 0's into upper halfword)

Sign extension duplicates that bit only.  So a sign extension of 0x0012 is 0x00000012.  A sign extension of 0x8012 would be 0xFFFF8012.

The purpose of sign extension is to PRESERVE the current value when changing the size of the integer.  This way the register contains the same logical value as both the shorter and longer integer.  This is because for two's complement, 16-bit -2 is 0xFFFE and 32-bit -2 is 0xFFFFFFFE.  Importantly, we do NOT use sign extension on unsigned integers, because doing so would change the value.  Sign extension always results in the same value before and after the extension.

There is no reason to sign extend after doing the store.  It's pointless (EDIT: especially because li already performs sign extension, as per my previous post, remember that li is actually addi).  Sign extension "fills up" missing bits to increase the size of an integer from e.g. 16-bits to 32-bits.  However, when storing a 16-bit value, the upper halfword of the register is ignored.  You can consider this "sign shortening", since it discards the replicated sign bits, but the term "sign shortening" is something I just made up right now.

Sign extension is only used when loading signed integers smaller than 32-bits into a 32-bit register.  It is not used when storing, period, ever.

Bully@Wiiplaza

#4
Yes, it´s obvious now. :-X

Great explanations. Nothing to add,
thank you!

I´ll go with li and sth since li already does what I need (sign extension) therefore I can omit the lha.

Writing FFFFFFFE can be "shortcut" with...

li rY, -2

...instead of...

lis rY, 0xFFFF
ori rY, rY, 0xFFFE
My Wii hacking site...
http://bullywiihacks.com/

My youtube account with a lot of hacking videos...
http://www.youtube.com/user/BullyWiiPlaza

~Bully

dcx2

#5
Quote from: Bully@Wiiplaza on October 09, 2012, 04:24:05 PM
Writing FFFFFFFE can be "shortcut" with...

li rY, -2

...instead of...

lis rY, 0xFFFF
ori rY, rY, 0xFFFE

Yes.

The flip side is that you cannot use li to load a register with an unsigned 16-bit integer greater than 32767. e.g. you want to put 0x8000 into rY

li rY, 0x8000  # rY now contains 0xFFFF8000

If you want to load 0x8000 directly, you must use ori, which means the register must be 0 ahead of time.  This is why we use lis/ori combination.  lis will set the upper 16 bits and zero the lower 16 bits, and then ori will load the lower 16 bits without sign extension.

lis rY, 0
ori rY, rY, 0x8000  # rY now contains 0x00008000