From ec0b209bc6d8057b603699c99698e54fb44ce9bc Mon Sep 17 00:00:00 2001 From: Andre Wagner Date: Wed, 29 Apr 2026 14:00:55 -0500 Subject: [PATCH] . --- CMakeLists.txt | 7 ++- doc/OPCODES | 11 +++- src/vm/code.cc | 31 +++++++++++ src/vm/code.hh | 2 + src/vm/instruction.cc | 127 ++++++++++++++++++++++++++++++++++++++++++ src/vm/instruction.hh | 89 +++++++++++++++++++++++++++++ 6 files changed, 262 insertions(+), 5 deletions(-) create mode 100644 src/vm/instruction.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 602eaa2..64f8546 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,9 @@ add_library(lib${PROJECT_NAME} SHARED src/common/overloaded.hh src/vm/code.cc src/vm/code.hh + src/bytecode/constant.hh + src/vm/instruction.hh + src/vm/instruction.cc ) target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings}) @@ -75,9 +78,7 @@ target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings}) # tests # -add_executable(${PROJECT_NAME}-bytecode-test src/bytecode/tests.cc - src/bytecode/constant.hh - src/vm/instruction.hh) +add_executable(${PROJECT_NAME}-bytecode-test src/bytecode/tests.cc) target_link_libraries(${PROJECT_NAME}-bytecode-test lib${PROJECT_NAME} gtest_main) add_test(NAME tyche_bytecode_test COMMAND ${PROJECT_NAME}-bytecode-test) diff --git a/doc/OPCODES b/doc/OPCODES index b6eb41c..a22f4c2 100644 --- a/doc/OPCODES +++ b/doc/OPCODES @@ -3,7 +3,14 @@ Operations Operations take either 0 or 1 parameter. The ones that take a parameter, it can be either a int8, int16 or int32. -The binary of the opcode is: XXYY.YYYY, where XX defines the parameter type, and YY.YYYY is the instruction. For the XX values: +Instructions follow this logic: + +00 ~ 9F : no parameter +A0 ~ BF : int8 (1 byte) +C0 ~ DF : int16 (2 bytes) +E0 ~ FF : int32 (4 bytes) + +The operations of 1, 2 and 4 bytes are always interchangeable by adding/subtracting 0x20. ,----------- no parameter | ,-------- int8 @@ -12,7 +19,7 @@ The binary of the opcode is: XXYY.YYYY, where XX defines the parameter type, and NP I8 I16 I32 Opc Instruction Description Stack operations: - a0 c0 e0 pushn [int] Push int + a0 c0 e0 pushi [int] Push int a1 c1 e1 pushc [index] Push constant a2 c2 e2 pushf [function] Push function id 00 pushz Push zero (or false) diff --git a/src/vm/code.cc b/src/vm/code.cc index d2207c5..365e320 100644 --- a/src/vm/code.cc +++ b/src/vm/code.cc @@ -1,10 +1,41 @@ #include "code.hh" +#include "../common/overloaded.hh" +#include "instruction.hh" namespace tyche { void Code::import_bytecode(Bytecode const& incoming) { + // TODO - adjust function calls, constants + bytecode_ = incoming; +} +std::string Code::disassemble() const +{ + std::string out; + + out += ".const\n"; + for (size_t i = 0; i < bytecode_.n_constants(); ++i) { + out += "\t" + std::to_string(i) + ": "; + std::visit(overloaded { + [&out](float f) { out += std::to_string(f); }, + [&out](std::string const& str) { out += "\"" + str + "\""; }, + }, bytecode_.get_constant(i)); + out += "\n"; + } + out += "\n"; + + for (size_t i = 0; i < bytecode_.n_functions(); ++i) { + out += ".func " + std::to_string(i) + "\n"; + uint32_t addr = 0; + while (addr < bytecode_.get_function_sz(i)) { + auto [op, sz] = debug_instruction(bytecode_, i, addr); + out += "\t" + op + "\n"; + addr += sz; + } + } + + return out; } } // tyche diff --git a/src/vm/code.hh b/src/vm/code.hh index 212b43e..ef558a9 100644 --- a/src/vm/code.hh +++ b/src/vm/code.hh @@ -9,6 +9,8 @@ class Code { public: void import_bytecode(Bytecode const& incoming); + [[nodiscard]] std::string disassemble() const; + private: Bytecode bytecode_; }; diff --git a/src/vm/instruction.cc b/src/vm/instruction.cc new file mode 100644 index 0000000..50077a5 --- /dev/null +++ b/src/vm/instruction.cc @@ -0,0 +1,127 @@ +#include "instruction.hh" + +namespace tyche { + +std::pair debug_instruction(Instruction inst, int oper) +{ + std::string out; + switch (inst) { + + case Instruction::PushInt8: + case Instruction::PushInt16: + case Instruction::PushInt32: + out = "pushi"; + break; + case Instruction::PushConstant8: + case Instruction::PushConstant16: + case Instruction::PushConstant32: + out = "pushc"; + break; + case Instruction::PushZero: out = "pushz"; break; + case Instruction::PushTrue: out = "pusht"; break; + case Instruction::NewArray: out = "newa"; break; + case Instruction::NewTable: out = "newt"; break; + case Instruction::Pop: out = "pop"; break; + case Instruction::Duplicate: out = "dup"; break; + case Instruction::SetLocal8: + case Instruction::SetLocal16: + case Instruction::SetLocal32: + out = "setl"; + break; + case Instruction::GetLocal8: + case Instruction::GetLocal16: + case Instruction::GetLocal32: + out = "getl"; + break; + case Instruction::SetGlobal8: + case Instruction::SetGlobal16: + case Instruction::SetGlobal32: + out = "setg"; + break; + case Instruction::GetGlobal8: + case Instruction::GetGlobal16: + case Instruction::GetGlobal32: + out = "getg"; + break; + case Instruction::Call8: + case Instruction::Call16: + case Instruction::Call32: + out = "call"; + break; + case Instruction::Return: out = "ret"; break; + case Instruction::ReturnNil: out = "retn"; break; + case Instruction::GetKeyValue: out = "getkv"; break; + case Instruction::SetKeyValue: out = "setkv"; break; + case Instruction::GetArrayItem: out = "geta"; break; + case Instruction::SetArrayItem: out = "seta"; break; + case Instruction::Append: out = "appnd"; break; + case Instruction::Next: out = "next"; break; + case Instruction::SetMetatable: out = "smt"; break; + case Instruction::GetMetatable: out = "mt"; break; + case Instruction::Sum: out = "sum"; break; + case Instruction::Subtract: out = "sub"; break; + case Instruction::Multiply: out = "mul"; break; + case Instruction::Divide: out = "div"; break; + case Instruction::DivideInt: out = "idiv"; break; + case Instruction::Equals: out = "eq"; break; + case Instruction::NotEquals: out = "neq"; break; + case Instruction::LessThan: out = "lt"; break; + case Instruction::LessThanEq: out = "lte"; break; + case Instruction::GreaterThan: out = "gt"; break; + case Instruction::GreaterThanEq: out = "gte"; break; + case Instruction::And: out = "and"; break; + case Instruction::Or: out = "or"; break; + case Instruction::Xor: out = "xor"; break; + case Instruction::Len: out = "len"; break; + case Instruction::Type: out = "type"; break; + case Instruction::Cast: out = "cast"; break; + case Instruction::Version: out = "ver"; break; + case Instruction::BranchIfZero8: + case Instruction::BranchIfZero16: + case Instruction::BranchIfZero32: + out = "bz"; + break; + case Instruction::BranchIfNotZero8: + case Instruction::BranchIfNotZero16: + case Instruction::BranchIfNotZero32: + out = "bnz"; + break; + case Instruction::Jump8: + case Instruction::Jump16: + case Instruction::Jump32: + out = "jmp"; + break; + case Instruction::Compile: out = "cmpl"; break; + case Instruction::Assemble: out = "asmbl"; break; + case Instruction::Load: out = "load"; break; + default: + out = "???"; + } + + if ((uint8_t) inst < 0xa0) + return { out, 1 }; + + out += " " + std::to_string(oper); + if ((uint8_t) inst >= 0xe0) + return { out, 5 }; + else if ((uint8_t) inst >= 0xc0) + return { out, 3 }; + else + return { out, 2 }; +} + +std::pair debug_instruction(Bytecode const& bt, uint32_t function_id, uint32_t addr) +{ + auto inst = (Instruction) bt.get_code_byte(function_id, addr); + + if ((uint8_t) inst >= 0xe0) + return debug_instruction(inst, bt.get_code_int32(function_id, addr + 1)); + else if ((uint8_t) inst >= 0xc0) + return debug_instruction(inst, bt.get_code_int16(function_id, addr + 1)); + else if ((uint8_t) inst >= 0xa0) + return debug_instruction(inst, bt.get_code_int8(function_id, addr + 1)); + + return debug_instruction(inst); +} + +} \ No newline at end of file diff --git a/src/vm/instruction.hh b/src/vm/instruction.hh index 8657817..0cb9959 100644 --- a/src/vm/instruction.hh +++ b/src/vm/instruction.hh @@ -2,13 +2,102 @@ #define TYCHE_INSTRUCTION_HH #include +#include +#include + +#include "../bytecode/bytecode.hh" namespace tyche { enum class Instruction : uint8_t { + // stack operations + PushInt8 = 0xa0, + PushInt16 = 0xc0, + PushInt32 = 0xe0, + PushConstant8 = 0xa1, + PushConstant16 = 0xc1, + PushConstant32 = 0xe1, + PushZero = 0x00, + PushTrue = 0x01, + NewArray = 0x02, + NewTable = 0x03, + Pop = 0x04, + Duplicate = 0x05, + + // local variables + SetLocal8 = 0xa3, + SetLocal16 = 0xc3, + SetLocal32 = 0xe3, + GetLocal8 = 0xa4, + GetLocal16 = 0xc4, + GetLocal32 = 0xe4, + SetGlobal8 = 0xa5, + SetGlobal16 = 0xc5, + SetGlobal32 = 0xe5, + GetGlobal8 = 0xa6, + GetGlobal16 = 0xc6, + GetGlobal32 = 0xe6, + + // function operations + Call8 = 0xa7, + Call16 = 0xc7, + Call32 = 0xe7, + Return = 0x10, + ReturnNil = 0x11, + + // table and array operations + GetKeyValue = 0x16, + SetKeyValue = 0x17, + GetArrayItem = 0x18, + SetArrayItem = 0x19, + Append = 0x1a, + Next = 0x1b, + SetMetatable = 0x1c, + GetMetatable = 0x1d, + + // logical/arithmetic + Sum = 0x20, + Subtract = 0x21, + Multiply = 0x22, + Divide = 0x23, + DivideInt = 0x24, + Equals = 0x25, + NotEquals = 0x26, + LessThan = 0x27, + LessThanEq = 0x28, + GreaterThan = 0x29, + GreaterThanEq = 0x2a, + And = 0x2b, + Or = 0x2c, + Xor = 0x2d, + + // other value operations + Len = 0x30, + Type = 0x31, + Cast = 0x32, + Version = 0x33, + + // control flow + BranchIfZero8 = 0xa8, + BranchIfZero16 = 0xc8, + BranchIfZero32 = 0xe8, + BranchIfNotZero8 = 0xa9, + BranchIfNotZero16 = 0xc9, + BranchIfNotZero32 = 0xe9, + Jump8 = 0xaa, + Jump16 = 0xca, + Jump32 = 0xea, + + // external code + Compile = 0x38, + Assemble = 0x39, + Load = 0x3a, }; +std::pair debug_instruction(Instruction inst, int oper=0); +std::pair debug_instruction(Bytecode const& bt, uint32_t function_id, uint32_t addr); + } #endif //TYCHE_INSTRUCTION_HH