#include "vm.hpp" #include #include 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(imm); if (offset >= 0) { return base + static_cast(offset); } else { return base - static_cast(-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(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(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(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(op1 << op2); break; case Op::RShl: *dst = static_cast(op2 << op1); break; case Op::Shr: *dst = static_cast(op1 >> op2); break; case Op::RShr: *dst = static_cast(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(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 ">.<"; }