docs: Add reference for Thumb2 inline assembler.
Thanks to Peter Hinch for contributing this.
This commit is contained in:
parent
aef3846c13
commit
2110dc5a6d
|
@ -1,3 +1,5 @@
|
|||
.. _pyboard_tutorial_assembler:
|
||||
|
||||
Inline assembler
|
||||
================
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
Arithmetic instructions
|
||||
=======================
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Notation: ``Rd, Rm, Rn`` denote ARM registers R0-R7. ``immN`` denotes an immediate
|
||||
value having a width of N bits e.g. ``imm8``, ``imm3``. ``carry`` denotes
|
||||
the carry condition flag, ``not(carry)`` denotes its complement. In the case of instructions
|
||||
with more than one register argument, it is permissible for some to be identical. For example
|
||||
the following will add the contents of R0 to itself, placing the result in R0:
|
||||
|
||||
* add(r0, r0, r0)
|
||||
|
||||
Arithmetic instructions affect the condition flags except where stated.
|
||||
|
||||
Addition
|
||||
--------
|
||||
|
||||
* add(Rdn, imm8) ``Rdn = Rdn + imm8``
|
||||
* add(Rd, Rn, imm3) ``Rd = Rn + imm3``
|
||||
* add(Rd, Rn, Rm) ``Rd = Rn +Rm``
|
||||
* adc(Rd, Rn) ``Rd = Rd + Rn + carry``
|
||||
|
||||
Subtraction
|
||||
-----------
|
||||
|
||||
* sub(Rdn, imm8) ``Rdn = Rdn - imm8``
|
||||
* sub(Rd, Rn, imm3) ``Rd = Rn - imm3``
|
||||
* sub(Rd, Rn, Rm) ``Rd = Rn - Rm``
|
||||
* sbc(Rd, Rn) ``Rd = Rd - Rn - not(carry)``
|
||||
|
||||
Negation
|
||||
--------
|
||||
|
||||
* neg(Rd, Rn) ``Rd = -Rn``
|
||||
|
||||
Multiplication and division
|
||||
---------------------------
|
||||
|
||||
* mul(Rd, Rn) ``Rd = Rd * Rn``
|
||||
|
||||
This produces a 32 bit result with overflow lost. The result may be treated as
|
||||
signed or unsigned according to the definition of the operands.
|
||||
|
||||
* sdiv(Rd, Rn, Rm) ``Rd = Rn / Rm``
|
||||
* udiv(Rd, Rn, Rm) ``Rd = Rn / Rm``
|
||||
|
||||
These functions perform signed and unsigned division respectively. Condition flags
|
||||
are not affected.
|
|
@ -0,0 +1,90 @@
|
|||
Comparison instructions
|
||||
=======================
|
||||
|
||||
These perform an arithmetic or logical instruction on two arguments, discarding the result
|
||||
but setting the condition flags. Typically these are used to test data values without changing
|
||||
them prior to executing a conditional branch.
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Notation: ``Rd, Rm, Rn`` denote ARM registers R0-R7. ``imm8`` denotes an immediate
|
||||
value having a width of 8 bits.
|
||||
|
||||
The Application Program Status Register (APSR)
|
||||
----------------------------------------------
|
||||
|
||||
This contains four bits which are tested by the conditional branch instructions. Typically a
|
||||
conditional branch will test multiple bits, for example ``bge(LABEL)``. The meaning of
|
||||
condition codes can depend on whether the operands of an arithmetic instruction are viewed as
|
||||
signed or unsigned integers. Thus ``bhi(LABEL)`` assumes unsigned numbers were processed while
|
||||
``bgt(LABEL)`` assumes signed operands.
|
||||
|
||||
APSR Bits
|
||||
---------
|
||||
|
||||
* Z (zero)
|
||||
|
||||
This is set if the result of an operation is zero or the operands of a comparison are equal.
|
||||
|
||||
* N (negative)
|
||||
|
||||
Set if the result is negative.
|
||||
|
||||
* C (carry)
|
||||
|
||||
An addition sets the carry flag when the result overflows out of the MSB, for example adding
|
||||
0x80000000 and 0x80000000. By the nature of two's complement arithmetic this behaviour is reversed
|
||||
on subtraction, with a borrow indicated by the carry bit being clear. Thus 0x10 - 0x01 is executed
|
||||
as 0x10 + 0xffffffff which will set the carry bit.
|
||||
|
||||
* V (overflow)
|
||||
|
||||
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 complement integer, is negative. Note that in this instance
|
||||
the carry bit is not set.
|
||||
|
||||
Comparison instructions
|
||||
-----------------------
|
||||
|
||||
These set the APSR (Application Program Status Register) N (negative), Z (zero), C (carry) and V
|
||||
(overflow) flags.
|
||||
|
||||
* cmp(Rn, imm8) ``Rn - imm8``
|
||||
* cmp(Rn, Rm) ``Rn - Rm``
|
||||
* cmn(Rn, Rm) ``Rn + Rm``
|
||||
* tst(Rn, Rm) ``Rn & Rm``
|
||||
|
||||
Conditional execution
|
||||
---------------------
|
||||
|
||||
The ``it`` and ``ite`` instructions provide a means of conditionally executing from one to four subsequent
|
||||
instructions without the need for a label.
|
||||
|
||||
* it(<condition>) If then
|
||||
|
||||
Execute the next instruction if <condition> is true:
|
||||
|
||||
::
|
||||
|
||||
cmp(r0, r1)
|
||||
it(eq)
|
||||
mov(r0, 100) # runs if r0 == r1
|
||||
# execution continues here
|
||||
|
||||
* ite(<condition>) If then else
|
||||
|
||||
If <condtion> is true, execute the next instruction, otherwise execute the
|
||||
subsequent one. Thus:
|
||||
|
||||
::
|
||||
|
||||
cmp(r0, r1)
|
||||
ite(eq)
|
||||
mov(r0, 100) # runs if r0 == r1
|
||||
mov(r0, 200) # runs if r0 != r1
|
||||
# execution continues here
|
||||
|
||||
This may be extended to control the execution of upto four subsequent instructions: it[x[y[z]]]
|
||||
where x,y,z=t/e; e.g. itt, itee, itete, ittte, itttt, iteee, etc.
|
|
@ -0,0 +1,36 @@
|
|||
Assembler Directives
|
||||
====================
|
||||
|
||||
Labels
|
||||
------
|
||||
|
||||
* label(INNER1)
|
||||
|
||||
This defines a label for use in a branch instruction. Thus elsewhere in the code a ``b(INNER1)``
|
||||
will cause execution to continue with the instruction after the label directive.
|
||||
|
||||
Defining inline data
|
||||
--------------------
|
||||
|
||||
The following assembler directives facilitate embedding data in an assembler code block.
|
||||
|
||||
* data(size, d0, d1 .. dn)
|
||||
|
||||
The data directive creates n array of data values in memory. The first argument specifies the
|
||||
size in bytes of the subsequent arguments. Hence the first statement below will cause the
|
||||
assembler to put three bytes (with values 2, 3 and 4) into consecutive memory locations
|
||||
while the second will cause it to emit two four byte words.
|
||||
|
||||
::
|
||||
|
||||
data(1, 2, 3, 4)
|
||||
data(4, 2, 100000)
|
||||
|
||||
Data values longer than a single byte are stored in memory in little-endian format.
|
||||
|
||||
* align(nBytes)
|
||||
|
||||
Align the following instruction to an nBytes value. ARM Thumb-2 instructions must be two
|
||||
byte aligned, hence it's advisable to issue ``align(2)`` after ``data`` directives and
|
||||
prior to any subsequent code. This ensures that the code will run irrespective of the
|
||||
size of the data array.
|
|
@ -0,0 +1,77 @@
|
|||
Floating Point instructions
|
||||
==============================
|
||||
|
||||
These instructions support the use of the ARM floating point coprocessor
|
||||
(on platforms such as the Pyboard which are equipped with one). The FPU
|
||||
has 32 registers known as ``s0-s31`` each of which can hold a single
|
||||
precision float. Data can be passed between the FPU registers and the
|
||||
ARM core registers with the ``vmov`` instruction.
|
||||
|
||||
Note that MicroPython doesn't support passing floats to
|
||||
assembler functions, nor can you put a float into ``r0`` and expect a
|
||||
reasonable result. There are two ways to overcome this. The first is to
|
||||
use arrays, and the second is to pass and/or return integers and convert
|
||||
to and from floats in code.
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Notation: ``Sd, Sm, Sn`` denote FPU registers, ``Rd, Rm, Rn`` denote ARM core
|
||||
registers. The latter can be any ARM core register although registers
|
||||
``R13-R15`` are unlikely to be appropriate in this context.
|
||||
|
||||
Arithmetic
|
||||
----------
|
||||
|
||||
* vadd(Sd, Sn, Sm) ``Sd = Sn + Sm``
|
||||
* vsub(Sd, Sn, Sm) ``Sd = Sn - Sm``
|
||||
* vneg(Sd, Sm) ``Sd = -Sm``
|
||||
* vmul(Sd, Sn, Sm) ``Sd = Sn * Sm``
|
||||
* vdiv(Sd, Sn, Sm) ``Sd = Sn / Sm``
|
||||
* vsqrt(Sd, Sm) ``Sd = sqrt(Sm)``
|
||||
|
||||
Registers may be identical: ``vmul(S0, S0, S0)`` will execute ``S0 = S0*S0``
|
||||
|
||||
Move between ARM core and FPU registers
|
||||
---------------------------------------
|
||||
|
||||
* vmov(Sd, Rm) ``Sd = Rm``
|
||||
* vmov(Rd, Sm) ``Rd = Sm``
|
||||
|
||||
The FPU has a register known as FPSCR, similar to the ARM core's APSR, which stores condition
|
||||
codes plus other data. The following instructions provide access to this.
|
||||
|
||||
* vmrs(APSR\_nzcv, FPSCR)
|
||||
|
||||
Move the floating-point N, Z, C, and V flags to the APSR N, Z, C, and V flags.
|
||||
|
||||
This is done after an instruction such as an FPU
|
||||
comparison to enable the condition codes to be tested by the assembler
|
||||
code. The following is a more general form of the instruction.
|
||||
|
||||
* vmrs(Rd, FPSCR) ``Rd = FPSCR``
|
||||
|
||||
Move between FPU register and memory
|
||||
------------------------------------
|
||||
|
||||
* vldr(Sd, [Rn, offset]) ``Sd = [Rn + offset]``
|
||||
* vstr(Sd, [Rn, offset]) ``[Rn + offset] = Sd``
|
||||
|
||||
Where ``[Rn + offset]`` denotes the memory address obtained by adding Rn to the offset. This
|
||||
is specified in bytes. Since each float value occupies a 32 bit word, when accessing arrays of
|
||||
floats the offset must always be a multiple of four bytes.
|
||||
|
||||
Data Comparison
|
||||
---------------
|
||||
|
||||
* vcmp(Sd, Sm)
|
||||
|
||||
Compare the values in Sd and Sm and set the FPU N, Z,
|
||||
C, and V flags. This would normally be followed by ``vmrs(APSR_nzcv, FPSCR)``
|
||||
to enable the results to be tested.
|
||||
|
||||
Convert between integer and float
|
||||
---------------------------------
|
||||
|
||||
* vcvt\_f32\_s32(Sd, Sm) ``Sd = float(Sm)``
|
||||
* vcvt\_s32\_f32(Sd, Sm) ``Sd = int(Sm)``
|
|
@ -0,0 +1,232 @@
|
|||
Hints and tips
|
||||
==============
|
||||
|
||||
The following are some examples of the use of the inline assembler and some
|
||||
information on how to work around its limitations. In this document the term
|
||||
"assembler function" refers to a function declared in Python with the
|
||||
``@micropython.asm_thumb`` decorator, whereas "subroutine" refers to assembler
|
||||
code called from within an assembler function.
|
||||
|
||||
Code branches and subroutines
|
||||
-----------------------------
|
||||
|
||||
It is important to appreciate that labels are local to an assembler function.
|
||||
There is currently no way for a subroutine defined in one function to be called
|
||||
from another.
|
||||
|
||||
To call a subroutine the instruction ``bl(LABEL)`` is issued. This transfers
|
||||
control to the instruction following the ``label(LABEL)`` directive and stores
|
||||
the return address in the link register (``lr`` or ``r14``). To return the
|
||||
instruction ``bx(lr)`` is issued which causes execution to continue with
|
||||
the instruction following the subroutine call. This mechanism implies that, if
|
||||
a subroutine is to call another, it must save the link register prior to
|
||||
the call and restore it before terminating.
|
||||
|
||||
The following rather contrived example illustrates a function call. Note that
|
||||
it's necessary at the start to branch around all subroutine calls: subroutines
|
||||
end execution with ``bx(lr)`` while the outer function simply "drops off the end"
|
||||
in the style of Python functions.
|
||||
|
||||
::
|
||||
|
||||
@micropython.asm_thumb
|
||||
def quad(r0):
|
||||
b(START)
|
||||
label(DOUBLE)
|
||||
add(r0, r0, r0)
|
||||
bx(lr)
|
||||
label(START)
|
||||
bl(DOUBLE)
|
||||
bl(DOUBLE)
|
||||
|
||||
print(quad(10))
|
||||
|
||||
The following code example demonstrates a nested (recursive) call: the classic
|
||||
Fibonacci sequence. Here, prior to a recursive call, the link register is saved
|
||||
along with other registers which the program logic requires to be preserved.
|
||||
|
||||
::
|
||||
|
||||
@micropython.asm_thumb
|
||||
def fib(r0):
|
||||
b(START)
|
||||
label(DOFIB)
|
||||
push({r1, r2, lr})
|
||||
cmp(r0, 1)
|
||||
ble(FIBDONE)
|
||||
sub(r0, 1)
|
||||
mov(r2, r0) # r2 = n -1
|
||||
bl(DOFIB)
|
||||
mov(r1, r0) # r1 = fib(n -1)
|
||||
sub(r0, r2, 1)
|
||||
bl(DOFIB) # r0 = fib(n -2)
|
||||
add(r0, r0, r1)
|
||||
label(FIBDONE)
|
||||
pop({r1, r2, lr})
|
||||
bx(lr)
|
||||
label(START)
|
||||
bl(DOFIB)
|
||||
|
||||
for n in range(10):
|
||||
print(fib(n))
|
||||
|
||||
Argument passing and return
|
||||
---------------------------
|
||||
|
||||
The tutorial details the fact that assembler functions can support from zero to
|
||||
three arguments, which must (if used) be named ``r0``, ``r1`` and ``r2``. When
|
||||
the code executes the registers will be initialised to those values.
|
||||
|
||||
The data types which can be passed in this way are integers and memory
|
||||
addresses. Further, integers are restricted in that the top two bits
|
||||
must be identical, limiting the range to -2**30 to 2**30 -1. Return
|
||||
values are similarly limited. These limitations can be overcome by means
|
||||
of the ``array`` module to allow any number of values of any type to
|
||||
be accessed.
|
||||
|
||||
Multiple arguments
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a Python array of integers is passed as an argument to an assembler
|
||||
function, the function will receive the address of a contiguous set of integers.
|
||||
Thus multiple arguments can be passed as elements of a single array. Similarly a
|
||||
function can return multiple values by assigning them to array elements.
|
||||
Assembler functions have no means of determining the length of an array:
|
||||
this will need to be passed to the function.
|
||||
|
||||
This use of arrays can be extended to enable more than three arrays to be used.
|
||||
This is done using indirection: the ``uctypes`` module supports ``addressof()``
|
||||
which will return the address of an array passed as its argument. Thus you can
|
||||
populate an integer array with the addresses of other arrays:
|
||||
|
||||
::
|
||||
|
||||
from uctypes import addressof
|
||||
@micropython.asm_thumb
|
||||
def getindirect(r0):
|
||||
ldr(r0, [r0, 0]) # Address of array loaded from passed array
|
||||
ldr(r0, [r0, 4]) # Return element 1 of indirect array (24)
|
||||
|
||||
def testindirect():
|
||||
a = array.array('i',[23, 24])
|
||||
b = array.array('i',[0,0])
|
||||
b[0] = addressof(a)
|
||||
print(getindirect(b))
|
||||
|
||||
Non-integer data types
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These may be handled by means of arrays of the appropriate data type. For
|
||||
example, single precison floating point data may be processed as follows.
|
||||
This code example takes an array of floats and replaces its contents with
|
||||
their squares.
|
||||
|
||||
::
|
||||
|
||||
from array import array
|
||||
|
||||
@micropython.asm_thumb
|
||||
def square(r0, r1):
|
||||
label(LOOP)
|
||||
vldr(s0, [r0, 0])
|
||||
vmul(s0, s0, s0)
|
||||
vstr(s0, [r0, 0])
|
||||
add(r0, 4)
|
||||
sub(r1, 1)
|
||||
bgt(LOOP)
|
||||
|
||||
a = array('f', (x for x in range(10)))
|
||||
square(a, len(a))
|
||||
print(a)
|
||||
|
||||
The uctypes module supports the use of data structures beyond simple
|
||||
arrays. It enables a Python data structure to be mapped onto a bytearray
|
||||
instance which may then be passed to the assembler function.
|
||||
|
||||
Named constants
|
||||
---------------
|
||||
|
||||
Assembler code may be made more readable and maintainable by using named
|
||||
constants rather than littering code with numbers. This may be achieved
|
||||
thus:
|
||||
|
||||
::
|
||||
|
||||
MYDATA = const(33)
|
||||
|
||||
@micropython.asm_thumb
|
||||
def foo():
|
||||
mov(r0, MYDATA)
|
||||
|
||||
The const() construct causes MicroPython to replace the variable name
|
||||
with its value at compile time. If constants are declared in an outer
|
||||
Python scope they can be shared between mutiple assembler functions and
|
||||
with Python code.
|
||||
|
||||
Assembler code as class methods
|
||||
-------------------------------
|
||||
|
||||
MicroPython passes the address of the object instance as the first argument
|
||||
to class methods. This is normally of little use to an assembler function.
|
||||
It can be avoided by declaring the function as a static method thus:
|
||||
|
||||
::
|
||||
|
||||
class foo:
|
||||
@staticmethod
|
||||
@micropython.asm_thumb
|
||||
def bar(r0):
|
||||
add(r0, r0, r0)
|
||||
|
||||
Use of unsupported instructions
|
||||
-------------------------------
|
||||
|
||||
These can be coded using the data statement as shown below. While
|
||||
``push()`` and ``pop()`` are supported the example below illustrates the
|
||||
principle. The necessary machine code may be found in the ARM v7-M
|
||||
Architecture Reference Manual. Note that the first argument of data
|
||||
calls such as
|
||||
|
||||
::
|
||||
|
||||
data(2, 0xe92d, 0x0f00) # push r8,r9,r10,r11
|
||||
|
||||
indicates that each subsequent argument is a two byte quantity.
|
||||
|
||||
Overcoming MicroPython's integer restriction
|
||||
--------------------------------------------
|
||||
|
||||
The Pyboard chip includes a CRC generator. Its use presents a problem in
|
||||
MicroPython because the returned values cover the full gamut of 32 bit
|
||||
quantities whereas small integers in MicroPython cannot have differing values
|
||||
in bits 30 and 31. This limitation is overcome with the following code, which
|
||||
uses assembler to put the result into an array and Python code to
|
||||
coerce the result into an arbitrary precision unsigned integer.
|
||||
|
||||
::
|
||||
|
||||
from array import array
|
||||
import stm
|
||||
|
||||
def enable_crc():
|
||||
stm.mem32[stm.RCC + stm.RCC_AHB1ENR] |= 0x1000
|
||||
|
||||
def reset_crc():
|
||||
stm.mem32[stm.CRC+stm.CRC_CR] = 1
|
||||
|
||||
@micropython.asm_thumb
|
||||
def getval(r0, r1):
|
||||
movwt(r3, stm.CRC + stm.CRC_DR)
|
||||
str(r1, [r3, 0])
|
||||
ldr(r2, [r3, 0])
|
||||
str(r2, [r0, 0])
|
||||
|
||||
def getcrc(value):
|
||||
a = array('i', [0])
|
||||
getval(a, value)
|
||||
return a[0] & 0xffffffff # coerce to arbitrary precision
|
||||
|
||||
enable_crc()
|
||||
reset_crc()
|
||||
for x in range(20):
|
||||
print(hex(getcrc(0)))
|
|
@ -0,0 +1,73 @@
|
|||
.. _asm_thumb2_index:
|
||||
|
||||
Inline Assembler for Thumb2 architectures
|
||||
=========================================
|
||||
|
||||
This document assumes some familiarity with assembly language programming and should be read after studying
|
||||
the :ref:`tutorial <pyboard_tutorial_assembler>`. For a detailed description of the instruction set consult the
|
||||
Architecture Reference Manual detailed below.
|
||||
The inline assembler supports a subset of the ARM Thumb-2 instruction set described here. 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.
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Where possible the behaviour of each instruction is described in Python, for example
|
||||
|
||||
* add(Rd, Rn, Rm) ``Rd = Rn + Rm``
|
||||
|
||||
This enables the effect of instructions to be demonstrated in Python. In certain case this is impossible
|
||||
because Python doesn't support concepts such as indirection. The pseudocode employed in such cases is
|
||||
described on the relevant page.
|
||||
|
||||
Instruction Categories
|
||||
----------------------
|
||||
|
||||
The following sections details the subset of the ARM Thumb-2 instruction set supported by MicroPython.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:numbered:
|
||||
|
||||
asm_thumb2_mov.rst
|
||||
asm_thumb2_ldr.rst
|
||||
asm_thumb2_str.rst
|
||||
asm_thumb2_logical_bit.rst
|
||||
asm_thumb2_arith.rst
|
||||
asm_thumb2_compare.rst
|
||||
asm_thumb2_label_branch.rst
|
||||
asm_thumb2_stack.rst
|
||||
asm_thumb2_misc.rst
|
||||
asm_thumb2_float.rst
|
||||
asm_thumb2_directives.rst
|
||||
|
||||
Usage examples
|
||||
--------------
|
||||
|
||||
These sections provide further code examples and hints on the use of the assembler.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:numbered:
|
||||
|
||||
asm_thumb2_hints_tips.rst
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
- :ref:`Assembler Tutorial <pyboard_tutorial_assembler>`
|
||||
- `Wiki hints and tips
|
||||
<http://wiki.micropython.org/platforms/boards/pyboard/assembler>`__
|
||||
- `uPy Inline Assembler source-code,
|
||||
emitinlinethumb.c <https://github.com/micropython/micropython/blob/master/py/emitinlinethumb.c>`__
|
||||
- `ARM Thumb2 Instruction Set Quick Reference
|
||||
Card <http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf>`__
|
||||
- `RM0090 Reference
|
||||
Manual <http://www.google.ae/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&sqi=2&ved=0CBoQFjAA&url=http%3A%2F%2Fwww.st.com%2Fst-web-ui%2Fstatic%2Factive%2Fen%2Fresource%2Ftechnical%2Fdocument%2Freference_manual%2FDM00031020.pdf&ei=G0rSU66xFeuW0QWYwoD4CQ&usg=AFQjCNFuW6TgzE4QpahO_U7g3f3wdwecAg&sig2=iET-R0y9on_Pbflzf9aYDw&bvm=bv.71778758,bs.1,d.bGQ>`__
|
||||
- ARM v7-M Architecture Reference Manual (Available on the
|
||||
ARM site after a simple registration procedure. Also available on academic sites but beware of out of date versions.)
|
|
@ -0,0 +1,85 @@
|
|||
Branch instructions
|
||||
===================
|
||||
|
||||
These cause execution to jump to a target location usually specified by a label (see the ``label``
|
||||
assembler directive). Conditional branches and the ``it`` and ``ite`` instructions test
|
||||
the Application Program Status Register (APSR) N (negative), Z (zero), C (carry) and V
|
||||
(overflow) flags to determine whether the branch should be executed.
|
||||
|
||||
Most of the exposed assembler instructions (including move operations) set the flags but
|
||||
there are explicit comparison instructions to enable values to be tested.
|
||||
|
||||
Further detail on the meaning of the condition flags is provided in the section
|
||||
describing comparison functions.
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Notation: ``Rm`` denotes ARM registers R0-R15. ``LABEL`` denotes a label defined with the
|
||||
``label()`` assembler directive. ``<condition>`` indicates one of the following condition
|
||||
specifiers:
|
||||
|
||||
* eq Equal to (result was zero)
|
||||
* ne Not equal
|
||||
* cs Carry set
|
||||
* cc Carry clear
|
||||
* mi Minus (negaive)
|
||||
* pl Plus (positive)
|
||||
* vs Overflow set
|
||||
* vc Overflow clear
|
||||
* hi > (unsigned comparison)
|
||||
* ls <= (unsigned comparison)
|
||||
* ge >= (signed comparison)
|
||||
* lt < (signed comparison)
|
||||
* gt > (signed comparison)
|
||||
* le <= (signed comparison)
|
||||
|
||||
Branch to label
|
||||
---------------
|
||||
|
||||
* 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)
|
||||
|
||||
Long branches
|
||||
-------------
|
||||
|
||||
The code produced by the branch instructions listed above uses a fixed bit width to specify the
|
||||
branch destination, which is PC relative. Consequently in long programs where the
|
||||
branch instruction is remote from its destination the assembler will produce a "branch not in
|
||||
range" error. This can be overcome with the "wide" variants such as
|
||||
|
||||
* beq\_w(LABEL) long branch if equal
|
||||
|
||||
Wide branches use 4 bytes to encode the instruction (compared with 2 bytes for standard branch instructions).
|
||||
|
||||
Subroutines (functions)
|
||||
-----------------------
|
||||
|
||||
When entering a subroutine the processor stores the return address in register r14, also
|
||||
known as the link register (lr). Return to the instruction after the subroutine call is
|
||||
performed by updating the program counter (r15 or pc) from the link register, This
|
||||
process is handled by the following instructions.
|
||||
|
||||
* bl(LABEL)
|
||||
|
||||
Transfer execution to the instruction after ``LABEL`` storing the return address in
|
||||
the link register (r14).
|
||||
|
||||
* bx(Rm) Branch to address specified by Rm.
|
||||
|
||||
Typically ``bx(lr)`` is issued to return from a subroutine. For nested subroutines the
|
||||
link register of outer scopes must be saved (usually on the stack) before performing
|
||||
inner subroutine calls.
|
|
@ -0,0 +1,23 @@
|
|||
Load register from memory
|
||||
=========================
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Notation: ``Rt, Rn`` denote ARM registers R0-R7 except where stated. ``immN`` represents an immediate
|
||||
value having a width of N bits hence ``imm5`` is constrained to the range 0-31. ``[Rn + immN]`` is the contents
|
||||
of the memory address obtained by adding Rn and the offset ``immN``. Offsets are measured in
|
||||
bytes. These instructions affect the condition flags.
|
||||
|
||||
Register Load
|
||||
-------------
|
||||
|
||||
* 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.
|
||||
|
||||
The specified immediate offsets are measured in bytes. Hence in the case of ``ldr`` the 7 bit value
|
||||
enables 32 bit word aligned values to be accessed with a maximum offset of 31 words. In the case of ``ldrh`` the
|
||||
6 bit value enables 16 bit half-word aligned values to be accessed with a maximum offset of 31 half-words.
|
|
@ -0,0 +1,53 @@
|
|||
Logical & Bitwise instructions
|
||||
==============================
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Notation: ``Rd, Rn`` denote ARM registers R0-R7 except in the case of the
|
||||
special instructions where R0-R15 may be used. ``Rn<a-b>`` denotes an ARM register
|
||||
whose contents must lie in range ``a <= contents <= b``. In the case of instructions
|
||||
with two register arguments, it is permissible for them to be identical. For example
|
||||
the following will zero R0 (Python ``R0 ^= R0``) regardless of its initial contents.
|
||||
|
||||
* eor(r0, r0)
|
||||
|
||||
These instructions affect the condition flags except where stated.
|
||||
|
||||
Logical instructions
|
||||
--------------------
|
||||
|
||||
* 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.
|
||||
|
||||
Shift and rotation instructions
|
||||
-------------------------------
|
||||
|
||||
* 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``
|
||||
|
||||
Special instructions
|
||||
--------------------
|
||||
|
||||
Condition codes are unaffected by these instructions.
|
||||
|
||||
* clz(Rd, Rn) ``Rd = count_leading_zeros(Rn)``
|
||||
|
||||
count_leading_zeros(Rn) returns the number of binary zero bits before the first binary one bit in Rn.
|
||||
|
||||
* rbit(Rd, Rn) ``Rd = bit_reverse(Rn)``
|
||||
|
||||
bit_reverse(Rn) returns the bit-reversed contents of Rn. If Rn contains bits ``b31 b30..b0`` Rd will be set
|
||||
to ``b0 b1 b2..b31``
|
||||
|
||||
Trailing zeros may be counted by performing a bit reverse prior to executing clz.
|
|
@ -0,0 +1,10 @@
|
|||
Miscellaneous instructions
|
||||
==========================
|
||||
|
||||
* nop() ``pass`` no operation.
|
||||
* wfi() Suspend execution in a low power state until an interrupt occurs.
|
||||
* cpsid(flags) set the Priority Mask Register - disable interrupts.
|
||||
* cpsie(flags) clear the Priority Mask Register - enable interrupts.
|
||||
|
||||
Currently the ``cpsie()`` and ``cpsid()`` functions are partially implemented.
|
||||
They require but ignore the flags argument and serve as a means of enabling and disabling interrupts.
|
|
@ -0,0 +1,28 @@
|
|||
Register move instructions
|
||||
==========================
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Notation: ``Rd, Rn`` denote ARM registers R0-R15. ``immN`` denotes an immediate
|
||||
value having a width of N bits. These instructions affect the condition flags.
|
||||
|
||||
Register moves
|
||||
--------------
|
||||
|
||||
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, imm30) ``Rd = imm30``
|
||||
|
||||
movwt is a pseudo-instruction: the MicroPython assembler emits a ``movw`` and a ``movt``
|
||||
to move a zero extended 30 bit value into Rd. Where the full 32 bits are required a
|
||||
workround is to use the movw and movt operations.
|
|
@ -0,0 +1,20 @@
|
|||
Stack push and pop
|
||||
==================
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
The ``push()`` and ``pop()`` instructions accept as their argument a register set containing
|
||||
a subset, or possibly all, of the general-purpose registers R0-R12 and the link register (lr or R14).
|
||||
As with any Python set the order in which the registers are specified is immaterial. Thus the
|
||||
in the following example the pop() instruction would restore R1, R7 and R8 to their contents prior
|
||||
to the push():
|
||||
|
||||
* push({r1, r8, r7}) Save three registers on the stack.
|
||||
* pop({r7, r1, r8}) Restore them
|
||||
|
||||
Stack operations
|
||||
----------------
|
||||
|
||||
* push({regset}) Push a set of registers onto the stack
|
||||
* pop({regset}) Restore a set of registers from the stack
|
|
@ -0,0 +1,21 @@
|
|||
Store register to memory
|
||||
========================
|
||||
|
||||
Document conventions
|
||||
--------------------
|
||||
|
||||
Notation: ``Rt, Rn`` denote ARM registers R0-R7 except where stated. ``immN`` represents an immediate
|
||||
value having a width of N bits hence ``imm5`` is constrained to the range 0-31. ``[Rn + imm5]`` is the
|
||||
contents of the memory address obtained by adding Rn and the offset ``imm5``. Offsets are measured in
|
||||
bytes. These instructions do not affect the condition flags.
|
||||
|
||||
Register Store
|
||||
--------------
|
||||
|
||||
* 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)
|
||||
|
||||
The specified immediate offsets are measured in bytes. Hence in the case of ``str`` the 7 bit value
|
||||
enables 32 bit word aligned values to be accessed with a maximum offset of 31 words. In the case of ``strh`` the
|
||||
6 bit value enables 16 bit half-word aligned values to be accessed with a maximum offset of 31 half-words.
|
Loading…
Reference in New Issue