aboutsummaryrefslogtreecommitdiff
path: root/vm.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm.c')
-rw-r--r--vm.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/vm.c b/vm.c
new file mode 100644
index 0000000..cf76c03
--- /dev/null
+++ b/vm.c
@@ -0,0 +1,412 @@
+// synacor challenge (@waffle87)
+#include "vm.h"
+
+u16 rPC = 0;
+u16 r0 = 0;
+u16 r1 = 0;
+u16 r2 = 0;
+u16 r3 = 0;
+u16 r4 = 0;
+u16 r5 = 0;
+u16 r6 = 0;
+u16 r7 = 0;
+
+int parse(char const *filename) {
+ FILE *fptr = fopen(filename, "rb");
+ if (!fptr) {
+ fprintf(stderr, "error: opening file: %s\n", filename);
+ return EXIT_FAILURE;
+ }
+ if (fseek(fptr, 0, SEEK_END)) {
+ fprintf(stderr, "error: opening file: %s\n", filename);
+ fclose(fptr);
+ return EXIT_FAILURE;
+ }
+ long file_size = ftell(fptr);
+ if (file_size == EOF) {
+ fprintf(stderr, "error: opening file: %s\n", filename);
+ fclose(fptr);
+ return EXIT_FAILURE;
+ }
+ if (fseek(fptr, 0, SEEK_SET)) {
+ fprintf(stderr, "error: opening file: %s\n", filename);
+ fclose(fptr);
+ return EXIT_FAILURE;
+ }
+ size_t size_read = fread(m.mem, 1, file_size, fptr);
+ if (size_read != file_size) {
+ fprintf(stderr, "error: reading file: %s\n", filename);
+ fclose(fptr);
+ return EXIT_FAILURE;
+ }
+ int res;
+ u16 opcode;
+ u8 *buffer = m.mem;
+ rPC = 0;
+ while (1) {
+ get_next_pair(buffer + rPC, &opcode);
+ res = operate(opcode, buffer + rPC + 2);
+ if (res == -1)
+ break;
+ rPC = res;
+ }
+ fclose(fptr);
+ return EXIT_SUCCESS;
+}
+
+int operate(u16 opcode, unsigned char *buffer) {
+ instruction_ptr instr;
+ int next;
+ instr = get_instruction(opcode);
+ if (!instr)
+ LOG("opcode: %d\n", opcode);
+ assert(instr);
+ next = instr(buffer);
+ if (next == -1)
+ return EXIT_FAILURE;
+ return next;
+}
+
+int is_valid(u16 opcode) { return opcode < 32776; }
+
+int is_literal(u16 opcode) { return opcode < 32768; }
+
+int is_register(u16 opcode) { return opcode >= 32768 && opcode <= 32775; }
+
+u16 *get_register(u16 opcode) {
+ switch (opcode) {
+ case 32768:
+ return &r0;
+ case 32769:
+ return &r1;
+ case 32770:
+ return &r2;
+ case 32771:
+ return &r3;
+ case 32772:
+ return &r4;
+ case 32773:
+ return &r5;
+ case 32774:
+ return &r6;
+ case 32775:
+ return &r7;
+ default:
+ return NULL;
+ }
+}
+
+instruction_ptr get_instruction(u16 opcode) {
+ switch (opcode) {
+ case 0:
+ return &instruction_halt;
+ case 1:
+ return &instruction_set;
+ case 2:
+ return &instruction_push;
+ case 3:
+ return &instruction_pop;
+ case 4:
+ return &instruction_eq;
+ case 5:
+ return &instruction_gt;
+ case 6:
+ return &instruction_jmp;
+ case 7:
+ return &instruction_jt;
+ case 8:
+ return &instruction_jf;
+ case 9:
+ return &instruction_add;
+ case 10:
+ return &instruction_mult;
+ case 11:
+ return &instruction_mod;
+ case 12:
+ return &instruction_and;
+ case 13:
+ return &instruction_or;
+ case 14:
+ return &instruction_not;
+ case 15:
+ return &instruction_rmem;
+ case 16:
+ return &instruction_wmem;
+ case 17:
+ return &instruction_call;
+ case 18:
+ return &instruction_ret;
+ case 19:
+ return &instruction_out;
+ case 20:
+ return &instruction_in;
+ case 21:
+ return &instruction_noop;
+ default:
+ return NULL;
+ }
+}
+
+int instruction_halt(u8 *buffer) { return -1; }
+
+int instruction_set(u8 *buffer) {
+ u16 next, *a;
+ buffer = get_next_pair(buffer, &next);
+ LOG("set: %d", next);
+ a = get_register(next);
+ buffer = get_next_pair(buffer, &next);
+ if (is_register(next))
+ next = *get_register(next);
+ LOG(" %d\n", next);
+ *a = next;
+ return rPC + 6;
+}
+
+int instruction_push(u8 *buffer) {
+ u16 next;
+ buffer = get_next_pair(buffer, &next);
+ if (is_register(next))
+ next = *get_register(next);
+ stack_push(&s, next);
+ LOG("push: %d\n", next);
+ return rPC + 4;
+}
+
+int instruction_pop(u8 *buffer) {
+ u16 next, *a;
+ buffer = get_next_pair(buffer, &next);
+ a = get_register(next);
+ *a = stack_pop(&s);
+ LOG("pop: %d\n", *a);
+ return rPC + 4;
+}
+
+int instruction_eq(u8 *buffer) {
+ u16 next, *a, b, c;
+ buffer = get_next_pair(buffer, &next);
+ LOG("%d: eq: %s", rPC, REG(next));
+ a = get_register(next);
+ buffer = get_next_pair(buffer, &b);
+ LOG(" %s", REG(b));
+ if (is_register(b))
+ b = *get_register(b);
+ LOG("(%d)", b);
+ buffer = get_next_pair(buffer, &c);
+ LOG(" %s", REG(c));
+ if (is_register(c))
+ c = *get_register(c);
+ LOG("(%d)\n", c);
+ *a = b == c ? 1 : 0;
+ return rPC + 8;
+}
+
+int instruction_gt(u8 *buffer) {
+ u16 next, *a, b, c;
+ buffer = get_next_pair(buffer, &next);
+ LOG("gt: %d", next);
+ a = get_register(next);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ buffer = get_next_pair(buffer, &c);
+ if (is_register(c))
+ c = *get_register(c);
+ *a = b > c ? 1 : 0;
+ LOG(" %d %d\n", b, c);
+ return rPC + 2 + 6;
+}
+
+int instruction_jmp(u8 *buffer) {
+ u16 next;
+ buffer = get_next_pair(buffer, &next);
+ if (is_register(next))
+ next = *get_register(next);
+ LOG("jmp: %d\n", next);
+ return next * 2;
+}
+
+int instruction_jt(u8 *buffer) {
+ u16 a, b;
+ buffer = get_next_pair(buffer, &a);
+ LOG("jt: %d", a);
+ if (is_register(a))
+ a = *get_register(a);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ LOG(" %d\n", b);
+ return a ? b * 2 : rPC + 6;
+}
+
+int instruction_jf(u8 *buffer) {
+ u16 a, b;
+ buffer = get_next_pair(buffer, &a);
+ LOG("jf: %d", a);
+ if (is_register(a))
+ a = *get_register(a);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ LOG(" %d\n", b);
+ return !a ? b * 2 : rPC + 6;
+}
+
+int instruction_add(u8 *buffer) {
+ u16 next, *a, b, c;
+ buffer = get_next_pair(buffer, &next);
+ a = get_register(next);
+ LOG("add: %d ", next);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ buffer = get_next_pair(buffer, &c);
+ if (is_register(c))
+ c = *get_register(c);
+ *a = (b + c) % 32768;
+ LOG("%d %d\n", b, c);
+ return rPC + 8;
+}
+
+int instruction_mult(u8 *buffer) {
+ u16 next, *a, b, c;
+ buffer = get_next_pair(buffer, &next);
+ a = get_register(next);
+ LOG("mult: %d ", next);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ buffer = get_next_pair(buffer, &c);
+ if (is_register(c))
+ c = *get_register(c);
+ *a = (b * c) % 32768;
+ LOG("%d %d\n", b, c);
+ return rPC + 8;
+}
+
+int instruction_mod(u8 *buffer) {
+ u16 next, *a, b, c;
+ buffer = get_next_pair(buffer, &next);
+ a = get_register(next);
+ LOG("mod: %d ", next);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ buffer = get_next_pair(buffer, &c);
+ if (is_register(c))
+ c = *get_register(c);
+ *a = (b % c);
+ LOG("%d %d\n", b, c);
+ return rPC + 8;
+}
+
+int instruction_and(u8 *buffer) {
+ u16 next, *a, b, c;
+ buffer = get_next_pair(buffer, &next);
+ LOG("and: %d", next);
+ a = get_register(next);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ buffer = get_next_pair(buffer, &c);
+ if (is_register(c))
+ c = *get_register(c);
+ LOG(" %d %d\n", b, c);
+ *a = b & c;
+ return rPC + 8;
+}
+
+int instruction_or(u8 *buffer) {
+ u16 next, *a, b, c;
+ buffer = get_next_pair(buffer, &next);
+ LOG("or: %d", next);
+ a = get_register(next);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ buffer = get_next_pair(buffer, &c);
+ if (is_register(c))
+ c = *get_register(c);
+ LOG(" %d %d\n", b, c);
+ *a = b | c;
+ return rPC + 8;
+}
+
+int instruction_not(u8 *buffer) {
+ u16 next, *a, b;
+ buffer = get_next_pair(buffer, &next);
+ LOG("not: %d", next);
+ a = get_register(next);
+ buffer = get_next_pair(buffer, &b);
+ if (is_register(b))
+ b = *get_register(b);
+ LOG(" %d\n", b);
+ *a = ~b;
+ *a ^= 0x8000;
+ return rPC + 6;
+}
+
+int instruction_rmem(u8 *buffer) {
+ u16 next, *a, b;
+ buffer = get_next_pair(buffer, &next);
+ LOG("%d: rmem: %d", rPC, next);
+ a = get_register(next);
+ buffer = get_next_pair(buffer, &b);
+ LOG(" %d\n", b);
+ if (is_register(b))
+ b = *get_register(b);
+ *a = mem_get(&m, b);
+ return rPC + 6;
+}
+
+int instruction_wmem(u8 *buffer) {
+ u16 a, b;
+ buffer = get_next_pair(buffer, &a);
+ LOG("wmem: %d", a);
+ if (is_register(a))
+ a = *get_register(a);
+ buffer = get_next_pair(buffer, &b);
+ LOG(" %d\n", b);
+ if (is_register(b))
+ b = *get_register(b);
+ mem_set(&m, a, b);
+ return rPC + 6;
+}
+
+int instruction_call(u8 *buffer) {
+ u16 a;
+ buffer = get_next_pair(buffer, &a);
+ if (is_register(a))
+ a = *get_register(a);
+ LOG("call: %d\n", a);
+ stack_push(&s, (rPC + 4) / 2);
+ return a * 2;
+}
+
+int instruction_ret(u8 *buffer) {
+ u16 addr;
+ LOG("ret\n");
+ addr = stack_pop(&s);
+ return addr * 2;
+}
+
+int instruction_out(u8 *buffer) {
+ u16 next;
+ buffer = get_next_pair(buffer, &next);
+ if (is_register(next))
+ next = *get_register(next);
+ fprintf(stdout, "%c", next);
+ return rPC + 4;
+}
+
+int instruction_in(u8 *buffer) {
+ char in;
+ u16 addr, *reg = NULL;
+ get_next_pair(buffer, &addr);
+ reg = get_register(addr);
+ LOG("in: %d\n", addr);
+ in = getchar();
+ *reg = in;
+ return rPC + 4;
+}
+
+int instruction_noop(u8 *buffer) { return rPC + 2; }