#include "vm.h" #include "debug.h" #include #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 */ }