Files
paco/vm.cpp

232 lines
8.3 KiB
C++

#include "vm.h"
#include <stdio.h>
#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,
};
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 = 0;
int vm_cycles = 0;
t_stack vm_datastack;
t_stack vm_callstack;
unsigned short rX = 0;
unsigned char rY = 0;
unsigned char rZ = 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);
fread(mem, fsize, 1, f);
fclose(f);
}
void vm_init(const char* filename) {
vm_pc = 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() {
char val, val2;
switch (vm_program[vm_pc++]) {
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(); } vm_cycles++; break;
case OP_JTR: if (stack_pop(vm_datastack) != 0) { vm_pc = WORD(); } 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]; vm_cycles++; break;
case OP_RJN: if (stack_pop(vm_datastack) != 0) vm_pc += vm_program[vm_pc]; vm_cycles++; break;
case OP_RBZ: if (stack_pop(vm_datastack) == 0) vm_pc -= vm_program[vm_pc]+1; vm_cycles++; break;
case OP_RBN: if (stack_pop(vm_datastack) != 0) vm_pc -= vm_program[vm_pc]+1; vm_cycles++; break;
case OP_RJYZ: if (rY == 0) vm_pc += vm_program[vm_pc]; vm_cycles++; break;
case OP_RJYN: if (rY != 0) vm_pc += vm_program[vm_pc]; vm_cycles++; break;
case OP_RBYZ: if (rY == 0) vm_pc -= vm_program[vm_pc]+1; vm_cycles++; break;
case OP_RBYN: if (rY != 0) vm_pc -= vm_program[vm_pc]+1; vm_cycles++; break;
case OP_RJZZ: if (rZ == 0) vm_pc += vm_program[vm_pc]; vm_cycles++; break;
case OP_RJZN: if (rZ != 0) vm_pc += vm_program[vm_pc]; vm_cycles++; break;
case OP_RBZZ: if (rZ == 0) vm_pc -= vm_program[vm_pc]+1; vm_cycles++; break;
case OP_RBZN: if (rZ != 0) vm_pc -= vm_program[vm_pc]+1; 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;
}
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) {
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++;
}