261 lines
6.3 KiB
C++
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 ">.<";
|
|
}
|
|
|