1085 lines
27 KiB
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 */
|
|
}
|