-
Notifications
You must be signed in to change notification settings - Fork 14
/
vm.c
278 lines (262 loc) · 7.73 KB
/
vm.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "vm.h"
#define MAX_GLOBALS 1024
#define STACK_SIZE 1024
#define FALSE 0
#define TRUE 1
#define SAFE_MODE 1
typedef struct {
char name[8];
int nargs;
} VM_INSTRUCTION;
VM_INSTRUCTION vm_instructions[] = {
{ "noop", 0 },
{ "iadd", 0 },
{ "isub", 0 },
{ "imul", 0 },
{ "ilt", 0 },
{ "ieq", 0 },
{ "ret", 0 },
{ "br", 1 },
{ "brt", 1 },
{ "brf", 1 },
{ "iconst", 1 },
{ "load", 1 },
{ "gload", 1 },
{ "store", 1 },
{ "gstore", 1 },
{ "print", 0 },
{ "pop", 0 },
{ "halt", 0 }
};
static void vm_print_instr(int *code, int ip);
static void vm_print_stack(int *stack, int count);
static void vm_print_data(int *globals, int count);
static void vm_panic(const char* format, ...);
void vm_exec(int *code, int count, int startip, int nglobals, int trace)
{
// registers
int ip = 0; // instruction pointer register
int sp = -1; // stack pointer register
int fp = -1; // frame pointer register
int opcode = code[ip];
int a = 0;
int b = 0;
int addr = 0;
int offset = 0;
// global variable space
int globals[MAX_GLOBALS];
// Operand stack, grows upwards
int stack[STACK_SIZE];
while (opcode != HALT && ip < count) {
if (trace) vm_print_instr(code, ip);
ip++; //jump to next instruction or to operand
switch (opcode) {
case IADD:
#ifdef SAFE_MODE
if (sp < 1) {
vm_panic("IADD stack underflow: %d\n", sp);
}
#endif
b = stack[sp--]; // 2nd opnd at top of stack
a = stack[sp--]; // 1st opnd 1 below top
stack[++sp] = a + b; // push result
break;
case ISUB:
#ifdef SAFE_MODE
if (sp < 1) {
vm_panic("ISUB stack underflow: %d\n", sp);
}
#endif
b = stack[sp--];
a = stack[sp--];
stack[++sp] = a - b;
break;
case IMUL:
#ifdef SAFE_MODE
if (sp < 1) {
vm_panic("IMUL stack underflow: %d\n", sp);
}
#endif
b = stack[sp--];
a = stack[sp--];
stack[++sp] = a * b;
break;
case ILT:
#ifdef SAFE_MODE
if (sp < 1) {
vm_panic("ILT stack underflow: %d\n", sp);
}
#endif
b = stack[sp--];
a = stack[sp--];
stack[++sp] = (a < b) ? TRUE : FALSE;
break;
case IEQ:
#ifdef SAFE_MODE
if (sp < 1) {
vm_panic("IEQ stack underflow: %d\n", sp);
}
#endif
b = stack[sp--];
a = stack[sp--];
stack[++sp] = (a == b) ? TRUE : FALSE;
break;
case BR:
#ifdef SAFE_MODE
if (ip >= count) {
vm_panic("BR target out of range: %d\n", ip);
}
#endif
ip = code[ip];
break;
case BRT:
#ifdef SAFE_MODE
if (ip + 1 >= count) {
vm_panic("BRT target out of range: %d\n", ip);
}
if (sp < 0) {
vm_panic("BRT stack underflow: %d\n", sp);
}
#endif
addr = code[ip++];
if (stack[sp--] == TRUE) ip = addr;
break;
case BRF:
#ifdef SAFE_MODE
if (ip + 1 >= count) {
vm_panic("BRF target out of range: %d\n", ip);
}
if (sp < 0) {
vm_panic("BRF stack underflow: %d\n", sp);
}
#endif
addr = code[ip++];
if (stack[sp--] == FALSE) ip = addr;
break;
case ICONST:
#ifdef SAFE_MODE
if (ip + 1 >= count) {
vm_panic("ICONST target out of range: %d\n", ip);
}
if (sp + 1 >= STACK_SIZE) {
vm_panic("ICONST stack overflow: %d\n", sp);
}
#endif
stack[++sp] = code[ip++]; // push operand
break;
case LOAD: // load local or arg; 1st local is fp+1, args are fp-3, fp-4, fp-5, ...
#ifdef SAFE_MODE
if (ip + 1 >= count) {
vm_panic("LOAD target out of range: %d\n", ip);
}
if (fp + offset < 0 || fp + offset >= STACK_SIZE) {
vm_panic("LOAD stack out of range: %d\n", fp + offset);
}
if (sp + 1 >= STACK_SIZE) {
vm_panic("LOAD destination stack overflow: %d\n", sp);
}
#endif
offset = code[ip++];
stack[++sp] = stack[fp+offset];
break;
case GLOAD: // load from global memory
#ifdef SAFE_MODE
if (ip + 1 >= count) {
vm_panic("GLOAD target out of range: %d\n", ip);
}
if (sp + 1 >= STACK_SIZE) {
vm_panic("GLOAD stack overflow: %d\n", sp);
}
#endif
addr = code[ip++];
stack[++sp] = globals[addr];
break;
case STORE:
#ifdef SAFE_MODE
if (ip + 1 >= count) {
vm_panic("STORE target out of range: %d\n", ip);
}
if (fp + offset < 0 || fp + offset >= STACK_SIZE) {
vm_panic("STORE stack out of range: %d\n", fp + offset);
}
#endif
offset = code[ip++];
stack[fp+offset] = stack[sp--];
break;
case GSTORE:
#ifdef SAFE_MODE
if (ip + 1 >= count) {
vm_panic("GSTORE target out of range: %d\n", ip);
}
if (sp < 0) {
vm_panic("GSTORE stack underflow: %d\n", sp);
}
#endif
addr = code[ip++];
globals[addr] = stack[sp--];
break;
case PRINT:
#ifdef SAFE_MODE
if (sp < 0) {
vm_panic("PRINT stack underflow: %d\n", sp);
}
#endif
printf("%d\n", stack[sp--]);
break;
case POP:
#ifdef SAFE_MODE
if (sp < 1) {
vm_panic("POP stack underflow: %d\n", sp);
}
#endif
--sp;
break;
default:
vm_panic("invalid opcode: %d at ip=%d\n", opcode, (ip - 1));
}
if (trace) vm_print_stack(stack, sp);
opcode = code[ip];
}
if (trace) vm_print_instr(code, ip);
if (trace) vm_print_stack(stack, sp);
if (trace) vm_print_data(globals, nglobals);
}
static void vm_print_instr(int *code, int ip)
{
int opcode = code[ip];
VM_INSTRUCTION *inst = &vm_instructions[opcode];
switch (inst->nargs) {
case 0:
printf("%04d: %-20s", ip, inst->name);
break;
case 1:
printf("%04d: %-10s%-10d", ip, inst->name, code[ip + 1]);
break;
}
}
static void vm_print_stack(int *stack, int count)
{
printf("stack=[");
for (int i = 0; i <= count; i++) {
printf(" %d", stack[i]);
}
printf(" ]\n");
}
static void vm_print_data(int *globals, int count)
{
printf("Data memory:\n");
for (int i = 0; i < count; i++) {
printf("%04d: %d\n", i, globals[i]);
}
}
static void vm_panic(const char* format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(1);
}