#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; /*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)(); 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]]) 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); //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()) { error_print(); } 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 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: PUSH(ds, vm_program[vm_pc++]); vm_cycles++; break; case OP_POP: POP(ds); vm_cycles++; break; case OP_DUP: PUSH(ds, PEEK(ds)); vm_cycles++; break; case OP_SWAP: val = POP(ds); val2 = POP(ds); PUSH(ds, val); PUSH(ds, val2); 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: val = POP(ds); mem[WORD() + rY] = val; 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); 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_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: PUSH(ds, POP(ds) + POP(ds)); vm_cycles++; break; case OP_SUB: PUSH(ds, POP(ds) - POP(ds)); vm_cycles++; break; case OP_MUL: PUSH(ds, POP(ds) * POP(ds)); vm_cycles++; break; case OP_DIV: PUSH(ds, POP(ds) / POP(ds)); vm_cycles++; break; case OP_MOD: PUSH(ds, POP(ds) % POP(ds)); vm_cycles++; break; case OP_AND: PUSH(ds, POP(ds) & POP(ds)); vm_cycles++; break; case OP_OR: PUSH(ds, POP(ds) | POP(ds)); vm_cycles++; break; case OP_NOT: PUSH(ds, !POP(ds)); vm_cycles++; break; case OP_NEG: PUSH(ds, -POP(ds)); vm_cycles++; break; case OP_INC: PUSH(ds, POP(ds) + 1); vm_cycles++; break; case OP_DEC: PUSH(ds, POP(ds) - 1); vm_cycles++; break; case OP_CONCAT: val = POP(ds); for (int i = 0; i < val; i++) PUSH(cs, POP(ds)); val2 = POP(ds); for (int i = 0; i < val; i++) PUSH(ds, POP(cs)); PUSH(ds, val + val2); vm_cycles++; break; case OP_EQ: PUSH(ds, POP(ds) == POP(ds)); vm_cycles++; break; case OP_NEQ: PUSH(ds, POP(ds) != POP(ds)); vm_cycles++; break; case OP_LT: PUSH(ds, POP(ds) < POP(ds)); vm_cycles++; break; case OP_GT: PUSH(ds, POP(ds) > POP(ds)); vm_cycles++; break; case OP_LEQ: PUSH(ds, POP(ds) <= POP(ds)); vm_cycles++; break; case OP_GEQ: PUSH(ds, POP(ds) >= POP(ds)); 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: SDL_Delay(POP(ds) * 1000); 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; 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; }