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::type; std::array 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 void Executor::perform(uint8_t *operand [[maybe_unused]]) { +template 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(nullptr); + ++program_counter_; + return; + + case AddressingMode::Accumulator: + perform(&a_); + ++program_counter_; + return; + + case AddressingMode::Immediate: + perform(&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(&memory_[address & 0x1fff]); + return; + } + + uint8_t value = memory_[address & 0x1fff]; + perform(&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 void Executor::perform(Executor *) { +template 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 { public: Executor(); @@ -58,21 +47,21 @@ class Executor: public CachingExecutor { fill(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 void fill_operation(Action::Performer *target) { + template void fill_operation(Performer *target) { *target = &Executor::perform; if constexpr (addressing_mode+1 < MaxAddressingMode) { fill_operation(target + 1); } } - template void fill(Action::Performer *target) { + template void fill(Performer *target) { fill_operation(target); target += MaxOperation - MinOperation; if constexpr (operation+1 < MaxOperation) { @@ -85,16 +74,21 @@ class Executor: public CachingExecutor { /*! Performs @c operation using @c operand as the value fetched from memory, if any. */ - template static void perform(uint8_t *operand); + template void perform(uint8_t *operand); /*! Performs @c operation in @c addressing_mode. */ - template static void perform(Executor *); + template void perform(); private: // MARK: - Instruction set state. + + // Memory. uint8_t memory_[0x2000]; + + // Registers. + uint8_t a_, x_, y_; }; }