Skip to content

Commit b7bc180

Browse files
committed
release 0.0.2
1 parent 9ed0cbe commit b7bc180

26 files changed

Lines changed: 739 additions & 1861 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
zig-cache
2-
zig-out
2+
zig-out
3+
.vscode

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
# What is AgarVM?
66

7-
Agar is a virtual machine. It comes with an assembly language.
7+
Agar is a register virtual machine. It comes with an assembly language.
88

99
You can test agar by writting assembly and running
1010

docs/developer/asm.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Assembler (asm.zig)
2+
3+
This file contains a struct that allows parsing strings into Agar bytecode. Label resolution is currently limited and only works with labels that are defined before the label usage. This will be fixed in the future.
4+
5+
Assembler uses a fairly standard patter with one exception. There is an additional step, you initialize it, then run the `.assembly_pass()` which creates the `instruction_buffer` which you can then copy elsewhere and finally deinitialize it.
6+
7+
The reason for the assembly pass step is that it is possible in the future to add more steps to the compilation.
8+
9+
The `.tokenize_*()` functions convert the strings into tokens, based on lines and spaces. There are no commas or anything like that, syntactic meaning is handled using just spaces and line breaks inside the assembly.
10+
11+
The `.parse_line()` function scraps lines beginning with `;`, if the first token ends with `:` then registers a label and otherwise tries to parse the line as a instruction. If the instruction is a pseudoinstruction then it is first expanded into a slice of instructions.
12+
13+
The `.label_to_offset()` function takes a label or a integer and converts it into a integer, if the label exists or is an integer. Any instruction can take an immediate in form of a label, they are mutually interchangeable.

docs/developer/binary.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Binary (binary.zig)
2+
3+
The original (and future) plan is to use the ELF binary format. But this turned out to be quite a complex task, so in the meantime Agar uses a very simple binary format. It has no checksum, but at least can check if the code has the correct version.
4+
5+
| start | number of bytes | meaning |
6+
| ----- | --------------- | ----------------------------------------------------------------------- |
7+
| 0 | 6 | the Agar magic number, it is the string "agarVM" in ASCII |
8+
| 6 | 2 | Agar version, it is changed each time the binary format is incompatible |
9+
| 8 | X | the Agar bytecode |

docs/developer/index.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# AgarVM architecture
2+
3+
*Agar consists of several modules. Each will be documented here.*
4+
## Table of contents
5+
### The bytecode architecture
6+
- [Instructions](./instructions.md)
7+
- [Registers](./registers.md)
8+
### The code architecture
9+
- [Virtual Machine](./vm.md)
10+
- [Page Manager](./page_manager.md)
11+
- [Assembler](./asm.md)
12+
- [Binary](./binary.md)

docs/developer/instructions.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Instructions
2+
3+
## Instruction format
4+
Instructions are modeled after the Risc-V architecture but with modifications. They are strictly 32 bits wide always saved in little endian.
5+
> Note that the format listed below is backwards from the real memory representation. The bit 0 is on the right and bit 31 is on the left. Same as we write numbers - MSB first.
6+
### the R-format for 3 operand instructions
7+
```
8+
|0|1|2 16|17 21|22 26|27 31|
9+
|0|0|xxxxxxxxxxxxxxx|xxxxx|xxxxx|xxxxx|
10+
|-|R|opcode |rd |rs1 |rs2 |
11+
```
12+
### the I-format for instructions with immediates
13+
```
14+
|0|1 9|10 14|15 19|20 31|
15+
|1|xxxxxxxxx|xxxxx|xxxxx|xxxxxxxxxxxx|
16+
|I|opcode |rd |rs1 |imm12 |
17+
```
18+
### the C-format for instructions with bigger immediates
19+
```
20+
|0|1|2 6|7 11|12 31|
21+
|0|1|xxxxx|xxxxx|xxxxxxxxxxxxxxxxxxxx|
22+
|-|C|opcod|rd |imm20 |
23+
```
24+
## Binary compatibility
25+
Opcode numbers *can* be changed between versions. To prevent malfunctioning code each `.agar` file has version of the instruction set written and should produce an error instead of running other versions (as described in the `AGAR_VM_VERSION` variable) of code.

docs/developer/page_manager.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Page Manager (page_manager.zig)
2+
3+
Memory is in Agar managed in pages. Each page is addressed by the 12 bit immediate. The only difference is that address inside a page is always between 0 and 4095, so there is no reason to use signed immediates and thus the immediate is casted to unsigned integer and used that way.
4+
5+
Page manager uses the zig feature of file structs. In reality it just means that the file itself is treated as a struct. If it hard to comprehend than imagine that you just wrap everything in the file inside `struct {}`.
6+
7+
## Initialization
8+
9+
The struct uses the usual zig `.init()` & `.deinit()` pattern.
10+
11+
## Runtime
12+
13+
There are only two functions in this struct. The one is for initializing new pages and the other is for releasing them. The pages are stored in a ArrayList and the id in which the struct identifies pages is the index in this array. There is no guarantee that the id is unique, usually pages are reused.
14+
15+
The handling of freed pages is that if they appear on the end of the array, they are released if they are in the middle of the array, they are marked as free but not released from the memory. This is done simply because this can assure that the access and all functions will be O(1) as rearranging arrays is O(n).
16+
17+
The free pages are stored into a singly linked list as it doesn't matter in which order they are reused and linked list allows for an easy O(1) LIFO queue.

docs/developer/registers.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Registers
2+
| register identifier | description |
3+
|---------------------|----------------------------------|
4+
| zero | register with the constant 0 |
5+
| ra | return address |
6+
| pp | page pointer |
7+
| t0-t6 | temporary registers |
8+
| s0-s11 | saved registers |
9+
| a0-a9 | function arguments/return values |
10+
11+
## The `zero` register
12+
Origin of this register is from the Risc-V architecture. Note that all registers are named only for user convenience. There is no guarantee that any register will have a value that it should have. **Not even the `zero` register!** You can assign a value to the `zero` register. But if you run any third-party code note that it can lead to undefined behavior.

docs/developer/vm.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# The Virtual Machine (vm.zig)
2+
3+
This file comprises of the actual execution engine. It has 32 64bit registers (this is a limitation due to register ids can only be stored in 5 bits). All instructions are saved in 32 bits. It has a few advantages, as near jump instructions (all pc modifying I-type instructions) can actually address a 4095 other addresses (2047 back and 2046 front).
4+
5+
All arithmetic is done on two's complement signed 64 bit integers if not stated otherwise.
6+
7+
## Initialization
8+
9+
The VM can be initialized in multiple ways. The standard way is to create a VM struct and then call the `.run()` function. It automatically initializes the virtual machine, and starts executing code.
10+
11+
```zig
12+
var vm = VM{
13+
.program = program[0..],
14+
};
15+
16+
vm.run(event_allocator, page_allocator);
17+
18+
// at the end of this scope, deinitialize all the virtual machine's memory.
19+
defer vm.deinit();
20+
```
21+
22+
The `.run()` function takes two allocators. These are responsible for the memory allocation of particular type (usually you can pass both the same allocator). The `event_allocator` is for the virtual machine events. These are messages that you can use to signal to the VM or the VM itself can use to signal to you.
23+
24+
The `page_allocator` is used to allocate memory inside the virtual machine, the `gp` and `fp` instructions use it to create and free pages. You should use a high performing allocator, it can be beneficial to use the `c_allocator` even though it can produce leaked memory and must be handled with caution.
25+
26+
The couple `.init()` and `.deinit()` are used to initialize and deinitialize structs inside the VM itself. It is a standard zig pattern. All heap allocated memory is freed in `.deinit()`.
27+
28+
## Runtime
29+
30+
`.next_instruction()` this function is a simple one. It returns the next instruction at position of the program counter (`pc`) and advances it by one.
31+
32+
`.exec_instruction()` this function executes one instruction on the VM. It can halt the VM or it should continue.
33+
34+
| returned value | meaning |
35+
| -------------- | ----------------------------------------------------------------------------------------- |
36+
| `VMError.*` | the VM encountered an error, you should handle it accordingly, the default is to halt |
37+
| `false` | the VM halted with the `HLT` instruction, the error code is written in the `VM.exit_code` |
38+
| `true` | the VM should continue it's execution |
39+
40+
There are some at first glance weird methods (starting with the `@` symbol) but that's just a type casting that is quite strict in zig and is very well defined contrary to C for example.

docs/instructions.org

Lines changed: 0 additions & 25 deletions
This file was deleted.

0 commit comments

Comments
 (0)