1#include "vm.h"
2
3#include <stdio.h>
4#include <string.h>
5
6static VMStatus fail(VM *vm, VMStatus status, const char *message) {
7 snprintf(vm->error, sizeof(vm->error), "%s", message);
8 return status;
9}
10
11void vm_init(VM *vm) {
12 memset(vm, 0, sizeof(*vm));
13}
14
15const char *vm_status_name(VMStatus status) {
16 switch (status) {
17 case VM_OK: return "ok";
18 case VM_ERR_STACK_UNDERFLOW: return "stack underflow";
19 case VM_ERR_STACK_OVERFLOW: return "stack overflow";
20 case VM_ERR_CALL_STACK_OVERFLOW: return "call stack overflow";
21 case VM_ERR_CALL_STACK_UNDERFLOW: return "call stack underflow";
22 case VM_ERR_DIV_ZERO: return "division by zero";
23 case VM_ERR_BAD_OPCODE: return "bad opcode";
24 case VM_ERR_BAD_JUMP: return "bad jump";
25 case VM_ERR_BAD_LOCAL: return "bad local";
26 case VM_ERR_NO_HALT: return "missing halt";
27 case VM_ERR_OUTPUT_OVERFLOW: return "output overflow";
28 }
29 return "unknown";
30}
31
32static int push(VM *vm, int value) {
33 if (vm->sp >= sizeof(vm->stack) / sizeof(vm->stack[0])) {
34 return 0;
35 }
36 vm->stack[vm->sp++] = value;
37 return 1;
38}
39
40static int pop(VM *vm, int *value) {
41 if (vm->sp == 0) {
42 return 0;
43 }
44 *value = vm->stack[--vm->sp];
45 return 1;
46}
47
48static int read_operand(const Program *program, size_t ip, int *value) {
49 if (ip >= program->count) {
50 return 0;
51 }
52 *value = program->code[ip];
53 return 1;
54}
55
56static int valid_target(const Program *program, int target) {
57 return target >= 0 && (size_t)target < program->count;
58}
59
60VMStatus vm_run(VM *vm, const Program *program) {
61 size_t ip = 0;
62
63 while (ip < program->count) {
64 int op = program->code[ip++];
65 int a;
66 int b;
67 int operand;
68
69 switch (op) {
70 case OP_PUSH:
71 if (!read_operand(program, ip++, &operand)) return fail(vm, VM_ERR_BAD_OPCODE, "PUSH missing operand");
72 if (!push(vm, operand)) return fail(vm, VM_ERR_STACK_OVERFLOW, "operand stack overflow");
73 break;
74 case OP_POP:
75 if (!pop(vm, &a)) return fail(vm, VM_ERR_STACK_UNDERFLOW, "POP needs one value");
76 break;
77 case OP_DUP:
78 if (vm->sp == 0) return fail(vm, VM_ERR_STACK_UNDERFLOW, "DUP needs one value");
79 if (!push(vm, vm->stack[vm->sp - 1])) return fail(vm, VM_ERR_STACK_OVERFLOW, "operand stack overflow");
80 break;
81 case OP_SWAP:
82 if (vm->sp < 2) return fail(vm, VM_ERR_STACK_UNDERFLOW, "SWAP needs two values");
83 a = vm->stack[vm->sp - 1];
84 vm->stack[vm->sp - 1] = vm->stack[vm->sp - 2];
85 vm->stack[vm->sp - 2] = a;
86 break;
87 case OP_ADD:
88 case OP_SUB:
89 case OP_MUL:
90 case OP_DIV:
91 case OP_MOD:
92 case OP_EQ:
93 case OP_LT:
94 case OP_GT:
95 if (!pop(vm, &b) || !pop(vm, &a)) return fail(vm, VM_ERR_STACK_UNDERFLOW, "binary op needs two values");
96 if (op == OP_DIV && b == 0) return fail(vm, VM_ERR_DIV_ZERO, "division by zero");
97 if (op == OP_MOD && b == 0) return fail(vm, VM_ERR_DIV_ZERO, "modulo by zero");
98 if (op == OP_ADD) operand = a + b;
99 else if (op == OP_SUB) operand = a - b;
100 else if (op == OP_MUL) operand = a * b;
101 else if (op == OP_DIV) operand = a / b;
102 else if (op == OP_MOD) operand = a % b;
103 else if (op == OP_EQ) operand = (a == b);
104 else if (op == OP_LT) operand = (a < b);
105 else operand = (a > b);
106 if (!push(vm, operand)) return fail(vm, VM_ERR_STACK_OVERFLOW, "operand stack overflow");
107 break;
108 case OP_NEG:
109 if (!pop(vm, &a)) return fail(vm, VM_ERR_STACK_UNDERFLOW, "NEG needs one value");
110 if (!push(vm, -a)) return fail(vm, VM_ERR_STACK_OVERFLOW, "operand stack overflow");
111 break;
112 case OP_JMP:
113 if (!read_operand(program, ip, &operand) || !valid_target(program, operand)) return fail(vm, VM_ERR_BAD_JUMP, "JMP target out of range");
114 ip = (size_t)operand;
115 break;
116 case OP_JZ:
117 case OP_JNZ:
118 if (!read_operand(program, ip++, &operand) || !valid_target(program, operand)) return fail(vm, VM_ERR_BAD_JUMP, "conditional jump target out of range");
119 if (!pop(vm, &a)) return fail(vm, VM_ERR_STACK_UNDERFLOW, "conditional jump needs one value");
120 if ((op == OP_JZ && a == 0) || (op == OP_JNZ && a != 0)) ip = (size_t)operand;
121 break;
122 case OP_CALL:
123 if (!read_operand(program, ip++, &operand) || !valid_target(program, operand)) return fail(vm, VM_ERR_BAD_JUMP, "CALL target out of range");
124 if (vm->csp >= sizeof(vm->call_stack) / sizeof(vm->call_stack[0]) - 1) return fail(vm, VM_ERR_CALL_STACK_OVERFLOW, "call stack overflow");
125 vm->call_stack[vm->csp++] = ip;
126 vm->frame++;
127 memset(vm->locals[vm->frame], 0, sizeof(vm->locals[vm->frame]));
128 ip = (size_t)operand;
129 break;
130 case OP_RET:
131 if (vm->csp == 0 || vm->frame == 0) return fail(vm, VM_ERR_CALL_STACK_UNDERFLOW, "RET without CALL");
132 vm->frame--;
133 ip = vm->call_stack[--vm->csp];
134 break;
135 case OP_LOAD:
136 if (!read_operand(program, ip++, &operand) || operand < 0 || operand >= 16) return fail(vm, VM_ERR_BAD_LOCAL, "LOAD local out of range");
137 if (!push(vm, vm->locals[vm->frame][operand])) return fail(vm, VM_ERR_STACK_OVERFLOW, "operand stack overflow");
138 break;
139 case OP_STORE:
140 if (!read_operand(program, ip++, &operand) || operand < 0 || operand >= 16) return fail(vm, VM_ERR_BAD_LOCAL, "STORE local out of range");
141 if (!pop(vm, &a)) return fail(vm, VM_ERR_STACK_UNDERFLOW, "STORE needs one value");
142 vm->locals[vm->frame][operand] = a;
143 break;
144 case OP_PRINT: {
145 int written;
146 if (!pop(vm, &a)) return fail(vm, VM_ERR_STACK_UNDERFLOW, "PRINT needs one value");
147 written = snprintf(vm->output + vm->output_len, sizeof(vm->output) - vm->output_len, "%d\n", a);
148 if (written < 0 || (size_t)written >= sizeof(vm->output) - vm->output_len) return fail(vm, VM_ERR_OUTPUT_OVERFLOW, "output buffer overflow");
149 vm->output_len += (size_t)written;
150 break;
151 }
152 case OP_HALT:
153 return VM_OK;
154 default:
155 return fail(vm, VM_ERR_BAD_OPCODE, "unknown opcode");
156 }
157 }
158
159 return fail(vm, VM_ERR_NO_HALT, "program ended without HALT");
160}
161
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.