ASM introduction
Talk0this wiki
Contents |
ARM overview
Edit
- 32-bit microcontroller
- 16 registers: R0 ... R15
- R13 contains the stack pointer (SP)
- R14 contains the return address (for subroutine calls). It's called LR (link register).
- R15 contains the program counter (PC, 24 bits) and flags (8 bits)
- Flags: Negative result, Zero result, Carry, oVerflowed result, IRQ Interrupt disable, FIQ Fast Interrupt disable, S1 Processor mode 1, S0 Processor mode 0.
- The ARM can run in 16 bit (Thumb) or 32 bit (standard ARM ASM). Dryos only uses 32bit. CHDK (which is a different beast) uses some Thumb to keep the code smaller [1].
- Branch instructions (BEQ, BNE, B, BL, BX, etc) load the destination address into the program counter (R15)
- The program counter (PC) is incremented by 4 bytes for each ARM instruction (2 bytes for each THUMB).
- The PC doesn't contain the address of the current instruction during execution, this is typically stored at pc-8 (ARM), or pc-4 (THUMB).
Simplest instructions
Edit
MOV R0, R1 ; Assignment: R0 = R1 ADD R0, R1, R2 ; R0 = R1 + R2 SUB and MUL: similar
Load and Store
Edit
LDR R0, <what to load> ; load from memory and store into a register STR R0, <where to store> ; store the contents of a register into memory STRD R2, [SP] ; store TWO registers: here, store R2 and R3 on the stack; [but... shouldn't the stack pointer decrease?!] LDRD R2, [SP] ; writes in R2 and R3? ADR R1, aLeddriverEdled ; "LedDriver\\EDLedDriver.c" ; load a pointer to a string? ADR R2, TH_intTimerCBR ; or a pointer to a function?
Addressing modes
Edit
Notation Meaning Example
#15 ; decimal constant 15 MOV R0, #15
#0xFF ; hex constant MOV R0, #0xFF
R1 ; the contents of R1 MOV R0, R1
[R1] ; memory address held in R1 LDR R0, [R1] ; R0 = *(R1) // C notation
(R1 contains a pointer) STR R0, [R1] ; *(R1) = R0
[base,offset] ; memory address is base+offset LDR R0, [R1+#15] ; is it possible to use MOV R0, [something] ?
=15 and =0xFF ; numerical constants LDR R0, =15 ; equivalent to MOV R0, #15
; for use in LDR instruction LDR R0, =0x55555555 ; The constant is too big and cannot be encoded as a single MOV
{R0-R12,R14} ; register list, for load/store
; on the stack
Conditional instructions
Edit
- CMP R0, R1 ; Compare two numbers by performing the difference and setting the flags. The difference is not stored anywhere.
- CMN <lhs>,<rhs> ; Compare Negative
- TST <lhs>,<rhs> ; Test Bits
- TEQ <lhs>,<rhs> ; Test Equivalence (?!)
Simple IFs can be written by adding some conditional suffixes to the instructions.
E.g. ADDEQ means: IF <EQ> THEN ADD ...
Conditional suffixes:
- AL Always
- NV Never
- EQ Equal (Zero flag set)
- NE Not Equal
- VS Overflow Set
- VC Overflow Clear
- MI Minus (N flag set)
- PL Plus
- CS Carry Set
- CC Carry Clear
- HI Higher
- LS Lower or Same
- GE Greater or Equal
- LT Less than
- GT Greater than
- LE Less than or equal
C calling convention
Edit
A subroutine call looks like this:
; store the subroutine arguments somewhere, usually in R0...R3 BL subroutine_name
When a BL is called, the return address (next instruction after the call) is stored in the link register LR.
When the subroutine is called, the first thing it does is decide which registers it will corrupt, and push those to the Stack (SP). This may include the LR if this sub calls another sub.
Before the subroutine returns, It pulls the regs from the Stack, then it MOV PC, LR (or LDR, or other means)
STMFD SP!, {R2-R6,LR} ; push to the stack the registers which will be corrupted
...
; subroutine code
...
LDMFD SP!, {R2-R6,PC} ; pull them back
Branch Instructions
Edit
Some functions are called with:
B subroutine_name ; -Immediately jump to supplied subroutine (or label) and resume code execution from there.
BL subroutine_name ; -Branch with link to sub, R14 (LR) is loaded with the address of the next instruction, just before the
; branch. You can then load the link register (R14) back into the program counter (R15)
; to return to the instruction that called the branch, after executing the branch.
BX ; -Branch and optionally switch mode (ARM or Thumb). DryOS only uses ARM, so as far as we are
; concerned, BX = B.
BEQ ; -Branch if EQUAL TO. Usually preceeded by a CMP, the logical result of this CMP operation
; decides if the branch will be executed or not.
BNE ; -Branch if NOT EQUAL TO. Same as above.
A couple common examples:
LDR R4, =0x762c ; R4 = 0x762c
LDR R0, [R4] ; R0 = value at 0x762c in memory
CMP R0, #0 ; Compare R0 to 0, don't do anything yet.
BNE loc_FF0678E0 ; Branch if NOT EQUAL - branch to loc_FF0678E0 if R0 != 0.
; PC = FF0678E0 now
Passing arguments to subroutines
Edit
A maximum of 4 arguments are passed to a subroutine in R0 ... R3. If more arguments are needed, they are put on the stack. [thanks AJ!]
- [AJ, can you give an example of a function with 5 or more arguments?]
The return value is stored in R0.
What happens when the return value is more than 4 bytes? R0 stores a pointer to it?
See also: [2]
Complete Spec: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf
Flags
Edit
There are 4 flags of interest in the ARM processor:
- N - "negative flag" - indicates a negative value
- Z - "zero flag" - this is set when an instruction produces a result of 0.
- C - "carry flag" - used when there is a carry generated by something like an addition operation.
- V - "overflow flag" - this is set in, like the name suggests, the event of an overflow.
Here is a simple example of how flags are used. Consider this basic loop:
MOV R0, #0 ; R0 = 0
B loop ; Branch to loop
loop:
ADD R0, R0, #1 ; Increment R0 by 1. R0 = R0 + 1
CMP R0, #9 ; Compare R0 to 9, set Z flag accordingly.
BNE loop ; "Branch if NOT-EQUAL" - looks at Z flag.
This website (http://www.mitchellwebdesign.com/arm/lecture3/lecture3-1.html) is a great resource with a very nice explanation of a lot of ASM basics.
Proper tail calls
Edit
See [3] for a nice explanation.
So, if at the end of a function there is a subroutine call, and nothing after, the compiler can make an optimization. This allows infinite recursion without filling the stack :)
In this case, the subroutine may look like this:
ROM:FF063CA4 TurnAVLineMuteOn
ROM:FF063CA4 STMFD SP!, {R4,LR}
...
ROM:FF063CE4 LDMFD SP!, {R4,LR}
ROM:FF063CE8 B give_semaphore
Got it?