Files
paco/vm.cpp

203 lines
7.7 KiB
C++

#include "vm.h"
#include <stdio.h>
#include <stdlib.h>
#include "parser.h"
#include "error.h"
#include "constants.h"
#include <SDL.h> // 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; }