This is another follow up from my previous follow up and this time I segmented the stack and memory into bytes so that it follows more closely to most machines. As I stated on my previous peer reviews, this virtual machine is designed to be embedded in a C++ game engine as a static library. I should very much say that this VM will power a subset of C as a scripting language, thus a lot of what the opcodes do is more or less designed around it for C.
I'm still aware that I shouldn't be using dispatch tables because it's non-standard and non-portable but I'm ignoring that.
My questions for this reviewing is: does the VM have enough features to be able to support even a subset of the C programming language?
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
/* here's the deal ok? make an opcode for each and erry n-bytes!
* 'l' - int32
* 's' - int16
* 'b' - byte | push and pop do not take bytes
* 'f' - float32
*/
#define INSTR_SET \
X(halt) \
X(pushl) X(pushs) X(pushb) X(pushsp) X(puship)\
X(popl) X(pops) X(popb) \
X(wrtl) X(wrts) X(wrtb) \
X(storel) X(stores) X(storeb) \
X(loadl) X(loads) X(loadb) \
X(copyl) X(copys) X(copyb) \
X(addl) X(uaddl) X(addf) X(subl) X(usubl) X(subf) \
X(mull) X(umull) X(mulf) X(divl) X(udivl) X(divf) X(modl) X(umodl)\
X(andl) X(orl) X(xorl) X(notl) X(shl) X(shr) X(incl) X(decl) \
X(ltl) X(ultl) X(ltf) X(gtl) X(ugtl) X(gtf) X(cmpl) X(ucmpl) X(compf) \
X(leql) X(uleql) X(leqf) X(geql) X(ugeql) X(geqf) \
X(jmp) X(jzl) X(jzs) X(jzb) X(jnzl) X(jnzs) X(jnzb) \
X(call) X(ret) \
X(nop) \
#define X(x) x,
enum InstrSet{ INSTR_SET };
#undef X
#define X(x) #x ,
const char *opcode2str[] = { INSTR_SET };
#undef X
#define WORD_SIZE 4
#define STK_SIZE 1024*WORD_SIZE // 4096 4Kb
#define CALLSTK_SIZE 256 // 1024 bytes
#define MEM_SIZE 256*WORD_SIZE // 1024 bytes
// 'b' for Byte, not bool
struct vm_cpu {
uint8_t bStack[STK_SIZE]; // 4096 bytes
uint32_t bCallstack[CALLSTK_SIZE]; // 1024 bytes
uint8_t bMemory[MEM_SIZE]; // 1024 bytes
uint32_t ip, sp, callsp, callbp; // 16 bytes
};
void debug_print_memory(const struct vm_cpu *restrict vm)
{
if( !vm )
return;
printf("DEBUG ...---===---... Printing Memory...\n");
uint32_t i;
for( i=0 ; i<MEM_SIZE ; i++ )
if( vm->bMemory[i] )
printf("Memory Index: 0x%x | data: %u\n", i, vm->bMemory[i]);
printf("\n");
}
void debug_print_stack(const struct vm_cpu *restrict vm)
{
if( !vm )
return;
printf("DEBUG ...---===---... Printing Stack...\n");
uint32_t i;
for( i=0 ; i<STK_SIZE ; i++ )
if( vm->bStack[i] )
printf("Stack Index: 0x%x | data: %u\n", i, vm->bStack[i]);
printf("\n");
}
void debug_print_callstack(const struct vm_cpu *restrict vm)
{
if( !vm )
return;
printf("DEBUG ...---===---... Printing Call Stack...\n");
uint32_t i;
for( i=0 ; i<CALLSTK_SIZE ; i++ )
if( vm->bCallstack[i] )
printf("Call Stack Index: 0x%x | data: %u\n", i, vm->bCallstack[i]);
printf("\n");
}
void debug_print_ptrs(const struct vm_cpu *restrict vm)
{
if( !vm )
return;
printf("DEBUG ...---===---... Printing Pointers...\n");
printf("Instruction Pointer: %u\
\nStack Pointer: %u\
\nCall Stack Pointer: %u\
\nCall Stack Frame Pointer: %u\n", vm->ip, vm->sp, vm->callsp, vm->callbp);
printf("\n");
}
//#include <unistd.h> // sleep() func
void vm_exec(const uint8_t *code, struct vm_cpu *const vm)
{
union {
uint32_t ui;
int32_t i;
float f;
uint16_t us;
int16_t s;
uint8_t c[4];
} conv;
uint32_t b, a;
float fa, fb;
uint16_t usa, usb;
#define X(x) &&exec_##x ,
static const void *dispatch[] = { INSTR_SET };
#undef INSTR_SET
if( code[vm->ip] > nop) {
printf("illegal instruction exception! instruction == \'%" PRIu8 "\' @ %u\n", code[vm->ip], vm->ip);
goto *dispatch[halt];
return;
}
//printf( "current instruction == \"%s\" @ ip == %u\n", opcode2str[code[vm->ip]], vm->ip );
#ifdef _UNISTD_H
#define DISPATCH() sleep(1); goto *dispatch[ code[++vm->ip] ]
#else
#define DISPATCH() goto *dispatch[ code[++vm->ip] ]
#endif
goto *dispatch[ code[vm->ip] ];
exec_nop:;
DISPATCH();
exec_halt:;
printf("===================== vm done\n");
return;
// opcodes for longs
exec_pushl:; // push 4 bytes onto the stack
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
printf("pushl: pushed %u\n", conv.ui);
DISPATCH();
exec_pushs:; // push 2 bytes onto the stack
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
printf("pushs: pushed %u\n", conv.us);
DISPATCH();
exec_pushb:; // push a byte onto the stack
vm->bStack[++vm->sp] = code[++vm->ip];
printf("pushb: pushed %u\n", vm->bStack[vm->sp]);
DISPATCH();
exec_pushsp:; // push sp onto the stack, uses 4 bytes since 'sp' is uint32
conv.ui = vm->sp;
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
printf("pushsp: pushed sp index: %u\n", conv.ui);
DISPATCH();
exec_puship:;
conv.ui = vm->ip;
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
printf("puship: pushed ip index: %u\n", conv.ui);
DISPATCH();
exec_popl:; // pop 4 bytes to eventually be overwritten
vm->sp -= 4;
printf("popl\n");
DISPATCH();
exec_pops:; // pop 2 bytes
vm->sp -= 2;
printf("pops\n");
DISPATCH();
exec_popb:; // pop a byte
--vm->sp;
printf("popb\n");
DISPATCH();
exec_wrtl:; // writes an int to memory, First operand is the memory address as 4 byte number, second is the int of data.
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
a = conv.ui;
vm->bMemory[a] = code[++vm->ip];
vm->bMemory[a+1] = code[++vm->ip];
vm->bMemory[a+2] = code[++vm->ip];
vm->bMemory[a+3] = code[++vm->ip];
conv.c[0] = vm->bMemory[a];
conv.c[1] = vm->bMemory[a+1];
conv.c[2] = vm->bMemory[a+2];
conv.c[3] = vm->bMemory[a+3];
printf("wrote int data - %u @ address 0x%x\n", conv.ui, a);
DISPATCH();
exec_wrts:; // writes a short to memory. First operand is the memory address as 4 byte number, second is the short of data.
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
a = conv.ui;
vm->bMemory[a] = code[++vm->ip];
vm->bMemory[a+1] = code[++vm->ip];
conv.c[0] = vm->bMemory[a];
conv.c[1] = vm->bMemory[a+1];
printf("wrote short data - %u @ address 0x%x\n", conv.us, a);
DISPATCH();
exec_wrtb:; // writes a byte to memory. First operand is the memory address as 32-bit number, second is the byte of data.
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->bMemory[conv.ui] = code[++vm->ip];
printf("wrote byte data - %u @ address 0x%x\n", vm->bMemory[conv.ui], conv.ui);
DISPATCH();
exec_storel:; // pops 4-byte value off stack and into a memory address.
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
a = conv.ui;
vm->bMemory[a+3] = vm->bStack[vm->sp--];
vm->bMemory[a+2] = vm->bStack[vm->sp--];
vm->bMemory[a+1] = vm->bStack[vm->sp--];
vm->bMemory[a] = vm->bStack[vm->sp--];
conv.c[0] = vm->bMemory[a];
conv.c[1] = vm->bMemory[a+1];
conv.c[2] = vm->bMemory[a+2];
conv.c[3] = vm->bMemory[a+3];
printf("stored int data - %u @ address 0x%x\n", conv.ui, a);
DISPATCH();
exec_stores:; // pops 2-byte value off stack and into a memory address.
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
a = conv.ui;
vm->bMemory[a+1] = vm->bStack[vm->sp--];
vm->bMemory[a] = vm->bStack[vm->sp--];
conv.c[0] = vm->bMemory[a];
conv.c[1] = vm->bMemory[a+1];
printf("stored short data - %u @ address 0x%x\n", conv.us, a);
DISPATCH();
exec_storeb:; // pops byte value off stack and into a memory address.
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
a = conv.ui;
vm->bMemory[a] = vm->bStack[vm->sp--];
conv.c[0] = vm->bMemory[a];
printf("stored byte data - %u @ address 0x%x\n", conv.c[0], a);
DISPATCH();
exec_loadl:;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
a = conv.ui;
conv.c[0] = vm->bStack[++vm->sp] = vm->bMemory[a];
conv.c[1] = vm->bStack[++vm->sp] = vm->bMemory[a+1];
conv.c[2] = vm->bStack[++vm->sp] = vm->bMemory[a+2];
conv.c[3] = vm->bStack[++vm->sp] = vm->bMemory[a+3];
printf("loaded int data to T.O.S. - %u from address 0x%x\n", conv.ui, a);
DISPATCH();
exec_loads:;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
a = conv.ui;
conv.c[1] = vm->bStack[++vm->sp] = vm->bMemory[a];
conv.c[0] = vm->bStack[++vm->sp] = vm->bMemory[a+1];
printf("loaded short data to T.O.S. - %u from address 0x%x\n", conv.us, a);
DISPATCH();
exec_loadb:;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
a = conv.ui;
vm->bStack[++vm->sp] = vm->bMemory[a];
printf("loaded byte data to T.O.S. - %u from address 0x%x\n", vm->bStack[vm->sp], a);
DISPATCH();
exec_copyl:; // copy 4 bytes of top of stack and put as new top of stack.
conv.c[0] = vm->bStack[vm->sp-3];
conv.c[1] = vm->bStack[vm->sp-2];
conv.c[2] = vm->bStack[vm->sp-1];
conv.c[3] = vm->bStack[vm->sp];
printf("copied int data from T.O.S. - %u\n", conv.ui);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_copys:;
conv.c[0] = vm->bStack[vm->sp-1];
conv.c[1] = vm->bStack[vm->sp];
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
printf("copied short data from T.O.S. - %u\n", conv.us);
DISPATCH();
exec_copyb:;
conv.c[0] = vm->bStack[vm->sp];
vm->bStack[++vm->sp] = conv.c[0];
printf("copied byte data from T.O.S. - %u\n", conv.c[0]);
DISPATCH();
exec_addl:; // pop 8 bytes, signed addition, and push 4 byte result to top of stack
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.i = (int32_t)a + (int32_t)b;
printf("signed 4 byte addition result: %i == %i + %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_uaddl:; // In C, all integers in an expression are promoted to int32, if number is bigger then unsigned int32 or int64
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a+b;
printf("unsigned 4 byte addition result: %u == %u + %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_addf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.f = fa+fb;
printf("float addition result: %f == %f + %f\n", conv.f, fa,fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_subl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.i = (int32_t)a - (int32_t)b;
printf("signed 4 byte subtraction result: %i == %i - %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_usubl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a-b;
printf("unsigned 4 byte subtraction result: %u == %u - %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_subf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.f = fa-fb;
printf("float subtraction result: %f == %f - %f\n", conv.f, fa,fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_mull:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.i = (int32_t)a * (int32_t)b;
printf("signed 4 byte mult result: %i == %i * %i\n", conv.i, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_umull:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a*b;
printf("unsigned 4 byte mult result: %u == %u * %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_mulf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.f = fa*fb;
printf("float mul result: %f == %f * %f\n", conv.f, fa,fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_divl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
if( !b ) {
printf("divl: divide by 0 error.\n");
goto *dispatch[halt];
}
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.i = (int32_t)a / (int32_t)b;
printf("signed 4 byte division result: %i == %i / %i\n", conv.i, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_udivl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
if( !b ) {
printf("udivl: divide by 0 error.\n");
goto *dispatch[halt];
}
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a/b;
printf("unsigned 4 byte division result: %u == %u / %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_divf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
if( !fb ) {
printf("divf: divide by 0.0 error.\n");
goto *dispatch[halt];
}
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.f = fa/fb;
printf("float division result: %f == %f / %f\n", conv.f, fa,fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_modl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
if( !b ) {
printf("modl: divide by 0 error.\n");
goto *dispatch[halt];
}
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.i = (int32_t)a % (int32_t)b;
printf("signed 4 byte modulo result: %i == %i %% %i\n", conv.i, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_umodl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
if( !b ) {
printf("umodl: divide by 0 error.\n");
goto *dispatch[halt];
}
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a%b;
printf("unsigned 4 byte modulo result: %u == %u %% %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_andl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a & b;
printf("4 byte AND result: %u == %i & %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_orl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a | b;
printf("4 byte OR result: %u == %i | %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_xorl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a ^ b;
printf("4 byte XOR result: %u == %i ^ %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_notl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = ~a;
printf("4 byte NOT result: %u\n", conv.ui);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_shl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a << b;
printf("4 byte Shift Left result: %u == %i >> %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_shr:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a >> b;
printf("4 byte Shift Right result: %u == %i >> %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_incl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = ++a;
printf("4 byte Increment result: %u\n", conv.ui);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_decl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = --a;
printf("4 byte Decrement result: %u\n", conv.ui);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_ltl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = (int32_t)a < (int32_t)b;
printf("4 byte Signed Less Than result: %u == %i < %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_ultl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a < b;
printf("4 byte Unsigned Less Than result: %u == %u < %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_ltf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.ui = fa < fb;
printf("4 byte Less Than Float result: %u == %f < %f\n", conv.ui, fa,fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_gtl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = (int32_t)a > (int32_t)b;
printf("4 byte Signed Greater Than result: %u == %i > %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_ugtl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a > b;
printf("4 byte Signed Greater Than result: %u == %u > %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_gtf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.ui = fa > fb;
printf("4 byte Greater Than Float result: %u == %f > %f\n", conv.ui, fa,fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_cmpl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = (int32_t)a == (int32_t)b;
printf("4 byte Signed Compare result: %u == %i == %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_ucmpl:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a == b;
printf("4 byte Unsigned Compare result: %u == %u == %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_compf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.ui = fa == fb;
printf("4 byte Compare Float result: %u == %f == %f\n", conv.ui, fa,fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_leql:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = (int32_t)a <= (int32_t)b;
printf("4 byte Signed Less Equal result: %u == %i <= %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_uleql:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a <= b;
printf("4 byte Unsigned Less Equal result: %u == %u <= %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_leqf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.ui = fa <= fb;
printf("4 byte Less Equal Float result: %u == %f <= %f\n", conv.ui, fa, fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_geql:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = (int32_t)a >= (int32_t)b;
printf("4 byte Signed Greater Equal result: %u == %i >= %i\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_ugeql:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
b=conv.ui;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
a=conv.ui;
conv.ui = a >= b;
printf("4 byte Unsigned Greater Equal result: %u == %u >= %u\n", conv.ui, a,b);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_geqf:;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fb=conv.f;
conv.c[3] = vm->bStack[vm->sp--];
conv.c[2] = vm->bStack[vm->sp--];
conv.c[1] = vm->bStack[vm->sp--];
conv.c[0] = vm->bStack[vm->sp--];
fa=conv.f;
conv.ui = fa >= fb;
printf("4 byte Greater Equal Float result: %u == %f >= %f\n", conv.ui, fa, fb);
vm->bStack[++vm->sp] = conv.c[0];
vm->bStack[++vm->sp] = conv.c[1];
vm->bStack[++vm->sp] = conv.c[2];
vm->bStack[++vm->sp] = conv.c[3];
DISPATCH();
exec_jmp:; // addresses are 4 bytes.
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->ip = conv.ui;
printf("jmping to instruction address: %u\n", vm->ip);
goto *dispatch[ code[vm->ip] ];
exec_jzl:; // check if the first 4 bytes on stack are zero, if yes then jump it.
conv.c[3] = vm->bStack[vm->sp];
conv.c[2] = vm->bStack[vm->sp-1];
conv.c[1] = vm->bStack[vm->sp-2];
conv.c[0] = vm->bStack[vm->sp-3];
a = conv.ui;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->ip = (!a) ? conv.ui : vm->ip+1 ;
printf("jzl'ing to instruction address: %u\n", vm->ip);
goto *dispatch[ code[vm->ip] ];
exec_jzs:;
conv.c[1] = vm->bStack[vm->sp];
conv.c[0] = vm->bStack[vm->sp-1];
a = conv.ui;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->ip = (!a) ? conv.ui : vm->ip+1 ;
printf("jzs'ing to instruction address: %u\n", vm->ip);
goto *dispatch[ code[vm->ip] ];
exec_jzb:;
conv.c[0] = vm->bStack[vm->sp];
a = conv.ui;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->ip = (!a) ? conv.ui : vm->ip+1 ;
printf("jzb'ing to instruction address: %u\n", vm->ip);
goto *dispatch[ code[vm->ip] ];
exec_jnzl:;
conv.c[3] = vm->bStack[vm->sp];
conv.c[2] = vm->bStack[vm->sp-1];
conv.c[1] = vm->bStack[vm->sp-2];
conv.c[0] = vm->bStack[vm->sp-3];
a = conv.ui;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->ip = (a) ? conv.ui : vm->ip+1 ;
printf("jnzl'ing to instruction address: %u\n", vm->ip);
goto *dispatch[ code[vm->ip] ];
exec_jnzs:;
conv.c[1] = vm->bStack[vm->sp];
conv.c[0] = vm->bStack[vm->sp-1];
a = conv.ui;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->ip = (a) ? conv.ui : vm->ip+1 ;
printf("jnzs'ing to instruction address: %u\n", vm->ip);
goto *dispatch[ code[vm->ip] ];
exec_jnzb:;
conv.c[0] = vm->bStack[vm->sp];
a = conv.ui;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
vm->ip = (a) ? conv.ui : vm->ip+1 ;
printf("jnzb'ing to instruction address: %u\n", vm->ip);
goto *dispatch[ code[vm->ip] ];
exec_call:;
vm->ip++;
printf("calling address: %u\n", vm->ip);
vm->bCallstack[++vm->callsp] = vm->ip + 1;
vm->callbp = vm->callsp;
vm->ip = code[vm->ip];
printf("call return addr: %u | frame ptr == %u\n", vm->bCallstack[vm->callsp], vm->callbp);
goto *dispatch[ code[vm->ip] ];
exec_ret:;
vm->callsp = vm->callbp;
printf("callsp set to callbp, callsp == %u\n", vm->callsp);
vm->ip = vm->bCallstack[vm->callsp--];
vm->callbp = vm->callsp;
printf("returning to address: %u\n", vm->ip);
goto *dispatch[ code[vm->ip] ];
}
uint64_t get_file_size(FILE *pFile)
{
uint64_t size = 0;
if( !pFile )
return size;
if( !fseek(pFile, 0, SEEK_END) ) {
size = (uint64_t)ftell(pFile);
rewind(pFile);
}
return size;
}
int main(void)
{
typedef uint8_t bytecode[] ;
/*
FILE *pFile = fopen("./myfile.cbyte", "rb");
if( !pFile )
return 0;
uint64_t size = get_file_size(pFile);
uint8_t *program = malloc(sizeof(uint8_t)*size);
fread(program, sizeof(uint8_t), size, pFile);
*/
const bytecode test1 = {
nop,
//pushl, 255, 1, 0, 0x0,
//pushs, 0xDD, 0xDD,
pushb, 0xAA,
//pushs, 0x0D, 0x0C,
//pops, popl,
//wrtl, 4,0,0,0, 0xFF,0xFF,0,0,
//storeb, 0,0,0,0,
//stores, 1,0,0,0,
//storel, 4,0,0,0,
//loadb, 4,0,0,0,
//storel, 0,0,0,0,
//loadl, 0,0,0,0,
//pushsp, puship, copyl, copys, copyb,
halt
};
const bytecode test2 = {
nop,
//jmp, 17,0,0,0,
// -16776961
//pushl, 255,0,0,255,
//pushl, 1,0,0,0,
// -855637761
//pushl, 255,0,0,205,
//pushl, 255,255,0,0,
//uaddl,
// 10.f in 4 bytes form
pushl, 0,0,32,65,
jzb, 17,0,0,0,
// 2.f in 4 bytes form
pushl, 0,0,0,64,
addf, // 12.f
//leqf,
halt
};
const bytecode test3 = {
nop,
call, 7,0,0,0, // 1
halt, // 6
// func1:
pushl, 9,0,0,0, // 7
call, 18,0,0,0, // 12
ret, // 17
// func2:
pushl, 5,0,0,0, // 18
pushl, 10,0,0,0, // 23
mull, // 28
mull,
call, 36,0,0,0, // 30
ret,
// func3:
pushl, 40,0,0,0, // 36
divl, // 41
ret,
};
struct vm_cpu *p_vm = malloc( sizeof(struct vm_cpu) ); //&(struct vm_cpu){ 0 };
if( p_vm ) { //printf("size == %u\n", sizeof(struct vm_cpu));
vm_exec( test3, p_vm );
debug_print_memory(p_vm);
debug_print_stack(p_vm);
//debug_print_callstack(p_vm);
//debug_print_ptrs(p_vm);
free(p_vm);
}
p_vm=NULL;
/*
fclose(pFile); pFile=NULL;
free(program); program=NULL;
*/
return 0;
}
2 Answers 2
Call/ret is bugged now
When you switched from code
being uint64_t
to uint8_t
, you didn't modify the exec_call
handler correctly:
exec_call:; vm->ip++; printf("calling address: %u\n", vm->ip); vm->bCallstack[++vm->callsp] = vm->ip + 1; // <-- Wrong ret addr vm->callbp = vm->callsp; vm->ip = code[vm->ip]; // <- Only 1 byte!
Here, you can see two problems:
- The call address is only 1 byte instead of 4.
- The return address is being set to somewhere inside the 4 byte of call address instead of just after the call address.
This causes the test3
program to terminate right after the first function return:
returning to address: 32 ===================== vm done
After I fixed exec_call
to be like this:
exec_call:;
vm->ip++;
printf("calling address: %u\n", vm->ip);
vm->bCallstack[++vm->callsp] = vm->ip + 4;
vm->callbp = vm->callsp;
conv.c[0] = code[vm->ip];
conv.c[1] = code[vm->ip+1];
conv.c[2] = code[vm->ip+2];
conv.c[3] = code[vm->ip+3];
vm->ip = conv.ui;
printf("call return addr: %u | frame ptr == %u\n", vm->bCallstack[vm->callsp], vm->callbp);
goto *dispatch[ code[vm->ip] ];
Then the program was able to return correctly from nested function call:
returning to address: 35 callsp set to callbp, callsp == 2 returning to address: 17 callsp set to callbp, callsp == 1 returning to address: 6 ===================== vm done
callbp is useless
You have something called vm->callbp
that serves no purpose. It is always set to the current value of vm->callsp
. You could eliminate that variable and just use vm->callsp
instead.
Endianness issues
I'm not sure what your plans are with this virtual machine, but currently the virtual machine's endianness depends on the host machine's endianness. This means that each program you write will only work if the endianness it was written for matches the host machine's endianness. So for example, since you wrote the test3
program assuming little endian, it runs fine on an x86 host. But if you were to recompile this program on a big endian host machine, it would fail to run test3
properly.
You could fix this by defining your virtual machine to be little endian, and then fixing your conversions appropriately.
Repetitive code
I see a lot of repeated code, such as this code that reads a 4-byte value from the instruction stream:
conv.c[0] = code[++vm->ip]; conv.c[1] = code[++vm->ip]; conv.c[2] = code[++vm->ip]; conv.c[3] = code[++vm->ip]; a = conv.ui;
You could use inline functions to simplify your code:
static inline uint32_t read_imm4(const uint8_t *code,
struct vm_cpu *const vm)
{
conv_union conv;
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
return conv.ui;
}
// At call site:
a = read_imm4(code, vm);
Not only does this reduce the amount of repeated code in your dispatch handlers, it also has the advantage of centralizing the conversion code to functions which you can then modify as necessary. For example, to do endianness correctly, you might modify the above to look like this:
static inline uint32_t read_imm4(const uint8_t *code,
struct vm_cpu *const vm)
{
conv_union conv;
#if defined(BIGENDIAN_HOST)
conv.c[3] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[0] = code[++vm->ip];
#else
conv.c[0] = code[++vm->ip];
conv.c[1] = code[++vm->ip];
conv.c[2] = code[++vm->ip];
conv.c[3] = code[++vm->ip];
#endif
return conv.ui;
}
Bounds checking
Right now, your virtual machine has no bounds checking. So for example, if the program does a wrtl
instruction with an address larger than MEM_SIZE
, your program doesn't catch that as an error, and will proceed to write to some arbitrary memory location. A malicious user could potentially figure out a way to exploit this and cause the program to start executing injected code.
I would advise guarding every array access with a bounds check.
-
\$\begingroup\$ thx man! I addressed the incorrect call/ret opcodes as you mentioned! About endianess issues, the VM is little endian and I'm not going to port this VM to any architecture that's big endian as this will only be used for a game engine. So mostly x86 and ARM and ARM is bi-endian. \$\endgroup\$Nergal– Nergal2017年07月07日 22:17:09 +00:00Commented Jul 7, 2017 at 22:17
-
\$\begingroup\$ Also, I forgot to put this but the reason I have callbp is for recursive functions, do recursive functions not really need a frame pointer? Also, how could a malicious user exploit this and inject code? \$\endgroup\$Nergal– Nergal2017年07月07日 22:18:39 +00:00Commented Jul 7, 2017 at 22:18
-
\$\begingroup\$ @Nergal You don't need callbp for recursive functions. Your
callsp
stack index is all you need to return from any type of function. As far as injecting code, the program could just use multiplewrtl
to write code onto your program's stack, and then use anotherwrtl
to overwrite the current function's return address on the stack to jump to the place on the stack where the injected code was written. Then the next time you return from a function, it would jump to the malicious code. \$\endgroup\$JS1– JS12017年07月07日 23:13:47 +00:00Commented Jul 7, 2017 at 23:13
I see a number of things that may help you improve your program.
Don't take the address of a label
Taking the address of a label is NOT supported by the standard and is, to my knowledge, solely a feature of gcc
. Besides the fact that this is a non-standard extension, it's also not a very good way to express what you're trying to do. See the next suggestion for a better way.
Use a switch
where appropriate
A much more direct, efficient, and portable method for exec
would be to use a switch
instead of goto
. Another way to organize things is shown in the next suggestion.
Use an appropriate data structure
Each instruction has an opcode (the number assigned), a name (such as "pushl") and associated code that performs the operation. Those could be very nicely be put into a coherent structure like this:
static const struct {
char *name;
bool (*evaluate)(struct vm_cpu *);
} Instruction[] = {
X(halt), X(pushl), // ...
X(nop),
};
static const int inst_count = sizeof(Instruction)/sizeof(Instruction[0]);
Labels are not statements
A label is not a statement and does not need a terminating semicolon.
Use portable constants for printing
On your machine, %llu
may well be the correct printf
format for printing a uint64_t
, but on my 64-bit machine, it would be %lu
. Rather than having to guess, simply use the constants defined in <intypes.h>
and do it like this:
printf("loaded %" PRIu64 " from reg[%" PRIu64 "]\n", stack[sp], a);
Eliminate global variables where practical
Having routines dependent on global variables makes it that much more difficult to understand the logic and introduces many opportunities for error. Eliminating global variables where practical is always a good idea. In this case, if you wrap all of the associated values into a struct
as in the previous suggestion, then you can pass a pointer to the struct
into exec
. This has many benefits, including the ability to have multiple simultaneous virtual machines running at the same time.
Create a reset function
Real CPUs have a reset pin which sets the machine to a defined, known state. You could implement a reset function for the virtual machine as well that would set everything to a known, predictable state.
Allow the user to control verbosity
Almost all of the instructions contain print functions of various types. That may be useful for debugging, but it would be annoying when trying to trace a larger program. I'd suggest creating a "verbose" flag within the virtual machine to control whether such statements are printed or not.
Allow the user to control range checking
I'd suggest that a boolean flag "safe" could be added to the VM. It would turn on checking for ranges (e.g. stack overflow, etc.) when debugging, or it could be turned off if you're looking for maximum VM speed at the expense of safety. I'd probably leave it on by default. Nobody likes fast programs that crash.
Putting it together
Here's a small sample showing how some of these suggestions might look:
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#define WORD_SIZE 4
#define STK_SIZE 1024*WORD_SIZE // 4096 4Kb
#define CALLSTK_SIZE 256 // 1024 bytes
#define MEM_SIZE 256*WORD_SIZE // 1024 bytes
// 'b' for Byte, not bool
struct vm_cpu {
const uint8_t *code;
size_t codesize;
uint8_t bStack[STK_SIZE]; // 4096 bytes
uint32_t bCallstack[CALLSTK_SIZE]; // 1024 bytes
uint8_t bMemory[MEM_SIZE]; // 1024 bytes
uint32_t ip, sp, callsp, callbp; // 16 bytes
bool verbose;
bool safe;
};
uint32_t as_uint32_t(const void *ptr) {
uint32_t val;
memcpy(&val, ptr, sizeof(val));
return val;
}
bool exec_halt(struct vm_cpu *vm) {
if (vm->verbose) {
printf("halted at %4.4x\n", vm->ip);
}
return false;
}
bool exec_nop(struct vm_cpu *vm) {
++vm->ip;
return true;
}
bool exec_pushl(struct vm_cpu *vm) {
if (vm->safe) {
// check pointers
}
++vm->ip;
memcpy(&vm->bStack[vm->sp], &vm->code[vm->ip], sizeof(uint32_t));
if (vm->verbose) {
printf("pushl: pushed %" PRIu32 "\n", as_uint32_t(&vm->code[vm->ip]));
}
vm->ip += sizeof(uint32_t);
vm->sp += sizeof(uint32_t);
return true;
}
#define X(x) { #x, exec_##x }
static const struct {
char *name;
bool (*evaluate)(struct vm_cpu *);
} Instruction[] = {
X(halt), X(pushl), // ... etc.
X(nop),
};
static const int inst_count = sizeof(Instruction)/sizeof(Instruction[0]);
void vm_init(struct vm_cpu *vm, const void *code, size_t code_len) {
vm->code = code;
vm->codesize = code_len;
vm->ip = vm->sp = vm->callsp = vm->callbp = 0;
vm->verbose = true;
vm->safe = true;
}
void vm_print(struct vm_cpu *vm) {
printf("sp = %4.4x, ip = %4.4x, next inst = %s\n",
vm->sp, vm->ip, Instruction[vm->code[vm->ip]].name
);
}
bool vm_eval(struct vm_cpu *vm) {
if (vm->verbose) {
vm_print(vm);
}
return Instruction[vm->code[vm->ip]].evaluate(vm);
}
int main() {
struct vm_cpu vm;
static uint8_t code[] = {
1, 1, 2, 3, 4, // pushl 0x04030201
2, // nop
0, // halt
};
size_t code_len = sizeof(code) / sizeof(code[0]);
vm_init(&vm, code, code_len);
while (vm_eval(&vm)
{} // just execute instructions
}
Sample output
sp = 0000, ip = 0000, next inst = pushl
pushl: pushed 67305985
sp = 0004, ip = 0005, next inst = nop
sp = 0004, ip = 0006, next inst = halt
halted at 0006
-
\$\begingroup\$ flagged for copy pasting \$\endgroup\$Nergal– Nergal2017年07月07日 20:06:11 +00:00Commented Jul 7, 2017 at 20:06
-
1\$\begingroup\$ @Nergal you can always comment and ask the author of an answer to clarify how copying parts of a previous answer is justified in this iteration, but there's nothing wrong with re-iterating previous advice that wasn't followed. \$\endgroup\$Mathieu Guindon– Mathieu Guindon2017年07月07日 20:18:14 +00:00Commented Jul 7, 2017 at 20:18
-
\$\begingroup\$ ok @Edward why are you telling me to group globals into a struct when there are no globals to group into a struct? \$\endgroup\$Nergal– Nergal2017年07月07日 21:00:54 +00:00Commented Jul 7, 2017 at 21:00
-
\$\begingroup\$ also, Use portable constants for printing? I'm not using non-portable printing constants... how is '%u' not portable? \$\endgroup\$Nergal– Nergal2017年07月07日 21:25:28 +00:00Commented Jul 7, 2017 at 21:25
-
1\$\begingroup\$ @Nergal: nice to see that you're reading these closely. I couldn't tell from earlier reviews. As for globals,
opcode2str
is global in the original code. If you'll note in the revised code I posted, theInstruction
array isstatic const
which limits it to file scope. Also%u
is not strictly portable unless anunsigned
anduint32_t
happen to be the same size (they're not on my machine). If you're printing particularly sized variables, as in this case, it makes sense to use the matching particularly sized formatting strings, as I have demonstrated in thepushl()
code I posted. \$\endgroup\$Edward– Edward2017年07月07日 23:38:29 +00:00Commented Jul 7, 2017 at 23:38