#include "vm.h" #include #define MAX_EXTERNAL_CALLS 256 #define MAX_DATA_STACK 256 #define MAX_CALL_STACK 256 typedef unsigned char byte; typedef unsigned short word; enum OPS { OP_NOP = 0, OP_PUSH, OP_POP, OP_DUP, OP_SWAP, OP_LOAD, OP_LOADI, OP_STORE, OP_STOREI, OP_LOADXY, OP_STOREXY, OP_SETX, OP_SETY, OP_SETZ, OP_GETY, OP_GETZ, OP_INCX, OP_DECX, OP_INCY, OP_DECY, OP_INCZ, OP_DECZ, OP_JMP, OP_JNT, OP_JTR, OP_JSR, OP_RET, OP_CALL, OP_RJ, OP_RB, OP_RJZ, OP_RJN, OP_RBZ, OP_RBN, OP_RJYZ, OP_RJYN, OP_RBYZ, OP_RBYN, OP_RJZZ, OP_RJZN, OP_RBZZ, OP_RBZN, OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_AND, OP_OR, OP_NOT, OP_NEG, OP_INC, OP_DEC, OP_CONCAT, OP_EQ, OP_NEQ, OP_LT, OP_GT, OP_LEQ, OP_GEQ, OP_IN, OP_OUT, OP_SLEEP, }; typedef void(*t_extcall)(t_stack&); t_extcall external_calls[MAX_EXTERNAL_CALLS]; int numcallbacks = 0; typedef void(*t_out_port)(const byte&); typedef byte(*t_in_port)(); t_out_port out_ports[256]; t_in_port in_ports[256]; unsigned char mem[65536]; const unsigned char* vm_program = mem; int vm_pc = 2; int vm_cycles = 0; t_stack vm_datastack; t_stack vm_callstack; unsigned short rX = 0; unsigned char rY = 0; unsigned char rZ = 0; bool sleeping = false; static void load_rom() { FILE *f = fopen("rom.bin", "rb"); fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); fread(&mem[0x8000], fsize, 1, f); fclose(f); } static void load_program(const char* filename) { FILE *f = fopen(filename, "rb"); fseek(f, 0, SEEK_END); long fsize = ftell(f); fseek(f, 0, SEEK_SET); fread(mem, fsize, 1, f); fclose(f); } void vm_init(const char* filename) { vm_pc = 2; rX = rY = rZ = 0; vm_datastack.data = &mem[0x8800]; vm_callstack.data = &mem[0x8900]; vm_datastack.top = vm_callstack.top = 0; vm_datastack.max = MAX_DATA_STACK; vm_callstack.max = MAX_CALL_STACK; load_rom(); load_program(filename); } inline unsigned short WORD() { vm_pc += 2; return vm_program[vm_pc - 2] + (vm_program[vm_pc - 1] << 8); } const int vm_step() { if (sleeping) return vm_cycles; char val, val2; OPS opcode = (OPS)vm_program[vm_pc++]; switch (opcode) { case OP_NOP: vm_cycles++; break; case OP_PUSH: stack_push(vm_datastack, vm_program[vm_pc++]); vm_cycles++; break; case OP_POP: stack_pop(vm_datastack); vm_cycles++; break; case OP_DUP: stack_push(vm_datastack, stack_peek(vm_datastack)); vm_cycles++; break; case OP_SWAP: val = stack_pop(vm_datastack); val2 = stack_pop(vm_datastack); stack_push(vm_datastack, val); stack_push(vm_datastack, val2); vm_cycles++; break; case OP_LOAD: stack_push(vm_datastack, mem[WORD()]); vm_cycles++; break; case OP_LOADI: stack_push(vm_datastack, mem[WORD() + rY]); vm_cycles++; break; case OP_STORE: mem[WORD()] = stack_pop(vm_datastack); vm_cycles++; break; case OP_STOREI: val = stack_pop(vm_datastack); mem[WORD() + rY] = val; vm_cycles++; break; case OP_LOADXY: stack_push(vm_datastack, mem[rX + rY]); vm_cycles++; break; case OP_STOREXY: mem[rX + rY] = stack_pop(vm_datastack); vm_cycles++; break; case OP_SETX: rX = WORD(); vm_cycles++; break; case OP_SETY: rY = stack_pop(vm_datastack); vm_cycles++; break; case OP_SETZ: rZ = stack_pop(vm_datastack); vm_cycles++; break; case OP_GETY: stack_push(vm_datastack, rY); vm_cycles++; break; case OP_GETZ: stack_push(vm_datastack, rZ); vm_cycles++; break; case OP_INCX: rX++; vm_cycles++; break; case OP_DECX: rX--; vm_cycles++; break; case OP_INCY: rY++; vm_cycles++; break; case OP_DECY: rY--; vm_cycles++; break; case OP_INCZ: rZ++; vm_cycles++; break; case OP_DECZ: rZ--; vm_cycles++; break; case OP_JMP: vm_pc = WORD(); vm_cycles++; break; case OP_JNT: if (stack_pop(vm_datastack) == 0) { vm_pc = WORD(); } else vm_pc+=2; vm_cycles++; break; case OP_JTR: if (stack_pop(vm_datastack) != 0) { vm_pc = WORD(); } else vm_pc+=2; vm_cycles++; break; case OP_JSR: stack_push(vm_callstack, vm_pc + 2); stack_push(vm_callstack, rX & 255); stack_push(vm_callstack, rX >> 8); stack_push(vm_callstack, rY); stack_push(vm_callstack, rZ); vm_pc = WORD(); vm_cycles++; break; case OP_RET: rZ = stack_pop(vm_callstack); rY = stack_pop(vm_callstack); rX = (stack_pop(vm_callstack) << 8) + stack_pop(vm_callstack); vm_pc = stack_pop(vm_callstack); vm_cycles++; break; case OP_CALL: external_calls[vm_program[vm_pc++]](vm_datastack); vm_cycles++; break; case OP_RJ: vm_pc += vm_program[vm_pc]; vm_cycles++; break; case OP_RB: vm_pc -= vm_program[vm_pc]+1; vm_cycles++; break; case OP_RJZ: if (stack_pop(vm_datastack) == 0) vm_pc += vm_program[vm_pc]; else vm_pc++; vm_cycles++; break; case OP_RJN: if (stack_pop(vm_datastack) != 0) vm_pc += vm_program[vm_pc]; else vm_pc++; vm_cycles++; break; case OP_RBZ: if (stack_pop(vm_datastack) == 0) vm_pc -= vm_program[vm_pc] + 1; else vm_pc++; vm_cycles++; break; case OP_RBN: if (stack_pop(vm_datastack) != 0) vm_pc -= vm_program[vm_pc] + 1; else vm_pc++; vm_cycles++; break; case OP_RJYZ: if (rY == 0) vm_pc += vm_program[vm_pc]; else vm_pc++; vm_cycles++; break; case OP_RJYN: if (rY != 0) vm_pc += vm_program[vm_pc]; else vm_pc++; vm_cycles++; break; case OP_RBYZ: if (rY == 0) vm_pc -= vm_program[vm_pc] + 1; else vm_pc++; vm_cycles++; break; case OP_RBYN: if (rY != 0) vm_pc -= vm_program[vm_pc] + 1; else vm_pc++; vm_cycles++; break; case OP_RJZZ: if (rZ == 0) vm_pc += vm_program[vm_pc]; else vm_pc++; vm_cycles++; break; case OP_RJZN: if (rZ != 0) vm_pc += vm_program[vm_pc]; else vm_pc++; vm_cycles++; break; case OP_RBZZ: if (rZ == 0) vm_pc -= vm_program[vm_pc] + 1; else vm_pc++; vm_cycles++; break; case OP_RBZN: if (rZ != 0) vm_pc -= vm_program[vm_pc] + 1; else vm_pc++; vm_cycles++; break; case OP_ADD: stack_push(vm_datastack, stack_pop(vm_datastack) + stack_pop(vm_datastack)); vm_cycles++; break; case OP_SUB: stack_push(vm_datastack, stack_pop(vm_datastack) - stack_pop(vm_datastack)); vm_cycles++; break; case OP_MUL: stack_push(vm_datastack, stack_pop(vm_datastack) * stack_pop(vm_datastack)); vm_cycles++; break; case OP_DIV: stack_push(vm_datastack, stack_pop(vm_datastack) / stack_pop(vm_datastack)); vm_cycles++; break; case OP_MOD: stack_push(vm_datastack, stack_pop(vm_datastack) % stack_pop(vm_datastack)); vm_cycles++; break; case OP_AND: stack_push(vm_datastack, stack_pop(vm_datastack) & stack_pop(vm_datastack)); vm_cycles++; break; case OP_OR: stack_push(vm_datastack, stack_pop(vm_datastack) | stack_pop(vm_datastack)); vm_cycles++; break; case OP_NOT: stack_push(vm_datastack, !stack_pop(vm_datastack)); vm_cycles++; break; case OP_NEG: stack_push(vm_datastack, -stack_pop(vm_datastack)); vm_cycles++; break; case OP_INC: stack_push(vm_datastack, stack_pop(vm_datastack)+1); vm_cycles++; break; case OP_DEC: stack_push(vm_datastack, stack_pop(vm_datastack) - 1); vm_cycles++; break; case OP_CONCAT: val = stack_pop(vm_datastack); for (int i = 0; i < val; i++) stack_push(vm_callstack, stack_pop(vm_datastack)); val2 = stack_pop(vm_datastack); for (int i = 0; i < val; i++) stack_push(vm_datastack, stack_pop(vm_callstack)); stack_push(vm_datastack, val + val2); vm_cycles++; break; case OP_EQ: stack_push(vm_datastack, stack_pop(vm_datastack) == stack_pop(vm_datastack)); vm_cycles++; break; case OP_NEQ: stack_push(vm_datastack, stack_pop(vm_datastack) != stack_pop(vm_datastack)); vm_cycles++; break; case OP_LT: stack_push(vm_datastack, stack_pop(vm_datastack) < stack_pop(vm_datastack)); vm_cycles++; break; case OP_GT: stack_push(vm_datastack, stack_pop(vm_datastack) > stack_pop(vm_datastack)); vm_cycles++; break; case OP_LEQ: stack_push(vm_datastack, stack_pop(vm_datastack) <= stack_pop(vm_datastack)); vm_cycles++; break; case OP_GEQ: stack_push(vm_datastack, stack_pop(vm_datastack) >= stack_pop(vm_datastack)); vm_cycles++; break; case OP_IN: stack_push(vm_datastack, in_ports[vm_program[vm_pc++]]()); vm_cycles++; break; case OP_OUT: out_ports[vm_program[vm_pc++]](stack_pop(vm_datastack)); vm_cycles++; break; case OP_SLEEP: sleeping = true; vm_cycles++; break; } return vm_cycles; } void vm_register_call(void(*callback)(t_stack&)) { external_calls[numcallbacks++] = callback; } void vm_register_in_port(const byte port, t_in_port callback) { in_ports[port] = callback; } void vm_register_out_port(const byte port, t_out_port callback) { out_ports[port] = callback; } void vm_call_interrupt(const char num) { sleeping = false; stack_push(vm_callstack, vm_pc); stack_push(vm_callstack, rX & 255); stack_push(vm_callstack, rX >> 8); stack_push(vm_callstack, rY); stack_push(vm_callstack, rZ); vm_pc = num == 0 ? 0xFFFA : 0xFFFD; vm_cycles++; }