not logged in | [Login]
Label's and the branch (and branch_if ... branch on result of a comparison) functions are the equivalent of 'goto' in other simple languages ... they are used to implement programme logic.
label(LABEL) - target of a branch or conditional branch (branch_if - bxx function), LABEL is of your own devising.
b(LABEL) - branch to (goto a) named label
@micropython.asm_thumb def test(): mov(r0, 42) #put 42 in register 0 b(END) #branch to label END mov(r0, 666) #put 666 in register 0 label(END) >>> test() 42 r0 never got set to 666, because we skipped past that operation to go to **label(END)***
'branch on logic' (aka if aka 'make a decision') is a 2 step operation;
To properly understand the use of the comparison instructions, it should be noted that most of the exposed ARM Thumb instructions set the condition flags. Aside from the explicit cmp instruction, the arithmetic instructions will set the condition codes depending on whether the result is greater than, equal to, or less than zero. More surprisingly from those coming from other assemblers, the mov instructions will also set the condition codes depending on the value deposited in the target register. Thus the code fragment below returns 0 regardless of its arguments unless the commented instruction is removed (when it returns 1 if the arguments differ, otherwise the equal value passed in the two registers).
@micropython.asm_thumb def test(r0, r1): sub(r3, r0, r1) mov(r0, 0) # sets eq beq(DONE) mov(r0, 1) label(DONE)
cmp(reg, val) compare reg with val (reg-val), put the result into CPSR flags
cmp(regA, regB) compare regA with regB (regA-regB), put the result into CPSR flags
cmn(regA, regB) add regA to regB (regA+regB), discard the result but set the CPSR flags. Typical use: compare registers holding numbers with opposite signs. If their absolute values are equal the eq flag will be set.
tst(regA,regB) and regA with regB (regA®B), discard the result but set the CPSR flags (i.e. multiple bit test). The behaviour of this may seem counterintuitive: the eq flag is set if the result of the and operation is zero. The behaviour is in fact consistent with that of other instructions.
CPSR flags are some magic place for holding the result of a comparison, that is 'inspected' by the bxx operations in the next step, the branch_if instruction.
bgt(LABEL) branch if greater than
@micropython.asm_thumb def greater_than_life(r0): cmp(r0, 42) bgt(GT) #r0>42 goto END mov(r0, 0) #r0 = 0 (False in uPy) b(END) #goto END label(GT) mov(r0, 1) #r0 = 1 (True in uPy) label(END) >>>greater_than_life(15) 0 >>>greater_than_life(42) 0 >>>greater_than_life(43) 1
beq(LABEL) branch if equal
bne(LABEL) branch if not equal
bge(LABEL) branch if greater than or equal to (>=) operands treated as signed
bgt(LABEL) branch if greater than (>) operands treated as signed
blt(LABEL) branch if less than (<) operands treated as signed
ble(LABEL) branch if less than or equal to (<=) operands treated as 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 (> using unsigned comparison)
bls(LABEL) branch if lower or equal (<= using unsigned comparison)
The overflow flag is set if the result, viewed as a two's compliment number, has the "wrong" sign in relation to the operands. For example adding 1 to 0x7fffffff will set the overflow bit because the result (0x8000000), viewed as a two's compliment integer, is negative. Note that in this instance the carry flag is not set: the result has overflowed into the most significant bit only.
An addition sets the carry flag when the result overflows out of the MSB, for example adding 0x80000000 and 0x80000000.
The branch instructions have a limited field width for branch destinations (which are PC relative). For long programs where the assembler produces a "branch not in range" error this can be overcome with the "wide" variants such as
beq_w(LABEL) long branch if equal
Execute the following instruction if a condition is true
cmp(r0, r1) it(eq) mov(r0, 100) # happens only if r0 == r1
The condition codes listed above are supported.
If a condition is true execute the following instruction, otherwise execute the one after
cmp(r0, r1) ite(eq) mov(r0, 100) # runs if r0 == r1 mov(r0, 200) # runs if r0 != r1
You can have subroutines (internal functions) within the body of an assembler function. The ARM processor uses r15 as the program counter and r14 as a link register. To call a subroutine the bl(label) instruction loads the link register with the return address and jumps to the passed label. To return (to the instruction after the bl call) the bx(lr) instruction loads the program counter with the link register contents
def f(r0): # jump over the internal functions b(entry) label(func1) add(r0, 2) bx(lr) # Return label(func2) sub(r0, 1) bx(lr) label(entry) bl(func1) # Subroutine call bl(func2)
Note that, for nested subroutines, you need to push and pop the link register.
Last edited by Peter Hinch, 2015-03-11 14:28:13