#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 */ }