vc5/src/vm.hpp
2026-01-13 16:25:12 +01:00

174 lines
2.7 KiB
C++

#pragma once
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <span>
#include <utility>
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<uint16_t>(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<uint8_t, 65536> m_mem = {};
std::array<uint16_t, 10> 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];
}
}
};
}