366 lines
11 KiB
C
366 lines
11 KiB
C
#include "vm.h"
|
|
#include "debug.h"
|
|
#include <string.h>
|
|
|
|
/* no inline fn in ANSI C :( */
|
|
#define COMPARE_AND_JUMP(type, accessor, op) \
|
|
do { \
|
|
type value = vm->frames[vm->fp].registers[src1].accessor; \
|
|
type value2 = vm->frames[vm->fp].registers[src2].accessor; \
|
|
vm->pc = \
|
|
(value op value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc; \
|
|
return true; \
|
|
} while (0)
|
|
|
|
#define MATH_OP(accessor, op) \
|
|
do { \
|
|
vm->frames[vm->fp].registers[dest].accessor = \
|
|
vm->frames[vm->fp] \
|
|
.registers[src1] \
|
|
.accessor op vm->frames[vm->fp] \
|
|
.registers[src2] \
|
|
.accessor; \
|
|
return true; \
|
|
} while (0)
|
|
|
|
/**
|
|
* Embeds a string into the VM
|
|
*/
|
|
uint32_t str_alloc(VM *vm, const char *str, uint32_t length) {
|
|
if (!length) {
|
|
length = strlen(str);
|
|
}
|
|
uint32_t str_addr = vm->mp;
|
|
vm->memory[vm->mp++].u = length;
|
|
uint32_t i, j = 0;
|
|
for (i = 0; i < length; i++) {
|
|
vm->memory[vm->mp].c[i % 4] = str[i];
|
|
if (++j == 4) {
|
|
j = 0;
|
|
vm->mp++;
|
|
}
|
|
}
|
|
vm->frames[vm->fp].allocated.end += length / 4;
|
|
return str_addr;
|
|
}
|
|
|
|
uint32_t real_alloc(VM *vm, float v) {
|
|
uint32_t addr = vm->mp;
|
|
vm->memory[vm->mp++].f = v;
|
|
vm->frames[vm->fp].allocated.end++;
|
|
return addr;
|
|
}
|
|
|
|
uint32_t nat_alloc(VM *vm, uint32_t v) {
|
|
uint32_t addr = vm->mp;
|
|
vm->memory[vm->mp++].u = v;
|
|
vm->frames[vm->fp].allocated.end++;
|
|
return addr;
|
|
}
|
|
|
|
uint32_t int_alloc(VM *vm, int32_t v) {
|
|
uint32_t addr = vm->mp;
|
|
vm->memory[vm->mp++].i = v;
|
|
vm->frames[vm->fp].allocated.end++;
|
|
return addr;
|
|
}
|
|
|
|
/**
|
|
* Step to the next opcode in the vm.
|
|
*/
|
|
bool step_vm(VM *vm) {
|
|
/* Get current instruction & Advance to next instruction */
|
|
uint32_t instruction = vm->code[vm->pc++].u;
|
|
|
|
uint8_t opcode = (instruction >> 24) & 0xFF;
|
|
uint8_t dest = (instruction >> 16) & 0xFF;
|
|
uint8_t src1 = (instruction >> 8) & 0xFF;
|
|
uint8_t src2 = instruction & 0xFF;
|
|
|
|
switch (opcode) {
|
|
case OP_HALT: {
|
|
return false;
|
|
}
|
|
case OP_CALL: {
|
|
uint32_t jmp = vm->code[vm->pc++].u; /* location of function in code */
|
|
vm->return_stack[vm->rp++].u = vm->pc; /* set return address */
|
|
vm->fp++; /* increment to the next free frame */
|
|
vm->frames[vm->fp].allocated.start =
|
|
vm->mp; /* set start of new memory block */
|
|
vm->pc = jmp;
|
|
return true;
|
|
}
|
|
case OP_RETURN: {
|
|
vm->frames[vm->fp].rp = 0; /* reset register ptr */
|
|
vm->pc = vm->return_stack[--vm->rp].u; /* set pc to return address */
|
|
vm->mp =
|
|
vm->frames[vm->fp--].allocated.start; /* reset memory pointer to start
|
|
of old slice, pop the frame */
|
|
return true;
|
|
}
|
|
case OP_LOAD: {
|
|
uint32_t ptr = vm->code[vm->pc++].u;
|
|
vm->frames[vm->fp].registers[dest] = vm->memory[ptr];
|
|
return true;
|
|
}
|
|
case OP_STORE: {
|
|
uint32_t ptr = vm->code[vm->pc++].u;
|
|
vm->memory[ptr] = vm->frames[vm->fp].registers[src1];
|
|
return true;
|
|
}
|
|
case OP_PUT: {
|
|
uint32_t addr = vm->frames[vm->fp].registers[dest].u;
|
|
vm->memory[addr] = vm->frames[vm->fp].registers[src1];
|
|
return true;
|
|
}
|
|
case OP_GET: {
|
|
uint32_t addr = vm->frames[vm->fp].registers[src1].u;
|
|
vm->frames[vm->fp].registers[dest] = vm->memory[addr];
|
|
return true;
|
|
}
|
|
case OP_OFFSET: {
|
|
uint32_t ptr = vm->frames[vm->fp].registers[src1].u;
|
|
uint32_t offset = vm->frames[vm->fp].registers[src2].u;
|
|
uint32_t result = ptr + offset;
|
|
vm->frames[vm->fp].registers[dest].u = result;
|
|
return true;
|
|
}
|
|
case OP_PUSH: {
|
|
vm->stack[++vm->sp] = vm->frames[vm->fp].registers[dest];
|
|
return true;
|
|
}
|
|
case OP_POP: {
|
|
vm->frames[vm->fp].registers[dest] = vm->stack[vm->sp--];
|
|
return true;
|
|
}
|
|
case OP_MEM_ALLOC: {
|
|
uint32_t mem_dest = (uint32_t)vm->frames[vm->fp]
|
|
.allocated.end; /* get start of unallocated */
|
|
vm->frames[vm->fp].registers[dest].u =
|
|
mem_dest; /* store ptr of array to dest register */
|
|
uint32_t size = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
|
vm->memory[mem_dest].u = size;
|
|
vm->frames[vm->fp].allocated.end +=
|
|
size; /* increment to end of allocated */
|
|
return true;
|
|
}
|
|
case OP_MEM_MOV: {
|
|
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
|
|
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
|
vm->memory[dest_addr] = vm->memory[src_addr];
|
|
return true;
|
|
}
|
|
case OP_MEM_SWAP: {
|
|
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
|
|
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
|
vm->memory[dest_addr].u ^= vm->memory[src_addr].u;
|
|
vm->memory[src_addr].u ^= vm->memory[dest_addr].u;
|
|
vm->memory[dest_addr].u ^= vm->memory[src_addr].u;
|
|
return true;
|
|
}
|
|
case OP_SYSCALL: {
|
|
}
|
|
case OP_ADD_INT:
|
|
MATH_OP(i, +);
|
|
case OP_SUB_INT:
|
|
MATH_OP(i, -);
|
|
case OP_MUL_INT:
|
|
MATH_OP(i, *);
|
|
case OP_DIV_INT:
|
|
MATH_OP(i, /);
|
|
case OP_ADD_UINT:
|
|
MATH_OP(u, +);
|
|
case OP_SUB_UINT:
|
|
MATH_OP(u, -);
|
|
case OP_MUL_UINT:
|
|
MATH_OP(u, *);
|
|
case OP_DIV_UINT:
|
|
MATH_OP(u, /);
|
|
case OP_ADD_REAL:
|
|
MATH_OP(f, +);
|
|
case OP_SUB_REAL:
|
|
MATH_OP(f, -);
|
|
case OP_MUL_REAL:
|
|
MATH_OP(f, *);
|
|
case OP_DIV_REAL:
|
|
MATH_OP(f, /);
|
|
case OP_REAL_TO_INT: {
|
|
vm->frames[vm->fp].registers[dest].i =
|
|
(int32_t)(vm->frames[vm->fp].registers[src1].f);
|
|
return true;
|
|
}
|
|
case OP_INT_TO_REAL: {
|
|
vm->frames[vm->fp].registers[dest].f =
|
|
(float)(vm->frames[vm->fp].registers[src1].i);
|
|
return true;
|
|
}
|
|
case OP_REAL_TO_UINT: {
|
|
vm->frames[vm->fp].registers[dest].u =
|
|
(uint32_t)(vm->frames[vm->fp].registers[src1].f);
|
|
return true;
|
|
}
|
|
case OP_UINT_TO_REAL: {
|
|
vm->frames[vm->fp].registers[dest].f =
|
|
(float)(vm->frames[vm->fp].registers[src1].u);
|
|
return true;
|
|
}
|
|
case OP_REG_SWAP: {
|
|
vm->frames[vm->fp].registers[dest].u ^=
|
|
vm->frames[vm->fp].registers[src1].u;
|
|
vm->frames[vm->fp].registers[src1].u ^=
|
|
vm->frames[vm->fp].registers[dest].u;
|
|
vm->frames[vm->fp].registers[dest].u ^=
|
|
vm->frames[vm->fp].registers[src1].u;
|
|
return true;
|
|
}
|
|
case OP_REG_MOV: {
|
|
vm->frames[vm->fp].registers[dest].i = vm->frames[vm->fp].registers[src1].i;
|
|
return true;
|
|
}
|
|
case OP_JMP: {
|
|
vm->pc = vm->frames[vm->fp].registers[dest].u; /* Jump to address */
|
|
return true;
|
|
}
|
|
case OP_JEQ_UINT: {
|
|
COMPARE_AND_JUMP(uint32_t, u, ==);
|
|
}
|
|
case OP_JGT_UINT: {
|
|
COMPARE_AND_JUMP(uint32_t, u, >);
|
|
}
|
|
case OP_JLT_UINT: {
|
|
COMPARE_AND_JUMP(uint32_t, u, <);
|
|
}
|
|
case OP_JLE_UINT: {
|
|
COMPARE_AND_JUMP(uint32_t, u, <=);
|
|
}
|
|
case OP_JGE_UINT: {
|
|
COMPARE_AND_JUMP(uint32_t, u, >=);
|
|
}
|
|
case OP_JEQ_INT: {
|
|
COMPARE_AND_JUMP(int32_t, i, ==);
|
|
}
|
|
case OP_JGT_INT: {
|
|
COMPARE_AND_JUMP(int32_t, i, >);
|
|
}
|
|
case OP_JLT_INT: {
|
|
COMPARE_AND_JUMP(int32_t, i, <);
|
|
}
|
|
case OP_JLE_INT: {
|
|
COMPARE_AND_JUMP(int32_t, i, <=);
|
|
}
|
|
case OP_JGE_INT: {
|
|
COMPARE_AND_JUMP(int32_t, i, >=);
|
|
}
|
|
case OP_JEQ_REAL: {
|
|
COMPARE_AND_JUMP(int32_t, i, ==);
|
|
}
|
|
case OP_JGT_REAL: {
|
|
COMPARE_AND_JUMP(float, u, >);
|
|
}
|
|
case OP_JLT_REAL: {
|
|
COMPARE_AND_JUMP(float, u, <);
|
|
}
|
|
case OP_JGE_REAL: {
|
|
COMPARE_AND_JUMP(float, u, >=);
|
|
}
|
|
case OP_JLE_REAL: {
|
|
COMPARE_AND_JUMP(float, u, <=);
|
|
}
|
|
case OP_INT_TO_STRING: {
|
|
int32_t a = (int32_t)vm->frames[vm->fp].registers[src1].i; /* get value */
|
|
char buffer[32];
|
|
int len = sprintf(buffer, "%d", a);
|
|
uint32_t ptr = str_alloc(vm, buffer, len); /* copy buffer to dest */
|
|
vm->frames[vm->fp].registers[dest].u = ptr;
|
|
return true;
|
|
}
|
|
case OP_UINT_TO_STRING: {
|
|
uint32_t a = (uint32_t)vm->frames[vm->fp].registers[src1].u; /* get value */
|
|
char buffer[32];
|
|
int len = sprintf(buffer, "%d", a);
|
|
uint32_t ptr = str_alloc(vm, buffer, len); /* copy buffer to dest */
|
|
vm->frames[vm->fp].registers[dest].u = ptr;
|
|
return true;
|
|
}
|
|
case OP_REAL_TO_STRING: {
|
|
float a = (float)vm->frames[vm->fp].registers[src1].f; /* get value */
|
|
char buffer[32];
|
|
int len = sprintf(buffer, "%f", a);
|
|
uint32_t ptr = str_alloc(vm, buffer, len); /* copy buffer to dest */
|
|
vm->frames[vm->fp].registers[dest].u = ptr;
|
|
return true;
|
|
}
|
|
case OP_DBG_READ_STRING: {
|
|
uint32_t str_addr = vm->mp++;
|
|
uint32_t length = 0;
|
|
int ch;
|
|
|
|
while ((ch = getchar()) != '\n' && ch != EOF) {
|
|
uint32_t idx = length % 4;
|
|
vm->memory[vm->mp].c[idx] = (char)ch;
|
|
length++;
|
|
|
|
if (idx == 3)
|
|
vm->mp++;
|
|
}
|
|
|
|
uint32_t i, rem = length % 4;
|
|
if (rem != 0) {
|
|
for (i = rem; i < 4; i++) {
|
|
vm->memory[vm->mp].c[i] = '\0';
|
|
}
|
|
vm->mp++;
|
|
}
|
|
|
|
vm->memory[str_addr].u = length;
|
|
vm->frames[vm->fp].allocated.end = vm->mp;
|
|
vm->frames[vm->fp].registers[dest].u = str_addr;
|
|
|
|
return true;
|
|
}
|
|
case OP_DBG_PRINT_STRING: {
|
|
uint32_t ptr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
|
uint32_t length = vm->memory[ptr].u;
|
|
uint32_t str_src = ptr + 1;
|
|
uint32_t i;
|
|
for (i = 0; i < length; i++) {
|
|
uint8_t ch = vm->memory[str_src + (i / 4)].c[i % 4];
|
|
if (ch == '\0')
|
|
break;
|
|
putchar(ch);
|
|
}
|
|
putchar('\n');
|
|
return true;
|
|
}
|
|
case OP_CMP_STRING: {
|
|
uint32_t addr1 = (uint32_t)vm->frames[vm->fp].registers[src1].u;
|
|
uint32_t addr2 = (uint32_t)vm->frames[vm->fp].registers[src2].u;
|
|
uint32_t length1 = vm->memory[addr1 - 1].u;
|
|
uint32_t length2 = vm->memory[addr2 - 1].u;
|
|
uint32_t equal =
|
|
1; /* we dont have a native boolean type so we use uint32_t */
|
|
|
|
if (length1 != length2) {
|
|
equal = 0;
|
|
} else {
|
|
uint32_t i = 0;
|
|
while (i < length1) {
|
|
uint32_t char1 = vm->memory[addr1 + i].u;
|
|
uint32_t char2 = vm->memory[addr2 + i].u;
|
|
if (char1 != char2) {
|
|
equal = 0;
|
|
break;
|
|
}
|
|
if ((char1 & 0xFF) == '\0' && (char2 & 0xFF) == '\0') {
|
|
equal = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
vm->memory[dest].u = equal;
|
|
return true;
|
|
}
|
|
}
|
|
return false; /* something bad happened */
|
|
}
|