From 165c63701a7af0a1302dfa7c6415b7d8524b922d Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Thu, 9 Sep 2021 22:35:04 -0600 Subject: [PATCH] Stub in support for commander x16 --- examples/hello_x16.cpp | 7 + examples/simple_game/chargen.hpp | 2 +- examples/simple_game/game.cpp | 2 +- examples/simple_game/x16.hpp | 29 ++++ include/np_int.hpp | 280 +++++++++++++++++++++++++++++++ include/personalities/x16.hpp | 61 +++++++ src/6502-c++.cpp | 18 +- test/tests.cpp | 2 +- 8 files changed, 394 insertions(+), 7 deletions(-) create mode 100644 examples/hello_x16.cpp create mode 100644 examples/simple_game/x16.hpp create mode 100644 include/np_int.hpp create mode 100644 include/personalities/x16.hpp diff --git a/examples/hello_x16.cpp b/examples/hello_x16.cpp new file mode 100644 index 0000000..cf0ac3e --- /dev/null +++ b/examples/hello_x16.cpp @@ -0,0 +1,7 @@ +#include + +int main() +{ + x16::vera::puts({15,20}, petscii::PETSCII("HELLO X16 FROM C++!")); +} + diff --git a/examples/simple_game/chargen.hpp b/examples/simple_game/chargen.hpp index b5a9efc..92b0778 100644 --- a/examples/simple_game/chargen.hpp +++ b/examples/simple_game/chargen.hpp @@ -1,7 +1,7 @@ #include namespace petscii { -constexpr static std::uint8_t uppercase[] = { +constexpr static std::array uppercase = { 0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x62, 0x3C, 0x00, 0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, diff --git a/examples/simple_game/game.cpp b/examples/simple_game/game.cpp index d8d9463..99e2432 100644 --- a/examples/simple_game/game.cpp +++ b/examples/simple_game/game.cpp @@ -167,7 +167,7 @@ template static constexpr auto from_pixels_to_petscii(const { petscii::Graphic result{}; - constexpr auto charset = load_charset(uppercase); + constexpr auto charset = petscii::load_charset(petscii::uppercase); for (uint8_t x = 0; x < pixels.width(); x += 8) { for (uint8_t y = 0; y < pixels.height(); y += 8) { diff --git a/examples/simple_game/x16.hpp b/examples/simple_game/x16.hpp new file mode 100644 index 0000000..23d59cf --- /dev/null +++ b/examples/simple_game/x16.hpp @@ -0,0 +1,29 @@ +#ifndef INC_6502_C_CPP_X16 +#define INC_6502_C_CPP_X16 + +#include <6502.hpp> +#include +#include +#include + +namespace x16::vera { +static constexpr std::uint16_t ADDR_L = 0x9f20; +static constexpr std::uint16_t ADDR_M = 0x9f21; +static constexpr std::uint16_t ADDR_H = 0x9f22; +static constexpr std::uint16_t DATA0 = 0x9f23; + +// this assumes we are already in a text mode, initialized by the kernal +static void puts(const geometry::point &loc, std::span str) { + mos6502::poke(ADDR_L, loc.x * 2); + mos6502::poke(ADDR_M, loc.y); + mos6502::poke(ADDR_H, 0x20); // auto increment by 2 after each poke of the character, we are skipping the attribute byte + + for (const auto c : str) { + mos6502::poke(DATA0, c); + } +} +} + +#endif + + diff --git a/include/np_int.hpp b/include/np_int.hpp new file mode 100644 index 0000000..c16977c --- /dev/null +++ b/include/np_int.hpp @@ -0,0 +1,280 @@ +#include +#include +#include +#include + +template struct np_int +{ + Int val; + + // intentionally implicit conversions + // from the underlying type allowed + [[gnu::always_inline]] constexpr np_int(Int i) noexcept : val{ i } {} + + [[gnu::always_inline]] constexpr explicit operator Int() noexcept { return val; } + + [[gnu::always_inline]] constexpr auto operator<=>(const np_int &) const noexcept = default; + + template + [[gnu::always_inline]] constexpr auto operator<=>(const np_int &other) const noexcept + requires(std::is_signed_v == std::is_signed_v) + { + return val == other.val ? std::strong_ordering::equal + : val < other.val ? std::strong_ordering::less + : std::strong_ordering::greater; + } +}; + +template constexpr bool np_or_integral_v = std::is_integral_v; + +template constexpr bool np_or_integral_v> = true; + +template concept np_or_integral = np_or_integral_v; + +template [[nodiscard, gnu::always_inline, gnu::const]] constexpr auto val(Int i) noexcept +{ + return i; +} + +template [[nodiscard, gnu::always_inline, gnu::const]] constexpr auto val(np_int i) noexcept +{ + return i.val; +} + +template consteval auto calculate_common_int() +{ + constexpr auto size = std::max(sizeof(LHS), sizeof(RHS)); + if constexpr (std::is_unsigned_v && std::is_unsigned_v) { + if constexpr (size == 1) { + return std::uint8_t{}; + } else if constexpr (size == 2) { + return std::uint16_t{}; + } else if constexpr (size == 4) { + return std::uint32_t{}; + } else if constexpr (size == 8) { + return std::uint64_t{}; + } + } else { + if constexpr (size == 1) { + return std::int8_t{}; + } else if constexpr (size == 2) { + return std::int16_t{}; + } else if constexpr (size == 4) { + return std::int32_t{}; + } else if constexpr (size == 8) { + return std::int64_t{}; + } + } +} + +template using common_int_t = decltype(calculate_common_int()); + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator>>(np_int lhs, + np_or_integral auto rhs) noexcept +{ + return np_int{ static_cast(lhs.val >> static_cast(rhs)) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator<<(np_int lhs, + np_or_integral auto rhs) noexcept +{ + return np_int{ static_cast(lhs.val << static_cast(rhs)) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator+(np_int lhs, np_int rhs) noexcept +{ + using Type = common_int_t; + return np_int{ static_cast(lhs.val + rhs.val) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator-(np_int lhs, np_int rhs) noexcept +{ + using Type = common_int_t; + return np_int{ static_cast(lhs.val - rhs.val) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator*(np_int lhs, np_int rhs) noexcept +{ + using Type = common_int_t; + return np_int{ static_cast(lhs.val * rhs.val) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator/(np_int lhs, np_int rhs) noexcept +{ + using Type = common_int_t; + return np_int{ static_cast(lhs.val / rhs.val) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator|(np_int lhs, np_int rhs) noexcept +{ + return np_int{ static_cast(lhs.val | rhs.val) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator&(np_int lhs, np_int rhs) noexcept +{ + return np_int{ static_cast(lhs.val & rhs.val) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator%(np_int lhs, np_int rhs) noexcept +{ + using Type = common_int_t; + return np_int{ static_cast(lhs.val % rhs.val) }; +} + +template +[[nodiscard, gnu::always_inline, gnu::const]] constexpr auto operator^(np_int lhs, np_int rhs) noexcept +{ + return np_int{ static_cast(lhs.val ^ rhs.val) }; +} + +template +[[gnu::always_inline]] constexpr auto &operator+=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val += static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator-=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val -= static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator/=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val /= static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator*=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val /= static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator%=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val %= static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator&=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val &= static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator|=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val |= static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator^=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val ^= static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator<<=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val <<= static_cast(rhs); + return lhs; +} + +template +[[gnu::always_inline]] constexpr auto &operator>>=(np_int &lhs, np_or_integral auto rhs) noexcept +{ + lhs.val >>= static_cast(rhs); + return lhs; +} + +using uint_np8_t = np_int; +using uint_np16_t = np_int; +using uint_np32_t = np_int; +using uint_np64_t = np_int; + +using int_np8_t = np_int; +using int_np16_t = np_int; +using int_np32_t = np_int; +using int_np64_t = np_int; + +template +constexpr bool np_meets_requirements = + sizeof(T) == sizeof(np_int) + && std::is_trivially_destructible_v> &&std::is_trivially_move_constructible_v> + &&std::is_trivially_copy_constructible_v> &&std::is_trivially_copy_assignable_v> + &&std::is_trivially_move_assignable_v>; + +static_assert(np_meets_requirements); +static_assert(np_meets_requirements); +static_assert(np_meets_requirements); +static_assert(np_meets_requirements); + +static_assert(np_meets_requirements); +static_assert(np_meets_requirements); +static_assert(np_meets_requirements); +static_assert(np_meets_requirements); + +// ensures that First paired with any of Rest, in any order +// results in the same type as First again +template +constexpr bool is_same_combinations_v = (std::is_same_v> && ...) + && (std::is_same_v> && ...); + +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); + +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); + +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); +static_assert(is_same_combinations_v); + +auto left_shift(uint_np8_t value, int val) { return value << val; } + +auto left_shift(std::uint8_t value, int val) { return static_cast(value << val); } + +std::uint8_t unsigned_value(); +std::int8_t signed_value(); + + +int main(int argc, const char **) +{ + uint_np8_t a{ 15 }; + auto value = a + uint_np16_t{ 10 }; + + value += uint_np16_t{ 16 }; + + [[maybe_unused]] const auto comparison = value < uint_np8_t{ 3 }; + // return static_cast(value); + + int i = 1; + [[maybe_unused]] int v = (i << argc); + + + return std::cmp_less(unsigned_value(), signed_value()); +} diff --git a/include/personalities/x16.hpp b/include/personalities/x16.hpp new file mode 100644 index 0000000..b44b9f7 --- /dev/null +++ b/include/personalities/x16.hpp @@ -0,0 +1,61 @@ +#ifndef INC_6502_C_X16_HPP +#define INC_6502_C_X16_HPP + +#include "../personality.hpp" + +struct X16 : Personality +{ + + void insert_autostart_sequence(std::vector &new_instructions) const override + { + constexpr static auto start_address = 0x0801; + + // first 2 bytes is the load address for a PRG file. + new_instructions.emplace_back(ASMLine::Type::Directive, ".word " + std::to_string(start_address)); + new_instructions.emplace_back(ASMLine::Type::Directive, "* = " + std::to_string(start_address)); + new_instructions.emplace_back(ASMLine::Type::Directive, "; jmp to start of program with BASIC"); + new_instructions.emplace_back(ASMLine::Type::Directive, ".byt $0B,$08,$0A,$00,$9E,$32,$30,$36,$31,$00,$00,$00"); + } + + [[nodiscard]] Operand get_register(const int reg_num) const override + { + switch (reg_num) { + // based on c64, they are basically the same system, but I'm using a different file so they can fork from each other + case 0: return Operand(Operand::Type::literal, "$4e");// unused, int->fp routine pointer + case 1: return Operand(Operand::Type::literal, "$4f"); + case 2: return Operand(Operand::Type::literal, "$50");// unused + case 3: return Operand(Operand::Type::literal, "$51");// unused + case 4: return Operand(Operand::Type::literal, "$52");// bit buffer for rs232 + case 5: return Operand(Operand::Type::literal, "$53");// counter for rs232 + case 6: return Operand(Operand::Type::literal, "$54");// unused + case 7: return Operand(Operand::Type::literal, "$55");// unused + case 8: return Operand(Operand::Type::literal, "$56");// unused + case 9: return Operand(Operand::Type::literal, "$57");// unused + case 10: return Operand(Operand::Type::literal, "$58");// Current BASIC line number + case 11: return Operand(Operand::Type::literal, "$59");// Current BASIC line number + case 12: return Operand(Operand::Type::literal, "$5a");// arithmetic register #3 + case 13: return Operand(Operand::Type::literal, "$5b"); + case 14: return Operand(Operand::Type::literal, "$5c"); + case 15: return Operand(Operand::Type::literal, "$5d"); + case 16: return Operand(Operand::Type::literal, "$5e"); + case 17: return Operand(Operand::Type::literal, "$5f"); + case 18: return Operand(Operand::Type::literal, "$60"); + case 19: return Operand(Operand::Type::literal, "$61"); + case 20: return Operand(Operand::Type::literal, "$62"); + case 21: return Operand(Operand::Type::literal, "$63"); + case 22: return Operand(Operand::Type::literal, "$64"); + case 23: return Operand(Operand::Type::literal, "$65"); + case 24: return Operand(Operand::Type::literal, "$66"); + case 25: return Operand(Operand::Type::literal, "$67"); + case 26: return Operand(Operand::Type::literal, "$68"); + case 27: return Operand(Operand::Type::literal, "$69"); + case 28: return Operand(Operand::Type::literal, "$6a"); + case 29: return Operand(Operand::Type::literal, "$6b"); + case 30: return Operand(Operand::Type::literal, "$6c"); + case 31: return Operand(Operand::Type::literal, "$6d"); + } + throw std::runtime_error("Unhandled register number: " + std::to_string(reg_num)); + } +}; + +#endif// INC_6502_C_X16_HPP diff --git a/src/6502-c++.cpp b/src/6502-c++.cpp index 27f38f7..d5be3e4 100644 --- a/src/6502-c++.cpp +++ b/src/6502-c++.cpp @@ -19,6 +19,7 @@ #include "include/lib1funcs.hpp" #include "include/optimizer.hpp" #include "include/personalities/c64.hpp" +#include "include/personalities/x16.hpp" int to_int(const std::string_view sv) { @@ -1311,12 +1312,12 @@ std::vector run(const Personality &personality, std::istream &input, co return new_instructions; } -enum struct Target { C64 }; +enum struct Target { C64, X16 }; int main(const int argc, const char **argv) { spdlog::set_level(spdlog::level::warn); - const std::map targets{ { "C64", Target::C64 } }; + const std::map targets{ { "C64", Target::C64 }, { "X16", Target::X16 } }; CLI::App app{ "C++ Compiler for 6502 processors" }; std::filesystem::path filename{}; @@ -1401,9 +1402,18 @@ int main(const int argc, const char **argv) std::ifstream input(avr_output_file); - C64 personality; - const auto new_instructions = run(personality, input, optimize); + const auto new_instructions = [&]() { + switch (target) { + case Target::C64: + return run(C64{}, input, optimize); + case Target::X16: + return run(X16{}, input, optimize); + default: + spdlog::critical("Unhandled target type"); + return std::vector{}; + } + }(); { // make sure file is closed before we try to re-open it with xa diff --git a/test/tests.cpp b/test/tests.cpp index e6fbfc2..e6b6b92 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -80,7 +80,7 @@ quit REQUIRE(system(fmt::format( - "{} -f {} -t C64 {} {}", mos6502_cpp_executable, source_filename, optimization_level, optimize_6502) + "{} {} -t C64 {} {}", mos6502_cpp_executable, source_filename, optimization_level, optimize_6502) .c_str()) == EXIT_SUCCESS); REQUIRE(