174 lines
2.7 KiB
C++
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];
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|