gcc PowerPC Assembly Quick Reference ("Cheat Sheet")


This page describes the syntax used by the MacOS X assembler "as".  Other assemblers, like "gas", use a slightly different syntax where registers have a percent sign in front of them, like "%r3"; while some IBM assemblers just say "3" for register 3 (so for "addi 3,3,3", you have to remember which 3 is a register!).

Instructions

Mnemonic
Purpose
Examples
li dest,const
Move the constant const into the register dest.  const can't be more than 16 bits long.
li r3, 4
addis dest,const
Add the constant const<<16 to dest.
addis r3, 0xf 
;   r3+=0xf0000
b label
"Branch".  Starts executing code at label.
b loop_start
bl func
"Branch and Link".  Saves return address in link register, then jumps to func. Used to call subroutines.  Be sure to save the link register to the stack first! (see stack example at right)
bl read_input
blr
"Branch to Link Register".  Jumps to lr.  Used to end a subroutine.
blr
lwz dest, off(ptr)
Load word from pointer.  dest=*(ptr+off); Copy the integer in memory at address off bytes from the ptr register into the dest register.
lwz r3,8(r4)
; Copy r3 from memory at 8 off r4
lwzu dest, off(ptr)
Load word with pointer update. dest=*(ptr+=off); Like lwz, but then changes the ptr register to equal ptr+off.
lwzu r3,8(r4) 
; Copy r3 from memory at 8 off r4; and add 8 to r4
stw src, off(ptr) Store word. *(ptr+off)=src;  There's also a "stwu" that does the pointer update like lwzu.
stw r3,8(r4) 
; Copy r3 to memory at 8 off r4
add dest, src1, src2
Add.  dest=src1+src2.  All three must be registers.  Register r0 is treated as if it has value zero, so "add %r3, %r5, %r0" is actually a move.
add r3,r3, r4
addi dest, src, const
Add immediate.  dest=src+constsrc and dest are registers, const is an integer constant.
addi r3, r3, 0x42
mullw dest,src1,src2
dest=src1*src2.  There's a corresponding "mulhi" to get the high 32 bits of the product.
mullw r3, r3, r4
cmp 0,0,a,b

Compare two values into condition register 0.  Sets flags that are used by the conditional jumps (below).
cmp 0,0,r3, r4
blt label Goto label if previous comparison came out as less-than.  Other conditionals available are: ble (<=), beq (==), bge (>=), bgt (>), and bne (!=). blt loop_again  ; Jump if r3 < r4

Stack Frame

Example when using frame pointer and two local variables:
Contents
offset
saved link register
20(r1)
ancient r1
16(r1)
Local variable 1
12(r1)
Local variable 2 8(r1)
(next link register)
4(r1)
old r1
0(r1)

my_sub: ; Adds 1 to first argument
  ; Prologue
  stwu r1,-16(r1) ; "push"
  mflr r0  ; r0 = link register
  stw r0,20(r1) ; Save link register
  ; Body of subroutine:
  addi r3, r3, 1
  ; Epilogue
  lwz r0,20(r1)
  mtlr r0
  addi r1, r1, 16 ; "pop"
  blr

Constants, Registers, Memory

Constants can just be written normally.  Weirdness: if a constant is more than 16 bits long, you can't load it in one instruction: you need to break it into two parts.
Use the "i" (immediate) form of the instruction if you want to put in a constant, or use "li" to load the constant into a register.
All memory access is via the load and store routines--you *can't* just get to a random memory location from any instruction.
lis     4,msg@ha    # load top 16 bits of &msg
addi 4,4,msg@l


Registers

r1 is the stack pointer
Return value in r3
First 8 integer arguments are in r3 through r10; remainder on stack.
Free for use (no save needed):
   r0, and r3 through r12
Must be saved:
   r13 through r31

Floating Point

Load and store from memory with lfs/lfd (load float single/double) stfs, stfd.
Arithmetic instructions start with "f", like "fadd", "fmul", "fmadd", "fdiv".  There are both single- and double-precision versions, with the single-precision versions ending in "s" (like fadds), but they both use the same register set.
Weird instructions: "fres" (computes approximate 1.0/x); "frsqrte"  (1.0/sqrt(x), approximate).

Floating Point Registers

Registers are all 64-bit doubles.
Floating point args are in f1-f13
  f0-f13 are free for any use
f14-f31 must be saved
Return value goes in f1

Common Errors

Error: operand out of range (123 not between 0 and 31)
   You passed in some constant (here 123); the assembler expected a register.
Error: operand out of range (100000 not between -32768 and 32767)
   You passed in a constant that was too big.  Use "li" and "addis" to stick the constant together from 16-bit pieces (or just choose another constant!)


There's an excellent table of PowerPC instructions here.  The IBM 32-Bit PowerPC Programming Environment gives all the instructions in chapter 8.1 and a good overview in chapter 4.  The IBM Compiler Writer's Guide gives the calling conventions. Readable tutorial walkthroughs at U. Conn (compare OS X and Linux interfaces),

Examples

Return a constant:
li r3,100
blr

(Try this in NetRun now!)

Add two integers:
li r7,3
li r9,10
add r3,r7,r9

blr

(Try this in NetRun now!)

Compare integers, and branch.  This is nearly identical to the x86 version.

li r4, 10
li r5, 100
cmp 0,0, r4,r5 ; compare r4 and r5. Put result into condition register 0
blt is_less
li r3,4
blr

is_less:
li r3,5
blr

(Try this in NetRun now!)

Load the two 16-bit halves of a 32-bit constant.  Note most Linux/IBM machines use "@ha" and "@l" suffixes instead of the OS X "ha16" and "lo16" functions.
lis r3,ha16(0xdeadbeef)
addi r3,r3,lo16(0xdeadbeef)

blr

(Try this in NetRun now!)

Load a constant from read-only memory:

lis r5,ha16(myStuff)
addi r5,r5,lo16(myStuff)
lwz r3,0(r5)

blr

myStuff:
.long 1234

(Try this in NetRun now!)

Store a constant to writeable memory:
lis r5,ha16(myStuff)
addi r5,r5,lo16(myStuff)
stw r3, 0(r5)

blr

.data
myStuff:
.long 1234

(Try this in NetRun now!)

Call a function: the hard part is saving the link register, so we can get back.
stwu r1,-16(r1); prologue
mflr r0
stw r0,20(r1)

li r3,1234
bl _print_int

lwz r0,20(r1) ; epilogue
mtlr r0
addi r1, r1, 16
blr

(Try this in NetRun now!)

It's a little easier to call functions if you don't ever come back.  Here "b function" branches away to the function, never to return.
lis r3,ha16(myFloats) ; first argument: pointer to floats
addi r3,r3,lo16(myFloats)
li r4,1 ; number of floats to print
b _farray_print ; tail call to print (returns directly to main)

.data
myFloats:
.long 0x3F9DF3B6 ; float 1.234 as an int, from: return *(int *)&someFloat

(Try this in NetRun now!)

Here we do a little floating point arithmetic before printing the value.  r3 is doing double duty: used by us first, then used as a function parameter.
lis r3,ha16(myFloats) ; first argument: pointer to floats
addi r3,r3,lo16(myFloats)

lfs f1,0(r3) ; load the float
fadd f1,f1,f1 ; add it to itself
stfs f1,0(r3) ; store back to memory

li r4,1 ; number of floats to print
b _farray_print ; tail call to print (returns directly to main)

.data
myFloats:
.long 0x3F9DF3B6 ; float 1.234 as an int, from: return *(int *)&someFloat

(Try this in NetRun now!)


O. Lawlor, ffosl@uaf.edu
Up to: Class Site, CS, UAF