Machine Code: Assembly Code:Like x86, PowerPC machine code consists of bytes, with addresses, that represent assembly instructions and operands. PowerPC machine code also spends most of its time manipulating values in registers.
Address Instruction Operands
0: 38 60 00 07 li r3,7
4: 4e 80 00 20 blr
li r3, 7Add, like most arithmetic on PowerPC, takes *three* registers: two sources, and a destination.
blr
li r8, 8There's a separate instruction named "addi" (add immediate) to add a constant; plain "add" only works on registers.
li r9, 1000
add r3,r8,r9
blr
li r8, 8PowerPC machine code always uses four bytes for every instruction (it's RISC), while x86 uses from one to a dozen bytes per instruction (it's CISC). Here's a good but long retrospective article on the RISC-vs-CISC war, which got pretty intense during the 1990's. Nowadays, RISC machines compress their instructions (like CISC), while CISC machines decode their instructions into fixed-size blocks (like RISC), so the war ended in the best possible way--both sides have basically joined forces!
addi r3,r8,1000
blr
li r3, 0xabcdef ; ERROR! out of range!Instead, you break the 32-bit constant into two 16-bit pieces. They have a dedicated load-and-shift instruction "lis":
blr
lis r3, 0xab ; "load immediate shifted" (the high half)
ori r3,r3, 0xcdef ; "or immediate" (the low half)
blr
Memory is accessed with the "lwz" (load word) and "stw" (store word) instructions. Unlike x86, these are the *only* instructions that access memory; you can't do an "add" with one operand in memory!
lwz r3, 0(r1) ; load register r3 from the stack
blr
Here I'm writing an integer out to the stack, then reading it in again.
li r7, 123
stw r7, 0(r1) ; store register r7 to the stack
lwz r3, 0(r1) ; load register r3 from the stack
blr
There are "updating" variants of load and store called "lwzu" and
"stwu". These actually change the value of the pointer used as an
address. For example,this code does two things:
stwu r7, -4(r1)
Here's an example:
li r7, 123
stwu r7, -16(r1) ; store register r7 to the stack (with push)
lwzu r3, 0(r1) ; load register r3 from the stack
addi r1,r1,16 ; clean up the stack
blr
Array indexing mostly has to be done manually. If r5 is the
start of the array, and r6 is the index, you have to do something like
this:
ori r5,r1,0 ; array pointer==stack pointer
li r6,2 ; array index
mulli r8,r6,4; array index*4
add r8,r8,r5; add base pointer
lwz r3,0(r8); access memory there
blr
You can combine the add and lwz with a "lwzx":
ori r5,r1,0 ; array pointer==stack pointer
li r6,2 ; array index
mulli r8,r6,4; array index*4
lwzx r3,r5,r8; access memory at base + index
blr
li r3,99Here, _print_int will end with its own "blr", which will jump straight back to main, skipping us. Getting control back from a function is much trickier. The problem is a function will end with "blr" (Branch to the Link Register); the Link Register can only hold one value at a time. So if you just overwrite the Link Register with your own value, you can't return to main!
b _print_int
blr
li r3,99
bl _print_int
blr ; Oops! We trashed LR with the "bl" above!
The sequence of events here is:
mflr r28 ; save main's link registerOK! Everybody returns correctly now, but main complains we overwrote its preserved data (r28 is preserved).
li r3,99
bl _print_int ; "bl" will overwrite LR, so print_int can return here
mtlr r28 ; restore main's link register
blr ; now this works... sorta
mflr r0 ; save main's link register...Whew! The x86 "call" and "ret" are looking a lot better now!
stwu r0,-32(r1); ... onto the stack
li r3,99
bl _print_int ; "bl" will overwrite LR, so print_int can return here
lwz r0,0(r1); grab main's link register from the stack
addi r1,r1,32 ; restore the stack
mtlr r0 ; restore main's link register
blr ; finally, this works correctly!