#ifndef UNDAR_VM_H #define UNDAR_VM_H #include "libc.h" typedef enum { OP_HALT, /* - `halt` | halt execution */ OP_CALL, /* ptr `call` | creates a new frame */ OP_RETURN, /* - `return` | returns from a frame to the parent frame */ OP_SYSCALL, /* id args mem_ptr `syscall` - | id args mem_ptr : does a system call based on id with args */ OP_LOAD_8, /* dest `load-8` u8 | push memory[obj1] onto stack as u8 */ OP_LOAD_16, /* dest `load-16` u16 | push memory[obj1] onto stack as u16 */ OP_LOAD_32, /* dest `load` u32 | push memory[obj1] onto stack as u32 */ OP_STORE_8, /* dest obj1 `store-8` - | memory[dest] = obj1 << 8 */ OP_STORE_16, /* dest obj1 `store-16`- | memory[dest] = obj1 << 16 */ OP_STORE_32, /* dest obj1 `store` - | memory[dest] = obj1 */ OP_MALLOC, /* size `malloc` ptr | allocate 'size + 4' of memory and push ptr to memory on stack */ OP_PUSH_8, /* const `push8` obj1 | push a 8 bit const onto the stack */ OP_PUSH_16, /* const `push16` obj1 | push a 16 bit const onto the stack */ OP_PUSH_32, /* const `push32` obj1 | push a 32 bit const onto the stack */ OP_POP, /* - `pop` - | removes top item from the stack */ OP_SET, /* obj1 local `set` - | set the value of the local slot */ /*OP_SET_ADDRESS, addr local `set_address` - | set the address of the value of the local slot */ OP_GET, /* local `get` obj1 | get the value of the local slot */ /*OP_GET_ADDRESS, local `get_address` obj1 | get the address of the value of the local slot */ OP_DUP, /* obj1 `dup` obj1 obj1 | duplicates the top of the stack */ OP_EXCH, /* obj2 obj1 `exch` obj1 obj2 | swaps the top two values on the stack */ OP_OVER, /* obj2 obj1 `over` obj2 | copys the 2nd to the top element and pushes to the stack */ OP_PICK, /* N `pick` objN | gets the nth element on the stack and pushes it on top */ OP_ROT, /* obj3 obj2 obj1 `rot` obj2 obj1 obj3 | takes the 3rd element and moves it to the top of the stack */ OP_DEPTH, /* - `depth` heap_count | pushes the number of elements on the stack to the stack*/ OP_MEM_ALLOC, /* size `alloc` ptr | allocate 'size + 4' of memory and push ptr to memory on stack */ OP_MEM_CPY_8, /* size src dest `memcpy_8` - | memory[src..src+size] = memory[dest..dest+size] */ OP_MEM_CPY_16, /* size src dest `memcpy_16` - | memory[src..src+size] = memory[dest..dest+size] */ OP_MEM_CPY_32, /* size src dest `memcpy_32` - | memory[src..src+size] = memory[dest..dest+size] */ OP_MEM_SET_8, /* size src dest `memset_8` - | memory[dest..dest+size] = local[src] as u8 */ OP_MEM_SET_16, /* size src dest `memset_16` - | memory[dest..dest+size] = local[src] as u16 */ OP_MEM_SET_32, /* size src dest `memset_32` - | memory[dest..dest+size] = local[src] as u32 */ OP_ADD_INT, /* obj2 obj1 `add_int` obj | obj1 + obj2 then push result on stack */ OP_SUB_INT, /* obj2 obj1 `sub_int` obj | obj1 - obj2 then push result on stack */ OP_MUL_INT, /* obj2 obj1 `mul_int` obj | obj1 * obj2 then push result on stack */ OP_DIV_INT, /* obj2 obj1 `div_int` obj | obj1 / obj2 then push result on stack */ OP_ADD_NAT, /* obj2 obj1 `add_nat` obj | obj1 + obj2 then push result on stack */ OP_SUB_NAT, /* obj2 obj1 `sub_nat` obj | obj1 - obj2 then push result on stack */ OP_MUL_NAT, /* obj2 obj1 `mul_nat` obj | obj1 * obj2 then push result on stack */ OP_DIV_NAT, /* obj2 obj1 `div_nat` obj | obj1 / obj2 then push result on stack */ OP_ADD_REAL, /* obj2 obj1 `add_real` obj | obj1 + obj2 then push result on stack */ OP_SUB_REAL, /* obj2 obj1 `sub_real` obj | obj1 - obj2 then push result on stack */ OP_MUL_REAL, /* obj2 obj1 `mul_real` obj | obj1 * obj2 then push result on stack */ OP_DIV_REAL, /* obj2 obj1 `div_real` obj | obj1 / obj2 then push result on stack */ OP_INT_TO_REAL, /* obj1 `int_to_real` obj1 as real | casts an int to a fixed number */ OP_INT_TO_NAT, /* obj1 `int_to_nat` obj1 as nat | casts an int to a unsigned int */ OP_NAT_TO_REAL, /* obj1 `nat_to_real` obj1 as real | casts a unsigned int to a fixed number */ OP_NAT_TO_INT, /* obj1 `nat_to_int` obj1 as int | casts a unsigned int to an int */ OP_REAL_TO_INT, /* obj1 `real_to_int` obj1 as int | casts a fixed number to an int */ OP_REAL_TO_NAT, /* obj1 `real_to_nat` obj1 as nat | casts a fixed number to an unsigned int */ OP_BIT_SHIFT_LEFT, /* obj2 obj1 `bit_shift_left` obj | src1] << locals[src2] */ OP_BIT_SHIFT_RIGHT, /* obj2 obj1 `bit_shift_right` obj | src1] >> locals[src2] */ OP_BIT_SHIFT_R_EXT, /* obj2 obj1 `bit_shift_r_ext` obj | src1 >> src2 then cast result as i32 */ OP_BIT_AND, /* obj2 obj1 `bit_and` obj | obj1 & obj2 */ OP_BIT_OR, /* obj2 obj1 `bit_or` obj | obj1 | obj2 */ OP_BIT_XOR, /* obj2 obj1 `bit_xor` obj | obj1 ^ obj2 */ OP_NEG, /* obj1 `neg` -obj | -obj1 */ OP_NOT, /* obj1 `bit_xor` !obj | not obj1 */ OP_JMP, /* pc `jump` | jump unconditionally */ OP_JMP_FLAG, /* pc `jump_if_flag` | jump to pc if flag > 0 */ OP_JMP_IF, /* obj1 pc `jump_if | jump to pc if obj1 != 0 */ OP_EQ, /* obj2 obj1 `eq` | obj1 == obj2*/ OP_NE, /* obj2 obj1 `ne` | obj1 != obj2*/ OP_GT, /* obj2 obj1 `gt` | obj1 > obj2*/ OP_LT, /* obj2 obj1 `lt` | obj1 < obj2*/ OP_LE, /* obj2 obj1 `le` | obj1 <= obj2*/ OP_GE, /* obj2 obj1 `ge` | obj1 >= obj2*/ OP_INT_TO_STR, /* obj1 `int_to_string` str_ptr | convert obj1 to str */ OP_NAT_TO_STR, /* obj1 `nat_to_string` str_ptr | convert obj1 to str */ OP_REAL_TO_STR, /* obj1 `real_to_string` str_ptr | convert obj1 to str */ OP_STR_TO_INT, /* str_ptr `string_to_int` obj | convert obj1 to int */ OP_STR_TO_NAT, /* str_ptr `string_to_nat` obj | convert obj1 to nat */ OP_STR_TO_REAL, /* str_ptr `string_to_real` obj | convert obj1 to real */ OP_MAX_OPCODE /* not an opcode count of instructions */ } Opcode; typedef enum { SYSCALL_CONSOLE_WRITE, SYSCALL_CONSOLE_READ, SYSCALL_MAX } Syscall; typedef struct frame_s Frame; struct frame_s { u32 return_pc; u32 start_mp; }; extern u32 pc; /* program counter */ extern u32 cp; /* code pointer */ extern u32 mp; /* memory pointer */ extern u32 fp; /* frame pointer */ extern u32 sp; /* stack pointer */ extern u8 lp; /* locals pointer */ extern u8 status; /* status flag */ extern u8 interrupt; /* device interrupt */ extern u32 *stack; /* stack */ extern u8 *code; /* code */ extern u8 *mem; /* memory */ #define READ_U8(addr) (mem[addr]) #define READ_U16(addr) \ (((u16)mem[(addr) + 1] << 8) | ((u16)mem[(addr)])) #define READ_U32(addr) \ (((u32)mem[(addr) + 3] << 24) | \ ((u32)mem[(addr) + 2] << 16) | \ ((u32)mem[(addr) + 1] << 8) | ((u32)mem[(addr)])) #define WRITE_U8(addr, value) \ do { \ mem[addr] = (value) & 0xFF; \ } while (0) #define WRITE_U16(addr, value) \ do { \ mem[addr] = (value) & 0xFF; \ mem[addr + 1] = ((value) >> 8) & 0xFF; \ } while (0) #define WRITE_U32(addr, value) \ do { \ mem[addr] = (value) & 0xFF; \ mem[addr + 1] = ((value) >> 8) & 0xFF; \ mem[addr + 2] = ((value) >> 16) & 0xFF; \ mem[addr + 3] = ((value) >> 24) & 0xFF; \ } while (0) #define MATH_OP(type, op) \ do { \ type b = (type)stack[--sp]; \ type a = (type)stack[--sp]; \ stack[sp++] = (type)(a op b); \ return true; \ } while (0) #define MATH_OP_NO_CAST(op) \ do { \ u32 b = stack[--sp]; \ u32 a = stack[--sp]; \ stack[sp++] = a op b; \ return true; \ } while (0) extern bool init_vm(); extern u32 syscall(u32 id, u32 mem_ptr); bool step_vm(); u32 str_alloc(char *str, u32 length); #endif