if(var >= 1) { printf("Hello!"); } else if(var <= -1) { printf("Goodbye!"); } else { printf("Error"); }
Anyone with programming experience(i.e. everyone reading this) knows what this code is and how it works. How does it work in assembly, though?
(these are not full functions)
ARM:
cmp r0, #0 ldrgt r0, =#ptr_hello ldrlt r0, =#ptr_goodbye ldreq r0, =#ptr_error bl printf ldmfd sp!, {lr} bx lr
THUMB:
cmp r0, #0 bgt greater blt less b zero greater: ldr r0, =#ptr_hello bl printf b end less: ldr r0, =#ptr_goodbye bl printf b end zero: ldr r0, =#ptr_error bl printf end: pop {lr} bx lr
Big difference, huh?
Note how I'm using conditional suffixes on non-branch instructions in ARM making it very compact, whereas in THUMB you would only be using the conditionals to jump around in the code and run different code "banks" as I like to call them. This is a list of applicable conditionals:
eq Z=1 Zero (EQual to 0) ne Z=0 Not zero (Not Equal to 0) cs C=1 Carry Set / unsigned Higher or Same cc C=0 Carry Clear / unsigned Lower mi N=1 Negative (MInus) pl N=0 Positive or zero (PLus) vs V=1 Signed overflow (oVerflow Set) vc V=0 No signed overflow (oVerflow Clear) hi C=1 & Z=0 Unsigned HIgher ls C=0 | Z=1 Unsigned Lower or Same ge N=V Signed Greater or Equal lt N != V Signed Less Than gt Z=0 & N=V Signed Greater Than le Z=1 | N != V Signed Less or Equal al - ALways (default) nv - NeVer
The conditions that are being checked are based on the condition flags on the CPU. A quick look at those:
Z: zero- result was zero C: carry- (unsigned) result overflowed the destination register N: negative- result was negative V: overflow- same as C, but for signed math.
And here's an example of them in IDA:
Let's very briefly check out an example or two:
ex. 1:
ldr r2, =0xFFFFFFFF mov r3, #0x1 adds r0, r2, r3
What does this set? 0xFFFFFFF is the max number for a 32-bit register, but our result is 0x100000000. So the top bit is lost due to the size restriction and the result is 0x00000000. So the flags are set as follows:
Z: The result was zero, so this is set
C: We lost some of the info in the final result due to the size, so the carry would be set
N: Zero is non-negative, so this would not be set
V: We're using two's compliment negatives here, so 0xFFFFFFFF is -1. So for this, the math would be -1 + 1 which is 0. Nothing overflowed here, so this is not set.
ex. 2:
mov r4, #0 loop: /* do stuff here */ add r4, r4, #1 cmp r4, #10 blt loop
What is set with this code and compare? r4 goes from 0-9(while it's true) which sets the flags as such:
Z: Will be set once r4 is 10 and break the loop. Otherwise unset.
C: The carry is set if register1 is >= register2. It will be set on r4 = 10.
N: r4 - 10 will always be negative until the loop ends @ r4 = 10. This is set.
V: No signed overflow. This is not set.
Note in the chart:
lt N != V
In our loop N is set and V is not, so the "lt" condition works.
Next time we'll go further in-depth on exactly how the flags are set and what exactly is happening in the different compare instructions. Also, a bit on efficiency with compares and the logic behind turning conditions in a higher level language into assembly.