Add opcodes, get rid of VM for less redirection, add encode/decode macros

This commit is contained in:
zongor 2026-01-03 23:10:17 -08:00
parent f465cdb39a
commit 6b625c4663
3 changed files with 433 additions and 269 deletions

View File

@ -11,8 +11,8 @@ especially microcontrollers and some retro game systems like the PS1.
** Numbers
| type | size (bytes) | description |
|------+--------------+---------------------------------------|
| u8 | 1 | unsigned 8bit, alias =char= and =byte= |
| bool | 1 | unsigned 8bit, =false= or =true= |
| u8 | 1 | unsigned 8bit, alias =char= and =byte= |
| i8 | 1 | signed 8bit for interop |
| u16 | 2 | unsigned 16bit for interop |
| i16 | 2 | signed 16bit for interop |
@ -30,130 +30,103 @@ In the C version you can see these are two seperate arrays 'code' and 'mem'.
During compilation constants and local variables are put onto 'mem'
* Opcodes
*** All 32 bit instructions (registers are all 32 bit values)
**** Type A: [8:opcode][8:dest][8:src1][8:src2]
- call : dest num-of-args ptr-to-function
- return : dest return-arg 0
- syscall : id num-of-args ptr-to-memory
- load-indirect-8 : dest src1 0
- load-indirect-16 : dest src1 0
- load-indirect-32 : dest src1 0
- load-absolute-8 : dest src1 0
- load-absolute-16 : dest src1 0
- load-absolute-32 : dest src1 0
- load-offset-8 : dest src1 src2
- load-offset-16 : dest src1 src2
- load-offset-32 : dest src1 src2
- store-absolute-8 : dest src1 0
- store-indirect-8 : dest src1 0
- store-offset-8 : dest src1 src2
- store-absolute-16 : dest src1 0
- store-indirect-16 : dest src1 0
- store-offset-16 : dest src1 src2
- store-absolute-32 : dest src1 0
- store-indirect-32 : dest src1 0
- store-offset-32 : dest src1 src2
- alloc : dest size 0
- memset-8 : dest src1 count
- memset-16 : dest src1 count
- memset-32 : dest src1 count
- memcpy-8 : dest src1 count
- memcpy-16 : dest src1 count
- memcpy-32 : dest src1 count
- mov : dest src1 0
- add-int : dest src1 src2
- sub-int : dest src1 src2
- mul-int : dest src1 src2
- div-int : dest src1 src2
- neg-int : dest src1 src2
- abs-int : dest src1 src2
- add-nat : dest src1 src2
- sub-nat : dest src1 src2
- mul-nat : dest src1 src2
- div-nat : dest src1 src2
- neg-nat : dest src1 src2
- abs-nat : dest src1 src2
- add-real : dest src1 src2
- sub-real : dest src1 src2
- mul-real : dest src1 src2
- div-real : dest src1 src2
- neg-real : dest src1 src2
- abs-real : dest src1 src2
- int-to-real : dest src1 src2
- int-to-nat : dest src1 src2
- nat-to-real : dest src1 src2
- nat-to-int : dest src1 src2
- real-to-int : dest src1 src2
- real-to-nat : dest src1 src2
- shift-left : dest src1 src2
- shift-right : dest src1 src2
- shift-right-extend : dest src1 src2
- and : dest src1 src2
- or : dest src1 src2
- xor : dest src1 src2
- jump-absolute : dest 0 0
- jump-offset : dest src1 0
- jmp-flag : dest 0 0 | jump if flag > 0
- jeq-int : dest src1 src2
- jne-int : dest src1 src2
- jgt-int : dest src1 src2
- jlt-int : dest src1 src2
- jle-int : dest src1 src2
- jge-int : dest src1 src2
- jeq-nat : dest src1 src2
- jne-nat : dest src1 src2
- jgt-nat : dest src1 src2
- jlt-nat : dest src1 src2
- jle-nat : dest src1 src2
- jge-nat : dest src1 src2
- jeq-real : dest src1 src2
- jne-real : dest src1 src2
- jgt-real : dest src1 src2
- jlt-real : dest src1 src2
- jle-real : dest src1 src2
- jge-real : dest src1 src2
| 2^n | count |
|----+-------|
| 2^3 | 8 |
| 2^4 | 16 |
| 2^5 | 32 |
| 2^6 | 64 |
| 2^8 | 128 |
**** Type B: [8:opcode][8:dest][16:immediate]
- load-immediate : dest imm : for small 16 bit consts, and lower part of 32 bit consts
- load-upper-immediate : dest imm : for large 32 bit consts
** could be encoded
- op type [this is to maximize jump immidate and load immidate size]
- memory location
- local value / register
- local value type
**** Type C: [8:opcode][24:immediate]
- halt : immediate is unused (all zeros)
- jump-immediate : immediate jump
*** Simplest
[opcode][dest][src1][src2]
[8][8][8][8]
*** Maximize inline jump and load-immidate
[0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0]
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 | 0 0 ] noop
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 1 | 0 0 ] call
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 1 0 | 0 0 ] return
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 1 1 | 0 0 ] syscall?
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 1][0 0 0 | 1 0 0 | 0 0 ] exit
[0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 | 0 1 ] jump ~1GB range
[0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 | 1 0 ] load-immidate 2^30 max
*** multibyte ops
- ones that would be easier if they were multibyte
- jump
- load immidate
- syscall
- call
0 0 - system, lowest because lower opcodes are faster
0 1 - memory
1 0 - math
1 1 - jump
[0 0][0 0 0 0 0 0] = [system][no op]
[0 0][1 0 0 0 0 0] = [system][loadimm]
[0 0][0 0 0 0 0 0] = [system][return]
J [0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 | 0 1]
L [0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 0 0 0 0 0 0 0][0 | o o o o o | 1 0]
R [r r r r r r | r r][r r r r | r r r r][r r | b b | 0 0 0 0][o o o o o o | 1 1]
[0 0 0 0 0 | r r r][r r r r | r r r r][r r | r r r r r r][b b | t | o o o | 1 1]
[0 0 0 | r r r r r][0 0 0 | r r r r r][0 0 0 | r r r r r][b b | t | o o o | 1 1]
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 | 1 | 0 0 1 | 1 1] [math][add][f][8]
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 | 1 | 0 1 0 | 1 1] [math][sub][f][8]
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 | 1 | 0 1 1 | 1 1] [math][mul][f][8]
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 | 1 | 1 0 0 | 1 1] [math][div][f][8]
[0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 0 | 0 0 0 0 0][0 0 | 1 | 1 0 1 | 1 1] [math][mod][f][8]
[0 1][0 0 1][0][0 0] = [math][sub][f][16]
[0 1][0 1 0][0][0 0] = [math][mul][f][32]
[0 1][0 1 1][0][0 0] = [math][div][f][?]
[0 1][1 0 0][0][0 0] = [math][and][f][?]
[1 1][0][0 0][0 0] = [jmp][u][eq]
[1 1][0][0 0][0 0] = [jmp][u][ne]
[1 1][0][0 0][0 0] = [jmp][u][lt]
[1 1][0][0 0][0 0] = [jmp][u][gt]
[1 1][1][0 0 0][0 0] = [jmp][s][le]
[1 1][1][0 0 0][0 0] = [jmp][s][ge]
[1 1][1][0 0 0][0 0] = [jmp][s][]
[1 1][1][0 0 0][0 0] = [jmp][s][]
3 2 2 1
[...] i 32 0
3 1 3 1
[...] u rl j
[...] u ab j
[...] u eq j
[...] u nq j
[...] u lt j
[...] u le j
[...] u gt j
[...] u ge j
[jmp][dest][18]
[lli][dest][2]
int 3
add
sub
mul
div
jeq
jne
jlt
jle
jgt
jge
Maybe opcodes could be Huffman encoded? That way the smaller opcodes get more operand data within the u32
At compile time each function gets N number of locals (up to 255). These are allocated onto memory along with everything else, but they come before the heap values.
Maybe instead of push pushing onto a stack it instead pushes onto a child frame? Pops would just mark what locals need to be replaced by the child function? Maybe we can get rid of call/return and just use jumps?
U8 u8 u8 u8
Push/pop parent_local child_local metadata
** Maybe more flexible calling convention?
At compile time each function gets N number of locals (up to 255). These are allocated onto memory along with everything else, but they come before the heap values.
Memory-to-memory with register characteristics?
Passed in values
@ -185,63 +158,3 @@ Update the new pointers local position.
Add the new size to the mp.
Repeat for each returned value that is a replaced heap value.
Opcodes are variable sized, each part is 8 bytes to maximize portability.
8 bit
Type Z: [u8:opcode]
16 bit
Type Q: [u8:opcode][u8:dest]
24 bit
Type I: [u8:opcode][u8:dest][u8:src1]
32 bit
Type M: [u8:opcode][u8:dest][u8:src1][u8:src2]
40 bit
Type J: [u8:opcode][u32:dest]
56 bit
Type S: [u8:opcode][u8:dest][u8:src1][u32:imm]
Fixed size
*** All 32 bit instructions (registers are all 32 bit values)
**** Type I: [8:opcode][8:dest][16:immediate]
- load-immediate : for small 16 bit consts, and lower part of 32 bit consts
- load-upper-immediate : for large 32 bit consts
- load-indirect : dest
- store-indirect : dest
- load-absolute : dest
- store-absolute : dest
- jump-absolute : dest
**** Type M: [8:opcode][8:dest][8:src1][8:src2]
- load-offset dest src1 src2
- store-offset dest src1 src2
- call dest num-of-args ptr-to-function
- return dest return-arg
- syscall id num-of-args ptr-to-memory
- memset dest src1 count
- long-jump : src1 used as address
- add
- sub
- mul
- div
- and
- or
- xor
- shift-left
- shift-right
- shift-right-sign-extend
- neg
- abs
- mov : move register value
- alloc : dest size unused
- jeq (jump eq)
- jne (jump not eq)
- jgt (jump greater than)
- jlt (jump less than)
- jle (jump less than or eq)
- jge (jump greater then or eq)
- jf (jump if flag < zero)
**** Type Z: [8:opcode][24:immediate]
- noop : immediate is unused (all zeros)
- jump : immediate jump

185
vm/vm.c
View File

@ -1,6 +1,187 @@
#include "vm.h"
bool step_vm(VM *vm) {
USED(vm);
u32 mp; /* memory pointer */
u32 cp; /* code pointer */
u32 pc; /* program counter */
u32 flag; /* flag */
u32 *code; /* code */
u8 *mem; /* memory */
bool step_vm() {
u32 instruction = code[pc];
u8 opcode = DECODE_OP(instruction);
switch (opcode) {
case OP_HALT: {
return false;
}
case OP_CALL: {
}
case OP_RETURN: {
}
case OP_SYSCALL: {
}
case OP_LOAD_IMM: {
}
case OP_LOAD_UPPER_IMM: {
}
case OP_LOAD_IND_8: {
}
case OP_LOAD_IND_16: {
}
case OP_LOAD_IND_32: {
}
case OP_LOAD_ABS_8: {
}
case OP_LOAD_ABS_16: {
}
case OP_LOAD_ABS_32: {
}
case OP_LOAD_OFF_8: {
}
case OP_LOAD_OFF_16: {
}
case OP_LOAD_OFF_32: {
}
case OP_STORE_ABS_8: {
}
case OP_STORE_ABS_16: {
}
case OP_STORE_ABS_32: {
}
case OP_STORE_IND_8: {
}
case OP_STORE_IND_16: {
}
case OP_STORE_IND_32: {
}
case OP_STORE_OFF_8: {
}
case OP_STORE_OFF_16: {
}
case OP_STORE_OFF_32: {
}
case OP_MEM_ALLOC: {
}
case OP_MEM_CPY: {
}
case OP_MEM_SET_8: {
}
case OP_MEM_SET_16: {
}
case OP_MEM_SET_32: {
}
case OP_REG_MOV: {
}
case OP_ADD_INT: {
}
case OP_SUB_INT: {
}
case OP_MUL_INT: {
}
case OP_DIV_INT: {
}
case OP_ABS_INT: {
}
case OP_NEG_INT: {
}
case OP_ADD_NAT: {
}
case OP_SUB_NAT: {
}
case OP_MUL_NAT: {
}
case OP_DIV_NAT: {
}
case OP_ABS_NAT: {
}
case OP_NEG_NAT: {
}
case OP_ADD_REAL: {
}
case OP_SUB_REAL: {
}
case OP_MUL_REAL: {
}
case OP_DIV_REAL: {
}
case OP_ABS_REAL: {
}
case OP_NEG_REAL: {
}
case OP_INT_TO_REAL: {
}
case OP_INT_TO_NAT: {
}
case OP_NAT_TO_REAL: {
}
case OP_NAT_TO_INT: {
}
case OP_REAL_TO_INT: {
}
case OP_REAL_TO_NAT: {
}
case OP_BIT_SHIFT_LEFT: {
}
case OP_BIT_SHIFT_RIGHT: {
}
case OP_BIT_SHIFT_R_EXT: {
}
case OP_BIT_AND: {
}
case OP_BIT_OR: {
}
case OP_BIT_XOR: {
}
case OP_JMP_IMM: {
}
case OP_JMP_ABS: {
}
case OP_JMP_OFF: {
}
case OP_JMP_FLAG: {
}
case OP_JEQ_INT: {
}
case OP_JNE_INT: {
}
case OP_JGT_INT: {
}
case OP_JLT_INT: {
}
case OP_JLE_INT: {
}
case OP_JGE_INT: {
}
case OP_JEQ_NAT: {
}
case OP_JNE_NAT: {
}
case OP_JGT_NAT: {
}
case OP_JLT_NAT: {
}
case OP_JLE_NAT: {
}
case OP_JGE_NAT: {
}
case OP_JEQ_REAL: {
}
case OP_JNE_REAL: {
}
case OP_JGE_REAL: {
}
case OP_JGT_REAL: {
}
case OP_JLT_REAL: {
}
case OP_JLE_REAL: {
}
}
/* something went very wrong */
flag = 255;
return false;
}

246
vm/vm.h
View File

@ -3,98 +3,168 @@
#include "libc.h"
typedef enum {
OP_NOOP, /* noop : no nothing for 1 cycle */
OP_CALL, /* call : creates a new frame */
OP_RETURN, /* return : returns from a frame to the parent frame */
OP_SYSCALL, /* syscall : syscall(u8 src1 count_args, u32 ptr_memory) */
OP_LOAD_IMM, /* load_immediate : locals[dest] = constant */
OP_LOAD_IND_8, /* load_indirect_8 : locals[dest] = memory[locals[src1]] as u8 */
OP_LOAD_IND_16, /* load_indirect_16 : locals[dest] = memory[locals[src1]] as u16 */
OP_LOAD_IND_32, /* load_indirect_32 : locals[dest] = memory[locals[src1]] as u32 */
OP_LOAD_ABS_8, /* load_absolute_8 : locals[dest] = memory[src1] as u8 */
OP_LOAD_ABS_16, /* load_absolute_16 : locals[dest] = memory[src1] as u16 */
OP_LOAD_ABS_32, /* load_absolute_32 : locals[dest] = memory[src1] as u32 */
OP_LOAD_OFF_8, /* load_offset_8 : locals[dest] = memory[locals[src1] + offset] as u8 */
OP_LOAD_OFF_16, /* load_offset_16 : locals[dest] = memory[locals[src1] + offset] as u16 */
OP_LOAD_OFF_32, /* load_offset_32 : locals[dest] = memory[locals[src1] + offset] as u32 */
OP_STORE_ABS_8, /* store_absolute_8 : memory[dest] = src1 && 0xFF */
OP_STORE_ABS_16, /* store_absolute_16 : memory[dest] = src1 && 0xFFFF */
OP_STORE_ABS_32, /* store_absolute_32 : memory[dest] = src1 */
OP_STORE_IND_8, /* store_indirect_8 : memory[dest] = locals[src1] && 0xFF */
OP_STORE_IND_16, /* store_indirect_16 : memory[dest] = locals[src1] && 0xFFFF*/
OP_STORE_IND_32, /* store_indirect_32 : memory[dest] = locals[src1] */
OP_STORE_OFF_8, /* store_offset_8 : memory[locals[dest] + offset] = locals[src1] && 0xFF */
OP_STORE_OFF_16, /* store_offset_16 : memory[locals[dest] + offset] = locals[src1] && 0xFFFF */
OP_STORE_OFF_32, /* store_offset_32 : memory[locals[dest] + offset] = locals[src1] */
OP_MALLOC, /* malloc : dest = fat ptr to memory of ((src1 as size) + 4) */
OP_MEMSET_8, /* memset_8 : dest <-> dest+count = src1 as u8 */
OP_MEMSET_16, /* memset_16 : dest <-> dest+count = src1 as u16 */
OP_MEMSET_32, /* memset_32 : dest <-> dest+count = src1 as u32 */
OP_REG_MOV, /* register_move : locals[dest] = locals[src1] */
OP_ADD_INT, /* add_int : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_INT, /* sub_int : locals[dest] = locals[src1] _ locals[src2] */
OP_MUL_INT, /* mul_int : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_INT, /* div_int : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_INT, /* abs_int : locals[dest] = | locals[src1] | */
OP_NEG_INT, /* neg_int : locals[dest] = -locals[src1] */
OP_ADD_NAT, /* add_nat : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_NAT, /* sub_nat : locals[dest] = locals[src1] _ locals[src2] */
OP_MUL_NAT, /* mul_nat : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_NAT, /* div_nat : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_NAT, /* abs_nat : locals[dest] = | locals[src1] | */
OP_NEG_NAT, /* neg_nat : locals[dest] = -locals[src1] */
OP_ADD_REAL, /* add_real : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_REAL, /* sub_real : locals[dest] = locals[src1] _ locals[src2] */
OP_MUL_REAL, /* mul_real : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_REAL, /* div_real : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_REAL, /* abs_real : locals[dest] = | locals[src1] | */
OP_NEG_REAL, /* neg_real : locals[dest] = _locals[src1] */
OP_INT_TO_REAL, /* int_to_real : locals[dest] = locals[src1] as real */
OP_INT_TO_NAT, /* int_to_nat : locals[dest] = locals[src1] as nat */
OP_NAT_TO_REAL, /* nat_to_real : locals[dest] = locals[src1] as real */
OP_NAT_TO_INT, /* nat_to_int : locals[dest] = locals[src1] as int */
OP_REAL_TO_INT, /* real_to_int : locals[dest] = locals[src1] as int */
OP_REAL_TO_NAT, /* real_to_nat : locals[dest] = locals[src1] as nat */
OP_BIT_SHIFT_LEFT, /* bit_shift_left : locals[dest] = locals[src1] << locals[src2] */
OP_BIT_SHIFT_RIGHT,/* bit_shift_right : locals[dest] = locals[src1] >> locals[src2] */
OP_BIT_SHIFT_R_EXT,/* bit_shift_r_ext : locals[dest] as i32 = locals[src1] >> locals[src2] */
OP_BAND, /* bit_and : locals[dest] = locals[src1] & locals[src2] */
OP_BOR, /* bit_or : locals[dest] = locals[src1] | locals[src2] */
OP_BXOR, /* bit_xor : locals[dest] = locals[src1] ^ locals[src2] */
OP_JMP, /* jump : jump to &dest unconditionally */
OP_JMPF, /* jump_if_flag : jump to &dest if flag != 0 */
OP_JEQ_INT, /* jump_eq_int : jump to &dest if locals[src1] as int == locals[src2] as int */
OP_JNEQ_INT, /* jump_neq_int : jump to &dest if locals[src1] as int != locals[src2] as int */
OP_JGT_INT, /* jump_gt_int : jump to &dest if locals[src1] as int > locals[src2] as int */
OP_JLT_INT, /* jump_lt_int : jump to &dest if locals[src1] as int < locals[src2] as int */
OP_JLE_INT, /* jump_le_int : jump to &dest if locals[src1] as int <= locals[src2] as int */
OP_JGE_INT, /* jump_ge_int : jump to &dest if locals[src1] as int >= locals[src2] as int */
OP_JEQ_NAT, /* jump_eq_nat : jump to &dest if locals[src1] as nat == locals[src2] as nat */
OP_JNEQ_NAT, /* jump_neq_nat : jump to &dest if locals[src1] as nat != locals[src2] as nat */
OP_JGT_NAT, /* jump_gt_nat : jump to &dest if locals[src1] as nat > locals[src2] as nat */
OP_JLT_NAT, /* jump_lt_nat : jump to &dest if locals[src1] as nat < locals[src2] as nat */
OP_JLE_NAT, /* jump_le_nat : jump to &dest if locals[src1] as nat <= locals[src2] as nat */
OP_JGE_NAT, /* jump_ge_nat : jump to &dest if locals[src1] as nat >= locals[src2] as nat */
OP_JEQ_REAL, /* jump_eq_real : jump to &dest if locals[src1] as real == locals[src2] as real */
OP_JNEQ_REAL, /* jump_neq_real : jump to &dest if locals[src1] as real != locals[src2] as real */
OP_JGE_REAL, /* jump_ge_real : jump to &dest if locals[src1] as real >= locals[src2] as real */
OP_JGT_REAL, /* jump_gt_real : jump to &dest if locals[src1] as real > locals[src2] as real */
OP_JLT_REAL, /* jump_lt_real : jump to &dest if locals[src1] as real < locals[src2] as real */
OP_JLE_REAL, /* jump_le_real : jump to &dest if locals[src1] as real <= locals[src2] as real */
OP_MAX_OPCODE /* not really an opcode but used to check max length of ops */
} Opcode;
/**
* Instruction Types
*
* A : [8:opcode][8:dest][8:src1][8:src2]
* B : [8:opcode][8:dest][16:immediate]
* C : [8:opcode][24:immediate]
*/
#define DECODE_OP(instruction) ((((u32)(instruction)) >> 24) & 0xFF)
typedef struct vm_s VM;
#define ENCODE_A(opcode, dest, src1, src2) ((((u32)(opcode) & 0xFF) << 24) | \
(((u32)(dest) & 0xFF) << 16) | \
(((u32)(src1) & 0xFF) << 8) | \
(((u32)(src2) & 0xFF)))
#define DECODE_A(instruction) \
u8 dest = (((u32)(instruction)) >> 16) & 0xFF; \
u8 src1 = (((u32)(instruction)) >> 8) & 0xFF; \
u8 src2 = ((u32)(instruction)) & 0xFF;
#define ENCODE_B(opcode, dest, imm) ((((u32)(opcode) & 0xFF) << 24) | \
(((u32)(dest) & 0xFF) << 16) | \
(((u32)(imm)) & 0xFFFF))
#define DECODE_B(instruction) \
u8 dest = (((u32)(instruction)) >> 16) & 0xFF; \
u16 imm = ((u32)(instruction)) & 0xFFFF;
#define ENCODE_C(opcode, imm) ((((u32)(opcode) & 0xFF) << 24) | \
(((u32)(imm)) & 0xFFFFFF))
#define DECODE_C(instruction) \
u32 imm = ((u32)(instruction)) & 0xFFFFFF;
typedef enum {
OP_HALT, /* halt : C : all zeros : halt execution */
OP_CALL, /* call : A : dest args fn_ptr : creates a new frame */
OP_RETURN, /* return : A : dest args : returns from a frame to the parent frame */
OP_SYSCALL, /* syscall : A : id args mem_ptr : does a system call based on id with args */
OP_LOAD_IMM, /* load_immediate : B : locals[dest] = const as u16 */
OP_LOAD_UPPER_IMM, /* load_upper_immediate : B : locals[dest] = const as u32 << 16 | u16 */
OP_LOAD_IND_8, /* load_indirect_8 : A : locals[dest] = memory[locals[src1]] as u8 */
OP_LOAD_IND_16, /* load_indirect_16 : A : locals[dest] = memory[locals[src1]] as u16 */
OP_LOAD_IND_32, /* load_indirect_32 : A : locals[dest] = memory[locals[src1]] as u32 */
OP_LOAD_ABS_8, /* load_absolute_8 : A : locals[dest] = memory[src1] as u8 */
OP_LOAD_ABS_16, /* load_absolute_16 : A : locals[dest] = memory[src1] as u16 */
OP_LOAD_ABS_32, /* load_absolute_32 : A : locals[dest] = memory[src1] as u32 */
OP_LOAD_OFF_8, /* load_offset_8 : A : locals[dest] = memory[locals[src1] + offset] as u8 */
OP_LOAD_OFF_16, /* load_offset_16 : A : locals[dest] = memory[locals[src1] + offset] as u16 */
OP_LOAD_OFF_32, /* load_offset_32 : A : locals[dest] = memory[locals[src1] + offset] as u32 */
OP_STORE_ABS_8, /* store_absolute_8 : A : memory[dest] = src1 && 0xFF */
OP_STORE_ABS_16, /* store_absolute_16 : A : memory[dest] = src1 && 0xFFFF */
OP_STORE_ABS_32, /* store_absolute_32 : A : memory[dest] = src1 */
OP_STORE_IND_8, /* store_indirect_8 : A : memory[dest] = locals[src1] && 0xFF */
OP_STORE_IND_16, /* store_indirect_16 : A : memory[dest] = locals[src1] && 0xFFFF*/
OP_STORE_IND_32, /* store_indirect_32 : A : memory[dest] = locals[src1] */
OP_STORE_OFF_8, /* store_offset_8 : A : memory[locals[dest] + offset] = locals[src1] && 0xFF */
OP_STORE_OFF_16, /* store_offset_16 : A : memory[locals[dest] + offset] = locals[src1] && 0xFFFF */
OP_STORE_OFF_32, /* store_offset_32 : A : memory[locals[dest] + offset] = locals[src1] */
OP_MEM_ALLOC, /* alloc : A : memory[dest] = [locals[src1] as size + 4] */
OP_MEM_CPY, /* memcpy : A : memory[src1 .. src1 + count] = memory[dest .. dest + count] */
OP_MEM_SET_8, /* memset_8 : A : memory[dest .. dest + count] = local[src1] as u8 */
OP_MEM_SET_16, /* memset_16 : A : memory[dest .. dest + count] = local[src1] as u16 */
OP_MEM_SET_32, /* memset_32 : A : memory[dest .. dest + count] = local[src1] as u32 */
OP_REG_MOV, /* register_move : A : locals[dest] = locals[src1] */
OP_ADD_INT, /* add_int : A : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_INT, /* sub_int : A : locals[dest] = locals[src1] - locals[src2] */
OP_MUL_INT, /* mul_int : A : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_INT, /* div_int : A : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_INT, /* abs_int : A : locals[dest] = | locals[src1] | */
OP_NEG_INT, /* neg_int : A : locals[dest] = -locals[src1] */
OP_ADD_NAT, /* add_nat : A : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_NAT, /* sub_nat : A : locals[dest] = locals[src1] - locals[src2] */
OP_MUL_NAT, /* mul_nat : A : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_NAT, /* div_nat : A : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_NAT, /* abs_nat : A : locals[dest] = | locals[src1] | */
OP_NEG_NAT, /* neg_nat : A : locals[dest] = -locals[src1] */
OP_ADD_REAL, /* add_real : A : locals[dest] = locals[src1] + locals[src2] */
OP_SUB_REAL, /* sub_real : A : locals[dest] = locals[src1] - locals[src2] */
OP_MUL_REAL, /* mul_real : A : locals[dest] = locals[src1] * locals[src2] */
OP_DIV_REAL, /* div_real : A : locals[dest] = locals[src1] / locals[src2] */
OP_ABS_REAL, /* abs_real : A : locals[dest] = | locals[src1] | */
OP_NEG_REAL, /* neg_real : A : locals[dest] = -locals[src1] */
OP_INT_TO_REAL, /* int_to_real : A : locals[dest] = locals[src1] as real */
OP_INT_TO_NAT, /* int_to_nat : A : locals[dest] = locals[src1] as nat */
OP_NAT_TO_REAL, /* nat_to_real : A : locals[dest] = locals[src1] as real */
OP_NAT_TO_INT, /* nat_to_int : A : locals[dest] = locals[src1] as int */
OP_REAL_TO_INT, /* real_to_int : A : locals[dest] = locals[src1] as int */
OP_REAL_TO_NAT, /* real_to_nat : A : locals[dest] = locals[src1] as nat */
OP_BIT_SHIFT_LEFT, /* bit_shift_left : A : locals[dest] = locals[src1] << locals[src2] */
OP_BIT_SHIFT_RIGHT,/* bit_shift_right : A : locals[dest] = locals[src1] >> locals[src2] */
OP_BIT_SHIFT_R_EXT,/* bit_shift_r_ext : A : locals[dest] as i32 = locals[src1] >> locals[src2] */
OP_BIT_AND, /* bit_and : A : locals[dest] = locals[src1] & locals[src2] */
OP_BIT_OR, /* bit_or : A : locals[dest] = locals[src1] | locals[src2] */
OP_BIT_XOR, /* bit_xor : A : locals[dest] = locals[src1] ^ locals[src2] */
OP_JMP_IMM, /* jump_immediate : C : jump to imm unconditionally */
OP_JMP_ABS, /* jump_absolute : A : jump to locals[dest] unconditionally */
OP_JMP_OFF, /* jump_offset : A : jump to locals[dest] + locals[src1] unconditionally */
OP_JMP_FLAG, /* jump_if_flag : A : jump to locals[dest] if flag > 0 */
OP_JEQ_INT, /* jump_eq_int : A : jump to locals[dest] if locals[src1] as int == locals[src2] as int */
OP_JNE_INT, /* jump_neq_int : A : jump to locals[dest] if locals[src1] as int != locals[src2] as int */
OP_JGT_INT, /* jump_gt_int : A : jump to locals[dest] if locals[src1] as int > locals[src2] as int */
OP_JLT_INT, /* jump_lt_int : A : jump to locals[dest] if locals[src1] as int < locals[src2] as int */
OP_JLE_INT, /* jump_le_int : A : jump to locals[dest] if locals[src1] as int <= locals[src2] as int */
OP_JGE_INT, /* jump_ge_int : A : jump to locals[dest] if locals[src1] as int >= locals[src2] as int */
OP_JEQ_NAT, /* jump_eq_nat : A : jump to locals[dest] if locals[src1] as nat == locals[src2] as nat */
OP_JNE_NAT, /* jump_neq_nat : A : jump to locals[dest] if locals[src1] as nat != locals[src2] as nat */
OP_JGT_NAT, /* jump_gt_nat : A : jump to locals[dest] if locals[src1] as nat > locals[src2] as nat */
OP_JLT_NAT, /* jump_lt_nat : A : jump to locals[dest] if locals[src1] as nat < locals[src2] as nat */
OP_JLE_NAT, /* jump_le_nat : A : jump to locals[dest] if locals[src1] as nat <= locals[src2] as nat */
OP_JGE_NAT, /* jump_ge_nat : A : jump to locals[dest] if locals[src1] as nat >= locals[src2] as nat */
OP_JEQ_REAL, /* jump_eq_real : A : jump to locals[dest] if locals[src1] as real == locals[src2] as real */
OP_JNE_REAL, /* jump_neq_real : A : jump to locals[dest] if locals[src1] as real != locals[src2] as real */
OP_JGE_REAL, /* jump_ge_real : A : jump to locals[dest] if locals[src1] as real >= locals[src2] as real */
OP_JGT_REAL, /* jump_gt_real : A : jump to locals[dest] if locals[src1] as real > locals[src2] as real */
OP_JLT_REAL, /* jump_lt_real : A : jump to locals[dest] if locals[src1] as real < locals[src2] as real */
OP_JLE_REAL, /* jump_le_real : A : jump to locals[dest] if locals[src1] as real <= locals[src2] as real */
OP_MAX_OPCODE /* not an opcode count of instructions */
} Opcode;
#define MEM_SIZE 65536
struct vm_s {
u32 pc;
u8 mem[MEM_SIZE];
};
extern u32 mp; /* memory pointer */
extern u32 cp; /* code pointer */
extern u32 pc; /* program counter */
extern u32 flag; /* flag */
extern u32 *code; /* code */
extern u8 *mem; /* memory */
extern bool init_vm(VM *vm);
bool step_vm(VM *vm);
#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 { \
if ((addr) < sizeof(mem)) { \
mem[(addr)] = (value) & 0xFF; \
} \
} while (0)
#define write_u16(addr, value) \
do { \
if ((addr) + 1 < sizeof(mem)) { \
mem[(addr)] = (value) & 0xFF; \
mem[(addr) + 1] = ((value) >> 8) & 0xFF; \
} \
} while (0)
#define write_u32(addr, value) \
do { \
if ((addr) + 3 < sizeof(mem)) { \
mem[(addr)] = (value) & 0xFF; \
mem[(addr) + 1] = ((value) >> 8) & 0xFF; \
mem[(addr) + 2] = ((value) >> 16) & 0xFF; \
mem[(addr) + 3] = ((value) >> 24) & 0xFF; \
} \
} while (0)
extern bool init_vm();
bool step_vm();
#endif