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

261 lines
6.3 KiB
C++

#include "vm.hpp"
#include <cstdint>
#include <print>
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;
while (not m_halted) {
int result = run_instruction();
if (result != 0)
return result;
}
return 0;
}
int VM::run_instruction()
{
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_value(Reg::R0)]);
switch (op) {
case Op::Nop:
break;
case Op::Hlt:
m_halted = true;
break;
case Op::Jmp: {
auto op1 = ins.op1_or_imm();
*m_rip = op1;
break;
}
case Op::Jnz: {
auto op1 = ins.op1();
auto op2 = ins.op2_or_imm();
if (op1 != 0) {
*m_rip = op2;
}
break;
}
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;
}
case Op::LoadWord: {
auto addr = mem_addr(ins, eat());
if ((addr & 0b1) != 0) {
std::println(stderr, "error: invalid address alignment");
*m_rfl |= 1 << flag_value(Flag::Err);
goto halt_execution;
}
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_value(Flag::Err);
goto halt_execution;
}
auto src = ins.op2();
m_mem[addr] = src >> 16;
m_mem[addr + 1] = src & 0xff;
break;
}
case Op::LoadByte: {
auto addr = mem_addr(ins, eat());
auto reg = ins.dst_reg();
m_regs[reg] = m_mem[addr];
break;
}
case Op::StoreByte: {
auto addr = mem_addr(ins, eat());
auto src = ins.op2();
m_mem[addr] = src & 0xff;
break;
}
case Op::Cmp: {
auto op1 = ins.op1();
auto op2 = ins.op2_or_imm();
if (op1 == op2) {
*m_rfl |= 1u << flag_value(Flag::Eq);
} else {
*m_rfl &= (uint16_t)~(1u << flag_value(Flag::Eq));
}
if (op1 < op2) {
*m_rfl |= 1u << flag_value(Flag::Be);
} else {
*m_rfl &= (uint16_t)~(1u << flag_value(Flag::Be));
}
if ((int16_t)op1 < (int16_t)op2) {
*m_rfl |= 1u << flag_value(Flag::Lt);
} else {
*m_rfl &= (uint16_t)~(1u << flag_value(Flag::Lt));
}
break;
}
case Op::Or:
case Op::And:
case Op::Xor:
case Op::Shl:
case Op::RShl:
case Op::Shr:
case Op::RShr:
case Op::Add:
case Op::Sub:
case Op::RSub: {
auto op1 = ins.op1();
auto op2 = ins.op2_or_imm();
auto dst_reg = ins.dst_reg();
uint16_t* dst = &m_regs[dst_reg];
switch (op) {
case Op::Or:
*dst = op1 | op2;
break;
case Op::Xor:
*dst = op1 ^ op2;
break;
case Op::And:
*dst = op1 & op2;
break;
case Op::Shl:
*dst = static_cast<uint16_t>(op1 << op2);
break;
case Op::RShl:
*dst = static_cast<uint16_t>(op2 << op1);
break;
case Op::Shr:
*dst = static_cast<uint16_t>(op1 >> op2);
break;
case Op::RShr:
*dst = static_cast<uint16_t>(op2 >> op1);
break;
case Op::Add:
*dst = op1 + op2;
break;
case Op::Sub:
*dst = op1 - op2;
break;
case Op::RSub:
*dst = op2 - op1;
break;
default:
break;
}
break;
}
}
halt_execution:
return 0;
}
uint16_t VM::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;
}
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 ">.<";
}