├── Programs ├── Functions │ ├── Functions.bce │ ├── Functions.pseudo │ └── Functions.bca ├── HelloWorld │ ├── HelloWorld.bce │ └── HelloWorld.bca └── DynamicAlloc │ └── DynAlloc.bca ├── source ├── Opcode.cpp ├── AtomicTypes.h ├── SymbolTable.h ├── Opcode.h ├── AssemblyCompiler.h ├── VirtualMachine.h ├── main.cpp ├── SymbolTable.cpp ├── AssemblyCompiler.cpp └── VirtualMachine.cpp ├── .gitignore ├── LICENSE.md └── README.md /Programs/Functions/Functions.bce: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Illation/BytecodeVM/HEAD/Programs/Functions/Functions.bce -------------------------------------------------------------------------------- /Programs/HelloWorld/HelloWorld.bce: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Illation/BytecodeVM/HEAD/Programs/HelloWorld/HelloWorld.bce -------------------------------------------------------------------------------- /source/Opcode.cpp: -------------------------------------------------------------------------------- 1 | #include "Opcode.h" 2 | 3 | std::string GetOpString(Opcode code) 4 | { 5 | std::string key = "invalid code"; 6 | for (auto &i : OpcodeNames) 7 | { 8 | if (i.second == code) 9 | { 10 | key = i.first; 11 | break; // to stop searching 12 | } 13 | } 14 | return key; 15 | } 16 | -------------------------------------------------------------------------------- /Programs/DynamicAlloc/DynAlloc.bca: -------------------------------------------------------------------------------- 1 | LITERAL 15 2 | ALLOC 3 | LITERAL #a 4 | STORE 5 | 6 | LITERAL 8 7 | ALLOC 8 | LITERAL #b 9 | STORE 10 | 11 | LITERAL #a 12 | LOAD 13 | FREE 14 | 15 | LITERAL 5 16 | ALLOC 17 | LITERAL #a 18 | STORE 19 | 20 | LITERAL 7 21 | ALLOC 22 | LITERAL #c 23 | STORE 24 | 25 | LITERAL #b 26 | LOAD 27 | FREE 28 | 29 | LITERAL #a 30 | LOAD 31 | FREE 32 | 33 | LITERAL #c 34 | LOAD 35 | FREE -------------------------------------------------------------------------------- /source/AtomicTypes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef WORD_LITTLE_ENDIAN 4 | #define WORD_LITTLE_ENDIAN 5 | #endif 6 | #define WORD_BIG_ENDIAN false 7 | 8 | typedef std::int8_t int8; 9 | typedef std::int16_t int16; 10 | typedef std::int32_t int32; 11 | typedef std::int64_t int64; 12 | 13 | typedef std::uint8_t uint8; 14 | typedef std::uint16_t uint16; 15 | typedef std::uint32_t uint32; 16 | typedef std::uint64_t uint64; -------------------------------------------------------------------------------- /Programs/Functions/Functions.pseudo: -------------------------------------------------------------------------------- 1 | <<("Hello World!") 2 | < 3 | #include 4 | 5 | #include "AtomicTypes.h" 6 | 7 | enum class SymbolType : uint8 8 | { 9 | FUNCTION, 10 | LABEL, 11 | STATIC, 12 | LOCAL, 13 | ARG 14 | }; 15 | 16 | class SymbolTable 17 | { 18 | public: 19 | SymbolTable(uint32 stackSize); 20 | 21 | bool AddFunction(const std::string &name, std::string &arguments); 22 | bool AddLabel(const std::string &name); 23 | bool AddVariable(const std::string &name, bool isArg = false); 24 | 25 | void SetParsingStatic(bool staticSection = true, std::string functionName = ""); 26 | void AllocateStatic(); 27 | 28 | bool HasSymbol(const std::string &name) const; 29 | uint32 GetValue(const std::string &name) const; 30 | uint32 GetFunctionArgCount(const std::string &name) const; 31 | uint32 GetFunctionVarCount(const std::string &name) const; 32 | uint32 GetStaticVarCount()const; 33 | 34 | uint32 m_NumInstructions = 0; 35 | 36 | private: 37 | uint32 m_StackSize; 38 | 39 | struct Symbol 40 | { 41 | std::string name; 42 | uint32 value = 0; 43 | SymbolType type; 44 | }; 45 | std::vector m_Table; 46 | 47 | struct Func 48 | { 49 | std::string name; 50 | uint32 numArg = 0; 51 | uint32 numLoc = 0; 52 | }; 53 | std::vector m_FuncTable; 54 | Func m_CurrentFunc; 55 | 56 | bool m_ParsingStatic = true; 57 | 58 | //Base addresses for static / automatic memory allocation 59 | uint32 m_StaticCounter = 0; 60 | }; 61 | -------------------------------------------------------------------------------- /source/Opcode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | enum class Opcode : char 7 | { 8 | //Memory Manipulation 9 | LITERAL, 10 | LITERAL_ARRAY, 11 | 12 | LOAD, 13 | STORE, 14 | LOAD_LCL, 15 | STORE_LCL, 16 | LOAD_ARG, 17 | 18 | ALLOC, 19 | FREE, 20 | 21 | //Arithmetic / Logic 22 | ADD, 23 | SUB, 24 | 25 | LESS, 26 | GREATER, 27 | NOT, 28 | EQUALS, 29 | 30 | //Flow Control 31 | JMP, 32 | JMP_IF, 33 | 34 | CALL, 35 | RETURN, 36 | 37 | PRINT, 38 | PRINT_INT, 39 | PRINT_ENDL 40 | }; 41 | static std::map OpcodeNames 42 | { 43 | {"LITERAL", Opcode::LITERAL}, 44 | {"LITERAL_ARRAY", Opcode::LITERAL_ARRAY}, 45 | 46 | {"LOAD", Opcode::LOAD}, 47 | {"STORE", Opcode::STORE}, 48 | {"LOAD_LCL", Opcode::LOAD_LCL}, 49 | {"STORE_LCL", Opcode::STORE_LCL}, 50 | {"LOAD_ARG", Opcode::LOAD_ARG}, 51 | 52 | {"ALLOC", Opcode::ALLOC}, 53 | {"FREE", Opcode::FREE}, 54 | 55 | {"ADD", Opcode::ADD}, 56 | {"SUB", Opcode::SUB}, 57 | 58 | {"LESS", Opcode::LESS}, 59 | {"GREATER", Opcode::GREATER}, 60 | {"NOT", Opcode::NOT}, 61 | {"EQUALS", Opcode::EQUALS}, 62 | 63 | {"JMP", Opcode::JMP}, 64 | {"JMP_IF", Opcode::JMP_IF}, 65 | 66 | {"CALL", Opcode::CALL}, 67 | {"RETURN", Opcode::RETURN}, 68 | 69 | {"PRINT", Opcode::PRINT}, 70 | {"PRINT_INT", Opcode::PRINT_INT}, 71 | {"PRINT_ENDL", Opcode::PRINT_ENDL} 72 | }; 73 | std::string GetOpString(Opcode code); 74 | -------------------------------------------------------------------------------- /source/AssemblyCompiler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "AtomicTypes.h" 7 | 8 | //Forward declaration 9 | class SymbolTable; 10 | 11 | class AssemblyCompiler 12 | { 13 | public: 14 | enum class CompState 15 | { 16 | INIT, 17 | SOURCE, 18 | COMPILED, 19 | FAILED 20 | }; 21 | 22 | public: 23 | AssemblyCompiler(); 24 | ~AssemblyCompiler(); 25 | 26 | void SetSource(std::vector lines); 27 | bool LoadSource(std::string filename); 28 | 29 | bool Compile(); 30 | 31 | CompState GetState(){return m_State;} 32 | bool Save(std::string filename); 33 | std::vector GetBytecode(); 34 | 35 | private: 36 | bool BuildSymbolTable(); 37 | bool CompileInstructions(); 38 | bool CompileHeader(); 39 | 40 | bool TokenizeLine(std::string line, std::string &opname, std::string &arguments); 41 | bool IsValidOpname(std::string opname, uint32 line); 42 | 43 | void CheckVar(std::string &arguments); 44 | 45 | bool HasValidArgs(std::string arguments, uint32 line, std::string opname); 46 | bool ParseLiteral(int32 &out, std::string &arguments); 47 | void WriteInt(int32 value); 48 | void WriteInt(int32 value, std::vector &target); 49 | 50 | void PrintAbort(uint32 line); 51 | 52 | private: 53 | CompState m_State = CompState::INIT; 54 | 55 | std::vector m_Lines; 56 | std::vector m_Bytecode; 57 | 58 | SymbolTable* m_pSymbolTable = nullptr; 59 | 60 | uint32 m_HeaderSize = 0; 61 | uint32 m_StackSize = 1048576; 62 | }; 63 | -------------------------------------------------------------------------------- /source/VirtualMachine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "AtomicTypes.h" 7 | 8 | #define VM_DEBUG_HEAP 9 | 10 | class VirtualMachine 11 | { 12 | public: 13 | VirtualMachine(); 14 | ~VirtualMachine(); 15 | 16 | bool LoadProgram(std::string filename); 17 | void SetProgram(std::vector bytecode); 18 | 19 | void Interpret(); 20 | 21 | private: 22 | //Stack Manipulation 23 | void Push(int32 value); 24 | int32 Pop(); 25 | 26 | //Manipulate memory with 4 bytes 27 | template 28 | T Unpack(uint32 address); 29 | template 30 | T Unpack(uint32 address, std::vector data); 31 | template 32 | void Pack(uint32 address, T value); 33 | 34 | void PrintHeap(bool baseOffset = false); 35 | 36 | private: 37 | //Static Sizes 38 | static const uint32 MAX_RAM = 536870912; //500 MB 39 | uint32 m_StackSize; 40 | uint32 m_NumInstructions = 0; 41 | uint32 m_StaticBase = 0; 42 | uint32 m_HeapBase = 0; 43 | 44 | //State 45 | bool ProgramLoaded = false; 46 | 47 | //RAM 48 | uint8* m_RAM; 49 | 50 | //Registers 51 | uint32 m_ProgramCounter = 0; 52 | 53 | //Stack frame, potentially static variables if so desired 54 | //*********** 55 | int32 m_StackPointer = -4; 56 | uint32 m_LCL = 0; //Current local base address 57 | uint32 m_ARG = 0; //Current argument base address 58 | uint32 m_RTN = 0; //Current return address 59 | uint32 m_THIS = 0; //Pointer to current object 60 | 61 | //Stack Frame Layout for function with n arguments and k locals 62 | /* 63 | ARG-> arg 0 ****Arguments 64 | arg 1 <--all arguments are items from previous functions working stack 65 | ... 66 | arg n-1 67 | LCL-16->saved RTN ****Stack frame begin 68 | saved LCL 69 | saved ARG 70 | saved THIS <--for object support 71 | LCL-> loc 0 ****Local variables 72 | loc 1 73 | loc ... 74 | loc k-1 75 | ws 0 ****Working Stack 76 | SP-> ws 1 77 | */ 78 | 79 | //Dynamic Memory Allocation 80 | //*************** 81 | uint32 m_FirstSegmentPtr = 0; 82 | }; 83 | -------------------------------------------------------------------------------- /Programs/Functions/Functions.bca: -------------------------------------------------------------------------------- 1 | //<<("Hello World!") <= ending.length()) 14 | { 15 | return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); 16 | } 17 | else 18 | { 19 | return false; 20 | } 21 | } 22 | 23 | int main(int argc, char** argv) 24 | { 25 | if(argc != 3) 26 | { 27 | std::cout << "usage: [operation] [filename]" << std::endl; 28 | return 1; 29 | } 30 | std::string filename = argv[2]; 31 | if(std::string(argv[1]) == "run") 32 | { 33 | std::cout << "running " << filename << std::endl; 34 | std::cout << std::endl; 35 | 36 | //Create a new VM / interpreter 37 | VirtualMachine* pVM = new VirtualMachine(); 38 | pVM->LoadProgram(filename); 39 | pVM->Interpret(); 40 | delete pVM; 41 | pVM = nullptr; 42 | 43 | std::cout << std::endl; 44 | std::cout << "=======================" << std::endl; 45 | std::cout << "script execution ended!" << std::endl; 46 | } 47 | else if(std::string(argv[1]) == "compile") 48 | { 49 | std::cout << "compiling " << filename << std::endl; 50 | std::cout << std::endl; 51 | 52 | AssemblyCompiler* pCmp = new AssemblyCompiler(); 53 | pCmp->LoadSource(filename); 54 | 55 | pCmp->Compile(); 56 | 57 | std::string outname; 58 | if(hasEnding(filename, AssemblyExtension)) 59 | { 60 | outname = filename.substr(0, filename.size() - AssemblyExtension.size()) + ExecutableExtension; 61 | } 62 | else outname = filename + ExecutableExtension; 63 | pCmp->Save(outname); 64 | 65 | std::cout << std::endl; 66 | std::cout << "=======================" << std::endl; 67 | if(!(pCmp->GetState() == AssemblyCompiler::CompState::COMPILED)) 68 | { 69 | std::cout << "script compilation failed!" << std::endl; 70 | delete pCmp; 71 | pCmp = nullptr; 72 | return 3; 73 | } 74 | else 75 | { 76 | std::cout << "script compilation succeded! >> output file: " << outname << std::endl; 77 | } 78 | 79 | delete pCmp; 80 | pCmp = nullptr; 81 | } 82 | else if(std::string(argv[1]) == "cRun") 83 | { 84 | std::cout << "compiling " << filename << std::endl; 85 | std::cout << std::endl; 86 | 87 | AssemblyCompiler* pCmp = new AssemblyCompiler(); 88 | pCmp->LoadSource(filename); 89 | 90 | pCmp->Compile(); 91 | 92 | std::cout << std::endl; 93 | std::cout << "=======================" << std::endl; 94 | if(!(pCmp->GetState() == AssemblyCompiler::CompState::COMPILED)) 95 | { 96 | std::cout << "script compilation failed!" << std::endl; 97 | delete pCmp; 98 | pCmp = nullptr; 99 | return 3; 100 | } 101 | std::cout << "running " << filename << std::endl; 102 | std::cout << std::endl; 103 | 104 | VirtualMachine* pVM = new VirtualMachine(); 105 | 106 | pVM->SetProgram(pCmp->GetBytecode()); 107 | 108 | delete pCmp; 109 | pCmp = nullptr; 110 | 111 | pVM->Interpret(); 112 | 113 | delete pVM; 114 | pVM = nullptr; 115 | 116 | std::cout << std::endl; 117 | std::cout << "=======================" << std::endl; 118 | 119 | } 120 | else 121 | { 122 | std::cout << "OPERATION NOT RECOGNIZED!" << std::endl; 123 | std::cout << "usage: [operation] [filename]" << std::endl; 124 | std::cout << "operations: " << std::endl; 125 | std::cout << "\trun >> Run virtual machine with executable bytecode" << std::endl; 126 | std::cout << "\tcompile >> compile assembly code to executable bytecode" << std::endl; 127 | return 2; 128 | } 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /source/SymbolTable.cpp: -------------------------------------------------------------------------------- 1 | #include "SymbolTable.h" 2 | 3 | #include 4 | #include 5 | 6 | SymbolTable::SymbolTable(uint32 stackSize) 7 | :m_StackSize(stackSize) 8 | { 9 | m_CurrentFunc = SymbolTable::Func(); 10 | } 11 | 12 | bool SymbolTable::AddFunction(const std::string &name, std::string &arguments) 13 | { 14 | if(HasSymbol(name)) 15 | return false; 16 | 17 | //Add Symbol 18 | auto sbl = SymbolTable::Symbol(); 19 | sbl.name = name; 20 | sbl.value = m_StackSize + m_NumInstructions; 21 | sbl.type = SymbolType::FUNCTION; 22 | m_Table.push_back(sbl); 23 | 24 | //The following variables are not static anymore 25 | SetParsingStatic(false, name); 26 | m_NumInstructions += 8;//First two instructions are int32 numArgs and int32 numLoc 27 | 28 | //Also add function arguments (parameters) 29 | while(!arguments.empty()) 30 | { 31 | std::string arg; 32 | std::size_t nDelim = arguments.find(' ', 1); 33 | if(nDelim == std::string::npos) 34 | { 35 | arg = arguments; 36 | arguments = std::string(); 37 | } 38 | else 39 | { 40 | arg = arguments.substr(0, nDelim); 41 | arguments = arguments.substr(nDelim+1); 42 | } 43 | if(!(AddVariable(arg, true))) return false; 44 | } 45 | return true; 46 | } 47 | 48 | bool SymbolTable::AddLabel(const std::string &name) 49 | { 50 | if(HasSymbol(name)) 51 | return false; 52 | auto sbl = SymbolTable::Symbol(); 53 | sbl.name = name; 54 | sbl.value = m_StackSize + m_NumInstructions; 55 | sbl.type = SymbolType::LABEL; 56 | m_Table.push_back(sbl); 57 | return true; 58 | } 59 | 60 | bool SymbolTable::AddVariable(const std::string &name, bool isArg) 61 | { 62 | if(HasSymbol(name)) 63 | return false; 64 | auto sbl = SymbolTable::Symbol(); 65 | sbl.name = name; 66 | 67 | if(isArg) 68 | { 69 | if(m_ParsingStatic) 70 | return false;//Static segments don't have arguments 71 | sbl.type = SymbolType::ARG; 72 | } 73 | else if(m_ParsingStatic) sbl.type = SymbolType::STATIC; 74 | else sbl.type = SymbolType::LOCAL; 75 | 76 | switch(sbl.type) 77 | { 78 | case SymbolType::STATIC: 79 | sbl.value = m_StaticCounter; 80 | m_StaticCounter += 4; //Multitype support should use variable size here 81 | break; 82 | case SymbolType::LOCAL: 83 | sbl.value = m_CurrentFunc.numLoc; 84 | m_CurrentFunc.numLoc += 4; 85 | break; 86 | case SymbolType::ARG: 87 | sbl.value = m_CurrentFunc.numArg; 88 | m_CurrentFunc.numArg += 4; 89 | break; 90 | default: 91 | break; 92 | } 93 | m_Table.push_back(sbl); 94 | return true; 95 | } 96 | 97 | void SymbolTable::SetParsingStatic(bool staticSection /*= true*/, std::string functionName) 98 | { 99 | m_ParsingStatic = staticSection; 100 | //Prepare for next function 101 | if (!m_CurrentFunc.name.empty()) 102 | { 103 | m_FuncTable.push_back(m_CurrentFunc); 104 | std::cout << "[SYMBOL] function: " << m_CurrentFunc.name << "; args: " << m_CurrentFunc.numArg << "; vars: " << m_CurrentFunc.numLoc << std::endl; 105 | } 106 | m_CurrentFunc = SymbolTable::Func(); 107 | m_CurrentFunc.name = functionName; 108 | } 109 | 110 | void SymbolTable::AllocateStatic() 111 | { 112 | std::cout << "[SYMBOL] Instruction count: " << m_NumInstructions << "; Symbols: " << std::endl; 113 | uint32 staticBase = m_StackSize + m_NumInstructions; 114 | for(auto & sbl : m_Table) 115 | { 116 | if(sbl.type == SymbolType::STATIC) 117 | { 118 | sbl.value += staticBase; 119 | } 120 | std::cout << "[SYMBOL] name: " << sbl.name << "; value: " << sbl.value << std::endl; 121 | if(sbl.type == SymbolType::FUNCTION) 122 | { 123 | std::cout << "[SYMBOL] instruction pointer: " << sbl.value - m_StackSize << std::endl; 124 | } 125 | } 126 | } 127 | 128 | bool SymbolTable::HasSymbol(const std::string &name) const 129 | { 130 | for(auto sbl : m_Table) 131 | { 132 | if(sbl.name == name) return true; 133 | } 134 | return false; 135 | } 136 | 137 | uint32 SymbolTable::GetValue(const std::string &name) const 138 | { 139 | for(auto sbl : m_Table) 140 | { 141 | if(sbl.name == name) return sbl.value; 142 | } 143 | std::cerr << "[SYMBOL] Could not find Symbol " << name << std::endl; 144 | return 0; 145 | } 146 | 147 | uint32 SymbolTable::GetFunctionArgCount(const std::string &name) const 148 | { 149 | for(auto func : m_FuncTable) 150 | { 151 | if(func.name == name) return func.numArg; 152 | } 153 | std::cerr << "[SYMBOL] Could not find Function " << name << std::endl; 154 | return 0; 155 | } 156 | 157 | uint32 SymbolTable::GetFunctionVarCount(const std::string &name) const 158 | { 159 | for(auto func : m_FuncTable) 160 | { 161 | if(func.name == name) return func.numLoc; 162 | } 163 | std::cerr << "[SYMBOL] Could not find Function " << name << std::endl; 164 | return 0; 165 | } 166 | 167 | uint32 SymbolTable::GetStaticVarCount() const 168 | { 169 | return m_StaticCounter; 170 | } -------------------------------------------------------------------------------- /source/AssemblyCompiler.cpp: -------------------------------------------------------------------------------- 1 | #include "AssemblyCompiler.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Opcode.h" 11 | #include "SymbolTable.h" 12 | 13 | //Constructor Destructor 14 | AssemblyCompiler::AssemblyCompiler() = default; 15 | 16 | AssemblyCompiler::~AssemblyCompiler() 17 | { 18 | m_Lines.clear(); 19 | m_Bytecode.clear(); 20 | 21 | delete m_pSymbolTable; 22 | m_pSymbolTable = nullptr; 23 | } 24 | 25 | 26 | //Input 27 | void AssemblyCompiler::SetSource(std::vector lines) 28 | { 29 | m_Lines = lines; 30 | m_State = CompState::SOURCE; 31 | } 32 | bool AssemblyCompiler::LoadSource(std::string filename) 33 | { 34 | std::ifstream file(filename); 35 | if(!file.good()) 36 | { 37 | std::cerr << "[ASM CMP] Input filestream could not be created" << std::endl; 38 | m_Lines.clear(); 39 | m_State = CompState::INIT; 40 | return false; 41 | } 42 | 43 | std::string line; 44 | while (std::getline(file, line)) 45 | { 46 | m_Lines.push_back(line); 47 | } 48 | 49 | if(m_Lines.empty()) 50 | { 51 | std::cerr << "[ASM CMP] No assembly lines loaded" << std::endl; 52 | m_Lines.clear(); 53 | m_State = CompState::INIT; 54 | return false; 55 | } 56 | std::cout << "[ASM CMP] Assembly file loaded!" << std::endl; 57 | m_State = CompState::SOURCE; 58 | return true; 59 | } 60 | 61 | 62 | //Compile 63 | bool AssemblyCompiler::Compile() 64 | { 65 | //Check valid input 66 | if(m_State == CompState::INIT) 67 | { 68 | std::cerr << "[ASM CMP] No assembly provided!" << std::endl; 69 | return false; 70 | } 71 | if(m_State == CompState::FAILED) 72 | { 73 | std::cerr << "[ASM CMP] Provide new source before recompiling!" << std::endl; 74 | return false; 75 | } 76 | if(m_State == CompState::COMPILED) 77 | { 78 | std::cerr << "[ASM CMP] No new compilation required!" << std::endl; 79 | return false; 80 | } 81 | 82 | if(!m_pSymbolTable) 83 | m_pSymbolTable = new SymbolTable(m_StackSize); 84 | else 85 | std::cerr << "[ASM CMP] Symbol table already created!" << std::endl; 86 | 87 | //Do compilation 88 | if(!BuildSymbolTable())return false; 89 | if(!CompileInstructions())return false; 90 | if(!CompileHeader())return false; 91 | 92 | std::cout << "[ASM CMP] Compilation Complete, no errors detected!" << std::endl; 93 | m_State = CompState::COMPILED; 94 | return true; 95 | } 96 | 97 | bool AssemblyCompiler::BuildSymbolTable() 98 | { 99 | for(uint32 line = 0; line < m_Lines.size(); ++line) 100 | { 101 | std::string opname; 102 | std::string arguments; 103 | if(!TokenizeLine(m_Lines[line], opname, arguments))continue; 104 | 105 | //Jump labels 106 | if(opname[0] == '@') 107 | { 108 | if(!m_pSymbolTable->AddLabel(opname)) 109 | { 110 | std::cerr << "[ASM CMP] " << line << ": label " << opname << " already defined!" << std::endl; 111 | return false; 112 | } 113 | continue; 114 | } 115 | if(opname[0] == '$') 116 | { 117 | if(!m_pSymbolTable->AddFunction(opname, arguments)) 118 | { 119 | std::cerr << "[ASM CMP] " << line << ": error adding function: " << opname << std::endl; 120 | return false; 121 | } 122 | continue; 123 | } 124 | 125 | if(!IsValidOpname(opname, line))return false; 126 | Opcode code = OpcodeNames[opname]; 127 | 128 | switch(code) 129 | { 130 | case Opcode::LITERAL: 131 | { 132 | m_pSymbolTable->m_NumInstructions += 5; 133 | if(!HasValidArgs(arguments, line, opname))return false; 134 | CheckVar(arguments); 135 | } 136 | break; 137 | 138 | case Opcode::LITERAL_ARRAY: 139 | { 140 | if(!HasValidArgs(arguments, line, opname))return false; 141 | 142 | if(arguments[0] == '\"') 143 | { 144 | if(arguments.size()==1) 145 | { 146 | std::cerr << "[ASM CMP] " << line << ", " << opname << ": Incorrect argument size!" << std::endl; 147 | PrintAbort(line); 148 | return false; 149 | } 150 | uint32 j = 1; 151 | while(arguments[j] != '\"') 152 | { 153 | m_pSymbolTable->m_NumInstructions+=4; 154 | ++j; 155 | } 156 | } 157 | else 158 | { 159 | while(!arguments.empty()) 160 | { 161 | m_pSymbolTable->m_NumInstructions+=4; 162 | CheckVar(arguments); 163 | } 164 | } 165 | m_pSymbolTable->m_NumInstructions+=5; 166 | } 167 | break; 168 | 169 | default: 170 | m_pSymbolTable->m_NumInstructions++; 171 | break; 172 | } 173 | } 174 | 175 | m_pSymbolTable->SetParsingStatic(); 176 | m_pSymbolTable->AllocateStatic(); 177 | 178 | return true; 179 | } 180 | 181 | bool AssemblyCompiler::CompileInstructions() 182 | { 183 | for(uint32 line = 0; line < m_Lines.size(); ++line) 184 | { 185 | std::string opname; 186 | std::string arguments; 187 | if(!TokenizeLine(m_Lines[line], opname, arguments))continue; 188 | if(opname[0] == '@') continue; //Skip labels 189 | if(opname[0] == '$') //Write num arguments and variables for function 190 | { 191 | WriteInt(m_pSymbolTable->GetFunctionArgCount(opname)); 192 | WriteInt(m_pSymbolTable->GetFunctionVarCount(opname)); 193 | continue; 194 | } 195 | 196 | if(!IsValidOpname(opname, line))return false; 197 | 198 | Opcode code = OpcodeNames[opname]; 199 | 200 | switch(code) 201 | { 202 | case Opcode::LITERAL: 203 | { 204 | if(!HasValidArgs(arguments, line, opname))return false; 205 | int32 parsed; 206 | if(ParseLiteral(parsed, arguments)) 207 | { 208 | m_Bytecode.push_back(static_cast(code)); 209 | WriteInt(parsed); 210 | } 211 | else 212 | { 213 | PrintAbort(line); 214 | return false; 215 | } 216 | } 217 | break; 218 | 219 | case Opcode::LITERAL_ARRAY: 220 | { 221 | if(!HasValidArgs(arguments, line, opname))return false; 222 | m_Bytecode.push_back(static_cast(code)); 223 | 224 | std::vector arr; 225 | if(arguments[0] == '\"') 226 | { 227 | if(arguments.size()==1) 228 | { 229 | std::cerr << "[ASM CMP] " << line << ", " << opname << ": Incorrect argument size!" << std::endl; 230 | PrintAbort(line); 231 | return false; 232 | } 233 | uint32 j = 1; 234 | while(arguments[j] != '\"') 235 | { 236 | if(j >= arguments.size()) 237 | { 238 | std::cerr << "[ASM CMP] " << line << ", " << opname << R"(: Expected ' " ' !)" << std::endl; 239 | PrintAbort(line); 240 | return false; 241 | } 242 | arr.push_back(int32(arguments[j])); 243 | 244 | ++j; 245 | } 246 | } 247 | else 248 | { 249 | while(!arguments.empty()) 250 | { 251 | int32 parsed; 252 | if(ParseLiteral(parsed, arguments)) 253 | { 254 | WriteInt(parsed); 255 | arr.push_back(parsed); 256 | } 257 | else 258 | { 259 | PrintAbort(line); 260 | return false; 261 | } 262 | } 263 | } 264 | WriteInt(static_cast(arr.size())); 265 | for(auto value : arr) WriteInt(value); 266 | } 267 | break; 268 | 269 | default: 270 | m_Bytecode.push_back(static_cast(code)); 271 | break; 272 | } 273 | } 274 | return true; 275 | } 276 | 277 | bool AssemblyCompiler::CompileHeader() 278 | { 279 | std::vector header; 280 | WriteInt(m_StackSize, header); 281 | WriteInt(m_pSymbolTable->GetStaticVarCount(), header); 282 | 283 | m_HeaderSize = header.size(); 284 | m_Bytecode.insert(m_Bytecode.begin(), header.begin(), header.end()); 285 | return true; 286 | } 287 | 288 | 289 | //Output Results 290 | bool AssemblyCompiler::Save(std::string filename) 291 | { 292 | if(!(m_State == CompState::COMPILED)) 293 | { 294 | std::cerr << "[ASM CMP] File not saved, source not compiled!" << std::endl; 295 | return false; 296 | } 297 | 298 | std::ofstream output( filename, std::ios::binary ); 299 | if(!(output.good())) 300 | { 301 | std::cerr << "[ASM CMP] File not saved, file " << filename << " could not be created" << std::endl; 302 | return false; 303 | } 304 | 305 | for(uint8 i : m_Bytecode) 306 | { 307 | output << i; 308 | } 309 | 310 | std::cout << "[ASM CMP] Executable saved!" << std::endl; 311 | return true; 312 | } 313 | std::vector AssemblyCompiler::GetBytecode() 314 | { 315 | if(!(m_State == CompState::COMPILED)) 316 | { 317 | std::cerr << "[ASM CMP] Warning, non compiled bytecode accessed!" << std::endl; 318 | } 319 | return m_Bytecode; 320 | } 321 | 322 | 323 | //Parsing helpers 324 | bool AssemblyCompiler::TokenizeLine(std::string line, std::string &opname, std::string &arguments) 325 | { 326 | //Comment 327 | if(line.size() > 1 && line[0] == '/' && line[1] == '/') 328 | return false; 329 | //Empty Line 330 | if(line.empty()) 331 | return false; 332 | //get op and arguments 333 | std::size_t firstSpace = line.find(' '); 334 | if(firstSpace!=std::string::npos) 335 | { 336 | opname = line.substr(0, firstSpace); 337 | if(firstSpace < line.size()-1) 338 | arguments = line.substr(firstSpace+1); 339 | } 340 | else opname = line; 341 | 342 | return true; 343 | } 344 | bool AssemblyCompiler::IsValidOpname(std::string opname, uint32 line) 345 | { 346 | if(!(OpcodeNames.count(opname))) 347 | { 348 | std::cerr << "[ASM CMP] " << line << ": Invalid Opcode '" << opname << "'!" << std::endl; 349 | PrintAbort(line); 350 | return false; 351 | } 352 | return true; 353 | } 354 | 355 | void AssemblyCompiler::CheckVar(std::string &arguments) 356 | { 357 | //Separate first argument out 358 | std::string arg; 359 | std::size_t nDelim = arguments.find(' ', 1); 360 | if(nDelim == std::string::npos) 361 | { 362 | arg = arguments; 363 | arguments = std::string(); 364 | } 365 | else 366 | { 367 | arg = arguments.substr(0, nDelim); 368 | arguments = arguments.substr(nDelim+1); 369 | } 370 | //Handle found variable 371 | if(arg[0]=='#') 372 | { 373 | m_pSymbolTable->AddVariable(arg); 374 | } 375 | } 376 | 377 | bool AssemblyCompiler::HasValidArgs(std::string arguments, uint32 line, std::string opname) 378 | { 379 | if(arguments.empty()) 380 | { 381 | std::cerr <<"[ASM CMP] " << line << ", " << opname << ": Incorrect amount of arguments!" << std::endl; 382 | PrintAbort(line); 383 | return false; 384 | } 385 | return true; 386 | } 387 | bool isNumber(const std::string& s) 388 | { 389 | std::string::const_iterator it = s.begin(); 390 | while (it != s.end() && std::isdigit(*it)) ++it; 391 | return !s.empty() && it == s.end(); 392 | } 393 | bool AssemblyCompiler::ParseLiteral(int32 &out, std::string &arguments) 394 | { 395 | if((arguments[0]=='#') || (arguments[0]=='@') || (arguments[0]=='$')) //Replace mnemonics (variables, lables, functions) 396 | { 397 | std::string arg; 398 | std::size_t nDelim = arguments.find(' ', 1); 399 | if(nDelim == std::string::npos) 400 | { 401 | arg = arguments; 402 | arguments = std::string(); 403 | } 404 | else 405 | { 406 | arg = arguments.substr(0, nDelim); 407 | arguments = arguments.substr(nDelim+1); 408 | } 409 | if(m_pSymbolTable->HasSymbol(arg)) 410 | { 411 | out = static_cast(m_pSymbolTable->GetValue(arg)); 412 | return true; 413 | } 414 | std::cerr << "[ASM CMP] Couldn't find symbol: " << arg << std::endl; 415 | } 416 | else if(arguments[0]=='\'') //char 417 | { 418 | out = int32(arguments[1]); 419 | std::size_t nDelim = arguments.find('\'', 1); 420 | if(nDelim == std::string::npos) 421 | { 422 | arguments = std::string(); 423 | } 424 | else arguments = arguments.substr(nDelim+1); 425 | return true; 426 | } 427 | else if(isNumber(arguments)) //int 428 | { 429 | std::size_t nDelim = arguments.find(' ', 1); 430 | if(nDelim == std::string::npos) 431 | { 432 | out = stoi(arguments); 433 | arguments = std::string(); 434 | } 435 | else 436 | { 437 | out = stoi(arguments.substr(0, nDelim)); 438 | arguments = arguments.substr(nDelim+1); 439 | } 440 | return true; 441 | } 442 | return false; 443 | } 444 | void AssemblyCompiler::WriteInt(int32 value) 445 | { 446 | WriteInt(value, m_Bytecode); 447 | } 448 | void AssemblyCompiler::WriteInt(int32 value, std::vector &target) 449 | { 450 | #ifdef WORD_BIG_ENDIAN 451 | target.push_back(value & 0xFF); 452 | target.push_back((value >> 8) & 0xFF); 453 | target.push_back((value >> 16) & 0xFF); 454 | target.push_back((value >> 24) & 0xFF); 455 | #elif 456 | target.push_back((value >> 24) & 0xFF); 457 | target.push_back((value >> 16) & 0xFF); 458 | target.push_back((value >> 8) & 0xFF); 459 | target.push_back(value & 0xFF); 460 | #endif 461 | } 462 | 463 | void AssemblyCompiler::PrintAbort(uint32 line) 464 | { 465 | std::cerr << m_Lines[line] << std::endl; 466 | std::cerr << "[ASM CMP]" << std::endl; 467 | std::cerr << "[ASM CMP] Aborting compilation!" << std::endl; 468 | m_State = CompState::FAILED; 469 | } 470 | -------------------------------------------------------------------------------- /source/VirtualMachine.cpp: -------------------------------------------------------------------------------- 1 | #include "VirtualMachine.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Opcode.h" 9 | #include "AtomicTypes.h" 10 | #include 11 | 12 | VirtualMachine::VirtualMachine() 13 | { 14 | m_RAM = new uint8[MAX_RAM]; 15 | } 16 | VirtualMachine::~VirtualMachine() 17 | { 18 | delete[] m_RAM; 19 | } 20 | 21 | bool VirtualMachine::LoadProgram(std::string filename) 22 | { 23 | std::ifstream file( filename, std::ios::binary ); 24 | if(!file.good()) 25 | { 26 | std::cerr << "[VM] Could not open bytecode executable" << std::endl; 27 | return false; 28 | } 29 | 30 | file.unsetf(std::ios::skipws); 31 | 32 | // get its size: 33 | std::streampos fileSize; 34 | 35 | file.seekg(0, std::ios::end); 36 | fileSize = file.tellg(); 37 | file.seekg(0, std::ios::beg); 38 | 39 | // reserve capacity 40 | std::vector bytecode; 41 | bytecode.reserve(static_cast(fileSize)); 42 | 43 | // read the data: 44 | bytecode.insert(bytecode.begin(), 45 | std::istream_iterator(file), 46 | std::istream_iterator()); 47 | 48 | SetProgram(bytecode); 49 | 50 | return true; 51 | } 52 | void VirtualMachine::SetProgram(std::vector bytecode) 53 | { 54 | uint32 headerSize = sizeof(uint32)*2; 55 | m_StackSize = Unpack(0, bytecode); 56 | auto numStaticVars = Unpack(1 * sizeof(uint32), bytecode); 57 | 58 | m_NumInstructions = bytecode.size()- headerSize; //Header size for now 59 | m_StaticBase = m_NumInstructions + m_StackSize; 60 | for(uint32 i = 0; i < m_NumInstructions; ++i) 61 | { 62 | m_RAM[i+m_StackSize] = bytecode[i+ headerSize]; 63 | } 64 | 65 | //Initialize Dynamic memory allocation 66 | m_FirstSegmentPtr = m_StaticBase + numStaticVars; 67 | m_HeapBase = m_FirstSegmentPtr+sizeof(uint32); 68 | Pack(m_FirstSegmentPtr, m_HeapBase); 69 | Pack(m_HeapBase, MAX_RAM - m_HeapBase); 70 | Pack(m_HeapBase + sizeof(uint32), 0); 71 | 72 | #ifdef VM_DEBUG_HEAP 73 | PrintHeap(); 74 | #endif 75 | 76 | ProgramLoaded = true; 77 | } 78 | 79 | void VirtualMachine::Interpret() 80 | { 81 | if(!ProgramLoaded) 82 | { 83 | std::cerr << "[VM] No program loaded" << std::endl; 84 | return; 85 | } 86 | 87 | m_ProgramCounter = m_StackSize; 88 | while( m_ProgramCounter < m_StaticBase) 89 | { 90 | assert(m_ProgramCounter - m_StackSize < m_NumInstructions); 91 | 92 | auto operation = static_cast(m_RAM[m_ProgramCounter]); 93 | 94 | std::cout << "[DBG] operation: " << GetOpString(operation) << std::endl; 95 | 96 | switch(operation) 97 | { 98 | //MEMORY OPERATIONS 99 | //Add a byte to the stack 100 | case Opcode::LITERAL: 101 | { 102 | Push(Unpack(++m_ProgramCounter)); 103 | m_ProgramCounter+=sizeof(int32); 104 | } 105 | continue; 106 | 107 | //Add multiple bytes to the stack 108 | case Opcode::LITERAL_ARRAY: 109 | { 110 | auto numValues = Unpack(++m_ProgramCounter); 111 | m_ProgramCounter+=sizeof(int32); 112 | while(numValues > 0) 113 | { 114 | Push(Unpack(m_ProgramCounter)); 115 | m_ProgramCounter+=sizeof(int32); 116 | --numValues; 117 | } 118 | } 119 | continue; 120 | 121 | //put memory at address on stack 122 | case Opcode::LOAD: 123 | { 124 | Push(Unpack(Pop())); 125 | ++m_ProgramCounter; 126 | } 127 | continue; 128 | //store a in memory at b 129 | case Opcode::STORE: 130 | { 131 | int32 address = Pop(); 132 | Pack(address, Pop()); 133 | ++m_ProgramCounter; 134 | } 135 | continue; 136 | //put memory at local address on stack 137 | case Opcode::LOAD_LCL: 138 | { 139 | Push(Unpack(m_LCL+Pop())); 140 | ++m_ProgramCounter; 141 | } 142 | continue; 143 | //store a in memory at local b 144 | case Opcode::STORE_LCL: 145 | { 146 | int32 address = m_LCL+Pop(); 147 | Pack(address, Pop()); 148 | ++m_ProgramCounter; 149 | } 150 | continue; 151 | //put memory at argument address on stack 152 | case Opcode::LOAD_ARG: 153 | { 154 | Push(Unpack(m_ARG+Pop())); 155 | ++m_ProgramCounter; 156 | } 157 | continue; 158 | 159 | //Mark (a) bytes on the heap as used and push a pointer to the base 160 | case Opcode::ALLOC: 161 | { 162 | uint32 requestedSize = Pop(); 163 | uint32 requiredSize = requestedSize + sizeof(uint32);//First 4 bytes of segment hold segment size -- maybe in future 4 more bytes for reference count 164 | 165 | auto firstSegment = Unpack(m_FirstSegmentPtr); 166 | uint32 nextSegment = firstSegment; 167 | 168 | uint32 bestFitSize = std::numeric_limits::max(); 169 | uint32 bestFitPtr = 0; 170 | uint32 prevNextPtr = m_FirstSegmentPtr; 171 | 172 | //Get best fitting segment 173 | bool earlyOut = false; 174 | while (nextSegment != 0 && !earlyOut) 175 | { 176 | auto segmentSize = Unpack(nextSegment); 177 | if (segmentSize >= requiredSize && segmentSize < bestFitSize) 178 | { 179 | if (segmentSize == requiredSize) earlyOut = true; 180 | bestFitPtr = nextSegment; 181 | bestFitSize = segmentSize; 182 | if(nextSegment != firstSegment) prevNextPtr = nextSegment + sizeof(uint32); 183 | } 184 | nextSegment = Unpack(nextSegment + sizeof(uint32)); 185 | } 186 | if (bestFitPtr == 0) 187 | { 188 | std::cerr << "[VM] Out of Memory Exception, could not allocate space for variable!" << std::endl; 189 | return; 190 | } 191 | //use best found segment 192 | uint32 remainingSize = bestFitSize - requiredSize; 193 | if (remainingSize >= sizeof(uint32)*2)//Split segment in two if the remainder is big enough to allocate (ie its bigger than a segment header) 194 | { 195 | uint32 newSegPtr = bestFitPtr + requiredSize; 196 | Pack(newSegPtr, remainingSize); //set the new segment size 197 | Pack(newSegPtr + sizeof(uint32), Unpack(bestFitPtr+sizeof(uint32))); //set the new segments nextPtr to the value of the allocated segments next ptr 198 | Pack(prevNextPtr, newSegPtr);//Link the previous segment to the new segment 199 | 200 | Pack(bestFitPtr, requiredSize); //Tell the allocated segment how big it is 201 | } 202 | else //Allocate the entire segment 203 | { 204 | Pack(prevNextPtr, Unpack(bestFitPtr+sizeof(uint32)));//Link the previous segment to next segment 205 | } 206 | Push(bestFitPtr+sizeof(uint32)); 207 | ++m_ProgramCounter; 208 | 209 | #ifdef VM_DEBUG_HEAP 210 | PrintHeap(); 211 | #endif 212 | } 213 | continue; 214 | //Mark the space at (a) as unused 215 | case Opcode::FREE: 216 | { 217 | uint32 segmentPtr = Pop()-sizeof(uint32); 218 | auto segmentSize = Unpack(segmentPtr); 219 | 220 | uint32 existingNextPtr = m_FirstSegmentPtr; 221 | auto nextSegment = Unpack(existingNextPtr); 222 | uint32 existingSegment = existingNextPtr; 223 | 224 | bool earlyOut = false; 225 | while (nextSegment != 0 && !earlyOut) 226 | { 227 | if (segmentPtr < nextSegment) 228 | { 229 | //insert 230 | bool standalone = true; 231 | if ((nextSegment != m_FirstSegmentPtr) && (existingSegment + Unpack(existingSegment) == segmentPtr))//Merge EXISTING+INSERTED 232 | { 233 | //simply expand the existing segment to accomodate our size 234 | segmentPtr = existingSegment; 235 | segmentSize += Unpack(existingSegment); 236 | Pack(segmentPtr, segmentSize); 237 | standalone = false; 238 | } 239 | else 240 | { 241 | Pack(segmentPtr + sizeof(uint32), nextSegment);//next = existing.next 242 | Pack(existingNextPtr, segmentPtr); //existing.next = this 243 | } 244 | if (segmentPtr + segmentSize == nextSegment)//Merge INSERTED+NEXT 245 | { 246 | segmentSize += Unpack(nextSegment); 247 | Pack(segmentPtr, segmentSize); 248 | Pack(segmentPtr + sizeof(uint32), Unpack(nextSegment + sizeof(uint32))); //next = next.next 249 | } 250 | else if(standalone) Pack(segmentPtr + sizeof(uint32), nextSegment); 251 | earlyOut = true; 252 | } 253 | existingSegment = nextSegment; 254 | existingNextPtr = existingSegment + sizeof(uint32); 255 | nextSegment = Unpack(existingNextPtr); 256 | } 257 | if (!earlyOut) 258 | { 259 | std::cerr << "[VM] Failed to free memory at " << segmentPtr << "; " << segmentSize << " bytes" << std::endl; 260 | return; 261 | } 262 | ++m_ProgramCounter; 263 | 264 | #ifdef VM_DEBUG_HEAP 265 | PrintHeap(); 266 | #endif 267 | } 268 | continue; 269 | 270 | //ARITHMETIC OPERATIONS 271 | //Add values together 272 | case Opcode::ADD: 273 | { 274 | int32 b = Pop(); 275 | int32 a = Pop(); 276 | Push(a + b); 277 | ++m_ProgramCounter; 278 | } 279 | continue; 280 | //a - b 281 | case Opcode::SUB: 282 | { 283 | int32 b = Pop(); 284 | int32 a = Pop(); 285 | Push(a - b); 286 | ++m_ProgramCounter; 287 | } 288 | continue; 289 | 290 | //LOGICAL OPERATIONS 291 | //a < b 292 | case Opcode::LESS: 293 | { 294 | int32 b = Pop(); 295 | int32 a = Pop(); 296 | Push(a < b); 297 | ++m_ProgramCounter; 298 | } 299 | continue; 300 | //a > b 301 | case Opcode::GREATER: 302 | { 303 | int32 b = Pop(); 304 | int32 a = Pop(); 305 | Push(a > b); 306 | ++m_ProgramCounter; 307 | } 308 | continue; 309 | //!a 310 | case Opcode::NOT: 311 | { 312 | int32 a = Pop(); 313 | Push(!a); 314 | ++m_ProgramCounter; 315 | } 316 | continue; 317 | //a == b 318 | case Opcode::EQUALS: 319 | { 320 | int32 b = Pop(); 321 | int32 a = Pop(); 322 | Push(a == b); 323 | ++m_ProgramCounter; 324 | } 325 | continue; 326 | 327 | //FLOW CONTROL 328 | //goto a 329 | case Opcode::JMP: 330 | { 331 | int32 address = Pop(); 332 | m_ProgramCounter = static_cast(address); 333 | } 334 | continue; 335 | //if(a) goto b 336 | case Opcode::JMP_IF: 337 | { 338 | int32 address = Pop(); 339 | int32 condition = Pop(); 340 | if(condition) 341 | { 342 | m_ProgramCounter = static_cast(address); 343 | } 344 | else 345 | { 346 | ++m_ProgramCounter; 347 | } 348 | } 349 | continue; 350 | 351 | //FUNCTIONS 352 | //put a new frame on the stack with n arguments and k local variables 353 | case Opcode::CALL: 354 | { 355 | uint32 ret = m_ProgramCounter + 1; 356 | m_ProgramCounter = static_cast(Pop()); 357 | Push(m_RTN); 358 | m_RTN = ret; 359 | Push(m_LCL); 360 | Push(m_ARG); 361 | Push(m_THIS); //This stays the same because we are doing a function not a method 362 | m_ARG = m_StackPointer - (Unpack(m_ProgramCounter) + 12 /*difference from this to return*/); 363 | m_ProgramCounter += sizeof(int32); 364 | m_LCL = m_StackPointer + sizeof(int32); 365 | m_StackPointer = m_LCL + Unpack(m_ProgramCounter); 366 | m_ProgramCounter += sizeof(int32); 367 | } 368 | continue; 369 | //Return from current function to previous function on stack and copy end values over 370 | case Opcode::RETURN: //#todo stop assuming return value size 371 | { 372 | m_ProgramCounter = m_RTN; 373 | Pack(m_ARG, Pop()); 374 | m_StackPointer = m_ARG; 375 | m_THIS = Unpack(m_LCL - (sizeof(int32) * 1)); 376 | m_ARG = Unpack(m_LCL - (sizeof(int32) * 2)); 377 | m_RTN = Unpack(m_LCL - (sizeof(int32) * 4)); 378 | m_LCL = Unpack(m_LCL - (sizeof(int32) * 3)); 379 | } 380 | continue; 381 | 382 | //"Library functions" should later be implemented differently 383 | //print x chars to console 384 | case Opcode::PRINT: 385 | { 386 | uint32 size = Pop(); 387 | std::string out; 388 | for(uint32 j = 0; j(Pop()) + out; 391 | } 392 | std::cout << out; 393 | ++m_ProgramCounter; 394 | } 395 | continue; 396 | //print one integer to console 397 | case Opcode::PRINT_INT: 398 | { 399 | std::cout << Pop(); 400 | ++m_ProgramCounter; 401 | } 402 | continue; 403 | //print one integer to console 404 | case Opcode::PRINT_ENDL: 405 | { 406 | std::cout << std::endl; 407 | ++m_ProgramCounter; 408 | } 409 | continue; 410 | 411 | //INVALID 412 | default: 413 | std::cerr << "Invalid opcode: " << GetOpString(operation) << std::endl; 414 | assert(false); 415 | continue; 416 | } 417 | } 418 | } 419 | 420 | void VirtualMachine::Push(int32 value) 421 | { 422 | assert(m_StackPointer + sizeof(int32) < m_StackSize); //Stack Overflow 423 | Pack(m_StackPointer+=sizeof(int32), value); 424 | } 425 | int VirtualMachine::Pop() 426 | { 427 | assert(m_StackPointer >= 0); //Invalid memory access "Stack underflow" - this does not protect against the SP underflowing the working stack 428 | auto value = Unpack(m_StackPointer); 429 | m_StackPointer -= sizeof(int32); 430 | return value; 431 | } 432 | 433 | template 434 | T VirtualMachine::Unpack(uint32 address) 435 | { 436 | #ifdef WORD_BIG_ENDIAN 437 | uint32 value = static_cast(m_RAM[address+3]) << 24 | 438 | static_cast(m_RAM[address+2]) << 16 | 439 | static_cast(m_RAM[address+1]) << 8 | 440 | static_cast(m_RAM[address+0]); 441 | #elif 442 | uint32 value = static_cast(m_RAM[address+0]) << 24 | 443 | static_cast(m_RAM[address+1]) << 16 | 444 | static_cast(m_RAM[address+2]) << 8 | 445 | static_cast(m_RAM[address+3]); 446 | #endif 447 | return static_cast(value); 448 | } 449 | template 450 | T VirtualMachine::Unpack(uint32 address, std::vector data) 451 | { 452 | assert(data.size() > address + 3); 453 | #ifdef WORD_BIG_ENDIAN 454 | uint32 value = static_cast(data[address+3]) << 24 | 455 | static_cast(data[address+2]) << 16 | 456 | static_cast(data[address+1]) << 8 | 457 | static_cast(data[address+0]); 458 | #elif 459 | uint32 value = static_cast(data[address+0]) << 24 | 460 | static_cast(data[address+1]) << 16 | 461 | static_cast(data[address+2]) << 8 | 462 | static_cast(data[address+3]); 463 | #endif 464 | return static_cast(value); 465 | } 466 | template 467 | void VirtualMachine::Pack(uint32 address, T value) 468 | { 469 | auto n = static_cast(value); 470 | #ifdef WORD_BIG_ENDIAN 471 | m_RAM[address+3] = (n >> 24) & 0xFF; 472 | m_RAM[address+2] = (n >> 16) & 0xFF; 473 | m_RAM[address+1] = (n >> 8) & 0xFF; 474 | m_RAM[address+0] = n & 0xFF; 475 | #elif //WORD_LITTLE_ENDIAN 476 | m_RAM[address+0] = (n >> 24) & 0xFF; 477 | m_RAM[address+1] = (n >> 16) & 0xFF; 478 | m_RAM[address+2] = (n >> 8) & 0xFF; 479 | m_RAM[address+3] = n & 0xFF; 480 | #endif 481 | } 482 | 483 | void VirtualMachine::PrintHeap(bool baseOffset) 484 | { 485 | uint32 offset = baseOffset ? m_HeapBase : 0; 486 | auto nextSegment = Unpack(m_FirstSegmentPtr); 487 | 488 | std::cout << "[DBG Heap]: "<< m_FirstSegmentPtr-offset << " first: " << nextSegment-offset << " \t"; 489 | while (nextSegment != 0) 490 | { 491 | std::cout << "@" << nextSegment-offset << "{s: " << Unpack(nextSegment); 492 | nextSegment = Unpack(nextSegment + sizeof(uint32)); 493 | std::cout << "; n: " << nextSegment-offset << "} "; 494 | } 495 | std::cout << std::endl; 496 | } 497 | --------------------------------------------------------------------------------