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.
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
$