1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
# 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
```asm
label: instruction arg1, arg2, ... ; Comment until the end of the line
```
Labels and comments are optional.
A label is a name, refer to [names](#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](#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
```asm
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.
|