(Added Sections on Assertions and the output bit pattern) |
m (Fixed typos) |
||
Line 52: | Line 52: | ||
</pre> | </pre> | ||
The default block_comments value is <code>{"/*":"*/}</code>. | The default block_comments value is <code>{"/*":"*/}</code>. | ||
{{note|type=info|Any parenthesis style | {{note|type=info|Any parenthesis style can be used for the list syntax: <code>[]</code>, <code>()</code> or <code>{}</code>.}} | ||
{{note|type=warn|While the compiler respects the <code>line_comments</code> setting, the syntax highlighter in the assembler window currently does not - not even the default tokens.}} | {{note|type=warn|While the compiler respects the <code>line_comments</code> setting, the syntax highlighter in the assembler window currently does not - not even the default tokens.}} | ||
Line 93: | Line 93: | ||
=== Example 3 - aarch64, again === | === Example 3 - aarch64, again === | ||
A final example from the same <code>aarch64.isa</code>: | A final example from the same <code>aarch64.isa</code>: | ||
<pre> | |||
pound | pound | ||
"#" 0 | "#" 0 | ||
"" 0 | "" 0 | ||
</pre> | |||
The features this time are: | The features this time are: | ||
* <code>"literal"</code> syntax to require a literal string be included in your instructions. | * <code>"literal"</code> syntax to require a literal string be included in your instructions. | ||
Line 131: | Line 133: | ||
==== Whitespace ==== | ==== Whitespace ==== | ||
Whitespace can be either tabs or spaces. If a single whitespace character is used, the space will be optional in the resulting assembly language. If the whitespace character is instead followed by another space ({{note}} | Whitespace can be either tabs or spaces. If a single whitespace character is used, the space will be optional in the resulting assembly language. If the whitespace character is instead followed by another space ({{note}} Not a tab), the assembler will require whitespace between the tokens. For example, an instruction like <code>function ()</code> would match <code>function()</code> or <code>function ()</code>, while the instruction <code>function ()</code> (with two spaces) will only match the latter, enforcing the space between the word "function" and the following parenthesis. | ||
==== Literals ==== | ==== Literals ==== | ||
"<code> | "<code>div</code>" in the motivating example is a literal. The user must type this exactly in order for the instruction to be matched. Of particular note however is that the commas (<code>,</code>) are also literals. | ||
{{note|type=info|The digraph <code>%%</code> must be used if you wish to use a literal <code>%</code> symbol in your assembly syntax, as <code>%</code> is a special character in the ISA language itself.}} | {{note|type=info|The digraph <code>%%</code> must be used if you wish to use a literal <code>%</code> symbol in your assembly syntax, as <code>%</code> is a special character in the ISA language itself.}} | ||
{{note|type=warn|The game does not currently prevent you from using your line or block comment tokens as literals. However, they will be treated as comment tokens by the assembler and will prevent the instruction from functioning.}} | {{note|type=warn|The game does not currently prevent you from using your line or block comment tokens as literals. However, they will be treated as comment tokens by the assembler and will prevent the instruction from functioning.}} | ||
Line 200: | Line 202: | ||
The game also provides a handful of built-in functions for the construction of virtual operands: | The game also provides a handful of built-in functions for the construction of virtual operands: | ||
{| class="wikitable" | {| class="wikitable" | ||
! Function !! Description !! Example !! Result if %a = -30000, 16-bit | ! Function !! Description !! Example !! Result if %a = -30000 and %b = 27, 16-bit | ||
|- | |- | ||
| <code>asr</code> || arithmetic shift right (ASR) || <code>asr(%a, 7)</code> || -235 | | <code>asr</code> || arithmetic shift right (ASR) || <code>asr(%a, 7)</code> || -235 | ||
|- | |- | ||
| <code>log2</code> || base-2 logarithm if >0, -1 otherwise || <code>log2(%a)</code> || -1 | | <code>log2</code> || floor of the base-2 logarithm if >0, -1 otherwise || <code>log2(%a)</code> <br> <code>log2(%b)</code> || -1 <br> 4 | ||
|- | |- | ||
| <code>popcount</code> || number of <code>1</code>s in the base-2 representation || <code>popcount(%a)</code> || 6 ({{note}} | | <code>popcount</code> || number of <code>1</code>s in the base-2 representation || <code>popcount(%a)</code> || 6 ({{note}} Currently reports 54 as of 0.1354. Mask out high bits as needed for <64bit values.) | ||
|- | |- | ||
| <code>trailing_zeros</code> || number of <code>0</code>s after the rightmost <code>1</code> in the base-2 representation || <code>trailing_zeros(%a)</code> || 4 | | <code>trailing_zeros</code> || number of <code>0</code>s after the rightmost <code>1</code> in the base-2 representation || <code>trailing_zeros(%a)</code> || 4 | ||
Line 218: | Line 220: | ||
==== Assignment ==== | ==== Assignment ==== | ||
The virtual operand being | The virtual operand being created—on the left of the equals (<code>=</code>) symbol—are written using the format <code>%name:size</code>, similar to instruction definition operands but lacking the fields specification. | ||
==== Bit widths ==== | ==== Bit widths ==== | ||
Line 252: | Line 254: | ||
<pre> | <pre> | ||
% | %c = 0b110001 | ||
c 0 ccccc 0 | c 0 ccccc 0 | ||
</pre> | </pre> |
Latest revision as of 14:38, 22 January 2025
The ISA ("Instruction Set Architecture") specification is the language used to define assembly instructions in Turing Complete. ISA specifications are stored alongside schematics in the player's save folder, with the name spec.isa
.
An ISA specification file consists of several sections, delimited by section headers enclosed in square brackets ([]
).
Settings[edit | edit source]
The [settings]
section defines several configuration properties for your ISA:
name[edit | edit source]
name = "Architecture name" name = architecture_name
Names the architecture this ISA specifies. It can be either a string or an identifier.
variant[edit | edit source]
variant = "Architecture variant"
Additional text that can be used to describe the ISA.
endianness[edit | edit source]
Defines the byte order of the instructions.
endianness = big endianness = little
The default is big endian.
For example, if we have an instruction defined as 11110000 00001111
then:
big
endian will encode the instruction as240 15
.little
endian will encode the same instruction as15 240
.
(as shown in, for example, the ROM component)
line_comments[edit | edit source]
Specifies the tokens used to mark the start of a single-line comment. Single-line comments start at the given token and consume the remainder of the line.
line_comments = "#" line_comments = [";", "//"]
The default line_comments value is [";", "//"]
.
[]
, ()
or {}
.line_comments
setting, the syntax highlighter in the assembler window currently only recognizes the default tokens.block_comments[edit | edit source]
Specifies the tokens used to mark the start and end of a block comment. Block comments start at the first token and consume everything up to the final token. This includes line breaks as well as additional block comment start tokens (preventing block comments from being nested).
block_comments = {"/*":"*/"}
The default block_comments value is {"/*":"*/}
.
[]
, ()
or {}
.line_comments
setting, the syntax highlighter in the assembler window currently does not - not even the default tokens.Fields[edit | edit source]
The [fields]
section defines different types of fields that can be included in the instruction definitions, as well as what values they can have. Fields are separated by blank lines, and each field starts with a field name on the first line followed by one or more allowable field values. Values are bit patterns and all values within a field must have the same bit length.
Example 1 - Overture[edit | edit source]
The Overture specification provides a good starting example:
register r0 000 r1 001 r2 010 r3 011 r4 100 r5 101 in 110 out 110
Here we can see a couple of features:
- Names are arbitrary.
r0
throughr5
are considered registers only by convention, whilein
andout
follow an entirely different naming scheme within the sameregister
field. - Value repetition is allowed.
in
andout
both have the same value of110
. - Completion is not required.
111
is not assigned to any label.
Example 2 - aarch64[edit | edit source]
Also note that the field name itself is arbitrary. We can take an excerpt from https://github.com/Stuffe/isa_spec/blob/main/spec_lib/aarch64/aarch64.isa as an example:
condition_code eq 0000 ne 0001 cs 0010 hs 0010
(In this case, all 16 values are included in the ISA itself, and has been truncated here for the sake of brevity.)
We can see a couple of other features in this example:
- The field name is not
register
, as noted. - The field is four bits wide rather than three. In principle, the fields can be any width desired (greater than zero).
Example 3 - aarch64, again[edit | edit source]
A final example from the same aarch64.isa
:
pound "#" 0 "" 0
The features this time are:
"literal"
syntax to require a literal string be included in your instructions.""
to allow an empty string, effectively making this field optional.
In this instance, the pound
field is used for immediate values, allowing the player to write either #37
or just 37
to describe the immediate value 37.
Reserved field names[edit | edit source]
There are two reserved names:
label
references labels within the assembler (eg:mylabel:
).immediate
references immediate values (eg:37
).
Instructions[edit | edit source]
The [instructions]
section is where we finally define the instructions themselves. Instructions are separated by a blank line and each consists of several lines:
To motivate with an example, let's consider the following hypothetical DIV instruction that could be added to the Symphony architecture:
div %a(register), %b(register), %c:S16(immediate) %bits = (0 - popcount(%c)) !assert %bits >> 63 == 1 01011100 0aaa0bbb cccccccc cccccccc Signed DIV %b by %c and store the result in %a.
Assembly format[edit | edit source]
div %a(register), %b(register), %c:S16(immediate)
The assembly format is a string of whitespace-delimited tokens, which come in two flavors: literals and operands.
Whitespace[edit | edit source]
Whitespace can be either tabs or spaces. If a single whitespace character is used, the space will be optional in the resulting assembly language. If the whitespace character is instead followed by another space ( Note: Not a tab), the assembler will require whitespace between the tokens. For example, an instruction like function ()
would match function()
or function ()
, while the instruction function ()
(with two spaces) will only match the latter, enforcing the space between the word "function" and the following parenthesis.
Literals[edit | edit source]
"div
" in the motivating example is a literal. The user must type this exactly in order for the instruction to be matched. Of particular note however is that the commas (,
) are also literals.
%%
must be used if you wish to use a literal %
symbol in your assembly syntax, as %
is a special character in the ISA language itself.Operands[edit | edit source]
Operands start with the %
prefix and are written in the form %name:size(fields)
.
name
is any identifier. These are typically kept short for convenience such as%a
or%imm
, but in principle can be any length.size
is eitherS
orU
for signed and unsigned values, respectively, followed by a size in bits. For exampleS32
orU3
. The default isU64
, and the size cannot currently exceed 64 bits.fields
is a list of one or more fields created in the prior section (or the reserved fields). The list is delimited by the pipe (|
) character.
Virtual operands[edit | edit source]
%bits = 0 - popcount(%c)
In addition to the operands included in the instruction syntax, additional "virtual" operands can be created to simplify instruction creation or, as in this example, to provide some minimal compile-time correctness guarantees.
Operators[edit | edit source]
The game provides a limited set of operators for the construction of virtual operands:
Operator | Description | Example | Result if %a = -30000, 16-bit |
---|---|---|---|
+ |
addition | %a + 7 |
-29993 |
- |
subtraction | %a - 7 |
-20007 |
* |
multiplication | %a * 7 |
-13392 |
/ |
division | %a / 7 |
-4285 |
% |
modulo (remainder after division) | %a % 7 |
-5 |
& |
bitwise AND | %a & 7 |
0 |
| |
bitwise OR | %a | 7 |
-29993 |
^ |
bitwise XOR | %a ^ 7 |
-29993 |
<< |
logical shift left (LSL) | %a << 7 |
26624 |
>> |
logical shift right (LSR) | %a >> 7 |
277 |
A notable omission is the lack of unary operators. However, the two most common unary operations can be written using binary operators as follows:
Operation | Alternative |
---|---|
NOT %a | (-1 ^ %a)
|
-%a | (0 - %a)
|
Operator precedence[edit | edit source]
The game only defines three precedence levels:
Precedence | Operators |
---|---|
Parenthesis | ()
|
Multiplicative | * , / , %
|
Everything else | + , - , & , | , ^ , << , >>
|
Functions[edit | edit source]
The game also provides a handful of built-in functions for the construction of virtual operands:
Function | Description | Example | Result if %a = -30000 and %b = 27, 16-bit |
---|---|---|---|
asr |
arithmetic shift right (ASR) | asr(%a, 7) |
-235 |
log2 |
floor of the base-2 logarithm if >0, -1 otherwise | log2(%a) log2(%b) |
-1 4 |
popcount |
number of 1 s in the base-2 representation |
popcount(%a) |
6 ( Note: Currently reports 54 as of 0.1354. Mask out high bits as needed for <64bit values.) |
trailing_zeros |
number of 0 s after the rightmost 1 in the base-2 representation |
trailing_zeros(%a) |
4 |
Bit slicing[edit | edit source]
The expression evaluator also provides syntax for bit slicing variables. This is written as value[start:end]
where all three can be arbitrary expressions. start
and end
are inclusive indices into the bits of value, with the LSB being 0 and numbering being right-to-left. To make this a bit simpler to visualize, start has to be larger than end.
Operands[edit | edit source]
Virtual operands can refer to any operands from the instruction definition, as well as any virtual operands created on the preceding lines.
Assignment[edit | edit source]
The virtual operand being created—on the left of the equals (=
) symbol—are written using the format %name:size
, similar to instruction definition operands but lacking the fields specification.
Bit widths[edit | edit source]
Some operations (such as multiplication) will easily allow you to exceed the bit width of the virtual operand you're creating, or of the final output bytes. The game will cause an error when that occurs. Additionally, negative values are prone to being interpreted as 64-bit unsigned values (as 0f 0.1354 Beta) which can cause unexpected errors and odd-looking error messages. When in doubt, mask out the result of your expressions to ensure they fit within the intended bit width, especially when working with signed values.
Assertions[edit | edit source]
!assert %rem == 0
Assertions can be intermixed with Virtual Operands and trigger an error if they are not true. Currently the only supported comparison operator is equals (==
), although this is going to get extended in the future. After the comparison a string can be added to produce a more context-specific error message.
Output bit pattern[edit | edit source]
11 000 ccc
After all Virtual Operands and assertions comes the bit pattern. It defines a sequence of bits that get interpreted as a single big-endian number (which then gets rearranged according to the [[#endianness|endianness] setting). In this line space characters (and only space characters) are completely ignored.
Bits can be defined by either individual letters/numbers or explicit variable references.
Fixed patterns[edit | edit source]
0
and 1
produce those bits in the output and require those exact bits to be there while disassembling. ?
also produces 0
while assembling, but this bit will be ignored when disassembling (once that gets reimplemented).
Individual letters[edit | edit source]
All lowercase letters can be used as shortcuts to refer to variables. A letter to refers to the first variable that starts with that letter, with the order being left-to-right in the syntax line and then top-to-bottom in the virtual operands.
If a letter appears multiple times, all of those appearances are seen together ignore intermediate parts. The bits are assigned LSB-to-MSB to the repeated letters in a right-to-left ordering. For example,
%c = 0b110001 c 0 ccccc 0
would result in 10100010
.
Explicit Variable References[edit | edit source]
00 %imm[5:0]
In addition to individual letters, variables can also be referenced by their full name by using the %
prefix. In this case you also need to explicitly tell the assembler which bits of the variable to use using the Bit slicing syntax, with the restriction that both arguments need to be unsigned integer literals.