code #4
@@ -65,6 +65,11 @@ add_library(lib${PROJECT_NAME} SHARED
|
|||||||
src/bytecode/bytecode.hh
|
src/bytecode/bytecode.hh
|
||||||
src/bytecode/bytecodeprototype.hh
|
src/bytecode/bytecodeprototype.hh
|
||||||
src/common/overloaded.hh
|
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})
|
target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
|
||||||
@@ -73,21 +78,24 @@ target_compile_options(lib${PROJECT_NAME} PRIVATE ${warnings})
|
|||||||
# tests
|
# tests
|
||||||
#
|
#
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME}-bytecode-test src/bytecode/tests.cc
|
add_executable(${PROJECT_NAME}-bytecode-test src/bytecode/tests.cc)
|
||||||
src/bytecode/constant.hh)
|
|
||||||
target_link_libraries(${PROJECT_NAME}-bytecode-test lib${PROJECT_NAME} gtest_main)
|
target_link_libraries(${PROJECT_NAME}-bytecode-test lib${PROJECT_NAME} gtest_main)
|
||||||
add_test(NAME tyche_bytecode_test COMMAND ${PROJECT_NAME}-bytecode-test)
|
add_test(NAME tyche_bytecode_test COMMAND ${PROJECT_NAME}-bytecode-test)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}-vm-test src/vm/tests.cc)
|
||||||
|
target_link_libraries(${PROJECT_NAME}-vm-test lib${PROJECT_NAME} gtest_main)
|
||||||
|
add_test(NAME tyche_vm_test COMMAND ${PROJECT_NAME}-vm-test)
|
||||||
|
|
||||||
#
|
#
|
||||||
# check for leaks
|
# check for leaks
|
||||||
#
|
#
|
||||||
|
|
||||||
add_custom_target(leaks)
|
add_custom_target(leaks-vm-test)
|
||||||
add_custom_command(TARGET leaks
|
add_custom_command(TARGET leaks-vm-test
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
COMMENT "Check for leaks using valgrind."
|
COMMENT "Check for leaks using valgrind."
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
COMMAND valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --suppressions=${CMAKE_SOURCE_DIR}/valgrind.supp ./${PROJECT_NAME}
|
COMMAND valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --suppressions=${CMAKE_SOURCE_DIR}/valgrind.supp ./${PROJECT_NAME}-vm-test
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|||||||
6
TODO.md
6
TODO.md
@@ -20,9 +20,9 @@ After some additional development:
|
|||||||
## VM
|
## VM
|
||||||
|
|
||||||
- [ ] VM
|
- [ ] VM
|
||||||
- [ ] Code
|
- [x] Code
|
||||||
- [ ] Simple bytecode loader
|
- [x] Simple bytecode loader
|
||||||
- [ ] Output bytecode format
|
- [x] Output bytecode format
|
||||||
- [ ] Value object
|
- [ ] Value object
|
||||||
- [ ] Stack object
|
- [ ] Stack object
|
||||||
- [ ] Function context
|
- [ ] Function context
|
||||||
|
|||||||
137
doc/OPCODES
137
doc/OPCODES
@@ -3,77 +3,86 @@ Operations
|
|||||||
|
|
||||||
Operations take either 0 or 1 parameter. The ones that take a parameter, it can be either a int8, int16 or int32.
|
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 - no parameter
|
00 ~ 9F : no parameter
|
||||||
01 - int8
|
A0 ~ BF : int8 (1 byte)
|
||||||
10 - int16
|
C0 ~ DF : int16 (2 bytes)
|
||||||
11 - int32
|
E0 ~ FF : int32 (4 bytes)
|
||||||
|
|
||||||
Stack operations: (0x00~0x1f)
|
The operations of 1, 2 and 4 bytes are always interchangeable by adding/subtracting 0x20.
|
||||||
pushn [int] Push int
|
|
||||||
pushc [index] Push constant
|
|
||||||
pushf [function] Push function id
|
|
||||||
pushz Push zero (or false)
|
|
||||||
pusht Push true
|
|
||||||
newa Push (create) empty array
|
|
||||||
newt Push (create) empty table
|
|
||||||
pop
|
|
||||||
dup
|
|
||||||
|
|
||||||
Local variables: (0x20~0x2f)
|
,----------- no parameter
|
||||||
setl [int] Set stack top as indexed local variable
|
| ,-------- int8
|
||||||
getl [int] Get indexed local variable and place on stack
|
| | ,----- int16
|
||||||
setg [int] Set global variable
|
| | | ,-- int32
|
||||||
getg [int] Get global variable
|
NP I8 I16 I32 Opc Instruction Description
|
||||||
|
|
||||||
Function operations: (0x30~0x3f)
|
Stack operations:
|
||||||
call [n_pars] Enter function on stack toplevel (passing n next stack values as parameters)
|
a0 c0 e0 pushi [int] Push int
|
||||||
ret Leave a function (return value in stack)
|
a1 c1 e1 pushc [index] Push constant
|
||||||
retn Leave a function (return nil)
|
a2 c2 e2 pushf [function] Push function id
|
||||||
|
00 pushz Push zero (or false)
|
||||||
|
01 pusht Push true
|
||||||
|
02 newa Push (create) empty array
|
||||||
|
03 newt Push (create) empty table
|
||||||
|
04 pop
|
||||||
|
05 dup
|
||||||
|
|
||||||
Control flow: (0x40~0x4f)
|
Local variables:
|
||||||
bz [pc] Branch if zero
|
a3 c3 e3 setl [int] Set stack top as indexed local variable
|
||||||
bnz [pc] Branch if not zero
|
a4 c4 e4 getl [int] Get indexed local variable and place on stack
|
||||||
jmp [pc] Unconditional jump
|
a5 c5 e5 setg [int] Set global variable
|
||||||
|
a6 c6 e6 getg [int] Get global variable
|
||||||
|
|
||||||
|
Function operations:
|
||||||
|
a7 c7 e7 call [n_pars] Enter function on stack toplevel (passing n next stack values as parameters)
|
||||||
|
10 ret Leave a function (return value in stack)
|
||||||
|
11 retn Leave a function (return nil)
|
||||||
|
|
||||||
|
Table and array operations:
|
||||||
|
16 getkv Get table's value based on key (pull 1 value, push 1 value)
|
||||||
|
17 setkv Set table's key and value (pull 2 values from stack)
|
||||||
|
18 geta Get array's position value
|
||||||
|
19 seta Set array's position value (pull 2 values from stack)
|
||||||
|
1a appnd Add value to the end of array
|
||||||
|
1b next Push the next pair into the stack (for loops)
|
||||||
|
1c smt Set value metatable
|
||||||
|
1d mt Get value metatable
|
||||||
|
|
||||||
|
Logical/arithmetic:
|
||||||
|
20 sum Sum top 2 values in stack
|
||||||
|
21 sub Subtract top 2 values in stack
|
||||||
|
22 mul Multiply top 2 values in stack
|
||||||
|
23 div Float division
|
||||||
|
24 idiv Integer division
|
||||||
|
25 eq Equality
|
||||||
|
26 neq Inequality
|
||||||
|
27 lt Less than
|
||||||
|
28 lte Less than or equals
|
||||||
|
29 gt Greater than
|
||||||
|
2a gte Greater than or equals
|
||||||
|
2b and Bitwise AND
|
||||||
|
2c or Bitwise OR
|
||||||
|
2d xor Bitwise XOR
|
||||||
|
|
||||||
|
Other value operations:
|
||||||
|
30 len Get table, array or string size
|
||||||
|
31 type Get type from value at the top of the stack
|
||||||
|
b0 cast [type] Cast type to another type
|
||||||
|
32 ver Return VM version
|
||||||
|
|
||||||
|
External code:
|
||||||
|
38 cmpl Compile code to assembly
|
||||||
|
39 asmbl Assemble code to bytecode format
|
||||||
|
3a load Load bytecode as function (will place function on stack)
|
||||||
|
|
||||||
|
Control flow:
|
||||||
|
a8 c8 e8 bz [pc] Branch if zero
|
||||||
|
a9 c9 e9 bnz [pc] Branch if not zero
|
||||||
|
aa ca ea jmp [pc] Unconditional jump
|
||||||
* Jumps can only happen within the same function.
|
* Jumps can only happen within the same function.
|
||||||
|
|
||||||
Logical/arithmetic: (0x50~0x6f)
|
|
||||||
sum Sum top 2 values in stack
|
|
||||||
sub Subtract top 2 values in stack
|
|
||||||
mul Multiply top 2 values in stack
|
|
||||||
div Float division
|
|
||||||
idiv Integer division
|
|
||||||
eq Equality
|
|
||||||
neq Inequality
|
|
||||||
lt Less than
|
|
||||||
lte Less than or equals
|
|
||||||
gt Greater than
|
|
||||||
gte Greater than or equals
|
|
||||||
and Bitwise AND
|
|
||||||
or Bitwise OR
|
|
||||||
xor Bitwise XOR
|
|
||||||
|
|
||||||
Table and array operations: (0x70~07xf)
|
|
||||||
getkv Get table's value based on key (pull 1 value, push 1 value)
|
|
||||||
setkv Set table's key and value (pull 2 values from stack)
|
|
||||||
geta Get array's position value
|
|
||||||
seta Set array's position value (pull 2 values from stack)
|
|
||||||
appnd Add value to the end of array
|
|
||||||
next Push the next pair into the stack (for loops)
|
|
||||||
smt Set value metatable
|
|
||||||
mt Get value metatable
|
|
||||||
|
|
||||||
Other value operations: (0x80~0x8f)
|
|
||||||
len Get table, array or string size
|
|
||||||
type Get type from value at the top of the stack
|
|
||||||
cast [type] Cast type to another type
|
|
||||||
ver Return VM version
|
|
||||||
|
|
||||||
External code: (0x90~0x9f)
|
|
||||||
cmpl Compile code to assembly
|
|
||||||
asmbl Assemble code to bytecode format
|
|
||||||
load Load bytecode as function (will place function on stack)
|
|
||||||
|
|
||||||
Error handling: (0xa0~0xaf)
|
Error handling: (0xa0~0xaf)
|
||||||
???
|
???
|
||||||
|
|||||||
@@ -7,13 +7,10 @@ namespace tyche {
|
|||||||
|
|
||||||
void ByteArray::set_byte(uint32_t addr, uint8_t byte)
|
void ByteArray::set_byte(uint32_t addr, uint8_t byte)
|
||||||
{
|
{
|
||||||
try {
|
if (data_.size() < (addr + 1))
|
||||||
data_.at(addr) = byte;
|
|
||||||
} catch (std::out_of_range&) {
|
|
||||||
data_.resize(addr + 1, 0);
|
data_.resize(addr + 1, 0);
|
||||||
data_.at(addr) = byte;
|
data_.at(addr) = byte;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ByteArray::set_int8(uint32_t addr, int8_t value)
|
void ByteArray::set_int8(uint32_t addr, int8_t value)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Bytecode::Bytecode(ByteArray ba)
|
|||||||
uint32_t code_start = byte_array_.get_uint32(TOC_START + (3 * TOC_RECORD_SZ));
|
uint32_t code_start = byte_array_.get_uint32(TOC_START + (3 * TOC_RECORD_SZ));
|
||||||
for (uint32_t i = 0; i < cache_.n_functions; ++i) {
|
for (uint32_t i = 0; i < cache_.n_functions; ++i) {
|
||||||
cache_.function_addr.emplace_back(code_start + byte_array_.get_uint32(cache_.functions_idx_addr + (i * FUNCTION_RECORD_SZ)));
|
cache_.function_addr.emplace_back(code_start + byte_array_.get_uint32(cache_.functions_idx_addr + (i * FUNCTION_RECORD_SZ)));
|
||||||
cache_.function_sz.emplace_back(code_start + byte_array_.get_uint32(cache_.functions_idx_addr + (i * FUNCTION_RECORD_SZ) + 8));
|
cache_.function_sz.emplace_back(byte_array_.get_uint32(cache_.functions_idx_addr + (i * FUNCTION_RECORD_SZ) + 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace tyche {
|
|||||||
|
|
||||||
class Bytecode {
|
class Bytecode {
|
||||||
public:
|
public:
|
||||||
|
Bytecode() = default;
|
||||||
explicit Bytecode(ByteArray ba);
|
explicit Bytecode(ByteArray ba);
|
||||||
|
|
||||||
[[nodiscard]] uint32_t n_constants() const;
|
[[nodiscard]] uint32_t n_constants() const;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "constant.hh"
|
#include "constant.hh"
|
||||||
|
#include "bytearray.hh"
|
||||||
|
|
||||||
namespace tyche {
|
namespace tyche {
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "gmock/gmock.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -141,6 +140,8 @@ TEST(Bytecode, Parsing)
|
|||||||
|
|
||||||
ASSERT_EQ(bc.n_constants(), 2);
|
ASSERT_EQ(bc.n_constants(), 2);
|
||||||
ASSERT_EQ(bc.n_functions(), 2);
|
ASSERT_EQ(bc.n_functions(), 2);
|
||||||
|
ASSERT_EQ(bc.get_function_sz(0), 2);
|
||||||
|
ASSERT_EQ(bc.get_function_sz(1), 1);
|
||||||
|
|
||||||
ASSERT_FLOAT_EQ(std::get<float>(bc.get_constant(0)), 3.14f);
|
ASSERT_FLOAT_EQ(std::get<float>(bc.get_constant(0)), 3.14f);
|
||||||
ASSERT_EQ(std::get<std::string>(bc.get_constant(1)), "HELLO");
|
ASSERT_EQ(std::get<std::string>(bc.get_constant(1)), "HELLO");
|
||||||
|
|||||||
43
src/vm/code.cc
Normal file
43
src/vm/code.cc
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#include "code.hh"
|
||||||
|
#include "../common/overloaded.hh"
|
||||||
|
#include "instruction.hh"
|
||||||
|
|
||||||
|
namespace tyche {
|
||||||
|
|
||||||
|
void Code::import_bytecode(ByteArray incoming)
|
||||||
|
{
|
||||||
|
Bytecode bc(std::move(incoming));
|
||||||
|
// TODO - adjust function calls, constants
|
||||||
|
|
||||||
|
bytecode_ = std::move(bc);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
20
src/vm/code.hh
Normal file
20
src/vm/code.hh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef TYCHE_CODE_HH
|
||||||
|
#define TYCHE_CODE_HH
|
||||||
|
|
||||||
|
#include "../bytecode/bytecode.hh"
|
||||||
|
|
||||||
|
namespace tyche {
|
||||||
|
|
||||||
|
class Code {
|
||||||
|
public:
|
||||||
|
void import_bytecode(ByteArray incoming);
|
||||||
|
|
||||||
|
[[nodiscard]] std::string disassemble() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Bytecode bytecode_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // tyche
|
||||||
|
|
||||||
|
#endif //TYCHE_CODE_HH
|
||||||
127
src/vm/instruction.cc
Normal file
127
src/vm/instruction.cc
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#include "instruction.hh"
|
||||||
|
|
||||||
|
namespace tyche {
|
||||||
|
|
||||||
|
std::pair<std::string, size_t> 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<std::string, size_t> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
103
src/vm/instruction.hh
Normal file
103
src/vm/instruction.hh
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
#ifndef TYCHE_INSTRUCTION_HH
|
||||||
|
#define TYCHE_INSTRUCTION_HH
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#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<std::string, size_t> debug_instruction(Instruction inst, int oper=0);
|
||||||
|
std::pair<std::string, size_t> debug_instruction(Bytecode const& bt, uint32_t function_id, uint32_t addr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //TYCHE_INSTRUCTION_HH
|
||||||
35
src/vm/tests.cc
Normal file
35
src/vm/tests.cc
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "../bytecode/bytecodeprototype.hh"
|
||||||
|
#include "../bytecode/bytearray.hh"
|
||||||
|
#include "../bytecode/bytecode.hh"
|
||||||
|
#include "code.hh"
|
||||||
|
|
||||||
|
using namespace tyche;
|
||||||
|
|
||||||
|
TEST(Code, ImportSingleAndDebug)
|
||||||
|
{
|
||||||
|
BytecodePrototype bp;
|
||||||
|
|
||||||
|
bp.constants.emplace_back(3.14f);
|
||||||
|
bp.constants.emplace_back("HELLO");
|
||||||
|
|
||||||
|
bp.functions.emplace_back(0, 0);
|
||||||
|
bp.functions.at(0).code.append_byte(0xa0); // pushi
|
||||||
|
bp.functions.at(0).code.append_int8(42);
|
||||||
|
|
||||||
|
bp.functions.emplace_back(2, 1);
|
||||||
|
bp.functions.at(1).code.append_byte(0x1a); // appnd
|
||||||
|
|
||||||
|
ByteArray ba = Bytecode::generate(bp);
|
||||||
|
|
||||||
|
Code code;
|
||||||
|
code.import_bytecode(std::move(ba));
|
||||||
|
printf("%s\n", code.disassemble().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user