From 3116ec1a1338883f8e77151fdfc4ef9d9006f603 Mon Sep 17 00:00:00 2001 From: sfja Date: Sun, 4 Jan 2026 23:28:32 +0100 Subject: [PATCH] init --- .clang-format | 14 ++++ .gitignore | 1 + Makefile | 27 +++++++ compile_flags.txt | 10 +++ src/builder.cpp | 185 ++++++++++++++++++++++++++++++++++++++++++ src/builder.hpp | 72 +++++++++++++++++ src/main.cpp | 6 ++ src/vm.cpp | 202 ++++++++++++++++++++++++++++++++++++++++++++++ src/vm.hpp | 155 +++++++++++++++++++++++++++++++++++ 9 files changed, 672 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 compile_flags.txt create mode 100644 src/builder.cpp create mode 100644 src/builder.hpp create mode 100644 src/main.cpp create mode 100644 src/vm.cpp create mode 100644 src/vm.hpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a56cbd0 --- /dev/null +++ b/.clang-format @@ -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 + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e58e90e --- /dev/null +++ b/Makefile @@ -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) + diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..88c23e4 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,10 @@ +-xc++ +-std=c++23 +-Wall +-Wextra +-Wpedantic +-Wconversion +-pedantic +-pedantic-errors +-Wno-unused-variable + diff --git a/src/builder.cpp b/src/builder.cpp new file mode 100644 index 0000000..f0a9040 --- /dev/null +++ b/src/builder.cpp @@ -0,0 +1,185 @@ +#include "builder.hpp" +#include "vm.hpp" +#include + +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) +{ +} diff --git a/src/builder.hpp b/src/builder.hpp new file mode 100644 index 0000000..b01e076 --- /dev/null +++ b/src/builder.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include "vm.hpp" +#include + +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; +}; + +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..b2a9d49 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,6 @@ +#include + +int main() +{ + std::println("hello world"); +} diff --git a/src/vm.cpp b/src/vm.cpp new file mode 100644 index 0000000..96b2d2b --- /dev/null +++ b/src/vm.cpp @@ -0,0 +1,202 @@ +#include "vm.hpp" +#include +#include + +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(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(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(eat()); + addr = static_cast( + static_cast(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(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(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(eat()); + addr = static_cast( + static_cast(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(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(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; +} diff --git a/src/vm.hpp b/src/vm.hpp new file mode 100644 index 0000000..8b31134 --- /dev/null +++ b/src/vm.hpp @@ -0,0 +1,155 @@ +#pragma once + +#include +#include +#include +#include + +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(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 m_mem = {}; + std::array m_regs = {}; + + uint16_t* m_rfl = &m_regs[reg(Reg::Rfl)]; + uint16_t* m_rip = &m_regs[reg(Reg::Rip)]; + + bool m_halted = false; +}; + +}