diff --git a/README.md b/README.md index 9ab5c2b..de6f65a 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,14 @@ afterwards can be a bit cryptic as to where it originated. ## Notation - `[operation][number type]`, e.g. `divi` for divide (div) integer -- `%[register]` for addressing registers -- `$[value]` for using immediate (literal) integer values -- `'a'` for using immediate character values +- `%[register]` for addressing registers, e.g. `%A` +- `$[value]` for using immediate (literal) integer values, e.g. `$38` +- `'[character]'` for using immediate character values, e.g. `'r'` - `;` for end of statement (mandatory) -- `[label]:` for labels +- `[label]:` for labels, e.g. `loop:` - `#[text]` for comments: any text is ignored till a newline (`\n`) is found -- `[[%register|$value]]` for accessing memory -- Elements must be separated by whitespace character +- `[[%register|$value]]` for accessing memory, e.g. `[$104]` +- Elements must be separated by whitespace character(s) - Good: `add $2 $5 %A;` - Bad: `add $2$5%A;` @@ -103,7 +103,8 @@ directive is not supported therefore. ### Registers -All registers are 32 bits wide. The following 4 registers currently exist: +All registers are 32 bits wide. The following 4 general purpose registers +currently exist: - A - B @@ -159,14 +160,14 @@ the first argument ## Interupts -- [0..3] Output to STDOUT +- [0..9] Output to STDOUT - `0` put value of register A as ASCII character on stdout - `1` put value of register A as decimal integer on stdout - `2` put value of register A as hexadecimal integer on stdout - `3` put the string pointed at by register A for the amount of characters defined by register B on stdout -- [4..5] Input from STDIN - - `4` get a single ASCII character from STDIN and store it in register A - - `5` get a string of a maximum length determined by register B and store it +- [10..19] Input from STDIN + - `10` get a single ASCII character from STDIN and store it in register A + - `11` get a string of a maximum length determined by register B and store it in the address specified by register A. After execution register B will contain the number of characters actually read. \ No newline at end of file diff --git a/bin/test.wasm b/bin/test.wasm index 6dae79b..74823ef 100644 --- a/bin/test.wasm +++ b/bin/test.wasm @@ -93,7 +93,7 @@ int $3; call noop_function; # Echo a character -int $4; +int $10; int $0; seti %A $10; # newline @@ -102,7 +102,7 @@ int $0; # Echo a string seti %A $0; seti %B $10; -int $5; +int $11; int $3; exit; diff --git a/include/compile/compiler.hpp b/include/compile/compiler.hpp new file mode 100644 index 0000000..22762a9 --- /dev/null +++ b/include/compile/compiler.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace Compile +{ + class Compiler + { + private: + std::unordered_map jumpLabelLocations; + std::vector> unresolvedJumpLabels; + + void InsertAsBytes( + Token::Token const & token, + std::vector & bytes); + + public: + bool Compile( + std::vector const & tokens, + std::vector & bytes); + }; +} \ No newline at end of file diff --git a/include/compile/errors.hpp b/include/compile/errors.hpp new file mode 100644 index 0000000..7eb6e0a --- /dev/null +++ b/include/compile/errors.hpp @@ -0,0 +1,28 @@ +#pragma once +#include +#include + +namespace Compile +{ + class CompilationError + { + public: + Token::Token errorToken; + + CompilationError(std::string const & message, Token::Token const & token); + + static CompilationError CreateExpectedArgumentError(Token::Token const & token); + static CompilationError CreateExpectedLabelError(Token::Token const & token); + static CompilationError CreateExpectedImmediateError(Token::Token const & token); + static CompilationError CreateExpectedImmediateOrRegisterOrMemory(Token::Token const & token); + static CompilationError CreateExpectedRegisterError(Token::Token const & token); + static CompilationError CreateExpectedRegisterOrMemoryError(Token::Token const & token); + + static CompilationError CreateExpectedOperandError(Token::Token const & token); + static CompilationError CreateTooManyArgumentsError(Token::Token const & token); + static CompilationError CreateTooFewArgumentsError(Token::Token const & token); + static CompilationError CreateExpectedEndOfStatementError(Token::Token const & token); + static CompilationError CreateDuplicateLabelError(Token::Token const & token); + static CompilationError CreateNonExistingLabelError(Token::Token const & token); + }; +} \ No newline at end of file diff --git a/include/configuration.hpp b/include/configuration.hpp deleted file mode 100644 index 53a9687..0000000 --- a/include/configuration.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include -#include -#include - -struct Configuration -{ - unsigned memorySize; - std::vector> strings; - - void PrepareMemory(std::vector & memory) const; - Configuration(); -}; \ No newline at end of file diff --git a/include/execute/argumentvalue.hpp b/include/execute/argumentvalue.hpp new file mode 100644 index 0000000..be5126f --- /dev/null +++ b/include/execute/argumentvalue.hpp @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace Execute +{ + enum class ArgumentType + { + Immediate, + Register, + Memory + }; + + class ArgumentValue + { + private: + ArgumentType type; + ArgumentType memoryValueType; + std::variant data; + + int & GetRegister(State & state) const; + std::uint8_t * GetMemory(State & state) const; + + public: + void Write(int const value, State & state) const; + int Read(State & state) const; + + // Returns the size of the argument in bytes + std::size_t Parse( + std::vector const & memory, + std::size_t const pos); + }; +} \ No newline at end of file diff --git a/include/execute/bytecode.hpp b/include/execute/bytecode.hpp new file mode 100644 index 0000000..f09d639 --- /dev/null +++ b/include/execute/bytecode.hpp @@ -0,0 +1,43 @@ +#pragma once +#include + +namespace Execute +{ + enum class InstructionByte : std::uint8_t + { + NOOP = 0, + /* Integer functions */ + ADD_INTEGER, + SUBTRACT_INTEGER, + DIVIDE_INTEGER, + MULTIPLY_INTEGER, + SHIFT_LEFT_INTEGER, + SHIFT_RIGHT_INTEGER, + SET_INTEGER, + /* Control flow */ + JUMP, + INTERRUPT, + CALL, + RETURN, + EXIT, + LESS_THAN_INTEGER, + GREATER_THAN_INTEGER, + EQUALS_INTEGER, + /* Memory */ + POP_INTEGER, + PUSH_INTEGER, + /* Values */ + IMMEDIATE_INTEGER, + REGISTER, + MEMORY_OP, + LABEL, + }; + + enum class RegisterByte : std::uint8_t + { + A = 1, + B, + C, + D + }; +} \ No newline at end of file diff --git a/include/execute/error.hpp b/include/execute/error.hpp index 8325da4..012950e 100644 --- a/include/execute/error.hpp +++ b/include/execute/error.hpp @@ -7,50 +7,44 @@ namespace Execute { protected: std::string message; + std::size_t byteLocation; public: std::string const & GetMessage() const; RuntimeError(); - RuntimeError(std::string const & what); - }; - - class StackUnderflow : public RuntimeError - { - public: - StackUnderflow(); - }; - - class StackOverflow : public RuntimeError - { - public: - StackOverflow(); - }; - - class MissingLabel : public RuntimeError - { - public: - MissingLabel(std::string const & label); + RuntimeError(std::string const & what, std::size_t const byteLocation); }; class InterruptIndexOutOfRange : public RuntimeError { public: - InterruptIndexOutOfRange(int const index); + InterruptIndexOutOfRange(std::size_t const location, int const index); + }; + + class AttemptedWriteToImmediate : public RuntimeError + { + public: + AttemptedWriteToImmediate(std::size_t const location); + }; + + class NonExecutableInstruction : public RuntimeError + { + public: + NonExecutableInstruction(std::size_t const location); + }; + + class NonArgumentByte : public RuntimeError + { + public: + NonArgumentByte(std::size_t const location); }; class OutOfMemory : public RuntimeError { public: - OutOfMemory(int const memoryLocation, int const memorySize); + OutOfMemory( + std::size_t const requiredMemorySize, + std::size_t const actualMemorySize); }; - - namespace Internal - { - class BadValueType : public RuntimeError - { - public: - BadValueType(); - }; - } } \ No newline at end of file diff --git a/include/execute/flags.hpp b/include/execute/flags.hpp deleted file mode 100644 index 3cc1da3..0000000 --- a/include/execute/flags.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -namespace Execute -{ - struct Flags - { - - }; -} diff --git a/include/execute/interrupts.hpp b/include/execute/interrupts.hpp index cc4d51c..5dbde01 100644 --- a/include/execute/interrupts.hpp +++ b/include/execute/interrupts.hpp @@ -3,9 +3,10 @@ #include #include -using InterruptFn = void (*)(Execute::Registers & registers, std::vector & memory); - namespace Execute { - std::vector GetInterrupts(); + void ExecuteInterrupt( + int const id, + Execute::Registers & registers, + std::vector & memory); } \ No newline at end of file diff --git a/include/execute/registers.hpp b/include/execute/registers.hpp index f7a9942..a883039 100644 --- a/include/execute/registers.hpp +++ b/include/execute/registers.hpp @@ -1,19 +1,14 @@ #pragma once +#include namespace Execute { struct Registers { - union - { - struct - { - int A; - int B; - int C; - int D; - }; - int registers[4]; - }; + int A, B, C, D; + std::size_t programCounter; + std::size_t stackPointer; + + // TODO status registers? }; } diff --git a/include/execute/state.hpp b/include/execute/state.hpp index 9a58f06..2cf2154 100644 --- a/include/execute/state.hpp +++ b/include/execute/state.hpp @@ -1,18 +1,17 @@ #pragma once -#include +#include #include -#include +#include namespace Execute { struct State { - unsigned currentStatement; - unsigned nextStatement; - std::unordered_map const * labelStatementIndice; - std::vector interrupts; - std::vector memory; - unsigned stackPointer; bool terminated; + Registers registers; + std::vector memory; + + void PushToStack(int const value); + int PopFromStack(); }; } \ No newline at end of file diff --git a/include/execute/virtualmachine.hpp b/include/execute/virtualmachine.hpp index ed7aa5e..2f6ffb8 100644 --- a/include/execute/virtualmachine.hpp +++ b/include/execute/virtualmachine.hpp @@ -1,8 +1,7 @@ -#include -#include -#include +#include +#include +#include #include -#include #include namespace Execute @@ -10,27 +9,39 @@ namespace Execute class VirtualMachine { private: - Flags flags; - Registers registers; State state; - std::unique_ptr codePtr; + void DoArithmatic( + InstructionByte const instruction, + std::array & arguments); + void SetInteger(std::array & arguments); + void ExecuteJump(std::array & arguments); + void ExecuteInterrupt(std::array & arguments); + void ExecuteCall( + std::array & arguments, + std::size_t const returnByte); + void ExecuteReturn(); + void DoBooleanLogic( + InstructionByte const instruction, + std::array & arguments, + std::size_t const nextInstruction); + void ExecutePop(std::array & arguments); + void ExecutePush(std::array & arguments); void Step(); public: void Run(); void SingleStep(); - void LoadConfiguration(Configuration const & c); - void LoadCode(std::unique_ptr code); - Flags const & GetFlags() const; - Registers const & GetRegisters() const; + void SetMemorySize(std::size_t const size); + void LoadCode( + std::vector const & byteCode, + bool const printDecodedBytes); + State const & GetState() const; - Interpret::Statement const * const GetCurrentStatement() const; + Execute::InstructionByte GetCurrentInstruction() const; bool IsTerminated() const; - - VirtualMachine(); }; } \ No newline at end of file diff --git a/include/interpret/code.hpp b/include/interpret/code.hpp deleted file mode 100644 index 5a65f1b..0000000 --- a/include/interpret/code.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -namespace Interpret -{ - struct Code - { - std::vector> statements; - std::unordered_map labelStatementIndice; - std::unordered_map declarations; - - Code() = default; - ~Code() = default; - Code(const Code&) = delete; - Code& operator=(const Code&) = delete; - }; -} \ No newline at end of file diff --git a/include/interpret/errors.hpp b/include/interpret/errors.hpp deleted file mode 100644 index f73efa6..0000000 --- a/include/interpret/errors.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include -#include -#include - -namespace Interpret -{ - struct InterpretationError : public std::exception - { - Token::Token errorToken; - InterpretationError(Token::Token const & token, std::string const & msg); - }; - - struct ExpectedArgument : public InterpretationError - { - ExpectedArgument(Token::Token const & token); - }; - - struct ExpectedLabel : public InterpretationError - { - ExpectedLabel(Token::Token const & token); - }; - - struct ExpectedValue : public InterpretationError - { - ExpectedValue(Token::Token const & token); - }; - - struct ExpectedImmediate : public InterpretationError - { - ExpectedImmediate(Token::Token const & token); - }; - - struct ExpectedImmediateOrMemory : public InterpretationError - { - ExpectedImmediateOrMemory(Token::Token const & token); - }; - - struct ExpectedRegister : public InterpretationError - { - ExpectedRegister(Token::Token const & token); - }; - - struct ExpectedRegisterOrMemory : public InterpretationError - { - ExpectedRegisterOrMemory(Token::Token const & token); - }; - - struct ExpectedOperand : public InterpretationError - { - ExpectedOperand(Token::Token const & token); - }; - - struct TooManyArguments : public InterpretationError - { - TooManyArguments(Token::Token const & token); - }; - - struct TooFewArguments : public InterpretationError - { - TooFewArguments(Token::Token const & token); - }; - - struct MissingEndOfStatment : public InterpretationError - { - MissingEndOfStatment(Token::Token const & token); - }; - - namespace Internal - { - struct BadTokenForValue : public InterpretationError - { - BadTokenForValue(Token::Token const & token); - }; - } -} diff --git a/include/interpret/interpreter.hpp b/include/interpret/interpreter.hpp deleted file mode 100644 index d36ec80..0000000 --- a/include/interpret/interpreter.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace Interpret -{ - class Interpreter - { - public: - void Interpret(std::vector const & tokens, Code & code); - }; -} \ No newline at end of file diff --git a/include/interpret/operanddefinitions.hpp b/include/interpret/operanddefinitions.hpp deleted file mode 100644 index 348a20d..0000000 --- a/include/interpret/operanddefinitions.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include -#include -#include - -namespace Interpret -{ - std::unique_ptr ExtractStatement(unsigned const operatorIndex, std::vector const & tokens); - - std::tuple ExtractDeclaration(unsigned const operatorIndex, std::vector const & tokens); - - int GetRequiredNumberOfArguments(Token::OperandType const type); -} \ No newline at end of file diff --git a/include/interpret/statement.hpp b/include/interpret/statement.hpp deleted file mode 100644 index 2760f83..0000000 --- a/include/interpret/statement.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once -#include -#include -#include -#include - -namespace Interpret -{ - struct Statement - { - virtual void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) = 0; - }; - - struct NoArgumentStatement : Statement - { - void (* function)(Execute::Flags & flags, Execute::Registers & registers); - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct OneArgumentStatement : Statement - { - void (* function)(Execute::Flags & flags, int argument1); - Value firstArgument; - - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct ControlFlowStatement : Statement - { - void (* function)(Execute::State & state, int argument1, int argument2); - Value firstArgument; - Value secondArgument; - - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct ArithmeticStatement : Statement - { - void (* function)(int argument1, int argument2, int & argument3); - Value firstArgument; - Value secondArgument; - Value thirdArgument; - - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct JumpStatement : Statement - { - private: - std::string const label; - - public: - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - - JumpStatement(std::string const & label); - }; - - struct FunctionCallStatement : Statement - { - private: - std::string const label; - - public: - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - - FunctionCallStatement(std::string const & label); - }; - - struct ReturnFromFunctionStatement : Statement - { - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct ExitProgramStatement : Statement - { - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct SetStatement : Statement - { - Value firstArgument; - Value secondArgument; - - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct InterruptStatement : Statement - { - Value firstArgument; - - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct PushStatement : Statement - { - Value firstArgument; - - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; - - struct PopStatement : Statement - { - Value firstArgument; - - void Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) override; - }; -} \ No newline at end of file diff --git a/include/interpret/value.hpp b/include/interpret/value.hpp deleted file mode 100644 index a57647a..0000000 --- a/include/interpret/value.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include -#include - -namespace Interpret -{ - enum class ValueType - { - Register, - ImmediateInteger, - MemoryLocation - }; - - enum class ValueDataType - { - Register, - Immediate - }; - - struct Value - { - ValueType type; - ValueDataType dataType; - int data; - - int & GetValue(Execute::State & state, Execute::Registers & registers); - - void CreateFromToken(Token::Token const & token); - }; -} \ No newline at end of file diff --git a/include/token/errors.hpp b/include/token/errors.hpp index 4943f84..46d9d73 100644 --- a/include/token/errors.hpp +++ b/include/token/errors.hpp @@ -10,9 +10,4 @@ namespace Token std::string errorMsg; TokenizationError(Token const & token, std::string const & msg); }; - - struct MissingEndOfString : public TokenizationError - { - MissingEndOfString(Token const & token); - }; } \ No newline at end of file diff --git a/include/token/token.hpp b/include/token/token.hpp index 15bf2c6..07e721b 100644 --- a/include/token/token.hpp +++ b/include/token/token.hpp @@ -39,13 +39,15 @@ namespace Token static Token CreateEmptyToken(int const lineNumber, int const lineColumn); static Token CreateErrorToken(std::string const & message, TokenType const type, int const lineNumber, int const lineColumn); static Token CreateStatementEndToken(int const lineNumber, int const lineColumn); - static Token CreateLabelToken(std::string const & string, int const lineNumber, int const lineColumn); + static Token CreateLabelDefinitionToken(std::string const & string, int const lineNumber, int const lineColumn); + static Token CreateLabelArgumentToken(std::string const & string, int const lineNumber, int const lineColumn); static Token CreateImmediateValueToken(int const value, int const lineNumber, int const lineColumn); static Token CreateRegisterToken(RegisterType const registerType, int const lineNumber, int const lineColumn); static Token CreateOperandToken(OperandType const operandType, int const lineNumber, int const lineColumn); static Token CreateMemoryToken(RegisterType const registerType, int const lineNumber, int const lineColumn); static Token CreateMemoryToken(int const value, int const lineNumber, int const lineColumn); + std::string GetName() const; void Print() const; }; } diff --git a/include/token/tokenizer.hpp b/include/token/tokenizer.hpp index c2cfb4f..f09cfc9 100644 --- a/include/token/tokenizer.hpp +++ b/include/token/tokenizer.hpp @@ -8,7 +8,7 @@ namespace Token class Tokenizer { private: - // argument for string should never be of length zero + // Argument for string should never be of length zero Token ExtractToken( std::string const & string, std::size_t const lineNumber, diff --git a/include/token/tokentype.hpp b/include/token/tokentype.hpp index 5ee79fb..2d9804c 100644 --- a/include/token/tokentype.hpp +++ b/include/token/tokentype.hpp @@ -9,7 +9,8 @@ namespace Token ImmediateInteger, Register, StatementEnd, - Label, + LabelDefinition, + LabelArgument, Memory }; } \ No newline at end of file diff --git a/include/utils.hpp b/include/utils.hpp index 181a91d..3504c4c 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -1,6 +1,8 @@ #pragma once +#include #include #include +#include namespace Utils { @@ -15,4 +17,14 @@ namespace Utils std::string getValueSurroundedByWhitespace( std::string const & src, std::size_t const pos); + + namespace Bytes + { + void Write( + int const value, + std::vector & vec, + std::size_t const pos); + + int Read(std::vector const & vec, std::size_t const pos); + } } \ No newline at end of file diff --git a/include/wassembler.hpp b/include/wassembler.hpp index 998b27e..d810d88 100644 --- a/include/wassembler.hpp +++ b/include/wassembler.hpp @@ -1,29 +1,44 @@ #pragma once -#include +#include #include -#include #include class Wassembler { private: - Configuration config; Execute::VirtualMachine vm; bool printSubstitutions; bool printTokens; + bool printTranslatedBytes; - bool LoadLinesFromFile(std::string const & filePath, std::vector & lines) const; - bool LoadTokens(std::vector const & lines, std::vector & tokens) const; + bool LoadTextFile( + std::string const & filePath, + std::vector & lines) const; + bool Preprocess(std::vector & lines) const; + bool Tokenize( + std::vector const & lines, + std::vector & tokens) const; + bool CompileToBytes( + std::vector const & tokens, + std::vector const & lines, + std::vector & bytes) const; + void ExecuteCode(std::vector const & bytes); + + bool CompileFile( + std::string const & filePath, + std::vector & bytes) const; public: void SetMemorySize(unsigned const size); void EnableSubstitutionsLogging(); void EnableTokensLogging(); + void EnableByteTranslationLogging(); - bool LoadFromFile(std::string const & filePath); - - void Run(); + bool CompileAndRun(std::string const & filePath); + bool CompileToFile( + std::string const & inputFilePath, + std::string const & outputFilePath); Wassembler() = default; }; \ No newline at end of file diff --git a/makefile b/makefile index 40d93ba..7c1d5e0 100644 --- a/makefile +++ b/makefile @@ -13,7 +13,7 @@ BINARY = bin/wassembler all: ${BINARY} check: ${BINARY} - ./$< ./bin/test.wasm + ./$< ./bin/test.wasm -e clean: -rm -rf build ./${BINARY} diff --git a/src/compile/compiler.cpp b/src/compile/compiler.cpp new file mode 100644 index 0000000..f8415fb --- /dev/null +++ b/src/compile/compiler.cpp @@ -0,0 +1,433 @@ +#include +#include +#include +#include +#include + +namespace Compile +{ + int GetRequiredNumberOfArguments(Token::OperandType const type) + { + switch (type) + { + case Token::OperandType::AddInteger: + case Token::OperandType::SubtractInteger: + case Token::OperandType::DivideInteger: + case Token::OperandType::MultiplyInteger: + case Token::OperandType::ShiftIntegerLeft: + case Token::OperandType::ShiftIntegerRight: + return 3; + + case Token::OperandType::LessThanInteger: + case Token::OperandType::GreaterThanInteger: + case Token::OperandType::EqualInteger: + case Token::OperandType::SetInteger: + return 2; + + case Token::OperandType::Jump: + case Token::OperandType::CallFunction: + case Token::OperandType::Interrupt: + case Token::OperandType::PushInteger: + case Token::OperandType::PopInteger: + return 1; + + default: + std::printf("WARNING: returning default argument length of 0 for operand type %i\n", static_cast(type)); + case Token::OperandType::ReturnFromFunction: + case Token::OperandType::ExitProgram: + return 0; + } + } + + bool IsArgumentToken(Token::Token const & t) + { + return + t.type == Token::TokenType::ImmediateInteger || + t.type == Token::TokenType::Register || + t.type == Token::TokenType::LabelArgument || + t.type == Token::TokenType::Memory; + } + + bool IsReadableToken(Token::Token const & t) + { + return + t.type == Token::TokenType::ImmediateInteger || + t.type == Token::TokenType::Register || + t.type == Token::TokenType::Memory; + } + + bool IsWriteableToken(Token::Token const & t) + { + return + t.type == Token::TokenType::Register || + t.type == Token::TokenType::Memory; + } + + void ValidateArguments( + std::vector const & tokens, + std::size_t const operandIndex) + { + auto const operandType = std::get(tokens[operandIndex].data); + switch(operandType) + { + // 2 Read values + 1 write value + case Token::OperandType::AddInteger: + case Token::OperandType::SubtractInteger: + case Token::OperandType::DivideInteger: + case Token::OperandType::MultiplyInteger: + case Token::OperandType::ShiftIntegerLeft: + case Token::OperandType::ShiftIntegerRight: + if (!IsReadableToken(tokens[operandIndex + 1])) + { + throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]); + } + if (!IsReadableToken(tokens[operandIndex + 2])) + { + throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 2]); + } + if (!IsWriteableToken(tokens[operandIndex + 3])) + { + throw CompilationError::CreateExpectedRegisterOrMemoryError(tokens[operandIndex + 3]); + } + break; + + // 2 Read values + case Token::OperandType::LessThanInteger: + case Token::OperandType::GreaterThanInteger: + case Token::OperandType::EqualInteger: + case Token::OperandType::SetInteger: + if (!IsReadableToken(tokens[operandIndex + 1])) + { + throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]); + } + if (!IsReadableToken(tokens[operandIndex + 2])) + { + throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 2]); + } + break; + + // 1 Label value + case Token::OperandType::Jump: + case Token::OperandType::CallFunction: + if (tokens[operandIndex + 1].type != Token::TokenType::LabelArgument) + { + throw CompilationError::CreateExpectedLabelError(tokens[operandIndex + 1]); + } + break; + + // 1 Read value + case Token::OperandType::Interrupt: + case Token::OperandType::PushInteger: + if (!IsReadableToken(tokens[operandIndex + 1])) + { + throw CompilationError::CreateExpectedImmediateOrRegisterOrMemory(tokens[operandIndex + 1]); + } + break; + + // 1 Write value + case Token::OperandType::PopInteger: + if (!IsWriteableToken(tokens[operandIndex + 1])) + { + throw CompilationError::CreateExpectedRegisterOrMemoryError(tokens[operandIndex + 1]); + } + break; + + default: + throw std::runtime_error("Unimplemented operandType case in ValidateArguments"); + } + } + + Execute::RegisterByte GetByteCodeRegister(Token::RegisterType const v) + { + switch(v) + { + case Token::RegisterType::A: + return Execute::RegisterByte::A; + + case Token::RegisterType::B: + return Execute::RegisterByte::B; + + case Token::RegisterType::C: + return Execute::RegisterByte::C; + + case Token::RegisterType::D: + return Execute::RegisterByte::D; + + default: + throw std::runtime_error("Unhandled register type in GetByteCodeRegister"); + } + } + + void Compiler::InsertAsBytes( + Token::Token const & token, + std::vector & bytes) + { + switch(token.type) + { + case Token::TokenType::ImmediateInteger: + bytes.push_back(static_cast(Execute::InstructionByte::IMMEDIATE_INTEGER)); + { + int value = std::get(token.data); + auto const insertionIndex = bytes.size(); + bytes.resize(bytes.size() + 4); + Utils::Bytes::Write(value, bytes, insertionIndex); + } + break; + + case Token::TokenType::Operand: + { + switch(std::get(token.data)) + { + case Token::OperandType::AddInteger: + bytes.push_back(static_cast(Execute::InstructionByte::ADD_INTEGER)); + break; + + case Token::OperandType::SubtractInteger: + bytes.push_back(static_cast(Execute::InstructionByte::SUBTRACT_INTEGER)); + break; + + case Token::OperandType::DivideInteger: + bytes.push_back(static_cast(Execute::InstructionByte::DIVIDE_INTEGER)); + break; + + case Token::OperandType::MultiplyInteger: + bytes.push_back(static_cast(Execute::InstructionByte::MULTIPLY_INTEGER)); + break; + + case Token::OperandType::ShiftIntegerLeft: + bytes.push_back(static_cast(Execute::InstructionByte::SHIFT_LEFT_INTEGER)); + break; + + case Token::OperandType::ShiftIntegerRight: + bytes.push_back(static_cast(Execute::InstructionByte::SHIFT_RIGHT_INTEGER)); + break; + + case Token::OperandType::LessThanInteger: + bytes.push_back(static_cast(Execute::InstructionByte::LESS_THAN_INTEGER)); + break; + + case Token::OperandType::GreaterThanInteger: + bytes.push_back(static_cast(Execute::InstructionByte::GREATER_THAN_INTEGER)); + break; + + case Token::OperandType::EqualInteger: + bytes.push_back(static_cast(Execute::InstructionByte::EQUALS_INTEGER)); + break; + + case Token::OperandType::SetInteger: + bytes.push_back(static_cast(Execute::InstructionByte::SET_INTEGER)); + break; + + case Token::OperandType::Jump: + bytes.push_back(static_cast(Execute::InstructionByte::JUMP)); + break; + + case Token::OperandType::CallFunction: + bytes.push_back(static_cast(Execute::InstructionByte::CALL)); + break; + + case Token::OperandType::Interrupt: + bytes.push_back(static_cast(Execute::InstructionByte::INTERRUPT)); + break; + + case Token::OperandType::PushInteger: + bytes.push_back(static_cast(Execute::InstructionByte::PUSH_INTEGER)); + break; + + case Token::OperandType::PopInteger: + bytes.push_back(static_cast(Execute::InstructionByte::POP_INTEGER)); + break; + + case Token::OperandType::ReturnFromFunction: + bytes.push_back(static_cast(Execute::InstructionByte::RETURN)); + break; + + case Token::OperandType::ExitProgram: + bytes.push_back(static_cast(Execute::InstructionByte::EXIT)); + break; + + break; + + default: + throw std::runtime_error("Unhandled operand type in InsertAsBytes"); + } + } + break; + + case Token::TokenType::Register: + bytes.push_back(static_cast(Execute::InstructionByte::REGISTER)); + bytes.push_back(static_cast(GetByteCodeRegister(std::get(token.data)))); + break; + + case Token::TokenType::StatementEnd: + case Token::TokenType::LabelDefinition: + // NO OP + break; + + case Token::TokenType::LabelArgument: + { + bytes.push_back(static_cast(Execute::InstructionByte::LABEL)); + auto const & label = std::get(token.data); + auto const findResult = jumpLabelLocations.find(label); + int jumpLocation = 0; + if (findResult == jumpLabelLocations.end()) + { + unresolvedJumpLabels.push_back(std::make_pair(token, bytes.size())); + } + else + { + jumpLocation = findResult->second; + } + + auto const insertionIndex = bytes.size(); + bytes.resize(bytes.size() + 4); + Utils::Bytes::Write(jumpLocation, bytes, insertionIndex); + } + break; + + case Token::TokenType::Memory: + { + bytes.push_back(static_cast(Execute::InstructionByte::MEMORY_OP)); + switch(token.valueType) + { + case Token::TokenValueType::Register: + bytes.push_back(static_cast(Execute::InstructionByte::REGISTER)); + bytes.push_back(static_cast(GetByteCodeRegister(std::get(token.data)))); + break; + + case Token::TokenValueType::Integer: + bytes.push_back(static_cast(Execute::InstructionByte::IMMEDIATE_INTEGER)); + { + auto const insertionIndex = bytes.size(); + bytes.resize(bytes.size() + 4); + Utils::Bytes::Write(std::get(token.data), bytes, insertionIndex); + } + break; + + default: + throw std::runtime_error("Unhandled value type for memory operand in InsertAsBytes"); + } + } + break; + + default: + throw std::runtime_error("Unhandled token type in InsertAsBytes"); + } + } + + bool Compiler::Compile( + std::vector const & tokens, + std::vector & bytes) + { + jumpLabelLocations.clear(); + unresolvedJumpLabels.clear(); + + enum class State + { + FindOperand, + FindArguments, + FindStatementEnd + }; + + State state = State::FindOperand; + Token::OperandType operandType; + unsigned operatorTokenIndex = 0u; + int expectedNumberOfArguments = 0; + for(std::size_t i = 0u; i < tokens.size(); ++i) + { + auto const & token = tokens[i]; + InsertAsBytes(token, bytes); + + switch(state) + { + case State::FindOperand: + switch(token.type) + { + case Token::TokenType::Operand: + operatorTokenIndex = i; + operandType = std::get(token.data); + expectedNumberOfArguments = GetRequiredNumberOfArguments(operandType); + + if (expectedNumberOfArguments < 1) + { + state = State::FindStatementEnd; + } + else + { + state = State::FindArguments; + } + break; + + case Token::TokenType::LabelDefinition: + { + auto findResult = jumpLabelLocations.find(std::get(token.data)); + if (findResult == jumpLabelLocations.end()) + { + jumpLabelLocations[std::get(token.data)] = bytes.size(); + } + else + { + throw CompilationError::CreateDuplicateLabelError(token); + } + } + break; + + case Token::TokenType::StatementEnd: + // NO OP + break; + + default: + throw CompilationError::CreateExpectedOperandError(token); + } + break; + + case State::FindArguments: + if (IsArgumentToken(token)) + { + expectedNumberOfArguments -= 1; + if (expectedNumberOfArguments < 1) + { + ValidateArguments(tokens, operatorTokenIndex); + state = State::FindStatementEnd; + } + } + else + { + // TODO Further specify this error? + throw CompilationError::CreateExpectedArgumentError(token); + } + break; + + case State::FindStatementEnd: + if (token.type != Token::TokenType::StatementEnd) + { + // TODO Further specify this error? + throw CompilationError::CreateExpectedEndOfStatementError(token); + } + else + { + InsertAsBytes( + token, + bytes); + state = State::FindOperand; + } + break; + } + } + + for(auto const & unresolved : unresolvedJumpLabels) + { + auto const & findResult = jumpLabelLocations.find(std::get(unresolved.first.data)); + if (findResult == jumpLabelLocations.end()) + { + throw CompilationError::CreateNonExistingLabelError(unresolved.first); + } + + int const jumpLocation = findResult->second; + auto const index = unresolved.second; + Utils::Bytes::Write(jumpLocation, bytes, index); + } + + return true; + } +} \ No newline at end of file diff --git a/src/compile/errors.cpp b/src/compile/errors.cpp new file mode 100644 index 0000000..2ebd449 --- /dev/null +++ b/src/compile/errors.cpp @@ -0,0 +1,75 @@ +#include +#include + +namespace Compile +{ + CompilationError::CompilationError( + std::string const & message, + Token::Token const & token) + : errorToken(token) + { + errorToken.errorMessage = message; + } + + CompilationError CompilationError::CreateExpectedArgumentError(Token::Token const & token) + { + return CompilationError("Expected an argument", token); + } + + CompilationError CompilationError::CreateExpectedLabelError(Token::Token const & token) + { + return CompilationError("Expected a label", token); + } + + CompilationError CompilationError::CreateExpectedImmediateError(Token::Token const & token) + { + return CompilationError("Expected an immediate value", token); + } + + CompilationError CompilationError::CreateExpectedImmediateOrRegisterOrMemory(Token::Token const & token) + { + return CompilationError("Expected an immediate value, a register or a memory location", token); + } + + CompilationError CompilationError::CreateExpectedRegisterError(Token::Token const & token) + { + return CompilationError("Expected a register", token); + } + + CompilationError CompilationError::CreateExpectedRegisterOrMemoryError(Token::Token const & token) + { + return CompilationError("Expected a register or a memory location", token); + } + + CompilationError CompilationError::CreateExpectedOperandError(Token::Token const & token) + { + return CompilationError("Expected an operand", token); + } + + CompilationError CompilationError::CreateTooManyArgumentsError(Token::Token const & token) + { + return CompilationError("Too many arguments for operand", token); + } + + CompilationError CompilationError::CreateTooFewArgumentsError(Token::Token const & token) + { + return CompilationError("Too few arguments for operand", token); + } + + CompilationError CompilationError::CreateExpectedEndOfStatementError(Token::Token const & token) + { + std::stringstream ss; + ss << "Expected end of statement (;), but got " << token.GetName() << " instead"; + return CompilationError(ss.str(), token); + } + + CompilationError CompilationError::CreateDuplicateLabelError(Token::Token const & token) + { + return CompilationError("Duplicate label definition", token); + } + + CompilationError CompilationError::CreateNonExistingLabelError(Token::Token const & token) + { + return CompilationError("Jumping to non existing label", token); + } +} \ No newline at end of file diff --git a/src/configuration.cpp b/src/configuration.cpp deleted file mode 100644 index 273cb10..0000000 --- a/src/configuration.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include - -void Configuration::PrepareMemory(std::vector & memory) const -{ - memory.resize(memorySize); - - for(auto const & pair : strings) - { - for(std::size_t i = 0; i < pair.second.size(); ++i) - { - auto const index = i + pair.first; - if (index >= memory.size()) - { - break; - } - - memory[index] = pair.second[i]; - } - } -} - -Configuration::Configuration() - : memorySize(1024) -{ -} \ No newline at end of file diff --git a/src/execute/argumentvalue.cpp b/src/execute/argumentvalue.cpp new file mode 100644 index 0000000..bd3cb36 --- /dev/null +++ b/src/execute/argumentvalue.cpp @@ -0,0 +1,144 @@ +#include +#include + +namespace Execute +{ + int & ArgumentValue::GetRegister(State & state) const + { + switch(std::get(data)) + { + case RegisterByte::A: + return state.registers.A; + + case RegisterByte::B: + return state.registers.B; + + case RegisterByte::C: + return state.registers.C; + + case RegisterByte::D: + return state.registers.D; + + default: + throw std::runtime_error("Unhandled register byte in GetRegister"); + } + } + + std::uint8_t * ArgumentValue::GetMemory(State & state) const + { + switch (memoryValueType) + { + case ArgumentType::Immediate: + return &(state.memory[std::get(data)]); + + case ArgumentType::Register: + return &(state.memory[GetRegister(state)]); + + default: + throw std::runtime_error("Unhandled argument type in GetMemory"); + } + } + + void ArgumentValue::Write(int const value, State & state) const + { + if (type == ArgumentType::Immediate) + { + throw AttemptedWriteToImmediate(state.registers.programCounter); + } + + switch(type) + { + case ArgumentType::Memory: + { + auto * ptr = GetMemory(state); + *ptr = value & 0xFF; + *(++ptr) = (value >> 8) & 0xFF; + *(++ptr) = (value >> 16) & 0xFF; + *(++ptr) = (value >> 24) & 0xFF; + } + break; + + case ArgumentType::Register: + GetRegister(state) = value; + break; + + default: + throw std::runtime_error("Unhandled argument type in Write"); + } + } + + int ArgumentValue::Read(State & state) const + { + switch(type) + { + case ArgumentType::Immediate: + return std::get(data); + + case ArgumentType::Memory: + { + int result = 0; + auto * ptr = GetMemory(state); + result |= static_cast(*ptr); + result |= static_cast(*(++ptr)) << 8; + result |= static_cast(*(++ptr)) << 16; + result |= static_cast(*(++ptr)) << 24; + } + break; + + case ArgumentType::Register: + return GetRegister(state); + + default: + throw std::runtime_error("Unhandled argument type in Read"); + } + } + + // Returns the size of the argument in bytes + std::size_t ArgumentValue::Parse( + std::vector const & memory, + std::size_t const pos) + { + InstructionByte const valueByte = static_cast(memory[pos]); + switch(valueByte) + { + case InstructionByte::IMMEDIATE_INTEGER: + case InstructionByte::LABEL: + type = ArgumentType::Immediate; + data = Utils::Bytes::Read(memory, pos + 1); + return 5; + + case InstructionByte::REGISTER: + type = ArgumentType::Register; + data = static_cast(memory[pos + 1]); + return 2; + + case InstructionByte::MEMORY_OP: + { + type = ArgumentType::Memory; + InstructionByte const memoryArgTypeByte = static_cast(memory[pos + 1]); + switch(memoryArgTypeByte) + { + case InstructionByte::IMMEDIATE_INTEGER: + memoryValueType = ArgumentType::Immediate; + data = Utils::Bytes::Read(memory, pos + 2); + return 6; + + case InstructionByte::REGISTER: + memoryValueType = ArgumentType::Register; + data = static_cast(memory[pos + 2]); + return 3; + + default: + // TODO throw more specific error? + throw NonArgumentByte(pos); + } + } + break; + + default: + throw NonArgumentByte(pos); + } + + throw std::runtime_error("Reached end of function in Parse"); + } +} \ No newline at end of file diff --git a/src/execute/error.cpp b/src/execute/error.cpp index 4781bc0..674e7fe 100644 --- a/src/execute/error.cpp +++ b/src/execute/error.cpp @@ -12,49 +12,54 @@ namespace Execute { } - RuntimeError::RuntimeError(std::string const & what) - : message(what) + RuntimeError::RuntimeError( + std::string const & what, + std::size_t const _byteLocation) + : message(what), + byteLocation(_byteLocation) { } - StackUnderflow::StackUnderflow() - : RuntimeError("Stack underflow error") + InterruptIndexOutOfRange::InterruptIndexOutOfRange(std::size_t const location, int const index) + : RuntimeError("", location) { - } - - StackOverflow::StackOverflow() - : RuntimeError("Stack overflow error") - { - } - - MissingLabel::MissingLabel(std::string const & label) - { - message = "Missing jump/function label \""; - message += label; - message += '"'; - } - - InterruptIndexOutOfRange::InterruptIndexOutOfRange(int const index) - { - message = "Interrupt index "; + message = "Interrupt at byte "; + message += std::to_string(location); + message += " with index "; message += std::to_string(index); message += " is out of range"; } - OutOfMemory::OutOfMemory(int const memoryLocation, int const memorySize) + AttemptedWriteToImmediate::AttemptedWriteToImmediate(std::size_t const location) + : RuntimeError("", location) { - message = "Attempted interaction at memory location "; - message += std::to_string(memoryLocation); - message += " with size "; - message += std::to_string(memorySize); - message += " failed"; + message = "Instruction at "; + message += std::to_string(location); + message += " attempted to write to an immediate value"; } - namespace Internal + NonExecutableInstruction::NonExecutableInstruction(std::size_t const location) + : RuntimeError("", location) { - BadValueType::BadValueType() - : RuntimeError("Internal error: bad value type") - { - } + message = "Attempted to execute byte at "; + message += std::to_string(location); + message += " which is not an instruction byte"; + } + + NonArgumentByte::NonArgumentByte(std::size_t const location) + : RuntimeError("", location) + { + message = "Expected an argument byte (immediate, register or memory location) at "; + message += std::to_string(location); + } + + OutOfMemory::OutOfMemory( + std::size_t const requiredMemorySize, + std::size_t const actualMemorySize) + { + message = "Not enough memory to fit code. Actual size is "; + message += std::to_string(actualMemorySize); + message += ". Minimal required size is "; + message += std::to_string(requiredMemorySize); } } \ No newline at end of file diff --git a/src/execute/interrupts.cpp b/src/execute/interrupts.cpp index e859be4..0f6f2d5 100644 --- a/src/execute/interrupts.cpp +++ b/src/execute/interrupts.cpp @@ -1,38 +1,43 @@ #include #include #include +#include namespace Execute { - std::vector GetInterrupts() + void ExecuteInterrupt( + int const id, + Execute::Registers & registers, + std::vector & memory) { - return std::vector + switch(id) { - /* STDOUT INTERRUPTS */ - // 0 print char - [](Execute::Registers & registers, std::vector & memory) { std::putc(registers.A, stdout); }, - // 1 print decimal integer - [](Execute::Registers & registers, std::vector & memory) { std::printf("%i", registers.A); }, - // 2 print hexadecimal integer - [](Execute::Registers & registers, std::vector & memory) { std::printf("0x%x", registers.A); }, - // 3 print string from memory - [](Execute::Registers & registers, std::vector & memory) + /* STDOUT interrupts */ + case 0: + std::putc(registers.A, stdout); + break; + + case 1: + std::printf("%i", registers.A); + break; + + case 2: + std::printf("0x%x", registers.A); + break; + + case 3: { unsigned const end = registers.A + registers.B; - if (end >= memory.size()) - { - throw OutOfMemory(registers.A, registers.B); - } - + // TODO handle out of bounds for(unsigned i = registers.A; i < end; ++i) { std::putc(memory[i], stdout); } - }, + } + break; - /* STDIN INTERRUPTS */ - // 4 get char from STDIN - [](Execute::Registers & registers, std::vector & memory) + /* STDIN interrupts */ + case 10: { registers.A = std::getchar(); if (registers.A == '\n') @@ -41,9 +46,10 @@ namespace Execute } while(std::getchar() != '\n'); - }, - // 5 get string from STDIN - [](Execute::Registers & registers, std::vector & memory) + } + break; + + case 11: { if (registers.B <= 0) { @@ -77,7 +83,11 @@ namespace Execute { while(std::getchar() != '\n'); } - }, - }; + } + break; + + default: + throw InterruptIndexOutOfRange(registers.programCounter, id); + } } } \ No newline at end of file diff --git a/src/execute/state.cpp b/src/execute/state.cpp new file mode 100644 index 0000000..2ee45be --- /dev/null +++ b/src/execute/state.cpp @@ -0,0 +1,28 @@ +#include + +namespace Execute +{ + void State::PushToStack(int const value) + { + memory[registers.stackPointer] = value & 0xFF; + memory[registers.stackPointer + 1] = (value >> 8) & 0xFF; + memory[registers.stackPointer + 2] = (value >> 16) & 0xFF; + memory[registers.stackPointer + 3] = (value >> 24) & 0xFF; + + registers.stackPointer += 4; + + return; + } + + int State::PopFromStack() + { + int value = static_cast(memory[registers.stackPointer - 1]) << 24; + value |= static_cast(memory[registers.stackPointer - 2]) << 16; + value |= static_cast(memory[registers.stackPointer - 3]) << 8; + value |= static_cast(memory[registers.stackPointer - 4]); + + registers.stackPointer -= 4; + + return value; + } +} \ No newline at end of file diff --git a/src/execute/virtualmachine.cpp b/src/execute/virtualmachine.cpp index 5324d7b..5a46614 100644 --- a/src/execute/virtualmachine.cpp +++ b/src/execute/virtualmachine.cpp @@ -1,32 +1,399 @@ -#include #include +#include +#include +#include +#include +#include namespace Execute { - void VirtualMachine::Step() + std::size_t GetArguments( + InstructionByte const instruction, + std::array & arguments, + std::vector const & memory, + std::size_t const memoryPos) { - state.nextStatement = state.currentStatement + 1u; - - try + std::size_t expectedNumberOfArguments = 0; + switch(instruction) { - codePtr->statements[state.currentStatement]->Execute(flags, state, registers); + case InstructionByte::JUMP: + case InstructionByte::INTERRUPT: + case InstructionByte::CALL: + case InstructionByte::POP_INTEGER: + case InstructionByte::PUSH_INTEGER: + expectedNumberOfArguments = 1; + break; + + case InstructionByte::SET_INTEGER: + case InstructionByte::LESS_THAN_INTEGER: + case InstructionByte::GREATER_THAN_INTEGER: + case InstructionByte::EQUALS_INTEGER: + expectedNumberOfArguments = 2; + break; + + case InstructionByte::ADD_INTEGER: + case InstructionByte::SUBTRACT_INTEGER: + case InstructionByte::DIVIDE_INTEGER: + case InstructionByte::MULTIPLY_INTEGER: + case InstructionByte::SHIFT_LEFT_INTEGER: + case InstructionByte::SHIFT_RIGHT_INTEGER: + expectedNumberOfArguments = 3; + break; + + default: + throw std::runtime_error("No instruction length set for instruction byte"); } - catch(RuntimeError & ex) - { - state.terminated = true; - std::puts("\nA fatal error occurred and execution has been halted:"); - std::printf("%s\n", ex.GetMessage().c_str()); + std::size_t memoryOffset = memoryPos; + for(std::size_t i = 0; i < expectedNumberOfArguments; ++i) + { + memoryOffset += arguments[i].Parse(memory, memoryOffset); + } + + return memoryOffset - memoryPos; + } + + void VirtualMachine::DoArithmatic( + InstructionByte const instruction, + std::array & arguments) + { + switch (instruction) + { + case InstructionByte::ADD_INTEGER: + arguments[2].Write(arguments[0].Read(state) + arguments[1].Read(state), state); + break; + + case InstructionByte::SUBTRACT_INTEGER: + arguments[2].Write(arguments[0].Read(state) - arguments[1].Read(state), state); + break; + + case InstructionByte::DIVIDE_INTEGER: + arguments[2].Write(arguments[0].Read(state) / arguments[1].Read(state), state); + break; + + case InstructionByte::MULTIPLY_INTEGER: + arguments[2].Write(arguments[0].Read(state) * arguments[1].Read(state), state); + break; + + case InstructionByte::SHIFT_LEFT_INTEGER: + arguments[2].Write(arguments[0].Read(state) >> arguments[1].Read(state), state); + break; + + case InstructionByte::SHIFT_RIGHT_INTEGER: + arguments[2].Write(arguments[0].Read(state) << arguments[1].Read(state), state); + break; + + default: + throw std::runtime_error("Unhandled instruction bytr in DoArithmatic"); + } + } + + void VirtualMachine::SetInteger(std::array & arguments) + { + arguments[0].Write(arguments[1].Read(state), state); + } + + void VirtualMachine::ExecuteJump(std::array & arguments) + { + state.registers.programCounter = arguments[0].Read(state); + } + + void VirtualMachine::ExecuteInterrupt(std::array & arguments) + { + int const interruptNo = arguments[0].Read(state); + Execute::ExecuteInterrupt(interruptNo, state.registers, state.memory); + } + + void VirtualMachine::ExecuteCall( + std::array & arguments, + std::size_t const returnByte) + { + state.PushToStack(returnByte); + state.registers.programCounter = arguments[0].Read(state); + } + + void VirtualMachine::ExecuteReturn() + { + int const returnByte = state.PopFromStack(); + state.registers.programCounter = returnByte; + } + + void VirtualMachine::DoBooleanLogic( + InstructionByte const instruction, + std::array & arguments, + std::size_t const nextInstruction) + { + bool executeNextInstruction = false; + switch(instruction) + { + case InstructionByte::LESS_THAN_INTEGER: + executeNextInstruction = arguments[0].Read(state) < arguments[1].Read(state); + break; + + case InstructionByte::GREATER_THAN_INTEGER: + executeNextInstruction = arguments[0].Read(state) > arguments[1].Read(state); + break; + + case InstructionByte::EQUALS_INTEGER: + executeNextInstruction = arguments[0].Read(state) == arguments[1].Read(state); + break; + + default: + throw std::runtime_error("Unhandled instruction byte for boolean logic"); + } + + if (executeNextInstruction) + { + state.registers.programCounter = nextInstruction; return; } - state.currentStatement = state.nextStatement; - if (state.currentStatement >= codePtr->statements.size()) + auto const argumentOffset = GetArguments( + static_cast(state.memory[nextInstruction]), + arguments, + state.memory, + nextInstruction + 1); + state.registers.programCounter = nextInstruction + 1 + argumentOffset; + } + + void VirtualMachine::ExecutePop(std::array & arguments) + { + arguments[0].Write(state.PopFromStack(), state); + } + + void VirtualMachine::ExecutePush(std::array & arguments) + { + state.PushToStack(arguments[0].Read(state)); + } + + void VirtualMachine::Step() + { + // Default to 1 byte (= 1 instruction) + std::size_t programCounterIncrement = 1; + std::array arguments; + InstructionByte const instruction = + static_cast(state.memory[state.registers.programCounter]); + switch(instruction) { + case InstructionByte::ADD_INTEGER: + case InstructionByte::SUBTRACT_INTEGER: + case InstructionByte::DIVIDE_INTEGER: + case InstructionByte::MULTIPLY_INTEGER: + case InstructionByte::SHIFT_LEFT_INTEGER: + case InstructionByte::SHIFT_RIGHT_INTEGER: + programCounterIncrement += GetArguments( + instruction, + arguments, + state.memory, + state.registers.programCounter + 1); + DoArithmatic(instruction, arguments); + state.registers.programCounter += programCounterIncrement; + break; + + case InstructionByte::SET_INTEGER: + programCounterIncrement += GetArguments( + instruction, + arguments, + state.memory, + state.registers.programCounter + 1); + SetInteger(arguments); + state.registers.programCounter += programCounterIncrement; + break; + + case InstructionByte::JUMP: + GetArguments( + instruction, + arguments, + state.memory, + state.registers.programCounter + 1); + ExecuteJump(arguments); + break; + + case InstructionByte::INTERRUPT: + programCounterIncrement += GetArguments( + instruction, + arguments, + state.memory, + state.registers.programCounter + 1); + ExecuteInterrupt(arguments); + state.registers.programCounter += programCounterIncrement; + break; + + case InstructionByte::CALL: + programCounterIncrement += GetArguments( + instruction, + arguments, + state.memory, + state.registers.programCounter + 1); + ExecuteCall(arguments, state.registers.programCounter + programCounterIncrement); + break; + + case InstructionByte::RETURN: + ExecuteReturn(); + break; + + case InstructionByte::EXIT: state.terminated = true; + return; + + case InstructionByte::LESS_THAN_INTEGER: + case InstructionByte::GREATER_THAN_INTEGER: + case InstructionByte::EQUALS_INTEGER: + programCounterIncrement += GetArguments( + instruction, + arguments, + state.memory, + state.registers.programCounter + 1); + DoBooleanLogic( + instruction, + arguments, + state.registers.programCounter + programCounterIncrement); + break; + + case InstructionByte::POP_INTEGER: + programCounterIncrement += GetArguments( + instruction, + arguments, + state.memory, + state.registers.programCounter + 1); + ExecutePop(arguments); + state.registers.programCounter += programCounterIncrement; + break; + + case InstructionByte::PUSH_INTEGER: + programCounterIncrement += GetArguments( + instruction, + arguments, + state.memory, + state.registers.programCounter + 1); + ExecutePush(arguments); + state.registers.programCounter += programCounterIncrement; + break; + + case InstructionByte::IMMEDIATE_INTEGER: + case InstructionByte::REGISTER: + case InstructionByte::MEMORY_OP: + case InstructionByte::LABEL: + default: + throw NonExecutableInstruction(state.registers.programCounter); + break; } } + void PrintOperand(std::size_t const index, std::string const name) + { + std::printf("\n%04lu %s", index, name.c_str()); + } + + void PrintBytes(std::vector const & byteCode) + { + for(std::size_t i = 0; i < byteCode.size(); ++i) + { + InstructionByte const id = static_cast(byteCode[i]); + switch(id) + { + case InstructionByte::ADD_INTEGER: + PrintOperand(i, "addi"); + break; + + case InstructionByte::SUBTRACT_INTEGER: + PrintOperand(i, "subi"); + break; + + case InstructionByte::DIVIDE_INTEGER: + PrintOperand(i, "divi"); + break; + + case InstructionByte::MULTIPLY_INTEGER: + PrintOperand(i, "muli"); + break; + + case InstructionByte::SHIFT_LEFT_INTEGER: + PrintOperand(i, "shli"); + break; + + case InstructionByte::SHIFT_RIGHT_INTEGER: + PrintOperand(i, "shri"); + break; + + case InstructionByte::SET_INTEGER: + PrintOperand(i, "sti"); + break; + + case InstructionByte::JUMP: + PrintOperand(i, "jmp"); + break; + + case InstructionByte::INTERRUPT: + PrintOperand(i, "int"); + break; + + case InstructionByte::CALL: + PrintOperand(i, "call"); + break; + + case InstructionByte::RETURN: + PrintOperand(i, "ret"); + break; + + case InstructionByte::EXIT: + PrintOperand(i, "exit"); + break; + + case InstructionByte::LESS_THAN_INTEGER: + PrintOperand(i, "lti"); + break; + + case InstructionByte::GREATER_THAN_INTEGER: + PrintOperand(i, "gti"); + break; + + case InstructionByte::EQUALS_INTEGER: + PrintOperand(i, "eqi"); + break; + + case InstructionByte::POP_INTEGER: + PrintOperand(i, "popi"); + break; + + case InstructionByte::PUSH_INTEGER: + PrintOperand(i, "pushi"); + break; + + case InstructionByte::IMMEDIATE_INTEGER: + std::printf("$%i", Utils::Bytes::Read(byteCode, i + 1)); + i += 4u; + break; + + case InstructionByte::REGISTER: + { + std::string registerName {"A"}; + registerName[0] += byteCode[i + 1] - 1; + std::printf("%%%s", registerName.c_str()); + ++i; + } + break; + + case InstructionByte::MEMORY_OP: + std::printf("[]"); + break; + + case InstructionByte::LABEL: + std::printf("%i:", Utils::Bytes::Read(byteCode, i + 1)); + i += 4u; + break; + + default: + std::printf("UNKNOWN"); + break; + } + + std::putc(' ', stdout); + } + + std::puts(""); + } + void VirtualMachine::Run() { while(!IsTerminated()) @@ -43,29 +410,38 @@ namespace Execute } } - void VirtualMachine::LoadConfiguration(Configuration const & c) + void VirtualMachine::SetMemorySize(std::size_t const size) { - c.PrepareMemory(state.memory); + state.memory.resize(size); } - void VirtualMachine::LoadCode(std::unique_ptr code) + void VirtualMachine::LoadCode( + std::vector const & byteCode, + bool const printDecodedBytes) { - codePtr = std::move(code); - state.labelStatementIndice = &(codePtr->labelStatementIndice); + if (printDecodedBytes) + { + PrintBytes(byteCode); + } + + if (state.memory.size() < byteCode.size()) + { + throw OutOfMemory(byteCode.size(), state.memory.size()); + } + + for (std::size_t i = 0; i < byteCode.size(); ++i) + { + state.memory[i] = byteCode[i]; + } + + state.registers.stackPointer = byteCode.size(); } - Flags const & VirtualMachine::GetFlags() const { return flags; } - Registers const & VirtualMachine::GetRegisters() const { return registers; } State const & VirtualMachine::GetState() const { return state; } - Interpret::Statement const * const VirtualMachine::GetCurrentStatement() const + Execute::InstructionByte VirtualMachine::GetCurrentInstruction() const { - return codePtr->statements[state.currentStatement].get(); + return static_cast(state.memory[state.registers.programCounter]); } bool VirtualMachine::IsTerminated() const { return state.terminated; } - - VirtualMachine::VirtualMachine() - { - state.interrupts = GetInterrupts(); - } } \ No newline at end of file diff --git a/src/interpret/errors.cpp b/src/interpret/errors.cpp deleted file mode 100644 index 1c07a0f..0000000 --- a/src/interpret/errors.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include - -namespace Interpret -{ - InterpretationError::InterpretationError(Token::Token const & token, std::string const & msg) - : errorToken(token) - { - errorToken.errorMessage = msg; - } - - ExpectedArgument::ExpectedArgument(Token::Token const & token) - : InterpretationError(token, "Expected an argument") - { - } - - ExpectedLabel::ExpectedLabel(Token::Token const & token) - : InterpretationError(token, "Expected a label") - { - } - - ExpectedValue::ExpectedValue(Token::Token const & token) - : InterpretationError(token, "Expected an immediate value, a register or a memory location") - { - } - - ExpectedImmediate::ExpectedImmediate(Token::Token const & token) - : InterpretationError(token, "Expected an immediate value") - { - } - - ExpectedImmediateOrMemory::ExpectedImmediateOrMemory(Token::Token const & token) - : InterpretationError(token, "Expected an immediate value or a memory location") - { - } - - ExpectedRegister::ExpectedRegister(Token::Token const & token) - : InterpretationError(token, "Expected a register") - { - } - - ExpectedRegisterOrMemory::ExpectedRegisterOrMemory(Token::Token const & token) - : InterpretationError(token, "Expected a register or a memory location") - { - } - - ExpectedOperand::ExpectedOperand(Token::Token const & token) - : InterpretationError(token, "Expected an operand") - { - } - - TooManyArguments::TooManyArguments(Token::Token const & token) - : InterpretationError(token, "Too many arguments for operand") - { - } - - TooFewArguments::TooFewArguments(Token::Token const & token) - : InterpretationError(token, "Too few arguments for operand") - { - } - - MissingEndOfStatment::MissingEndOfStatment(Token::Token const & token) - : InterpretationError(token, "Missing end of line terminator (;)") - { - } - - namespace Internal - { - BadTokenForValue::BadTokenForValue(Token::Token const & token) - : InterpretationError(token, "Internal error when converting token to value") - { - } - } -} \ No newline at end of file diff --git a/src/interpret/interpreter.cpp b/src/interpret/interpreter.cpp deleted file mode 100644 index 5a59332..0000000 --- a/src/interpret/interpreter.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include -#include - -namespace Interpret -{ - bool IsArgumentToken(Token::Token const & t) - { - return - t.type == Token::TokenType::ImmediateInteger || - t.type == Token::TokenType::Register || - t.type == Token::TokenType::Label || - t.type == Token::TokenType::Memory; - } - - void Interpreter::Interpret(std::vector const & tokens, Code & code) - { - enum class InterpreterState - { - FindOperand, - FindArguments, - FindStatementEnd - }; - - InterpreterState state = InterpreterState::FindOperand; - unsigned operatorTokenIndex = 0u; - int expectedNumberOfArguments = 0; - for(unsigned i = 0u; i < tokens.size(); ++i) - { - auto const & token = tokens[i]; - switch(state) - { - case InterpreterState::FindOperand: - if (token.type == Token::TokenType::Operand) - { - operatorTokenIndex = i; - expectedNumberOfArguments = GetRequiredNumberOfArguments(std::get(token.data)); - if (expectedNumberOfArguments < 1) - { - state = InterpreterState::FindStatementEnd; - } - else - { - state = InterpreterState::FindArguments; - } - } - else if (token.type == Token::TokenType::Label) - { - code.labelStatementIndice[std::get(token.data)] = code.statements.size(); - } - else if (token.type != Token::TokenType::StatementEnd) - { - throw ExpectedOperand(token); - } - break; - - case InterpreterState::FindArguments: - if (IsArgumentToken(token)) - { - expectedNumberOfArguments -= 1; - if (expectedNumberOfArguments < 1) - { - state = InterpreterState::FindStatementEnd; - } - } - else - { - throw ExpectedArgument(token); - } - break; - - case InterpreterState::FindStatementEnd: - if (token.type != Token::TokenType::StatementEnd) - { - throw MissingEndOfStatment(token); - } - else - { - code.statements.emplace_back(ExtractStatement(operatorTokenIndex, tokens)); - state = InterpreterState::FindOperand; - } - break; - } - } - } -} \ No newline at end of file diff --git a/src/interpret/operanddefinitions.cpp b/src/interpret/operanddefinitions.cpp deleted file mode 100644 index e09b13c..0000000 --- a/src/interpret/operanddefinitions.cpp +++ /dev/null @@ -1,327 +0,0 @@ -#include -#include - -namespace Interpret -{ - Value GetImmediateArgument(unsigned const index, std::vector const & tokens) - { - auto const & token = tokens[index]; - if (token.type == Token::TokenType::ImmediateInteger) - { - Value v; - v.CreateFromToken(token); - - return v; - } - - throw ExpectedImmediate(token); - } - - Value GetRegisterArgument(unsigned const index, std::vector const & tokens) - { - auto const & token = tokens[index]; - if (token.type == Token::TokenType::Register) - { - Value v; - v.CreateFromToken(token); - - return v; - } - - throw ExpectedRegister(token); - } - - Value GetRegisterOrMemoryArgument(unsigned const index, std::vector const & tokens) - { - auto const & token = tokens[index]; - if (token.type == Token::TokenType::Register || token.type == Token::TokenType::Memory) - { - Value v; - v.CreateFromToken(token); - - return v; - } - - throw ExpectedRegisterOrMemory(token); - } - - Value GetValueArgument(unsigned const index, std::vector const & tokens) - { - auto const & token = tokens[index]; - if (token.type == Token::TokenType::ImmediateInteger || token.type == Token::TokenType::Register || token.type == Token::TokenType::Memory) - { - Value v; - v.CreateFromToken(token); - - return v; - } - - throw ExpectedValue(token); - } - - void AddArithmeticArguments(ArithmeticStatement & statement, unsigned const operandIndex, std::vector const & tokens) - { - statement.firstArgument = GetValueArgument(operandIndex + 1u, tokens); - statement.secondArgument = GetValueArgument(operandIndex + 2u, tokens); - statement.thirdArgument = GetRegisterOrMemoryArgument(operandIndex + 3u, tokens); - } - - void AddLogicArguments(ControlFlowStatement & statement, unsigned const operandIndex, std::vector const & tokens) - { - statement.firstArgument = GetValueArgument(operandIndex + 1u, tokens); - statement.secondArgument = GetValueArgument(operandIndex + 2u, tokens); - } - - std::unique_ptr ExtractStatement(unsigned const operandIndex, std::vector const & tokens) - { - auto const & token = tokens[operandIndex]; - switch(std::get(token.data)) - { - case Token::OperandType::AddInteger: - { - auto statement = std::make_unique(); - statement->function = [](int argument1, int argument2, int & argument3) - { - argument3 = argument1 + argument2; - }; - AddArithmeticArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::SubtractInteger: - { - auto statement = std::make_unique(); - statement->function = [](int argument1, int argument2, int & argument3) - { - argument3 = argument1 - argument2; - }; - AddArithmeticArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::DivideInteger: - { - auto statement = std::make_unique(); - statement->function = [](int argument1, int argument2, int & argument3) - { - argument3 = argument1 / argument2; - }; - AddArithmeticArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::MultiplyInteger: - { - auto statement = std::make_unique(); - statement->function = [](int argument1, int argument2, int & argument3) - { - argument3 = argument1 * argument2; - }; - AddArithmeticArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::ShiftIntegerLeft: - { - auto statement = std::make_unique(); - statement->function = [](int argument1, int argument2, int & argument3) - { - argument3 = argument1 << argument2; - }; - AddArithmeticArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::ShiftIntegerRight: - { - auto statement = std::make_unique(); - statement->function = [](int argument1, int argument2, int & argument3) - { - argument3 = argument1 >> argument2; - }; - AddArithmeticArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::Jump: - { - auto labelToken = tokens[operandIndex + 1u]; - if (labelToken.type != Token::TokenType::Label) - { - throw ExpectedLabel(labelToken); - } - - return std::make_unique(std::get(labelToken.data)); - } - - case Token::OperandType::CallFunction: - { - auto labelToken = tokens[operandIndex + 1u]; - if (labelToken.type != Token::TokenType::Label) - { - throw ExpectedLabel(labelToken); - } - - return std::make_unique(std::get(labelToken.data)); - } - - case Token::OperandType::ReturnFromFunction: - { - return std::make_unique(); - } - - case Token::OperandType::ExitProgram: - { - return std::make_unique(); - } - - case Token::OperandType::LessThanInteger: - { - auto statement = std::make_unique(); - statement->function = [](Execute::State & state, int argument1, int argument2) - { - if (argument1 < argument2) - { - state.nextStatement = state.currentStatement + 1u; - } - else - { - state.nextStatement = state.currentStatement + 2u; - } - }; - AddLogicArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::GreaterThanInteger: - { - auto statement = std::make_unique(); - statement->function = [](Execute::State & state, int argument1, int argument2) - { - if (argument1 > argument2) - { - state.nextStatement = state.currentStatement + 1u; - } - else - { - state.nextStatement = state.currentStatement + 2u; - } - }; - AddLogicArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::EqualInteger: - { - auto statement = std::make_unique(); - statement->function = [](Execute::State & state, int argument1, int argument2) - { - if (argument1 == argument2) - { - state.nextStatement = state.currentStatement + 1u; - } - else - { - state.nextStatement = state.currentStatement + 2u; - } - }; - AddLogicArguments(*statement, operandIndex, tokens); - return statement; - } - - case Token::OperandType::SetInteger: - { - auto statement = std::make_unique(); - statement->firstArgument = GetRegisterOrMemoryArgument(operandIndex + 1u, tokens); - statement->secondArgument = GetValueArgument(operandIndex + 2u, tokens); - - return statement; - } - - case Token::OperandType::Interrupt: - { - auto statement = std::make_unique(); - statement->firstArgument = GetValueArgument(operandIndex + 1u, tokens); - - return statement; - } - - case Token::OperandType::PopInteger: - { - auto statement = std::make_unique(); - statement->firstArgument = GetRegisterArgument(operandIndex + 1u, tokens); - - return statement; - } - - case Token::OperandType::PushInteger: - { - auto statement = std::make_unique(); - statement->firstArgument = GetValueArgument(operandIndex + 1u, tokens); - - return statement; - } - - default: - { - auto statement = std::make_unique(); - - // TODO throw error? - statement->function = [](Execute::Flags & flags, Execute::Registers & registers) - { - std::puts("ExtractStatement: Extracted unhandled operator type"); - }; - return statement; - } - } - } - - std::tuple ExtractDeclaration(unsigned const operatorIndex, std::vector const & tokens) - { - if (tokens[operatorIndex + 1u].type != Token::TokenType::Label) - { - throw ExpectedLabel(tokens[operatorIndex + 1u]); - } - if (tokens[operatorIndex + 2u].type != Token::TokenType::ImmediateInteger) - { - throw ExpectedImmediate(tokens[operatorIndex + 2u]); - } - - auto const label = std::get(tokens[operatorIndex + 1u].data); - auto const value = std::get(tokens[operatorIndex + 2u].data); - return std::make_tuple(label, value); - } - - int GetRequiredNumberOfArguments(Token::OperandType const type) - { - switch (type) - { - case Token::OperandType::AddInteger: - case Token::OperandType::SubtractInteger: - case Token::OperandType::DivideInteger: - case Token::OperandType::MultiplyInteger: - case Token::OperandType::ShiftIntegerLeft: - case Token::OperandType::ShiftIntegerRight: - return 3; - - case Token::OperandType::LessThanInteger: - case Token::OperandType::GreaterThanInteger: - case Token::OperandType::EqualInteger: - case Token::OperandType::SetInteger: - return 2; - - case Token::OperandType::Jump: - case Token::OperandType::CallFunction: - case Token::OperandType::Interrupt: - case Token::OperandType::PushInteger: - case Token::OperandType::PopInteger: - return 1; - - default: - std::printf("WARNING: returning default argument length of 0 for operand type %i\n", static_cast(type)); - case Token::OperandType::ReturnFromFunction: - case Token::OperandType::ExitProgram: - return 0; - } - } -} \ No newline at end of file diff --git a/src/interpret/statement.cpp b/src/interpret/statement.cpp deleted file mode 100644 index e6db243..0000000 --- a/src/interpret/statement.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include - -namespace Interpret -{ - void NoArgumentStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - function(flags, registers); - } - - void OneArgumentStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - function(flags, firstArgument.GetValue(state, registers)); - } - - void ControlFlowStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - function(state, firstArgument.GetValue(state, registers), secondArgument.GetValue(state, registers)); - } - - void ArithmeticStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - function(firstArgument.GetValue(state, registers), secondArgument.GetValue(state, registers), thirdArgument.GetValue(state, registers)); - } - - void JumpStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - auto const & elem = state.labelStatementIndice->find(label); - if (elem != state.labelStatementIndice->end()) - { - state.nextStatement = elem->second; - } - else - { - throw Execute::MissingLabel(label); - } - } - - JumpStatement::JumpStatement(std::string const & _label) - : label(_label) - { - } - - void FunctionCallStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - auto const & elem = state.labelStatementIndice->find(label); - if (elem == state.labelStatementIndice->end()) - { - throw Execute::MissingLabel(label); - } - - if ((state.memory.size() - state.stackPointer) < sizeof(unsigned)) - { - throw Execute::StackOverflow(); - } - - *(reinterpret_cast(state.memory.data() + state.stackPointer + (sizeof(unsigned) - 1))) = state.nextStatement; - - state.stackPointer += sizeof(int); - state.nextStatement = elem->second; - } - - FunctionCallStatement::FunctionCallStatement(std::string const & _label) - : label(_label) - { - } - - void ReturnFromFunctionStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - if (state.stackPointer < sizeof(unsigned)) - { - throw Execute::StackUnderflow(); - } - - state.nextStatement = *(reinterpret_cast(state.memory.data() + (state.stackPointer - 1u))); - state.stackPointer -= sizeof(unsigned); - } - - void ExitProgramStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - state.terminated = true; - } - - void SetStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - firstArgument.GetValue(state, registers) = secondArgument.GetValue(state, registers); - } - - void InterruptStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - auto const interruptIndex = firstArgument.GetValue(state, registers); - if (interruptIndex < 0 || static_cast(interruptIndex) > state.interrupts.size()) - { - throw Execute::InterruptIndexOutOfRange(interruptIndex); - } - - state.interrupts[interruptIndex](registers, state.memory); - } - - void PopStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - if (state.stackPointer < sizeof(int)) - { - throw Execute::StackUnderflow(); - } - - auto const value = *(reinterpret_cast(state.memory.data() + (state.stackPointer - 1u))); - firstArgument.GetValue(state, registers) = value; - - state.stackPointer -= sizeof(int); - } - - void PushStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) - { - if ((state.memory.size() - state.stackPointer) < sizeof(int)) - { - throw Execute::StackOverflow(); - } - - *(reinterpret_cast(state.memory.data() + state.stackPointer + (sizeof(int) - 1))) = firstArgument.GetValue(state, registers); - - state.stackPointer += sizeof(int); - } -} \ No newline at end of file diff --git a/src/interpret/value.cpp b/src/interpret/value.cpp deleted file mode 100644 index 4d15432..0000000 --- a/src/interpret/value.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include - -namespace Interpret -{ - int & Value::GetValue(Execute::State & state, Execute::Registers & registers) - { - switch(type) - { - case ValueType::ImmediateInteger: - return data; - - case ValueType::Register: - return registers.registers[data]; - - case ValueType::MemoryLocation: - if (dataType == ValueDataType::Register) - { - return *reinterpret_cast(state.memory.data() + registers.registers[data]); - } - else if (dataType == ValueDataType::Immediate) - { - return *reinterpret_cast(state.memory.data() + data); - } - else - { - throw Execute::Internal::BadValueType(); - } - break; - - default: - throw Execute::Internal::BadValueType(); - } - } - - void Value::CreateFromToken(Token::Token const & token) - { - switch(token.type) - { - case Token::TokenType::ImmediateInteger: - type = ValueType::ImmediateInteger; - dataType = ValueDataType::Immediate; - data = std::get(token.data); - break; - - case Token::TokenType::Register: - type = ValueType::Register; - dataType = ValueDataType::Register; - data = static_cast(std::get(token.data)); - break; - - case Token::TokenType::Memory: - type = ValueType::MemoryLocation; - if (token.valueType == Token::TokenValueType::Integer) - { - dataType = ValueDataType::Immediate; - data = std::get(token.data); - } - else if(token.valueType == Token::TokenValueType::Register) - { - dataType = ValueDataType::Register; - data = static_cast(std::get(token.data)); - } - else - { - throw Internal::BadTokenForValue(token); - } - break; - - default: - throw Internal::BadTokenForValue(token); - } - } -} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 5d25070..eea22c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,13 +6,22 @@ int main(int argc, char ** argv) { std::string inputFile; - unsigned memorySize = 1024; - bool printSubstitutions = false; - bool printTokens = false; + unsigned memorySize = 4096; + bool printSubstitutions = false, printTokens = false, printBytes = false; + bool execute = false, compile = false; + std::string outputFile("program.bin"); auto cli = ( - clipp::value("input wasm file").set(inputFile), - clipp::option("-m", "--memory-size") & clipp::value("memory size", memorySize), + clipp::value("input file").set(inputFile), + ( + clipp::required("-e", "--execute").set(execute), + clipp::option("-m", "--memory-size") & clipp::value("memory size in bytes (defaults to 4096)", memorySize), + clipp::option("-pb", "--print-bytes").set(printBytes) + ) | + ( + clipp::required("-c", "--compile").set(compile), + clipp::option("-o", "--output-file") & clipp::value("output file", outputFile) + ), clipp::option("-ps", "--print-substitutions").set(printSubstitutions), clipp::option("-pt", "--print-tokens").set(printTokens) ); @@ -35,12 +44,26 @@ int main(int argc, char ** argv) wassembler.EnableTokensLogging(); } - if (!wassembler.LoadFromFile(inputFile)) + if (execute) { - exit(1); + if (printBytes) + { + wassembler.EnableByteTranslationLogging(); + } + + if (!wassembler.CompileAndRun(inputFile)) + { + exit(1); + } } - wassembler.Run(); + if (compile) + { + if (!wassembler.CompileToFile(inputFile, outputFile)) + { + exit(1); + } + } return 0; } \ No newline at end of file diff --git a/src/token/errors.cpp b/src/token/errors.cpp index 54a924b..c244110 100644 --- a/src/token/errors.cpp +++ b/src/token/errors.cpp @@ -7,9 +7,4 @@ namespace Token errorMsg(msg) { } - - MissingEndOfString::MissingEndOfString(Token const & token) - : TokenizationError(token, "Missing string terminator (\")") - { - } } \ No newline at end of file diff --git a/src/token/token.cpp b/src/token/token.cpp index cacc5c6..8e8806e 100644 --- a/src/token/token.cpp +++ b/src/token/token.cpp @@ -86,9 +86,14 @@ namespace Token return Token(TokenType::StatementEnd, true, lineNumber, lineColumn); } - Token Token::CreateLabelToken(std::string const & string, int const lineNumber, int const lineColumn) + Token Token::CreateLabelDefinitionToken(std::string const & string, int const lineNumber, int const lineColumn) { - return Token(TokenType::Label, string, true, lineNumber, lineColumn); + return Token(TokenType::LabelDefinition, string, true, lineNumber, lineColumn); + } + + Token Token::CreateLabelArgumentToken(std::string const & string, int const lineNumber, int const lineColumn) + { + return Token(TokenType::LabelArgument, string, true, lineNumber, lineColumn); } Token Token::CreateImmediateValueToken(int const value, int const lineNumber, int const lineColumn) @@ -121,6 +126,35 @@ namespace Token return Token(TokenType::Memory, value, true, lineNumber, lineColumn); } + std::string Token::GetName() const + { + switch(type) + { + case TokenType::ImmediateInteger: + return "immediate value"; + + case TokenType::Operand: + return "operand"; + + case TokenType::Register: + return "register"; + + case TokenType::StatementEnd: + return "end of statement"; + + case TokenType::LabelDefinition: + case TokenType::LabelArgument: + return "label"; + + case TokenType::Memory: + return "memory location"; + + case TokenType::Unknown: + default: + return "UNKNOWN"; + } + } + void Token::Print() const { std::putc(' ', stdout); @@ -183,8 +217,12 @@ namespace Token std::printf("EOS"); break; - case TokenType::Label: - std::printf("LABEL=%s", std::get(data).c_str()); + case TokenType::LabelDefinition: + std::printf("LABEL_DEF=%s", std::get(data).c_str()); + break; + + case TokenType::LabelArgument: + std::printf("LABEL_ARG=%s", std::get(data).c_str()); break; case TokenType::Memory: diff --git a/src/token/tokenizer.cpp b/src/token/tokenizer.cpp index 476538f..d6ca65c 100644 --- a/src/token/tokenizer.cpp +++ b/src/token/tokenizer.cpp @@ -178,7 +178,7 @@ namespace Token case ':': // TODO check if label is an Operand? - return Token::CreateLabelToken( + return Token::CreateLabelDefinitionToken( string.substr(0, string.size() - 1), lineNumber, lineColumn); @@ -196,7 +196,7 @@ namespace Token } // Last resort: it must be a jump target - return Token::CreateLabelToken(string, lineNumber, lineColumn); + return Token::CreateLabelArgumentToken(string, lineNumber, lineColumn); } void Tokenizer::Tokenize( diff --git a/src/utils.cpp b/src/utils.cpp index 0984bfc..d786925 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -37,4 +37,28 @@ namespace Utils return src.substr(pos); } + + namespace Bytes + { + void Write( + int const value, + std::vector & vec, + std::size_t const pos) + { + vec[pos] = value & 0xFF; + vec[pos + 1] = (value >> 8) & 0xFF; + vec[pos + 2] = (value >> 16) & 0xFF; + vec[pos + 3] = (value >> 24) & 0xFF; + } + + int Read(std::vector const & vec, std::size_t const pos) + { + int value = vec[pos]; + value |= static_cast(vec[pos + 1]) << 8; + value |= static_cast(vec[pos + 2]) << 16; + value |= static_cast(vec[pos + 3]) << 24; + + return value; + } + } } \ No newline at end of file diff --git a/src/wassembler.cpp b/src/wassembler.cpp index 349cac4..fc53811 100644 --- a/src/wassembler.cpp +++ b/src/wassembler.cpp @@ -1,5 +1,7 @@ +#include +#include +#include #include -#include #include #include #include @@ -19,18 +21,13 @@ void PrintBadToken(Token::Token const & token, std::vector const & std::puts("^"); } -void PrintTokenError(Interpret::InterpretationError const & err, std::vector const & lines) -{ - PrintBadToken(err.errorToken, lines); -} - void PrintTokenError(Token::TokenizationError const & err, std::vector const & lines) { std::printf("%s ", err.errorMsg.c_str()); PrintBadToken(err.errorToken, lines); } -bool Wassembler::LoadLinesFromFile(std::string const & filePath, std::vector & lines) const +bool Wassembler::LoadTextFile(std::string const & filePath, std::vector & lines) const { std::ifstream input(filePath); if (!input.is_open()) @@ -48,7 +45,19 @@ bool Wassembler::LoadLinesFromFile(std::string const & filePath, std::vector const & lines, std::vector & tokens) const +bool Wassembler::Preprocess(std::vector & lines) const +{ + Preprocessor preprocessor; + preprocessor.process(lines); + if (printSubstitutions) + { + preprocessor.printSubstitutions(); + } + + return true; +} + +bool Wassembler::Tokenize(std::vector const & lines, std::vector & tokens) const { Token::Tokenizer tokenizer; bool tokenizationError = false; @@ -98,9 +107,64 @@ bool Wassembler::LoadTokens(std::vector const & lines, std::vector< return !(syntaxError || tokenizationError); } +bool Wassembler::CompileToBytes( + std::vector const & tokens, + std::vector const & lines, + std::vector & bytes) const +{ + Compile::Compiler compiler; + try + { + compiler.Compile(tokens, bytes); + } + catch(Compile::CompilationError & e) + { + std::printf("Semantic error "); + PrintBadToken(e.errorToken, lines); + + return false; + } + + return true; +} + +void Wassembler::ExecuteCode(std::vector const & bytes) +{ + vm.LoadCode(bytes, printTranslatedBytes); + // TODO clear memory? + vm.Run(); +} + +bool Wassembler::CompileFile( + std::string const & filePath, + std::vector & bytes) const +{ + std::vector lines; + if (!LoadTextFile(filePath, lines)) + { + std::printf("Error: Cannot open file %s for reading", filePath.c_str()); + return false; + } + + if(!Preprocess(lines)) + { + std::puts("Aborting due to preprocessor error(s)"); + return false; + } + + std::vector tokens; + if (!Tokenize(lines, tokens) || !CompileToBytes(tokens, lines, bytes)) + { + std::puts("Aborting due to syntax error(s)"); + return false; + } + + return true; +} + void Wassembler::SetMemorySize(unsigned const size) { - config.memorySize = size; + vm.SetMemorySize(size); } void Wassembler::EnableSubstitutionsLogging() @@ -113,50 +177,54 @@ void Wassembler::EnableTokensLogging() printTokens = true; } -bool Wassembler::LoadFromFile(std::string const & filePath) +void Wassembler::EnableByteTranslationLogging() +{ + printTranslatedBytes = true; +} + +bool Wassembler::CompileAndRun(std::string const & filePath) { std::vector lines; - if (!LoadLinesFromFile(filePath, lines)) + std::vector bytes; + if (!CompileFile(filePath, bytes)) { - std::printf("Error: Cannot open file %s for reading", filePath.c_str()); return false; } - Preprocessor preprocessor; - preprocessor.process(lines); - if (printSubstitutions) - { - preprocessor.printSubstitutions(); - } - - std::vector tokens; - if (!LoadTokens(lines, tokens)) - { - std::puts("Aborting due to syntax error(s)"); - return false; - } - - Interpret::Interpreter interpreter; - auto codePtr = std::make_unique(); try { - interpreter.Interpret(tokens, *codePtr); + ExecuteCode(bytes); } - catch(Interpret::InterpretationError & e) + catch (Execute::RuntimeError const & e) { - std::printf("Semantic error "); - PrintBadToken(e.errorToken, lines); - std::puts("Aborting due to semantic error(s)"); + std::puts(e.GetMessage().c_str()); + std::puts("Aborting due to runtime error(s)"); return false; } - vm.LoadCode(std::move(codePtr)); - vm.LoadConfiguration(config); - return true; } -void Wassembler::Run() +bool Wassembler::CompileToFile( + std::string const & inputFilePath, + std::string const & outputFilePath) { - vm.Run(); + std::vector bytes; + if (!CompileFile(inputFilePath, bytes)) + { + return false; + } + + std::ofstream output(outputFilePath, std::ios::binary | std::ios::trunc); + if (!output.is_open()) + { + std::printf("Error: Cannot open file %s for writing", outputFilePath.c_str()); + return false; + } + for(std::size_t i = 0; i < bytes.size(); ++i) + { + output << bytes[i]; + } + + return true; } \ No newline at end of file