aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2024-06-19 11:08:28 +0200
committerBad Diode <bd@badd10de.dev>2024-06-19 11:08:28 +0200
commit06220393b4d3e9bdd227f333650883971e37b9f9 (patch)
tree57355861a2c93e0e6c9325d404ddd6ff12c0851f
parent27e6bf016e87702d30ebb7c0716ff3e4f08a8823 (diff)
downloadbdl-06220393b4d3e9bdd227f333650883971e37b9f9.tar.gz
bdl-06220393b4d3e9bdd227f333650883971e37b9f9.zip
Add barebones stack vm
-rw-r--r--Makefile2
-rw-r--r--src/main.c128
2 files changed, 127 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 732766e..dd3f3ae 100644
--- a/Makefile
+++ b/Makefile
@@ -7,7 +7,7 @@ BUILD_DIR := build
7TESTS_DIR := tests 7TESTS_DIR := tests
8TEST_FILES := $(wildcard $(TESTS_DIR)/*.bad) 8TEST_FILES := $(wildcard $(TESTS_DIR)/*.bad)
9SRC_MAIN := $(SRC_DIR)/main.c 9SRC_MAIN := $(SRC_DIR)/main.c
10SRC_RUN := tests/loops.bad 10SRC_RUN := tests/expressions.bad
11WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") 11WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h")
12INC_DIRS := $(shell find $(SRC_DIR) -type d) 12INC_DIRS := $(shell find $(SRC_DIR) -type d)
13INC_FLAGS := $(addprefix -I,$(INC_DIRS)) 13INC_FLAGS := $(addprefix -I,$(INC_DIRS))
diff --git a/src/main.c b/src/main.c
index 66142bd..a6e1cf6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -21,6 +21,104 @@ init(void) {
21 log_init_default(); 21 log_init_default();
22} 22}
23 23
24typedef struct Instruction {
25 u8 a;
26 u8 b;
27 u8 c;
28 u8 op;
29} Instruction;
30
31typedef union Constant {
32 s64 i;
33 u64 u;
34 double d;
35 ptrsize ptr;
36} Constant;
37
38typedef struct Chunk {
39 Instruction *code;
40 Constant *constants;
41 Str file_name;
42 // TODO: line/col info for debugging.
43} Chunk;
44
45typedef enum OpCode {
46 OP_HALT,
47 OP_ADD_INT,
48} OpCode;
49
50Str op_str[] = {
51 [OP_HALT] = cstr("HALT "),
52 [OP_ADD_INT] = cstr("ADD "),
53};
54
55void
56disassemble_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
67void
68disassemble_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
82typedef struct VM {
83 Chunk *chunk;
84 Constant regs[256];
85 Instruction *ip;
86} VM;
87
88void
89vm_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
99void
100vm_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
24void 122void
25process_file(Str path) { 123process_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
92stop: 216stop:
93 // Free up resources. 217 // Free up resources.