init
This commit is contained in:
commit
3116ec1a13
14
.clang-format
Normal file
14
.clang-format
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: WebKit
|
||||||
|
IndentWidth: 4
|
||||||
|
ColumnLimit: 80
|
||||||
|
IndentCaseLabels: true
|
||||||
|
InsertNewlineAtEOF: true
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
|
||||||
|
BinPackArguments: false
|
||||||
|
AllowAllArgumentsOnNextLine: true
|
||||||
|
|
||||||
|
BinPackParameters: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
|
||||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/
|
||||||
27
Makefile
Normal file
27
Makefile
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
MAKEFLAGS += -j16
|
||||||
|
|
||||||
|
CXXFLAGS := -std=c++23 -Wall -Wextra -pedantic-errors -fsanitize=address,undefined
|
||||||
|
LDFLAGS :=
|
||||||
|
|
||||||
|
build_dir = build
|
||||||
|
obj_dir = $(build_dir)/obj
|
||||||
|
|
||||||
|
sources := $(shell find src/ -name *.cpp)
|
||||||
|
|
||||||
|
all: $(build_dir)/vc5
|
||||||
|
|
||||||
|
$(build_dir)/vc5: $(sources:%.cpp=$(obj_dir)/%.o)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
g++ $^ -o $@ $(CXXFLAGS) $(LDFLAGS)
|
||||||
|
|
||||||
|
$(obj_dir)/%.o: %.cpp
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
g++ $< -c -o $@ -MMD -MP $(CXXFLAGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -rf $(build_dir)
|
||||||
|
|
||||||
|
-include $(sources:%.cpp=$(obj_dir)/%.d)
|
||||||
|
|
||||||
10
compile_flags.txt
Normal file
10
compile_flags.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
-xc++
|
||||||
|
-std=c++23
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-Wconversion
|
||||||
|
-pedantic
|
||||||
|
-pedantic-errors
|
||||||
|
-Wno-unused-variable
|
||||||
|
|
||||||
185
src/builder.cpp
Normal file
185
src/builder.cpp
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#include "builder.hpp"
|
||||||
|
#include "vm.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
using namespace vc5;
|
||||||
|
|
||||||
|
void Builder::nop()
|
||||||
|
{
|
||||||
|
auto ins = std::to_underlying(Op::Nop);
|
||||||
|
push(ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::hlt()
|
||||||
|
{
|
||||||
|
auto ins = std::to_underlying(Op::Nop);
|
||||||
|
push(ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::jmp_reg(Reg op1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::jmp_imm(uint16_t op1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::jnz_reg(Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::jnz_imm(Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_word_load_reg(Reg dst, Reg src)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_word_load_imm(Reg dst, uint16_t imm)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_word_load_mem_reg(Reg dst, Reg addr, uint16_t offset)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_word_load_mem_imm(Reg dst, uint16_t addr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_word_store_reg_reg(Reg dst, uint16_t offset, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_word_store_reg_imm(Reg dst, uint16_t offset, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_word_store_imm_reg(uint16_t dst, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_word_store_imm_imm(uint16_t dst, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::mov_byte_load_reg(Reg dst, Reg src)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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::cmp_reg(Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::cmp_imm(Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::or_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::and_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::xor_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::shl_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::rshl_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::shr_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::rshr_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::add_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::sub_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::rsub_reg(Reg dst, Reg op1, Reg op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::or_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::and_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::xor_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::shl_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::rshl_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::shr_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::rshr_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::add_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::sub_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Builder::rsub_imm(Reg dst, Reg op1, uint16_t op2)
|
||||||
|
{
|
||||||
|
}
|
||||||
72
src/builder.hpp
Normal file
72
src/builder.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "vm.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace vc5 {
|
||||||
|
|
||||||
|
class Builder {
|
||||||
|
public:
|
||||||
|
Builder(uint8_t* data)
|
||||||
|
: m_data(data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void nop();
|
||||||
|
void hlt();
|
||||||
|
void jmp_reg(Reg op1);
|
||||||
|
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 cmp_reg(Reg op1, Reg op2);
|
||||||
|
void cmp_imm(Reg op1, Reg 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);
|
||||||
|
void shl_reg(Reg dst, Reg op1, Reg op2);
|
||||||
|
void rshl_reg(Reg dst, Reg op1, Reg op2);
|
||||||
|
void shr_reg(Reg dst, Reg op1, Reg op2);
|
||||||
|
void rshr_reg(Reg dst, Reg op1, Reg op2);
|
||||||
|
void add_reg(Reg dst, Reg op1, Reg op2);
|
||||||
|
void sub_reg(Reg dst, Reg op1, Reg op2);
|
||||||
|
void rsub_reg(Reg dst, Reg op1, Reg op2);
|
||||||
|
void or_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void and_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void xor_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void shl_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void rshl_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void shr_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void rshr_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void add_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void sub_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
void rsub_imm(Reg dst, Reg op1, uint16_t op2);
|
||||||
|
|
||||||
|
private:
|
||||||
|
inline void push(uint16_t v)
|
||||||
|
{
|
||||||
|
m_data[m_ip] = v >> 16;
|
||||||
|
m_data[m_ip + 1] = v & 0xff;
|
||||||
|
m_ip += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* m_data;
|
||||||
|
size_t m_ip = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
6
src/main.cpp
Normal file
6
src/main.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include <print>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::println("hello world");
|
||||||
|
}
|
||||||
202
src/vm.cpp
Normal file
202
src/vm.cpp
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
#include "vm.hpp"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <print>
|
||||||
|
|
||||||
|
using namespace vc5;
|
||||||
|
|
||||||
|
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 ins = eat_ins();
|
||||||
|
auto op = static_cast<Op>(ins & 0b11'1111);
|
||||||
|
|
||||||
|
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::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);
|
||||||
|
auto dst = static_cast<uint16_t>(ins >> 12 & 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((addr & 0b1) != 0) {
|
||||||
|
std::println(stderr, "error: invalid address alignment");
|
||||||
|
*m_rfl |= 1 << flag(Flag::Err);
|
||||||
|
goto halt_execution;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_store) {
|
||||||
|
auto src = ins.op2_or_imm();
|
||||||
|
m_mem[addr] = src >> 16;
|
||||||
|
m_mem[addr + 1] = 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::MovByte: {
|
||||||
|
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);
|
||||||
|
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: {
|
||||||
|
auto op1 = ins.op1();
|
||||||
|
auto op2 = ins.op2_or_imm();
|
||||||
|
|
||||||
|
if (op1 == op2) {
|
||||||
|
*m_rfl |= 1u << flag(Flag::Eq);
|
||||||
|
} else {
|
||||||
|
*m_rfl &= (uint16_t)~(1u << flag(Flag::Eq));
|
||||||
|
}
|
||||||
|
if (op1 < op2) {
|
||||||
|
*m_rfl |= 1u << flag(Flag::Be);
|
||||||
|
} else {
|
||||||
|
*m_rfl &= (uint16_t)~(1u << flag(Flag::Be));
|
||||||
|
}
|
||||||
|
if ((int16_t)op1 < (int16_t)op2) {
|
||||||
|
*m_rfl |= 1u << flag(Flag::Lt);
|
||||||
|
} else {
|
||||||
|
*m_rfl &= (uint16_t)~(1u << flag(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;
|
||||||
|
}
|
||||||
155
src/vm.hpp
Normal file
155
src/vm.hpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace vc5 {
|
||||||
|
|
||||||
|
enum class Op : uint16_t {
|
||||||
|
Nop,
|
||||||
|
Hlt,
|
||||||
|
Jmp,
|
||||||
|
Jnz,
|
||||||
|
MovWord,
|
||||||
|
MovByte,
|
||||||
|
Cmp,
|
||||||
|
Or,
|
||||||
|
And,
|
||||||
|
Xor,
|
||||||
|
Shl,
|
||||||
|
RShl,
|
||||||
|
Shr,
|
||||||
|
RShr,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
RSub,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Reg : uint16_t {
|
||||||
|
R0 = 0,
|
||||||
|
R1 = 1,
|
||||||
|
R2 = 2,
|
||||||
|
R3 = 3,
|
||||||
|
R4 = 4,
|
||||||
|
R5 = 5,
|
||||||
|
Rbp = 6,
|
||||||
|
Rsp = 7,
|
||||||
|
Rfl = 8,
|
||||||
|
Rip = 9,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Flag : uint16_t {
|
||||||
|
Zero = 0,
|
||||||
|
Eq = 1,
|
||||||
|
Be = 2,
|
||||||
|
Lt = 3,
|
||||||
|
Err = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
class VM {
|
||||||
|
public:
|
||||||
|
struct Ins {
|
||||||
|
Ins(VM& vm, uint16_t ins)
|
||||||
|
: vm(&vm)
|
||||||
|
, ins(ins)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VM* vm;
|
||||||
|
uint16_t ins;
|
||||||
|
|
||||||
|
operator uint16_t()
|
||||||
|
{
|
||||||
|
return ins;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t dst_reg() const
|
||||||
|
{
|
||||||
|
return ins >> 13 & 0b111;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t op1_reg() const
|
||||||
|
{
|
||||||
|
return ins >> 10 & 0b111;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t op2_reg() const
|
||||||
|
{
|
||||||
|
return ins >> 7 & 0b111;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t op1() const
|
||||||
|
{
|
||||||
|
return vm->m_regs[op1_reg()];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t op2() const
|
||||||
|
{
|
||||||
|
|
||||||
|
return vm->m_regs[op2_reg()];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t op1_or_imm()
|
||||||
|
{
|
||||||
|
return reg_val_or_imm(6, 10, 0b111);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t op2_or_imm()
|
||||||
|
{
|
||||||
|
return reg_val_or_imm(6, 7, 0b111);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint16_t reg_val_or_imm(
|
||||||
|
uint16_t is_imm_bit, uint16_t reg_bit, uint16_t reg_mask)
|
||||||
|
{
|
||||||
|
bool is_imm = (ins >> is_imm_bit & 1) != 0;
|
||||||
|
if (is_imm) {
|
||||||
|
return vm->eat();
|
||||||
|
} else {
|
||||||
|
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