1#include "vm.h"
2#include <string.h>
3
4void vm_init(VM *vm, const uint8_t *code, size_t code_size, FILE *out) {
5 if (!vm) return;
6 vm->code = code;
7 vm->code_size = code_size;
8 vm->ip = 0;
9 vm->sp = 0;
10 vm->fp = 0;
11 memset(vm->stack, 0, sizeof(vm->stack));
12 memset(vm->call_stack, 0, sizeof(vm->call_stack));
13 memset(vm->current_locals, 0, sizeof(vm->current_locals));
14 vm->out = out ? out : stdout;
15 vm->halted = false;
16}
17
18VMResult vm_step(VM *vm) {
19 if (!vm) return VM_ERR_INVALID_OPCODE;
20 if (vm->halted) return VM_OK;
21 if (vm->ip >= vm->code_size) return VM_ERR_INVALID_JUMP;
22
23 uint8_t op = vm->code[vm->ip++];
24
25 switch (op) {
26 case OP_HALT:
27 vm->halted = true;
28 return VM_OK;
29
30 case OP_PUSH: {
31 if (vm->ip + 8 > vm->code_size) return VM_ERR_INVALID_OPCODE;
32 if (vm->sp >= VM_STACK_CAPACITY) return VM_ERR_STACK_OVERFLOW;
33 int64_t val;
34 memcpy(&val, &vm->code[vm->ip], 8);
35 vm->ip += 8;
36 vm->stack[vm->sp++] = val;
37 return VM_OK;
38 }
39
40 case OP_POP: {
41 if (vm->sp == 0) return VM_ERR_STACK_UNDERFLOW;
42 vm->sp--;
43 return VM_OK;
44 }
45
46 case OP_DUP: {
47 if (vm->sp == 0) return VM_ERR_STACK_UNDERFLOW;
48 if (vm->sp >= VM_STACK_CAPACITY) return VM_ERR_STACK_OVERFLOW;
49 vm->stack[vm->sp] = vm->stack[vm->sp - 1];
50 vm->sp++;
51 return VM_OK;
52 }
53
54 case OP_SWAP: {
55 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
56 int64_t tmp = vm->stack[vm->sp - 1];
57 vm->stack[vm->sp - 1] = vm->stack[vm->sp - 2];
58 vm->stack[vm->sp - 2] = tmp;
59 return VM_OK;
60 }
61
62 case OP_ADD: {
63 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
64 int64_t b = vm->stack[--vm->sp];
65 int64_t a = vm->stack[--vm->sp];
66 vm->stack[vm->sp++] = a + b;
67 return VM_OK;
68 }
69
70 case OP_SUB: {
71 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
72 int64_t b = vm->stack[--vm->sp];
73 int64_t a = vm->stack[--vm->sp];
74 vm->stack[vm->sp++] = a - b;
75 return VM_OK;
76 }
77
78 case OP_MUL: {
79 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
80 int64_t b = vm->stack[--vm->sp];
81 int64_t a = vm->stack[--vm->sp];
82 vm->stack[vm->sp++] = a * b;
83 return VM_OK;
84 }
85
86 case OP_DIV: {
87 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
88 int64_t b = vm->stack[--vm->sp];
89 int64_t a = vm->stack[--vm->sp];
90 if (b == 0) return VM_ERR_DIVISION_BY_ZERO;
91 if (a == INT64_MIN && b == -1) {
92 vm->stack[vm->sp++] = INT64_MIN;
93 } else {
94 vm->stack[vm->sp++] = a / b;
95 }
96 return VM_OK;
97 }
98
99 case OP_MOD: {
100 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
101 int64_t b = vm->stack[--vm->sp];
102 int64_t a = vm->stack[--vm->sp];
103 if (b == 0) return VM_ERR_DIVISION_BY_ZERO;
104 if (b == -1) {
105 vm->stack[vm->sp++] = 0;
106 } else {
107 vm->stack[vm->sp++] = a % b;
108 }
109 return VM_OK;
110 }
111
112 case OP_NEG: {
113 if (vm->sp < 1) return VM_ERR_STACK_UNDERFLOW;
114 vm->stack[vm->sp - 1] = -vm->stack[vm->sp - 1];
115 return VM_OK;
116 }
117
118 case OP_EQ: {
119 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
120 int64_t b = vm->stack[--vm->sp];
121 int64_t a = vm->stack[--vm->sp];
122 vm->stack[vm->sp++] = (a == b) ? 1 : 0;
123 return VM_OK;
124 }
125
126 case OP_LT: {
127 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
128 int64_t b = vm->stack[--vm->sp];
129 int64_t a = vm->stack[--vm->sp];
130 vm->stack[vm->sp++] = (a < b) ? 1 : 0;
131 return VM_OK;
132 }
133
134 case OP_GT: {
135 if (vm->sp < 2) return VM_ERR_STACK_UNDERFLOW;
136 int64_t b = vm->stack[--vm->sp];
137 int64_t a = vm->stack[--vm->sp];
138 vm->stack[vm->sp++] = (a > b) ? 1 : 0;
139 return VM_OK;
140 }
141
142 case OP_JMP: {
143 if (vm->ip + 4 > vm->code_size) return VM_ERR_INVALID_OPCODE;
144 uint32_t target;
145 memcpy(&target, &vm->code[vm->ip], 4);
146 if (target >= vm->code_size) return VM_ERR_INVALID_JUMP;
147 vm->ip = target;
148 return VM_OK;
149 }
150
151 case OP_JZ: {
152 if (vm->sp < 1) return VM_ERR_STACK_UNDERFLOW;
153 if (vm->ip + 4 > vm->code_size) return VM_ERR_INVALID_OPCODE;
154 uint32_t target;
155 memcpy(&target, &vm->code[vm->ip], 4);
156 int64_t cond = vm->stack[--vm->sp];
157 if (cond == 0) {
158 if (target >= vm->code_size) return VM_ERR_INVALID_JUMP;
159 vm->ip = target;
160 } else {
161 vm->ip += 4;
162 }
163 return VM_OK;
164 }
165
166 case OP_JNZ: {
167 if (vm->sp < 1) return VM_ERR_STACK_UNDERFLOW;
168 if (vm->ip + 4 > vm->code_size) return VM_ERR_INVALID_OPCODE;
169 uint32_t target;
170 memcpy(&target, &vm->code[vm->ip], 4);
171 int64_t cond = vm->stack[--vm->sp];
172 if (cond != 0) {
173 if (target >= vm->code_size) return VM_ERR_INVALID_JUMP;
174 vm->ip = target;
175 } else {
176 vm->ip += 4;
177 }
178 return VM_OK;
179 }
180
181 case OP_CALL: {
182 if (vm->ip + 4 > vm->code_size) return VM_ERR_INVALID_OPCODE;
183 if (vm->fp >= VM_CALL_STACK_CAPACITY) return VM_ERR_CALL_STACK_OVERFLOW;
184 uint32_t target;
185 memcpy(&target, &vm->code[vm->ip], 4);
186 if (target >= vm->code_size) return VM_ERR_INVALID_JUMP;
187
188 vm->call_stack[vm->fp].return_address = vm->ip + 4;
189 memcpy(vm->call_stack[vm->fp].locals, vm->current_locals, sizeof(vm->current_locals));
190 vm->fp++;
191 memset(vm->current_locals, 0, sizeof(vm->current_locals));
192 vm->ip = target;
193 return VM_OK;
194 }
195
196 case OP_RET: {
197 if (vm->fp == 0) return VM_ERR_CALL_STACK_UNDERFLOW;
198 vm->fp--;
199 vm->ip = vm->call_stack[vm->fp].return_address;
200 memcpy(vm->current_locals, vm->call_stack[vm->fp].locals, sizeof(vm->current_locals));
201 return VM_OK;
202 }
203
204 case OP_LOAD: {
205 if (vm->ip + 1 > vm->code_size) return VM_ERR_INVALID_OPCODE;
206 uint8_t idx = vm->code[vm->ip++];
207 if (idx >= VM_LOCALS_COUNT) return VM_ERR_INVALID_LOCAL_INDEX;
208 if (vm->sp >= VM_STACK_CAPACITY) return VM_ERR_STACK_OVERFLOW;
209 vm->stack[vm->sp++] = vm->current_locals[idx];
210 return VM_OK;
211 }
212
213 case OP_STORE: {
214 if (vm->ip + 1 > vm->code_size) return VM_ERR_INVALID_OPCODE;
215 uint8_t idx = vm->code[vm->ip++];
216 if (idx >= VM_LOCALS_COUNT) return VM_ERR_INVALID_LOCAL_INDEX;
217 if (vm->sp == 0) return VM_ERR_STACK_UNDERFLOW;
218 vm->current_locals[idx] = vm->stack[--vm->sp];
219 return VM_OK;
220 }
221
222 case OP_PRINT: {
223 if (vm->sp == 0) return VM_ERR_STACK_UNDERFLOW;
224 int64_t val = vm->stack[--vm->sp];
225 fprintf(vm->out, "%" PRId64 "\n", val);
226 return VM_OK;
227 }
228
229 default:
230 return VM_ERR_INVALID_OPCODE;
231 }
232}
233
234VMResult vm_run(VM *vm) {
235 if (!vm) return VM_ERR_INVALID_OPCODE;
236 while (!vm->halted) {
237 if (vm->ip >= vm->code_size) {
238 return VM_ERR_INVALID_JUMP;
239 }
240 VMResult res = vm_step(vm);
241 if (res != VM_OK) {
242 return res;
243 }
244 }
245 return VM_OK;
246}
247
248const char *vm_result_to_string(VMResult result) {
249 switch (result) {
250 case VM_OK: return "OK";
251 case VM_ERR_STACK_OVERFLOW: return "Stack overflow";
252 case VM_ERR_STACK_UNDERFLOW: return "Stack underflow";
253 case VM_ERR_CALL_STACK_OVERFLOW: return "Call stack overflow";
254 case VM_ERR_CALL_STACK_UNDERFLOW: return "Call stack underflow";
255 case VM_ERR_DIVISION_BY_ZERO: return "Division by zero";
256 case VM_ERR_INVALID_OPCODE: return "Invalid opcode";
257 case VM_ERR_INVALID_JUMP: return "Invalid jump target";
258 case VM_ERR_INVALID_LOCAL_INDEX: return "Invalid local variable index";
259 default: return "Unknown error";
260 }
261}
262
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.