295 lines
9.7 KiB
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 */
|
|
}
|