zongors-reality-engine/src/vm.c

295 lines
9.7 KiB
C

#include "vm.h"
#include "debug.h"
#include <string.h>
#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)
/**
* String copy in data memory.
*/
void mem_strcpy(Value *memory, const char *str, uint32_t length,
uint32_t dest) {
memory[dest].u = length;
uint32_t buffer = dest + 1;
uint32_t i;
for (i = 0; i < length; i++) {
memory[buffer + (i / 4)].c[i % 4] = str[i];
}
}
/**
* Step to the next opcode in the vm.
*/
bool step_vm(VM *vm) {
/* Get current instruction & Advance to next instruction */
uint32_t instruction = vm->memory[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:; /* whats up with this semicolon? ANSI C does not allow you to create a variabel after a case, so this noop is here */
uint32_t jmp = vm->memory[vm->pc++].u; /* location of function in memory */
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->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_LOADI:
vm->frames[vm->fp].registers[dest].i = vm->memory[vm->pc++].i;
return true;
case OP_LOADU:
vm->frames[vm->fp].registers[dest].u = vm->memory[vm->pc++].u;
return true;
case OP_LOADF:
vm->frames[vm->fp].registers[dest].f = vm->memory[vm->pc++].f;
return true;
case OP_STOREI:
vm->memory[vm->pc++].i = vm->frames[vm->fp].registers[dest].i;
return true;
case OP_STOREU:
vm->memory[vm->pc++].u = vm->frames[vm->fp].registers[dest].u;
return true;
case OP_STOREF:
vm->memory[vm->pc++].f = vm->frames[vm->fp].registers[dest].f;
return true;
case OP_PUSHI:
vm->stack[++vm->sp].i = vm->frames[vm->fp].registers[dest].i;
return true;
case OP_PUSHU:
vm->stack[++vm->sp].u = vm->frames[vm->fp].registers[dest].u;
return true;
case OP_PUSHF:
vm->stack[++vm->sp].f = vm->frames[vm->fp].registers[dest].f;
return true;
case OP_POPI:
vm->frames[vm->fp].registers[dest].i = vm->stack[vm->sp--].i;
return true;
case OP_POPU:
vm->frames[vm->fp].registers[dest].u = vm->stack[vm->sp--].u;
return true;
case OP_POPF:
vm->frames[vm->fp].registers[dest].f = vm->stack[vm->sp--].f;
return true;
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_MOV:
vm->frames[vm->fp].registers[dest] = vm->frames[vm->fp].registers[src1];
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 */
uint32_t str_dest = (uint32_t)vm->frames[vm->fp]
.allocated.end; /* get start of unallocated */
vm->frames[vm->fp].registers[dest].u =
str_dest; /* store ptr of string to dest register */
char buffer[32];
int len = sprintf(buffer, "%d", a);
mem_strcpy(vm->memory, buffer, len, str_dest); /* copy buffer to dest */
vm->frames[vm->fp].allocated.end +=
((len / 4) + (len % 4) + 1); /* increment to end of allocated */
return true;
}
case OP_UINT_TO_STRING: {
uint32_t a = (uint32_t)vm->frames[vm->fp].registers[src1].u; /* get value */
uint32_t str_dest = (uint32_t)vm->frames[vm->fp]
.allocated.end; /* get start of unallocated */
vm->frames[vm->fp].registers[dest].u =
str_dest; /* store ptr of string to dest register */
char buffer[32];
int len = sprintf(buffer, "%d", a);
mem_strcpy(vm->memory, buffer, len, str_dest); /* copy buffer to dest */
vm->frames[vm->fp].allocated.end +=
((len / 4) + (len % 4) + 1); /* increment to end of allocated */
return true;
}
case OP_REAL_TO_STRING: {
float a = (float)vm->frames[vm->fp].registers[src1].f; /* get value */
uint32_t str_dest = (uint32_t)vm->frames[vm->fp]
.allocated.end; /* get start of unallocated */
vm->frames[vm->fp].registers[dest].u =
str_dest; /* store ptr of string to dest register */
char buffer[32];
int len = sprintf(buffer, "%f", a);
mem_strcpy(vm->memory, buffer, len, str_dest); /* copy buffer to dest */
vm->frames[vm->fp].allocated.end +=
((len / 4) + (len % 4) + 1); /* increment to end of allocated */
return true;
}
case OP_READ_STRING: {
uint32_t str_dest = (uint32_t)vm->frames[vm->fp]
.allocated.end; /* get start of unallocated */
vm->frames[vm->fp].registers[dest].u =
str_dest; /* store ptr of string to dest register */
uint32_t buffer = str_dest + 1;
uint32_t length = 0;
while (1) {
int ch = getchar();
if (ch == '\n' || ch == EOF) {
vm->memory[buffer + (length / 4)].c[length % 4] = '\0';
break;
}
vm->memory[buffer + (length / 4)].c[length % 4] = ch;
length++;
}
vm->memory[str_dest].u = length;
vm->frames[vm->fp].allocated.end +=
((length / 4) + (length % 4) + 1); /* increment to end of allocated */
return true;
}
case OP_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 */
}