vc5/src/builder.cpp
2026-01-29 23:19:00 +01:00

352 lines
6.3 KiB
C++

#define DEFINE_BINARY_INSTRUCTIONS
#include "builder.hpp"
#include "vm.hpp"
#include <cstdint>
#include <print>
#include <utility>
using namespace vc5::tools;
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;
};
}
#define LOG_F \
{ \
std::println("assembling {} at {}", __func__, this->m_ip); \
}
#define LOG
void Builder::nop()
{
LOG;
auto i = InsBuilder(Op::Nop);
i.build(*this);
}
void Builder::hlt()
{
LOG;
auto i = InsBuilder(Op::Hlt);
i.build(*this);
}
void Builder::jmp_reg(Reg op1)
{
LOG;
auto i = InsBuilder(Op::Jmp);
i.op1_reg(op1);
i.build(*this);
}
void Builder::jmp_imm(uint16_t op1)
{
LOG;
auto i = InsBuilder(Op::Jmp);
i.imm(op1);
i.build(*this);
}
void Builder::jnz_reg(Reg op1, Reg op2)
{
LOG;
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)
{
LOG;
auto i = InsBuilder(Op::Jnz);
i.op1_reg(op1);
i.imm(op2);
i.build(*this);
}
void Builder::mov_reg(Reg dst, Reg src)
{
LOG;
auto i = InsBuilder(Op::Mov);
i.reg(dst, 12, 0b1111);
i.reg(src, 7, 0b1111);
i.build(*this);
}
void Builder::mov_imm(Reg dst, uint16_t imm)
{
LOG;
auto i = InsBuilder(Op::Mov);
i.reg(dst, 12, 0b1111);
i.imm(imm);
i.build(*this);
}
void Builder::ldw_reg(Reg dst, Reg addr, uint16_t offset)
{
LOG;
auto i = InsBuilder(Op::LdW);
i.dst_reg(dst);
i.op1_reg(addr);
i.imm_without_flag(offset);
i.build(*this);
}
void Builder::ldw_imm(Reg dst, uint16_t addr)
{
LOG;
auto i = InsBuilder(Op::LdW);
i.dst_reg(dst);
i.imm(addr);
i.build(*this);
}
void Builder::stw_reg(Reg dst, uint16_t offset, Reg op2)
{
LOG;
auto i = InsBuilder(Op::StW);
i.dst_reg(dst);
i.imm_without_flag(offset);
i.op2_reg(op2);
i.build(*this);
}
void Builder::stw_imm(uint16_t dst, Reg op2)
{
LOG;
auto i = InsBuilder(Op::StW);
i.imm(dst);
i.op2_reg(op2);
i.build(*this);
}
void Builder::ldb_reg(Reg dst, Reg addr, uint16_t offset)
{
LOG;
auto i = InsBuilder(Op::LdB);
i.dst_reg(dst);
i.op1_reg(addr);
i.imm_without_flag(offset);
i.build(*this);
}
void Builder::ldb_imm(Reg dst, uint16_t addr)
{
LOG;
auto i = InsBuilder(Op::LdB);
i.dst_reg(dst);
i.imm(addr);
i.build(*this);
}
void Builder::stb_reg(Reg dst, uint16_t offset, Reg op2)
{
LOG;
auto i = InsBuilder(Op::StB);
i.dst_reg(dst);
i.imm_without_flag(offset);
i.op2_reg(op2);
i.build(*this);
}
void Builder::stb_imm(uint16_t dst, Reg op2)
{
LOG;
auto i = InsBuilder(Op::StB);
i.imm(dst);
i.op2_reg(op2);
i.build(*this);
}
void Builder::cmp_reg(Reg op1, Reg op2)
{
LOG;
auto i = InsBuilder(Op::Cmp);
i.op1_reg(op1);
i.op2_reg(op2);
i.build(*this);
}
void Builder::cmp_imm(Reg op1, uint16_t op2)
{
LOG;
auto i = InsBuilder(Op::Cmp);
i.op1_reg(op1);
i.imm(op2);
i.build(*this);
}
#define X(NAME, OP) \
void Builder::NAME##_reg(Reg dst, Reg op1, Reg op2) \
{ \
LOG; \
binary_reg(dst, op1, op2, Op::OP); \
} \
void Builder::NAME##_imm(Reg dst, Reg op1, uint16_t op2) \
{ \
LOG; \
binary_imm(dst, op1, op2, Op::OP); \
}
BINARY_INSTRUCTIONS
#undef X
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);
}
void Builder::call_reg(Reg op1)
{
LOG;
auto i = InsBuilder(Op::Call);
i.op1_reg(op1);
i.build(*this);
}
void Builder::call_imm(uint16_t op1)
{
LOG;
auto i = InsBuilder(Op::Call);
i.imm(op1);
i.build(*this);
}
void Builder::ret()
{
LOG;
auto i = InsBuilder(Op::Ret);
i.build(*this);
}
void Builder::reti()
{
LOG;
auto i = InsBuilder(Op::RetI);
i.build(*this);
}
void Builder::lvcd_reg(Reg op1)
{
LOG;
auto i = InsBuilder(Op::LVCD);
i.op1_reg(op1);
i.build(*this);
}
void Builder::lvcd_imm(uint16_t op1)
{
LOG;
auto i = InsBuilder(Op::LVCD);
i.imm(op1);
i.build(*this);
}
void Builder::lkbd_reg(Reg op1)
{
LOG;
auto i = InsBuilder(Op::LKBD);
i.op1_reg(op1);
i.build(*this);
}
void Builder::lkbd_imm(uint16_t op1)
{
LOG;
auto i = InsBuilder(Op::LKBD);
i.imm(op1);
i.build(*this);
}
void Builder::dskr(Reg dst, Reg op1)
{
LOG;
auto i = InsBuilder(Op::DSKR);
i.dst_reg(dst);
i.op1_reg(op1);
i.build(*this);
}
void Builder::dskw(Reg dst, Reg op1)
{
LOG;
auto i = InsBuilder(Op::DSKW);
i.dst_reg(dst);
i.op1_reg(op1);
i.build(*this);
}