#include "vm.h" #include #include #include "parser.h" #include "error.h" #include "constants.h" #include // For SDL_Delay #define MAX_EXTERNAL_CALLS 256 #define MAX_DATA_STACK 256 #define MAX_CALL_STACK 256 typedef unsigned char byte; typedef unsigned short word; typedef void(*t_out_port)(const byte&); typedef byte(*t_in_port)(); static t_out_port out_ports[256]; static t_in_port in_ports[256]; static unsigned char mem[65536]; static const unsigned char* vm_program = mem; static unsigned char* ds = &mem[0x8800]; // data stack static unsigned char* cs = &mem[0x8900]; // call stack static unsigned short& rX = *(unsigned short*)&mem[0xFFFA]; static unsigned char& rY = mem[0xFFFC]; static unsigned char& rZ = mem[0xFFFD]; static unsigned short& vm_pc = *(unsigned short*)&mem[0xFFFE]; static int vm_cycles = 0; #define PUSH(x, y) x[++x[0]] = y #define POP(x) (x[x[0]--]) #define PEEK(x) (x[x[0]]) #define GRAB(x) a = x[x[0]--]; b = x[x[0]--]; bool sleeping = false; Uint32 time, delay = 0; 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); //same as rewind(f); char *program = (char*)malloc(fsize + 1); fread(program, fsize, 1, f); fclose(f); program[fsize] = 0; register_constants(); parser_parse(program, mem); free(program); if (error_raised()) { vm_pc = 0xA000; //error_print(&mem[0xA000]); } else { printf("Compilation complete!\nProgram size: %d bytes\nMemory usage: %d bytes\n", parser_get_codesize(), parser_get_memory_usage()); /*FILE *f = fopen("test.bin", "wb"); if (f == nullptr) perror("Error");// printf("Error: %d (%s)\n", errno, strerror(errno)); fwrite(rawCode, parser_get_codesize(), 1, f); fclose(f);*/ } } void vm_init(const char* filename) { vm_pc = 2; rX = rY = rZ = 0; ds[0] = cs[0] = 0; 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 unsigned short vm_step() { if (delay > 0) { if (SDL_GetTicks() - time < delay) return vm_pc; delay = 0; } unsigned char a, b, c; OPS opcode = (OPS)vm_program[vm_pc++]; switch (opcode) { case OP_NOP: vm_cycles++; break; case OP_PUSH: PUSH(ds, vm_program[vm_pc++]); vm_cycles++; break; case OP_POP: ds[0]--; vm_cycles++; break; case OP_DUP: a = PEEK(ds); PUSH(ds, a); vm_cycles++; break; case OP_SWAP: a = POP(ds); b = POP(ds); PUSH(ds, a); PUSH(ds, b); vm_cycles++; break; case OP_LOAD: PUSH(ds, mem[WORD()]); vm_cycles++; break; case OP_LOADI: PUSH(ds, mem[WORD() + rY]); vm_cycles++; break; case OP_STORE: mem[WORD()] = POP(ds); vm_cycles++; break; case OP_STOREI: a = POP(ds); mem[WORD() + rY] = a; vm_cycles++; break; case OP_LOADXY: PUSH(ds, mem[rX + rY]); vm_cycles++; break; case OP_STOREXY: mem[rX + rY] = POP(ds); vm_cycles++; break; case OP_SETX: rX = WORD(); vm_cycles++; break; case OP_SETY: rY = POP(ds); vm_cycles++; break; case OP_SETZ: rZ = POP(ds); vm_cycles++; break; case OP_GETY: PUSH(ds, rY); vm_cycles++; break; case OP_GETZ: PUSH(ds, 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 (POP(ds) == 0) { vm_pc = WORD(); } else vm_pc+=2; vm_cycles++; break; case OP_JTR: if (POP(ds) != 0) { vm_pc = WORD(); } else vm_pc+=2; vm_cycles++; break; case OP_JSR: PUSH(cs, (vm_pc + 2) & 0xff); PUSH(cs, (vm_pc + 2) >> 8); PUSH(cs, rX & 255); PUSH(cs, rX >> 8); PUSH(cs, rY); PUSH(cs, rZ); vm_pc = WORD(); vm_cycles++; break; case OP_RET: rZ = POP(cs); rY = POP(cs); rX = (POP(cs) << 8) + POP(cs); vm_pc = POP(cs); vm_pc = (vm_pc << 8) + POP(cs); 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 (POP(ds) == 0) vm_pc += vm_program[vm_pc]; else vm_pc++; vm_cycles++; break; case OP_RJN: if (POP(ds) != 0) vm_pc += vm_program[vm_pc]; else vm_pc++; vm_cycles++; break; case OP_RBZ: if (POP(ds) == 0) vm_pc -= vm_program[vm_pc] + 1; else vm_pc++; vm_cycles++; break; case OP_RBN: if (POP(ds) != 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: GRAB(ds); PUSH(ds, b + a); vm_cycles++; break; case OP_SUB: GRAB(ds); PUSH(ds, b - a); vm_cycles++; break; case OP_MUL: GRAB(ds); PUSH(ds, b * a); vm_cycles++; break; case OP_DIV: GRAB(ds); PUSH(ds, b / a); vm_cycles++; break; case OP_MOD: GRAB(ds); PUSH(ds, b % a); vm_cycles++; break; case OP_AND: GRAB(ds); PUSH(ds, b & a); vm_cycles++; break; case OP_OR: GRAB(ds); PUSH(ds, b | a); vm_cycles++; break; case OP_NOT: a = POP(ds); PUSH(ds, !a); vm_cycles++; break; case OP_NEG: a = POP(ds); PUSH(ds, -a); vm_cycles++; break; case OP_INC: a = POP(ds); PUSH(ds, a + 1); vm_cycles++; break; case OP_DEC: a = POP(ds); PUSH(ds, a - 1); vm_cycles++; break; case OP_CONCAT: a = POP(ds); for (int i = 0; i < a; i++) { c = POP(ds); PUSH(cs, c); } b = POP(ds); for (int i = 0; i < a; i++) { c = POP(cs); PUSH(ds, c); } PUSH(ds, a + b); vm_cycles++; break; case OP_EQ: GRAB(ds); PUSH(ds, a == b); vm_cycles++; break; case OP_NEQ: GRAB(ds); PUSH(ds, a != b); vm_cycles++; break; case OP_LT: GRAB(ds); PUSH(ds, b < a); vm_cycles++; break; case OP_GT: GRAB(ds); PUSH(ds, b > a); vm_cycles++; break; case OP_LEQ: GRAB(ds); PUSH(ds, b <= a); vm_cycles++; break; case OP_GEQ: GRAB(ds); PUSH(ds, b >= a); vm_cycles++; break; case OP_IN: PUSH(ds, in_ports[vm_program[vm_pc++]]()); vm_cycles++; break; case OP_OUT: out_ports[vm_program[vm_pc++]](POP(ds)); vm_cycles++; break; case OP_SLEEP: delay = POP(ds) * 1000; time = SDL_GetTicks(); vm_cycles++; break; } return vm_pc; } const unsigned short vm_big_step() { word* lines = parser_get_lines(); word current_line = lines[vm_pc]; while ((vm_pc >= 32768) || (lines[vm_pc] == current_line)) { vm_step(); } return vm_pc; } /*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; PUSH(cs, vm_pc); PUSH(cs, rX & 255); PUSH(cs, rX >> 8); PUSH(cs, rY); PUSH(cs, rZ); vm_pc = num == 0 ? 0xFFFA : 0xFFFD; vm_cycles++; } unsigned char* vm_get_memory() { return mem; }