2 releases
0.7.2 | May 16, 2024 |
---|---|
0.7.1 | May 12, 2024 |
0.7.0 |
|
#542 in Hardware support
320KB
9K
SLoC
Trion
Trion is an assembler (with some disassembling capabilities) designed to be used with the Raspberry Pico (RP2040) microcontroller.
Running
You can run the executables in this package in one of 2 ways:
- First using
cargo install trion
(see also the--bin <name>
option in the cargo docs) and later run the installed version with justtrias <args>
ortridas <args>
. - Directly with
cargo run --bin <name>
wheretrias
is the assembler andtridas
the disassembler.
If you opt for the latter option, keep in mind that this documentation uses the former method for brevity. Installing also doesn't require the command to be run from within the package folder, but on the other hand requires you to manually reinstall after every version change and is thus unsuitable for development.
The package can also be used as a library (as used by the samples repo) and therefore does not have a default executable (you must specify one). When used as such, the main code which handles file reading, assembling and writing the output will be absent but the functionality responsible for assembly (including the parser, instructions and output format helper) are available to be used in different contexts.
Assembly Syntax
Trion's assembly syntax is mostly similar to that of other assemblers, with slight modifications to simplify the parsing step.
An assembly file (typically but not necessarily *.asm
) consists of a number of statements, which can be any of the following:
- Directives:
"." <name> [<argument> {"," <argument>}] ";"
- Labels:
<name> ":"
- Instructions
<name>["." <flags>] [<argument> {"," <argument>}] ";"
The number and types of arguments accepted depend on the directive or instruction used, but parsing may continue if they are wrong.
Arguments
Directives and instructions often take arguments that control their behavior. Atomic arguments are:
- Immediate: Integer values encoded in binary, octal, decimal or hexadecimal format in the source code.
The prefixes for binary, octal and hexadecimal are
"0b"
,"0o"
and"0x"
respectively. - Character: Effectively an immediate but formatted as a unicode code point delimited by single quotes.
- Identifier: Names that can refer to registers or constants.
Valid characters are
"A"
to"Z"
(case-insensitive) and"_"
, after the first"$"
,"."
,"@"
or"0"
to"9"
are also valid. - String: A sequence of unicode code points delimited by double quotes.
Further, trion supports a number of numeric operations (in order of highest precedence first):
- Negation
"-" <arg>
, Bitwise not"!" <arg>
- Multiplication
<lhs> "*" <rhs>
, Division<lhs> "/" <rhs>
, Modulo<lhs> "%" <rhs>
- Addition
<lhs> "+" <rhs>
, Subtraction<lhs> "-" <rhs>
- Left shift (logical)
<lhs> "<<" <rhs>
, Right shift (logical)<lhs> ">>" <rhs>
- Binary and
<lhs> & <rhs>
- Binary xor
<lhs> ^ <rhs>
- Binary or
<lhs> | <rhs>
Finally, there are a number of special operators:
- Address (
"[" <arg> "]"
): Indicates that the processor should access the memory location instead of the numeric value. - Sequence (
"{" [<argument> {, <argument>}] "}"
): A generic collection of arguments, typically a register set. - Function (
<name> "(" [<argument> {"," <argument>}] ")"
): A general-purpose to perform computations on values of any type.
As with regular mathematics, it is always valid to wrap any argument in parentheses to affect evaluation order. For numeric operations, though, all arguments must evaluate to a register or numeric value to be valid. In addition, it is an error for the result of a mathematical operation to overflow the constant's integer limit (64-bit signed).
Constants
Trion utilizes compile-time constants to enable simplification and source code reuse.
Constants must not have the same name as any register in the current instruction set.
Constants are signed 64-bit integers to comfortably encompass the RP2040's 32-bit address and register size.
Labels are themselves constants, with a value equivalent to their location, thus arbitrary numeric arguments can be used as branch/load targets.
Use of constants is dictated by scope: with the exception of importing/exporting, all directives and instructions use local constants.
It is possible to declare a constant after it is used (especially labels) but search won't escape to parent scope unless the constant is deferred.
Assembling
Usage: trias <input_file.asm> [<ouput_file.uf2>]
The assembler is used to turn human-readable assembly source code into a UF2 container recognized by the RP2040's startup routine.
As such, it is possible for the assembler to directly upload code onto the device via USB by choosing an output path within the device's drive.
The output file argument is optional however, if omitted the assembler will proceed as normal but will not save the final output.
This is intended for syntax validation or testing purposes.
Directives
".addr" <addr> ";"
: Changes the current output address toaddr
(where data and instructions are written to).".align" <alignment> ";"
: Aligns the current address toalignment
bytes. The empty space (if any) is filled with0xBE
(the opcode for theBKPT
instruction)".const" <name> "," <value> ";"
: Creates a local constant with the given name (identifier) and value.value
must evaluate to a numeric value containing no deferred constants.".du8" <value> ";"
,".du16" <value> ";"
,".du32" <value> ";"
: Outputs an unsigned integer value, which must be in the specified range.".dhex" <value> ";"
: Decodes a hexadecimal string and outputs the resulting bytes.".dstr" <value> ";"
: Outputs a string as-is (without padding, length prefix or terminal character).".dfile" <path> ";"
: Outputs the named file as-is, thepath
must be a string relative to the path of the current file.".global" <name> ";"
: Declares a deferred constantname
which is exported at the end of the local scope. It is valid for the constant to already exist locally, including if deferred. In the former case, it is exported immediately.".import" <name> ";"
: Imports the (possibly deferred) constantname
from the parent scope to the local scope.".export" <name> ";"
: Exports the existing, non-deferred constantname
from the local scope to the parent scope.".include" <path> ";"
: Assembles the file at the relativepath
(must be a string) and outputs the result at the current address. The file is assembled with its own local scope, but it can import/export constants from/to the current file's local scope.
Directives (and instructions) which can write data can only be used after an .addr
has been set.
The assembler does not require proper alignment, but the RP2040 will not be able to access data or execute instructions which aren't aligned.
Instructions
Trion currently supports only the ARMv6-M instruction set (which is used by the RP2040).
Instructions have the same names but, unlike ARM's UAL, require a terminal semicolon.
Flags are only supported (and in fact required) for the UDF
instruction (which has narrow and wide representations).
Disassembling
Usage: tridas <input_file.bin>
The disassembler is not the exact inverse of the assembler. For example, it requires as input a binary file containing only the opcodes, not a UF2 container.
Therefore, and because assembling strips label names, the disassembler will assume the binary is mounted at addess 0x2000000
(the start of SRAM on the
RP2040) and generate hexadecimal label names based on these addresses.
It currently only follows labels to other code, and won't generate any directives for relevant data items (such as with an immediate LDR
).