MicroPython allows programmers to use inline assembler. The inline assembler supports a subset of the ARM Thumb-2 instruction set. The syntax tries to be as close as possible to that defined in the above ARM manual, converted to Python function calls.
Instructions operate on 32 bit signed integer data except where stated otherwise. Most supported instructions operate on registers R0-R7
only: where R8-R15
are supported this is stated. Registers R8-R12 must be restored to their initial value before return from a function. Registers R13-R15
constitute the Link Register, Stack Pointer and Program Counter respectively.
Where immediate values are used, these are zero-extended to 32 bits. Thus mov(R0, 0xff)
will set R0
to 255
.
mov(Rd, imm8)
→ Rd = imm8
mov(Rd, Rn)
→ Rd = Rn
movw(Rd, imm16)
→ Rd = imm16
movt(Rd, imm16)
→ Rd = (Rd & 0xffff) | (imm16 « 16)
movt
writes an immediate value to the top halfword of the destination register. It does not affect the contents of the bottom halfword.
movwt(Rd, imm32)
→ Rd = imm32
movwt
is a pseudo-instruction: the MicroPython assembler emits a movw followed by a movt to move a 32-bit value into Rd
.
ldr(Rt, [Rn, imm7])
→ Rt = [Rn + imm7] Load a 32 bit word
ldrb(Rt, [Rn, imm5])
→ Rt = [Rn + imm5] Load a byte
ldrh(Rt, [Rn, imm6])
→ Rt = [Rn + imm6] Load a 16 bit half word
Where a byte or half word is loaded, it is zero-extended to 32 bits.
str(Rt, [Rn, imm7])
→ [Rn + imm7] = Rt Store a 32 bit word
strb(Rt, [Rn, imm5])
→ [Rn + imm5] = Rt Store a byte (b0-b7)
strh(Rt, [Rn, imm6])
→ [Rn + imm6] = Rt Store a 16 bit half word (b0-b15)
and_(Rd, Rn)
→ Rd &= Rn
orr(Rd, Rn)
→ Rd |= Rn
eor(Rd, Rn)
→ Rd ^= Rn
mvn(Rd, Rn)
→ Rd = Rn ^ 0xffffffff i.e. Rd = 1’s complement of Rn
bic(Rd, Rn)
→ Rd &= ~Rn bit clear Rd using mask in Rn
Note the use of and_
instead of and
, because and
is a reserved keyword in Python.
lsl(Rd, Rn<0-31>)
→ Rd «= Rn
lsr(Rd, Rn<1-32>)
→ Rd = (Rd & 0xffffffff) » Rn Logical shift right
asr(Rd, Rn<1-32>)
→ Rd »= Rn arithmetic shift right
ror(Rd, Rn<1-31>)
→ Rd = rotate_right(Rd, Rn) Rd is rotated right Rn bits.
A rotation by (for example) three bits works as follows. If Rd initially contains bits b31 b30..b0
after rotation it will contain b2 b1 b0 b31 b30..b3
b(LABEL)
→ Unconditional branch
beq(LABEL)
→ branch if equal
bne(LABEL)
→ branch if not equal
bge(LABEL)
→ branch if greater than or equal
bgt(LABEL)
→ branch if greater than
blt(LABEL)
→ branch if less than (<) (signed)
ble(LABEL)
→ branch if less than or equal to (⇐) (signed)
bcs(LABEL)
→ branch if carry flag is set
bcc(LABEL)
→ branch if carry flag is clear
bmi(LABEL)
→ branch if negative
bpl(LABEL)
→ branch if positive
bvs(LABEL)
→ branch if overflow flag set
bvc(LABEL)
→ branch if overflow flag is clear
bhi(LABEL)
→ branch if higher (unsigned)
bls(LABEL)
→ branch if lower or equal (unsigned)
Inline assembler functions are denoted by a special function decorator. Let’s start with the simplest example:
@micropython.asm_thumb def fun(): movw(r0, 42)
This function takes no arguments and returns the number 42
. r0
is a register, and the value in this register when the function returns is the value that is returned. MicroPython always interprets the r0
as an integer, and converts it to an integer object for the caller.
If you run print(fun())
you will see it print out 42
.
Inline assembler functions can accept up to 4 arguments. If they are used, they must be named r0
, r1
, r2
and r3
to reflect the registers and the calling conventions.
Here is a function that adds its arguments:
@micropython.asm_thumb def asm_add(r0, r1): add(r0, r0, r1)
This performs the computation r0 = r0 + r1
. Since the result is put in r0
, that is what is returned. Try asm_add(1, 2)
, it should return 3
.
To manipulate with GPIO output we can use SIO memory mapped registers:
0xd0000000
0x010
0x014
0x018
Registers GPIO_OUT_SET
and GPIO_OUT_CLR
perform an atomic bit-set and bit-clear respectively.