Assembly Reference
Assembly Reference
armvm assembles ARMv7 32-bit ARM code written in ARM UAL (Unified Assembly Language) syntax with Apple Mach-O conventions. This is the dialect produced by clang -arch armv7 -target armv7-apple-darwin.
Registers
| Name | Number | Role |
|---|---|---|
r0–r12 |
0–12 | General-purpose |
sp / r13 |
13 | Stack pointer |
lr / r14 |
14 | Link register (return address) |
pc / r15 |
15 | Program counter |
- r0–r3: argument/result registers (caller-saved)
- r4–r11: callee-saved
- r12: intra-procedure scratch (
ip) - sp decrements on
pushand increments onpop - lr receives the return address on
bl; restored viapop {pc}orbx lr
Condition codes
Every data-processing and branch instruction can carry an optional condition suffix.
| Suffix | Code | Flags | Meaning |
|---|---|---|---|
eq |
0000 | Z=1 | Equal |
ne |
0001 | Z=0 | Not equal |
cs / hs |
0010 | C=1 | Carry set / unsigned higher or same |
cc / lo |
0011 | C=0 | Carry clear / unsigned lower |
mi |
0100 | N=1 | Minus / negative |
pl |
0101 | N=0 | Plus / positive or zero |
vs |
0110 | V=1 | Overflow |
vc |
0111 | V=0 | No overflow |
hi |
1000 | C=1, Z=0 | Unsigned higher |
ls |
1001 | C=0 or Z=1 | Unsigned lower or same |
ge |
1010 | N=V | Signed greater or equal |
lt |
1011 | N≠V | Signed less than |
gt |
1100 | Z=0, N=V | Signed greater than |
le |
1101 | Z=1 or N≠V | Signed less than or equal |
al |
1110 | — | Always (default) |
Example:
cmp r0, #10
addgt r1, r1, #1 @ add only if r0 > 10
movle r0, #0 @ move only if r0 ≤ 10
Data-processing instructions
Syntax: <op>[cond][s] Rd, Rn, <operand2>
The optional s suffix sets the CPSR flags (N, Z, C, V) from the result.
| Instruction | Operation |
|---|---|
and |
Rd = Rn AND Op2 |
eor |
Rd = Rn EOR Op2 |
sub |
Rd = Rn − Op2 |
rsb |
Rd = Op2 − Rn |
add |
Rd = Rn + Op2 |
adc |
Rd = Rn + Op2 + C |
sbc |
Rd = Rn − Op2 + C − 1 |
rsc |
Rd = Op2 − Rn + C − 1 |
tst |
Sets flags for Rn AND Op2; no result written |
teq |
Sets flags for Rn EOR Op2; no result written |
cmp |
Sets flags for Rn − Op2; no result written |
cmn |
Sets flags for Rn + Op2; no result written |
orr |
Rd = Rn OR Op2 |
mov |
Rd = Op2 |
bic |
Rd = Rn AND NOT Op2 |
mvn |
Rd = NOT Op2 |
Operand2 can be:
- An 8-bit immediate with optional even-bit rotation:
#value - A register:
Rm -
A register with an immediate shift: Rm, lsl #nRm, lsr #nRm, asr #nRm, ror #n - A register with a register-specified shift:
Rm, lsl Rs
mov r0, #10 @ r0 = 10
add r1, r0, r2 @ r1 = r0 + r2
subs r0, r0, #1 @ r0 -= 1; set flags
mov r3, r4, lsl #2 @ r3 = r4 << 2
add r0, r0, r1, lsr r2
Shift instructions (pseudo)
These are aliases for mov Rd, Rm, <shift>:
lsl r0, r1, #3 @ r0 = r1 << 3
lsr r0, r1, #1 @ r0 = r1 >> 1 (logical)
asr r0, r1, #1 @ r0 = r1 >> 1 (arithmetic, sign-extended)
ror r0, r1, #4 @ r0 = r1 rotated right by 4
Multiply instructions
mul Rd, Rm, Rs @ Rd = Rm * Rs
mla Rd, Rm, Rs, Rn @ Rd = Rm * Rs + Rn
umull RdLo, RdHi, Rm, Rs @ {RdHi,RdLo} = Rm * Rs (unsigned 64-bit)
smull RdLo, RdHi, Rm, Rs @ {RdHi,RdLo} = Rm * Rs (signed 64-bit)
umlal RdLo, RdHi, Rm, Rs @ {RdHi,RdLo} += Rm * Rs (unsigned accumulate)
smlal RdLo, RdHi, Rm, Rs @ {RdHi,RdLo} += Rm * Rs (signed accumulate)
Memory instructions
Single register load/store
ldr Rd, [Rn] @ Rd = *(DWORD *)(Rn)
ldr Rd, [Rn, #offset] @ pre-indexed, no write-back
ldr Rd, [Rn, #offset]! @ pre-indexed with write-back
ldr Rd, [Rn], #offset @ post-indexed
str Rd, [Rn, #offset] @ store word
ldrb Rd, [Rn, #offset] @ load byte (zero-extended)
strb Rd, [Rn, #offset] @ store byte
ldrh Rd, [Rn, #offset] @ load halfword (zero-extended)
strh Rd, [Rn, #offset] @ store halfword
ldrsb Rd, [Rn, #offset] @ load signed byte
ldrsh Rd, [Rn, #offset] @ load signed halfword
Block data transfer (push/pop)
push {r0, r1, lr} @ STMDB sp!, {r0, r1, lr}
pop {r0, r1, pc} @ LDMIA sp!, {r0, r1, pc}
ldm Rn, {reglist} @ load multiple
stm Rn, {reglist} @ store multiple
The {reglist} may be a comma-separated list of registers and ranges: {r0-r3, r7, lr}.
pop {pc} is the standard way to return from a function that saved lr on the stack.
PC-relative load
ldr r0, LCPI0_0 @ load the 32-bit constant at label LCPI0_0
LCPI0_0:
.long 0xdeadbeef
Branch instructions
b label @ unconditional branch
bl function @ branch with link (saves return address in lr)
bx lr @ branch and exchange (return via lr)
bgt label @ branch if greater than
beq loop_start @ branch if equal
bl saves pc (the address of the next instruction) into lr before branching.
Directives
| Directive | Description |
|---|---|
.globl symbol |
Declare symbol as globally visible |
.asciz "text" |
Emit NUL-terminated string |
.ascii "text" |
Emit string without NUL terminator |
.byte value |
Emit 8-bit value |
.short value |
Emit 16-bit value |
.long value |
Emit 32-bit value |
.space n |
Emit n zero bytes |
.p2align n |
Align to 2ⁿ bytes |
.set name, expr |
Define assembler constant |
.section … |
Section marker (ignored by armvm) |
.build_version … |
Platform metadata (ignored) |
.subsections_via_symbols |
Apple linker hint (ignored) |
.indirect_symbol sym |
Reserve a slot for an external symbol |
.zerofill seg,sect,label,size,align |
Zero-initialised data |
.comm symbol,size,align |
Common symbol |
Labels
Labels end with a colon and can start with a letter, digit, or underscore:
_main: @ function entry (stripped underscore becomes "main")
loop_top: @ local label
L_.str.1: @ Clang-generated string label
LCPI0_0: @ Clang-generated literal pool label
The special label _main sets the main_label variable used by the
compile_buffer caller as the execution entry point.
Comment syntax
@ This is a comment (ARM UAL style)
; This is also a comment
Escape sequences in strings
Inside .ascii / .asciz:
| Sequence | Character |
|---|---|
\n |
newline |
\t |
tab |
\r |
carriage return |
\\ |
backslash |
\" |
double-quote |
\0 |
NUL |
\nnn |
octal / decimal byte value |