diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-19 11:08:28 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-19 11:08:28 +0200 |
commit | 06220393b4d3e9bdd227f333650883971e37b9f9 (patch) | |
tree | 57355861a2c93e0e6c9325d404ddd6ff12c0851f | |
parent | 27e6bf016e87702d30ebb7c0716ff3e4f08a8823 (diff) | |
download | bdl-06220393b4d3e9bdd227f333650883971e37b9f9.tar.gz bdl-06220393b4d3e9bdd227f333650883971e37b9f9.zip |
Add barebones stack vm
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | src/main.c | 128 |
2 files changed, 127 insertions, 3 deletions
@@ -7,7 +7,7 @@ BUILD_DIR := build | |||
7 | TESTS_DIR := tests | 7 | TESTS_DIR := tests |
8 | TEST_FILES := $(wildcard $(TESTS_DIR)/*.bad) | 8 | TEST_FILES := $(wildcard $(TESTS_DIR)/*.bad) |
9 | SRC_MAIN := $(SRC_DIR)/main.c | 9 | SRC_MAIN := $(SRC_DIR)/main.c |
10 | SRC_RUN := tests/loops.bad | 10 | SRC_RUN := tests/expressions.bad |
11 | WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") | 11 | WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") |
12 | INC_DIRS := $(shell find $(SRC_DIR) -type d) | 12 | INC_DIRS := $(shell find $(SRC_DIR) -type d) |
13 | INC_FLAGS := $(addprefix -I,$(INC_DIRS)) | 13 | INC_FLAGS := $(addprefix -I,$(INC_DIRS)) |
@@ -21,6 +21,104 @@ init(void) { | |||
21 | log_init_default(); | 21 | log_init_default(); |
22 | } | 22 | } |
23 | 23 | ||
24 | typedef struct Instruction { | ||
25 | u8 a; | ||
26 | u8 b; | ||
27 | u8 c; | ||
28 | u8 op; | ||
29 | } Instruction; | ||
30 | |||
31 | typedef union Constant { | ||
32 | s64 i; | ||
33 | u64 u; | ||
34 | double d; | ||
35 | ptrsize ptr; | ||
36 | } Constant; | ||
37 | |||
38 | typedef struct Chunk { | ||
39 | Instruction *code; | ||
40 | Constant *constants; | ||
41 | Str file_name; | ||
42 | // TODO: line/col info for debugging. | ||
43 | } Chunk; | ||
44 | |||
45 | typedef enum OpCode { | ||
46 | OP_HALT, | ||
47 | OP_ADD_INT, | ||
48 | } OpCode; | ||
49 | |||
50 | Str op_str[] = { | ||
51 | [OP_HALT] = cstr("HALT "), | ||
52 | [OP_ADD_INT] = cstr("ADD "), | ||
53 | }; | ||
54 | |||
55 | void | ||
56 | disassemble_instruction(Instruction instruction) { | ||
57 | switch (instruction.op) { | ||
58 | case OP_ADD_INT: | ||
59 | println("%s %d %d %d", op_str[instruction.op], instruction.a, | ||
60 | instruction.b, instruction.c); | ||
61 | break; | ||
62 | case OP_HALT: println("%s", op_str[instruction.op]); break; | ||
63 | default: println("Unknown opcode %d", instruction.op); break; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | void | ||
68 | disassemble_chunk(Chunk chunk) { | ||
69 | println("%s: ========= code =========", chunk.file_name); | ||
70 | for (sz i = 0; i < array_size(chunk.code); i++) { | ||
71 | print("%s: %x{4}: ", chunk.file_name, i); | ||
72 | disassemble_instruction(chunk.code[i]); | ||
73 | } | ||
74 | if (array_size(chunk.constants) > 0) { | ||
75 | println("%s: ======= constants ======", chunk.file_name); | ||
76 | for (sz i = 0; i < array_size(chunk.constants); i++) { | ||
77 | println("%s: %x{2}: %f", chunk.file_name, i, chunk.constants[i]); | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | typedef struct VM { | ||
83 | Chunk *chunk; | ||
84 | Constant regs[256]; | ||
85 | Instruction *ip; | ||
86 | } VM; | ||
87 | |||
88 | void | ||
89 | vm_init(VM *vm, Chunk *chunk) { | ||
90 | assert(vm); | ||
91 | assert(chunk); | ||
92 | vm->chunk = chunk; | ||
93 | for (sz i = 0; i < array_size(chunk->constants); i++) { | ||
94 | vm->regs[i] = chunk->constants[i]; | ||
95 | } | ||
96 | vm->ip = vm->chunk->code; | ||
97 | } | ||
98 | |||
99 | void | ||
100 | vm_run(VM *vm) { | ||
101 | println("VM running..."); | ||
102 | while (true) { | ||
103 | Instruction instruction = *vm->ip++; | ||
104 | #if DEBUG == 1 | ||
105 | print("IP: %d -> ", vm->ip - vm->chunk->code - 1); | ||
106 | disassemble_instruction(instruction); | ||
107 | #endif | ||
108 | switch (instruction.op) { | ||
109 | case OP_ADD_INT: { | ||
110 | u8 dst = instruction.a; | ||
111 | u8 src_a = instruction.b; | ||
112 | u8 src_b = instruction.c; | ||
113 | vm->regs[dst].u = vm->regs[src_a].u + vm->regs[src_b].u; | ||
114 | } break; | ||
115 | case OP_HALT: { | ||
116 | return; | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
24 | void | 122 | void |
25 | process_file(Str path) { | 123 | process_file(Str path) { |
26 | Arena lexer_arena = arena_create(LEXER_MEM, os_allocator); | 124 | Arena lexer_arena = arena_create(LEXER_MEM, os_allocator); |
@@ -73,6 +171,7 @@ process_file(Str path) { | |||
73 | break; | 171 | break; |
74 | } | 172 | } |
75 | } | 173 | } |
174 | parse_consume(&parser, TOK_EOF, cstr("expected end of file")); | ||
76 | if (parser.err) { | 175 | if (parser.err) { |
77 | exit(EXIT_FAILURE); | 176 | exit(EXIT_FAILURE); |
78 | } | 177 | } |
@@ -80,14 +179,39 @@ process_file(Str path) { | |||
80 | graph_ast(parser.nodes); | 179 | graph_ast(parser.nodes); |
81 | goto stop; | 180 | goto stop; |
82 | } | 181 | } |
83 | 182 | // TODO: Semantic analysis. | |
183 | // TODO: Type checking. | ||
184 | // TODO: Compile roots. | ||
84 | sz n_roots = array_size(parser.nodes); | 185 | sz n_roots = array_size(parser.nodes); |
85 | for (sz i = 0; i < n_roots; i++) { | 186 | for (sz i = 0; i < n_roots; i++) { |
86 | // The parser stores the root nodes as a stack. | 187 | // The parser stores the root nodes as a stack. |
87 | Node *root = parser.nodes[i]; | 188 | Node *root = parser.nodes[i]; |
189 | // compile_expr(Node *root) | ||
88 | (void)root; | 190 | (void)root; |
89 | } | 191 | } |
90 | parse_consume(&parser, TOK_EOF, cstr("expected end of file")); | 192 | |
193 | // Run bytecode on VM. | ||
194 | Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); | ||
195 | Chunk chunk = {.file_name = path}; | ||
196 | array_init(chunk.constants, 256, &bytecode_arena); | ||
197 | array_init(chunk.code, 0xffff, &bytecode_arena); | ||
198 | |||
199 | // FIXME: Some debugging instructions for now. | ||
200 | array_push(chunk.constants, (Constant){.u = 1}, &bytecode_arena); | ||
201 | array_push(chunk.constants, (Constant){.u = 2}, &bytecode_arena); | ||
202 | Instruction add = (Instruction){.op = OP_ADD_INT, .a = 2, .b = 0, .c = 1}; | ||
203 | array_push(chunk.code, add, &bytecode_arena); | ||
204 | array_push(chunk.code, (Instruction){.op = OP_HALT}, &bytecode_arena); | ||
205 | disassemble_chunk(chunk); | ||
206 | |||
207 | // Testing VM execution here. | ||
208 | VM vm = {0}; | ||
209 | vm_init(&vm, &chunk); | ||
210 | println("VM REGISTERS BEFORE:\n%{Mem}", | ||
211 | &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); | ||
212 | vm_run(&vm); | ||
213 | println("VM REGISTERS AFTER:\n%{Mem}", | ||
214 | &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); | ||
91 | 215 | ||
92 | stop: | 216 | stop: |
93 | // Free up resources. | 217 | // Free up resources. |