diff --git a/src/opcodes.h b/src/opcodes.h index e46dca4..7767a59 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -2,69 +2,70 @@ #define ZRL_OPCODES_H #include "common.h" +#include "fixed.h" #include typedef enum { - OP_HALT, /* halt : terminate execution */ - OP_JMP, /* jump : jump to address dest unconditionally */ - OP_GET_PC, /* pc : dest = current program counter */ - OP_CALL, /* call : creates a new frame */ - OP_RETURN, /* retn : returns from a frame to the parent frame */ - OP_LOAD, /* load : dest = &[next memory location] */ - OP_STORE, /* stor : next memory location = src1 as float */ - OP_PUSH, /* push : push str ref from register onto the stack and copy str */ - OP_POP, /* pop : pop int from stack onto the register */ - OP_REG_MOV, /* rmov : dest = src1 */ - OP_REG_SWAP, /* rswp : dest = src1, src1 = dest */ - OP_GET_ACC, /* gacc : dest = accumulator */ - OP_MEM_SWAP, /* mswp : &dest = &src1, &src1 = &dest */ - OP_MEM_MOV, /* mmov : &dest = &src1 */ - OP_MEM_ALLOC, /* aloc : dest [next memory location as size] */ - OP_GET, /* get : dest = ptr : dest = memory[ptr] */ - OP_PUT, /* put : ptr = src1 : memory[ptr] = src */ - OP_OFFSET, /* offs : dest = ptr + src1 : dest = p + o */ - OP_SYSCALL, /* sysc : */ - OP_ADD_INT, /* addi : dest = src1 + src2 */ - OP_SUB_INT, /* subi : dest = src1 - src2 */ - OP_MUL_INT, /* muli : dest = src1 * src2 */ - OP_DIV_INT, /* divi : dest = src1 / src2 */ - OP_ADD_UINT, /* addu : dest = src1 + src2 */ - OP_SUB_UINT, /* subu : dest = src1 - src2 */ - OP_MUL_UINT, /* mulu : dest = src1 * src2 */ - OP_DIV_UINT, /* divu : dest = src1 / src2 */ - OP_ADD_REAL, /* addr : dest = src1 + src2 */ - OP_SUB_REAL, /* subr : dest = src1 - src2 */ - OP_MUL_REAL, /* mulr : dest = src1 * src2 */ - OP_DIV_REAL, /* divr : dest = src1 / src2 */ - OP_INT_TO_REAL, /* itor : dest = src1 as real */ - OP_UINT_TO_REAL, /* utor : dest = src1 as real */ - OP_REAL_TO_INT, /* rtoi : dest = src1 as int */ - OP_REAL_TO_UINT, /* rtou : dest = src1 as uint */ - OP_JEQ_INT, /* jeqi : jump to address dest if src1 as int == src2 as int */ - OP_JGT_INT, /* jgti : jump to address dest if src1 as int > src2 as int*/ - OP_JLT_INT, /* jlti : jump to address dest if src1 as int < src2 as int */ - OP_JLE_INT, /* jlei : jump to address dest if src1 as int <= src2 as int */ - OP_JGE_INT, /* jgei : jump to address dest if src1 as int >= src2 as int*/ - OP_JEQ_UINT, /* jequ : jump to address dest if src1 as int == src2 as uint */ - OP_JGT_UINT, /* jgtu : jump to address dest if src1 as int > src2 as uint*/ - OP_JLT_UINT, /* jltu : jump to address dest if src1 as int < src2 as uint */ - OP_JLE_UINT, /* jleu : jump to address dest if src1 as int <= src2 as uint */ - OP_JGE_UINT, /* jgeu : jump to address dest if src1 as int >= src2 as uint*/ - OP_JEQ_REAL, /* jeqr : jump to address dest if src1 as real == src2 as real */ - OP_JGE_REAL, /* jgtr : jump to address dest if src1 as real >= src2 as real */ - OP_JGT_REAL, /* jltr : jump to address dest if src1 as real > src2 as real */ - OP_JLT_REAL, /* jler : jump to address dest if src1 as real < src2 as real */ - OP_JLE_REAL, /* jger : jump to address dest if src1 as real <= src2 as real */ - OP_INT_TO_STRING, /* itos : dest = src1 as str */ - OP_UINT_TO_STRING, /* utos : dest = src1 as str */ - OP_REAL_TO_STRING, /* rtos : dest = src1 as str */ - OP_CMP_STRING, /* cmps : dest = (str == src2) as bool */ - OP_STRING_TO_INT, /* stoi : dest = src1 as int */ - OP_STRING_TO_UINT, /* stou : dest = src1 as uint */ - OP_STRING_TO_REAL, /* stor : dest = src1 as real */ + OP_HALT, /* halt : terminate execution */ + OP_JMP, /* jump : jump to address dest unconditionally */ + OP_GET_PC, /* pc : dest = current program counter */ + OP_CALL, /* call : creates a new frame */ + OP_RETURN, /* retn : returns from a frame to the parent frame */ + OP_LOAD, /* load : dest = &[next code location] */ + OP_STORE, /* stor : next code location = src1 as float */ + OP_PUSH, /* push : push str ref from register onto the stack and copy str */ + OP_POP, /* pop : pop int from stack onto the register */ + OP_REG_MOV, /* rmov : dest = src1 */ + OP_REG_SWAP, /* rswp : dest = src1, src1 = dest */ + OP_GET_ACC, /* gacc : dest = accumulator */ + OP_MEM_SWAP, /* mswp : &dest = &src1, &src1 = &dest */ + OP_MEM_MOV, /* mmov : &dest = &src1 */ + OP_MEM_ALLOC, /* aloc : dest [next memory location as size] */ + OP_GET, /* get : dest = ptr : dest = memory[ptr] */ + OP_PUT, /* put : ptr = src1 : memory[ptr] = src */ + OP_OFFSET, /* offs : dest = ptr + src1 : dest = p + o */ + OP_SYSCALL, /* sysc : */ + OP_ADD_INT, /* addi : dest = src1 + src2 */ + OP_SUB_INT, /* subi : dest = src1 - src2 */ + OP_MUL_INT, /* muli : dest = src1 * src2 */ + OP_DIV_INT, /* divi : dest = src1 / src2 */ + OP_ADD_UINT, /* addu : dest = src1 + src2 */ + OP_SUB_UINT, /* subu : dest = src1 - src2 */ + OP_MUL_UINT, /* mulu : dest = src1 * src2 */ + OP_DIV_UINT, /* divu : dest = src1 / src2 */ + OP_ADD_REAL, /* addr : dest = src1 + src2 */ + OP_SUB_REAL, /* subr : dest = src1 - src2 */ + OP_MUL_REAL, /* mulr : dest = src1 * src2 */ + OP_DIV_REAL, /* divr : dest = src1 / src2 */ + OP_INT_TO_REAL, /* itor : dest = src1 as real */ + OP_UINT_TO_REAL, /* utor : dest = src1 as real */ + OP_REAL_TO_INT, /* rtoi : dest = src1 as int */ + OP_REAL_TO_UINT, /* rtou : dest = src1 as uint */ + OP_JEQ_INT, /* jeqi : jump to address dest if src1 as int == src2 as int */ + OP_JGT_INT, /* jgti : jump to address dest if src1 as int > src2 as int*/ + OP_JLT_INT, /* jlti : jump to address dest if src1 as int < src2 as int */ + OP_JLE_INT, /* jlei : jump to address dest if src1 as int <= src2 as int */ + OP_JGE_INT, /* jgei : jump to address dest if src1 as int >= src2 as int*/ + OP_JEQ_UINT, /* jequ : jump to address dest if src1 as int == src2 as uint */ + OP_JGT_UINT, /* jgtu : jump to address dest if src1 as int > src2 as uint*/ + OP_JLT_UINT, /* jltu : jump to address dest if src1 as int < src2 as uint */ + OP_JLE_UINT, /* jleu : jump to address dest if src1 as int <= src2 as uint */ + OP_JGE_UINT, /* jgeu : jump to address dest if src1 as int >= src2 as uint*/ + OP_JEQ_REAL, /* jeqr : jump to address dest if src1 as real == src2 as real */ + OP_JGE_REAL, /* jgtr : jump to address dest if src1 as real >= src2 as real */ + OP_JGT_REAL, /* jltr : jump to address dest if src1 as real > src2 as real */ + OP_JLT_REAL, /* jler : jump to address dest if src1 as real < src2 as real */ + OP_JLE_REAL, /* jger : jump to address dest if src1 as real <= src2 as real */ + OP_INT_TO_STRING, /* itos : dest = src1 as str */ + OP_UINT_TO_STRING,/* utos : dest = src1 as str */ + OP_REAL_TO_STRING,/* rtos : dest = src1 as str */ + OP_CMP_STRING, /* cmps : dest = (str == src2) as bool */ + OP_STRING_TO_INT, /* stoi : dest = src1 as int */ + OP_STRING_TO_UINT,/* stou : dest = src1 as uint */ + OP_STRING_TO_REAL,/* stor : dest = src1 as real */ /* to remove (replace with device), just for testing for now */ - OP_DBG_PRINT_STRING, - OP_DBG_READ_STRING, + OP_DBG_PRINT_STRING,/* puts : write src1 as str to stdout */ + OP_DBG_READ_STRING, /* gets : read to dest as str from stdin */ } Opcode; /* defines a uint32 opcode */ @@ -76,7 +77,7 @@ typedef enum { typedef union value_u { int32_t i; /* Integers */ - float f; /* Float */ + fixed_t f; /* Fixed point */ uint32_t u; /* Unsigned integers, also used for pointer address */ char c[4]; /* 4 Byte char array for string packing */ } Value; @@ -123,7 +124,7 @@ typedef struct device_s { uint32_t flags; /* permissions, status, etc. */ } Device; -#define MEMORY_SIZE (640 * 480 + 65536) +#define MEMORY_SIZE ((640 * 480) + 65536) #define CODE_SIZE 8192 #define FRAMES_SIZE 128 #define STACK_SIZE 256 diff --git a/src/vm.c b/src/vm.c index e636b87..2e4d46d 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1,6 +1,8 @@ #include "vm.h" #include "device.h" -#include +#include "fixed.h" +#include "opcodes.h" +#include /* no inline fn in ANSI C :( */ #define COMPARE_AND_JUMP(type, accessor, op) \ @@ -12,17 +14,6 @@ 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 */ @@ -49,11 +40,17 @@ uint32_t str_alloc(VM *vm, const char *str, uint32_t length) { vm->memory[str_addr].u = length; vm->frames[vm->fp].allocated.end = vm->mp; - return str_addr; } -uint32_t real_alloc(VM *vm, float v) { +uint32_t float_as_real_alloc(VM *vm, float v) { + uint32_t addr = vm->mp; + vm->memory[vm->mp++].f = float_to_fixed(v); + vm->frames[vm->fp].allocated.end++; + return addr; +} + +uint32_t real_alloc(VM *vm, fixed_t v) { uint32_t addr = vm->mp; vm->memory[vm->mp++].f = v; vm->frames[vm->fp].allocated.end++; @@ -74,6 +71,94 @@ uint32_t int_alloc(VM *vm, int32_t v) { return addr; } +#define MAX_LEN_INT32 12 /* -2147483648 plus null terminator */ +#define MAX_LEN_UINT32 11 /* 4294967295 plus null terminator */ +#define MAX_LEN_FIXED 20 /* Enough for fixed-point representation */ +const char radix_set[11] = "0123456789"; + +/* Convert int32 to string */ +uint32_t int_to_string(VM *vm, int32_t v) { + char buffer[MAX_LEN_INT32] = {0}; + int32_t n = v; + bool neg = n < 0; + if (neg) + n = -n; + int i = MAX_LEN_INT32; + do { + buffer[--i] = radix_set[n % 10]; + n /= 10; + } while (n > 0); + if (neg) + buffer[--i] = '-'; + /* Ensure at least one digit is written for 0 */ + if (v == 0) + buffer[--i] = '0'; + + uint32_t len = MAX_LEN_INT32 - i; + return str_alloc(vm, buffer + i, len); +} + +/* Convert uint32 to string */ +uint32_t nat_to_string(VM *vm, uint32_t v) { + char buffer[MAX_LEN_INT32] = {0}; + uint32_t n = v; + int i = MAX_LEN_INT32; + do { + buffer[--i] = radix_set[n % 10]; + n /= 10; + } while (n > 0); + /* Ensure at least one digit is written for 0 */ + if (v == 0) + buffer[--i] = '0'; + + uint32_t len = MAX_LEN_INT32 - i; + return str_alloc(vm, buffer + i, len); +} + +/* Convert fixed-point to string */ +uint32_t real_to_string(VM *vm, fixed_t q) { + char buffer[MAX_LEN_FIXED] = {0}; + + + /* Extract integer part (top 16 bits) */ + int32_t int_part = q >> 16; + /* Extract fractional part (bottom 16 bits) */ + int32_t frac_part = q & 0xFFFF; + + + int32_t n = frac_part; + bool neg = n < 0; + if (neg) + n = -n; + int i = MAX_LEN_FIXED; + do { + buffer[--i] = radix_set[n % 10]; + n /= 10; + } while (n > 0); + if (neg) + buffer[--i] = '-'; + /* Ensure at least one digit is written for 0 */ + if (frac_part == 0) + buffer[--i] = '0'; + + + /* Convert integer part to string (reverse order) */ + do { + buffer[--i] = radix_set[int_part % 10]; + int_part /= 10; + } while (int_part > 0); + /* Ensure at least one digit is written for 0 */ + if (int_part == 0) + buffer[--i] = '0'; + + /* Null-terminate */ + buffer[i] = '\0'; + + int32_t len = (MAX_LEN_INT32 - i); + printf("i=%d, len=%d", i, len); + return str_alloc(vm, buffer + i, len); +} + /** * Step to the next opcode in the vm. */ @@ -108,7 +193,9 @@ bool step_vm(VM *vm) { return true; } case OP_LOAD: { - vm->frames[vm->fp].registers[dest].u = vm->code[vm->pc++].u; + uint32_t ptr = vm->code[vm->pc++].u; + Value v = vm->memory[ptr]; + vm->frames[vm->fp].registers[dest] = v; return true; } case OP_STORE: { @@ -134,11 +221,13 @@ bool step_vm(VM *vm) { return true; } case OP_PUSH: { - vm->stack[++vm->sp] = vm->frames[vm->fp].registers[dest]; + Value v = vm->frames[vm->fp].registers[dest]; + vm->stack[++vm->sp] = v; return true; } case OP_POP: { - vm->frames[vm->fp].registers[dest] = vm->stack[vm->sp--]; + Value v = vm->stack[vm->sp--]; + vm->frames[vm->fp].registers[dest] = v; return true; } case OP_MEM_ALLOC: { @@ -317,48 +406,98 @@ bool step_vm(VM *vm) { } 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_ADD_INT: { + vm->frames[vm->fp].registers[dest].i = + vm->frames[vm->fp].registers[src1].i + + vm->frames[vm->fp].registers[src2].i; + return true; + } + case OP_SUB_INT:{ + vm->frames[vm->fp].registers[dest].i = + vm->frames[vm->fp].registers[src1].i - + vm->frames[vm->fp].registers[src2].i; + return true; + } + case OP_MUL_INT:{ + vm->frames[vm->fp].registers[dest].i = + vm->frames[vm->fp].registers[src1].i * + vm->frames[vm->fp].registers[src2].i; + return true; + } + case OP_DIV_INT:{ + vm->frames[vm->fp].registers[dest].i = + vm->frames[vm->fp].registers[src1].i / + vm->frames[vm->fp].registers[src2].i; + return true; + } + case OP_ADD_UINT:{ + vm->frames[vm->fp].registers[dest].u = + vm->frames[vm->fp].registers[src1].u + + vm->frames[vm->fp].registers[src2].u; + return true; + } + case OP_SUB_UINT:{ + vm->frames[vm->fp].registers[dest].u = + vm->frames[vm->fp].registers[src1].u - + vm->frames[vm->fp].registers[src2].u; + return true; + } + case OP_MUL_UINT:{ + vm->frames[vm->fp].registers[dest].u = + vm->frames[vm->fp].registers[src1].u * + vm->frames[vm->fp].registers[src2].u; + return true; + } + case OP_DIV_UINT:{ + vm->frames[vm->fp].registers[dest].u = + vm->frames[vm->fp].registers[src1].u / + vm->frames[vm->fp].registers[src2].u; + return true; + } + case OP_ADD_REAL:{ + vm->frames[vm->fp].registers[dest].f = + fixed_add(vm->frames[vm->fp].registers[src1].f, + vm->frames[vm->fp].registers[src2].f); + return true; + } + case OP_SUB_REAL:{ + vm->frames[vm->fp].registers[dest].f = + fixed_sub(vm->frames[vm->fp].registers[src1].f, + vm->frames[vm->fp].registers[src2].f); + return true; + } + case OP_MUL_REAL: { + vm->frames[vm->fp].registers[dest].f = + fixed_mul(vm->frames[vm->fp].registers[src1].f, + vm->frames[vm->fp].registers[src2].f); + return true; + } + case OP_DIV_REAL: { + vm->frames[vm->fp].registers[dest].f = + fixed_div(vm->frames[vm->fp].registers[src1].f, + vm->frames[vm->fp].registers[src2].f); + return true; + } case OP_REAL_TO_INT: { vm->frames[vm->fp].registers[dest].i = - (int32_t)(vm->frames[vm->fp].registers[src1].f); + fixed_to_int(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); + int_to_fixed(vm->frames[vm->fp].registers[src1].i); return true; } case OP_REAL_TO_UINT: { + fixed_t f = vm->frames[vm->fp].registers[src1].f; + int32_t i = fixed_to_int(f); vm->frames[vm->fp].registers[dest].u = - (uint32_t)(vm->frames[vm->fp].registers[src1].f); + (uint32_t)i; return true; } case OP_UINT_TO_REAL: { vm->frames[vm->fp].registers[dest].f = - (float)(vm->frames[vm->fp].registers[src1].u); + int_to_fixed(vm->frames[vm->fp].registers[src1].u); return true; } case OP_REG_SWAP: { @@ -420,38 +559,48 @@ bool step_vm(VM *vm) { COMPARE_AND_JUMP(int32_t, i, ==); } case OP_JGT_REAL: { - COMPARE_AND_JUMP(float, u, >); + fixed_t value = vm->frames[vm->fp].registers[src1].f; + fixed_t value2 = vm->frames[vm->fp].registers[src2].f; + vm->pc = + fixed_gt(value, value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc; + return true; } case OP_JLT_REAL: { - COMPARE_AND_JUMP(float, u, <); + fixed_t value = vm->frames[vm->fp].registers[src1].f; + fixed_t value2 = vm->frames[vm->fp].registers[src2].f; + vm->pc = + fixed_lt(value, value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc; + return true; } case OP_JGE_REAL: { - COMPARE_AND_JUMP(float, u, >=); + fixed_t value = vm->frames[vm->fp].registers[src1].f; + fixed_t value2 = vm->frames[vm->fp].registers[src2].f; + vm->pc = + fixed_ge(value, value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc; + return true; } case OP_JLE_REAL: { - COMPARE_AND_JUMP(float, u, <=); + fixed_t value = vm->frames[vm->fp].registers[src1].f; + fixed_t value2 = vm->frames[vm->fp].registers[src2].f; + vm->pc = + fixed_le(value, value2) ? vm->frames[vm->fp].registers[dest].u : vm->pc; + return true; } 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 */ + int32_t a = vm->frames[vm->fp].registers[src1].i; + uint32_t ptr = int_to_string(vm, a); 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 */ + uint32_t a = vm->frames[vm->fp].registers[src1].u; + uint32_t ptr = nat_to_string(vm, a); 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 */ + fixed_t a = vm->frames[vm->fp].registers[src1].f; + uint32_t ptr = real_to_string(vm, a); vm->frames[vm->fp].registers[dest].u = ptr; return true; } @@ -484,7 +633,7 @@ bool step_vm(VM *vm) { return true; } case OP_DBG_PRINT_STRING: { - uint32_t ptr = (uint32_t)vm->frames[vm->fp].registers[src1].u; + uint32_t ptr = vm->frames[vm->fp].registers[src1].u; uint32_t length = vm->memory[ptr].u; uint32_t str_src = ptr + 1; uint32_t i; @@ -498,8 +647,8 @@ bool step_vm(VM *vm) { 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 addr1 = vm->frames[vm->fp].registers[src1].u; + uint32_t addr2 = 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 = diff --git a/src/vm.h b/src/vm.h index 2b0470d..53b859c 100644 --- a/src/vm.h +++ b/src/vm.h @@ -6,7 +6,8 @@ VM* init_vm(); bool step_vm(VM *vm); uint32_t str_alloc(VM *vm, const char *str, uint32_t length); -uint32_t real_alloc(VM *vm, float v); +uint32_t float_as_real_alloc(VM *vm, float v); +uint32_t real_alloc(VM *vm, fixed_t v); uint32_t nat_alloc(VM *vm, uint32_t v); uint32_t int_alloc(VM *vm, int32_t v);