(Код написан так чтобы работал, правильнее писать более безопасно и читабельнее)
#include <stdio.h>
enum Opcode {
OP_LOAD_ARGUMENT,
OP_LOAD_STRING,
OP_SUBTRACT,
OP_GOTO,
OP_GOTO_IF_TRUE,
OP_CALL,
OP_RETURN
};
union Argument {
int value;
const void* pointer;
};
struct Instruction {
Opcode opcode;
Argument arg;
};
void run_vm(const Instruction* instructions, const Argument* args) {
Argument stack[16];
Argument* stack_pointer = &stack[0];
int address = 0;
while(true) {
const Instruction instruction = instructions[address];
address++;
switch(instruction.opcode) {
case OP_RETURN:
return;
case OP_LOAD_ARGUMENT:
*stack_pointer = args[instruction.arg.value];
stack_pointer++;
break;
case OP_LOAD_STRING:
*stack_pointer = instruction.arg;
stack_pointer++;
break;
case OP_SUBTRACT:
{
stack_pointer--;
int b = stack_pointer->value;
stack_pointer--;
int a = stack_pointer->value;
stack_pointer->value = a - b;
stack_pointer++;
break;
}
case OP_GOTO:
address = instruction.arg.value;
break;
case OP_GOTO_IF_TRUE:
stack_pointer--;
if(stack_pointer->value != 0)
address = instruction.arg.value;
break;
case OP_CALL:
void (* fn)(const void*) = (void (*)(const void*))instruction.arg.pointer;
stack_pointer--;
fn(stack_pointer->pointer);
break;
}
}
}
void print(const char* text) { printf("%s\n", text); }
int main(int argc, char** argv) {
const Instruction instructions[] = {
/* 0 */ { OP_LOAD_ARGUMENT, { 0 } },
/* 1 */ { OP_LOAD_ARGUMENT, { 1 } },
/* 2 */ { OP_SUBTRACT, {} },
/* 3 */ { OP_GOTO_IF_TRUE, { 0x7 } },
/* 4 */ { OP_LOAD_STRING, { .pointer = "zero" } },
/* 5 */ { OP_CALL, { .pointer = (void*)&print } }, // функции надо где-то регистрировать, чтобы знать сколько у них параметров и какого они типа
/* 6 */ { OP_GOTO, { 0x9 } },
/* 7 */ { OP_LOAD_STRING, { .pointer = "not zero" } },
/* 8 */ { OP_CALL, { .pointer = (void*)&print } },
/* 9 */ { OP_LOAD_STRING, { .pointer = "done" } },
/* A */ { OP_CALL, { .pointer = (void*)&print } },
/* B */ { OP_RETURN, {} }
};
const Argument args[] = {
{ 100500 },
{ 777 }
};
run_vm(instructions, args);
return 0;
}