1
0
Fork 0
reality-engine/src/vm/vm.c

1085 lines
27 KiB
C

#include "vm.h"
#include "common.h"
#include "device.h"
#include "fixed.h"
#include "libc.h"
#include "opcodes.h"
#define MAX_LEN_REAL32 12
#define MAX_LEN_INT32 11
const char radix_set[11] = "0123456789";
#define COMPARE_AND_JUMP(type, op) \
do { \
i32 cond; \
u32 mask, target; \
u8 src1, src2; \
type value; \
type value2; \
target = read_u32(vm, code, vm->pc); \
vm->pc += 4; \
src1 = read_u8(vm, code, vm->pc); \
vm->pc++; \
src2 = read_u8(vm, code, vm->pc); \
vm->pc++; \
value = (type)frame->locals[src1]; \
value2 = (type)frame->locals[src2]; \
cond = !!(value op value2); \
mask = -(u32)cond; \
vm->pc = (target & mask) | (vm->pc & ~mask); \
return true; \
} while (0)
#define MATH_OP(type, op) \
do { \
u8 src1, src2, dest; \
u32 *regs = frame->locals; \
src1 = read_u8(vm, code, vm->pc); \
vm->pc++; \
src2 = read_u8(vm, code, vm->pc); \
vm->pc++; \
dest = read_u8(vm, code, vm->pc); \
vm->pc++; \
regs[dest] = (type)regs[src1] op(type) regs[src2]; \
return true; \
} while (0)
#define BIT_OP(op) \
do { \
u8 src1, src2, dest; \
u32 *regs = frame->locals; \
src1 = read_u8(vm, code, vm->pc); \
vm->pc++; \
src2 = read_u8(vm, code, vm->pc); \
vm->pc++; \
dest = read_u8(vm, code, vm->pc); \
vm->pc++; \
regs[dest] = regs[src1] op regs[src2]; \
return true; \
} while (0)
/* Set heap status for a register in current frame */
void set_heap_status(VM *vm, u8 reg, bool is_heap) {
u32 index = reg / 32;
u32 mask = 1 << (reg % 32);
if (is_heap) {
vm->frames[vm->fp].heap_mask[index] |= mask;
} else {
vm->frames[vm->fp].heap_mask[index] &= ~mask;
}
}
/* Check if register contains heap pointer */
bool is_heap_value(VM *vm, u8 reg) {
u32 index = reg / 32;
return (vm->frames[vm->fp].heap_mask[index] >> (reg % 32)) & 1;
}
u32 str_alloc(VM *vm, Frame *frame, const char *str, u32 length) {
u32 str_addr = vm->mp;
u32 i = 0;
vm->mp += 4;
while (i < length) {
vm->memory[vm->mp++] = str[i++];
}
vm->memory[vm->mp++] = '\0';
write_u32(vm, memory, str_addr, length);
frame->end = vm->mp;
return str_addr;
}
/**
* Step to the next opcode in the vm.
*/
bool step_vm(VM *vm) {
/* Get current instruction & Advance to next instruction */
u8 opcode = vm->code[vm->pc++];
Frame *frame = &vm->frames[vm->fp];
switch (opcode) {
case OP_EXIT: {
vm->flag = read_u32(vm, code, vm->pc);
return false;
}
case OP_CALL: {
u8 N, return_reg, src_reg, args[MAX_LOCALS];
Frame *child;
u32 jmp, mask, i;
jmp = read_u32(vm, code, vm->pc);
vm->pc += 4;
N = vm->code[vm->pc++];
for (i = 0; i < N; i++) {
args[i] = vm->code[vm->pc++];
}
return_reg = vm->code[vm->pc++];
frame->return_reg = return_reg;
if (vm->sp >= STACK_SIZE)
return false;
vm->stack[vm->sp++] = vm->pc;
if (vm->fp >= FRAMES_SIZE - 1)
return false;
vm->fp++;
child = &vm->frames[vm->fp];
child->start = vm->mp;
child->end = vm->mp;
child->return_reg = 0;
for (i = 0; i < N; i++) {
src_reg = args[i];
child->locals[i] = frame->locals[src_reg];
mask = 1 << (src_reg % 32);
if (frame->heap_mask[src_reg / 32] & mask) {
child->heap_mask[i / 32] |= 1 << (i % 32);
}
}
vm->pc = jmp;
return true;
}
case OP_RETURN: {
u8 child_return_reg;
u32 value;
u32 ptr;
u32 size;
u32 new_ptr;
Frame *child;
Frame *parent;
child_return_reg = vm->code[vm->pc++];
child = frame;
parent = &vm->frames[vm->fp - 1];
if (child_return_reg != 0xFF && parent->return_reg != 0xFF) {
value = child->locals[child_return_reg];
if (is_heap_value(vm, child_return_reg)) {
ptr = value;
size = *(u32 *)(vm->memory + ptr - 4);
new_ptr = parent->end;
if (parent->end + size + 4 > MEMORY_SIZE) {
return false;
}
*(u32 *)(vm->memory + new_ptr) = size;
memcopy(vm->memory + new_ptr + 4, vm->memory + ptr + 4, size);
parent->end += size + 4;
parent->locals[parent->return_reg] = new_ptr;
parent->heap_mask[parent->return_reg / 32] |= (1 << parent->return_reg);
} else {
parent->locals[parent->return_reg] = value;
parent->heap_mask[parent->return_reg / 32] &= ~(1 << parent->return_reg);
}
}
vm->pc = vm->stack[--vm->sp];
vm->mp = child->start;
vm->fp--;
return true;
}
case OP_LOAD_IMM: {
u32 v;
u8 dest;
v = read_u32(vm, code, vm->pc);
vm->pc += 4;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = v;
return true;
}
case OP_MALLOC: {
u8 src1, dest;
u32 size;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = vm->mp;
size = frame->locals[src1];
write_u32(vm, memory, vm->mp, size);
vm->mp += (size + 4);
set_heap_status(vm, dest, true); /* Mark as heap pointer */
return true;
}
case OP_LOAD_ABS_32: {
u32 v, ptr;
u8 dest;
ptr = read_u32(vm, code, vm->pc);
vm->pc += 4;
v = read_u32(vm, memory, ptr);
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = v;
return true;
}
case OP_LOAD_ABS_16: {
u32 v, ptr;
u8 dest;
ptr = read_u32(vm, code, vm->pc);
vm->pc += 4;
v = read_u16(vm, memory, ptr);
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = v;
return true;
}
case OP_LOAD_ABS_8: {
u32 v, ptr;
u8 dest;
ptr = read_u32(vm, code, vm->pc);
vm->pc += 4;
v = read_u8(vm, memory, ptr);
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = v;
return true;
}
case OP_LOAD_IND_32: {
u32 v, ptr;
u8 dest, src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
v = frame->locals[src1];
ptr = read_u32(vm, memory, v);
frame->locals[dest] = ptr;
return true;
}
case OP_LOAD_IND_16: {
u32 v;
u8 dest, src1;
u16 v16;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
v = frame->locals[src1];
v16 = read_u16(vm, memory, v);
frame->locals[dest] = v16;
return true;
}
case OP_LOAD_IND_8: {
u32 v;
u8 dest, src1;
u8 v8;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
v = frame->locals[src1];
v8 = read_u8(vm, memory, v);
frame->locals[dest] = v8;
return true;
}
case OP_LOAD_OFF_8: {
u32 v;
u8 dest, src1;
u32 offset;
u8 v8;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
offset = read_u32(vm, code, vm->pc);
vm->pc += 4;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
v = frame->locals[src1];
v8 = read_u8(vm, memory, (v + offset));
frame->locals[dest] = v8;
return true;
}
case OP_LOAD_OFF_16: {
u32 v;
u8 dest, src1;
u32 offset;
u16 v16;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
offset = read_u32(vm, code, vm->pc);
vm->pc += 4;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
v = frame->locals[src1];
v16 = read_u16(vm, memory, (v + offset));
frame->locals[dest] = v16;
return true;
}
case OP_LOAD_OFF_32: {
u32 v, ptr;
u8 dest, src1;
u32 offset;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
offset = read_u32(vm, code, vm->pc);
vm->pc += 4;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
v = frame->locals[src1];
ptr = read_u32(vm, memory, (v + offset));
frame->locals[dest] = ptr;
return true;
}
case OP_STORE_ABS_32: {
u32 v, ptr;
u8 src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = read_u32(vm, code, vm->pc);
vm->pc += 4;
v = frame->locals[src1];
write_u32(vm, memory, ptr, v);
return true;
}
case OP_STORE_ABS_16: {
u32 v, ptr;
u8 src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = read_u32(vm, code, vm->pc);
vm->pc += 4;
v = frame->locals[src1];
write_u16(vm, memory, ptr, v);
return true;
}
case OP_STORE_ABS_8: {
u32 v, ptr;
u8 src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = read_u32(vm, code, vm->pc);
vm->pc += 4;
v = frame->locals[src1];
write_u8(vm, memory, ptr, v);
return true;
}
case OP_STORE_IND_32: {
u32 v, ptr;
u8 dest, src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = frame->locals[dest];
v = frame->locals[src1];
write_u32(vm, memory, ptr, v);
return true;
}
case OP_STORE_IND_16: {
u32 ptr;
u8 dest, src1;
u16 v16;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = frame->locals[dest];
v16 = frame->locals[src1];
write_u16(vm, memory, ptr, v16);
return true;
}
case OP_STORE_IND_8: {
u32 ptr;
u8 dest, src1;
u8 v8;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = frame->locals[dest];
v8 = frame->locals[src1];
write_u8(vm, memory, ptr, v8);
return true;
}
case OP_STORE_OFF_8: {
u32 ptr;
u8 dest, src1;
u32 offset;
u8 v8;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
offset = read_u32(vm, code, vm->pc);
vm->pc += 4;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = frame->locals[dest];
v8 = frame->locals[src1];
write_u8(vm, memory, (ptr + offset), v8);
return true;
}
case OP_STORE_OFF_16: {
u32 ptr;
u8 dest, src1;
u32 offset;
u16 v16;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
offset = read_u32(vm, code, vm->pc);
vm->pc += 4;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = frame->locals[dest];
v16 = frame->locals[src1];
write_u16(vm, memory, (ptr + offset), v16);
return true;
}
case OP_STORE_OFF_32: {
u32 v, ptr;
u8 dest, src1;
u32 offset;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
offset = read_u32(vm, code, vm->pc);
vm->pc += 4;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = frame->locals[dest];
v = frame->locals[src1];
write_u32(vm, memory, (ptr + offset), v);
return true;
}
case OP_MEMSET_32: {
u32 i, start, end;
u8 value_reg = read_u8(vm, code, vm->pc++);
u8 count_reg = read_u8(vm, code, vm->pc++);
u8 dest_reg = read_u8(vm, code, vm->pc++);
u32 dest = frame->locals[dest_reg];
u32 value = frame->locals[value_reg];
u32 count = frame->locals[count_reg];
if (count == 0) {
vm->flag = 1;
return true;
}
start = dest;
end = dest + count;
if (start >= vm->mp || count > vm->mp || end > vm->mp) {
vm->flag = 0;
return true;
}
for (i = start; i < end; i += 4) {
write_u32(vm, memory, i, value);
}
frame->locals[0] = dest;
vm->flag = 1;
return true;
}
case OP_MEMSET_16: {
u32 i, start, end;
u8 value_reg = read_u8(vm, code, vm->pc++);
u8 count_reg = read_u8(vm, code, vm->pc++);
u8 dest_reg = read_u8(vm, code, vm->pc++);
u32 dest = frame->locals[dest_reg];
u16 value = (u16)(frame->locals[value_reg]);
u32 count = frame->locals[count_reg];
if (count == 0) {
vm->flag = 1;
return true;
}
start = dest;
end = dest + count;
if (start >= vm->mp || count > vm->mp || end > vm->mp) {
vm->flag = 0;
return true;
}
for (i = start; i < end; i += 2) {
write_u16(vm, memory, i, value);
}
frame->locals[0] = dest;
vm->flag = 1;
return true;
}
case OP_MEMSET_8: {
u32 i, start, end;
u8 value_reg = read_u8(vm, code, vm->pc++);
u8 count_reg = read_u8(vm, code, vm->pc++);
u8 dest_reg = read_u8(vm, code, vm->pc++);
u32 dest = frame->locals[dest_reg];
u8 value = (u8)(frame->locals[value_reg]);
u32 count = frame->locals[count_reg];
if (count == 0) {
vm->flag = 1;
return true;
}
start = dest;
end = dest + count;
if (start >= vm->mp || count > vm->mp || end > vm->mp) {
vm->flag = 0;
return true;
}
for (i = start; i < end; i++) {
write_u8(vm, memory, i, value);
}
frame->locals[0] = dest;
vm->flag = 1;
return true;
}
case OP_REG_MOV: {
u8 dest, src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = frame->locals[src1];
if (is_heap_value(vm, src1)) {
set_heap_status(vm, dest, true);
} else {
set_heap_status(vm, dest, false);
}
return true;
}
case OP_JMP: {
u32 jmp = read_u32(vm, code, vm->pc);
vm->pc = jmp; /* Jump to address */
return true;
}
case OP_JMPF: { /* error handling for syscall, jump if flag == 0 */
u32 mask;
u32 jmp = read_u32(vm, code, vm->pc);
mask = -(u32)(vm->flag == 0);
vm->pc = (jmp & mask) | (vm->pc & ~mask);
return true;
}
case OP_SYSCALL: {
u32 syscall_id;
syscall_id = read_u32(vm, code, vm->pc);
vm->pc += 4;
switch (syscall_id) {
case SYSCALL_DEVICE_OPEN: {
Device *dev;
u32 path_ptr, mode, device_ptr;
u8 path_reg, mode_reg, dest_reg;
path_reg = read_u8(vm, code, vm->pc);
vm->pc++;
mode_reg = read_u8(vm, code, vm->pc);
vm->pc++;
dest_reg = read_u8(vm, code, vm->pc);
vm->pc++;
path_ptr = frame->locals[path_reg];
mode = frame->locals[mode_reg];
dev = find_device_by_path(vm, (const char *)&vm->memory[path_ptr + 4]);
if (dev) {
if (dev->ops->open) {
/* return device plex to user */
device_ptr = vm->mp;
frame->locals[dest_reg] = device_ptr;
/* malloc size for device */
write_u32(vm, memory, device_ptr, dev->size);
vm->mp += (dev->size + 4);
/* set flag from user */
vm->flag = dev->ops->open(dev->data, mode, dev->handle,
&vm->memory[device_ptr + 4], dev->size);
} else {
vm->flag = 1; /* success, no open needed */
}
} else {
vm->flag = 0; /* error */
}
return true;
}
case SYSCALL_DEVICE_READ: {
Device *dev;
u32 device_ptr, buffer_ptr, size;
u8 device_reg, buffer_reg, size_reg, handle;
device_reg = read_u8(vm, code, vm->pc);
vm->pc++;
buffer_reg = read_u8(vm, code, vm->pc);
vm->pc++;
size_reg = read_u8(vm, code, vm->pc);
vm->pc++;
device_ptr = frame->locals[device_reg]; /* device pointer */
buffer_ptr = frame->locals[buffer_reg];
size = frame->locals[size_reg]; /* size */
handle = vm->memory[device_ptr + 4]; /* get device handle */
dev = &vm->devices[handle];
if (dev && dev->ops->read) {
vm->flag = dev->ops->read(dev->data, &vm->memory[buffer_ptr + 4], size);
} else {
vm->flag = 0;
}
return true;
}
case SYSCALL_DEVICE_REFRESH: {
Device *dev;
u32 handle, device_ptr;
u8 device_reg;
device_reg = read_u8(vm, code, vm->pc);
vm->pc++;
device_ptr = frame->locals[device_reg]; /* device pointer */
handle = vm->memory[device_ptr + 4]; /* get device handle */
dev = &vm->devices[handle];
if (dev && dev->ops->refresh) {
vm->flag = dev->ops->refresh(dev->data, &vm->memory[device_ptr + 4]);
} else {
vm->flag = 0;
}
return true;
}
case SYSCALL_DEVICE_WRITE: {
Device *dev;
u32 handle, buffer_ptr, size, device_ptr;
u8 device_reg, buffer_reg, size_reg;
device_reg = read_u8(vm, code, vm->pc);
vm->pc++;
buffer_reg = read_u8(vm, code, vm->pc);
vm->pc++;
size_reg = read_u8(vm, code, vm->pc);
vm->pc++;
device_ptr = frame->locals[device_reg]; /* device pointer */
buffer_ptr = frame->locals[buffer_reg]; /* R1: buffer pointer */
size = frame->locals[size_reg]; /* R2: size */
handle = vm->memory[device_ptr + 4]; /* get device handle */
dev = &vm->devices[handle];
if (dev && dev->ops->write) {
vm->flag = dev->ops->write(
dev->data, (const u8 *)&vm->memory[buffer_ptr + 4], size);
} else {
vm->flag = 0;
}
return true;
}
case SYSCALL_DEVICE_CLOSE: {
Device *dev;
u32 handle, device_ptr;
u8 device_reg;
device_reg = read_u8(vm, code, vm->pc);
vm->pc++;
device_ptr = frame->locals[device_reg]; /* device pointer */
handle = vm->memory[device_ptr + 4]; /* get device handle */
dev = &vm->devices[handle];
if (dev && dev->ops->close) {
i32 result = dev->ops->close(dev->data);
vm->flag = result;
} else {
vm->flag = 0;
}
return true;
}
case SYSCALL_DEVICE_IOCTL: {
Device *dev;
u32 handle, args_ptr, cmd, device_ptr;
u8 device_reg, cmd_reg, args_ptr_reg;
device_reg = read_u8(vm, code, vm->pc);
vm->pc++;
cmd_reg = read_u8(vm, code, vm->pc);
vm->pc++;
args_ptr_reg = read_u8(vm, code, vm->pc);
vm->pc++;
device_ptr = frame->locals[device_reg]; /* device pointer */
cmd = frame->locals[cmd_reg]; /* R1: ioctl command */
args_ptr = frame->locals[args_ptr_reg]; /* R2: args pointer */
handle = vm->memory[device_ptr + 4]; /* get device handle */
dev = &vm->devices[handle];
if (dev && dev->ops && dev->ops->ioctl) {
i32 result = dev->ops->ioctl(dev->data, cmd, &vm->memory[args_ptr]);
vm->flag = result;
} else {
vm->flag = 0; /* error or no ioctl support */
}
return true;
}
case SYSCALL_EXIT: {
return false;
}
default: {
vm->flag = 0; /* unknown syscall */
return true;
}
}
return true;
}
case OP_BIT_SHIFT_LEFT:
BIT_OP(<<);
case OP_BIT_SHIFT_RIGHT:
BIT_OP(>>);
case OP_BIT_SHIFT_R_EXT:
MATH_OP(i32, >>);
case OP_BAND:
BIT_OP(&);
case OP_BOR:
BIT_OP(|);
case OP_BXOR:
BIT_OP(^);
case OP_ADD_INT:
MATH_OP(i32, +);
case OP_SUB_INT:
MATH_OP(i32, -);
case OP_MUL_INT:
MATH_OP(i32, *);
case OP_DIV_INT:
MATH_OP(i32, /);
case OP_ABS_INT: {
u8 dest, src1;
i32 value;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
value = frame->locals[src1];
if (value < 0) {
value = -value;
}
frame->locals[dest] = value;
return true;
}
case OP_NEG_INT: {
u8 dest, src1;
i32 value;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
value = frame->locals[src1];
frame->locals[dest] = -value;
return true;
}
case OP_ADD_NAT:
MATH_OP(u32, +);
case OP_SUB_NAT:
MATH_OP(u32, -);
case OP_MUL_NAT:
MATH_OP(u32, *);
case OP_DIV_NAT:
MATH_OP(u32, /);
case OP_MUL_REAL: {
u8 dest, src1, src2;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
src2 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = real_mul(frame->locals[src1], frame->locals[src2]);
return true;
}
case OP_DIV_REAL: {
u8 dest, src1, src2;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
src2 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = real_div(frame->locals[src1], frame->locals[src2]);
return true;
}
case OP_ADD_REAL: {
u8 dest, src1, src2;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
src2 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = real_add(frame->locals[src1], frame->locals[src2]);
return true;
}
case OP_SUB_REAL: {
u8 dest, src1, src2;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
src2 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = real_sub(frame->locals[src1], frame->locals[src2]);
return true;
}
case OP_REAL_TO_INT: {
u8 dest, src1;
i32 value;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
value = frame->locals[src1];
frame->locals[dest] = real_to_int(value);
return true;
}
case OP_INT_TO_REAL: {
u8 dest, src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = int_to_real(frame->locals[src1]);
return true;
}
case OP_REAL_TO_NAT: {
u8 dest, src1;
u32 value;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
value = frame->locals[src1];
frame->locals[dest] = real_to_int(value);
return true;
}
case OP_NAT_TO_REAL: {
u8 dest, src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
frame->locals[dest] = int_to_real(frame->locals[src1]);
return true;
}
case OP_JEQ_NAT: {
COMPARE_AND_JUMP(u32, ==);
}
case OP_JNEQ_NAT: {
COMPARE_AND_JUMP(u32, !=);
}
case OP_JGT_NAT: {
COMPARE_AND_JUMP(u32, >);
}
case OP_JLT_NAT: {
COMPARE_AND_JUMP(u32, <);
}
case OP_JLE_NAT: {
COMPARE_AND_JUMP(u32, <=);
}
case OP_JGE_NAT: {
COMPARE_AND_JUMP(u32, >=);
}
case OP_JEQ_INT: {
COMPARE_AND_JUMP(i32, ==);
}
case OP_JNEQ_INT: {
COMPARE_AND_JUMP(i32, !=);
}
case OP_JGT_INT: {
COMPARE_AND_JUMP(i32, >);
}
case OP_JLT_INT: {
COMPARE_AND_JUMP(i32, <);
}
case OP_JLE_INT: {
COMPARE_AND_JUMP(i32, <=);
}
case OP_JGE_INT: {
COMPARE_AND_JUMP(i32, >=);
}
case OP_JEQ_REAL: {
COMPARE_AND_JUMP(i32, ==);
}
case OP_JNEQ_REAL: {
COMPARE_AND_JUMP(i32, !=);
}
case OP_JGT_REAL: {
COMPARE_AND_JUMP(i32, >);
}
case OP_JLT_REAL: {
COMPARE_AND_JUMP(i32, <);
}
case OP_JGE_REAL: {
COMPARE_AND_JUMP(i32, >=);
}
case OP_JLE_REAL: {
COMPARE_AND_JUMP(i32, <=);
}
case OP_INT_TO_STRING: {
char buffer[MAX_LEN_INT32];
i32 v;
i32 n;
u32 i = MAX_LEN_INT32;
u8 dest, src1;
bool neg;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
v = AS_INT(frame->locals[src1]);
n = v;
neg = n < 0;
if (neg)
n = -n;
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';
frame->locals[dest] = str_alloc(vm, frame, buffer + i, MAX_LEN_INT32 - i);
set_heap_status(vm, dest, true); /* Mark as heap pointer */
return true;
}
case OP_NAT_TO_STRING: {
char buffer[MAX_LEN_INT32];
u32 v;
u32 n;
u32 i = MAX_LEN_INT32;
u8 dest, src1;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
v = AS_NAT(frame->locals[src1]);
n = v;
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';
/* Copy from buffer[i] to buffer + MAX_LEN_INT32 */
frame->locals[dest] = str_alloc(vm, frame, buffer + i, MAX_LEN_INT32 - i);
set_heap_status(vm, dest, true); /* Mark as heap pointer */
return true;
}
case OP_REAL_TO_STRING: {
char buffer[MAX_LEN_REAL32];
r32 q;
bool neg;
u8 dest, src1;
i32 int_part;
u32 frac_part;
u32 i = MAX_LEN_REAL32;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
q = (r32)frame->locals[src1];
if (q < 0) {
q = -q;
}
int_part = q >> 16;
frac_part = q & 0xFFFF;
do {
buffer[--i] = radix_set[frac_part % 10];
frac_part /= 10;
} while (frac_part > 0);
buffer[--i] = '.';
neg = int_part < 0;
if (neg)
int_part = -int_part;
do {
buffer[--i] = radix_set[int_part % 10];
int_part /= 10;
} while (int_part > 0);
if (neg)
buffer[--i] = '-';
frame->locals[dest] = str_alloc(vm, frame, buffer + i, MAX_LEN_REAL32 - i);
set_heap_status(vm, dest, true); /* Mark as heap pointer */
return true;
}
case OP_STRLEN: {
u8 dest, src1;
u32 ptr, length;
src1 = read_u8(vm, code, vm->pc);
vm->pc++;
dest = read_u8(vm, code, vm->pc);
vm->pc++;
ptr = frame->locals[src1];
length = read_u32(vm, memory, ptr);
frame->locals[dest] = length;
return true;
}
case OP_STRCAT: {
/* not implemented yet */
return false;
}
case OP_STR_GET_CHAR: {
/* not implemented yet */
return false;
}
case OP_STR_FIND_CHAR: {
/* not implemented yet */
return false;
}
case OP_STR_SLICE: {
/* not implemented yet */
return false;
}
case OP_STRING_TO_INT: {
/* not implemented yet */
return false;
}
case OP_STRING_TO_NAT: {
/* not implemented yet */
return false;
}
case OP_STRING_TO_REAL: {
/* not implemented yet */
return false;
}
}
return false; /* something bad happened */
}