From 418f6faa4f5e0b5c6af739a4d4fb6f2c11de07cb Mon Sep 17 00:00:00 2001 From: "[RealKC]" <16511305+RealKC@users.noreply.github.com> Date: Mon, 1 Jun 2020 22:57:27 +0300 Subject: [PATCH 01/10] Meta: Set `AllowShortFunctionsOnASingleLine` to `Inline` inside .clang-format --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index 1046800..ef59e6d 100644 --- a/.clang-format +++ b/.clang-format @@ -10,7 +10,7 @@ AllowAllArgumentsOnNextLine: 'false' AllowAllConstructorInitializersOnNextLine: 'true' AllowShortBlocksOnASingleLine: 'false' AllowShortCaseLabelsOnASingleLine: 'false' -AllowShortFunctionsOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: Inline BinPackParameters: 'false' BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Attach From e46c6f62c0ad7e515bd60d84449f00730e82f9a0 Mon Sep 17 00:00:00 2001 From: "[RealKC]" <16511305+RealKC@users.noreply.github.com> Date: Mon, 1 Jun 2020 23:49:12 +0300 Subject: [PATCH 02/10] Assembler: Add a basic instruction class Eventually we want to switch to using this everywhere. Also eventually this class should be tasked with codegen for itself. --- assembler/src/instruction.cpp | 162 ++++++++++++++++++++++++++++++++++ assembler/src/instruction.hpp | 60 +++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 assembler/src/instruction.cpp create mode 100644 assembler/src/instruction.hpp diff --git a/assembler/src/instruction.cpp b/assembler/src/instruction.cpp new file mode 100644 index 0000000..33414f2 --- /dev/null +++ b/assembler/src/instruction.cpp @@ -0,0 +1,162 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "instruction.hpp" + +#include "../../common/unreachable.hpp" +#include "error_reporting.hpp" + +#include +#include +#include +#include + +namespace reqvm { + +auto instruction::parse_from(std::string_view line) -> instruction { + auto first_space_index = line.find_first_of(' '); + + auto opcode = magic_enum::enum_cast( + std::string {line.begin() + 1, line.begin() + first_space_index}); + if (not opcode.has_value()) { + // FIXME: Better error message + report_to_user(level::error, "The line did not have a valid mnemonic"); + return {}; + } + + switch (number_of_arguments(opcode.value())) { + case 0: + return {opcode.value(), {}}; + case 1: { + auto arg_begin = line.begin() + first_space_index; + while (std::isspace(*arg_begin)) { + arg_begin++; + } + + auto arg_end = [&] { + auto arg_end_index = line.find_first_of(';'); + if (arg_end_index != std::string_view::npos) { + while (std::isspace(line[arg_end_index])) { + arg_end_index--; + } + return line.begin() + arg_end_index; + } + auto arg_end_it = line.end() - 1; + while (std::isspace(*arg_end_it)) { + arg_end_it--; + } + return arg_end_it; + }(); + + std::vector arguments; + arguments.emplace_back(arg_begin, arg_end); + + return {opcode.value(), std::move(arguments)}; + } + case 2: { + auto first_arg_begin = line.begin() + first_space_index; + while (std::isspace(*first_arg_begin)) { + first_arg_begin++; + } + auto comment_begin = line.find_first_of(';'); + auto inbetween_the_arguments = line.find_first_of(','); + if (comment_begin < inbetween_the_arguments) { + report_to_user(level::error, "Opcode needed two arguments"); + return {}; + } + + auto first_argument = std::string { + first_arg_begin, line.begin() + inbetween_the_arguments - 1}; + + inbetween_the_arguments++; // skip the comma + while (std::isspace(line[inbetween_the_arguments])) { + inbetween_the_arguments++; + } + + auto second_arg_end = [&] { + auto search_for_arg_end_from = comment_begin; + if (search_for_arg_end_from == std::string_view::npos) { + search_for_arg_end_from = line.size() - 1; + } + while (std::isspace(line[search_for_arg_end_from])) { + search_for_arg_end_from--; + } + + return search_for_arg_end_from; + }(); + + auto second_argument = + std::string {line.begin() + inbetween_the_arguments, + line.begin() + second_arg_end}; + std::vector arguments; + arguments.reserve(2); + arguments.push_back(first_argument); + arguments.push_back(second_argument); + return {opcode.value(), std::move(arguments)}; + } + default: + UNREACHABLE("number_of_arguments(opcode) returned invalid value"); + } +} + +auto instruction::number_of_arguments(common::opcode op) noexcept + -> std::uint8_t { + switch (op) { + using common::opcode; + case opcode::noop: + case opcode::ret: + case opcode::halt: + return 0; + case opcode::pushc: + case opcode::not_: + case opcode::pop: + case opcode::push: + case opcode::call: + case opcode::jmp: + case opcode::jeq: + case opcode::jneq: + case opcode::jg: + case opcode::jgeq: + case opcode::jl: + case opcode::jleq: + return 1; + case opcode::add: + case opcode::sub: + case opcode::mul: + case opcode::div: + case opcode::mod: + case opcode::and_: + case opcode::or_: + case opcode::xor_: + case opcode::lshft: + case opcode::rshft: + case opcode::cmp: + case opcode::io: + return 2; + + // default: + // TOOD: internal assembler error maybe? assert it's not reached? + } +} +} // namespace reqvm diff --git a/assembler/src/instruction.hpp b/assembler/src/instruction.hpp new file mode 100644 index 0000000..5552d09 --- /dev/null +++ b/assembler/src/instruction.hpp @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../../common/opcodes.hpp" + +#include +#include +#include +#include + +namespace reqvm { + +class instruction { +public: + static auto parse_from(std::string_view line) -> instruction; + + ~instruction() noexcept = default; + + auto append_argument(const std::string& arg) -> void { + _arguments.push_back(arg); + } + + auto is_invalid() const noexcept { return _invalid; } + +private: + instruction() noexcept : _opcode {common::opcode::noop}, _invalid {true} {} + instruction(common::opcode op, std::vector&& args) noexcept + : _opcode {op}, _arguments {std::move(args)} {} + + static auto number_of_arguments(common::opcode op) noexcept -> std::uint8_t; + + common::opcode _opcode; + bool _invalid {false}; + std::vector _arguments; +}; + +} // namespace reqvm From 2db44c9b11cc04617c2e4e8b7a480cec12fc8df4 Mon Sep 17 00:00:00 2001 From: "[RealKC]" <16511305+RealKC@users.noreply.github.com> Date: Thu, 4 Jun 2020 23:48:16 +0300 Subject: [PATCH 03/10] Documentation: change how define works --- documentation/assembler_syntax.md | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/documentation/assembler_syntax.md b/documentation/assembler_syntax.md index 21c40a5..d43f4a2 100644 --- a/documentation/assembler_syntax.md +++ b/documentation/assembler_syntax.md @@ -5,15 +5,39 @@ All preprocessor directives must start with a `%`. The following preprocessor directives are supported: |directive|syntax|notes| |:-------:|:----:|-----| -|`define`|`%define NAME body`|will textually replace `NAME` with `body`. Must end with an `%end`| +|`define`| See [define synatax](#define-syntax)| See [define notes](#define-notes) | |`if`|`%if cond`|The text between the `%if` and the following `%else`, `%elif`, `%end` will only be assembled if `cond` is true.| |`else`|`%else`|The text following the `%else` will be assembled if the condition of the previous `%if`/`%elif` was false| |`elif`|`%elif cond`|The text between the `%elif` and the following `%else`, `%elif` or `%end` will be assembled only if the condition of the previous `%if`/`%elif` was false and `cond` is true.| |`end`|`%end`|Ends a preprocessor sequence.| -Macros defined it with `%define` must have their name prefixed with `@` in order to be expanded as macros. +### `%define` -`__counter__` is a predefined preprocessor macro that acts as a counter starting from 0, and incrementing with each expansion. +#### define syntax + +```reqvmasm +%define NAME %type integral +%define NAME %type string +%define NAME %type instruction_list +``` + +where: + +* `` is a number, such as 1, 0xDEADC0DE, or 0o531565 +* `` is a string literal such as "foobar" +* `` is a list of zero or more instructions, an instruction may appear on the same line as the define itself. + +The above are called 'bodies'. + +`%type` must be followed by either `integral`, `string`, or `instruction_list`, or otherwise the assembler will output an error, and code generation will not be ran. The assembler will *not* try to infer the type on its own. + +A `%define` must be paired with an `%end` directive, which should appear on its own line. + +#### define notes + +The `%define` preprocessor directive creates a *macro*. Macros are replaced with their bodies **only** when their names are prefixed with `@`, otherwise the name is treated as a normal symbol. + +`__counter__` is a special macro predefined by the preprocessor, which has its value incremented with each expansion, and whose initial value is 0. ## Label syntax From a12bec377767312fef0e3b39c41f9edceb645fe1 Mon Sep 17 00:00:00 2001 From: "[RealKC]" <16511305+RealKC@users.noreply.github.com> Date: Fri, 5 Jun 2020 00:53:26 +0300 Subject: [PATCH 04/10] Assembler: add ASSERT(cond) and ASSERTM(cond, msg) --- assembler/src/assertions.cpp | 50 +++++++++++++++++++++++++++++ assembler/src/assertions.hpp | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 assembler/src/assertions.cpp create mode 100644 assembler/src/assertions.hpp diff --git a/assembler/src/assertions.cpp b/assembler/src/assertions.cpp new file mode 100644 index 0000000..fdbb606 --- /dev/null +++ b/assembler/src/assertions.cpp @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "assertions.hpp" + +#ifndef NDEBUG + +# include +# include +# include + +namespace reqvm::assertions { + +auto assertion_failed(const char* file, + std::uint64_t line, + const char* function_name, + const char* condition, + const char* message) noexcept -> void { + std::printf("\n\nASSERTION FAILED!\n in %s:%" PRIu64 + ":%s: %s was false.\n"); + if (message) { + std::printf(" Notes: %s\n", message); + }; + std::abort(); +} + +} // namespace reqvm::assertions + +#endif diff --git a/assembler/src/assertions.hpp b/assembler/src/assertions.hpp new file mode 100644 index 0000000..49507cc --- /dev/null +++ b/assembler/src/assertions.hpp @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#ifndef NDEBUG + +# include "../../common/crosscompiler_defines.hpp" + +# include + +# define ASSERTM(cond, msg) \ + do { \ + if (cond) { \ + /* do nothing */ \ + } else { \ + reqvm::assertions::assertion_failed( \ + __FILE__, __LINE__, REQVM_COMMON_FUNCTION, #cond, msg); \ + } \ + } while (0) + +# define ASSERT(cond) ASSERTM(cond, nullptr) + +namespace reqvm { +namespace assertions { + +[[noreturn]] auto assertion_failed(const char* file, + std::uint64_t line, + const char* function_name, + const char* condition, + const char* message) noexcept -> void; + +} +} // namespace reqvm + +#else + +# define ASSERT(c) // ignore macro params +# define ASSERTM(c, m) + +#endif From b5515bb26c02a84f866ada3ce4d3521e86c66904 Mon Sep 17 00:00:00 2001 From: "[RealKC]" <16511305+RealKC@users.noreply.github.com> Date: Fri, 5 Jun 2020 01:02:40 +0300 Subject: [PATCH 05/10] Assembler: add preprocessor skeleton --- assembler/src/preprocessor.cpp | 93 ++++++++++++++++++++++++++++++ assembler/src/preprocessor.hpp | 100 +++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 assembler/src/preprocessor.cpp create mode 100644 assembler/src/preprocessor.hpp diff --git a/assembler/src/preprocessor.cpp b/assembler/src/preprocessor.cpp new file mode 100644 index 0000000..e0a4664 --- /dev/null +++ b/assembler/src/preprocessor.cpp @@ -0,0 +1,93 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "preprocessor.hpp" + +#include +#include +#include + +namespace reqvm { + +preprocessor::preprocessor(std::string&& source_str) { + std::istringstream source {std::move(source_str)}; + + bool is_in_instruction_list {false}; + bool may_get_else_or_elif_directive {false}; + bool done {false}; + + std::string line; + while (std::getline(source, line) && not done) { + if (line[0] == '%') { + switch (get_keyword_from(line)) { + case keyword::if_: + may_get_else_or_elif_directive = true; + // TODO + break; + case keyword::else_: + // TODO + break; + case keyword::elif: + may_get_else_or_elif_directive = true; + // TODO + break; + case keyword::define: + // TODO + break; + case keyword::end: + // TODO + break; + case keyword::invalid: + // TODO + break; + } + } else if (line[0] == '\t') { + // TODO + } else { + // TODO + } + } +} + +auto preprocessor::get_keyword_from(const std::string& line) -> keyword { + ASSERTM(line[0] == '%', "get_keyword_from should not be used to extract a " + "keyword from a line not starting with '%'"); + + if (line.length() < (sizeof("%define") - 1)) { + return keyword::invalid; + } + + static std::unordered_map keywords { + {"if", keyword::if_}, + {"elif", keyword::elif}, + {"else", keyword::else_}, + {"end", keyword::end}, + {"define", keyword::define}}; + + std::string_view attempt {line.data() + 1, line.find_first_of(' ')}; + return keywords.find(attempt) != keywords.end() ? keywords[attempt] + : keyword::invalid; +} + +} // namespace reqvm diff --git a/assembler/src/preprocessor.hpp b/assembler/src/preprocessor.hpp new file mode 100644 index 0000000..4780270 --- /dev/null +++ b/assembler/src/preprocessor.hpp @@ -0,0 +1,100 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "assertions.hpp" +#include "instruction.hpp" + +#include +#include +#include +#include + + +namespace reqvm { + +class macro { + friend class preprocessor; + +public: + macro() = default; + macro(const macro&) = default; + macro(macro&&) = default; + + macro& operator=(const macro&) = default; + macro& operator=(macro&) = default; + + ~macro() noexcept = default; + + enum class type { + integral, + string, + instruction_list, + }; + + type type_of_self() { return _type; } + + auto as_integral() { + ASSERT(_type == type::integral); + return _integral_value; + } + auto as_string() -> std::string& { + ASSERT(_type == type::string); + return _string_value; + } + auto as_instruction_list() -> std::vector& { + ASSERT(_type == type::instruction_list); + return _instruction_list; + } + +private: + type _type; + + std::uint64_t _integral_value; + std::string _string_value; + std::vector _instruction_list; +}; + +class preprocessor { +public: + explicit preprocessor(std::string&& source); + ~preprocessor() noexcept = default; + +private: + enum class keyword { + invalid, + if_, + else_, + elif, + end, + define, + }; + static auto get_keyword_from(const std::string& line) -> keyword; + + bool _had_errors {false}; + macro _result; +}; + +} // namespace reqvm From fc5300d473ecc28ef7cc3bfe09fb26883682b4bb Mon Sep 17 00:00:00 2001 From: "[RealKC]" <16511305+RealKC@users.noreply.github.com> Date: Fri, 5 Jun 2020 01:34:18 +0300 Subject: [PATCH 06/10] Assembler: Add macro(type) constructor --- assembler/src/preprocessor.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assembler/src/preprocessor.hpp b/assembler/src/preprocessor.hpp index 4780270..f49f58b 100644 --- a/assembler/src/preprocessor.hpp +++ b/assembler/src/preprocessor.hpp @@ -32,7 +32,6 @@ #include #include - namespace reqvm { class macro { @@ -70,6 +69,8 @@ class macro { } private: + macro(type t) : _type {t} {} + type _type; std::uint64_t _integral_value; From 5950eda092cc4aae612a5b707bf852d7a3af6e99 Mon Sep 17 00:00:00 2001 From: "[RealKC]" <16511305+RealKC@users.noreply.github.com> Date: Sat, 6 Jun 2020 17:28:22 +0300 Subject: [PATCH 07/10] Assembler: move reqvm::macro to its own file --- assembler/src/macro.hpp | 88 ++++++++++++++++++++++++++++++++++ assembler/src/preprocessor.hpp | 45 +---------------- 2 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 assembler/src/macro.hpp diff --git a/assembler/src/macro.hpp b/assembler/src/macro.hpp new file mode 100644 index 0000000..f442836 --- /dev/null +++ b/assembler/src/macro.hpp @@ -0,0 +1,88 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#include "assertions.hpp" +#include "instruction.hpp" + +#include +#include +#include +#include + +namespace reqvm { + +class macro { +public: + enum class type { + invalid, + integral, + string, + instruction_list, + identifier, + }; + + class value { + public: + value(type t) : _type {t} {} + ~value() noexcept = default; + + auto as_integral() noexcept -> std::uint64_t { + ASSERT(_type == type::integral); + return std::get(_storage); + } + auto as_string() noexcept -> const std::string& { + ASSERT(_type == type::string); + return std::get(_storage); + } + auto as_instruction_list() noexcept -> const std::vector& { + ASSERT(_type == type::instruction_list); + return std::get>(_storage); + } + auto as_identifier() noexcept -> const std::string& { + ASSERT(_type == type::identifier); + return std::get(_storage); + } + + private: + type _type; + std::variant> + _storage; + }; + + auto expand(const std::unordered_map& all_macros) + -> void { + // FIXME: Implement me + // FIXME: make this return a macro::value + } + + auto my_type() noexcept { return _type; } + auto set_type(type t) noexcept { _type = t; } + +private: + type _type; + std::string _source; +}; + +} // namespace reqvm \ No newline at end of file diff --git a/assembler/src/preprocessor.hpp b/assembler/src/preprocessor.hpp index f49f58b..d313efa 100644 --- a/assembler/src/preprocessor.hpp +++ b/assembler/src/preprocessor.hpp @@ -26,6 +26,7 @@ #include "assertions.hpp" #include "instruction.hpp" +#include "macro.hpp" #include #include @@ -34,50 +35,6 @@ namespace reqvm { -class macro { - friend class preprocessor; - -public: - macro() = default; - macro(const macro&) = default; - macro(macro&&) = default; - - macro& operator=(const macro&) = default; - macro& operator=(macro&) = default; - - ~macro() noexcept = default; - - enum class type { - integral, - string, - instruction_list, - }; - - type type_of_self() { return _type; } - - auto as_integral() { - ASSERT(_type == type::integral); - return _integral_value; - } - auto as_string() -> std::string& { - ASSERT(_type == type::string); - return _string_value; - } - auto as_instruction_list() -> std::vector& { - ASSERT(_type == type::instruction_list); - return _instruction_list; - } - -private: - macro(type t) : _type {t} {} - - type _type; - - std::uint64_t _integral_value; - std::string _string_value; - std::vector _instruction_list; -}; - class preprocessor { public: explicit preprocessor(std::string&& source); From 7ce5c5bf26ac1f68ed907f51bda0d249e89e1d4c Mon Sep 17 00:00:00 2001 From: "[RealKC]" <16511305+RealKC@users.noreply.github.com> Date: Sat, 6 Jun 2020 18:12:22 +0300 Subject: [PATCH 08/10] Assembler: actually print things in assertion_failed, oops --- assembler/src/assertions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/assembler/src/assertions.cpp b/assembler/src/assertions.cpp index fdbb606..743eb59 100644 --- a/assembler/src/assertions.cpp +++ b/assembler/src/assertions.cpp @@ -38,7 +38,8 @@ auto assertion_failed(const char* file, const char* condition, const char* message) noexcept -> void { std::printf("\n\nASSERTION FAILED!\n in %s:%" PRIu64 - ":%s: %s was false.\n"); + ":%s: %s was false.\n", + file, line, function_name, condition); if (message) { std::printf(" Notes: %s\n", message); }; From 6f14274ba1e90398811c82547ac72b589458cc11 Mon Sep 17 00:00:00 2001 From: RealKC Date: Sun, 26 Jul 2020 23:00:30 +0300 Subject: [PATCH 09/10] snip --- .editorconfig | 8 +- .gitignore | 16 +- CONTRIBUTING.md | 46 +- LICENSE.txt | 42 +- README.md | 44 +- assembler/src/assembler.cpp | 950 +++++++++--------- assembler/src/assembler.hpp | 190 ++-- assembler/src/assertions.cpp | 102 +- assembler/src/assertions.hpp | 124 +-- assembler/src/error_reporting.cpp | 50 +- assembler/src/error_reporting.hpp | 82 +- assembler/src/instruction.cpp | 324 +++--- assembler/src/instruction.hpp | 120 +-- assembler/src/logger.hpp | 102 +- assembler/src/macro.hpp | 175 ++-- assembler/src/main.cpp | 188 ++-- assembler/src/preprocessor.cpp | 228 +++-- assembler/src/preprocessor.hpp | 116 +-- common/crosscompiler_defines.hpp | 66 +- common/opcodes.hpp | 150 +-- common/preamble.hpp | 88 +- common/registers.hpp | 292 +++--- common/unreachable.hpp | 138 +-- documentation/assembler_syntax.md | 88 +- documentation/build_instructions.md | 64 +- documentation/specification.md | 150 +-- examples/assembler/assembler_test.reqasm | 66 +- vm/Makefile | 98 +- vm/src/binary_manager.cpp | 82 +- vm/src/binary_manager.hpp | 120 +-- vm/src/binary_managers/exceptions.hpp | 146 +-- .../memory_mapped_file_backed.cpp | 110 +- .../memory_mapped_file_backed.hpp | 140 +-- .../memory_mapped_file_backed.posix.ipp | 408 ++++---- .../memory_mapped_file_backed.win32.ipp | 436 ++++---- vm/src/binary_managers/vector_backed.cpp | 98 +- vm/src/binary_managers/vector_backed.hpp | 104 +- vm/src/detect_platform.hpp | 80 +- vm/src/exceptions.hpp | 308 +++--- vm/src/flags.hpp | 84 +- vm/src/io.cpp | 152 +-- vm/src/io.hpp | 118 +-- vm/src/main.cpp | 244 ++--- vm/src/registers.cpp | 184 ++-- vm/src/registers.hpp | 176 ++-- vm/src/stack.cpp | 96 +- vm/src/stack.hpp | 110 +- vm/src/utility.hpp | 70 +- vm/src/vm.cpp | 778 +++++++------- vm/src/vm.hpp | 116 +-- 50 files changed, 4156 insertions(+), 4111 deletions(-) diff --git a/.editorconfig b/.editorconfig index 2d86b6a..d55ee43 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,5 @@ -root = true - -[examples/assembler/*.reqasm] -indent_style = tab +root = true + +[examples/assembler/*.reqasm] +indent_style = tab indent_size = 8 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 476da24..cbc20db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ -.vscode - -assembler/obj/** -vm/obj/** - -# ignore binary files -*.exe -*.reqvm +.vscode + +assembler/obj/** +vm/obj/** + +# ignore binary files +*.exe +*.reqvm diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 52f1058..7172832 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,23 +1,23 @@ -# Contributing guidelines - -## Issues - -When creating an issue to report a bug please include as much information as possible and make sure it's an actual bug in my code instead of one in your program. Include steps to reproduce the bug. - -## Pull requests - -Note that if you need to add new source files to either the assembler or the VM, you will not need to make any changes to the Makefiles, as they can find new files by themselves. - -Do: - -* submit code that uses C++17 features -* format your code using clang-format and the provided .clang-format file -* make sure to not cross the 72-column mark for each line in your commit messages - -Do not: - -* submit unformatted code -* write in C -* submit code that triggers compiler warnings or does not compile - -Naming conventions: `snake_case` must be used for functions, names of types and namespaces. `CAPITAL_SNAKE_CASE` must be used for macros. Private variables must have their names starting with an underscore(`_`). +# Contributing guidelines + +## Issues + +When creating an issue to report a bug please include as much information as possible and make sure it's an actual bug in my code instead of one in your program. Include steps to reproduce the bug. + +## Pull requests + +Note that if you need to add new source files to either the assembler or the VM, you will not need to make any changes to the Makefiles, as they can find new files by themselves. + +Do: + +* submit code that uses C++17 features +* format your code using clang-format and the provided .clang-format file +* make sure to not cross the 72-column mark for each line in your commit messages + +Do not: + +* submit unformatted code +* write in C +* submit code that triggers compiler warnings or does not compile + +Naming conventions: `snake_case` must be used for functions, names of types and namespaces. `CAPITAL_SNAKE_CASE` must be used for macros. Private variables must have their names starting with an underscore(`_`). diff --git a/LICENSE.txt b/LICENSE.txt index 671e321..cfd15c2 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2020 - present Mitca Dumitru - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2020 - present Mitca Dumitru + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 883158d..52591a2 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ -# reqvm - -reqvm is a bytecode virtual machine written as learning experience, there's probably some weird design choices, but I don't really care. - -This repository also contains a basic assembler for the VM. - -If you wish to build the project yourself, check out the [build instructions](documentation/build_instructions.md). - -If you wish to contribute, check out the [contribution guidelines](CONTRIBUTING.md). - -## Documentation - -* [the specification document](documentation/specification.md) -* [the assembler syntax](documentation/assembler_syntax.md) - -More documentation to come as I write it. - -## Legal - -The project is licensed under the MIT License, a copy of which is included [here](LICENSE.txt). - -The project uses [magic_enum](https://github.com/Neargye/magic_enum/), which is licensed under the MIT License. +# reqvm + +reqvm is a bytecode virtual machine written as learning experience, there's probably some weird design choices, but I don't really care. + +This repository also contains a basic assembler for the VM. + +If you wish to build the project yourself, check out the [build instructions](documentation/build_instructions.md). + +If you wish to contribute, check out the [contribution guidelines](CONTRIBUTING.md). + +## Documentation + +* [the specification document](documentation/specification.md) +* [the assembler syntax](documentation/assembler_syntax.md) + +More documentation to come as I write it. + +## Legal + +The project is licensed under the MIT License, a copy of which is included [here](LICENSE.txt). + +The project uses [magic_enum](https://github.com/Neargye/magic_enum/), which is licensed under the MIT License. diff --git a/assembler/src/assembler.cpp b/assembler/src/assembler.cpp index 8a5612c..8c3c2ce 100644 --- a/assembler/src/assembler.cpp +++ b/assembler/src/assembler.cpp @@ -1,475 +1,475 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "assembler.hpp" - -#include "../../common/preamble.hpp" -#include "error_reporting.hpp" -#include "logger.hpp" - -#include -#include - -#ifdef AGGRESIVE_LOGGING -using namespace magic_enum::ostream_operators; -#endif - -namespace reqvm { - -assembler::assembler(const std::string& filename) : _file {filename} { - auto output_filename = std::string { - filename.begin(), filename.begin() + filename.find_last_of('.')}; - output_filename += ".reqvm"; - _out.open(output_filename, std::ios::binary); -} - -auto assembler::run() -> int { - std::string line; - write_preamble(); - while (std::getline(_file, line)) { - LOG1(_pc); - switch (line[0]) { - case '%': - // TODO: error because preprocessing should be done first - LOG_MSG("PP directive.\n"); - break; - case ';': - // line starts with comment, ignore - LOG_MSG("Comment.\n"); - break; - case '.': { - LOG_MSG("label handling"); - auto label = get_label(line); - if (_labels.find(label) != _labels.end()) { - if (_labels[label][0] == 0) { - _labels[label][0] = _pc; - } - } else { - _labels[label] = std::vector {}; - _labels[label].push_back(_pc); - } - break; - } - case '\t': { - if (line[1] == ';') { - // TODO: add function to consume comments - break; - } - auto op = get_opcode(line); - switch (get_category(op)) { - case opcode_category::nullary: - emit(op); - break; - case opcode_category::unary_constant: { - auto try_parse_num = - [](const std::string& line) -> std::uint64_t { - // Skip the instruction - std::size_t num_start = line.find_first_of(' '); - while (not isdigit(line[num_start])) { - num_start++; - } - auto num_candidate = - std::string {line.begin() + num_start, line.end()}; - try { - return std::stoull(num_candidate, nullptr); - } catch (const std::out_of_range& e) { - // error - return 0; - } - }; - auto num = try_parse_num(line); - emit(op, num); - break; - } - case opcode_category::unary_label: { - std::size_t label_start = line.find_first_of(' '); - while (not std::isalpha(line[label_start])) { - label_start++; - } - std::size_t label_end = label_start; - while (std::isalpha(line[label_start])) { - label_end++; - } - emit(op, std::string {line.begin() + label_start, - line.begin() + label_end}); - break; - } - case opcode_category::unary_register: { - auto reg = get_register(line); - if (not reg.has_value()) { - // TODO: report error - break; - } - emit(op, reg.value()); - break; - } - case opcode_category::binary_registers: { - auto regs = get_register_pair(line); - if (not regs.has_value()) { - // TODO: report error - break; - } - emit(op, regs.value()); - break; - } - case opcode_category::binary_byte_then_register: { - auto byte_and_reg = get_io_op_and_reg(line); - if (not byte_and_reg.has_value()) { - break; - } - emit(op, byte_and_reg.value().first, - byte_and_reg.value().second); - break; - } - } - break; - } - - default: - // TODO: Report some error - break; - } - } - emit(common::opcode::halt); - return 0; -} - -auto assembler::write_preamble() -> void { - _out.write(common::magic_byte_string, - sizeof(common::magic_byte_string) - 1); - std::array version {0}; - version[0] = '!'; - version[3] = ';'; - version[6] = ';'; - version[9] = ';'; - { - using namespace common; - version[1] = static_cast(version::major >> 8); - version[2] = static_cast((version::major << 8) >> 8); - version[4] = static_cast(version::minor >> 8); - version[5] = static_cast((version::minor << 8) >> 8); - version[7] = static_cast(version::patch >> 8); - version[8] = static_cast((version::patch << 8) >> 8); - } - _out.write(version.data(), version.size()); - - const auto nullbyte = '\0'; - for (auto i = sizeof(common::magic_byte_string); i < 266; i++) { - _out.write(&nullbyte, 1); - } - _out.seekp(256); -} - -auto assembler::emit(common::opcode op) -> void { - LOG1(op); - if (_has_errors) { - return; - } - _pc++; - const auto as_char = static_cast(op); - _out.write(&as_char, 1); -} - -auto assembler::emit(common::opcode op, common::registers reg) -> void { - LOG2(op, reg); - if (_has_errors) { - return; - } - _pc += 2; - const char chars[] = {static_cast(op), static_cast(reg)}; - _out.write(chars, 2); -} - -auto assembler::emit(common::opcode op, std::uint64_t num) -> void { - LOG2(op, num); - if (_has_errors) { - return; - } - _pc += 9; - const auto as_char = static_cast(op); - _out.write(&as_char, 1); - const char bytes[] = {static_cast(num >> 56), - static_cast((num << 8) >> 56), - static_cast((num << 16) >> 56), - static_cast((num << 24) >> 56), - static_cast((num << 32) >> 56), - static_cast((num << 40) >> 56), - static_cast((num << 48) >> 56), - static_cast((num << 56) >> 56)}; - _out.write(bytes, 8); -} - -auto assembler::emit(common::opcode op, std::string label) -> void { - LOG2(op, label); - if (_labels.find(label) == _labels.end()) { - _labels[label].push_back(0); - _labels[label].push_back(_pc); - // Defer the rest of the work to the (opcode, uint64) overload - emit(op, 0); - return; - } - if (_labels[label][0] == 0) { - _labels[label].push_back(_pc); - // Defer the rest of the work to the (opcode, uint64) overload - emit(op, 0); - return; - } - // Defer the rest of the work to the (opcode, uint64) overload - emit(op, _labels[label][0]); -} - -auto assembler::emit(common::opcode op, - std::pair regs) - -> void { - LOG1_NONL(op) /* << */ PAIR(regs); - if (_has_errors) { - return; - } - _pc += 3; - const char chars[] = {static_cast(op), static_cast(regs.first), - static_cast(regs.second)}; - _out.write(chars, 3); -} - -auto assembler::emit(common::opcode op, - common::io_op subop, - common::registers reg) -> void { - if (_has_errors) { - return; - } - _pc += 3; - const char chars[] = {static_cast(op), static_cast(subop), - static_cast(reg)}; - _out.write(chars, sizeof(chars)); -} - -auto assembler::emit_remaining_labels() -> void { - for (const auto& vecs : _labels) { - if (vecs.second.size() > 1) { - auto address = vecs.second[0]; - for (auto hole : vecs.second) { - _out.seekp(hole); - const char bytes[] = {static_cast(address >> 56), - static_cast((address << 8) >> 56), - static_cast((address << 16) >> 56), - static_cast((address << 24) >> 56), - static_cast((address << 32) >> 56), - static_cast((address << 40) >> 56), - static_cast((address << 48) >> 56), - static_cast((address << 56) >> 56)}; - _out.write(bytes, 8); - } - } - } -} - -auto assembler::get_label(const std::string& line) -> std::string { - LOG1(line); - // TODO: reject code like .label : - auto label_end = line.find_first_of(':'); - return std::string {line.begin() + 1, line.begin() + label_end}; -} - -auto assembler::get_opcode(const std::string& line) -> common::opcode { - LOG1(line); - auto instruction_name = - std::string {line.begin() + 1, line.begin() + line.find_first_of(' ')}; - auto op = magic_enum::enum_cast(instruction_name); - if (not op.has_value()) { - report_to_user(level::error, instruction_name + " in line '" + line - + "' is not a valid mnemonic"); - } - return op.value(); -} - -auto assembler::get_category(common::opcode op) -> opcode_category { - LOG1(op); - using common::opcode; - switch (op) { - case opcode::noop: - case opcode::ret: - case opcode::halt: - return opcode_category::nullary; - case opcode::pushc: - return opcode_category::unary_constant; - case opcode::not_: - case opcode::pop: - case opcode::push: - return opcode_category::unary_register; - case opcode::call: - case opcode::jmp: - case opcode::jeq: - case opcode::jneq: - case opcode::jg: - case opcode::jgeq: - case opcode::jl: - case opcode::jleq: - return opcode_category::unary_label; - case opcode::add: - case opcode::sub: - case opcode::mul: - case opcode::div: - case opcode::mod: - case opcode::and_: - case opcode::or_: - case opcode::xor_: - case opcode::lshft: - case opcode::rshft: - case opcode::cmp: - return opcode_category::binary_registers; - case opcode::io: - return opcode_category::binary_byte_then_register; - // default: - // TOOD: internal assembler error maybe? assert it's not reached? - } -} - -auto assembler::get_register(const std::string& line) - -> std::optional { - LOG1(line); - auto reg_name_start = line.find_first_of(' '); - while (not std::isalpha(line[reg_name_start])) { - reg_name_start++; - } - auto reg_name_end = reg_name_start; - while (std::isalnum(line[reg_name_end])) { - reg_name_end++; - } - return parse_register( - {line.begin() + reg_name_start, line.begin() + reg_name_end}); -} - -auto assembler::get_register_pair(const std::string& line) - -> std::optional> { - LOG1(line); - auto first_reg_start = line.find_first_of(' '); - while (not std::isalpha(line[first_reg_start])) { - first_reg_start++; - } - auto first_reg_end = line.find_first_of(','); - while (not std::isalnum(line[first_reg_end])) { - first_reg_end--; - } - auto second_reg_start = line.find_first_of(','); - while (not std::isalpha(line[second_reg_start])) { - second_reg_start++; - } - auto second_reg_end = second_reg_start; - while (std::isalnum(line[second_reg_end])) { - second_reg_end++; - } - auto r1 = parse_register( - {line.begin() + first_reg_start, line.begin() + first_reg_end + 1}); - if (not r1.has_value()) { - // tODO: help the user or smth - return {}; - } - auto r2 = parse_register( - {line.begin() + second_reg_start, line.begin() + second_reg_end}); - if (not r2.has_value()) { - // todo: some error maybe - return {}; - } - LOG1(r2.value()); - return std::pair {r1.value(), r2.value()}; -} - -auto assembler::get_io_op_and_reg(const std::string& line) - -> std::optional> { - LOG1(line); - auto the_io_op = get_io_op(line); - if (not the_io_op.has_value()) { - // Maybe report some proper error? - return {}; - } - auto reg_start = line.find_first_of(' '); - // Find the start of the IO op - while (not std::isalpha(line[reg_start])) { - reg_start++; - } - reg_start += 4; - // put8c is the only IO operation to have 5 characters currently - // FIXME(?): more robust logic or nah? - reg_start += (the_io_op.value() == common::io_op::put8c); - while (not std::isalpha(line[reg_start])) { - reg_start++; - } - auto reg_end = reg_start; - while (std::isalnum(line[reg_end])) { - reg_end++; - } - auto the_register = - parse_register({line.begin() + reg_start, line.begin() + reg_end}); - if (not the_register.has_value()) { - // Maybe some proper error handling? - return {}; - } - return std::pair {the_io_op.value(), the_register.value()}; -} - -auto assembler::get_io_op(const std::string& line) - -> std::optional { - LOG1(line); - auto op_start = line.find_first_of(' '); - while (not std::isalpha(line[op_start])) { - op_start++; - } - auto op_end = op_start; - while (std::isalnum(line[op_end])) { - op_end++; - } - auto op = std::string {line.begin() + op_start, line.begin() + op_end}; - auto the_op = magic_enum::enum_cast(op); - if (not the_op.has_value()) { - report_to_user(level::error, op + " in line '" + line - + "' is not a valid IO operation."); - } - return the_op.value(); -} - -auto assembler::parse_register(const std::string& reg) - -> std::optional { - LOG1(reg); - auto the_register = magic_enum::enum_cast(reg); - if (not the_register.has_value()) { - auto err_msg = "'" + reg + "' is not a valid register."; - report_to_user(level::error, err_msg); - } - LOG1(the_register.value()); - return the_register.value(); -} - -auto assembler::is_read_only(common::registers reg) noexcept -> bool { - LOG1(reg); - using common::registers; - switch (reg) { - case registers::sp: - case registers::pc: - return true; - default: - return false; - } -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "assembler.hpp" + +#include "../../common/preamble.hpp" +#include "error_reporting.hpp" +#include "logger.hpp" + +#include +#include + +#ifdef AGGRESIVE_LOGGING +using namespace magic_enum::ostream_operators; +#endif + +namespace reqvm { + +assembler::assembler(const std::string& filename) : _file {filename} { + auto output_filename = std::string { + filename.begin(), filename.begin() + filename.find_last_of('.')}; + output_filename += ".reqvm"; + _out.open(output_filename, std::ios::binary); +} + +auto assembler::run() -> int { + std::string line; + write_preamble(); + while (std::getline(_file, line)) { + LOG1(_pc); + switch (line[0]) { + case '%': + // TODO: error because preprocessing should be done first + LOG_MSG("PP directive.\n"); + break; + case ';': + // line starts with comment, ignore + LOG_MSG("Comment.\n"); + break; + case '.': { + LOG_MSG("label handling"); + auto label = get_label(line); + if (_labels.find(label) != _labels.end()) { + if (_labels[label][0] == 0) { + _labels[label][0] = _pc; + } + } else { + _labels[label] = std::vector {}; + _labels[label].push_back(_pc); + } + break; + } + case '\t': { + if (line[1] == ';') { + // TODO: add function to consume comments + break; + } + auto op = get_opcode(line); + switch (get_category(op)) { + case opcode_category::nullary: + emit(op); + break; + case opcode_category::unary_constant: { + auto try_parse_num = + [](const std::string& line) -> std::uint64_t { + // Skip the instruction + std::size_t num_start = line.find_first_of(' '); + while (not isdigit(line[num_start])) { + num_start++; + } + auto num_candidate = + std::string {line.begin() + num_start, line.end()}; + try { + return std::stoull(num_candidate, nullptr); + } catch (const std::out_of_range& e) { + // error + return 0; + } + }; + auto num = try_parse_num(line); + emit(op, num); + break; + } + case opcode_category::unary_label: { + std::size_t label_start = line.find_first_of(' '); + while (not std::isalpha(line[label_start])) { + label_start++; + } + std::size_t label_end = label_start; + while (std::isalpha(line[label_start])) { + label_end++; + } + emit(op, std::string {line.begin() + label_start, + line.begin() + label_end}); + break; + } + case opcode_category::unary_register: { + auto reg = get_register(line); + if (not reg.has_value()) { + // TODO: report error + break; + } + emit(op, reg.value()); + break; + } + case opcode_category::binary_registers: { + auto regs = get_register_pair(line); + if (not regs.has_value()) { + // TODO: report error + break; + } + emit(op, regs.value()); + break; + } + case opcode_category::binary_byte_then_register: { + auto byte_and_reg = get_io_op_and_reg(line); + if (not byte_and_reg.has_value()) { + break; + } + emit(op, byte_and_reg.value().first, + byte_and_reg.value().second); + break; + } + } + break; + } + + default: + // TODO: Report some error + break; + } + } + emit(common::opcode::halt); + return 0; +} + +auto assembler::write_preamble() -> void { + _out.write(common::magic_byte_string, + sizeof(common::magic_byte_string) - 1); + std::array version {0}; + version[0] = '!'; + version[3] = ';'; + version[6] = ';'; + version[9] = ';'; + { + using namespace common; + version[1] = static_cast(version::major >> 8); + version[2] = static_cast((version::major << 8) >> 8); + version[4] = static_cast(version::minor >> 8); + version[5] = static_cast((version::minor << 8) >> 8); + version[7] = static_cast(version::patch >> 8); + version[8] = static_cast((version::patch << 8) >> 8); + } + _out.write(version.data(), version.size()); + + const auto nullbyte = '\0'; + for (auto i = sizeof(common::magic_byte_string); i < 266; i++) { + _out.write(&nullbyte, 1); + } + _out.seekp(256); +} + +auto assembler::emit(common::opcode op) -> void { + LOG1(op); + if (_has_errors) { + return; + } + _pc++; + const auto as_char = static_cast(op); + _out.write(&as_char, 1); +} + +auto assembler::emit(common::opcode op, common::registers reg) -> void { + LOG2(op, reg); + if (_has_errors) { + return; + } + _pc += 2; + const char chars[] = {static_cast(op), static_cast(reg)}; + _out.write(chars, 2); +} + +auto assembler::emit(common::opcode op, std::uint64_t num) -> void { + LOG2(op, num); + if (_has_errors) { + return; + } + _pc += 9; + const auto as_char = static_cast(op); + _out.write(&as_char, 1); + const char bytes[] = {static_cast(num >> 56), + static_cast((num << 8) >> 56), + static_cast((num << 16) >> 56), + static_cast((num << 24) >> 56), + static_cast((num << 32) >> 56), + static_cast((num << 40) >> 56), + static_cast((num << 48) >> 56), + static_cast((num << 56) >> 56)}; + _out.write(bytes, 8); +} + +auto assembler::emit(common::opcode op, std::string label) -> void { + LOG2(op, label); + if (_labels.find(label) == _labels.end()) { + _labels[label].push_back(0); + _labels[label].push_back(_pc); + // Defer the rest of the work to the (opcode, uint64) overload + emit(op, 0); + return; + } + if (_labels[label][0] == 0) { + _labels[label].push_back(_pc); + // Defer the rest of the work to the (opcode, uint64) overload + emit(op, 0); + return; + } + // Defer the rest of the work to the (opcode, uint64) overload + emit(op, _labels[label][0]); +} + +auto assembler::emit(common::opcode op, + std::pair regs) + -> void { + LOG1_NONL(op) /* << */ PAIR(regs); + if (_has_errors) { + return; + } + _pc += 3; + const char chars[] = {static_cast(op), static_cast(regs.first), + static_cast(regs.second)}; + _out.write(chars, 3); +} + +auto assembler::emit(common::opcode op, + common::io_op subop, + common::registers reg) -> void { + if (_has_errors) { + return; + } + _pc += 3; + const char chars[] = {static_cast(op), static_cast(subop), + static_cast(reg)}; + _out.write(chars, sizeof(chars)); +} + +auto assembler::emit_remaining_labels() -> void { + for (const auto& vecs : _labels) { + if (vecs.second.size() > 1) { + auto address = vecs.second[0]; + for (auto hole : vecs.second) { + _out.seekp(hole); + const char bytes[] = {static_cast(address >> 56), + static_cast((address << 8) >> 56), + static_cast((address << 16) >> 56), + static_cast((address << 24) >> 56), + static_cast((address << 32) >> 56), + static_cast((address << 40) >> 56), + static_cast((address << 48) >> 56), + static_cast((address << 56) >> 56)}; + _out.write(bytes, 8); + } + } + } +} + +auto assembler::get_label(const std::string& line) -> std::string { + LOG1(line); + // TODO: reject code like .label : + auto label_end = line.find_first_of(':'); + return std::string {line.begin() + 1, line.begin() + label_end}; +} + +auto assembler::get_opcode(const std::string& line) -> common::opcode { + LOG1(line); + auto instruction_name = + std::string {line.begin() + 1, line.begin() + line.find_first_of(' ')}; + auto op = magic_enum::enum_cast(instruction_name); + if (not op.has_value()) { + report_to_user(level::error, instruction_name + " in line '" + line + + "' is not a valid mnemonic"); + } + return op.value(); +} + +auto assembler::get_category(common::opcode op) -> opcode_category { + LOG1(op); + using common::opcode; + switch (op) { + case opcode::noop: + case opcode::ret: + case opcode::halt: + return opcode_category::nullary; + case opcode::pushc: + return opcode_category::unary_constant; + case opcode::not_: + case opcode::pop: + case opcode::push: + return opcode_category::unary_register; + case opcode::call: + case opcode::jmp: + case opcode::jeq: + case opcode::jneq: + case opcode::jg: + case opcode::jgeq: + case opcode::jl: + case opcode::jleq: + return opcode_category::unary_label; + case opcode::add: + case opcode::sub: + case opcode::mul: + case opcode::div: + case opcode::mod: + case opcode::and_: + case opcode::or_: + case opcode::xor_: + case opcode::lshft: + case opcode::rshft: + case opcode::cmp: + return opcode_category::binary_registers; + case opcode::io: + return opcode_category::binary_byte_then_register; + // default: + // TOOD: internal assembler error maybe? assert it's not reached? + } +} + +auto assembler::get_register(const std::string& line) + -> std::optional { + LOG1(line); + auto reg_name_start = line.find_first_of(' '); + while (not std::isalpha(line[reg_name_start])) { + reg_name_start++; + } + auto reg_name_end = reg_name_start; + while (std::isalnum(line[reg_name_end])) { + reg_name_end++; + } + return parse_register( + {line.begin() + reg_name_start, line.begin() + reg_name_end}); +} + +auto assembler::get_register_pair(const std::string& line) + -> std::optional> { + LOG1(line); + auto first_reg_start = line.find_first_of(' '); + while (not std::isalpha(line[first_reg_start])) { + first_reg_start++; + } + auto first_reg_end = line.find_first_of(','); + while (not std::isalnum(line[first_reg_end])) { + first_reg_end--; + } + auto second_reg_start = line.find_first_of(','); + while (not std::isalpha(line[second_reg_start])) { + second_reg_start++; + } + auto second_reg_end = second_reg_start; + while (std::isalnum(line[second_reg_end])) { + second_reg_end++; + } + auto r1 = parse_register( + {line.begin() + first_reg_start, line.begin() + first_reg_end + 1}); + if (not r1.has_value()) { + // tODO: help the user or smth + return {}; + } + auto r2 = parse_register( + {line.begin() + second_reg_start, line.begin() + second_reg_end}); + if (not r2.has_value()) { + // todo: some error maybe + return {}; + } + LOG1(r2.value()); + return std::pair {r1.value(), r2.value()}; +} + +auto assembler::get_io_op_and_reg(const std::string& line) + -> std::optional> { + LOG1(line); + auto the_io_op = get_io_op(line); + if (not the_io_op.has_value()) { + // Maybe report some proper error? + return {}; + } + auto reg_start = line.find_first_of(' '); + // Find the start of the IO op + while (not std::isalpha(line[reg_start])) { + reg_start++; + } + reg_start += 4; + // put8c is the only IO operation to have 5 characters currently + // FIXME(?): more robust logic or nah? + reg_start += (the_io_op.value() == common::io_op::put8c); + while (not std::isalpha(line[reg_start])) { + reg_start++; + } + auto reg_end = reg_start; + while (std::isalnum(line[reg_end])) { + reg_end++; + } + auto the_register = + parse_register({line.begin() + reg_start, line.begin() + reg_end}); + if (not the_register.has_value()) { + // Maybe some proper error handling? + return {}; + } + return std::pair {the_io_op.value(), the_register.value()}; +} + +auto assembler::get_io_op(const std::string& line) + -> std::optional { + LOG1(line); + auto op_start = line.find_first_of(' '); + while (not std::isalpha(line[op_start])) { + op_start++; + } + auto op_end = op_start; + while (std::isalnum(line[op_end])) { + op_end++; + } + auto op = std::string {line.begin() + op_start, line.begin() + op_end}; + auto the_op = magic_enum::enum_cast(op); + if (not the_op.has_value()) { + report_to_user(level::error, op + " in line '" + line + + "' is not a valid IO operation."); + } + return the_op.value(); +} + +auto assembler::parse_register(const std::string& reg) + -> std::optional { + LOG1(reg); + auto the_register = magic_enum::enum_cast(reg); + if (not the_register.has_value()) { + auto err_msg = "'" + reg + "' is not a valid register."; + report_to_user(level::error, err_msg); + } + LOG1(the_register.value()); + return the_register.value(); +} + +auto assembler::is_read_only(common::registers reg) noexcept -> bool { + LOG1(reg); + using common::registers; + switch (reg) { + case registers::sp: + case registers::pc: + return true; + default: + return false; + } +} + +} // namespace reqvm diff --git a/assembler/src/assembler.hpp b/assembler/src/assembler.hpp index 0361ba5..0d5de3f 100644 --- a/assembler/src/assembler.hpp +++ b/assembler/src/assembler.hpp @@ -1,95 +1,95 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../../common/opcodes.hpp" -#include "../../common/registers.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace reqvm { - -class assembler { -public: - explicit assembler(const std::string& filename); - ~assembler() noexcept = default; - - auto has_errors() const noexcept -> bool { - return _has_errors; - } - - auto run() -> int; - -private: - static auto get_label(const std::string& line) -> std::string; - static auto get_opcode(const std::string& line) -> common::opcode; - static auto parse_register(const std::string& name) - -> std::optional; - static auto get_register(const std::string& line) - -> std::optional; - static auto get_register_pair(const std::string& line) - -> std::optional>; - static auto get_io_op(const std::string& line) - -> std::optional; - static auto get_io_op_and_reg(const std::string& line) - -> std::optional>; - static auto is_read_only(common::registers reg) noexcept -> bool; - - enum class opcode_category { - nullary, - unary_register, - unary_label, - unary_constant, - binary_registers, - binary_byte_then_register, - }; - static auto get_category(common::opcode op) -> opcode_category; - - auto write_preamble() -> void; - auto emit(common::opcode op) -> void; - auto emit(common::opcode op, std::string label) -> void; - auto emit(common::opcode op, common::registers reg) -> void; - auto emit(common::opcode op, std::uint64_t num) -> void; - auto emit(common::opcode op, - std::pair regs) -> void; - auto emit(common::opcode op, common::io_op subop, common::registers reg) - -> void; - auto emit_remaining_labels() -> void; - - std::ifstream _file; - std::ofstream _out; - std::string _output_name; - std::unordered_map> _labels; - std::uint64_t _pc {256}; - bool _has_errors {false}; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../../common/opcodes.hpp" +#include "../../common/registers.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace reqvm { + +class assembler { +public: + explicit assembler(const std::string& filename); + ~assembler() noexcept = default; + + auto has_errors() const noexcept -> bool { + return _has_errors; + } + + auto run() -> int; + +private: + static auto get_label(const std::string& line) -> std::string; + static auto get_opcode(const std::string& line) -> common::opcode; + static auto parse_register(const std::string& name) + -> std::optional; + static auto get_register(const std::string& line) + -> std::optional; + static auto get_register_pair(const std::string& line) + -> std::optional>; + static auto get_io_op(const std::string& line) + -> std::optional; + static auto get_io_op_and_reg(const std::string& line) + -> std::optional>; + static auto is_read_only(common::registers reg) noexcept -> bool; + + enum class opcode_category { + nullary, + unary_register, + unary_label, + unary_constant, + binary_registers, + binary_byte_then_register, + }; + static auto get_category(common::opcode op) -> opcode_category; + + auto write_preamble() -> void; + auto emit(common::opcode op) -> void; + auto emit(common::opcode op, std::string label) -> void; + auto emit(common::opcode op, common::registers reg) -> void; + auto emit(common::opcode op, std::uint64_t num) -> void; + auto emit(common::opcode op, + std::pair regs) -> void; + auto emit(common::opcode op, common::io_op subop, common::registers reg) + -> void; + auto emit_remaining_labels() -> void; + + std::ifstream _file; + std::ofstream _out; + std::string _output_name; + std::unordered_map> _labels; + std::uint64_t _pc {256}; + bool _has_errors {false}; +}; + +} // namespace reqvm diff --git a/assembler/src/assertions.cpp b/assembler/src/assertions.cpp index 743eb59..195b729 100644 --- a/assembler/src/assertions.cpp +++ b/assembler/src/assertions.cpp @@ -1,51 +1,51 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "assertions.hpp" - -#ifndef NDEBUG - -# include -# include -# include - -namespace reqvm::assertions { - -auto assertion_failed(const char* file, - std::uint64_t line, - const char* function_name, - const char* condition, - const char* message) noexcept -> void { - std::printf("\n\nASSERTION FAILED!\n in %s:%" PRIu64 - ":%s: %s was false.\n", - file, line, function_name, condition); - if (message) { - std::printf(" Notes: %s\n", message); - }; - std::abort(); -} - -} // namespace reqvm::assertions - -#endif +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "assertions.hpp" + +#ifndef NDEBUG + +# include +# include +# include + +namespace reqvm::assertions { + +auto assertion_failed(const char* file, + std::uint64_t line, + const char* function_name, + const char* condition, + const char* message) noexcept -> void { + std::printf("\n\nASSERTION FAILED!\n in %s:%" PRIu64 + ":%s: %s was false.\n", + file, line, function_name, condition); + if (message) { + std::printf(" Notes: %s\n", message); + }; + std::abort(); +} + +} // namespace reqvm::assertions + +#endif diff --git a/assembler/src/assertions.hpp b/assembler/src/assertions.hpp index 49507cc..ad29bd6 100644 --- a/assembler/src/assertions.hpp +++ b/assembler/src/assertions.hpp @@ -1,62 +1,62 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#ifndef NDEBUG - -# include "../../common/crosscompiler_defines.hpp" - -# include - -# define ASSERTM(cond, msg) \ - do { \ - if (cond) { \ - /* do nothing */ \ - } else { \ - reqvm::assertions::assertion_failed( \ - __FILE__, __LINE__, REQVM_COMMON_FUNCTION, #cond, msg); \ - } \ - } while (0) - -# define ASSERT(cond) ASSERTM(cond, nullptr) - -namespace reqvm { -namespace assertions { - -[[noreturn]] auto assertion_failed(const char* file, - std::uint64_t line, - const char* function_name, - const char* condition, - const char* message) noexcept -> void; - -} -} // namespace reqvm - -#else - -# define ASSERT(c) // ignore macro params -# define ASSERTM(c, m) - -#endif +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#ifndef NDEBUG + +# include "../../common/crosscompiler_defines.hpp" + +# include + +# define ASSERTM(cond, msg) \ + do { \ + if (cond) { \ + /* do nothing */ \ + } else { \ + reqvm::assertions::assertion_failed( \ + __FILE__, __LINE__, REQVM_COMMON_FUNCTION, #cond, msg); \ + } \ + } while (0) + +# define ASSERT(cond) ASSERTM(cond, nullptr) + +namespace reqvm { +namespace assertions { + +[[noreturn]] auto assertion_failed(const char* file, + std::uint64_t line, + const char* function_name, + const char* condition, + const char* message) noexcept -> void; + +} +} // namespace reqvm + +#else + +# define ASSERT(c) // ignore macro params +# define ASSERTM(c, m) + +#endif diff --git a/assembler/src/error_reporting.cpp b/assembler/src/error_reporting.cpp index f96bf3d..aa7b55c 100644 --- a/assembler/src/error_reporting.cpp +++ b/assembler/src/error_reporting.cpp @@ -1,25 +1,25 @@ -#include "error_reporting.hpp" - -#include - -namespace reqvm { - -auto report_to_user(level lvl, const std::string& msg) noexcept -> void { - switch (lvl) { - case level::info: - std::cout << "INFO: "; - break; - case level::warning: - std::cout << "WARNING: "; - break; - case level::error: - std::cout << "ERROR: "; - break; - case level::internal_assembler_error: - std::cout << "INTERNAL ASSEMBLER ERROR: "; - break; - } - std::cout << msg; -} - -} // namespace reqvm +#include "error_reporting.hpp" + +#include + +namespace reqvm { + +auto report_to_user(level lvl, const std::string& msg) noexcept -> void { + switch (lvl) { + case level::info: + std::cout << "INFO: "; + break; + case level::warning: + std::cout << "WARNING: "; + break; + case level::error: + std::cout << "ERROR: "; + break; + case level::internal_assembler_error: + std::cout << "INTERNAL ASSEMBLER ERROR: "; + break; + } + std::cout << msg; +} + +} // namespace reqvm diff --git a/assembler/src/error_reporting.hpp b/assembler/src/error_reporting.hpp index 9217239..0250bb7 100644 --- a/assembler/src/error_reporting.hpp +++ b/assembler/src/error_reporting.hpp @@ -1,41 +1,41 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include -#include - -namespace reqvm { - -enum class level { - info, - warning, - error, - internal_assembler_error, -}; - -auto report_to_user(level lvl, const std::string& message) noexcept -> void; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include + +namespace reqvm { + +enum class level { + info, + warning, + error, + internal_assembler_error, +}; + +auto report_to_user(level lvl, const std::string& message) noexcept -> void; + +} // namespace reqvm diff --git a/assembler/src/instruction.cpp b/assembler/src/instruction.cpp index 33414f2..2d98bae 100644 --- a/assembler/src/instruction.cpp +++ b/assembler/src/instruction.cpp @@ -1,162 +1,162 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "instruction.hpp" - -#include "../../common/unreachable.hpp" -#include "error_reporting.hpp" - -#include -#include -#include -#include - -namespace reqvm { - -auto instruction::parse_from(std::string_view line) -> instruction { - auto first_space_index = line.find_first_of(' '); - - auto opcode = magic_enum::enum_cast( - std::string {line.begin() + 1, line.begin() + first_space_index}); - if (not opcode.has_value()) { - // FIXME: Better error message - report_to_user(level::error, "The line did not have a valid mnemonic"); - return {}; - } - - switch (number_of_arguments(opcode.value())) { - case 0: - return {opcode.value(), {}}; - case 1: { - auto arg_begin = line.begin() + first_space_index; - while (std::isspace(*arg_begin)) { - arg_begin++; - } - - auto arg_end = [&] { - auto arg_end_index = line.find_first_of(';'); - if (arg_end_index != std::string_view::npos) { - while (std::isspace(line[arg_end_index])) { - arg_end_index--; - } - return line.begin() + arg_end_index; - } - auto arg_end_it = line.end() - 1; - while (std::isspace(*arg_end_it)) { - arg_end_it--; - } - return arg_end_it; - }(); - - std::vector arguments; - arguments.emplace_back(arg_begin, arg_end); - - return {opcode.value(), std::move(arguments)}; - } - case 2: { - auto first_arg_begin = line.begin() + first_space_index; - while (std::isspace(*first_arg_begin)) { - first_arg_begin++; - } - auto comment_begin = line.find_first_of(';'); - auto inbetween_the_arguments = line.find_first_of(','); - if (comment_begin < inbetween_the_arguments) { - report_to_user(level::error, "Opcode needed two arguments"); - return {}; - } - - auto first_argument = std::string { - first_arg_begin, line.begin() + inbetween_the_arguments - 1}; - - inbetween_the_arguments++; // skip the comma - while (std::isspace(line[inbetween_the_arguments])) { - inbetween_the_arguments++; - } - - auto second_arg_end = [&] { - auto search_for_arg_end_from = comment_begin; - if (search_for_arg_end_from == std::string_view::npos) { - search_for_arg_end_from = line.size() - 1; - } - while (std::isspace(line[search_for_arg_end_from])) { - search_for_arg_end_from--; - } - - return search_for_arg_end_from; - }(); - - auto second_argument = - std::string {line.begin() + inbetween_the_arguments, - line.begin() + second_arg_end}; - std::vector arguments; - arguments.reserve(2); - arguments.push_back(first_argument); - arguments.push_back(second_argument); - return {opcode.value(), std::move(arguments)}; - } - default: - UNREACHABLE("number_of_arguments(opcode) returned invalid value"); - } -} - -auto instruction::number_of_arguments(common::opcode op) noexcept - -> std::uint8_t { - switch (op) { - using common::opcode; - case opcode::noop: - case opcode::ret: - case opcode::halt: - return 0; - case opcode::pushc: - case opcode::not_: - case opcode::pop: - case opcode::push: - case opcode::call: - case opcode::jmp: - case opcode::jeq: - case opcode::jneq: - case opcode::jg: - case opcode::jgeq: - case opcode::jl: - case opcode::jleq: - return 1; - case opcode::add: - case opcode::sub: - case opcode::mul: - case opcode::div: - case opcode::mod: - case opcode::and_: - case opcode::or_: - case opcode::xor_: - case opcode::lshft: - case opcode::rshft: - case opcode::cmp: - case opcode::io: - return 2; - - // default: - // TOOD: internal assembler error maybe? assert it's not reached? - } -} -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "instruction.hpp" + +#include "../../common/unreachable.hpp" +#include "error_reporting.hpp" + +#include +#include +#include +#include + +namespace reqvm { + +auto instruction::parse_from(std::string_view line) -> instruction { + auto first_space_index = line.find_first_of(' '); + + auto opcode = magic_enum::enum_cast( + std::string {line.begin() + 1, line.begin() + first_space_index}); + if (not opcode.has_value()) { + // FIXME: Better error message + report_to_user(level::error, "The line did not have a valid mnemonic"); + return {}; + } + + switch (number_of_arguments(opcode.value())) { + case 0: + return {opcode.value(), {}}; + case 1: { + auto arg_begin = line.begin() + first_space_index; + while (std::isspace(*arg_begin)) { + arg_begin++; + } + + auto arg_end = [&] { + auto arg_end_index = line.find_first_of(';'); + if (arg_end_index != std::string_view::npos) { + while (std::isspace(line[arg_end_index])) { + arg_end_index--; + } + return line.begin() + arg_end_index; + } + auto arg_end_it = line.end() - 1; + while (std::isspace(*arg_end_it)) { + arg_end_it--; + } + return arg_end_it; + }(); + + std::vector arguments; + arguments.emplace_back(arg_begin, arg_end); + + return {opcode.value(), std::move(arguments)}; + } + case 2: { + auto first_arg_begin = line.begin() + first_space_index; + while (std::isspace(*first_arg_begin)) { + first_arg_begin++; + } + auto comment_begin = line.find_first_of(';'); + auto inbetween_the_arguments = line.find_first_of(','); + if (comment_begin < inbetween_the_arguments) { + report_to_user(level::error, "Opcode needed two arguments"); + return {}; + } + + auto first_argument = std::string { + first_arg_begin, line.begin() + inbetween_the_arguments - 1}; + + inbetween_the_arguments++; // skip the comma + while (std::isspace(line[inbetween_the_arguments])) { + inbetween_the_arguments++; + } + + auto second_arg_end = [&] { + auto search_for_arg_end_from = comment_begin; + if (search_for_arg_end_from == std::string_view::npos) { + search_for_arg_end_from = line.size() - 1; + } + while (std::isspace(line[search_for_arg_end_from])) { + search_for_arg_end_from--; + } + + return search_for_arg_end_from; + }(); + + auto second_argument = + std::string {line.begin() + inbetween_the_arguments, + line.begin() + second_arg_end}; + std::vector arguments; + arguments.reserve(2); + arguments.push_back(first_argument); + arguments.push_back(second_argument); + return {opcode.value(), std::move(arguments)}; + } + default: + UNREACHABLE("number_of_arguments(opcode) returned invalid value"); + } +} + +auto instruction::number_of_arguments(common::opcode op) noexcept + -> std::uint8_t { + switch (op) { + using common::opcode; + case opcode::noop: + case opcode::ret: + case opcode::halt: + return 0; + case opcode::pushc: + case opcode::not_: + case opcode::pop: + case opcode::push: + case opcode::call: + case opcode::jmp: + case opcode::jeq: + case opcode::jneq: + case opcode::jg: + case opcode::jgeq: + case opcode::jl: + case opcode::jleq: + return 1; + case opcode::add: + case opcode::sub: + case opcode::mul: + case opcode::div: + case opcode::mod: + case opcode::and_: + case opcode::or_: + case opcode::xor_: + case opcode::lshft: + case opcode::rshft: + case opcode::cmp: + case opcode::io: + return 2; + + // default: + // TOOD: internal assembler error maybe? assert it's not reached? + } +} +} // namespace reqvm diff --git a/assembler/src/instruction.hpp b/assembler/src/instruction.hpp index 5552d09..5a05190 100644 --- a/assembler/src/instruction.hpp +++ b/assembler/src/instruction.hpp @@ -1,60 +1,60 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../../common/opcodes.hpp" - -#include -#include -#include -#include - -namespace reqvm { - -class instruction { -public: - static auto parse_from(std::string_view line) -> instruction; - - ~instruction() noexcept = default; - - auto append_argument(const std::string& arg) -> void { - _arguments.push_back(arg); - } - - auto is_invalid() const noexcept { return _invalid; } - -private: - instruction() noexcept : _opcode {common::opcode::noop}, _invalid {true} {} - instruction(common::opcode op, std::vector&& args) noexcept - : _opcode {op}, _arguments {std::move(args)} {} - - static auto number_of_arguments(common::opcode op) noexcept -> std::uint8_t; - - common::opcode _opcode; - bool _invalid {false}; - std::vector _arguments; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../../common/opcodes.hpp" + +#include +#include +#include +#include + +namespace reqvm { + +class instruction { +public: + static auto parse_from(std::string_view line) -> instruction; + + ~instruction() noexcept = default; + + auto append_argument(const std::string& arg) -> void { + _arguments.push_back(arg); + } + + auto is_invalid() const noexcept { return _invalid; } + +private: + instruction() noexcept : _opcode {common::opcode::noop}, _invalid {true} {} + instruction(common::opcode op, std::vector&& args) noexcept + : _opcode {op}, _arguments {std::move(args)} {} + + static auto number_of_arguments(common::opcode op) noexcept -> std::uint8_t; + + common::opcode _opcode; + bool _invalid {false}; + std::vector _arguments; +}; + +} // namespace reqvm diff --git a/assembler/src/logger.hpp b/assembler/src/logger.hpp index 69c880f..6c0877e 100644 --- a/assembler/src/logger.hpp +++ b/assembler/src/logger.hpp @@ -1,51 +1,51 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../../common/crosscompiler_defines.hpp" - -#include - -#ifdef AGGRESIVE_LOGGING -# define LOG_HEADER() \ - std::cerr << __FILE__ << ":" << __LINE__ << ": in " \ - << REQVM_COMMON_FUNCTION << ":\n\t" -# define LOG1(first) LOG_HEADER() << # first ": " << first << '\n' -# define LOG1_NONL(first) LOG_HEADER() << # first << first -# define PAIR(pair) \ - << #pair "{ .first: " << pair.first << ", .second: " << pair.second \ - << "}\n" -# define LOG2(first, second) \ - LOG_HEADER() << #first ": " << first << ", " #second ": " << second \ - << '\n' -# define LOG_MSG(msg) LOG_HEADER() << msg -#else -# define LOG_HEADER() -# define LOG1(f) -# define LOG1_NONL(f) -# define LOG_PAIR(p) -# define LOG2(f, s) -# define LOG_MSG(m) -#endif +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../../common/crosscompiler_defines.hpp" + +#include + +#ifdef AGGRESIVE_LOGGING +# define LOG_HEADER() \ + std::cerr << __FILE__ << ":" << __LINE__ << ": in " \ + << REQVM_COMMON_FUNCTION << ":\n\t" +# define LOG1(first) LOG_HEADER() << # first ": " << first << '\n' +# define LOG1_NONL(first) LOG_HEADER() << # first << first +# define PAIR(pair) \ + << #pair "{ .first: " << pair.first << ", .second: " << pair.second \ + << "}\n" +# define LOG2(first, second) \ + LOG_HEADER() << #first ": " << first << ", " #second ": " << second \ + << '\n' +# define LOG_MSG(msg) LOG_HEADER() << msg +#else +# define LOG_HEADER() +# define LOG1(f) +# define LOG1_NONL(f) +# define LOG_PAIR(p) +# define LOG2(f, s) +# define LOG_MSG(m) +#endif diff --git a/assembler/src/macro.hpp b/assembler/src/macro.hpp index f442836..c71f121 100644 --- a/assembler/src/macro.hpp +++ b/assembler/src/macro.hpp @@ -1,88 +1,89 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once -#include "assertions.hpp" -#include "instruction.hpp" - -#include -#include -#include -#include - -namespace reqvm { - -class macro { -public: - enum class type { - invalid, - integral, - string, - instruction_list, - identifier, - }; - - class value { - public: - value(type t) : _type {t} {} - ~value() noexcept = default; - - auto as_integral() noexcept -> std::uint64_t { - ASSERT(_type == type::integral); - return std::get(_storage); - } - auto as_string() noexcept -> const std::string& { - ASSERT(_type == type::string); - return std::get(_storage); - } - auto as_instruction_list() noexcept -> const std::vector& { - ASSERT(_type == type::instruction_list); - return std::get>(_storage); - } - auto as_identifier() noexcept -> const std::string& { - ASSERT(_type == type::identifier); - return std::get(_storage); - } - - private: - type _type; - std::variant> - _storage; - }; - - auto expand(const std::unordered_map& all_macros) - -> void { - // FIXME: Implement me - // FIXME: make this return a macro::value - } - - auto my_type() noexcept { return _type; } - auto set_type(type t) noexcept { _type = t; } - -private: - type _type; - std::string _source; -}; - +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#include "assertions.hpp" +#include "instruction.hpp" + +#include +#include +#include +#include +#include + +namespace reqvm { + +class macro { +public: + enum class type { + invalid, + integral, + string, + instruction_list, + identifier, + }; + + class value { + public: + value(type t) : _type {t} {} + ~value() noexcept = default; + + auto as_integral() noexcept -> std::uint64_t { + ASSERT(_type == type::integral); + return std::get(_storage); + } + auto as_string() noexcept -> const std::string& { + ASSERT(_type == type::string); + return std::get(_storage); + } + auto as_instruction_list() noexcept -> const std::vector& { + ASSERT(_type == type::instruction_list); + return std::get>(_storage); + } + auto as_identifier() noexcept -> const std::string& { + ASSERT(_type == type::identifier); + return std::get(_storage); + } + + private: + type _type; + std::variant> + _storage; + }; + + auto expand(const std::unordered_map& all_macros) + -> void { + // FIXME: Implement me + // FIXME: make this return a macro::value + } + + auto my_type() noexcept { return _type; } + auto set_type(type t) noexcept { _type = t; } + +private: + type _type; + std::string _source; +}; + } // namespace reqvm \ No newline at end of file diff --git a/assembler/src/main.cpp b/assembler/src/main.cpp index a8da4a5..a2a26bb 100644 --- a/assembler/src/main.cpp +++ b/assembler/src/main.cpp @@ -1,94 +1,94 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "assembler.hpp" - -#include -#include - -auto print_self_info() noexcept -> void; -auto print_thirdparty_licenses() noexcept -> void; - -auto main(int argc, char** argv) -> int { - print_self_info(); - - if (argc < 2) { - printf("Usage: assembler file (temp)"); - } - - // FIXME: actually parse command line arguments and only print this when - // an argument asking for it is passed. - if (argc == 3) { - print_thirdparty_licenses(); - } - - auto the_assembler = reqvm::assembler {argv[1]}; - return the_assembler.run(); -} - -auto print_self_info() noexcept -> void { - constexpr char info[] = R"( -The reqvm assembler. -Copyright (c) Mitca Dumitru 2020-present. (MIT license) - -Pass --help or -h for help (not implemented yet). -Pass --licenses to see third party licenses. (Not implemented yet, shows for 3 arguments). -)"; - - std::puts(info); -} - -auto print_thirdparty_licenses() noexcept -> void { - const char* libs[] = { - "magic enum\nCopyright (c) 2019 - 2020 Daniil Goncharov\nLicense: " - "MIT.\n", - }; - const char* licenses[] = { - R"(MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.)", - }; - for (auto& lib : libs) { - printf("%s\n", lib); - } - for (auto& license : licenses) { - printf("%s\n\n", license); - } -} +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "assembler.hpp" + +#include +#include + +auto print_self_info() noexcept -> void; +auto print_thirdparty_licenses() noexcept -> void; + +auto main(int argc, char** argv) -> int { + print_self_info(); + + if (argc < 2) { + printf("Usage: assembler file (temp)"); + } + + // FIXME: actually parse command line arguments and only print this when + // an argument asking for it is passed. + if (argc == 3) { + print_thirdparty_licenses(); + } + + auto the_assembler = reqvm::assembler {argv[1]}; + return the_assembler.run(); +} + +auto print_self_info() noexcept -> void { + constexpr char info[] = R"( +The reqvm assembler. +Copyright (c) Mitca Dumitru 2020-present. (MIT license) + +Pass --help or -h for help (not implemented yet). +Pass --licenses to see third party licenses. (Not implemented yet, shows for 3 arguments). +)"; + + std::puts(info); +} + +auto print_thirdparty_licenses() noexcept -> void { + const char* libs[] = { + "magic enum\nCopyright (c) 2019 - 2020 Daniil Goncharov\nLicense: " + "MIT.\n", + }; + const char* licenses[] = { + R"(MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.)", + }; + for (auto& lib : libs) { + printf("%s\n", lib); + } + for (auto& license : licenses) { + printf("%s\n\n", license); + } +} diff --git a/assembler/src/preprocessor.cpp b/assembler/src/preprocessor.cpp index e0a4664..3981c9d 100644 --- a/assembler/src/preprocessor.cpp +++ b/assembler/src/preprocessor.cpp @@ -1,93 +1,135 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "preprocessor.hpp" - -#include -#include -#include - -namespace reqvm { - -preprocessor::preprocessor(std::string&& source_str) { - std::istringstream source {std::move(source_str)}; - - bool is_in_instruction_list {false}; - bool may_get_else_or_elif_directive {false}; - bool done {false}; - - std::string line; - while (std::getline(source, line) && not done) { - if (line[0] == '%') { - switch (get_keyword_from(line)) { - case keyword::if_: - may_get_else_or_elif_directive = true; - // TODO - break; - case keyword::else_: - // TODO - break; - case keyword::elif: - may_get_else_or_elif_directive = true; - // TODO - break; - case keyword::define: - // TODO - break; - case keyword::end: - // TODO - break; - case keyword::invalid: - // TODO - break; - } - } else if (line[0] == '\t') { - // TODO - } else { - // TODO - } - } -} - -auto preprocessor::get_keyword_from(const std::string& line) -> keyword { - ASSERTM(line[0] == '%', "get_keyword_from should not be used to extract a " - "keyword from a line not starting with '%'"); - - if (line.length() < (sizeof("%define") - 1)) { - return keyword::invalid; - } - - static std::unordered_map keywords { - {"if", keyword::if_}, - {"elif", keyword::elif}, - {"else", keyword::else_}, - {"end", keyword::end}, - {"define", keyword::define}}; - - std::string_view attempt {line.data() + 1, line.find_first_of(' ')}; - return keywords.find(attempt) != keywords.end() ? keywords[attempt] - : keyword::invalid; -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "preprocessor.hpp" + +#include +#include +#include +#include + +namespace reqvm { + +preprocessor::preprocessor(std::string&& source_str) { + std::istringstream source {std::move(source_str)}; + + bool is_in_instruction_list {false}; + bool may_get_else_or_elif_directive {false}; + bool line_was_consumed_already {false}; + bool done {false}; + + std::string line; + while (not done) { + if (not line_was_consumed_already) { + if (not std::getline(source, line)) { + _had_errors = true; + break; + } + } + if (line[0] == '%') { + switch (get_keyword_from(line)) { + case keyword::if_: + may_get_else_or_elif_directive = true; + // TODO + break; + case keyword::else_: + if (not may_get_else_or_elif_directive) { + // Error + } + may_get_else_or_elif_directive = false; + // TODO + break; + case keyword::elif: + may_get_else_or_elif_directive = true; + // TODO + break; + case keyword::define: { + macro m {extract_macro_type(line)}; + break; + } + case keyword::end: + done = true; + // TODO + break; + case keyword::invalid: + // TODO + break; + } + } else if (line[0] == '\t') { + // TODO + } else { + // TODO + } + } +} + +auto preprocessor::get_keyword_from(const std::string& line) -> keyword { + ASSERTM(line[0] == '%', "get_keyword_from should not be used to extract a " + "keyword from a line not starting with '%'"); + + if (line.length() < (sizeof("%define") - 1)) { + return keyword::invalid; + } + + static std::unordered_map keywords { + {"if", keyword::if_}, + {"elif", keyword::elif}, + {"else", keyword::else_}, + {"end", keyword::end}, + {"define", keyword::define}}; + + std::string_view attempt {line.data() + 1, line.find_first_of(' ')}; + return keywords.find(attempt) != keywords.end() ? keywords[attempt] + : keyword::invalid; +} + +auto preprocessor::extract_macro_type(const std::string& line) -> macro::type { + auto type_start = line.find_first_of('%', 1) + 1; // Skips leading '%' + + if (std::string_view {line.data() + type_start, 4} != "type") { + _had_errors = true; + return macro::type::invalid; + } + + type_start += 4; + + while (std::isspace(line[type_start])) { + type_start++; + } + + static std::unordered_map macro_types { + {"integral", macro::type::integral}, + {"string", macro::type::string}, + {"instruction_list", macro::type::instruction_list}}; + + auto next_space = line.find_first_of(' ', type_start); + std::string_view attempt {line.data() + type_start, + next_space - type_start}; + + return macro_types.find(attempt) != macro_types.end() + ? macro_types[attempt] + : macro::type::invalid; +} + +} // namespace reqvm diff --git a/assembler/src/preprocessor.hpp b/assembler/src/preprocessor.hpp index d313efa..c085c75 100644 --- a/assembler/src/preprocessor.hpp +++ b/assembler/src/preprocessor.hpp @@ -1,58 +1,58 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "assertions.hpp" -#include "instruction.hpp" -#include "macro.hpp" - -#include -#include -#include -#include - -namespace reqvm { - -class preprocessor { -public: - explicit preprocessor(std::string&& source); - ~preprocessor() noexcept = default; - -private: - enum class keyword { - invalid, - if_, - else_, - elif, - end, - define, - }; - static auto get_keyword_from(const std::string& line) -> keyword; - - bool _had_errors {false}; - macro _result; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "assertions.hpp" +#include "instruction.hpp" +#include "macro.hpp" + +#include +#include +#include +#include + +namespace reqvm { + +class preprocessor { +public: + explicit preprocessor(std::string&& source); + ~preprocessor() noexcept = default; + +private: + enum class keyword { + invalid, + if_, + else_, + elif, + end, + define, + }; + static auto get_keyword_from(const std::string& line) -> keyword; + + bool _had_errors {false}; + macro _result; +}; + +} // namespace reqvm diff --git a/common/crosscompiler_defines.hpp b/common/crosscompiler_defines.hpp index 78c3139..7431c09 100644 --- a/common/crosscompiler_defines.hpp +++ b/common/crosscompiler_defines.hpp @@ -1,33 +1,33 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#if defined(__GNUC__) -# define REQVM_COMMON_FUNCTION __PRETTY_FUNCTION__ -#elif defined(_MSC_VER) -# define REQVM_COMMON_FUNCTION __FUNCSIG__ -#else -# define REQVM_COMMON_FUNCTION __func__ -#endif +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#if defined(__GNUC__) +# define REQVM_COMMON_FUNCTION __PRETTY_FUNCTION__ +#elif defined(_MSC_VER) +# define REQVM_COMMON_FUNCTION __FUNCSIG__ +#else +# define REQVM_COMMON_FUNCTION __func__ +#endif diff --git a/common/opcodes.hpp b/common/opcodes.hpp index 507fcb5..c7d12ba 100644 --- a/common/opcodes.hpp +++ b/common/opcodes.hpp @@ -1,75 +1,75 @@ -/* -* MIT License -* -* Copyright (c) 2020 Mitca Dumitru -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ - -#pragma once - -namespace common { - -// this enum represent all the opcodes of reqvm -enum class opcode : unsigned char { - noop = 0, - call = 1, - ret = 2, - - // Metainstruction - io = 10, - // Integer arithmetics - add = 20, - sub = 21, - mul = 22, - div = 23, - mod = 24, - and_ = 25, - or_ = 26, - xor_ = 27, - not_ = 28, - lshft = 29, - rshft = 30, - - // Stack operations - push = 35, - pushc = 36, - pop = 37, - - // Branching - cmp = 42, - jmp = 43, - jeq = 44, - jneq = 45, - jl = 46, - jleq = 47, - jg = 48, - jgeq = 49, - - halt = 255, -}; - -enum class io_op : unsigned char { - getc = 1, - putc = 2, - put8c = 3, - putn = 4, -}; - -} // namespace common +/* +* MIT License +* +* Copyright (c) 2020 Mitca Dumitru +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#pragma once + +namespace common { + +// this enum represent all the opcodes of reqvm +enum class opcode : unsigned char { + noop = 0, + call = 1, + ret = 2, + + // Metainstruction + io = 10, + // Integer arithmetics + add = 20, + sub = 21, + mul = 22, + div = 23, + mod = 24, + and_ = 25, + or_ = 26, + xor_ = 27, + not_ = 28, + lshft = 29, + rshft = 30, + + // Stack operations + push = 35, + pushc = 36, + pop = 37, + + // Branching + cmp = 42, + jmp = 43, + jeq = 44, + jneq = 45, + jl = 46, + jleq = 47, + jg = 48, + jgeq = 49, + + halt = 255, +}; + +enum class io_op : unsigned char { + getc = 1, + putc = 2, + put8c = 3, + putn = 4, +}; + +} // namespace common diff --git a/common/preamble.hpp b/common/preamble.hpp index 1e723a9..51f0688 100644 --- a/common/preamble.hpp +++ b/common/preamble.hpp @@ -1,44 +1,44 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -namespace common { - -// Valid reqvm binaries must start with this string, -// followed by a `\n'(ascii byte 10) -static constexpr char magic_byte_string[] = - "!reqvm;" - "VSszMEI2VSszMEVGVSszMEZDVSszMEVCVSszMEM5VSswMDIxVSs2NjQyVSszMDg4VSs2QjYyVS" - "szMDdFVSszMDhDVSswMDIx\n"; - -namespace version { - -static constexpr std::uint16_t major = 0; -static constexpr std::uint16_t minor = 1; -static constexpr std::uint16_t patch = 5; - -} // namespace version - -} // namespace common +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +namespace common { + +// Valid reqvm binaries must start with this string, +// followed by a `\n'(ascii byte 10) +static constexpr char magic_byte_string[] = + "!reqvm;" + "VSszMEI2VSszMEVGVSszMEZDVSszMEVCVSszMEM5VSswMDIxVSs2NjQyVSszMDg4VSs2QjYyVS" + "szMDdFVSszMDhDVSswMDIx\n"; + +namespace version { + +static constexpr std::uint16_t major = 0; +static constexpr std::uint16_t minor = 1; +static constexpr std::uint16_t patch = 5; + +} // namespace version + +} // namespace common diff --git a/common/registers.hpp b/common/registers.hpp index c514a58..8683f00 100644 --- a/common/registers.hpp +++ b/common/registers.hpp @@ -1,147 +1,147 @@ -/* -* MIT License -* -* Copyright (c) 2020 Mitca Dumitru -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ - -#pragma once - -#include - -namespace common { - -enum class registers : std::uint8_t { - none = 0, - pc = 1, - sp = 2, - - // general purpose registers - gp00 = 64, - gp01 = 65, - gp02 = 66, - gp03 = 67, - gp04 = 68, - gp05 = 69, - gp06 = 70, - gp07 = 71, - gp08 = 72, - gp09 = 73, - gp10 = 74, - gp11 = 75, - gp12 = 76, - gp13 = 77, - gp14 = 78, - gp15 = 79, - gp16 = 80, - gp17 = 81, - gp18 = 82, - gp19 = 83, - gp20 = 84, - gp21 = 85, - gp22 = 86, - gp23 = 87, - gp24 = 88, - gp25 = 89, - gp26 = 90, - gp27 = 91, - gp28 = 92, - gp29 = 93, - gp30 = 94, - gp31 = 95, - gp32 = 96, - gp33 = 97, - gp34 = 98, - gp35 = 99, - gp36 = 100, - gp37 = 101, - gp38 = 102, - gp39 = 103, - gp40 = 104, - gp41 = 105, - gp42 = 106, - gp43 = 107, - gp44 = 108, - gp45 = 109, - gp46 = 110, - gp47 = 111, - gp48 = 112, - gp49 = 113, - gp50 = 114, - gp51 = 115, - gp52 = 116, - gp53 = 117, - gp54 = 118, - gp55 = 119, - gp56 = 120, - gp57 = 121, - gp58 = 122, - gp59 = 123, - gp60 = 124, - gp61 = 125, - gp62 = 126, - gp63 = 127, - - // Registers related to functions - ire = 128, - - ifa00 = 132, - ifa01 = 133, - ifa02 = 134, - ifa03 = 135, - ifa04 = 136, - ifa05 = 137, - ifa06 = 138, - ifa07 = 139, - ifa08 = 140, - ifa09 = 141, - ifa10 = 142, - ifa11 = 143, - ifa12 = 144, - ifa13 = 145, - ifa14 = 146, - ifa15 = 147 -}; - -constexpr bool operator==(registers lhs, registers rhs) noexcept { - return static_cast(lhs) == static_cast(rhs); -} - -constexpr bool operator!=(registers lhs, registers rhs) noexcept { - return !(lhs == rhs); -} - -constexpr bool operator<(registers lhs, registers rhs) noexcept { - return static_cast(lhs) < static_cast(rhs); -} - -constexpr bool operator>(registers lhs, registers rhs) noexcept { - return rhs < lhs; -} - -constexpr bool operator<=(registers lhs, registers rhs) noexcept { - return !(lhs > rhs); -} - -constexpr bool operator>=(registers lhs, registers rhs) noexcept { - return !(lhs < rhs); -} - +/* +* MIT License +* +* Copyright (c) 2020 Mitca Dumitru +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#pragma once + +#include + +namespace common { + +enum class registers : std::uint8_t { + none = 0, + pc = 1, + sp = 2, + + // general purpose registers + gp00 = 64, + gp01 = 65, + gp02 = 66, + gp03 = 67, + gp04 = 68, + gp05 = 69, + gp06 = 70, + gp07 = 71, + gp08 = 72, + gp09 = 73, + gp10 = 74, + gp11 = 75, + gp12 = 76, + gp13 = 77, + gp14 = 78, + gp15 = 79, + gp16 = 80, + gp17 = 81, + gp18 = 82, + gp19 = 83, + gp20 = 84, + gp21 = 85, + gp22 = 86, + gp23 = 87, + gp24 = 88, + gp25 = 89, + gp26 = 90, + gp27 = 91, + gp28 = 92, + gp29 = 93, + gp30 = 94, + gp31 = 95, + gp32 = 96, + gp33 = 97, + gp34 = 98, + gp35 = 99, + gp36 = 100, + gp37 = 101, + gp38 = 102, + gp39 = 103, + gp40 = 104, + gp41 = 105, + gp42 = 106, + gp43 = 107, + gp44 = 108, + gp45 = 109, + gp46 = 110, + gp47 = 111, + gp48 = 112, + gp49 = 113, + gp50 = 114, + gp51 = 115, + gp52 = 116, + gp53 = 117, + gp54 = 118, + gp55 = 119, + gp56 = 120, + gp57 = 121, + gp58 = 122, + gp59 = 123, + gp60 = 124, + gp61 = 125, + gp62 = 126, + gp63 = 127, + + // Registers related to functions + ire = 128, + + ifa00 = 132, + ifa01 = 133, + ifa02 = 134, + ifa03 = 135, + ifa04 = 136, + ifa05 = 137, + ifa06 = 138, + ifa07 = 139, + ifa08 = 140, + ifa09 = 141, + ifa10 = 142, + ifa11 = 143, + ifa12 = 144, + ifa13 = 145, + ifa14 = 146, + ifa15 = 147 +}; + +constexpr bool operator==(registers lhs, registers rhs) noexcept { + return static_cast(lhs) == static_cast(rhs); +} + +constexpr bool operator!=(registers lhs, registers rhs) noexcept { + return !(lhs == rhs); +} + +constexpr bool operator<(registers lhs, registers rhs) noexcept { + return static_cast(lhs) < static_cast(rhs); +} + +constexpr bool operator>(registers lhs, registers rhs) noexcept { + return rhs < lhs; +} + +constexpr bool operator<=(registers lhs, registers rhs) noexcept { + return !(lhs > rhs); +} + +constexpr bool operator>=(registers lhs, registers rhs) noexcept { + return !(lhs < rhs); +} + } // namespace common \ No newline at end of file diff --git a/common/unreachable.hpp b/common/unreachable.hpp index 85d5cf8..5fc64e4 100644 --- a/common/unreachable.hpp +++ b/common/unreachable.hpp @@ -1,69 +1,69 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "crosscompiler_defines.hpp" - -#include -#include -#include - -namespace common { - -class unreachable_code_reached : public std::runtime_error { -public: - explicit unreachable_code_reached(const char* what_arg) - : runtime_error {what_arg} {} - explicit unreachable_code_reached(const std::string& what_arg) - : runtime_error {what_arg} {} - virtual ~unreachable_code_reached() noexcept = default; -}; - -} // namespace common - -#ifndef UNREACHABLE -# ifdef NDEBUG -# if defined(_MSC_VER) -// See https://docs.microsoft.com/en-us/cpp/intrinsics/assume?view=vs-2019 -// "The __assume(0) statement is a special case. Use __assume(0) to indicate a -// code path that cannot be reached." -# define UNREACHABLE(msg) __assume(0) -# elif defined(__GNUC__) -# define UNREACHABLE(msg) __builtin_unreachable() -# else -# define UNREACHABLE(msg) -# endif -# else -# define UNREACHABLE(msg) \ - throw common::unreachable_code_reached { \ - [](const auto& the_msg) -> std::string { \ - std::ostringstream str; \ - str << __FILE__ << ":" << __LINE__ << ":" \ - << REQVM_COMMON_FUNCTION << ": " << the_msg; \ - return str.str(); \ - }(msg) \ - } -# endif -#endif +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "crosscompiler_defines.hpp" + +#include +#include +#include + +namespace common { + +class unreachable_code_reached : public std::runtime_error { +public: + explicit unreachable_code_reached(const char* what_arg) + : runtime_error {what_arg} {} + explicit unreachable_code_reached(const std::string& what_arg) + : runtime_error {what_arg} {} + virtual ~unreachable_code_reached() noexcept = default; +}; + +} // namespace common + +#ifndef UNREACHABLE +# ifdef NDEBUG +# if defined(_MSC_VER) +// See https://docs.microsoft.com/en-us/cpp/intrinsics/assume?view=vs-2019 +// "The __assume(0) statement is a special case. Use __assume(0) to indicate a +// code path that cannot be reached." +# define UNREACHABLE(msg) __assume(0) +# elif defined(__GNUC__) +# define UNREACHABLE(msg) __builtin_unreachable() +# else +# define UNREACHABLE(msg) +# endif +# else +# define UNREACHABLE(msg) \ + throw common::unreachable_code_reached { \ + [](const auto& the_msg) -> std::string { \ + std::ostringstream str; \ + str << __FILE__ << ":" << __LINE__ << ":" \ + << REQVM_COMMON_FUNCTION << ": " << the_msg; \ + return str.str(); \ + }(msg) \ + } +# endif +#endif diff --git a/documentation/assembler_syntax.md b/documentation/assembler_syntax.md index d43f4a2..b8c8e32 100644 --- a/documentation/assembler_syntax.md +++ b/documentation/assembler_syntax.md @@ -1,44 +1,44 @@ -# Syntax notes - -## Preprocessor - -All preprocessor directives must start with a `%`. The following preprocessor directives are supported: -|directive|syntax|notes| -|:-------:|:----:|-----| -|`define`| See [define synatax](#define-syntax)| See [define notes](#define-notes) | -|`if`|`%if cond`|The text between the `%if` and the following `%else`, `%elif`, `%end` will only be assembled if `cond` is true.| -|`else`|`%else`|The text following the `%else` will be assembled if the condition of the previous `%if`/`%elif` was false| -|`elif`|`%elif cond`|The text between the `%elif` and the following `%else`, `%elif` or `%end` will be assembled only if the condition of the previous `%if`/`%elif` was false and `cond` is true.| -|`end`|`%end`|Ends a preprocessor sequence.| - -### `%define` - -#### define syntax - -```reqvmasm -%define NAME %type integral -%define NAME %type string -%define NAME %type instruction_list -``` - -where: - -* `` is a number, such as 1, 0xDEADC0DE, or 0o531565 -* `` is a string literal such as "foobar" -* `` is a list of zero or more instructions, an instruction may appear on the same line as the define itself. - -The above are called 'bodies'. - -`%type` must be followed by either `integral`, `string`, or `instruction_list`, or otherwise the assembler will output an error, and code generation will not be ran. The assembler will *not* try to infer the type on its own. - -A `%define` must be paired with an `%end` directive, which should appear on its own line. - -#### define notes - -The `%define` preprocessor directive creates a *macro*. Macros are replaced with their bodies **only** when their names are prefixed with `@`, otherwise the name is treated as a normal symbol. - -`__counter__` is a special macro predefined by the preprocessor, which has its value incremented with each expansion, and whose initial value is 0. - -## Label syntax - -Labels will follow this syntax: `.label_name:`. Labels must be alphanumeric and should not start with digits(for now). +# Syntax notes + +## Preprocessor + +All preprocessor directives must start with a `%`. The following preprocessor directives are supported: +|directive|syntax|notes| +|:-------:|:----:|-----| +|`define`| See [define synatax](#define-syntax)| See [define notes](#define-notes) | +|`if`|`%if cond`|The text between the `%if` and the following `%else`, `%elif`, `%end` will only be assembled if `cond` is true.| +|`else`|`%else`|The text following the `%else` will be assembled if the condition of the previous `%if`/`%elif` was false| +|`elif`|`%elif cond`|The text between the `%elif` and the following `%else`, `%elif` or `%end` will be assembled only if the condition of the previous `%if`/`%elif` was false and `cond` is true.| +|`end`|`%end`|Ends a preprocessor sequence.| + +### `%define` + +#### define syntax + +```reqvmasm +%define NAME %type integral +%define NAME %type string +%define NAME %type instruction_list +``` + +where: + +* `` is a number, such as 1, 0xDEADC0DE, or 0o531565 +* `` is a string literal such as "foobar" +* `` is a list of zero or more instructions, an instruction may appear on the same line as the define itself. + +The above are called 'bodies'. + +`%type` must be followed by either `integral`, `string`, or `instruction_list`, or otherwise the assembler will output an error, and code generation will not be ran. The assembler will *not* try to infer the type on its own. + +A `%define` must be paired with an `%end` directive, which should appear on its own line. + +#### define notes + +The `%define` preprocessor directive creates a *macro*. Macros are replaced with their bodies **only** when their names are prefixed with `@`, otherwise the name is treated as a normal symbol. + +`__counter__` is a special macro predefined by the preprocessor, which has its value incremented with each expansion, and whose initial value is 0. + +## Label syntax + +Labels will follow this syntax: `.label_name:`. Labels must be alphanumeric and should not start with digits(for now). diff --git a/documentation/build_instructions.md b/documentation/build_instructions.md index f9d62fc..ff92710 100644 --- a/documentation/build_instructions.md +++ b/documentation/build_instructions.md @@ -1,32 +1,32 @@ -# Build instructions - -The reqvm projects consists of two programs, the reqvm virtual machine and the reqvm assembler. - -Note that this guide assumes you're in the root of the repository. - -## Building the virtual machine - -```sh -$ make -C ./vm -``` - -You may optionally specify: - -*`DEBUG`(`yes|no`) - turns off optimizations and adds debug symbols. By default the value is `yes`. - -The binary will be under ./vm by the name vm (with the platform extension suffix if needed). - -## Building the assembler - -```sh -$ make -C ./assembler -``` - -You may optionaly specify: - -* `DEBUG`(`yes|no`), by default the value is `yes` -* `AGGRESIVE_LOGGING`(`yes|no`), by default the value is `yes`. This option makes the assembler log all function call parameters. Please note that this is very verbose. - -The assembler depends on the [magic_enum](https://github.com/Neargye/magic_enum/) library and it is included as a git submodule(under ./assembler/thirdparty), you may clone recursively(`git clone --recursive`), init the submodules yourself(`git submodule init`), or do nothing, the Makefile should be able to take care of it. - -The binary will be under ./assembler by the name assembler (with the platform extension suffix if needed). +# Build instructions + +The reqvm projects consists of two programs, the reqvm virtual machine and the reqvm assembler. + +Note that this guide assumes you're in the root of the repository. + +## Building the virtual machine + +```sh +$ make -C ./vm +``` + +You may optionally specify: + +*`DEBUG`(`yes|no`) - turns off optimizations and adds debug symbols. By default the value is `yes`. + +The binary will be under ./vm by the name vm (with the platform extension suffix if needed). + +## Building the assembler + +```sh +$ make -C ./assembler +``` + +You may optionaly specify: + +* `DEBUG`(`yes|no`), by default the value is `yes` +* `AGGRESIVE_LOGGING`(`yes|no`), by default the value is `yes`. This option makes the assembler log all function call parameters. Please note that this is very verbose. + +The assembler depends on the [magic_enum](https://github.com/Neargye/magic_enum/) library and it is included as a git submodule(under ./assembler/thirdparty), you may clone recursively(`git clone --recursive`), init the submodules yourself(`git submodule init`), or do nothing, the Makefile should be able to take care of it. + +The binary will be under ./assembler by the name assembler (with the platform extension suffix if needed). diff --git a/documentation/specification.md b/documentation/specification.md index 9e27ab0..43a0030 100644 --- a/documentation/specification.md +++ b/documentation/specification.md @@ -1,75 +1,75 @@ -# reqvm specification - -reqvm is a register based bytecode VM. - -Floating point won't be supported for a while. - -reqvm has a 8MiB stack, however the operations work on 8-byte integers, as such you can store -1'048'576 values on it. - -## Binaries - -Binaries start with a 256 byte preamble. I've no idea if I'll ever fill this up, but whatever. - -### Preamble format - -Binaries will start with the following magic byte string `!reqvm;VSszMEI2VSszMEVGVSszMEZDVSszMEVCVSszMEM5VSswMDIxVSs2NjQyVSszMDg4VSs2QjYyVSszMDdFVSszMDhDVSswMDIx\n`. -Then a string of bytes following this format: `!;;;;`. `major`, `minor`, and `patch` are 2-byte numbers. `enabled features` is a string of bytes, every byte represents an individual optional feature, and the presence of the byte enables it. -This is padded with `noop`s until 256 bytes have been reached. - -After that follows the program itself. - -## Registers - -All registers will be 64bit. List of registers: - -* 64 general purpose registers: `gp0..63` -* 16 integer function argument registers: `ifa0..15` -* program counter(read only): `pc` -* a register dedicated to the return value of the last function: `ire` -* the stack pointer: `sp`(read only) - -## Internal VM flags - -* CF("comparison flag") - stores the result of a comparison between two registers. Possible values(these will be reffered to as `cf::value`): `eq`(equal), `less`(less than), `gr`(greater than). - -## Instruction table - -| opcode(byte) | mnemonic | instruction | notes | -|:------:|----------|-------------|------------| -| `00` | `noop` | `noop` | is a noop | -| `01` | `call` | `call func_name` | calls the function `func_name` | -| `02` | `ret` | `ret` | returns from a function, performing necessary cleanup | -| `10` | `io` | `io op reg` | the `io` metainstruction expect a 1-byte argument after it called the `op` which represent the I/O operation to be performed. See [I/O operations](#I/O-Operations) | -| `20` | `add` | `add r1, r2` | adds `r1` and `r2`, stores result in `r1`| -| `21` | `sub` | `sub r1, r2`| subtract `r2` from `r1`, stores result in `r1`| -| `22` | `mul` | `mul r1, r2` | multiplies `r1` by `r2`, stores result in `r1`| -| `23` | `div` | `div r1, r2` | divides `r1` by `r2`, stores quotient in `r1`| -| `24` | `mod` | `mod r1, r2` | divides `r1` by `r2`, stores remainder in `r1`| -| `25` | `and` | `and r1, r2` | bitwise ANDs `r1` and `r2`, stores result in `r1`| -| `26` | `or` | `or r1, r2` | bitwise ORs `r1` and `r2`, stores result in `r1`| -| `27` | `xor` | `xor r1, r2` | bitwise XORs `r1` and `r2`, stores result in `r1`| -| `28` | `not` | `not r1` | bitwise NOTs `r1`, stores result in `r1`| -| `29` | `lshft` | `lshft r1, r2` | Performs `r1 <<= r2` | -| `30` | `rshft` | `rshft r1, r2` | Performs `r1 >>= r2` | -| `35` | `push` | `push reg` | pushes `reg` onto the stack, increases the `sp` by 1| -| `36` | `pushc` | `pushc constant` | pushes the `constant` on the stack, stored in the binary as 8 bytes| -| `37` | `pop` | `pop reg` | pops the top value from the stack into `reg`| -| `42` | `cmp` | `cmp r1, r2` | compares `r1` and `r2`, stores result in CF | -| `43` | `jmp` | `jmp label` | jumps to `label` | -| `44` | `jeq` | `jeq label` | if `CF == cf::eq`, jumps to `label` | -| `45` | `jneq` | `jneq label` | if `CF != cf::eq` jumps to `label` | -| `46` | `jl` | `jl label` | if `CF == cf::less`, jumps to `label` | -| `47` | `jleq` | `jleq label` | if `CF == cf::less` or `CF == cf::eq`, jumps to `label` | -| `48` | `jg` | `jg label` | if `CF == cf::gr`, jumps to `label` | -| `49` | `jgeq` | `jgeq label` | if `CF == cf::gr` or `CF == cf::eq`, jumps to `label` -| `255` | `halt` | `halt` | stops program execution, and the VM, returning the value in `ire` to the OS | - -### I/O Operations - -|byte|mnemonic|argument|notes| -|---|---------|--------|-----| -|`01`|`getc`|a register |gets a character from stdin and stores it in the register passed as argument| -|`02`|`putc`|a register|interprets the register given as argument as an **8-bit** character and outputs it to stdout| -|`03`|`put8c`|a register|interprets the register as a string of 8 **8-bit** characters and outputs them to stdout| -|`04`|`putn`|a register|interprets the register as a **64-bit** number and outputs it to stdout| +# reqvm specification + +reqvm is a register based bytecode VM. + +Floating point won't be supported for a while. + +reqvm has a 8MiB stack, however the operations work on 8-byte integers, as such you can store +1'048'576 values on it. + +## Binaries + +Binaries start with a 256 byte preamble. I've no idea if I'll ever fill this up, but whatever. + +### Preamble format + +Binaries will start with the following magic byte string `!reqvm;VSszMEI2VSszMEVGVSszMEZDVSszMEVCVSszMEM5VSswMDIxVSs2NjQyVSszMDg4VSs2QjYyVSszMDdFVSszMDhDVSswMDIx\n`. +Then a string of bytes following this format: `!;;;;`. `major`, `minor`, and `patch` are 2-byte numbers. `enabled features` is a string of bytes, every byte represents an individual optional feature, and the presence of the byte enables it. +This is padded with `noop`s until 256 bytes have been reached. + +After that follows the program itself. + +## Registers + +All registers will be 64bit. List of registers: + +* 64 general purpose registers: `gp0..63` +* 16 integer function argument registers: `ifa0..15` +* program counter(read only): `pc` +* a register dedicated to the return value of the last function: `ire` +* the stack pointer: `sp`(read only) + +## Internal VM flags + +* CF("comparison flag") - stores the result of a comparison between two registers. Possible values(these will be reffered to as `cf::value`): `eq`(equal), `less`(less than), `gr`(greater than). + +## Instruction table + +| opcode(byte) | mnemonic | instruction | notes | +|:------:|----------|-------------|------------| +| `00` | `noop` | `noop` | is a noop | +| `01` | `call` | `call func_name` | calls the function `func_name` | +| `02` | `ret` | `ret` | returns from a function, performing necessary cleanup | +| `10` | `io` | `io op reg` | the `io` metainstruction expect a 1-byte argument after it called the `op` which represent the I/O operation to be performed. See [I/O operations](#I/O-Operations) | +| `20` | `add` | `add r1, r2` | adds `r1` and `r2`, stores result in `r1`| +| `21` | `sub` | `sub r1, r2`| subtract `r2` from `r1`, stores result in `r1`| +| `22` | `mul` | `mul r1, r2` | multiplies `r1` by `r2`, stores result in `r1`| +| `23` | `div` | `div r1, r2` | divides `r1` by `r2`, stores quotient in `r1`| +| `24` | `mod` | `mod r1, r2` | divides `r1` by `r2`, stores remainder in `r1`| +| `25` | `and` | `and r1, r2` | bitwise ANDs `r1` and `r2`, stores result in `r1`| +| `26` | `or` | `or r1, r2` | bitwise ORs `r1` and `r2`, stores result in `r1`| +| `27` | `xor` | `xor r1, r2` | bitwise XORs `r1` and `r2`, stores result in `r1`| +| `28` | `not` | `not r1` | bitwise NOTs `r1`, stores result in `r1`| +| `29` | `lshft` | `lshft r1, r2` | Performs `r1 <<= r2` | +| `30` | `rshft` | `rshft r1, r2` | Performs `r1 >>= r2` | +| `35` | `push` | `push reg` | pushes `reg` onto the stack, increases the `sp` by 1| +| `36` | `pushc` | `pushc constant` | pushes the `constant` on the stack, stored in the binary as 8 bytes| +| `37` | `pop` | `pop reg` | pops the top value from the stack into `reg`| +| `42` | `cmp` | `cmp r1, r2` | compares `r1` and `r2`, stores result in CF | +| `43` | `jmp` | `jmp label` | jumps to `label` | +| `44` | `jeq` | `jeq label` | if `CF == cf::eq`, jumps to `label` | +| `45` | `jneq` | `jneq label` | if `CF != cf::eq` jumps to `label` | +| `46` | `jl` | `jl label` | if `CF == cf::less`, jumps to `label` | +| `47` | `jleq` | `jleq label` | if `CF == cf::less` or `CF == cf::eq`, jumps to `label` | +| `48` | `jg` | `jg label` | if `CF == cf::gr`, jumps to `label` | +| `49` | `jgeq` | `jgeq label` | if `CF == cf::gr` or `CF == cf::eq`, jumps to `label` +| `255` | `halt` | `halt` | stops program execution, and the VM, returning the value in `ire` to the OS | + +### I/O Operations + +|byte|mnemonic|argument|notes| +|---|---------|--------|-----| +|`01`|`getc`|a register |gets a character from stdin and stores it in the register passed as argument| +|`02`|`putc`|a register|interprets the register given as argument as an **8-bit** character and outputs it to stdout| +|`03`|`put8c`|a register|interprets the register as a string of 8 **8-bit** characters and outputs them to stdout| +|`04`|`putn`|a register|interprets the register as a **64-bit** number and outputs it to stdout| diff --git a/examples/assembler/assembler_test.reqasm b/examples/assembler/assembler_test.reqasm index 0bd6f97..25500df 100644 --- a/examples/assembler/assembler_test.reqasm +++ b/examples/assembler/assembler_test.reqasm @@ -1,32 +1,34 @@ -;; -;; MIT License -;; -;; Copyright (c) 2020 Mitca Dumitru -;; -;; Permission is hereby granted, free of charge, to any person obtaining a copy -;; of this software and associated documentation files (the "Software"), to deal -;; in the Software without restriction, including without limitation the rights -;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -;; copies of the Software, and to permit persons to whom the Software is -;; furnished to do so, subject to the following conditions: -;; -;; The above copyright notice and this permission notice shall be included in all -;; copies or substantial portions of the Software. -;; -;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -;; SOFTWARE. -;; - -; this is just a basic thing to test the assembler - pushc 1 - pushc 2 - pop gp01 - pop gp02 - add gp01, gp02 - push gp01 - add gp01, sp \ No newline at end of file +;; +;; MIT License +;; +;; Copyright (c) 2020 Mitca Dumitru +;; +;; Permission is hereby granted, free of charge, to any person obtaining a copy +;; of this software and associated documentation files (the "Software"), to deal +;; in the Software without restriction, including without limitation the rights +;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +;; copies of the Software, and to permit persons to whom the Software is +;; furnished to do so, subject to the following conditions: +;; +;; The above copyright notice and this permission notice shall be included in all +;; copies or substantial portions of the Software. +;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +;; SOFTWARE. +;; + +; this is just a basic thing to test the assembler + pushc 1 + pushc 2 + pop gp01 + pop gp02 + add gp01, gp02 + push gp01 + add gp01, sp + io putn gp01 + io putn sp \ No newline at end of file diff --git a/vm/Makefile b/vm/Makefile index d005741..107b6c4 100644 --- a/vm/Makefile +++ b/vm/Makefile @@ -1,49 +1,49 @@ -# The Ark Makefile:tm: -NAME= vm - -CC= g++ -CFLAGS= -std=c++17 -Wall -Wextra -fexceptions -LDFLAGS= - -SRCDIR= src -OBJDIR= obj -# END CONFIG - - -rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) -SRC = $(call rwildcard,$(SRCDIR),*.cpp) -OBJ = $(SRC:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o) - -#$(warning $(OBJ)) -#$(warning $(SRC)) - -DEBUG = yes -ifeq ($(DEBUG), yes) - CFLAGS += -Og -g -else - CFLAGS += -O3 -endif -# AUTO VARIABLE DEFINITION - - -build: $(NAME) - -$(NAME): $(OBJ) - $(CC) -o $@ $^ $(CFLAGS) - --include $(OBJ:.o=.d) - -$(OBJDIR)/%.o: $(SRCDIR)/%.cpp - @mkdir -p $(@D) - $(CC) -o $@ $< $(CFLAGS) -c -MMD - @mv -f $(OBJDIR)/$*.d $(OBJDIR)/$*.d.tmp - @sed -e 's|.*:|$(OBJDIR)/$*.o:|' < $(OBJDIR)/$*.d.tmp > $(OBJDIR)/$*.d - @sed -e 's/.*://' -e 's/\\$$//' < $(OBJDIR)/$*.d.tmp | fmt -1 | \ - sed -e 's/^ *//' -e 's/$$/:/' >> $(OBJDIR)/$*.d - @sed -i '/\\\:/d' $(OBJDIR)/$*.d - @rm -f $(OBJDIR)/$*.d.tmp - -.PHONY: clean -clean: - rm -f $(NAME) - rm -rf $(OBJDIR)/* +# The Ark Makefile:tm: +NAME= vm + +CC= g++ +CFLAGS= -std=c++17 -Wall -Wextra -fexceptions +LDFLAGS= + +SRCDIR= src +OBJDIR= obj +# END CONFIG + + +rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) +SRC = $(call rwildcard,$(SRCDIR),*.cpp) +OBJ = $(SRC:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o) + +#$(warning $(OBJ)) +#$(warning $(SRC)) + +DEBUG = yes +ifeq ($(DEBUG), yes) + CFLAGS += -Og -g +else + CFLAGS += -O3 +endif +# AUTO VARIABLE DEFINITION + + +build: $(NAME) + +$(NAME): $(OBJ) + $(CC) -o $@ $^ $(CFLAGS) + +-include $(OBJ:.o=.d) + +$(OBJDIR)/%.o: $(SRCDIR)/%.cpp + @mkdir -p $(@D) + $(CC) -o $@ $< $(CFLAGS) -c -MMD + @mv -f $(OBJDIR)/$*.d $(OBJDIR)/$*.d.tmp + @sed -e 's|.*:|$(OBJDIR)/$*.o:|' < $(OBJDIR)/$*.d.tmp > $(OBJDIR)/$*.d + @sed -e 's/.*://' -e 's/\\$$//' < $(OBJDIR)/$*.d.tmp | fmt -1 | \ + sed -e 's/^ *//' -e 's/$$/:/' >> $(OBJDIR)/$*.d + @sed -i '/\\\:/d' $(OBJDIR)/$*.d + @rm -f $(OBJDIR)/$*.d.tmp + +.PHONY: clean +clean: + rm -f $(NAME) + rm -rf $(OBJDIR)/* diff --git a/vm/src/binary_manager.cpp b/vm/src/binary_manager.cpp index f84050d..e3c1d6d 100644 --- a/vm/src/binary_manager.cpp +++ b/vm/src/binary_manager.cpp @@ -1,41 +1,41 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "binary_manager.hpp" - -#include "binary_managers/memory_mapped_file_backed.hpp" -#include "binary_managers/vector_backed.hpp" - -namespace reqvm { - -auto load_from(const fs::path& path) -> std::unique_ptr { - auto file_size = fs::file_size(path); - if (file_size < std::uintmax_t {64} * 1024 * 1024) { - return std::make_unique( - path, static_cast(file_size)); - } - return std::make_unique(path); -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "binary_manager.hpp" + +#include "binary_managers/memory_mapped_file_backed.hpp" +#include "binary_managers/vector_backed.hpp" + +namespace reqvm { + +auto load_from(const fs::path& path) -> std::unique_ptr { + auto file_size = fs::file_size(path); + if (file_size < std::uintmax_t {64} * 1024 * 1024) { + return std::make_unique( + path, static_cast(file_size)); + } + return std::make_unique(path); +} + +} // namespace reqvm diff --git a/vm/src/binary_manager.hpp b/vm/src/binary_manager.hpp index 63da273..65b9d7d 100644 --- a/vm/src/binary_manager.hpp +++ b/vm/src/binary_manager.hpp @@ -1,60 +1,60 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "utility.hpp" - -#include -#include -#include -#include - -namespace fs = std::filesystem; - -namespace reqvm { - -/* - * A binary_manager is an interface modeling the operations that the VM needs to - * do on a binary. - * - * As a polymoprhic type it is not movable or copyable to prevent slicing - * issues. - */ -class binary_manager { - REQVM_MAKE_NONCOPYABLE(binary_manager) - REQVM_MAKE_NONMOVABLE(binary_manager) -public: - binary_manager() noexcept = default; - - virtual ~binary_manager() noexcept = default; - - virtual auto operator[](std::size_t idx) noexcept -> std::uint8_t = 0; - - virtual auto size() noexcept -> std::size_t = 0; -}; - -auto load_from(const fs::path&) -> std::unique_ptr; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "utility.hpp" + +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace reqvm { + +/* + * A binary_manager is an interface modeling the operations that the VM needs to + * do on a binary. + * + * As a polymoprhic type it is not movable or copyable to prevent slicing + * issues. + */ +class binary_manager { + REQVM_MAKE_NONCOPYABLE(binary_manager) + REQVM_MAKE_NONMOVABLE(binary_manager) +public: + binary_manager() noexcept = default; + + virtual ~binary_manager() noexcept = default; + + virtual auto operator[](std::size_t idx) noexcept -> std::uint8_t = 0; + + virtual auto size() noexcept -> std::size_t = 0; +}; + +auto load_from(const fs::path&) -> std::unique_ptr; + +} // namespace reqvm diff --git a/vm/src/binary_managers/exceptions.hpp b/vm/src/binary_managers/exceptions.hpp index 2886889..58cec51 100644 --- a/vm/src/binary_managers/exceptions.hpp +++ b/vm/src/binary_managers/exceptions.hpp @@ -1,73 +1,73 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../detect_platform.hpp" - -#include - -namespace reqvm { - -/* - * Represents an error encountered in the process of memory mapping a file - * - * Definitions for the member functions of this class are present in - * memory_mapped_file_backed.{win32,posix}.ipp in a platform appropriate manner - */ -class mmap_error : public std::exception { -public: -#if defined(REQVM_ON_WINDOWS) - // ::DWORD is defined as unsigned long, and as Microsoft is known for their - // backwards compatibility, that won't change. - // This way we can avoid including windows.h here. - using error_code_t = unsigned long; -#elif defined(REQVM_ON_POSIX) - using error_code_t = int; -#endif - enum class kind { - file = 1, - mapping, - file_size, - }; - - mmap_error(error_code_t, kind) noexcept; - - virtual ~mmap_error() noexcept; - - auto what() const noexcept -> const char* override; - -private: -// On MS Windows we store the message associated with the error code -// On POSIX Platforms we store the error code and its kind and retrieve a -// message associated with it in ::what() -#if defined(REQVM_ON_WINDOWS) - char* _message {nullptr}; -#elif defined(REQVM_ON_POSIX) - error_code_t _ec {0}; - kind _k {0}; -#endif -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../detect_platform.hpp" + +#include + +namespace reqvm { + +/* + * Represents an error encountered in the process of memory mapping a file + * + * Definitions for the member functions of this class are present in + * memory_mapped_file_backed.{win32,posix}.ipp in a platform appropriate manner + */ +class mmap_error : public std::exception { +public: +#if defined(REQVM_ON_WINDOWS) + // ::DWORD is defined as unsigned long, and as Microsoft is known for their + // backwards compatibility, that won't change. + // This way we can avoid including windows.h here. + using error_code_t = unsigned long; +#elif defined(REQVM_ON_POSIX) + using error_code_t = int; +#endif + enum class kind { + file = 1, + mapping, + file_size, + }; + + mmap_error(error_code_t, kind) noexcept; + + virtual ~mmap_error() noexcept; + + auto what() const noexcept -> const char* override; + +private: +// On MS Windows we store the message associated with the error code +// On POSIX Platforms we store the error code and its kind and retrieve a +// message associated with it in ::what() +#if defined(REQVM_ON_WINDOWS) + char* _message {nullptr}; +#elif defined(REQVM_ON_POSIX) + error_code_t _ec {0}; + kind _k {0}; +#endif +}; + +} // namespace reqvm diff --git a/vm/src/binary_managers/memory_mapped_file_backed.cpp b/vm/src/binary_managers/memory_mapped_file_backed.cpp index be7589f..20565ff 100644 --- a/vm/src/binary_managers/memory_mapped_file_backed.cpp +++ b/vm/src/binary_managers/memory_mapped_file_backed.cpp @@ -1,55 +1,55 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "memory_mapped_file_backed.hpp" - -#define REQVM_IN_THE_MMF_CPP_FILE -#if defined(REQVM_ON_WINDOWS) -# include "memory_mapped_file_backed.win32.ipp" -#elif defined(REQVM_ON_POSIX) -# include "memory_mapped_file_backed.posix.ipp" -#endif -#undef REQVM_IN_THE_MMF_CPP_FILE - -/* - * README: - * - * This file is only meant to contain the platform agnostic code of - * mmf_backed_binary_manager. - * - * All platform specific code should reside in the appropriate .ipp files. - */ - -namespace reqvm { - -auto mmf_backed_binary_manager::operator[](std::size_t idx) noexcept - -> std::uint8_t { - return _data[idx]; -} - -auto mmf_backed_binary_manager::size() noexcept -> std::size_t { - return _size; -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "memory_mapped_file_backed.hpp" + +#define REQVM_IN_THE_MMF_CPP_FILE +#if defined(REQVM_ON_WINDOWS) +# include "memory_mapped_file_backed.win32.ipp" +#elif defined(REQVM_ON_POSIX) +# include "memory_mapped_file_backed.posix.ipp" +#endif +#undef REQVM_IN_THE_MMF_CPP_FILE + +/* + * README: + * + * This file is only meant to contain the platform agnostic code of + * mmf_backed_binary_manager. + * + * All platform specific code should reside in the appropriate .ipp files. + */ + +namespace reqvm { + +auto mmf_backed_binary_manager::operator[](std::size_t idx) noexcept + -> std::uint8_t { + return _data[idx]; +} + +auto mmf_backed_binary_manager::size() noexcept -> std::size_t { + return _size; +} + +} // namespace reqvm diff --git a/vm/src/binary_managers/memory_mapped_file_backed.hpp b/vm/src/binary_managers/memory_mapped_file_backed.hpp index 6db9f57..2427e96 100644 --- a/vm/src/binary_managers/memory_mapped_file_backed.hpp +++ b/vm/src/binary_managers/memory_mapped_file_backed.hpp @@ -1,70 +1,70 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../binary_manager.hpp" -#include "../detect_platform.hpp" -#include "../utility.hpp" - -#include -#include - -namespace reqvm { - -class mmf_backed_binary_manager final : public binary_manager { - REQVM_MAKE_NONCOPYABLE(mmf_backed_binary_manager) - REQVM_MAKE_NONMOVABLE(mmf_backed_binary_manager) -public: - mmf_backed_binary_manager() = delete; - explicit mmf_backed_binary_manager(const fs::path&); - - virtual ~mmf_backed_binary_manager() noexcept; - - auto operator[](std::size_t idx) noexcept -> std::uint8_t override; - - auto size() noexcept -> std::size_t override; - -private: -#if defined(REQVM_ON_WINDOWS) - // We use this instead of ::HANDLE to avoid including here - // "But what if Microsoft decides to change that typedef?" - // Well blimey, that'd be quiet the ABI breaking change, and Microsoft is - // known for their backwards compat, so it won't be happening dawg. - using handle_t = void*; - handle_t _file; - handle_t _mapping; -#endif - /* NOTE: - * * On MS Windows this is a view into the mapping - * * On POSIX platforms this is both the view and the handle to the - * mapping - */ - std::uint8_t* _data; - - // We cache the size as it is requested every cycle by the VM - std::size_t _size; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../binary_manager.hpp" +#include "../detect_platform.hpp" +#include "../utility.hpp" + +#include +#include + +namespace reqvm { + +class mmf_backed_binary_manager final : public binary_manager { + REQVM_MAKE_NONCOPYABLE(mmf_backed_binary_manager) + REQVM_MAKE_NONMOVABLE(mmf_backed_binary_manager) +public: + mmf_backed_binary_manager() = delete; + explicit mmf_backed_binary_manager(const fs::path&); + + virtual ~mmf_backed_binary_manager() noexcept; + + auto operator[](std::size_t idx) noexcept -> std::uint8_t override; + + auto size() noexcept -> std::size_t override; + +private: +#if defined(REQVM_ON_WINDOWS) + // We use this instead of ::HANDLE to avoid including here + // "But what if Microsoft decides to change that typedef?" + // Well blimey, that'd be quiet the ABI breaking change, and Microsoft is + // known for their backwards compat, so it won't be happening dawg. + using handle_t = void*; + handle_t _file; + handle_t _mapping; +#endif + /* NOTE: + * * On MS Windows this is a view into the mapping + * * On POSIX platforms this is both the view and the handle to the + * mapping + */ + std::uint8_t* _data; + + // We cache the size as it is requested every cycle by the VM + std::size_t _size; +}; + +} // namespace reqvm diff --git a/vm/src/binary_managers/memory_mapped_file_backed.posix.ipp b/vm/src/binary_managers/memory_mapped_file_backed.posix.ipp index 727be63..32e05d1 100644 --- a/vm/src/binary_managers/memory_mapped_file_backed.posix.ipp +++ b/vm/src/binary_managers/memory_mapped_file_backed.posix.ipp @@ -1,204 +1,204 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "memory_mapped_file_backed.hpp" - -/* - * README: - * - * Please note that this is not a classical header file (and as such lacks a - * #pragma once directive) and is only meant to contained the POSIX specific - * code of mmf_backed_binary_manager. - * - * It only has a .ipp extension so the Makefile ignores it. (There probably is a - * better way, but I'm lazy. PR the better way if this bothers you that much. - * Putting all the platform specific code in a single file is not a better way.) - */ - -#if !defined(REQVM_ON_POSIX) -# error "This file should only be used when compiling for POSIX OS'es" -#endif - -#if !defined(REQVM_IN_THE_MMF_CPP_FILE) -# error "This file should only be included by memory_mapped_file_backed.cpp" -#endif - -#include "exceptions.hpp" - -#include -#include -#include -#include -#include -#include - -namespace reqvm { - -// Not sure how strong my error handling game is here. If you wish for it to be -// improved, send a PR my way. -mmf_backed_binary_manager::mmf_backed_binary_manager(const fs::path& path) { -#define IGNORE_RETURN(x) (void)(x) - - auto fd = ::open(path.c_str(), O_RDONLY); - - if (fd == -1) { - throw mmap_error {errno, mmap_error::kind::file}; - } - - { - // We ask the kernel for the file size so we're 100% in sync with what - // the kernel believes the file size is. - struct stat st; - if (::fstat(fd, &st) != 0) { - auto old_errno = errno; - // I think the fstat error is more important in my case - // All file descriptors should be closed soon anyway, since we're - // gonna shut down anyway. - // Though we could just leave it to the kernel to clean it? 🤔 - IGNORE_RETURN(::close(fd)); - throw mmap_error {old_errno, mmap_error::kind::file_size}; - } - _size = static_cast(st.st_size); - } - - _data = static_cast( - ::mmap(nullptr, _size, PROT_READ, MAP_SHARED, fd, 0)); - if (_data == static_cast(MAP_FAILED)) { - auto old_errno = errno; - // See previous disclaimer on ::close() - IGNORE_RETURN(::close(fd)); - throw mmap_error {errno, mmap_error::kind::mapping}; - } - - /* - * We close the file descriptor here, as this Stack Overflow post: - * https://stackoverflow.com/a/17490185 indicates it should be safe - * Notes on errors: - * * EBADF shouldn't happen. - * * On EINTR: https://stackoverflow.com/a/33114363 - * There are other POSIX systems sure, but whatever, the file - * stays open anyway(because it's mmaped), so I'll ignore it. - * * We don't write, so I don't think ENOSPC or EDQUOT should occur - * So we end up caring for EIO only. - */ - if (::close(fd) != 0 && errno == EIO) { - // Maybe ignoring this unmap error isn't that good of an idea, BUT - // the error we wish to report is EIO, so... - IGNORE_RETURN(::munmap(_data, _size)); - throw mmap_error {EIO, mmap_error::kind::file}; - } -#undef IGNORE_RETURN -} - -mmf_backed_binary_manager::~mmf_backed_binary_manager() { - // Unmapping can fail, how (if we should) handle that? - ::munmap(_data, _size); -} - -mmap_error::mmap_error(error_code_t ec, kind k) noexcept : _ec {ec}, _k {k} {} - -mmap_error::~mmap_error() noexcept = default; - -auto mmap_error::what() const noexcept -> const char* { -#define CASE(the_error_code, message) \ - case the_error_code: \ - return PREFIX message " (" #the_error_code ")"; - // We check the error values in their respective domains as that's the only - // way to make sense of them. - switch (_k) { - case kind::file: { -#define PREFIX "An error occured while trying to open the binary:" - switch (_ec) { - // clang-format off - // Note: this doesn't check all error codes from open - // I only picked those that seemed relevant for my usecase - // I also have written error messages corresponding to the - // kind of errors we should get in our usecase - CASE(EACCES, "Access to the file not allowed, or search permission " - "denied for one of the directories in the path.") - CASE(ELOOP, "Too many symbolic links were encountered while " - "trying to resolve the path of the binary.") - CASE(ENAMETOOLONG, "The path was too long.") - CASE(ENFILE, "The system-wide limit on the total number of open " - "files was reached.") - CASE(ENOENT, "The file does not exist or a directory in your path " - "doesn't exist or is a dangling symbolic link.") - CASE(ENOMEM, "There wasn't enough kernel memory.") - CASE(ENOTDIR, "A directory component in your path is not actually " - "a directory.") - CASE(EOVERFLOW, "The file was too large to be opened.") - CASE(EIO, "An I/O error occured.") - // clang-format on - - default: - return "An unknown error occured while trying to open the file."; - } -#undef PREFIX - } - case kind::file_size: { -#define PREFIX \ - "An error occured while trying to obtain the size of the binary: " - switch (_ec) { - // clang-format off - // Note: See previous note, same things apply - // Except errors that can happen at the ::open level are not - // checked for here - CASE(ENOMEM, "There wasn't enough kernel memory.") - CASE(EOVERFLOW, "The file size/inode number/number of bytes cannot be" - "expressed in the type off_t/ino_t/blkcnt_t.") - // clang-format on - default: - return "An unknown error occured while trying to obtain the size " - "of the binary."; - } -#undef PREFIX - } - case kind::mapping: { -#define PREFIX "An error occured while trying to memory map the binary: " - switch (_ec) { - // clang-format off - CASE(EACCES, "The file descriptor referring to your file is nonregular") - CASE(ENFILE, "The system-wide limit on the total number of open " - "files was reached.") - CASE(ENOMEM, "No memory is available") // Can this happen in our situation ??? - CASE(ENODEV, "The filesystem on which your binary resides does not" - " support memory mapping.") - CASE(EINVAL, "Either your file was zero-sized (this indicates a bug in " - "reqvm as this code path is not supposed to be reached " - "in that case. Please report a bug at" - "https://github.com/RealKC/reqvm) or your file was too" - "large to be memory mapped.") - // clang-format on - - default: - return "An unknown error occured while trying to memory map the " - "binary."; - } -#undef PREFIX - } - } -#undef CASE -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "memory_mapped_file_backed.hpp" + +/* + * README: + * + * Please note that this is not a classical header file (and as such lacks a + * #pragma once directive) and is only meant to contained the POSIX specific + * code of mmf_backed_binary_manager. + * + * It only has a .ipp extension so the Makefile ignores it. (There probably is a + * better way, but I'm lazy. PR the better way if this bothers you that much. + * Putting all the platform specific code in a single file is not a better way.) + */ + +#if !defined(REQVM_ON_POSIX) +# error "This file should only be used when compiling for POSIX OS'es" +#endif + +#if !defined(REQVM_IN_THE_MMF_CPP_FILE) +# error "This file should only be included by memory_mapped_file_backed.cpp" +#endif + +#include "exceptions.hpp" + +#include +#include +#include +#include +#include +#include + +namespace reqvm { + +// Not sure how strong my error handling game is here. If you wish for it to be +// improved, send a PR my way. +mmf_backed_binary_manager::mmf_backed_binary_manager(const fs::path& path) { +#define IGNORE_RETURN(x) (void)(x) + + auto fd = ::open(path.c_str(), O_RDONLY); + + if (fd == -1) { + throw mmap_error {errno, mmap_error::kind::file}; + } + + { + // We ask the kernel for the file size so we're 100% in sync with what + // the kernel believes the file size is. + struct stat st; + if (::fstat(fd, &st) != 0) { + auto old_errno = errno; + // I think the fstat error is more important in my case + // All file descriptors should be closed soon anyway, since we're + // gonna shut down anyway. + // Though we could just leave it to the kernel to clean it? 🤔 + IGNORE_RETURN(::close(fd)); + throw mmap_error {old_errno, mmap_error::kind::file_size}; + } + _size = static_cast(st.st_size); + } + + _data = static_cast( + ::mmap(nullptr, _size, PROT_READ, MAP_SHARED, fd, 0)); + if (_data == static_cast(MAP_FAILED)) { + auto old_errno = errno; + // See previous disclaimer on ::close() + IGNORE_RETURN(::close(fd)); + throw mmap_error {errno, mmap_error::kind::mapping}; + } + + /* + * We close the file descriptor here, as this Stack Overflow post: + * https://stackoverflow.com/a/17490185 indicates it should be safe + * Notes on errors: + * * EBADF shouldn't happen. + * * On EINTR: https://stackoverflow.com/a/33114363 + * There are other POSIX systems sure, but whatever, the file + * stays open anyway(because it's mmaped), so I'll ignore it. + * * We don't write, so I don't think ENOSPC or EDQUOT should occur + * So we end up caring for EIO only. + */ + if (::close(fd) != 0 && errno == EIO) { + // Maybe ignoring this unmap error isn't that good of an idea, BUT + // the error we wish to report is EIO, so... + IGNORE_RETURN(::munmap(_data, _size)); + throw mmap_error {EIO, mmap_error::kind::file}; + } +#undef IGNORE_RETURN +} + +mmf_backed_binary_manager::~mmf_backed_binary_manager() { + // Unmapping can fail, how (if we should) handle that? + ::munmap(_data, _size); +} + +mmap_error::mmap_error(error_code_t ec, kind k) noexcept : _ec {ec}, _k {k} {} + +mmap_error::~mmap_error() noexcept = default; + +auto mmap_error::what() const noexcept -> const char* { +#define CASE(the_error_code, message) \ + case the_error_code: \ + return PREFIX message " (" #the_error_code ")"; + // We check the error values in their respective domains as that's the only + // way to make sense of them. + switch (_k) { + case kind::file: { +#define PREFIX "An error occured while trying to open the binary:" + switch (_ec) { + // clang-format off + // Note: this doesn't check all error codes from open + // I only picked those that seemed relevant for my usecase + // I also have written error messages corresponding to the + // kind of errors we should get in our usecase + CASE(EACCES, "Access to the file not allowed, or search permission " + "denied for one of the directories in the path.") + CASE(ELOOP, "Too many symbolic links were encountered while " + "trying to resolve the path of the binary.") + CASE(ENAMETOOLONG, "The path was too long.") + CASE(ENFILE, "The system-wide limit on the total number of open " + "files was reached.") + CASE(ENOENT, "The file does not exist or a directory in your path " + "doesn't exist or is a dangling symbolic link.") + CASE(ENOMEM, "There wasn't enough kernel memory.") + CASE(ENOTDIR, "A directory component in your path is not actually " + "a directory.") + CASE(EOVERFLOW, "The file was too large to be opened.") + CASE(EIO, "An I/O error occured.") + // clang-format on + + default: + return "An unknown error occured while trying to open the file."; + } +#undef PREFIX + } + case kind::file_size: { +#define PREFIX \ + "An error occured while trying to obtain the size of the binary: " + switch (_ec) { + // clang-format off + // Note: See previous note, same things apply + // Except errors that can happen at the ::open level are not + // checked for here + CASE(ENOMEM, "There wasn't enough kernel memory.") + CASE(EOVERFLOW, "The file size/inode number/number of bytes cannot be" + "expressed in the type off_t/ino_t/blkcnt_t.") + // clang-format on + default: + return "An unknown error occured while trying to obtain the size " + "of the binary."; + } +#undef PREFIX + } + case kind::mapping: { +#define PREFIX "An error occured while trying to memory map the binary: " + switch (_ec) { + // clang-format off + CASE(EACCES, "The file descriptor referring to your file is nonregular") + CASE(ENFILE, "The system-wide limit on the total number of open " + "files was reached.") + CASE(ENOMEM, "No memory is available") // Can this happen in our situation ??? + CASE(ENODEV, "The filesystem on which your binary resides does not" + " support memory mapping.") + CASE(EINVAL, "Either your file was zero-sized (this indicates a bug in " + "reqvm as this code path is not supposed to be reached " + "in that case. Please report a bug at" + "https://github.com/RealKC/reqvm) or your file was too" + "large to be memory mapped.") + // clang-format on + + default: + return "An unknown error occured while trying to memory map the " + "binary."; + } +#undef PREFIX + } + } +#undef CASE +} + +} // namespace reqvm diff --git a/vm/src/binary_managers/memory_mapped_file_backed.win32.ipp b/vm/src/binary_managers/memory_mapped_file_backed.win32.ipp index 5f4e6da..7836ea5 100644 --- a/vm/src/binary_managers/memory_mapped_file_backed.win32.ipp +++ b/vm/src/binary_managers/memory_mapped_file_backed.win32.ipp @@ -1,218 +1,218 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "memory_mapped_file_backed.hpp" - -/* - * README: - * - * Please note that this is not a classical header file (and as such lacks a - * #pragma once directive) and is only meant to include the Windows specific - * code of mmf_backed_binary_manager. - * - * It only has a .ipp extension so the Makefile ignores it. (There probably is a - * better way, but I'm lazy. PR the better way if this bothers you that much. - * Putting all the platform specific code in a single file is not a better way.) - */ - -#if !defined(REQVM_ON_WINDOWS) -# error "This file should only be used when compiling for MS Windows" -#endif - -#if !defined(REQVM_IN_THE_MMF_CPP_FILE) -# error "This file should only be included by memory_mapped_file_backed.cpp" -#endif - -#include "../utility.hpp" -#include "exceptions.hpp" - -#include -#include - -// How many macros are you even defining? -// Like 1 or 2 my dude -#ifndef NOMINMAX -# define NOMINMAX -#endif -#define WIN32_LEAN_AND_MEAN -// You are like a child -// watch this -#define NOATOM -#define NOCOLOR -#define NOCOMM -#define NOCTLMGR -#define NODRAWTEXT -#define NOGDICAPMASKS -#define NOHELP -#define NOICONS -#define NOKEYSTATES -#define NOMB -#define NOMCX -#define NOMETAFILE -#define NOMSG -#define NORASTEROPS -#define NOSCROLL -#define NOSERVICE -#define NOSHOWINDOW -#define NOSOUND -#define NOSYSMETRICS -#define NOTEXTMETRIC -#define NOVIRTUALKEYCODES -#define NOWH -#define NOWINMESSAGES -#define NOWINSTYLES -#define NOWINSTYLES -#include -// Memes aside, all those defines should help with building times -// It's not like that stuff removes anything we use anyway -// Those defines were brought to you by https://stackoverflow.com/a/1394929 -// Think those above are enough, some are implied by WIN32_LEAN_AND_MEAN - -namespace reqvm { - -mmf_backed_binary_manager::mmf_backed_binary_manager(const fs::path& path) { - _file = ::CreateFileW(path.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, nullptr); - if (_file == INVALID_HANDLE_VALUE) { - throw mmap_error {::GetLastError(), mmap_error::kind::file}; - } - - { - // We ask the kernel for the file size so we're 100% sure we're in sync - // with what the kernel believes. - ::LARGE_INTEGER sz; - if (::GetFileSizeEx(_file, &sz)) { - throw mmap_error {::GetLastError(), mmap_error::kind::file_size}; - } - _size = static_cast(sz.QuadPart); - } - - _mapping = - ::CreateFileMappingA(_file, nullptr, PAGE_READONLY, 0, 0, nullptr); - if (not _mapping) { - throw mmap_error {::GetLastError(), mmap_error::kind::mapping}; - } - - _data = static_cast( - ::MapViewOfFile(_mapping, FILE_MAP_READ, 0, 0, 0)); - if (not _data) { - throw mmap_error {::GetLastError(), mmap_error::kind::mapping}; - } -} - -mmf_backed_binary_manager::~mmf_backed_binary_manager() noexcept { - // Technically these functions can fail, but I'm not sure how to communicate - // that. TODO: find a way - // Though this destructor only gets called when the VM is shutting down. - // So is failing really an issue? Win32 API Gods help me plz. - ::UnmapViewOfFile(_data); - ::CloseHandle(_mapping); - ::CloseHandle(_file); -} - -mmap_error::mmap_error(error_code_t ec, kind k) noexcept { - class deferred_local_free final { - REQVM_MAKE_NONCOPYABLE(deferred_local_free) - REQVM_MAKE_NONMOVABLE(deferred_local_free) - - deferred_local_free(void* p) noexcept : _p {p} {} - ~deferred_local_free() noexcept { - ::LocalFree(_p); - } - - private: - void* _p; - }; - - char* err_msg {nullptr}; - auto succeeded = ::FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM - | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, ec, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&err_msg, - 0, nullptr); - - if (not succeeded) { - const char* fmt_string = - "An error occured while retrieving extended format information. " - "The error code returned by the system API: %d."; - int ec_as_int = static_cast(ec); - auto size = std::snprintf(nullptr, 0, fmt_string, ec_as_int); - if (size < 0) { - // Probably memory exhaustion? kinda weird given this should only be - // called on start up. Oh well, leave _message untouched. - // We'll check for this in ::what() - return; - } - _message = new (std::nothrow) char[size + 1]; - if (not _message) { - return; - } - if (std::sprintf(_message, fmt_string, ec_as_int) < 0) { - _message = nullptr; - return; - } - return; - } - - deferred_local_free dlf {err_msg}; - - const char* fmt_string {nullptr}; - switch (k) { - case kind::file: - fmt_string = "An error occured while opening the binary: %s"; - break; - case kind::file_size: - fmt_string = - "An error occured while calculating the size of your binary: %s"; - break; - case kind::mapping: - fmt_string = "An error occured while memory mapping the binary: %s"; - break; - } - - auto buf_size = std::snprintf(nullptr, 0, fmt_string, _message); - if (buf_size < 0) { - return; - } - _message = new (std::nothrow) char[buf_size + 1]; - if (not _message) { - return; - } - if (std::sprintf(_message, fmt_string, err_msg) < 0) { - _message = nullptr; // ensure _message is null - } - return; -} - -mmap_error::~mmap_error() noexcept { - delete _message; -} - -auto mmap_error::what() const noexcept -> const char* { - return _message ? _message - : "There was an error allocating a buffer to store the " - "error message."; -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "memory_mapped_file_backed.hpp" + +/* + * README: + * + * Please note that this is not a classical header file (and as such lacks a + * #pragma once directive) and is only meant to include the Windows specific + * code of mmf_backed_binary_manager. + * + * It only has a .ipp extension so the Makefile ignores it. (There probably is a + * better way, but I'm lazy. PR the better way if this bothers you that much. + * Putting all the platform specific code in a single file is not a better way.) + */ + +#if !defined(REQVM_ON_WINDOWS) +# error "This file should only be used when compiling for MS Windows" +#endif + +#if !defined(REQVM_IN_THE_MMF_CPP_FILE) +# error "This file should only be included by memory_mapped_file_backed.cpp" +#endif + +#include "../utility.hpp" +#include "exceptions.hpp" + +#include +#include + +// How many macros are you even defining? +// Like 1 or 2 my dude +#ifndef NOMINMAX +# define NOMINMAX +#endif +#define WIN32_LEAN_AND_MEAN +// You are like a child +// watch this +#define NOATOM +#define NOCOLOR +#define NOCOMM +#define NOCTLMGR +#define NODRAWTEXT +#define NOGDICAPMASKS +#define NOHELP +#define NOICONS +#define NOKEYSTATES +#define NOMB +#define NOMCX +#define NOMETAFILE +#define NOMSG +#define NORASTEROPS +#define NOSCROLL +#define NOSERVICE +#define NOSHOWINDOW +#define NOSOUND +#define NOSYSMETRICS +#define NOTEXTMETRIC +#define NOVIRTUALKEYCODES +#define NOWH +#define NOWINMESSAGES +#define NOWINSTYLES +#define NOWINSTYLES +#include +// Memes aside, all those defines should help with building times +// It's not like that stuff removes anything we use anyway +// Those defines were brought to you by https://stackoverflow.com/a/1394929 +// Think those above are enough, some are implied by WIN32_LEAN_AND_MEAN + +namespace reqvm { + +mmf_backed_binary_manager::mmf_backed_binary_manager(const fs::path& path) { + _file = ::CreateFileW(path.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, nullptr); + if (_file == INVALID_HANDLE_VALUE) { + throw mmap_error {::GetLastError(), mmap_error::kind::file}; + } + + { + // We ask the kernel for the file size so we're 100% sure we're in sync + // with what the kernel believes. + ::LARGE_INTEGER sz; + if (::GetFileSizeEx(_file, &sz)) { + throw mmap_error {::GetLastError(), mmap_error::kind::file_size}; + } + _size = static_cast(sz.QuadPart); + } + + _mapping = + ::CreateFileMappingA(_file, nullptr, PAGE_READONLY, 0, 0, nullptr); + if (not _mapping) { + throw mmap_error {::GetLastError(), mmap_error::kind::mapping}; + } + + _data = static_cast( + ::MapViewOfFile(_mapping, FILE_MAP_READ, 0, 0, 0)); + if (not _data) { + throw mmap_error {::GetLastError(), mmap_error::kind::mapping}; + } +} + +mmf_backed_binary_manager::~mmf_backed_binary_manager() noexcept { + // Technically these functions can fail, but I'm not sure how to communicate + // that. TODO: find a way + // Though this destructor only gets called when the VM is shutting down. + // So is failing really an issue? Win32 API Gods help me plz. + ::UnmapViewOfFile(_data); + ::CloseHandle(_mapping); + ::CloseHandle(_file); +} + +mmap_error::mmap_error(error_code_t ec, kind k) noexcept { + class deferred_local_free final { + REQVM_MAKE_NONCOPYABLE(deferred_local_free) + REQVM_MAKE_NONMOVABLE(deferred_local_free) + + deferred_local_free(void* p) noexcept : _p {p} {} + ~deferred_local_free() noexcept { + ::LocalFree(_p); + } + + private: + void* _p; + }; + + char* err_msg {nullptr}; + auto succeeded = ::FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, ec, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&err_msg, + 0, nullptr); + + if (not succeeded) { + const char* fmt_string = + "An error occured while retrieving extended format information. " + "The error code returned by the system API: %d."; + int ec_as_int = static_cast(ec); + auto size = std::snprintf(nullptr, 0, fmt_string, ec_as_int); + if (size < 0) { + // Probably memory exhaustion? kinda weird given this should only be + // called on start up. Oh well, leave _message untouched. + // We'll check for this in ::what() + return; + } + _message = new (std::nothrow) char[size + 1]; + if (not _message) { + return; + } + if (std::sprintf(_message, fmt_string, ec_as_int) < 0) { + _message = nullptr; + return; + } + return; + } + + deferred_local_free dlf {err_msg}; + + const char* fmt_string {nullptr}; + switch (k) { + case kind::file: + fmt_string = "An error occured while opening the binary: %s"; + break; + case kind::file_size: + fmt_string = + "An error occured while calculating the size of your binary: %s"; + break; + case kind::mapping: + fmt_string = "An error occured while memory mapping the binary: %s"; + break; + } + + auto buf_size = std::snprintf(nullptr, 0, fmt_string, _message); + if (buf_size < 0) { + return; + } + _message = new (std::nothrow) char[buf_size + 1]; + if (not _message) { + return; + } + if (std::sprintf(_message, fmt_string, err_msg) < 0) { + _message = nullptr; // ensure _message is null + } + return; +} + +mmap_error::~mmap_error() noexcept { + delete _message; +} + +auto mmap_error::what() const noexcept -> const char* { + return _message ? _message + : "There was an error allocating a buffer to store the " + "error message."; +} + +} // namespace reqvm diff --git a/vm/src/binary_managers/vector_backed.cpp b/vm/src/binary_managers/vector_backed.cpp index 940541b..4f73dbe 100644 --- a/vm/src/binary_managers/vector_backed.cpp +++ b/vm/src/binary_managers/vector_backed.cpp @@ -1,49 +1,49 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "vector_backed.hpp" - -#include - -namespace reqvm { - -vector_backed_binary_manager::vector_backed_binary_manager( - const fs::path& path, std::size_t file_size) { - _binary.resize(file_size); - std::ifstream the_file {path, std::ios::binary}; - if (not the_file.read(reinterpret_cast(_binary.data()), file_size)) { - // TODO: report an error - } -} - -auto vector_backed_binary_manager::operator[](std::size_t idx) noexcept - -> std::uint8_t { - return _binary[idx]; -} - -auto vector_backed_binary_manager::size() noexcept -> std::size_t { - return _binary.size(); -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "vector_backed.hpp" + +#include + +namespace reqvm { + +vector_backed_binary_manager::vector_backed_binary_manager( + const fs::path& path, std::size_t file_size) { + _binary.resize(file_size); + std::ifstream the_file {path, std::ios::binary}; + if (not the_file.read(reinterpret_cast(_binary.data()), file_size)) { + // TODO: report an error + } +} + +auto vector_backed_binary_manager::operator[](std::size_t idx) noexcept + -> std::uint8_t { + return _binary[idx]; +} + +auto vector_backed_binary_manager::size() noexcept -> std::size_t { + return _binary.size(); +} + +} // namespace reqvm diff --git a/vm/src/binary_managers/vector_backed.hpp b/vm/src/binary_managers/vector_backed.hpp index ef8884e..c92e134 100644 --- a/vm/src/binary_managers/vector_backed.hpp +++ b/vm/src/binary_managers/vector_backed.hpp @@ -1,52 +1,52 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../binary_manager.hpp" -#include "../utility.hpp" - -#include -#include - -namespace reqvm { - -class vector_backed_binary_manager final : public binary_manager { - REQVM_MAKE_NONCOPYABLE(vector_backed_binary_manager) - REQVM_MAKE_NONMOVABLE(vector_backed_binary_manager) -public: - vector_backed_binary_manager() = delete; - vector_backed_binary_manager(const fs::path&, std::size_t file_size); - - virtual ~vector_backed_binary_manager() noexcept = default; - - auto operator[](std::size_t) noexcept -> std::uint8_t override; - - auto size() noexcept -> std::size_t override; - -private: - std::vector _binary; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../binary_manager.hpp" +#include "../utility.hpp" + +#include +#include + +namespace reqvm { + +class vector_backed_binary_manager final : public binary_manager { + REQVM_MAKE_NONCOPYABLE(vector_backed_binary_manager) + REQVM_MAKE_NONMOVABLE(vector_backed_binary_manager) +public: + vector_backed_binary_manager() = delete; + vector_backed_binary_manager(const fs::path&, std::size_t file_size); + + virtual ~vector_backed_binary_manager() noexcept = default; + + auto operator[](std::size_t) noexcept -> std::uint8_t override; + + auto size() noexcept -> std::size_t override; + +private: + std::vector _binary; +}; + +} // namespace reqvm diff --git a/vm/src/detect_platform.hpp b/vm/src/detect_platform.hpp index 68193b4..b6fb19d 100644 --- a/vm/src/detect_platform.hpp +++ b/vm/src/detect_platform.hpp @@ -1,40 +1,40 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#if defined(_WIN32) -# define REQVM_ON_WINDOWS 1 -#elif defined(__linux__) || defined(__APPLE__) || defined(__unix__) \ - || defined(_POSIX_VERSION) -// The check above should probably catch anything that qualifies as POSIX -// Except, maybe iOS or Android, but I don't see why you'd compile this project -// for those platforms. -# define REQVM_ON_POSIX 1 -#else -# error Unknown platform. Consider submitting a patch upstream to add your \ - platform. As this is a for fun project I am willing to accept even weird \ - and/or unpopular platforms as long as their addition is not too big of a \ - maintenance burden. -#endif +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#if defined(_WIN32) +# define REQVM_ON_WINDOWS 1 +#elif defined(__linux__) || defined(__APPLE__) || defined(__unix__) \ + || defined(_POSIX_VERSION) +// The check above should probably catch anything that qualifies as POSIX +// Except, maybe iOS or Android, but I don't see why you'd compile this project +// for those platforms. +# define REQVM_ON_POSIX 1 +#else +# error Unknown platform. Consider submitting a patch upstream to add your \ + platform. As this is a for fun project I am willing to accept even weird \ + and/or unpopular platforms as long as their addition is not too big of a \ + maintenance burden. +#endif diff --git a/vm/src/exceptions.hpp b/vm/src/exceptions.hpp index 4740368..a17c57d 100644 --- a/vm/src/exceptions.hpp +++ b/vm/src/exceptions.hpp @@ -1,154 +1,154 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../../common/opcodes.hpp" -#include "../../common/registers.hpp" - -#include -#include - -namespace reqvm { - -class vm_exception : public std::runtime_error { -public: - vm_exception() : runtime_error {"A fatal exception has occured"} {} - explicit vm_exception(const char* what_arg) : runtime_error {what_arg} {} - explicit vm_exception(const std::string& what_arg) - : runtime_error {what_arg} {} - - virtual ~vm_exception() noexcept = default; - - virtual auto the_opcode() const noexcept -> common::opcode = 0; - virtual auto the_register() const noexcept -> common::registers = 0; -}; - -class invalid_opcode : public vm_exception { -public: - // Disallow default construction so the exception may provide a meaningful - // message (that includes the opcode) - invalid_opcode() = delete; - - explicit invalid_opcode(common::opcode op) - : vm_exception {"The following invalid opcode was found in the " - "provided binary: "} - , _op {op} {} - invalid_opcode(const char* what_arg, common::opcode op) - : vm_exception {what_arg}, _op {op} {} - invalid_opcode(const std::string& what_arg, common::opcode op) - : vm_exception {what_arg}, _op {op} {} - - virtual ~invalid_opcode() noexcept = default; - - auto the_opcode() const noexcept -> common::opcode override { - return _op; - } - auto the_register() const noexcept -> common::registers override { - return common::registers::none; - } - -private: - common::opcode _op; -}; - -class invalid_register : public vm_exception { -public: - // Disallow default construction to force a meaningful message (that - // includes the register) - invalid_register() = delete; - - invalid_register(const char* what_arg, common::registers reg) - : vm_exception {what_arg}, _reg {reg} {} - invalid_register(const std::string& what_arg, common::registers reg) - : vm_exception {what_arg}, _reg {reg} {} - - virtual ~invalid_register() noexcept = default; - - auto the_opcode() const noexcept -> common::opcode override { - return common::opcode::noop; - } - auto the_register() const noexcept -> common::registers override { - return _reg; - } - -private: - common::registers _reg; -}; - -class bad_argument : public vm_exception { -public: - // Delete default constructor to force a meaningful message - bad_argument() = delete; - - explicit bad_argument(const char* what_arg) : vm_exception {what_arg} {}; - explicit bad_argument(const std::string& what_arg) - : vm_exception {what_arg} {} - - virtual ~bad_argument() noexcept = default; - - auto the_register() const noexcept -> common::registers override { - return common::registers::none; - } - auto the_opcode() const noexcept -> common::opcode override { - return common::opcode::noop; - } -}; - -class preamble_error : public std::exception { -public: - enum class kind : std::uint8_t { - version_too_high, - nonstandard_mbs, - bad_version_serialization, - unknown_feature, - }; - explicit preamble_error(kind k) : _kind {k} {} - virtual ~preamble_error() noexcept = default; - - const char* what() const noexcept override { - // PONDER: should this be moved out of line? - switch (_kind) { - case kind::version_too_high: - return "reqvm was unable to start because it is too outdated to " - "run the binary. Please upgrade reqvm."; - case kind::nonstandard_mbs: - return "reqvm was unable to start because the loaded binary has a " - "magic byte string which does not follow the standard " - "format."; - case kind::bad_version_serialization: - return "reqvm was unable to start because the version string in " - "the preamble is badly serialised/nonstandard and as such " - "it is not possible to verify instruction set " - "compatibility."; - default: - return "Unknown preamble error. This is likely an internal VM bug."; - } - } - -private: - kind _kind; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../../common/opcodes.hpp" +#include "../../common/registers.hpp" + +#include +#include + +namespace reqvm { + +class vm_exception : public std::runtime_error { +public: + vm_exception() : runtime_error {"A fatal exception has occured"} {} + explicit vm_exception(const char* what_arg) : runtime_error {what_arg} {} + explicit vm_exception(const std::string& what_arg) + : runtime_error {what_arg} {} + + virtual ~vm_exception() noexcept = default; + + virtual auto the_opcode() const noexcept -> common::opcode = 0; + virtual auto the_register() const noexcept -> common::registers = 0; +}; + +class invalid_opcode : public vm_exception { +public: + // Disallow default construction so the exception may provide a meaningful + // message (that includes the opcode) + invalid_opcode() = delete; + + explicit invalid_opcode(common::opcode op) + : vm_exception {"The following invalid opcode was found in the " + "provided binary: "} + , _op {op} {} + invalid_opcode(const char* what_arg, common::opcode op) + : vm_exception {what_arg}, _op {op} {} + invalid_opcode(const std::string& what_arg, common::opcode op) + : vm_exception {what_arg}, _op {op} {} + + virtual ~invalid_opcode() noexcept = default; + + auto the_opcode() const noexcept -> common::opcode override { + return _op; + } + auto the_register() const noexcept -> common::registers override { + return common::registers::none; + } + +private: + common::opcode _op; +}; + +class invalid_register : public vm_exception { +public: + // Disallow default construction to force a meaningful message (that + // includes the register) + invalid_register() = delete; + + invalid_register(const char* what_arg, common::registers reg) + : vm_exception {what_arg}, _reg {reg} {} + invalid_register(const std::string& what_arg, common::registers reg) + : vm_exception {what_arg}, _reg {reg} {} + + virtual ~invalid_register() noexcept = default; + + auto the_opcode() const noexcept -> common::opcode override { + return common::opcode::noop; + } + auto the_register() const noexcept -> common::registers override { + return _reg; + } + +private: + common::registers _reg; +}; + +class bad_argument : public vm_exception { +public: + // Delete default constructor to force a meaningful message + bad_argument() = delete; + + explicit bad_argument(const char* what_arg) : vm_exception {what_arg} {}; + explicit bad_argument(const std::string& what_arg) + : vm_exception {what_arg} {} + + virtual ~bad_argument() noexcept = default; + + auto the_register() const noexcept -> common::registers override { + return common::registers::none; + } + auto the_opcode() const noexcept -> common::opcode override { + return common::opcode::noop; + } +}; + +class preamble_error : public std::exception { +public: + enum class kind : std::uint8_t { + version_too_high, + nonstandard_mbs, + bad_version_serialization, + unknown_feature, + }; + explicit preamble_error(kind k) : _kind {k} {} + virtual ~preamble_error() noexcept = default; + + const char* what() const noexcept override { + // PONDER: should this be moved out of line? + switch (_kind) { + case kind::version_too_high: + return "reqvm was unable to start because it is too outdated to " + "run the binary. Please upgrade reqvm."; + case kind::nonstandard_mbs: + return "reqvm was unable to start because the loaded binary has a " + "magic byte string which does not follow the standard " + "format."; + case kind::bad_version_serialization: + return "reqvm was unable to start because the version string in " + "the preamble is badly serialised/nonstandard and as such " + "it is not possible to verify instruction set " + "compatibility."; + default: + return "Unknown preamble error. This is likely an internal VM bug."; + } + } + +private: + kind _kind; +}; + +} // namespace reqvm diff --git a/vm/src/flags.hpp b/vm/src/flags.hpp index 5b8f85a..cf59227 100644 --- a/vm/src/flags.hpp +++ b/vm/src/flags.hpp @@ -1,42 +1,42 @@ -/* -* MIT License -* -* Copyright (c) 2020 Mitca Dumitru -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ - -#pragma once - -#include - -namespace reqvm { - -struct flags { - enum class cf { - eq = 0b0000'0000, - less = 0b0000'0001, - gr = 0b0000'0010, - }; - - std::uint64_t cmp_flag : 2; - std::uint64_t reserved : 62; -}; - -} // namespace reqvm +/* +* MIT License +* +* Copyright (c) 2020 Mitca Dumitru +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#pragma once + +#include + +namespace reqvm { + +struct flags { + enum class cf { + eq = 0b0000'0000, + less = 0b0000'0001, + gr = 0b0000'0010, + }; + + std::uint64_t cmp_flag : 2; + std::uint64_t reserved : 62; +}; + +} // namespace reqvm diff --git a/vm/src/io.cpp b/vm/src/io.cpp index 2efe240..3632a98 100644 --- a/vm/src/io.cpp +++ b/vm/src/io.cpp @@ -1,77 +1,77 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "io.hpp" - -#include -#include - -namespace reqvm { -namespace io { - -common::io_op parse_from_byte(std::uint8_t byte) { - using common::io_op; - switch (byte) { - case 1: - return io_op::getc; - case 2: - return io_op::putc; - case 3: - return io_op::put8c; - case 4: - return io_op::putn; - default: - throw error {"Unknown operation passed as argument to 'io': ", byte}; - } -} - -auto getc() -> std::uint64_t { - return static_cast(std::fgetc(stdin)); -} - -auto putc(std::uint64_t ch) -> void { - std::putc(static_cast(ch), stdout); -} - -auto put8c(std::uint64_t string) -> void { - char chars[9] = {0}; - chars[0] = static_cast(string >> 56); - chars[1] = static_cast((string << 8) >> 56); - chars[2] = static_cast((string << 16) >> 56); - chars[3] = static_cast((string << 24) >> 56); - chars[4] = static_cast((string << 32) >> 56); - chars[5] = static_cast((string << 40) >> 56); - chars[6] = static_cast((string << 48) >> 56); - chars[7] = static_cast((string << 56) >> 56); - for (auto ch : chars) { - std::putc(ch, stdout); - } -} - -auto putn(std::uint64_t num) -> void { - std::fprintf(stdout, "%" PRId64, num); -} - -} // namespace io +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "io.hpp" + +#include +#include + +namespace reqvm { +namespace io { + +common::io_op parse_from_byte(std::uint8_t byte) { + using common::io_op; + switch (byte) { + case 1: + return io_op::getc; + case 2: + return io_op::putc; + case 3: + return io_op::put8c; + case 4: + return io_op::putn; + default: + throw error {"Unknown operation passed as argument to 'io': ", byte}; + } +} + +auto getc() -> std::uint64_t { + return static_cast(std::fgetc(stdin)); +} + +auto putc(std::uint64_t ch) -> void { + std::putc(static_cast(ch), stdout); +} + +auto put8c(std::uint64_t string) -> void { + char chars[9] = {0}; + chars[0] = static_cast(string >> 56); + chars[1] = static_cast((string << 8) >> 56); + chars[2] = static_cast((string << 16) >> 56); + chars[3] = static_cast((string << 24) >> 56); + chars[4] = static_cast((string << 32) >> 56); + chars[5] = static_cast((string << 40) >> 56); + chars[6] = static_cast((string << 48) >> 56); + chars[7] = static_cast((string << 56) >> 56); + for (auto ch : chars) { + std::putc(ch, stdout); + } +} + +auto putn(std::uint64_t num) -> void { + std::fprintf(stdout, "%" PRId64, num); +} + +} // namespace io } // namespace reqvm \ No newline at end of file diff --git a/vm/src/io.hpp b/vm/src/io.hpp index da090ed..fac1405 100644 --- a/vm/src/io.hpp +++ b/vm/src/io.hpp @@ -1,59 +1,59 @@ -/* -* MIT License -* -* Copyright (c) 2020 Mitca Dumitru -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ - -#pragma once - -#include "../../common/opcodes.hpp" - -#include -#include - -namespace reqvm { -namespace io { - -common::io_op parse_from_byte(std::uint8_t byte); - -auto getc() -> std::uint64_t; -auto putc(std::uint64_t ch) -> void; -auto put8c(std::uint64_t chars) -> void; -auto putn(std::uint64_t num) -> void; - -class error : public std::runtime_error { -public: - // Delete default constructor to force a meaningful message - error() = delete; - error(const char* what_arg, std::uint8_t byte) - : runtime_error {what_arg}, _op {byte} {}; - virtual ~error() noexcept = default; - - auto the_invalid_op() const noexcept -> std::uint64_t { - return _op; - } - -private: - std::uint8_t _op; -}; - -} // namespace io -} // namespace reqvm +/* +* MIT License +* +* Copyright (c) 2020 Mitca Dumitru +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#pragma once + +#include "../../common/opcodes.hpp" + +#include +#include + +namespace reqvm { +namespace io { + +common::io_op parse_from_byte(std::uint8_t byte); + +auto getc() -> std::uint64_t; +auto putc(std::uint64_t ch) -> void; +auto put8c(std::uint64_t chars) -> void; +auto putn(std::uint64_t num) -> void; + +class error : public std::runtime_error { +public: + // Delete default constructor to force a meaningful message + error() = delete; + error(const char* what_arg, std::uint8_t byte) + : runtime_error {what_arg}, _op {byte} {}; + virtual ~error() noexcept = default; + + auto the_invalid_op() const noexcept -> std::uint64_t { + return _op; + } + +private: + std::uint8_t _op; +}; + +} // namespace io +} // namespace reqvm diff --git a/vm/src/main.cpp b/vm/src/main.cpp index d60d171..20a95ae 100644 --- a/vm/src/main.cpp +++ b/vm/src/main.cpp @@ -1,122 +1,122 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "../../common/unreachable.hpp" -#include "binary_managers/exceptions.hpp" -#include "exceptions.hpp" -#include "io.hpp" -#include "vm.hpp" - -#include -#include -#include - -static constexpr auto panic = R"( - __ ____ __ _____ _ - \ \ / / \/ | | __ \ (_) - \ \ / /| \ / | | |__) |_ _ _ __ _ ___ - \ \/ / | |\/| | | ___/ _` | '_ \| |/ __| - \ / | | | | | | | (_| | | | | | (__ - \/ |_| |_| |_| \__,_|_| |_|_|\___| - - -)"; - -using std::printf; -using std::puts; -auto main(int argc, char** argv) -> int try { - if (argc < 2) { - printf("usage: vm binary.reqvm"); - return EXIT_SUCCESS; - } - auto the_vm = reqvm::vm {argv[1]}; - return the_vm.run(); -} catch (const reqvm::invalid_opcode& e) { - puts(panic); - puts("reqvm has encountered an error during the execution of your " - "program.\n"); - printf("e.what(): %s %#x (%u)\n", e.what(), - static_cast(e.the_opcode()), - static_cast(e.the_opcode())); - return EXIT_FAILURE; -} catch (const reqvm::invalid_register& e) { - puts(panic); - puts("reqvm has encountered an error during the execution of your " - "program.\n"); - printf("e.what(): %s %#x (%u)\n", e.what(), - static_cast(e.the_register()), - static_cast(e.the_register())); - return EXIT_FAILURE; -} catch (const reqvm::bad_argument& e) { - puts(panic); - puts("reqvm has encountered an error during the execution of your " - "program.\n"); - printf("e.what(): %s\n", e.what()); - return EXIT_FAILURE; -} catch (const reqvm::vm_exception& e) { - puts(panic); - puts("reqvm has encountered an unhandled reqvm::vm_exception during " - "the execution of your program. Please file an issue at " - "https://github.com/RealKC/reqvm/issues as this is not meant to " - "happen.\n"); - printf("e.what(): %s\n", e.what()); - return EXIT_FAILURE; -} catch (const reqvm::stack_error& e) { - puts(panic); - puts("reqvm has detected an illegal manipulation of the stack\n"); - printf("e.what(): %s\n", e.what()); - return EXIT_FAILURE; -} catch (const reqvm::preamble_error& e) { - puts(panic); - puts("reqvm has an ecountered an issue with the format of your binary.\n"); - printf("e.what(): %s\n", e.what()); - return EXIT_FAILURE; -} catch (const reqvm::io::error& e) { - puts(panic); - puts("reqvm has encountered an issue during the execution of your " - "binary.\n"); - printf("e.what(): %s %#x (%u)\n", e.what(), - static_cast(e.the_invalid_op()), - static_cast(e.the_invalid_op())); - return EXIT_FAILURE; -} catch (const reqvm::mmap_error& e) { - puts(panic); - puts("reqvm has encountered an issue trying to open your binary.\n"); - printf("e.what(): %s\n", e.what()); - return EXIT_FAILURE; -#ifndef NDEBUG -} catch (const common::unreachable_code_reached& e) { - puts(panic); - puts("reqvm has detected a critical issue in its code. Please file an issue" - " at https://github.com/RealKC/reqvm/issues .\n"); - printf("e.what(): %s\n", e.what()); - return EXIT_FAILURE; -#endif -} catch (const std::exception& e) { - puts(panic); - puts("reqvm has encountered an error during the execution of your " - "program.\n"); - printf("e.what(): %s\n", e.what()); - return EXIT_FAILURE; -} +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "../../common/unreachable.hpp" +#include "binary_managers/exceptions.hpp" +#include "exceptions.hpp" +#include "io.hpp" +#include "vm.hpp" + +#include +#include +#include + +static constexpr auto panic = R"( + __ ____ __ _____ _ + \ \ / / \/ | | __ \ (_) + \ \ / /| \ / | | |__) |_ _ _ __ _ ___ + \ \/ / | |\/| | | ___/ _` | '_ \| |/ __| + \ / | | | | | | | (_| | | | | | (__ + \/ |_| |_| |_| \__,_|_| |_|_|\___| + + +)"; + +using std::printf; +using std::puts; +auto main(int argc, char** argv) -> int try { + if (argc < 2) { + printf("usage: vm binary.reqvm"); + return EXIT_SUCCESS; + } + auto the_vm = reqvm::vm {argv[1]}; + return the_vm.run(); +} catch (const reqvm::invalid_opcode& e) { + puts(panic); + puts("reqvm has encountered an error during the execution of your " + "program.\n"); + printf("e.what(): %s %#x (%u)\n", e.what(), + static_cast(e.the_opcode()), + static_cast(e.the_opcode())); + return EXIT_FAILURE; +} catch (const reqvm::invalid_register& e) { + puts(panic); + puts("reqvm has encountered an error during the execution of your " + "program.\n"); + printf("e.what(): %s %#x (%u)\n", e.what(), + static_cast(e.the_register()), + static_cast(e.the_register())); + return EXIT_FAILURE; +} catch (const reqvm::bad_argument& e) { + puts(panic); + puts("reqvm has encountered an error during the execution of your " + "program.\n"); + printf("e.what(): %s\n", e.what()); + return EXIT_FAILURE; +} catch (const reqvm::vm_exception& e) { + puts(panic); + puts("reqvm has encountered an unhandled reqvm::vm_exception during " + "the execution of your program. Please file an issue at " + "https://github.com/RealKC/reqvm/issues as this is not meant to " + "happen.\n"); + printf("e.what(): %s\n", e.what()); + return EXIT_FAILURE; +} catch (const reqvm::stack_error& e) { + puts(panic); + puts("reqvm has detected an illegal manipulation of the stack\n"); + printf("e.what(): %s\n", e.what()); + return EXIT_FAILURE; +} catch (const reqvm::preamble_error& e) { + puts(panic); + puts("reqvm has an ecountered an issue with the format of your binary.\n"); + printf("e.what(): %s\n", e.what()); + return EXIT_FAILURE; +} catch (const reqvm::io::error& e) { + puts(panic); + puts("reqvm has encountered an issue during the execution of your " + "binary.\n"); + printf("e.what(): %s %#x (%u)\n", e.what(), + static_cast(e.the_invalid_op()), + static_cast(e.the_invalid_op())); + return EXIT_FAILURE; +} catch (const reqvm::mmap_error& e) { + puts(panic); + puts("reqvm has encountered an issue trying to open your binary.\n"); + printf("e.what(): %s\n", e.what()); + return EXIT_FAILURE; +#ifndef NDEBUG +} catch (const common::unreachable_code_reached& e) { + puts(panic); + puts("reqvm has detected a critical issue in its code. Please file an issue" + " at https://github.com/RealKC/reqvm/issues .\n"); + printf("e.what(): %s\n", e.what()); + return EXIT_FAILURE; +#endif +} catch (const std::exception& e) { + puts(panic); + puts("reqvm has encountered an error during the execution of your " + "program.\n"); + printf("e.what(): %s\n", e.what()); + return EXIT_FAILURE; +} diff --git a/vm/src/registers.cpp b/vm/src/registers.cpp index 187f9b5..b32db6c 100644 --- a/vm/src/registers.cpp +++ b/vm/src/registers.cpp @@ -1,92 +1,92 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "registers.hpp" - -#include "../../common/unreachable.hpp" -#include "exceptions.hpp" - -namespace reqvm { - -auto registers::parse_from_byte(std::uint8_t byte) -> registers::tag { - const auto reg = static_cast(byte); - - if (reg == common::registers::sp) { - return {registers::tag::kind::sp, 0}; - } - if (reg == common::registers::pc) { - return {registers::tag::kind::pc, 0}; - } - if (reg == common::registers::ire) { - return {registers::tag::kind::ire, 0}; - } - -// Let me tell you something fun, the standard says that arithmetic on types -// smaller than `int` must have its operands cast to int, so we ignore a -// narrowing warning here, because we know our operands won't overflow a uint8_t -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnarrowing" - if (common::registers::gp00 <= reg && reg <= common::registers::gp63) { - return {registers::tag::kind::gp, - byte - static_cast(common::registers::gp00)}; - } - if (common::registers::ifa00 <= reg && reg <= common::registers::ifa15) { - return {registers::tag::kind::ifa, - byte - static_cast(common::registers::ifa00)}; - } -#pragma GCC diagnostic pop - - throw invalid_register {"A byte that does not name a register was supplied " - "as operand to an opcode", - static_cast(byte)}; -} - -auto registers::operator[](registers::tag tag) -> std::uint64_t& { - switch (tag.kind) { - case registers::tag::kind::pc: - return _program_counter; - case registers::tag::kind::sp: - return _stack_pointer; - case registers::tag::kind::ire: - return _integer_return; - case registers::tag::kind::gp: - return _general_purpose[tag.idx]; - case registers::tag::kind::ifa: - return _integer_functions_args[tag.idx]; - default: - UNREACHABLE("register::operator[]: switch was not actually exhaustive"); - } -} - -auto registers::is_error_on_lhs(registers::tag reg) noexcept -> bool { - switch (reg.kind) { - case registers::tag::kind::pc: - case registers::tag::kind::sp: - return true; - default: - return false; - } -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "registers.hpp" + +#include "../../common/unreachable.hpp" +#include "exceptions.hpp" + +namespace reqvm { + +auto registers::parse_from_byte(std::uint8_t byte) -> registers::tag { + const auto reg = static_cast(byte); + + if (reg == common::registers::sp) { + return {registers::tag::kind::sp, 0}; + } + if (reg == common::registers::pc) { + return {registers::tag::kind::pc, 0}; + } + if (reg == common::registers::ire) { + return {registers::tag::kind::ire, 0}; + } + +// Let me tell you something fun, the standard says that arithmetic on types +// smaller than `int` must have its operands cast to int, so we ignore a +// narrowing warning here, because we know our operands won't overflow a uint8_t +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnarrowing" + if (common::registers::gp00 <= reg && reg <= common::registers::gp63) { + return {registers::tag::kind::gp, + byte - static_cast(common::registers::gp00)}; + } + if (common::registers::ifa00 <= reg && reg <= common::registers::ifa15) { + return {registers::tag::kind::ifa, + byte - static_cast(common::registers::ifa00)}; + } +#pragma GCC diagnostic pop + + throw invalid_register {"A byte that does not name a register was supplied " + "as operand to an opcode", + static_cast(byte)}; +} + +auto registers::operator[](registers::tag tag) -> std::uint64_t& { + switch (tag.kind) { + case registers::tag::kind::pc: + return _program_counter; + case registers::tag::kind::sp: + return _stack_pointer; + case registers::tag::kind::ire: + return _integer_return; + case registers::tag::kind::gp: + return _general_purpose[tag.idx]; + case registers::tag::kind::ifa: + return _integer_functions_args[tag.idx]; + default: + UNREACHABLE("register::operator[]: switch was not actually exhaustive"); + } +} + +auto registers::is_error_on_lhs(registers::tag reg) noexcept -> bool { + switch (reg.kind) { + case registers::tag::kind::pc: + case registers::tag::kind::sp: + return true; + default: + return false; + } +} + +} // namespace reqvm diff --git a/vm/src/registers.hpp b/vm/src/registers.hpp index 7e2d6f5..5af1ecb 100644 --- a/vm/src/registers.hpp +++ b/vm/src/registers.hpp @@ -1,88 +1,88 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../../common/registers.hpp" - -#include -#include - -namespace reqvm { - -class registers final { -public: - struct tag { - enum class kind : std::uint8_t { - pc = 0, - sp, - ire, - gp, - ifa, - } kind; - std::uint8_t idx; - }; - - static auto parse_from_byte(std::uint8_t byte) -> tag; - static auto is_error_on_lhs(tag reg) noexcept -> bool; - - registers() noexcept = default; - ~registers() noexcept = default; - - auto operator[](tag reg) -> std::uint64_t&; - - auto general_purpose() noexcept -> std::array& { - return _general_purpose; - } - - auto integer_function_args() noexcept -> std::array& { - return _integer_functions_args; - } - auto ire() noexcept -> std::uint64_t& { - return _integer_return; - } - - auto pc() noexcept -> const std::uint64_t& { - return _program_counter; - } - auto advance_pc(std::uint64_t n) noexcept -> void { - _program_counter += n; - } - auto jump_to(std::uint64_t address) noexcept -> void { - _program_counter = address; - } - - auto sp() noexcept -> std::uint64_t& { - return _stack_pointer; - } - -private: - std::array _general_purpose {0}; - std::array _integer_functions_args {0}; - std::uint64_t _program_counter {0}; - std::uint64_t _stack_pointer {0}; - std::uint64_t _integer_return {0}; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../../common/registers.hpp" + +#include +#include + +namespace reqvm { + +class registers final { +public: + struct tag { + enum class kind : std::uint8_t { + pc = 0, + sp, + ire, + gp, + ifa, + } kind; + std::uint8_t idx; + }; + + static auto parse_from_byte(std::uint8_t byte) -> tag; + static auto is_error_on_lhs(tag reg) noexcept -> bool; + + registers() noexcept = default; + ~registers() noexcept = default; + + auto operator[](tag reg) -> std::uint64_t&; + + auto general_purpose() noexcept -> std::array& { + return _general_purpose; + } + + auto integer_function_args() noexcept -> std::array& { + return _integer_functions_args; + } + auto ire() noexcept -> std::uint64_t& { + return _integer_return; + } + + auto pc() noexcept -> const std::uint64_t& { + return _program_counter; + } + auto advance_pc(std::uint64_t n) noexcept -> void { + _program_counter += n; + } + auto jump_to(std::uint64_t address) noexcept -> void { + _program_counter = address; + } + + auto sp() noexcept -> std::uint64_t& { + return _stack_pointer; + } + +private: + std::array _general_purpose {0}; + std::array _integer_functions_args {0}; + std::uint64_t _program_counter {0}; + std::uint64_t _stack_pointer {0}; + std::uint64_t _integer_return {0}; +}; + +} // namespace reqvm diff --git a/vm/src/stack.cpp b/vm/src/stack.cpp index 0d91b0e..506b92a 100644 --- a/vm/src/stack.cpp +++ b/vm/src/stack.cpp @@ -1,48 +1,48 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "stack.hpp" - -namespace reqvm { - -auto stack::push(std::uint64_t val, registers& regs) -> void { - if (regs.sp() + 1 > (1024 * 1024)) { - throw stack_error { - "Stack overflow: the binary has tried writing past the end of the " - "stack."}; - } - _storage[regs.sp()++] = val; -} - -auto stack::pop(registers& regs) -> std::uint64_t { - if (regs.sp() == 0) { - throw stack_error { - "Stack underflow: the binary has tried reading before the start " - "of the stack."}; - } - regs.sp()--; - return _storage[regs.sp() + 1]; -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "stack.hpp" + +namespace reqvm { + +auto stack::push(std::uint64_t val, registers& regs) -> void { + if (regs.sp() + 1 > (1024 * 1024)) { + throw stack_error { + "Stack overflow: the binary has tried writing past the end of the " + "stack."}; + } + _storage[regs.sp()++] = val; +} + +auto stack::pop(registers& regs) -> std::uint64_t { + if (regs.sp() == 0) { + throw stack_error { + "Stack underflow: the binary has tried reading before the start " + "of the stack."}; + } + regs.sp()--; + return _storage[regs.sp() + 1]; +} + +} // namespace reqvm diff --git a/vm/src/stack.hpp b/vm/src/stack.hpp index 8ddfae3..54a5937 100644 --- a/vm/src/stack.hpp +++ b/vm/src/stack.hpp @@ -1,55 +1,55 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "registers.hpp" - -#include -#include - -namespace reqvm { - -class stack_error : public std::runtime_error { -public: - stack_error(const char* what_arg) : runtime_error(what_arg) {} - - virtual ~stack_error() noexcept = default; -}; - -class stack final { -public: - stack() : _storage {new std::uint64_t[1024 * 1024]} {} - ~stack() noexcept { - delete[] _storage; - } - - auto push(std::uint64_t val, registers& regs) -> void; - auto pop(registers& regs) -> std::uint64_t; - -private: - std::uint64_t* _storage; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "registers.hpp" + +#include +#include + +namespace reqvm { + +class stack_error : public std::runtime_error { +public: + stack_error(const char* what_arg) : runtime_error(what_arg) {} + + virtual ~stack_error() noexcept = default; +}; + +class stack final { +public: + stack() : _storage {new std::uint64_t[1024 * 1024]} {} + ~stack() noexcept { + delete[] _storage; + } + + auto push(std::uint64_t val, registers& regs) -> void; + auto pop(registers& regs) -> std::uint64_t; + +private: + std::uint64_t* _storage; +}; + +} // namespace reqvm diff --git a/vm/src/utility.hpp b/vm/src/utility.hpp index 3455e3c..51807e8 100644 --- a/vm/src/utility.hpp +++ b/vm/src/utility.hpp @@ -1,35 +1,35 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#define REQVM_MAKE_NONCOPYABLE(type) \ -public: \ - type(const type&) = delete; \ - auto operator=(const type&)->type& = delete; - -#define REQVM_MAKE_NONMOVABLE(type) \ -public: \ - type(type&&) = delete; \ - auto operator=(type &&)->type& = delete; +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#define REQVM_MAKE_NONCOPYABLE(type) \ +public: \ + type(const type&) = delete; \ + auto operator=(const type&)->type& = delete; + +#define REQVM_MAKE_NONMOVABLE(type) \ +public: \ + type(type&&) = delete; \ + auto operator=(type &&)->type& = delete; diff --git a/vm/src/vm.cpp b/vm/src/vm.cpp index 150bb58..b2d0838 100644 --- a/vm/src/vm.cpp +++ b/vm/src/vm.cpp @@ -1,389 +1,389 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "vm.hpp" - -#include "../../common/preamble.hpp" -#include "exceptions.hpp" -#include "io.hpp" - -#include -#include - -static inline auto is_version_compatible(std::uint16_t major, - std::uint16_t minor, - std::uint16_t patch) noexcept -> bool { - using namespace common; - if (major <= version::major) { - return true; - } - if (minor <= version::minor) { - return true; - } - if (patch <= version::patch) { - return true; - } - return false; -} - -namespace reqvm { - -vm::vm(const std::string& binary) { - auto path = std::filesystem::path {binary}; - _binary = load_from(path); -} - -auto vm::run() -> int { - read_preamble(); - while (_regs.pc() <= _binary->size() && !_halted) { - cycle(static_cast((*_binary)[_regs.pc()])); - } - return static_cast(_regs.ire()); -} - -auto vm::read_preamble() -> void { - std::size_t i {0}; - for (; i < sizeof(common::magic_byte_string) - 1; i++) { - if ((*_binary)[i] != common::magic_byte_string[i]) { - throw preamble_error {preamble_error::kind::nonstandard_mbs}; - } - } - std::array version_string; - for (; i < sizeof(common::magic_byte_string) + 9; i++) { - version_string[i - (sizeof(common::magic_byte_string) - 1)] = - (*_binary)[i]; - } - if (version_string[0] != '!' || version_string[3] != ';' - || version_string[6] != ';' || version_string[9] != ';') { - throw preamble_error {preamble_error::kind::bad_version_serialization}; - } - auto major = - static_cast(version_string[1]) << 8 | version_string[2]; - auto minor = - static_cast(version_string[4]) << 8 | version_string[5]; - auto patch = - static_cast(version_string[7]) << 8 | version_string[8]; - if (not ::is_version_compatible(major, minor, patch)) { - throw preamble_error {preamble_error::kind::version_too_high}; - } - // TODO: read ~~the version +~~ :^) the features - _regs.jump_to(256); -} - -auto vm::cycle(common::opcode op) -> void { -#define CHECK_LHS_REG(opcode, reg) \ - do { \ - if (registers::is_error_on_lhs((reg))) { \ - throw reqvm::invalid_register { \ - "Invalid lhs operand for opcode '" #opcode "': ", \ - static_cast((*_binary)[_regs.pc() + 1])}; \ - } \ - } while (0) - -#define MAKE_8_BYTE_VAL(val) \ - auto val = static_cast((*_binary)[_regs.pc() + 1]) << 56 \ - | static_cast((*_binary)[_regs.pc() + 2]) << 48 \ - | static_cast((*_binary)[_regs.pc() + 3]) << 40 \ - | static_cast((*_binary)[_regs.pc() + 4]) << 32 \ - | static_cast((*_binary)[_regs.pc() + 5]) << 24 \ - | static_cast((*_binary)[_regs.pc() + 6]) << 16 \ - | static_cast((*_binary)[_regs.pc() + 7]) << 8 \ - | static_cast((*_binary)[_regs.pc() + 8]) - -#define CHECK_AT_LEAST_8_BYTES(opcode) \ - if (_binary->size() - _regs.pc() < 8) { \ - throw bad_argument { \ - "Opcode '" #opcode \ - "c' is the last opcode in your binary and after" \ - " it there are less than 8 bytes, as such it is not possible to " \ - "build its argument."}; \ - } - - switch (op) { - using common::opcode; - case opcode::noop: { - // do nothing but advance pc - _regs.advance_pc(1); - break; - } - case opcode::call: { - CHECK_AT_LEAST_8_BYTES(call); - MAKE_8_BYTE_VAL(address); - for (auto& gp : _regs.general_purpose()) { - gp = 0; - } - _stack.push(_regs.pc() + 9, _regs); - _regs.jump_to(address); - break; - } - case opcode::ret: { - auto ret_addr = _stack.pop(_regs); - _regs.jump_to(ret_addr); - break; - } - case opcode::io: { - switch (io::parse_from_byte((*_binary)[_regs.pc() + 1])) { - using common::io_op; - case io_op::getc: { - auto reg = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - if (registers::is_error_on_lhs(reg)) { - throw invalid_register { - "Invalid lhs register for opcode 'io getc':", - static_cast((*_binary)[_regs.pc() + 2])}; - } - _regs[reg] = io::getc(); - break; - } - case io_op::putc: { - auto reg = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - io::putc(_regs[reg]); - break; - } - case io_op::put8c: { - auto reg = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - io::put8c(_regs[reg]); - break; - } - case io_op::putn: { - auto reg = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - io::putn(_regs[reg]); - break; - } - } - _regs.advance_pc(3); - break; - } - case opcode::add: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(add, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] += _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::sub: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(sub, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] -= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::mul: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(mul, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] *= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::div: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(div, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] /= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::mod: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(mod, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] %= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::and_: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(and, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] &= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::or_: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(or, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] |= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::xor_: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(xor, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] ^= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::not_: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - if (registers::is_error_on_lhs(r1)) { - throw invalid_register { - "Invalid operand for opcode 'not': ", - static_cast((*_binary)[_regs.pc() + 1])}; - } - _regs[r1] = ~_regs[r1]; - _regs.advance_pc(2); - break; - } - case opcode::lshft: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(lshft, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] <<= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::rshft: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - CHECK_LHS_REG(rhsft, r1); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - _regs[r1] >>= _regs[r2]; - _regs.advance_pc(3); - break; - } - case opcode::push: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - _stack.push(_regs[r1], _regs); - _regs.advance_pc(2); - break; - } - case opcode::pushc: { - CHECK_AT_LEAST_8_BYTES(pushc); - MAKE_8_BYTE_VAL(val); - _stack.push(val, _regs); - _regs.advance_pc(9); - break; - } - case opcode::pop: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - if (registers::is_error_on_lhs(r1)) { - throw invalid_register { - "Invalid operand for opcode 'pop': ", - static_cast((*_binary)[_regs.pc() + 1])}; - } - _regs[r1] = _stack.pop(_regs); - _regs.advance_pc(2); - break; - } - case opcode::cmp: { - auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); - auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); - - if (_regs[r1] < _regs[r2]) { - _flags.cmp_flag = static_cast(flags::cf::less); - } else if (_regs[r1] > _regs[r2]) { - _flags.cmp_flag = static_cast(flags::cf::gr); - } else { - _flags.cmp_flag = static_cast(flags::cf::eq); - } - - _regs.advance_pc(3); - break; - } - case opcode::jmp: { - CHECK_AT_LEAST_8_BYTES(jmp); - MAKE_8_BYTE_VAL(address); - _regs.jump_to(address); - break; - } -#define U64(x) static_cast(x) - - case opcode::jeq: { - CHECK_AT_LEAST_8_BYTES(jeq); - if (_flags.cmp_flag == U64(flags::cf::eq)) { - MAKE_8_BYTE_VAL(address); - _regs.jump_to(address); - } else { - _regs.advance_pc(9); - } - break; - } - case opcode::jneq: { - CHECK_AT_LEAST_8_BYTES(jneq) - if (_flags.cmp_flag != U64(flags::cf::eq)) { - MAKE_8_BYTE_VAL(address); - _regs.jump_to(address); - } else { - _regs.advance_pc(9); - } - break; - } - case opcode::jl: { - CHECK_AT_LEAST_8_BYTES(jl) - if (_flags.cmp_flag == U64(flags::cf::less)) { - MAKE_8_BYTE_VAL(address); - _regs.jump_to(address); - } else { - _regs.advance_pc(9); - } - break; - } - case opcode::jleq: { - CHECK_AT_LEAST_8_BYTES(jleq) - if (_flags.cmp_flag == U64(flags::cf::less) - || _flags.cmp_flag == U64(flags::cf::eq)) { - MAKE_8_BYTE_VAL(address); - _regs.jump_to(address); - } else { - _regs.advance_pc(9); - } - break; - } - case opcode::jg: { - CHECK_AT_LEAST_8_BYTES(jq) - if (_flags.cmp_flag == U64(flags::cf::gr)) { - MAKE_8_BYTE_VAL(address); - _regs.jump_to(address); - } else { - _regs.advance_pc(9); - } - break; - } - case opcode::jgeq: { - CHECK_AT_LEAST_8_BYTES(jgeq) - if (_flags.cmp_flag == U64(flags::cf::gr) - || _flags.cmp_flag == U64(flags::cf::eq)) { - MAKE_8_BYTE_VAL(address); - _regs.jump_to(address); - } else { - _regs.advance_pc(9); - } - break; - } -#undef U64 - case opcode::halt: { - _halted = true; - break; - } - } - -#undef CHECK_LHS_REG -#undef MAKE_8_BYTE_VAL -#undef CHECK_AT_LEAST_8_BYTES -} - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "vm.hpp" + +#include "../../common/preamble.hpp" +#include "exceptions.hpp" +#include "io.hpp" + +#include +#include + +static inline auto is_version_compatible(std::uint16_t major, + std::uint16_t minor, + std::uint16_t patch) noexcept -> bool { + using namespace common; + if (major <= version::major) { + return true; + } + if (minor <= version::minor) { + return true; + } + if (patch <= version::patch) { + return true; + } + return false; +} + +namespace reqvm { + +vm::vm(const std::string& binary) { + auto path = std::filesystem::path {binary}; + _binary = load_from(path); +} + +auto vm::run() -> int { + read_preamble(); + while (_regs.pc() <= _binary->size() && !_halted) { + cycle(static_cast((*_binary)[_regs.pc()])); + } + return static_cast(_regs.ire()); +} + +auto vm::read_preamble() -> void { + std::size_t i {0}; + for (; i < sizeof(common::magic_byte_string) - 1; i++) { + if ((*_binary)[i] != common::magic_byte_string[i]) { + throw preamble_error {preamble_error::kind::nonstandard_mbs}; + } + } + std::array version_string; + for (; i < sizeof(common::magic_byte_string) + 9; i++) { + version_string[i - (sizeof(common::magic_byte_string) - 1)] = + (*_binary)[i]; + } + if (version_string[0] != '!' || version_string[3] != ';' + || version_string[6] != ';' || version_string[9] != ';') { + throw preamble_error {preamble_error::kind::bad_version_serialization}; + } + auto major = + static_cast(version_string[1]) << 8 | version_string[2]; + auto minor = + static_cast(version_string[4]) << 8 | version_string[5]; + auto patch = + static_cast(version_string[7]) << 8 | version_string[8]; + if (not ::is_version_compatible(major, minor, patch)) { + throw preamble_error {preamble_error::kind::version_too_high}; + } + // TODO: read ~~the version +~~ :^) the features + _regs.jump_to(256); +} + +auto vm::cycle(common::opcode op) -> void { +#define CHECK_LHS_REG(opcode, reg) \ + do { \ + if (registers::is_error_on_lhs((reg))) { \ + throw reqvm::invalid_register { \ + "Invalid lhs operand for opcode '" #opcode "': ", \ + static_cast((*_binary)[_regs.pc() + 1])}; \ + } \ + } while (0) + +#define MAKE_8_BYTE_VAL(val) \ + auto val = static_cast((*_binary)[_regs.pc() + 1]) << 56 \ + | static_cast((*_binary)[_regs.pc() + 2]) << 48 \ + | static_cast((*_binary)[_regs.pc() + 3]) << 40 \ + | static_cast((*_binary)[_regs.pc() + 4]) << 32 \ + | static_cast((*_binary)[_regs.pc() + 5]) << 24 \ + | static_cast((*_binary)[_regs.pc() + 6]) << 16 \ + | static_cast((*_binary)[_regs.pc() + 7]) << 8 \ + | static_cast((*_binary)[_regs.pc() + 8]) + +#define CHECK_AT_LEAST_8_BYTES(opcode) \ + if (_binary->size() - _regs.pc() < 8) { \ + throw bad_argument { \ + "Opcode '" #opcode \ + "c' is the last opcode in your binary and after" \ + " it there are less than 8 bytes, as such it is not possible to " \ + "build its argument."}; \ + } + + switch (op) { + using common::opcode; + case opcode::noop: { + // do nothing but advance pc + _regs.advance_pc(1); + break; + } + case opcode::call: { + CHECK_AT_LEAST_8_BYTES(call); + MAKE_8_BYTE_VAL(address); + for (auto& gp : _regs.general_purpose()) { + gp = 0; + } + _stack.push(_regs.pc() + 9, _regs); + _regs.jump_to(address); + break; + } + case opcode::ret: { + auto ret_addr = _stack.pop(_regs); + _regs.jump_to(ret_addr); + break; + } + case opcode::io: { + switch (io::parse_from_byte((*_binary)[_regs.pc() + 1])) { + using common::io_op; + case io_op::getc: { + auto reg = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + if (registers::is_error_on_lhs(reg)) { + throw invalid_register { + "Invalid lhs register for opcode 'io getc':", + static_cast((*_binary)[_regs.pc() + 2])}; + } + _regs[reg] = io::getc(); + break; + } + case io_op::putc: { + auto reg = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + io::putc(_regs[reg]); + break; + } + case io_op::put8c: { + auto reg = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + io::put8c(_regs[reg]); + break; + } + case io_op::putn: { + auto reg = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + io::putn(_regs[reg]); + break; + } + } + _regs.advance_pc(3); + break; + } + case opcode::add: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(add, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] += _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::sub: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(sub, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] -= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::mul: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(mul, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] *= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::div: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(div, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] /= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::mod: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(mod, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] %= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::and_: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(and, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] &= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::or_: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(or, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] |= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::xor_: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(xor, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] ^= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::not_: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + if (registers::is_error_on_lhs(r1)) { + throw invalid_register { + "Invalid operand for opcode 'not': ", + static_cast((*_binary)[_regs.pc() + 1])}; + } + _regs[r1] = ~_regs[r1]; + _regs.advance_pc(2); + break; + } + case opcode::lshft: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(lshft, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] <<= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::rshft: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + CHECK_LHS_REG(rhsft, r1); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + _regs[r1] >>= _regs[r2]; + _regs.advance_pc(3); + break; + } + case opcode::push: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + _stack.push(_regs[r1], _regs); + _regs.advance_pc(2); + break; + } + case opcode::pushc: { + CHECK_AT_LEAST_8_BYTES(pushc); + MAKE_8_BYTE_VAL(val); + _stack.push(val, _regs); + _regs.advance_pc(9); + break; + } + case opcode::pop: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + if (registers::is_error_on_lhs(r1)) { + throw invalid_register { + "Invalid operand for opcode 'pop': ", + static_cast((*_binary)[_regs.pc() + 1])}; + } + _regs[r1] = _stack.pop(_regs); + _regs.advance_pc(2); + break; + } + case opcode::cmp: { + auto r1 = registers::parse_from_byte((*_binary)[_regs.pc() + 1]); + auto r2 = registers::parse_from_byte((*_binary)[_regs.pc() + 2]); + + if (_regs[r1] < _regs[r2]) { + _flags.cmp_flag = static_cast(flags::cf::less); + } else if (_regs[r1] > _regs[r2]) { + _flags.cmp_flag = static_cast(flags::cf::gr); + } else { + _flags.cmp_flag = static_cast(flags::cf::eq); + } + + _regs.advance_pc(3); + break; + } + case opcode::jmp: { + CHECK_AT_LEAST_8_BYTES(jmp); + MAKE_8_BYTE_VAL(address); + _regs.jump_to(address); + break; + } +#define U64(x) static_cast(x) + + case opcode::jeq: { + CHECK_AT_LEAST_8_BYTES(jeq); + if (_flags.cmp_flag == U64(flags::cf::eq)) { + MAKE_8_BYTE_VAL(address); + _regs.jump_to(address); + } else { + _regs.advance_pc(9); + } + break; + } + case opcode::jneq: { + CHECK_AT_LEAST_8_BYTES(jneq) + if (_flags.cmp_flag != U64(flags::cf::eq)) { + MAKE_8_BYTE_VAL(address); + _regs.jump_to(address); + } else { + _regs.advance_pc(9); + } + break; + } + case opcode::jl: { + CHECK_AT_LEAST_8_BYTES(jl) + if (_flags.cmp_flag == U64(flags::cf::less)) { + MAKE_8_BYTE_VAL(address); + _regs.jump_to(address); + } else { + _regs.advance_pc(9); + } + break; + } + case opcode::jleq: { + CHECK_AT_LEAST_8_BYTES(jleq) + if (_flags.cmp_flag == U64(flags::cf::less) + || _flags.cmp_flag == U64(flags::cf::eq)) { + MAKE_8_BYTE_VAL(address); + _regs.jump_to(address); + } else { + _regs.advance_pc(9); + } + break; + } + case opcode::jg: { + CHECK_AT_LEAST_8_BYTES(jq) + if (_flags.cmp_flag == U64(flags::cf::gr)) { + MAKE_8_BYTE_VAL(address); + _regs.jump_to(address); + } else { + _regs.advance_pc(9); + } + break; + } + case opcode::jgeq: { + CHECK_AT_LEAST_8_BYTES(jgeq) + if (_flags.cmp_flag == U64(flags::cf::gr) + || _flags.cmp_flag == U64(flags::cf::eq)) { + MAKE_8_BYTE_VAL(address); + _regs.jump_to(address); + } else { + _regs.advance_pc(9); + } + break; + } +#undef U64 + case opcode::halt: { + _halted = true; + break; + } + } + +#undef CHECK_LHS_REG +#undef MAKE_8_BYTE_VAL +#undef CHECK_AT_LEAST_8_BYTES +} + +} // namespace reqvm diff --git a/vm/src/vm.hpp b/vm/src/vm.hpp index 33351fc..36c3273 100644 --- a/vm/src/vm.hpp +++ b/vm/src/vm.hpp @@ -1,58 +1,58 @@ -/* - * MIT License - * - * Copyright (c) 2020 Mitca Dumitru - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#pragma once - -#include "../../common/opcodes.hpp" -#include "binary_manager.hpp" -#include "flags.hpp" -#include "registers.hpp" -#include "stack.hpp" - -#include -#include -#include - -namespace reqvm { - -class vm final { -public: - vm() = delete; - explicit vm(const std::string& binary); - ~vm() noexcept = default; - - auto run() -> int; - -private: - auto read_preamble() -> void; - auto cycle(common::opcode op) -> void; - - std::unique_ptr _binary; - registers _regs; - stack _stack; - flags _flags; - bool _halted {false}; -}; - -} // namespace reqvm +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "../../common/opcodes.hpp" +#include "binary_manager.hpp" +#include "flags.hpp" +#include "registers.hpp" +#include "stack.hpp" + +#include +#include +#include + +namespace reqvm { + +class vm final { +public: + vm() = delete; + explicit vm(const std::string& binary); + ~vm() noexcept = default; + + auto run() -> int; + +private: + auto read_preamble() -> void; + auto cycle(common::opcode op) -> void; + + std::unique_ptr _binary; + registers _regs; + stack _stack; + flags _flags; + bool _halted {false}; +}; + +} // namespace reqvm From 857ca26afecc3d2ed2b8bd53e4be45e52b9c6391 Mon Sep 17 00:00:00 2001 From: RealKC Date: Sun, 26 Jul 2020 23:13:49 +0300 Subject: [PATCH 10/10] snip2 --- assembler/src/expression_parser.cpp | 124 ++++++++++++++++++++++++++++ assembler/src/expression_parser.hpp | 69 ++++++++++++++++ assembler/src/macro.cpp | 27 ++++++ 3 files changed, 220 insertions(+) create mode 100644 assembler/src/expression_parser.cpp create mode 100644 assembler/src/expression_parser.hpp create mode 100644 assembler/src/macro.cpp diff --git a/assembler/src/expression_parser.cpp b/assembler/src/expression_parser.cpp new file mode 100644 index 0000000..e4431df --- /dev/null +++ b/assembler/src/expression_parser.cpp @@ -0,0 +1,124 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "expression_parser.hpp" + +#include "error_reporting.hpp" + +namespace reqvm { + +expression_parser::expression_parser( + const std::string& source, + const std::unordered_map& macros) { + auto tokens = lex(source); + if (tokens.empty()) { + return; + } + parse(tokens, macros); +} + +auto expression_parser::lex(const std::string& source) -> std::vector { + std::vector tokens; + + for (std::size_t i = 0; i < source.size(); i++) { + if (std::isspace(source[i])) { + continue; + } + + // Skip new line escapes + if (source[i] == '\\') { + continue; + } + + token current_token; + + if (std::isdigit(source[i])) { + current_token.type = token::type::number; + + auto number_end = i; + while (not std::isspace(source[number_end])) { + number_end++; + } + current_token.source = {source.data() + i, number_end - i}; + tokens.push_back(current_token); + continue; + } + + if (source[i] == '@') { + current_token.type = token::type::macro; + + auto macro_name_end = i + 1; + while (not std::isspace(source[macro_name_end])) { + macro_name_end++; + } + + if (macro_name_end == i + 1) { + report_to_user(level::error, "Stray '@' in expression"); + return {}; + } + + current_token.source = {source.data() + i + 1, + macro_name_end - i + 1}; + tokens.push_back(current_token); + continue; + } + + auto is_single_char_operator = [](char c) { + return c == '+' || c == '-' || c == '%' || c == '/' || c == '*' + || c == '~' || c == '!'; + }; + if (is_single_char_operator(source[i])) { + current_token.type == token::type::operator_; + current_token.source = {source.data() + i, 1}; + tokens.push_back(current_token); + continue; + } + + if (source[i] == '&' || source[i] == '|') { + if (source.size() - i < 2) { + report_to_user(level::error, + "Invalid operator at end of expression"); + return {}; + } + if (source[i + 1] == source[i]) { + current_token.type = token::type::operator_; + current_token.source = {source.data() + i, 2}; + tokens.push_back(current_token); + continue; + } + current_token.type = token::type::operator_; + current_token.source = {source.data() + i, 1}; + tokens.push_back(current_token); + continue; + } + + report_to_user(level::error, "Invalid character in expression"); + return {}; + } + return tokens; +} + +using parse_function = void (*)() + +} // namespace reqvm diff --git a/assembler/src/expression_parser.hpp b/assembler/src/expression_parser.hpp new file mode 100644 index 0000000..68a5cb7 --- /dev/null +++ b/assembler/src/expression_parser.hpp @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "macro.hpp" + +#include +#include +#include +#include +#include +#include + +namespace reqvm { + +class expression_parser { +public: + explicit expression_parser( + const std::string& source, + const std::unordered_map& macros); + + auto had_errors() noexcept { return not _result.has_value(); } + auto result() noexcept { + ASSERT(_result.has_value()); + return _result.value(); + } + +private: + struct token { + enum class type { + number, + macro, + operator_, + lparen, + rparen, + } type; + std::string_view source; + }; + auto lex(const std::string& source) -> std::vector; + + auto parse(const std::vector&, + const std::unordered_map& macros) -> void; + + std::optional _result; +}; + +} // namespace reqvm diff --git a/assembler/src/macro.cpp b/assembler/src/macro.cpp new file mode 100644 index 0000000..2ad4d85 --- /dev/null +++ b/assembler/src/macro.cpp @@ -0,0 +1,27 @@ +/* + * MIT License + * + * Copyright (c) 2020 Mitca Dumitru + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "macro.hpp" + +namespace reqvm {} \ No newline at end of file