#pragma once #include #include #include #include #include #include namespace vc5 { enum class Op : uint16_t { Nop, Hlt, Jmp, Jnz, Mov, LoadWord, StoreWord, LoadByte, StoreByte, Cmp, Or, And, Xor, Shl, RShl, Shr, RShr, Add, Sub, RSub, }; const char* op_str(Op op); enum class Reg : uint16_t { R0 = 0, R1 = 1, R2 = 2, R3 = 3, R4 = 4, R5 = 5, Rbp = 6, Rsp = 7, Rfl = 8, Rip = 9, }; constexpr uint16_t reg_value(Reg reg) { return std::to_underlying(reg); } enum class Flag : uint16_t { Zero = 0, Eq = 1, Be = 2, Lt = 3, Err = 4, }; constexpr uint16_t flag_value(Flag flag) { return std::to_underlying(flag); } class VM { public: void load(uint16_t offset, const uint8_t* data, size_t data_size) { std::memcpy(&m_mem[offset], data, data_size); } int run(); uint16_t reg(Reg reg) const { return m_regs[reg_value(reg)]; }; uint16_t word(uint16_t addr) const { uint16_t value = 0; value |= static_cast(m_mem[addr]) << 8; value |= m_mem[addr + 1]; return value; } uint8_t byte(uint16_t addr) const { return m_mem[addr]; } private: friend struct Ins; int run_instruction(); uint16_t eat(); std::array m_mem = {}; std::array m_regs = {}; uint16_t* m_rfl = &m_regs[reg_value(Reg::Rfl)]; uint16_t* m_rip = &m_regs[reg_value(Reg::Rip)]; bool m_halted = false; }; struct Ins { Ins(VM& vm) : vm(&vm) , ins(vm.eat()) { } VM* vm; uint16_t ins; operator uint16_t() { return ins; } inline uint16_t dst_reg() const { return ins >> 13 & 0b111; } inline uint16_t op1_reg() const { return ins >> 10 & 0b111; } inline uint16_t op2_reg() const { return ins >> 7 & 0b111; } inline uint16_t op1() const { return vm->m_regs[op1_reg()]; } inline uint16_t op2() const { return vm->m_regs[op2_reg()]; } inline uint16_t op1_or_imm() { return reg_val_or_imm(6, 10, 0b111); } inline uint16_t op2_or_imm() { return reg_val_or_imm(6, 7, 0b111); } inline uint16_t reg_val_or_imm( uint16_t is_imm_bit, uint16_t reg_bit, uint16_t reg_mask) { bool is_imm = (ins >> is_imm_bit & 1) != 0; if (is_imm) { return vm->eat(); } else { return vm->m_regs[ins >> reg_bit & reg_mask]; } } }; }