| Name | Size | |
|---|---|---|
| .. | ||
| Cargo.toml | 133 B | log stats plain blame |
| README.md | 4.5 KiB | log stats plain blame |
| src | 100 B | log stats plain |
tonkobali assembler
This is a very limited assembler. Since we support only a single instruction (SUBLEQ), the assembler can also take in "higher-level" instructions which are then simplified (an operation which I call in the codebase as "collapse") to SUBLEQ.
The main goal is to use these higher-order instructions, not subleq itself.
Look at the examples directory for examples.
Text format
There are two types of lines in your source file: instruction calls and variable definitions. Blank lines are allowed.
Instruction calls
label: instruction arg1, arg2, ... ; Comment until the end of the line
Labels and comments are optional. A label is a name, refer to names for allowed characters.
You may separate a label and it's instruction by a newline and blank lines. You may issue multiple labels one after another, but only when separated by at least one newline (meaning you may not define two labels on the same line).
Instruction must be separated from their first argument with a blank (space or tab). Capitalisation is not ignored.
Arguments are values, with minor type limitations in certain higher-order instructions. They are seprated by commas with arbitrary amount of blank (space or tab) characters around the commas. All arguments must be on the same line as their instruction.
Variable definitions
name = value ; Comment until the end of the line
Comments are optional.
Names
A name is a sequence of ASCII letters (capital and lowercase), numbers, underscore, dot, $ and ?. Capitalization is not ignored.
There is no scoping of names, or in other words, every name is global. Labels are also names.
Repeat names overwrite previous ones globally, meaning no matter where two definitions are, the second will override the first everywhere in your program.
Values
All values are reduced to addresses in the memory (after all, all SUBLEQ arguments are addresses). We distinguish between three "types" of values:
- Absolute addresses, these are addresses in the memory and are passed down as-is
They are defined as unsigned numbers, prepended with the % symbol.
For example: %384.
- Number constants, they're placed somewhere in memory and their address is passed
They are signed 64-bit numbers (as is the Word type for our VM).
For example: 56, -912.
- Strings, they're converted into a sequence of numbers, each being the decimal ASCII representation of the character, ending with a zero. The address of the first "ltter" is passed
They are a sequence of characters, surrounded by double quotes.
Double quotes and backwards slash can be escaped by prepending a backwards slash.
For example: "Hello World", "He said \"Wow\"", "C:\\System".
Higher-order instructions
| Instruction | Description | Argument 1 | Argument 2 |
|---|---|---|---|
| jmp | Jump to address | Location to jump to | |
| inc | Increment value | Location of value | |
| dec | Decrement value | Location of value | |
| add | Add the first value to the second and store the result in the second | First value | Second value |
| sub | Subtract the first value to the second and store the result in the second | First value | Second value |
| mov | Move (copy) the value from the first address to the second address | First location | Second location |
| beq | Branch if first value is equal to zero | Value to check | Location to jump to |
| jsr | Jump to subroutine | Location to jump to | |
| rts | Return from subroutine |
The idea of subroutines is that jumping to one is the same as normal jump, however the address after the jump is stored. So, returning from a subroutine allows you to jump back after the location you entered the subroutine from. Addresses are stored in the memory via a "stack", in the first 1024 addresses.
