fix
This commit is contained in:
parent
3116ec1a13
commit
65713e6b9f
222
src/builder.cpp
222
src/builder.cpp
@ -1,185 +1,329 @@
|
||||
#include "builder.hpp"
|
||||
#include "vm.hpp"
|
||||
#include <cstdint>
|
||||
#include <print>
|
||||
#include <utility>
|
||||
|
||||
using namespace vc5;
|
||||
|
||||
namespace {
|
||||
|
||||
class InsBuilder {
|
||||
public:
|
||||
InsBuilder(Op op)
|
||||
: m_ins(std::to_underlying(op))
|
||||
{
|
||||
}
|
||||
|
||||
void build(Builder& vm) const
|
||||
{
|
||||
vm.push(m_ins);
|
||||
if (m_has_imm) {
|
||||
vm.push(m_imm);
|
||||
}
|
||||
}
|
||||
|
||||
void set(uint16_t bit)
|
||||
{
|
||||
m_ins |= 1 << bit;
|
||||
}
|
||||
|
||||
void reg(Reg reg, uint16_t bit, uint16_t mask)
|
||||
{
|
||||
m_ins |= (std::to_underlying(reg) & mask) << bit;
|
||||
}
|
||||
|
||||
void op1_reg(Reg reg)
|
||||
{
|
||||
this->reg(reg, 10, 0b111);
|
||||
}
|
||||
|
||||
void op2_reg(Reg reg)
|
||||
{
|
||||
this->reg(reg, 7, 0b111);
|
||||
}
|
||||
|
||||
void dst_reg(Reg reg)
|
||||
{
|
||||
this->reg(reg, 13, 0b111);
|
||||
}
|
||||
|
||||
void imm_without_flag(uint16_t imm)
|
||||
{
|
||||
m_imm = imm;
|
||||
m_has_imm = true;
|
||||
}
|
||||
|
||||
void imm(uint16_t imm)
|
||||
{
|
||||
imm_without_flag(imm);
|
||||
set(6);
|
||||
}
|
||||
|
||||
private:
|
||||
uint16_t m_ins;
|
||||
uint16_t m_imm = 0;
|
||||
bool m_has_imm = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void Builder::nop()
|
||||
{
|
||||
auto ins = std::to_underlying(Op::Nop);
|
||||
push(ins);
|
||||
auto i = InsBuilder(Op::Nop);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::hlt()
|
||||
{
|
||||
auto ins = std::to_underlying(Op::Nop);
|
||||
push(ins);
|
||||
auto i = InsBuilder(Op::Hlt);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::jmp_reg(Reg op1)
|
||||
{
|
||||
auto i = InsBuilder(Op::Jmp);
|
||||
i.op1_reg(op1);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::jmp_imm(uint16_t op1)
|
||||
{
|
||||
auto i = InsBuilder(Op::Jmp);
|
||||
i.imm(op1);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::jnz_reg(Reg op1, Reg op2)
|
||||
{
|
||||
auto i = InsBuilder(Op::Jnz);
|
||||
i.op1_reg(op1);
|
||||
i.op2_reg(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::jnz_imm(Reg op1, uint16_t op2)
|
||||
{
|
||||
auto i = InsBuilder(Op::Jnz);
|
||||
i.op1_reg(op1);
|
||||
i.imm(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_word_load_reg(Reg dst, Reg src)
|
||||
void Builder::mov_reg(Reg dst, Reg src)
|
||||
{
|
||||
auto i = InsBuilder(Op::Mov);
|
||||
i.reg(dst, 12, 0b1111);
|
||||
i.reg(src, 7, 0b1111);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_word_load_imm(Reg dst, uint16_t imm)
|
||||
void Builder::mov_imm(Reg dst, uint16_t imm)
|
||||
{
|
||||
auto i = InsBuilder(Op::Mov);
|
||||
i.reg(dst, 12, 0b1111);
|
||||
i.imm(imm);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_word_load_mem_reg(Reg dst, Reg addr, uint16_t offset)
|
||||
void Builder::load_word_reg(Reg dst, Reg addr, uint16_t offset)
|
||||
{
|
||||
auto i = InsBuilder(Op::LoadWord);
|
||||
i.dst_reg(dst);
|
||||
i.op1_reg(addr);
|
||||
i.imm_without_flag(offset);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_word_load_mem_imm(Reg dst, uint16_t addr)
|
||||
void Builder::load_word_imm(Reg dst, uint16_t addr)
|
||||
{
|
||||
auto i = InsBuilder(Op::LoadWord);
|
||||
i.dst_reg(dst);
|
||||
i.imm(addr);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_word_store_reg_reg(Reg dst, uint16_t offset, Reg op2)
|
||||
void Builder::store_word_reg(Reg dst, uint16_t offset, Reg op2)
|
||||
{
|
||||
auto i = InsBuilder(Op::StoreWord);
|
||||
i.dst_reg(dst);
|
||||
i.imm_without_flag(offset);
|
||||
i.op2_reg(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_word_store_reg_imm(Reg dst, uint16_t offset, uint16_t op2)
|
||||
void Builder::store_word_imm(uint16_t dst, Reg op2)
|
||||
{
|
||||
auto i = InsBuilder(Op::StoreWord);
|
||||
i.imm(dst);
|
||||
i.op2_reg(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_word_store_imm_reg(uint16_t dst, Reg op2)
|
||||
void Builder::load_byte_reg(Reg dst, Reg addr, uint16_t offset)
|
||||
{
|
||||
auto i = InsBuilder(Op::LoadByte);
|
||||
i.dst_reg(dst);
|
||||
i.op1_reg(addr);
|
||||
i.imm_without_flag(offset);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_word_store_imm_imm(uint16_t dst, uint16_t op2)
|
||||
void Builder::load_byte_imm(Reg dst, uint16_t addr)
|
||||
{
|
||||
auto i = InsBuilder(Op::LoadByte);
|
||||
i.dst_reg(dst);
|
||||
i.imm(addr);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_byte_load_reg(Reg dst, Reg src)
|
||||
void Builder::store_byte_reg(Reg dst, uint16_t offset, Reg op2)
|
||||
{
|
||||
auto i = InsBuilder(Op::StoreByte);
|
||||
i.dst_reg(dst);
|
||||
i.imm_without_flag(offset);
|
||||
i.op2_reg(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::mov_byte_load_imm(Reg dst, uint8_t imm)
|
||||
{
|
||||
}
|
||||
|
||||
void Builder::mov_byte_load_mem_reg(Reg dst, Reg addr, uint16_t offset)
|
||||
{
|
||||
}
|
||||
|
||||
void Builder::mov_byte_load_mem_imm(Reg dst, uint16_t addr)
|
||||
{
|
||||
}
|
||||
|
||||
void Builder::mov_byte_store_reg_reg(Reg dst, uint16_t offset, Reg op2)
|
||||
{
|
||||
}
|
||||
|
||||
void Builder::mov_byte_store_reg_imm(Reg dst, uint16_t offset, uint8_t op2)
|
||||
{
|
||||
}
|
||||
|
||||
void Builder::mov_byte_store_imm_reg(uint16_t dst, Reg op2)
|
||||
{
|
||||
}
|
||||
|
||||
void Builder::mov_byte_store_imm_imm(uint16_t dst, uint8_t op2)
|
||||
void Builder::store_byte_imm(uint16_t dst, Reg op2)
|
||||
{
|
||||
auto i = InsBuilder(Op::StoreByte);
|
||||
i.imm(dst);
|
||||
i.op2_reg(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::cmp_reg(Reg op1, Reg op2)
|
||||
{
|
||||
auto i = InsBuilder(Op::Cmp);
|
||||
i.op1_reg(op1);
|
||||
i.op2_reg(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::cmp_imm(Reg op1, Reg op2)
|
||||
void Builder::cmp_imm(Reg op1, uint16_t op2)
|
||||
{
|
||||
auto i = InsBuilder(Op::Cmp);
|
||||
i.op1_reg(op1);
|
||||
i.imm(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::or_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::Or);
|
||||
}
|
||||
|
||||
void Builder::and_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::And);
|
||||
}
|
||||
|
||||
void Builder::xor_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::Xor);
|
||||
}
|
||||
|
||||
void Builder::shl_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::Shl);
|
||||
}
|
||||
|
||||
void Builder::rshl_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::RShl);
|
||||
}
|
||||
|
||||
void Builder::shr_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::Shr);
|
||||
}
|
||||
|
||||
void Builder::rshr_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::RShr);
|
||||
}
|
||||
|
||||
void Builder::add_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::Add);
|
||||
}
|
||||
|
||||
void Builder::sub_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::Sub);
|
||||
}
|
||||
|
||||
void Builder::rsub_reg(Reg dst, Reg op1, Reg op2)
|
||||
{
|
||||
binary_reg(dst, op1, op2, Op::RSub);
|
||||
}
|
||||
|
||||
void Builder::or_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::Xor);
|
||||
}
|
||||
|
||||
void Builder::and_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::And);
|
||||
}
|
||||
|
||||
void Builder::xor_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::Xor);
|
||||
}
|
||||
|
||||
void Builder::shl_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::Shl);
|
||||
}
|
||||
|
||||
void Builder::rshl_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::RShl);
|
||||
}
|
||||
|
||||
void Builder::shr_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::Shr);
|
||||
}
|
||||
|
||||
void Builder::rshr_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::RShr);
|
||||
}
|
||||
|
||||
void Builder::add_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::Add);
|
||||
}
|
||||
|
||||
void Builder::sub_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::Sub);
|
||||
}
|
||||
|
||||
void Builder::rsub_imm(Reg dst, Reg op1, uint16_t op2)
|
||||
{
|
||||
binary_imm(dst, op1, op2, Op::RSub);
|
||||
}
|
||||
|
||||
void Builder::binary_reg(Reg dst, Reg op1, Reg op2, Op op)
|
||||
{
|
||||
auto i = InsBuilder(op);
|
||||
i.dst_reg(dst);
|
||||
i.op1_reg(op1);
|
||||
i.op2_reg(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
void Builder::binary_imm(Reg dst, Reg op1, uint16_t op2, Op op)
|
||||
{
|
||||
auto i = InsBuilder(op);
|
||||
i.dst_reg(dst);
|
||||
i.op1_reg(op1);
|
||||
i.imm(op2);
|
||||
i.build(*this);
|
||||
}
|
||||
|
||||
@ -2,9 +2,12 @@
|
||||
|
||||
#include "vm.hpp"
|
||||
#include <cstdint>
|
||||
#include <print>
|
||||
|
||||
namespace vc5 {
|
||||
|
||||
using namespace vc5;
|
||||
|
||||
class Builder {
|
||||
public:
|
||||
Builder(uint8_t* data)
|
||||
@ -18,24 +21,18 @@ public:
|
||||
void jmp_imm(uint16_t op1);
|
||||
void jnz_reg(Reg op1, Reg op2);
|
||||
void jnz_imm(Reg op1, uint16_t op2);
|
||||
void mov_word_load_reg(Reg dst, Reg src);
|
||||
void mov_word_load_imm(Reg dst, uint16_t imm);
|
||||
void mov_word_load_mem_reg(Reg dst, Reg addr, uint16_t offset);
|
||||
void mov_word_load_mem_imm(Reg dst, uint16_t addr);
|
||||
void mov_word_store_reg_reg(Reg dst, uint16_t offset, Reg op2);
|
||||
void mov_word_store_reg_imm(Reg dst, uint16_t offset, uint16_t op2);
|
||||
void mov_word_store_imm_reg(uint16_t dst, Reg op2);
|
||||
void mov_word_store_imm_imm(uint16_t dst, uint16_t op2);
|
||||
void mov_byte_load_reg(Reg dst, Reg src);
|
||||
void mov_byte_load_imm(Reg dst, uint8_t imm);
|
||||
void mov_byte_load_mem_reg(Reg dst, Reg addr, uint16_t offset);
|
||||
void mov_byte_load_mem_imm(Reg dst, uint16_t addr);
|
||||
void mov_byte_store_reg_reg(Reg dst, uint16_t offset, Reg op2);
|
||||
void mov_byte_store_reg_imm(Reg dst, uint16_t offset, uint8_t op2);
|
||||
void mov_byte_store_imm_reg(uint16_t dst, Reg op2);
|
||||
void mov_byte_store_imm_imm(uint16_t dst, uint8_t op2);
|
||||
void mov_reg(Reg dst, Reg src);
|
||||
void mov_imm(Reg dst, uint16_t imm);
|
||||
void load_word_reg(Reg dst, Reg addr, uint16_t offset);
|
||||
void load_word_imm(Reg dst, uint16_t addr);
|
||||
void store_word_reg(Reg dst, uint16_t offset, Reg op2);
|
||||
void store_word_imm(uint16_t dst, Reg op2);
|
||||
void load_byte_reg(Reg dst, Reg addr, uint16_t offset);
|
||||
void load_byte_imm(Reg dst, uint16_t addr);
|
||||
void store_byte_reg(Reg dst, uint16_t offset, Reg op2);
|
||||
void store_byte_imm(uint16_t dst, Reg op2);
|
||||
void cmp_reg(Reg op1, Reg op2);
|
||||
void cmp_imm(Reg op1, Reg op2);
|
||||
void cmp_imm(Reg op1, uint16_t op2);
|
||||
void or_reg(Reg dst, Reg op1, Reg op2);
|
||||
void and_reg(Reg dst, Reg op1, Reg op2);
|
||||
void xor_reg(Reg dst, Reg op1, Reg op2);
|
||||
@ -57,14 +54,27 @@ public:
|
||||
void sub_imm(Reg dst, Reg op1, uint16_t op2);
|
||||
void rsub_imm(Reg dst, Reg op1, uint16_t op2);
|
||||
|
||||
private:
|
||||
uint16_t ip() const
|
||||
{
|
||||
return m_ip & 0xffff;
|
||||
}
|
||||
|
||||
void set_ip(uint16_t ip)
|
||||
{
|
||||
m_ip = ip;
|
||||
}
|
||||
|
||||
inline void push(uint16_t v)
|
||||
{
|
||||
m_data[m_ip] = v >> 16;
|
||||
m_data[m_ip] = v >> 8;
|
||||
m_data[m_ip + 1] = v & 0xff;
|
||||
m_ip += 2;
|
||||
}
|
||||
|
||||
private:
|
||||
void binary_reg(Reg dst, Reg op1, Reg op2, Op op);
|
||||
void binary_imm(Reg dst, Reg op1, uint16_t op2, Op op);
|
||||
|
||||
uint8_t* m_data;
|
||||
size_t m_ip = 0;
|
||||
};
|
||||
|
||||
62
src/main.cpp
62
src/main.cpp
@ -1,6 +1,66 @@
|
||||
#include "builder.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <print>
|
||||
#include <utility>
|
||||
|
||||
using namespace vc5;
|
||||
using R = Reg;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::println("hello world");
|
||||
constexpr R r0 = R::R0;
|
||||
constexpr R r1 = R::R1;
|
||||
constexpr R r2 = R::R2;
|
||||
constexpr R r3 = R::R3;
|
||||
constexpr R r4 = R::R4;
|
||||
constexpr R r5 = R::R5;
|
||||
constexpr R rbp = R::Rbp;
|
||||
constexpr R rsp = R::Rsp;
|
||||
constexpr R rfl = R::Rfl;
|
||||
constexpr R rip = R::Rip;
|
||||
|
||||
auto program = std::array<uint8_t, 65535>();
|
||||
|
||||
auto l = Builder(program.data());
|
||||
|
||||
l.mov_imm(r0, 4);
|
||||
|
||||
auto l0 = l.ip();
|
||||
l.cmp_imm(r0, 0);
|
||||
l.mov_reg(r1, rfl);
|
||||
l.and_imm(r1, r1, 1 << std::to_underlying(Flag::Eq));
|
||||
|
||||
auto i0 = l.ip();
|
||||
l.jnz_imm(r1, 0);
|
||||
|
||||
l.sub_imm(r0, r0, 1);
|
||||
l.jmp_imm(l0);
|
||||
|
||||
auto l1 = l.ip();
|
||||
l.hlt();
|
||||
|
||||
l.set_ip(i0 + 2);
|
||||
l.push(l1);
|
||||
|
||||
auto vm = VM();
|
||||
vm.load(0, program.data(), program.size());
|
||||
|
||||
vm.run();
|
||||
|
||||
std::println("--- regs ---");
|
||||
std::println("r0\t{}", vm.reg(r0));
|
||||
std::println("r1\t{}", vm.reg(r1));
|
||||
std::println("r2\t{}", vm.reg(r2));
|
||||
std::println("r3\t{}", vm.reg(r3));
|
||||
std::println("r4\t{}", vm.reg(r4));
|
||||
std::println("r5\t{}", vm.reg(r5));
|
||||
std::println("rbp\t{}", vm.reg(rbp));
|
||||
std::println("rsp\t{}", vm.reg(rsp));
|
||||
std::println("rfl\t{}", vm.reg(rfl));
|
||||
std::println("rip\t{}", vm.reg(rip));
|
||||
std::println("--- mem ---");
|
||||
for (uint16_t i = 0; i < 32; i += 2) {
|
||||
std::println("{: 4x}\t{}", i, vm.word(i));
|
||||
}
|
||||
}
|
||||
|
||||
184
src/vm.cpp
184
src/vm.cpp
@ -4,6 +4,26 @@
|
||||
|
||||
using namespace vc5;
|
||||
|
||||
namespace {
|
||||
|
||||
uint16_t mem_addr(Ins& ins, uint16_t imm)
|
||||
{
|
||||
bool is_imm = ins >> 6 & 1;
|
||||
if (is_imm) {
|
||||
return imm;
|
||||
}
|
||||
|
||||
auto base = ins.op2();
|
||||
auto offset = static_cast<int16_t>(imm);
|
||||
if (offset >= 0) {
|
||||
return base + static_cast<uint16_t>(offset);
|
||||
} else {
|
||||
return base - static_cast<uint16_t>(-offset);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int VM::run()
|
||||
{
|
||||
m_halted = false;
|
||||
@ -17,8 +37,17 @@ int VM::run()
|
||||
|
||||
int VM::run_instruction()
|
||||
{
|
||||
auto ins = eat_ins();
|
||||
auto ip = *m_rip;
|
||||
if (ip > 64)
|
||||
return -1;
|
||||
|
||||
auto ins = Ins(*this);
|
||||
auto op = static_cast<Op>(ins & 0b11'1111);
|
||||
std::println("[{: 4x}]: {:04x} {}, r0 = {}",
|
||||
ip,
|
||||
ins.operator uint16_t(),
|
||||
op_str(op),
|
||||
m_regs[reg_id(Reg::R0)]);
|
||||
|
||||
switch (op) {
|
||||
case Op::Nop:
|
||||
@ -40,85 +69,57 @@ int VM::run_instruction()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Op::MovWord: {
|
||||
bool is_memory = ins >> 10 & 1;
|
||||
bool addr_is_reg = ins >> 11 & 1;
|
||||
bool is_store = ins >> 12 & 1;
|
||||
|
||||
if (!is_memory) {
|
||||
auto src = ins.reg_val_or_imm(6, 7, 0b1111);
|
||||
case Op::Mov: {
|
||||
auto dst = static_cast<uint16_t>(ins >> 12 & 0b1111);
|
||||
auto src = ins.reg_val_or_imm(6, 7, 0b1111);
|
||||
m_regs[dst] = src;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t addr;
|
||||
if (addr_is_reg) {
|
||||
auto reg = is_store ? ins.dst_reg() : ins.op2_reg();
|
||||
auto offset = static_cast<int16_t>(eat());
|
||||
addr = static_cast<uint16_t>(
|
||||
static_cast<int16_t>(m_regs[reg]) + offset);
|
||||
} else {
|
||||
addr = eat();
|
||||
}
|
||||
case Op::LoadWord: {
|
||||
auto addr = mem_addr(ins, eat());
|
||||
|
||||
if ((addr & 0b1) != 0) {
|
||||
std::println(stderr, "error: invalid address alignment");
|
||||
*m_rfl |= 1 << flag(Flag::Err);
|
||||
*m_rfl |= 1 << flag_id(Flag::Err);
|
||||
goto halt_execution;
|
||||
}
|
||||
|
||||
if (is_store) {
|
||||
auto src = ins.op2_or_imm();
|
||||
auto reg = ins.dst_reg();
|
||||
|
||||
uint16_t value = 0;
|
||||
value |= static_cast<uint16_t>(m_mem[addr]) << 16;
|
||||
value |= m_mem[addr + 1];
|
||||
|
||||
m_regs[reg] = value;
|
||||
break;
|
||||
}
|
||||
case Op::StoreWord: {
|
||||
auto addr = mem_addr(ins, eat());
|
||||
|
||||
if ((addr & 0b1) != 0) {
|
||||
std::println(stderr, "error: invalid address alignment");
|
||||
*m_rfl |= 1 << flag_id(Flag::Err);
|
||||
goto halt_execution;
|
||||
}
|
||||
|
||||
auto src = ins.op2();
|
||||
m_mem[addr] = src >> 16;
|
||||
m_mem[addr + 1] = src & 0xff;
|
||||
} else {
|
||||
|
||||
break;
|
||||
}
|
||||
case Op::LoadByte: {
|
||||
auto addr = mem_addr(ins, eat());
|
||||
auto reg = ins.dst_reg();
|
||||
|
||||
uint16_t value = 0;
|
||||
value |= static_cast<uint16_t>(m_mem[addr]) << 16;
|
||||
value |= m_mem[addr + 1];
|
||||
|
||||
m_regs[reg] = value;
|
||||
}
|
||||
|
||||
m_regs[reg] = m_mem[addr];
|
||||
break;
|
||||
}
|
||||
case Op::MovByte: {
|
||||
bool is_memory = ins >> 10 & 1;
|
||||
bool addr_is_reg = ins >> 11 & 1;
|
||||
bool is_store = ins >> 12 & 1;
|
||||
case Op::StoreByte: {
|
||||
auto addr = mem_addr(ins, eat());
|
||||
auto src = ins.op2();
|
||||
|
||||
if (!is_memory) {
|
||||
auto src = ins.reg_val_or_imm(6, 7, 0b1111);
|
||||
auto dst = static_cast<uint16_t>(ins >> 12 & 0b1111);
|
||||
m_regs[dst] = src & 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t addr;
|
||||
if (addr_is_reg) {
|
||||
auto reg = is_store ? ins.dst_reg() : ins.op2_reg();
|
||||
auto offset = static_cast<int16_t>(eat());
|
||||
addr = static_cast<uint16_t>(
|
||||
static_cast<int16_t>(m_regs[reg]) + offset);
|
||||
} else {
|
||||
addr = eat();
|
||||
}
|
||||
|
||||
if (is_store) {
|
||||
auto src = ins.op2_or_imm();
|
||||
m_mem[addr] = src & 0xff;
|
||||
} else {
|
||||
auto reg = ins.dst_reg();
|
||||
|
||||
uint16_t value = 0;
|
||||
value |= static_cast<uint16_t>(m_mem[addr]) << 16;
|
||||
value |= m_mem[addr + 1];
|
||||
|
||||
m_regs[reg] = value;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Op::Cmp: {
|
||||
@ -126,19 +127,19 @@ int VM::run_instruction()
|
||||
auto op2 = ins.op2_or_imm();
|
||||
|
||||
if (op1 == op2) {
|
||||
*m_rfl |= 1u << flag(Flag::Eq);
|
||||
*m_rfl |= 1u << flag_id(Flag::Eq);
|
||||
} else {
|
||||
*m_rfl &= (uint16_t)~(1u << flag(Flag::Eq));
|
||||
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Eq));
|
||||
}
|
||||
if (op1 < op2) {
|
||||
*m_rfl |= 1u << flag(Flag::Be);
|
||||
*m_rfl |= 1u << flag_id(Flag::Be);
|
||||
} else {
|
||||
*m_rfl &= (uint16_t)~(1u << flag(Flag::Be));
|
||||
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Be));
|
||||
}
|
||||
if ((int16_t)op1 < (int16_t)op2) {
|
||||
*m_rfl |= 1u << flag(Flag::Lt);
|
||||
*m_rfl |= 1u << flag_id(Flag::Lt);
|
||||
} else {
|
||||
*m_rfl &= (uint16_t)~(1u << flag(Flag::Lt));
|
||||
*m_rfl &= (uint16_t)~(1u << flag_id(Flag::Lt));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -200,3 +201,50 @@ int VM::run_instruction()
|
||||
halt_execution:
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* vc5::op_str(Op op)
|
||||
{
|
||||
switch (op) {
|
||||
case Op::Nop:
|
||||
return "Nop";
|
||||
case Op::Hlt:
|
||||
return "Hlt";
|
||||
case Op::Jmp:
|
||||
return "Jmp";
|
||||
case Op::Jnz:
|
||||
return "Jnz";
|
||||
case Op::Mov:
|
||||
return "Mov";
|
||||
case Op::LoadWord:
|
||||
return "LoadWord";
|
||||
case Op::StoreWord:
|
||||
return "StoreWord";
|
||||
case Op::LoadByte:
|
||||
return "LoadByte";
|
||||
case Op::StoreByte:
|
||||
return "StoreByte";
|
||||
case Op::Cmp:
|
||||
return "Cmp";
|
||||
case Op::Or:
|
||||
return "Or";
|
||||
case Op::And:
|
||||
return "And";
|
||||
case Op::Xor:
|
||||
return "Xor";
|
||||
case Op::Shl:
|
||||
return "Shl";
|
||||
case Op::RShl:
|
||||
return "RShl";
|
||||
case Op::Shr:
|
||||
return "Shr";
|
||||
case Op::RShr:
|
||||
return "RShr";
|
||||
case Op::Add:
|
||||
return "Add";
|
||||
case Op::Sub:
|
||||
return "Sub";
|
||||
case Op::RSub:
|
||||
return "RSub";
|
||||
}
|
||||
return ">.<";
|
||||
}
|
||||
|
||||
115
src/vm.hpp
115
src/vm.hpp
@ -3,6 +3,8 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
namespace vc5 {
|
||||
@ -12,8 +14,11 @@ enum class Op : uint16_t {
|
||||
Hlt,
|
||||
Jmp,
|
||||
Jnz,
|
||||
MovWord,
|
||||
MovByte,
|
||||
Mov,
|
||||
LoadWord,
|
||||
StoreWord,
|
||||
LoadByte,
|
||||
StoreByte,
|
||||
Cmp,
|
||||
Or,
|
||||
And,
|
||||
@ -27,6 +32,8 @@ enum class Op : uint16_t {
|
||||
RSub,
|
||||
};
|
||||
|
||||
const char* op_str(Op op);
|
||||
|
||||
enum class Reg : uint16_t {
|
||||
R0 = 0,
|
||||
R1 = 1,
|
||||
@ -48,12 +55,71 @@ enum class Flag : uint16_t {
|
||||
Err = 4,
|
||||
};
|
||||
|
||||
struct Ins;
|
||||
|
||||
class VM {
|
||||
public:
|
||||
struct Ins {
|
||||
Ins(VM& vm, uint16_t ins)
|
||||
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_id(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;
|
||||
|
||||
static constexpr uint16_t reg_id(Reg reg)
|
||||
{
|
||||
return std::to_underlying(reg);
|
||||
}
|
||||
|
||||
static constexpr uint16_t flag_id(Flag flag)
|
||||
{
|
||||
return std::to_underlying(flag);
|
||||
}
|
||||
|
||||
int run_instruction();
|
||||
|
||||
inline uint16_t eat()
|
||||
{
|
||||
uint16_t ins = 0;
|
||||
ins |= static_cast<uint16_t>(m_mem[*m_rip]) << 8;
|
||||
ins |= m_mem[*m_rip + 1];
|
||||
*m_rip += 2;
|
||||
return ins;
|
||||
}
|
||||
|
||||
std::array<uint8_t, 65536> m_mem = {};
|
||||
std::array<uint16_t, 10> m_regs = {};
|
||||
|
||||
uint16_t* m_rfl = &m_regs[reg_id(Reg::Rfl)];
|
||||
uint16_t* m_rip = &m_regs[reg_id(Reg::Rip)];
|
||||
|
||||
bool m_halted = false;
|
||||
};
|
||||
|
||||
struct Ins {
|
||||
Ins(VM& vm)
|
||||
: vm(&vm)
|
||||
, ins(ins)
|
||||
, ins(vm.eat())
|
||||
{
|
||||
}
|
||||
|
||||
@ -111,45 +177,6 @@ public:
|
||||
return vm->m_regs[ins >> reg_bit & reg_mask];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int run();
|
||||
|
||||
private:
|
||||
int run_instruction();
|
||||
|
||||
inline uint16_t eat()
|
||||
{
|
||||
uint16_t ins = 0;
|
||||
ins |= static_cast<uint16_t>(m_mem[*m_rip]) << 16;
|
||||
ins |= m_mem[*m_rip + 1];
|
||||
*m_rip += 2;
|
||||
return ins;
|
||||
}
|
||||
|
||||
inline Ins eat_ins()
|
||||
{
|
||||
auto ins = eat();
|
||||
return Ins(*this, ins);
|
||||
}
|
||||
|
||||
static constexpr uint16_t reg(Reg reg)
|
||||
{
|
||||
return std::to_underlying(reg);
|
||||
}
|
||||
|
||||
static constexpr uint16_t flag(Flag flag)
|
||||
{
|
||||
return std::to_underlying(flag);
|
||||
}
|
||||
|
||||
std::array<uint8_t, 65536> m_mem = {};
|
||||
std::array<uint16_t, 8> m_regs = {};
|
||||
|
||||
uint16_t* m_rfl = &m_regs[reg(Reg::Rfl)];
|
||||
uint16_t* m_rip = &m_regs[reg(Reg::Rip)];
|
||||
|
||||
bool m_halted = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user