Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- VM implementation for http://challenge.synacor.com
- By Preston Hunt | @presto8 | www.prestonhunt.com | me@prestonhunt.com
- Released 2012-07-20 into the public domain
- Thanks to @topaz2078 and @synacor for a great challenge at #oscon 2012.
- Compiling
- =========
- Recommended way to compile:
- gcc -g -Wall -Werror -o main main.c
- Debugging
- =========
- There is not a lot of error checking in this program. Debugging with gdb is
- recommended in the event of a segmentation fault.
- gdb ./main
- Running
- =======
- Emulator output is sent to stderr. It is low-level debugging and verbose. It is
- recommended to redirect it to a file or to /dev/null.
- Recommended invocation:
- ./main 2>vmlog
- This will allow interactive use on stdin and stdout, but you can still view
- the VM log by "tailf vmlog" in another window.
- Default operation is to load the challenge.bin and boot the VM from scratch.
- Use special command "!s" at the VM prompt to save the VM. It can then be
- restored as shown below.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- struct {
- short prog[50000];
- short stack[500];
- short reg[8];
- short* pc;
- short* sp;
- long prog_size;
- } G;
- short teleporter;
- void halt(void)
- {
- fprintf(stderr, "halt\n");
- fflush(stdout);
- fflush(stderr);
- exit(0);
- }
- void _halt(short a, short b, short c)
- {
- halt();
- }
- void reset_vm(void)
- {
- memset(&G, 0, sizeof(G));
- G.pc = G.prog;
- G.sp = G.stack;
- }
- void save_vm(const char* fname)
- {
- FILE* fp = fopen(fname, "wb");
- fwrite(&G, sizeof(G), 1, fp);
- fclose(fp);
- fprintf(stderr, "dumped vm to %s\n", fname);
- }
- void restore_vm(const char* fname)
- {
- FILE* fp = fopen(fname, "rb");
- fread(&G, sizeof(G), 1, fp);
- fclose(fp);
- fprintf(stderr, "restored vm from %s\n", fname);
- }
- void read_file(const char* fname)
- {
- reset_vm();
- FILE* fp = fopen(fname, "rb");
- if (!fp) {
- fprintf(stderr, "couldn't open %s\n", fname);
- halt();
- }
- G.prog_size = fread(G.prog, sizeof(G.prog[0]), sizeof(G.prog)/sizeof(G.prog[0]), fp);
- fclose(fp);
- }
- int to_reg(short a)
- {
- if (a >= 0) {
- fprintf(stderr, "CRITICAL %d should be <0\n", a);
- halt();
- }
- a += 32768;
- return a;
- }
- int get_pc(void)
- {
- return *G.pc++;
- }
- short fix_range(short a)
- {
- return a >= 0 ? a : a + 32768;
- }
- int get_pc_addr(void)
- {
- short pc_addr = G.pc - G.prog;
- return pc_addr;
- }
- int get_reg(short a)
- {
- return G.reg[to_reg(a)];
- }
- int to_arg(short a)
- {
- return a >= 0 ? a : get_reg(a);
- }
- void out(short a, short ignore1, short ignore2)
- {
- static char buf[1000];
- static int bufi;
- a = to_arg(a);
- fputc(a, stdout);
- buf[bufi++ % 1000] = a;
- /*buf[bufi++ % 1000] = 0;*/
- fprintf(stderr, "out: %c", a);
- /*fprintf(stderr, "out %d: %s", a, buf);*/
- }
- void set(short a, short b)
- {
- a = to_reg(a);
- b = to_arg(b);
- fprintf(stderr, "set #%d -> r%d", b, a);
- G.reg[a] = b;
- }
- void _set(short a, short b, short ignore)
- {
- set(a, b);
- }
- void in(short a, short ignore1, short ignore2)
- {
- a = to_reg(a);
- G.reg[7] = teleporter; // for teleporter
- fprintf(stderr, "in %d> ", a);
- int c = getchar();
- if (c == '!') {
- int c = getchar();
- if (c == 'q') halt();
- if (c == 's') {
- save_vm("vm.dump");
- return;
- }
- return;
- }
- fprintf(stderr, "[read %d]\n", c);
- G.reg[a] = c;
- }
- void jump(short addr)
- {
- fprintf(stderr, "jump to %d", addr);
- G.pc = &G.prog[addr];
- }
- void _jump(short a, short b, short c)
- {
- jump(a);
- }
- void jt(short a, short b, short c)
- {
- a = to_arg(a);
- b = to_arg(b);
- fprintf(stderr, "jt %d %d -> ", a, b);
- if (a > 0) {
- jump(b);
- } else {
- fprintf(stderr, "(no jump)");
- }
- }
- void jf(short a, short b, short c)
- {
- a = to_arg(a);
- b = to_arg(b);
- fprintf(stderr, "jf %d %d -> ", a, b);
- if (!a) {
- jump(b);
- } else {
- fprintf(stderr, "(no jump)");
- }
- }
- void add(short a, short b, short c)
- {
- b = to_arg(b);
- c = to_arg(c);
- fprintf(stderr, "add %d + %d -> ", b, c);
- short ans = b + c;
- set(a, fix_range(ans));
- }
- void mult(short a, short b, short c)
- {
- b = to_arg(b);
- c = to_arg(c);
- fprintf(stderr, "mult %d * %d -> ", b, c);
- short ans = b * c;
- set(a, fix_range(ans));
- }
- void mod(short a, short b, short c)
- {
- b = to_arg(b);
- c = to_arg(c);
- fprintf(stderr, "mod %d mod %d -> ", b, c);
- short ans = b % c;
- set(a, ans);
- }
- void eq(short a, short b, short c)
- {
- b = to_arg(b);
- c = to_arg(c);
- fprintf(stderr, "eq %d == %d -> ", b, c);
- set(a, b == c);
- }
- void and(short a, short b, short c)
- {
- b = to_arg(b);
- c = to_arg(c);
- fprintf(stderr, "and %d & %d -> ", b, c);
- set(a, b & c);
- }
- void not(short a, short b, short c)
- {
- b = to_arg(b);
- fprintf(stderr, "not !%d -> ", b);
- short ans = ~b;
- set(a, fix_range(ans));
- }
- void rmem(short a, short b, short ignore)
- {
- b = to_arg(b);
- short b_ = G.prog[b];
- fprintf(stderr, "rmem (A%d)#%d -> ", b, b_);
- set(a, b_);
- }
- void wmem(short a, short b, short ignore)
- {
- a = to_arg(a);
- b = to_arg(b);
- fprintf(stderr, "wmem #%d -> (A%d)", b, a);
- G.prog[a] = b;
- }
- void push(short a)
- {
- a = to_arg(a);
- fprintf(stderr, "push %d", a);
- *G.sp++ = a;
- }
- void _push(short a, short b, short c)
- {
- push(a);
- }
- void call(short a, short ignore1, short ignore2)
- {
- a = to_arg(a);
- fprintf(stderr, "call %d -> ", a);
- push(get_pc_addr());
- fprintf(stderr, " -> ");
- jump(a);
- }
- void ret(short ignore1, short ignore2, short ignore3)
- {
- short a = *--G.sp;
- fprintf(stderr, "ret %d -> ", a);
- /*exit(1);*/
- jump(a);
- }
- void or(short a, short b, short c)
- {
- b = to_arg(b);
- c = to_arg(c);
- fprintf(stderr, "or %d | %d -> ", b, c);
- set(a, b | c);
- }
- void gt(short a, short b, short c)
- {
- b = to_arg(b);
- c = to_arg(c);
- fprintf(stderr, "gt %d > %d -> ", b, c);
- set(a, b > c);
- }
- void pop(short a, short b, short c)
- {
- a = to_reg(a);
- G.reg[a] = *--G.sp;
- fprintf(stderr, "pop r%d=%d", a, G.reg[a]);
- }
- void noop(short a, short b, short c)
- {
- }
- struct ophandle {
- short opcode;
- char* label;
- int args;
- void (*fn_execute)(short a, short b, short c);
- //void (*fn_disassemble)();
- } opcode_map[] = {
- { 0 , "HALT" , 0, _halt } ,
- { 1 , "SET" , 2, _set } ,
- { 2 , "PUSH" , 1, _push } ,
- { 3 , "POP" , 1, pop } ,
- { 4 , "EQ" , 3, eq } ,
- { 5 , "GT" , 3, gt } ,
- { 6 , "JUMP" , 1, _jump } ,
- { 7 , "JT" , 2, jt } ,
- { 8 , "JF" , 2, jf } ,
- { 9 , "ADD" , 3, add } ,
- { 10 , "MULT" , 3, mult } ,
- { 11 , "MOD" , 3, mod } ,
- { 12 , "AND" , 3, and } ,
- { 13 , "OR" , 3, or } ,
- { 14 , "NOT" , 2, not } ,
- { 15 , "RMEM" , 2, rmem } ,
- { 16 , "WMEM" , 2, wmem } ,
- { 17 , "CALL" , 1, call } ,
- { 18 , "RET" , 0, ret } ,
- { 19 , "OUT" , 1, out } ,
- { 20 , "IN" , 1, in } ,
- { 21 , "NOOP" , 0, noop } ,
- { 100 , NULL , 0, NULL }
- };
- void print_arg(short a)
- {
- if (a < 0) { // reg
- int rnum = to_reg(a);
- fprintf(stderr, "r%d(%d)", rnum, G.reg[rnum]);
- } else {
- fprintf(stderr, "%d", a);
- }
- }
- void handle_opcode(int opcode)
- {
- short a = *G.pc;
- short b = *(G.pc+1);
- short c = *(G.pc+2);
- fprintf(stderr, "a="); print_arg(a);
- fprintf(stderr, " b="); print_arg(b);
- fprintf(stderr, " c="); print_arg(c);
- fprintf(stderr, "\n\t");
- if (opcode < 0 || opcode > 21) {
- fprintf(stderr, "Invalid opcode\n");
- exit(1);
- }
- struct ophandle* oph = &opcode_map[opcode];
- G.pc += oph->args;
- oph->fn_execute(a, b, c);
- fprintf(stderr, "\n");
- }
- void hacks(void);
- int main(int argc, const char *argv[])
- {
- if (1) {
- read_file("challenge.bin");
- fprintf(stderr, "read %ld words (%ld bytes per word)\n", G.prog_size, sizeof(G.prog[0]));
- } else {
- /*restore_vm("after_boot.vm");*/
- restore_vm("ruins.vm");
- }
- if (argc >= 2) {
- teleporter = atoi(argv[1]);
- fprintf(stderr, "using teleporter value: %d\n", teleporter);
- fprintf(stdout, "using teleporter value: %d\n", teleporter);
- }
- while (1) {
- int pc_addr = get_pc_addr();
- hacks();
- int op = get_pc();
- fprintf(stderr, "PC=%d ", pc_addr);
- handle_opcode(op);
- }
- fprintf(stderr, "aborted\n");
- return 0;
- }
- /*
- * NOTE: SPOILERS below. Do not read if you are still working on the challenge.
- *
- * These are my notes from the challenge. They are incomplete, rough, and
- * contain spoilers.
- */
- /* Don't read this function "hacks" if you are still working on the teleporter */
- void hacks(void)
- {
- /*if (pc_addr == 5473) break;*/ // right before testing transporter
- /*G.prog[5473] = 7; // change jf -> jt*/
- /*G.prog[5494] = 18; // change to ret to avoid transporter checksum*/
- /*G.prog[6049] = 18; // change to ret to avoid transporter checksum*/
- /*if (pc_addr == 1522) break;*/
- /*if (pc_addr == 5494) break; // start of transporter checksum*/
- }
- #if 0
- Walk-through up to teleporter
- =============================
- These are the commands starting after a fresh boot all the way to the ruins:
- take tablet
- use tablet
- doorway
- north
- north
- north
- bridge
- continue
- down
- east
- take empty lantern
- west
- west
- passage
- ladder
- west
- south
- north
- take can
- use can
- use lantern
- west
- ladder
- darkness
- continue
- west
- west
- west
- west
- north
- take red coin
- north
- west
- take blue coin
- up
- take shiny coin
- down
- east
- east
- take concave coin
- down
- take corroded coin
- up
- west
- use blue coin
- use red coin
- use shiny coin
- use concave coin
- use corroded coin
- north
- take teleporter
- use teleporter
- Maze hints
- ==========
- You are in a maze of twisty little passages, all dimly lit by more bioluminescent moss. There is a ladder here leading up.
- EAST
- You are in a twisty maze of little passages, all alike.
- You are in a maze of little twisty passages, all alike.
- NORTH
- You are in a little maze of twisty passages, all alike.
- NOT NORTH
- SOUTH
- You are in a maze of twisty little passages, all alike.
- WEST
- You are in a twisty alike of little passages, all maze.
- NORTH
- Coins challenge
- ===============
- 9 2 5 7 3 399
- blue red shiny concave corroded
- Trying to reverse engineering the checksum algorithm
- ====================================================
- PC=1522 subroute to print letter
- PC=1480 subroute to print a string?
- PC=1725 subroute state machine?
- PC=5494 subroute to check transporter code
- PC=6049 start of checksum
- if r0 > 0 then jump to PC=6057
- PC=6057
- if r1 > 0 then jump to PC=6070
- PC=6070
- push r0 on stack
- decrements r1 by 1
- if r1 >0, jumps to PC=6070
- if r1 <=0, subtracts 1 from r0 and puts key in r1, jumps to 6049 again
- PC=6070
- r0=4
- r1=1
- r2=3
- 6076: r1 = func_6049(4,1,1)
- 6078: r1 = r0
- func_6049(r0, r1,r8) {
- if (r0 == 0) { //6049
- return 1 + r1; //6052
- }
- if (r1 > 0) { //6057
- r0 + func_6049(r0, r1-1, r8); //6070
- } else { //6060
- r1 = func_6049(r0-1, r8, r8);
- }
- }
- 6052:
- r0 = r1 + 1
- ret 6069: ret 6078:
- r1 = r0
- r0 = pop
- r0 = r0 + r1
- func_6049(4, 1, r8=5)
- f(4,1,1)
- 4 + f(4,0,1)
- f(3,1,1)
- 3 + f(3,0,1)
- f(2,1,1)
- 2 + f(2,0,1)
- f(1,1,1)
- 1 + f(1,0,1)
- f(0,1,1)
- ret 1+r1 = 1+1 = 2
- r1 = r0
- r0 = r0 - 1
- r1 = 1
- r1 = r1 - 1
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement