From ad03858c6e5477e470000be08b5e7ba81eb9d205 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Sun, 17 Jan 2021 20:53:11 -0500 Subject: [PATCH] Switches performers to member functions. Very slightly starts work on M50740 performers. --- InstructionSets/CachingExecutor.hpp | 2 +- InstructionSets/M50740/Executor.cpp | 71 ++++++++++++++++++++++++++++- InstructionSets/M50740/Executor.hpp | 28 +++++------- 3 files changed, 81 insertions(+), 20 deletions(-) diff --git a/InstructionSets/CachingExecutor.hpp b/InstructionSets/CachingExecutor.hpp index 63a7b4ff1..f9c534edd 100644 --- a/InstructionSets/CachingExecutor.hpp +++ b/InstructionSets/CachingExecutor.hpp @@ -63,7 +63,7 @@ template < public: protected: - using Performer = void (*)(Executor *); + using Performer = void (Executor::*)(); using PerformerIndex = typename MinIntTypeValue<max_performer_count>::type; std::array<Performer, max_performer_count> performers_; diff --git a/InstructionSets/M50740/Executor.cpp b/InstructionSets/M50740/Executor.cpp index 3c637bb62..8a9f2e286 100644 --- a/InstructionSets/M50740/Executor.cpp +++ b/InstructionSets/M50740/Executor.cpp @@ -20,8 +20,75 @@ Executor::Executor() { } } -template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_unused]]) { +template <Operation operation, AddressingMode addressing_mode> void Executor::perform() { + // Deal with all modes that don't access memory up here; + // those that access memory will go through a slightly longer + // sequence below that wraps the address and checks whether + // a write is valid [if required]. + + int address; +#define next8() memory_[(program_counter_ + 1) & 0x1fff] +#define next16() memory_[(program_counter_ + 1) & 0x1fff] | (memory_[(program_counter_ + 2) & 0x1fff] << 8) + + switch(addressing_mode) { + + // Addressing modes with no further memory access. + + case AddressingMode::Implied: + perform<operation>(nullptr); + ++program_counter_; + return; + + case AddressingMode::Accumulator: + perform<operation>(&a_); + ++program_counter_; + return; + + case AddressingMode::Immediate: + perform<operation>(&next8()); + program_counter_ += 2; + return; + + // Addressing modes with a memory access. + + case AddressingMode::Absolute: + address = next16(); + program_counter_ += 3; + break; + + case AddressingMode::AbsoluteX: + address = next16() + x_; + program_counter_ += 3; + break; + + case AddressingMode::AbsoluteY: + address = next16() + y_; + program_counter_ += 3; + break; + + /* TODO: the rest. */ + + default: + assert(false); + } + +#undef next16 +#undef next8 + + assert(access_type(operation) != AccessType::None); + + if constexpr(access_type(operation) == AccessType::Read) { + perform<operation>(&memory_[address & 0x1fff]); + return; + } + + uint8_t value = memory_[address & 0x1fff]; + perform<operation>(&value); + + // TODO: full writing logic here; only the first 96 bytes are RAM, + // there are also timers and IO ports to handle. + memory_[address & 0x1fff] = value; } -template <Operation operation, AddressingMode addressing_mode> void Executor::perform(Executor *) { +template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_unused]]) { } diff --git a/InstructionSets/M50740/Executor.hpp b/InstructionSets/M50740/Executor.hpp index f547d6608..2b2a5ee3c 100644 --- a/InstructionSets/M50740/Executor.hpp +++ b/InstructionSets/M50740/Executor.hpp @@ -18,17 +18,6 @@ namespace M50740 { class Executor; -/*! - M50740 actions require no further context; the addressing mode and operation is baked in, - so using the Executor to enquire of memory and the program counter is sufficient. -*/ -struct Action { - using Performer = void (*)(Executor *); - Performer perform = nullptr; - - inline Action(Performer performer) noexcept : perform(performer) {} -}; - class Executor: public CachingExecutor<Executor, 0x2000, 256, false> { public: Executor(); @@ -58,21 +47,21 @@ class Executor: public CachingExecutor<Executor, 0x2000, 256, false> { fill<int(MinOperation), int(MinAddressingMode)>(performers); } - Action::Performer performer(Operation operation, AddressingMode addressing_mode) { + Performer performer(Operation operation, AddressingMode addressing_mode) { return performers[int(addressing_mode) * (MaxOperation - MinOperation) + int(operation) - MinOperation]; } private: - Action::Performer performers[(MaxAddressingMode - MinAddressingMode) * (MaxOperation - MinOperation)]; + Performer performers[(MaxAddressingMode - MinAddressingMode) * (MaxOperation - MinOperation)]; - template<int operation, int addressing_mode> void fill_operation(Action::Performer *target) { + template<int operation, int addressing_mode> void fill_operation(Performer *target) { *target = &Executor::perform<Operation(operation), AddressingMode(addressing_mode)>; if constexpr (addressing_mode+1 < MaxAddressingMode) { fill_operation<operation, addressing_mode+1>(target + 1); } } - template<int operation, int addressing_mode> void fill(Action::Performer *target) { + template<int operation, int addressing_mode> void fill(Performer *target) { fill_operation<operation, addressing_mode>(target); target += MaxOperation - MinOperation; if constexpr (operation+1 < MaxOperation) { @@ -85,16 +74,21 @@ class Executor: public CachingExecutor<Executor, 0x2000, 256, false> { /*! Performs @c operation using @c operand as the value fetched from memory, if any. */ - template <Operation operation> static void perform(uint8_t *operand); + template <Operation operation> void perform(uint8_t *operand); /*! Performs @c operation in @c addressing_mode. */ - template <Operation operation, AddressingMode addressing_mode> static void perform(Executor *); + template <Operation operation, AddressingMode addressing_mode> void perform(); private: // MARK: - Instruction set state. + + // Memory. uint8_t memory_[0x2000]; + + // Registers. + uint8_t a_, x_, y_; }; }