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_;
 };
 
 }