PowerPC Assembly Load Immediate
assembly, powerpc
Solution
`li` is a pseudo-opcode which actually translates as an `addi` instruction.
`addi rD, rA, SIMM` adds the signed immediate `SIMM` to `rA`, placing the result in `rD`, except when the `rA` field is 0, in which case it specifies a literal 0 instead of `r0`. `li rD, SIMM` is really `addi rD, 0, SIMM`.
This is good for loading values from `0`-`0x7fff` and `0xffff8000`-`0xffffffff` into a register, but not other values.
The immediate bitwise operation opcodes (`ori` etc.) do interpret the 16-bit immediate field as an unsigned value. However they do not have the "`r0` means a literal `0`" behaviour of `addi`.
You will need to use two instructions to load a constant of `0xfc10` into a register: load the register with 0 using `li` (or `lis`, as if you were loading the top half of an arbitrary 32-bit constant) and then OR in the unsigned 16-bit value with `ori`.
This is exactly what `gcc` does under the circumstances:
$ cat test.c
unsigned int test(void)
{
return 0xfc10;
}
$ gcc -O2 -c test.c
$ objdump -d test.o
test.o: file format elf32-powerpc
Disassembly of section .text:
00000000 <test>:
0: 38 60 00 00 li r3,0
4: 60 63 fc 10 ori r3,r3,64528
8: 4e 80 00 20 blr
c: 60 00 00 00 nop
$
I don't think the GNU assembler has any way of automatically generating two instructions to load such a value from a single source instruction when assembling for PowerPC. However you can use the `@h` and `@l` suffixes to extract the (unsigned) high and low 16-bit halves of a constant, e.g.:
lis r3, 0x12345678@h /* => li r3, 0x1234 */
ori r3, r3, 0x12345678@l /* => ori r3, r3, 0x5678 */
You could use this to write your own macro for arbitrary constant loading...
$ cat test2.s
.macro load_const rD, const
.if (\const >= -0x8000) && (\const <= 0x7fff)
li \rD, \const
.else
lis \rD, \const@h
ori \rD, \rD, \const@l
.endif
.endm
load_const r3, 0
load_const r4, 1
load_const r5, -1
load_const r6, 0x7fff
load_const r7, 0x8000
load_const r8, -32767
load_const r9, -32768
load_const r10, -32769
load_const r11, 0xfc10
$ as -mregnames -o test2.o test2.s
$ objdump -d test2.o
test2.o: file format elf32-powerpc
Disassembly of section .text:
00000000 <.text>:
0: 38 60 00 00 li r3,0
4: 38 80 00 01 li r4,1
8: 38 a0 ff ff li r5,-1
c: 38 c0 7f ff li r6,32767
10: 3c e0 00 00 lis r7,0
14: 60 e7 80 00 ori r7,r7,32768
18: 39 00 80 01 li r8,-32767
1c: 39 20 80 00 li r9,-32768
20: 3d 40 ff ff lis r10,-1
24: 61 4a 7f ff ori r10,r10,32767
28: 3d 60 00 00 lis r11,0
2c: 61 6b fc 10 ori r11,r11,64528
$
Problem
I am new to PowerPC assembly. I am working with an MPC8245 (yes, old school). This is a 603e family processor. I would like to know how to create a load immediate instruction where the immediate 16-bit value is 'unsigned'. Example: li r3,0xFC10 The gcc cross-compiler, 4.4.5, I am using does not allow this instruction as the value is not a signed constant. Yes, I could use minus the 2-s complement, but this makes reading and documenting the code more difficult. When loading bit fields for device registers, having the exact bit fields in the instruction is much easier to read.