#include #include #define REGISTER_SIZE (1 << 6) // 64 register , overkill IK /* the types are defined using the X macro patter in this order (enum name, actual c type, the value used in the union) */ #define VM_TYPES \ X(UINT8, uint8_t, _uint8_val) \ X(UINT16, uint16_t, _uint16_val) \ X(UINT32, uint32_t, _uint32_val) \ X(UINT64, uint64_t, _uint64_val) \ X(PTR, uintptr_t, _uintptr_val) #define VM_OPCODES \ X(OP_ADD, vm_add) \ X(OP_SET_REG, vm_set_reg) \ X(OP_PRINT, vm_print) \ X(OP_END, vm_end) // given context pointer perform operations on the register #define GET_REGISTER(CTX_P, IDX) (CTX_P)->registers[(IDX)] #define SET_REGISTER(CTX_P, IDX, TYPE, VALUE) ((CTX_P)->registers[(IDX)].type = (TYPE), vm_set_value((CTX_P), (IDX), (TYPE), (VALUE))) // clang-format off typedef enum { #define X(name, ctype, field) name, VM_TYPES #undef X } VMTypes; typedef enum { #define X(op_name, op_func) op_name, VM_OPCODES #undef X } VMOPCodes; typedef struct { VMTypes type; union { #define X(name, ctype, field) ctype field; VM_TYPES #undef X } value; } VMType; typedef struct { VMType registers[REGISTER_SIZE]; } VMCtx; typedef void (*vm_op_func_t)(VMCtx *, uint8_t *, size_t *); // clang-format on void vm_set_value(VMCtx *ctx, size_t idx, VMTypes type, uintptr_t value) { // clang-format off switch(type) { #define X(name, ctype, field) \ case name: { \ ctx->registers[idx].value.field = value; \ break; \ } \ VM_TYPES #undef X } // clang-format on } void vm_add(VMCtx *ctx, uint8_t *program, size_t *pc) { (*pc)++; size_t op1_idx = program[(*pc)++]; size_t op2_idx = program[(*pc)++]; size_t dest_idx = program[(*pc)++]; vm_set_value(ctx, dest_idx, UINT8, GET_REGISTER(ctx, op1_idx).value._uint8_val + GET_REGISTER(ctx, op2_idx).value._uint8_val); } void vm_set_reg(VMCtx *ctx, uint8_t *program, size_t *pc) { (*pc)++; size_t idx = program[(*pc)++]; VMTypes type = (VMTypes)program[(*pc)++]; vm_set_value(ctx, idx, type, program[(*pc)++]); } void vm_print(VMCtx *ctx, uint8_t *program, size_t *pc) { (*pc)++; printf("vm_print %d\n", GET_REGISTER(ctx, program[(*pc)++]).value._uint8_val); } void vm_end(VMCtx *ctx, uint8_t *program, size_t *pc) { (*pc)++; } // clang-format off vm_op_func_t vm_funcs[] = { #define X(op_name, op_func) [op_name] = op_func, VM_OPCODES #undef X }; // clang-format on void vm_eval(VMCtx *ctx, uint8_t *program) { size_t pc = 0; while (program[pc] != OP_END) { // pc++; vm_funcs[program[pc]](ctx, program, &pc); } } int main() { VMCtx ctx = {0}; // clang-format off uint8_t program[] = { OP_SET_REG, 0, UINT8, 35, OP_SET_REG, 1, UINT8, 34, OP_ADD, 0, 1, 2, OP_PRINT, 2, OP_END }; // clang-format on // SET_REGISTER(&ctx, 0, UINT8, 12); // SET_REGISTER(&ctx, 1, UINT8, 15); // printf("Add = %d\n", ctx.registers[0].value._uint8_val + ctx.registers[1].value._uint8_val); vm_eval(&ctx, program); return 0; }