203 lines
7.7 KiB
C++
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; }
|