diff --git a/bin/example.wasm b/bin/example.wasm index 006dea0..fd1c3c9 100644 --- a/bin/example.wasm +++ b/bin/example.wasm @@ -4,10 +4,32 @@ muli $2 %B %C; divi $2 %C %D; # Set A to zero -addi $1 $0 %A; +seti %A $0; -# Increment A until it is greater than 9 +# Loop from 0 to 10 loop: addi $1 %A %A; + +# Print the current value +int $1; +seti %B %A; +seti %A $10; # new line +int $0; +seti %A %B; + lti %A $10; -jmp loop; \ No newline at end of file +jmp loop; + +# Hello world +seti %A $72; # H +int $0; +seti %A $101; # e +int $0; +seti %A $108; # l +int $0; +int $0; +seti %A $111; # o +int $0; + +seti %A $10; # newline +int $0; \ No newline at end of file diff --git a/design.md b/design.md index e80632f..4dd9ecd 100644 --- a/design.md +++ b/design.md @@ -61,6 +61,8 @@ All symbols are reserved keywords and can therefore NOT be used as labels. - `muli` multiply the first by the second argument and store the result in the third argument - `shli` shift left the first argument by the number of positions given by the second argument and store the result in the third argument - `shri` shift right the first argument by the number of positions given by the second argument and store the result in the third argument +- `seti` set the first register argument to the second argument +- `int` calls the interrupt specified by the first (integer) argument ### Control Flow @@ -68,3 +70,9 @@ All symbols are reserved keywords and can therefore NOT be used as labels. - `lti` execute next statement if argument 1 is less than argument 2 else skip the next statement - `gti` execute next statement if argument 1 is greater than argument 2 else skip the next statement - `eqi` execute the next statement if argument 1 is equal to argument 2 else skip the next statement + +## Interupts + +- 0..9 range: + - `0` put value of register A as ASCII character on stdout + - `1` put value of register A as decimal integer on stdout \ No newline at end of file diff --git a/include/execute/interrupts.hpp b/include/execute/interrupts.hpp new file mode 100644 index 0000000..eb00aac --- /dev/null +++ b/include/execute/interrupts.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +using InterruptFn = void (*)(Execute::Registers & registers); + +namespace Execute +{ + std::vector GetInterrupts(); +} \ No newline at end of file diff --git a/include/execute/state.hpp b/include/execute/state.hpp index 32604a7..7c82b29 100644 --- a/include/execute/state.hpp +++ b/include/execute/state.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -9,7 +10,7 @@ namespace Execute unsigned currentStatement; unsigned nextStatement; std::unordered_map const & labelStatementIndice; - + std::vector const interrupts; State(std::unordered_map const & labelStatementIndice); }; diff --git a/include/interpret/errors.hpp b/include/interpret/errors.hpp index 91ed7ed..f07171d 100644 --- a/include/interpret/errors.hpp +++ b/include/interpret/errors.hpp @@ -22,6 +22,11 @@ namespace Interpret ExpectedLabel(Token::Token const & token); }; + struct ExpectedValue : public TokenError + { + ExpectedValue(Token::Token const & token); + }; + struct ExpectedImmediate : public TokenError { ExpectedImmediate(Token::Token const & token); diff --git a/include/interpret/statement.hpp b/include/interpret/statement.hpp index b7be3b1..fbad40c 100644 --- a/include/interpret/statement.hpp +++ b/include/interpret/statement.hpp @@ -54,4 +54,19 @@ namespace Interpret JumpStatement(std::string const & label); }; + + 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; + }; } \ No newline at end of file diff --git a/include/token/operandtype.hpp b/include/token/operandtype.hpp index d50bce8..da1b903 100644 --- a/include/token/operandtype.hpp +++ b/include/token/operandtype.hpp @@ -3,7 +3,7 @@ namespace Token { - enum class OperandType + enum class OperandType : int { Unknown = -1, AddInteger = 0, @@ -15,7 +15,9 @@ namespace Token Jump, LessThanInteger, GreaterThanInteger, - EqualInteger + EqualInteger, + SetInteger, + Interrupt }; OperandType GetOperandType(std::string const & op); diff --git a/src/execute/interrupts.cpp b/src/execute/interrupts.cpp new file mode 100644 index 0000000..d120366 --- /dev/null +++ b/src/execute/interrupts.cpp @@ -0,0 +1,16 @@ +#include +#include + +namespace Execute +{ + std::vector GetInterrupts() + { + return std::vector + { + // 0 print char + [](Execute::Registers & registers) { std::putc(registers.A, stdout); }, + // 1 print decimal integer + [](Execute::Registers & registers) { std::printf("%i", registers.A); } + }; + } +} \ No newline at end of file diff --git a/src/execute/state.cpp b/src/execute/state.cpp index 8846a0e..5969c41 100644 --- a/src/execute/state.cpp +++ b/src/execute/state.cpp @@ -5,7 +5,8 @@ namespace Execute State::State(std::unordered_map const & _labelStatementIndice) : currentStatement(0u), nextStatement(1u), - labelStatementIndice(_labelStatementIndice) + labelStatementIndice(_labelStatementIndice), + interrupts(GetInterrupts()) { } } \ No newline at end of file diff --git a/src/interpret/error.cpp b/src/interpret/error.cpp index 8da2050..b107ad0 100644 --- a/src/interpret/error.cpp +++ b/src/interpret/error.cpp @@ -18,6 +18,11 @@ namespace Interpret { } + ExpectedValue::ExpectedValue(Token::Token const & token) + : TokenError(token, "Expected an immediate value or register") + { + } + ExpectedImmediate::ExpectedImmediate(Token::Token const & token) : TokenError(token, "Expected an immediate value") { diff --git a/src/interpret/operanddefinitions.cpp b/src/interpret/operanddefinitions.cpp index 832c8a3..1d2005b 100644 --- a/src/interpret/operanddefinitions.cpp +++ b/src/interpret/operanddefinitions.cpp @@ -42,7 +42,7 @@ namespace Interpret return v; } - throw ExpectedRegister(token); + throw ExpectedValue(token); } void AddArithmeticArguments(ArithmeticStatement & statement, unsigned const operandIndex, std::vector const & tokens) @@ -193,9 +193,27 @@ namespace Interpret return statement; } + case Token::OperandType::SetInteger: + { + auto statement = std::make_unique(); + statement->firstArgument = GetRegisterArgument(operandIndex + 1u, tokens); + statement->secondArgument = GetImmediateOrRegisterArgument(operandIndex + 2u, tokens); + + return statement; + } + + case Token::OperandType::Interrupt: + { + auto statement = std::make_unique(); + statement->firstArgument = GetImmediateOrRegisterArgument(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; @@ -218,12 +236,15 @@ namespace Interpret 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::Interrupt: return 1; default: + std::printf("WARNING: returning default argument length of 0 for operand type %i\n", static_cast(type)); return 0; } } diff --git a/src/interpret/statement.cpp b/src/interpret/statement.cpp index fcf1a68..ca68c4a 100644 --- a/src/interpret/statement.cpp +++ b/src/interpret/statement.cpp @@ -39,4 +39,14 @@ namespace Interpret : label(_label) { } + + void SetStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) + { + firstArgument.GetValue(registers) = secondArgument.GetValue(registers); + } + + void InterruptStatement::Execute(Execute::Flags & flags, Execute::State & state, Execute::Registers & registers) + { + state.interrupts[firstArgument.GetValue(registers)](registers); + } } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8d6a9a6..39f474c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -114,6 +114,8 @@ int main(int argc, char ** argv) std::puts("\n*** Execution ***"); Execute::VirtualMachine vm(code); + vm.Run(); + /* while(!vm.IsTerminated()) { vm.SingleStep(); @@ -125,6 +127,7 @@ int main(int argc, char ** argv) std::puts("Press any key to step..."); std::getchar(); } + */ // END DEBUG return 0; diff --git a/src/token/operandtype.cpp b/src/token/operandtype.cpp index 2ce3228..be20d00 100644 --- a/src/token/operandtype.cpp +++ b/src/token/operandtype.cpp @@ -17,6 +17,8 @@ namespace Token { "lti", OperandType::LessThanInteger }, { "gti", OperandType::GreaterThanInteger }, { "eqi", OperandType::EqualInteger }, + { "seti", OperandType::SetInteger }, + { "int", OperandType::Interrupt } }; auto const & result = operations.find(op); diff --git a/src/token/token.cpp b/src/token/token.cpp index 78b3d04..2f58616 100644 --- a/src/token/token.cpp +++ b/src/token/token.cpp @@ -64,49 +64,26 @@ namespace Token case TokenType::ImmediateInteger: if (isValid) { - std::printf("$int=%i", std::get(data)); + std::printf("%i", std::get(data)); } else { - std::printf("BAD_IMM_INT"); + std::printf("BAD_IMM"); } break; case TokenType::Operand: if (isValid) { - switch(std::get(data)) + OperandType const opType = std::get(data); + switch(opType) { - case OperandType::AddInteger: - std::printf("addi"); - break; - - case OperandType::MultiplyInteger: - std::printf("muli"); - break; - - case OperandType::SubtractInteger: - std::printf("subi"); - break; - - case OperandType::DivideInteger: - std::printf("divi"); - break; - - case OperandType::ShiftIntegerLeft: - std::printf("shli"); - break; - - case OperandType::ShiftIntegerRight: - std::printf("shri"); - break; - - case OperandType::Jump: - std::printf("jump"); + case OperandType::Unknown: + std::printf("unknown_op"); break; default: - std::printf("unknown_op"); + std::printf("op%i", static_cast(opType)); break; } } @@ -119,25 +96,14 @@ namespace Token case TokenType::Register: if (isValid) { - switch(std::get(data)) + RegisterType const regType = std::get(data); + switch(regType) { - case RegisterType::A: - std::printf("%%A"); - break; - - case RegisterType::B: - std::printf("%%B"); - break; - - case RegisterType::C: - std::printf("%%C"); - break; - - case RegisterType::D: - std::printf("%%D"); - break; - default: + std::printf("%%%i", static_cast(regType)); + break; + + case RegisterType::Unknown: std::printf("%%unknown_reg"); break; } @@ -153,7 +119,7 @@ namespace Token break; case TokenType::Label: - std::printf("label=%s", std::get(data).c_str()); + std::printf("LABEL=%s", std::get(data).c_str()); break; case TokenType::Unknown: diff --git a/src/token/tokenizer.cpp b/src/token/tokenizer.cpp index 42cd56a..8ef0d2b 100644 --- a/src/token/tokenizer.cpp +++ b/src/token/tokenizer.cpp @@ -70,32 +70,32 @@ namespace Token LookForTokenEnd, }; TokenizerState state = TokenizerState::LookForNextToken; - unsigned tokenStart = 0; - for(unsigned i = 0u; i < line.size(); ++i) + unsigned columnTokenStart = 0; + for(unsigned column = 0u; column < line.size(); ++column) { switch(state) { case TokenizerState::LookForNextToken: - if (!IsWhiteSpace(line[i])) + if (!IsWhiteSpace(line[column])) { - if (line[i] == '#') + if (line[column] == '#') { // Ignore comments return; } - tokenStart = i; + columnTokenStart = column; state = TokenizerState::LookForTokenEnd; } break; case TokenizerState::LookForTokenEnd: - if (IsWhiteSpace(line[i]) || line[i] == ';') + if (IsWhiteSpace(line[column]) || line[column] == ';') { - tokens.push_back(ExtractToken(line.substr(tokenStart, i - tokenStart), lineNumber, tokenStart)); - if (line[i] == ';') + tokens.push_back(ExtractToken(line.substr(columnTokenStart, column - columnTokenStart), lineNumber, columnTokenStart)); + if (line[column] == ';') { - tokens.push_back(ExtractToken(line.substr(i, 1), lineNumber, tokenStart)); + tokens.push_back(ExtractToken(line.substr(column, 1), lineNumber, column)); } state = TokenizerState::LookForNextToken; } @@ -104,7 +104,7 @@ namespace Token } if (state == TokenizerState::LookForTokenEnd) { - tokens.push_back(ExtractToken(line.substr(tokenStart, line.size()), lineNumber, tokenStart)); + tokens.push_back(ExtractToken(line.substr(columnTokenStart, line.size()), lineNumber, columnTokenStart)); } } } \ No newline at end of file