From b62f484d93bc9c0f70e178ea31a64e4c00efcc9c Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 18 Jun 2022 13:28:15 -0400
Subject: [PATCH 01/31] Start scaffolding a 65816 test generator.

---
 .../Clock Signal.xcodeproj/project.pbxproj    |   4 +
 .../65816ComparativeTests.mm                  | 107 ++++++++++++++++++
 2 files changed, 111 insertions(+)
 create mode 100644 OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm

diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj
index 5ced38bd7..1744ac269 100644
--- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
+++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj	
@@ -12,6 +12,7 @@
 		4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
 		4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
 		4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; };
+		4B04C899285E3DC800AA8FD6 /* 65816ComparativeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B04C898285E3DC800AA8FD6 /* 65816ComparativeTests.mm */; };
 		4B051C912669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; };
 		4B051C922669C90B00CA44E8 /* ROMCatalogue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */; };
 		4B051C93266D9D6900CA44E8 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; };
@@ -1110,6 +1111,7 @@
 		4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
 		4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; };
 		4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
+		4B04C898285E3DC800AA8FD6 /* 65816ComparativeTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = 65816ComparativeTests.mm; sourceTree = "<group>"; };
 		4B051C5826670A9300CA44E8 /* ROMCatalogue.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ROMCatalogue.cpp; sourceTree = "<group>"; };
 		4B051C5926670A9300CA44E8 /* ROMCatalogue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ROMCatalogue.hpp; sourceTree = "<group>"; };
 		4B051C94266EF50200CA44E8 /* AppleIIController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleIIController.swift; sourceTree = "<group>"; };
@@ -4230,6 +4232,7 @@
 			children = (
 				4B85322922778E4200F26553 /* Comparative68000.hpp */,
 				4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
+				4B04C898285E3DC800AA8FD6 /* 65816ComparativeTests.mm */,
 				4B90467522C6FD6E000E2074 /* 68000ArithmeticTests.mm */,
 				4B9D0C4A22C7D70900DE1AD3 /* 68000BCDTests.mm */,
 				4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */,
@@ -6053,6 +6056,7 @@
 				4B778EF023A5D68C0000D260 /* 68000Storage.cpp in Sources */,
 				4B7752B228217EAE0073E2C5 /* StaticAnalyser.cpp in Sources */,
 				4B7752BC28217F1D0073E2C5 /* Disk.cpp in Sources */,
+				4B04C899285E3DC800AA8FD6 /* 65816ComparativeTests.mm in Sources */,
 				4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */,
 				4B778EFA23A5EB790000D260 /* DMK.cpp in Sources */,
 				4BEE1EC122B5E2FD000A26A6 /* Encoder.cpp in Sources */,
diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
new file mode 100644
index 000000000..0af1b0ae9
--- /dev/null
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -0,0 +1,107 @@
+//
+//  65816ComparativeTests.m
+//  Clock SignalTests
+//
+//  Created by Thomas Harte on 18/06/2022.
+//  Copyright © 2022 Thomas Harte. All rights reserved.
+//
+
+#include "65816.hpp"
+
+#import <XCTest/XCTest.h>
+#include <array>
+#include <vector>
+#include <unordered_map>
+
+namespace {
+
+struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
+	// Use a map to store RAM contents, in order to preserve initialised state.
+	std::unordered_map<uint32_t, uint8_t> ram;
+
+	Cycles perform_bus_operation(CPU::MOS6502Esque::BusOperation operation, uint32_t address, uint8_t *value) {
+		// Record the basics of the operation.
+		auto &cycle = cycles.emplace_back();
+		cycle.address = address;
+		cycle.operation = operation;
+		cycle.value = 0xff;
+
+		// Perform the operation, and fill in the cycle's value.
+		using BusOperation = CPU::MOS6502Esque::BusOperation;
+		auto ram_value = ram.find(address);
+		switch(operation) {
+			case BusOperation::ReadOpcode:
+				++opcodes_fetched_;
+			case BusOperation::Read:
+			case BusOperation::ReadProgram:
+			case BusOperation::ReadVector:
+				if(ram_value != ram.end()) {
+					cycle.value = *value = ram_value->second;
+				} else {
+					cycle.value = *value = uint8_t(rand() >> 8);
+					ram[address] = cycle.value;
+				}
+			break;
+
+			case BusOperation::Write:
+				cycle.value = ram[address] = *value;
+			break;
+
+			default: break;
+		}
+
+		// Don't occupy any bonus time.
+		return Cycles(1);
+	}
+
+	int opcodes_fetched_ = 0;
+
+	struct Cycle {
+		CPU::MOS6502Esque::BusOperation operation;
+		uint32_t address;
+		uint8_t value;
+	};
+	std::vector<Cycle> cycles;
+};
+
+}
+
+// MARK: - New test generator.
+
+@interface TestGenerator : NSObject
+@end
+
+@implementation TestGenerator
+
+- (void)generate {
+	BusHandler handler;
+	CPU::WDC65816::Processor<BusHandler, false> processor(handler);
+
+	for(int operation = 0; operation < 512; operation++) {
+		const bool is_emulated = operation & 256;
+		const uint8_t opcode = operation & 255;
+
+		// TODO: set up for opcode and emulation mode.
+
+		// TODO: run for a bit longer than this, of course.
+		processor.run_for(Cycles(1));
+	}
+
+	printf("");
+}
+
+@end
+
+// MARK: - Existing test evaluator.
+
+@interface WDC65816ComparativeTests : XCTestCase
+@end
+
+@implementation WDC65816ComparativeTests
+
+// A generator for tests; not intended to be a permanent fixture.
+- (void)testGenerate {
+	[[[TestGenerator alloc] init] generate];
+}
+
+@end

From 586ef4810bbe627120fec3ba46e1898443d35cac Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 18 Jun 2022 16:25:57 -0400
Subject: [PATCH 02/31] Add `restart_operation_fetch`, to aid with testing.

---
 Processors/65816/65816.hpp                              | 9 ++++++++-
 Processors/65816/Implementation/65816Implementation.hpp | 7 +++++++
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/Processors/65816/65816.hpp b/Processors/65816/65816.hpp
index e65b7cd45..d9390113e 100644
--- a/Processors/65816/65816.hpp
+++ b/Processors/65816/65816.hpp
@@ -45,7 +45,8 @@ class ProcessorBase: protected ProcessorStorage {
 		inline bool get_is_resetting() const;
 
 		/*!
-			Returns the current state of all lines not ordinarily pushed to the BusHandler.
+			Returns the current state of all lines not ordinarily pushed to the BusHandler,
+			as listed in the ExtendedBusOutput enum.
 		*/
 		inline int get_extended_bus_output();
 
@@ -54,6 +55,12 @@ class ProcessorBase: protected ProcessorStorage {
 		*/
 		inline bool is_jammed() const;
 
+		/*!
+			FOR TESTING PURPOSES ONLY: forces the processor into a state where
+			the next thing it intends to do is fetch a new opcode.
+		*/
+		inline void restart_operation_fetch();
+
 		void set_value_of_register(Register r, uint16_t value);
 		uint16_t get_value_of_register(Register r) const;
 };
diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index f3128e550..71bf18f2a 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -1065,3 +1065,10 @@ int ProcessorBase::get_extended_bus_output() {
 		(registers_.mx_flags[1] ? ExtendedBusOutput::IndexSize : 0) |
 		(registers_.emulation_flag ? ExtendedBusOutput::Emulation : 0);
 }
+
+void ProcessorBase::restart_operation_fetch() {
+	// Find a OperationMoveToNextProgram, so that the main loop can make
+	// relevant decisions.
+	next_op_ = micro_ops_.data();
+	while(*next_op_ != OperationMoveToNextProgram) ++next_op_;
+}

From f8e695473928f2a71f033c67032354c3c85b892f Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 18 Jun 2022 16:26:40 -0400
Subject: [PATCH 03/31] Ensure complete runs of each tested opcode.

---
 .../65816ComparativeTests.mm                  | 47 +++++++++++++++----
 1 file changed, 39 insertions(+), 8 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index 0af1b0ae9..7699b368c 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -15,9 +15,12 @@
 
 namespace {
 
+struct StopException {};
+
 struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 	// Use a map to store RAM contents, in order to preserve initialised state.
 	std::unordered_map<uint32_t, uint8_t> ram;
+	std::unordered_map<uint32_t, uint8_t> inventions;
 
 	Cycles perform_bus_operation(CPU::MOS6502Esque::BusOperation operation, uint32_t address, uint8_t *value) {
 		// Record the basics of the operation.
@@ -31,7 +34,11 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		auto ram_value = ram.find(address);
 		switch(operation) {
 			case BusOperation::ReadOpcode:
-				++opcodes_fetched_;
+				--opcodes_remaining;
+				if(!opcodes_remaining) {
+					cycles.pop_back();
+					throw StopException();
+				}
 			case BusOperation::Read:
 			case BusOperation::ReadProgram:
 			case BusOperation::ReadVector:
@@ -39,7 +46,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 					cycle.value = *value = ram_value->second;
 				} else {
 					cycle.value = *value = uint8_t(rand() >> 8);
-					ram[address] = cycle.value;
+					inventions[address] = ram[address] = cycle.value;
 				}
 			break;
 
@@ -54,7 +61,19 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		return Cycles(1);
 	}
 
-	int opcodes_fetched_ = 0;
+	template <typename Processor> void setup(Processor &processor, uint8_t opcode) {
+		ram.clear();
+		inventions.clear();
+		cycles.clear();
+
+		using Register = CPU::MOS6502Esque::Register;
+		const uint32_t pc =
+			processor.get_value_of_register(Register::ProgramCounter) |
+			(processor.get_value_of_register(Register::ProgramBank) << 8);
+		inventions[pc] = ram[pc] = opcode;
+	}
+
+	int opcodes_remaining = 0;
 
 	struct Cycle {
 		CPU::MOS6502Esque::BusOperation operation;
@@ -77,17 +96,29 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 	BusHandler handler;
 	CPU::WDC65816::Processor<BusHandler, false> processor(handler);
 
+	// Never run the official reset procedure.
+	processor.set_power_on(false);
+
 	for(int operation = 0; operation < 512; operation++) {
 		const bool is_emulated = operation & 256;
 		const uint8_t opcode = operation & 255;
 
-		// TODO: set up for opcode and emulation mode.
+		// Ensure processor's next action is an opcode fetch.
+		processor.restart_operation_fetch();
 
-		// TODO: run for a bit longer than this, of course.
-		processor.run_for(Cycles(1));
+		// Randomise processor state.
+		using Register = CPU::MOS6502Esque::Register;
+		processor.set_value_of_register(Register::EmulationFlag, is_emulated);
+
+		// Establish the opcode.
+		handler.setup(processor, opcode);
+
+		// Run to the second opcode fetch.
+		handler.opcodes_remaining = 2;
+		try {
+			processor.run_for(Cycles(100));
+		} catch (const StopException &) {}
 	}
-
-	printf("");
 }
 
 @end

From eb82e06fab52e4ff820299eb5098f0de9928b628 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 18 Jun 2022 19:21:56 -0400
Subject: [PATCH 04/31] Add randomised initial state, fix PC.

---
 .../65816ComparativeTests.mm                  | 25 +++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index 7699b368c..605a4db8d 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -69,7 +69,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		using Register = CPU::MOS6502Esque::Register;
 		const uint32_t pc =
 			processor.get_value_of_register(Register::ProgramCounter) |
-			(processor.get_value_of_register(Register::ProgramBank) << 8);
+			(processor.get_value_of_register(Register::ProgramBank) << 16);
 		inventions[pc] = ram[pc] = opcode;
 	}
 
@@ -99,6 +99,10 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 	// Never run the official reset procedure.
 	processor.set_power_on(false);
 
+	// Make tests repeatable, at least for any given instance of
+	// the runtime.
+	srand(65816);
+
 	for(int operation = 0; operation < 512; operation++) {
 		const bool is_emulated = operation & 256;
 		const uint8_t opcode = operation & 255;
@@ -106,18 +110,35 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		// Ensure processor's next action is an opcode fetch.
 		processor.restart_operation_fetch();
 
-		// Randomise processor state.
+		// Randomise most of the processor state...
 		using Register = CPU::MOS6502Esque::Register;
+		processor.set_value_of_register(Register::A, rand() >> 8);
+		processor.set_value_of_register(Register::Flags, rand() >> 8);
+		processor.set_value_of_register(Register::X, rand() >> 8);
+		processor.set_value_of_register(Register::Y, rand() >> 8);
+		processor.set_value_of_register(Register::ProgramCounter, rand() >> 8);
+		processor.set_value_of_register(Register::StackPointer, rand() >> 8);
+		processor.set_value_of_register(Register::DataBank, rand() >> 8);
+		processor.set_value_of_register(Register::ProgramBank, rand() >> 8);
+		processor.set_value_of_register(Register::Direct, rand() >> 8);
+
+		// ... except for emulation mode, which is a given.
+		// And is set last to ensure proper internal state is applied.
 		processor.set_value_of_register(Register::EmulationFlag, is_emulated);
 
 		// Establish the opcode.
 		handler.setup(processor, opcode);
 
+		// TODO: dump current state.
+
 		// Run to the second opcode fetch.
 		handler.opcodes_remaining = 2;
 		try {
 			processor.run_for(Cycles(100));
 		} catch (const StopException &) {}
+
+		// TODO: dump initial and final memory contents, and final state.
+		printf("");
 	}
 }
 

From 0c24a27ba61bea59613349efa20bcd03988a3971 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 18 Jun 2022 21:32:50 -0400
Subject: [PATCH 05/31] Completely prints tests.

---
 .../65816ComparativeTests.mm                  | 131 ++++++++++++++----
 1 file changed, 105 insertions(+), 26 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index 605a4db8d..9ee2b3644 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -83,6 +83,31 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 	std::vector<Cycle> cycles;
 };
 
+template <typename Processor> void print_registers(const Processor &processor, int pc_offset) {
+	using Register = CPU::MOS6502Esque::Register;
+	printf("\"pc\": %d, ", (processor.get_value_of_register(Register::ProgramCounter) + pc_offset) & 65535);
+	printf("\"s\": %d, ", processor.get_value_of_register(Register::StackPointer));
+	printf("\"p\": %d, ", processor.get_value_of_register(Register::Flags));
+	printf("\"a\": %d, ", processor.get_value_of_register(Register::A));
+	printf("\"x\": %d, ", processor.get_value_of_register(Register::X));
+	printf("\"y\": %d, ", processor.get_value_of_register(Register::Y));
+	printf("\"dbr\": %d, ", processor.get_value_of_register(Register::DataBank));
+	printf("\"d\": %d, ", processor.get_value_of_register(Register::Direct));
+	printf("\"pbr\": %d, ", processor.get_value_of_register(Register::ProgramBank));
+	printf("\"e\": %d, ", processor.get_value_of_register(Register::EmulationFlag));
+}
+
+void print_ram(const std::unordered_map<uint32_t, uint8_t> &data) {
+	printf("\"ram\": [");
+	bool is_first = true;
+	for(const auto &pair: data) {
+		if(!is_first) printf(", ");
+		is_first = false;
+		printf("[%d, %d]", pair.first, pair.second);
+	}
+	printf("]");
+}
+
 }
 
 // MARK: - New test generator.
@@ -107,38 +132,92 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		const bool is_emulated = operation & 256;
 		const uint8_t opcode = operation & 255;
 
-		// Ensure processor's next action is an opcode fetch.
-		processor.restart_operation_fetch();
+		for(int test = 0; test < 1; test++) {
+			// Ensure processor's next action is an opcode fetch.
+			processor.restart_operation_fetch();
 
-		// Randomise most of the processor state...
-		using Register = CPU::MOS6502Esque::Register;
-		processor.set_value_of_register(Register::A, rand() >> 8);
-		processor.set_value_of_register(Register::Flags, rand() >> 8);
-		processor.set_value_of_register(Register::X, rand() >> 8);
-		processor.set_value_of_register(Register::Y, rand() >> 8);
-		processor.set_value_of_register(Register::ProgramCounter, rand() >> 8);
-		processor.set_value_of_register(Register::StackPointer, rand() >> 8);
-		processor.set_value_of_register(Register::DataBank, rand() >> 8);
-		processor.set_value_of_register(Register::ProgramBank, rand() >> 8);
-		processor.set_value_of_register(Register::Direct, rand() >> 8);
+			// Randomise most of the processor state...
+			using Register = CPU::MOS6502Esque::Register;
+			processor.set_value_of_register(Register::A, rand() >> 8);
+			processor.set_value_of_register(Register::Flags, rand() >> 8);
+			processor.set_value_of_register(Register::X, rand() >> 8);
+			processor.set_value_of_register(Register::Y, rand() >> 8);
+			processor.set_value_of_register(Register::ProgramCounter, rand() >> 8);
+			processor.set_value_of_register(Register::StackPointer, rand() >> 8);
+			processor.set_value_of_register(Register::DataBank, rand() >> 8);
+			processor.set_value_of_register(Register::ProgramBank, rand() >> 8);
+			processor.set_value_of_register(Register::Direct, rand() >> 8);
 
-		// ... except for emulation mode, which is a given.
-		// And is set last to ensure proper internal state is applied.
-		processor.set_value_of_register(Register::EmulationFlag, is_emulated);
+			// ... except for emulation mode, which is a given.
+			// And is set last to ensure proper internal state is applied.
+			processor.set_value_of_register(Register::EmulationFlag, is_emulated);
 
-		// Establish the opcode.
-		handler.setup(processor, opcode);
+			// Establish the opcode.
+			handler.setup(processor, opcode);
 
-		// TODO: dump current state.
+			// Dump initial state.
+			printf("{ \"name\": \"%02x %c %d\", ", opcode, is_emulated ? 'e' : 'n', test + 1);
+			printf("\"initial\": {");
+			print_registers(processor, 0);
 
-		// Run to the second opcode fetch.
-		handler.opcodes_remaining = 2;
-		try {
-			processor.run_for(Cycles(100));
-		} catch (const StopException &) {}
+			// Run to the second opcode fetch.
+			handler.opcodes_remaining = 2;
+			try {
+				processor.run_for(Cycles(100));
+			} catch (const StopException &) {}
 
-		// TODO: dump initial and final memory contents, and final state.
-		printf("");
+			// Dump all inventions as initial memory state.
+			print_ram(handler.inventions);
+
+			// Dump final state.
+			printf("}, \"final\": {");
+			print_registers(processor, -1);
+			print_ram(handler.ram);
+			printf("}, ");
+
+			// Append cycles.
+			printf("\"cycles\": [");
+
+			bool is_first = true;
+			for(const auto &cycle: handler.cycles) {
+				if(!is_first) printf(",");
+				is_first = false;
+
+				bool vda = false;
+				bool vpa = false;
+				bool vpb = false;
+				bool read = false;
+				bool wait = false;
+				using BusOperation = CPU::MOS6502Esque::BusOperation;
+				switch(cycle.operation) {
+					case BusOperation::Read:					read = vda = true;			break;
+					case BusOperation::ReadOpcode:				read = vda = vpa = true;	break;
+					case BusOperation::ReadProgram:				read = vpa = true;			break;
+					case BusOperation::ReadVector:				read = vpb = true;			break;
+					case BusOperation::InternalOperationRead:	read = true;				break;
+
+					case BusOperation::Write:					vda = true;					break;
+					case BusOperation::InternalOperationWrite:								break;
+
+					case BusOperation::None:
+					case BusOperation::Ready:					wait = true;				break;
+
+					default:
+						assert(false);
+				}
+
+				printf("[%d, %d, %c%c%c%c]",
+					cycle.address,
+					cycle.value,
+					vda ? 'd' : '-',
+					vpa ? 'p' : '-',
+					vpb ? 'v' : '-',
+					wait ? '-' : (read ? 'r' : 'w'));
+			}
+
+			// Terminate object.
+			printf("]},\n");
+		}
 	}
 }
 

From 15ac2c3e5a5f979120f860e636b94e7a2e66c59f Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 18 Jun 2022 22:00:50 -0400
Subject: [PATCH 06/31] Output to files, at volume, with extended bus flags.

---
 .../65816ComparativeTests.mm                  | 131 +++++++++++-------
 1 file changed, 81 insertions(+), 50 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index 9ee2b3644..6fc80612c 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -28,6 +28,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		cycle.address = address;
 		cycle.operation = operation;
 		cycle.value = 0xff;
+		cycle.extended_bus = processor.get_extended_bus_output();
 
 		// Perform the operation, and fill in the cycle's value.
 		using BusOperation = CPU::MOS6502Esque::BusOperation;
@@ -61,7 +62,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		return Cycles(1);
 	}
 
-	template <typename Processor> void setup(Processor &processor, uint8_t opcode) {
+	void setup(uint8_t opcode) {
 		ram.clear();
 		inventions.clear();
 		cycles.clear();
@@ -79,33 +80,42 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		CPU::MOS6502Esque::BusOperation operation;
 		uint32_t address;
 		uint8_t value;
+		int extended_bus;
 	};
 	std::vector<Cycle> cycles;
+
+	CPU::WDC65816::Processor<BusHandler, false> processor;
+
+	BusHandler() : processor(*this) {
+		// Never run the official reset procedure.
+		processor.set_power_on(false);
+
+	}
 };
 
-template <typename Processor> void print_registers(const Processor &processor, int pc_offset) {
+template <typename Processor> void print_registers(FILE *file, const Processor &processor, int pc_offset) {
 	using Register = CPU::MOS6502Esque::Register;
-	printf("\"pc\": %d, ", (processor.get_value_of_register(Register::ProgramCounter) + pc_offset) & 65535);
-	printf("\"s\": %d, ", processor.get_value_of_register(Register::StackPointer));
-	printf("\"p\": %d, ", processor.get_value_of_register(Register::Flags));
-	printf("\"a\": %d, ", processor.get_value_of_register(Register::A));
-	printf("\"x\": %d, ", processor.get_value_of_register(Register::X));
-	printf("\"y\": %d, ", processor.get_value_of_register(Register::Y));
-	printf("\"dbr\": %d, ", processor.get_value_of_register(Register::DataBank));
-	printf("\"d\": %d, ", processor.get_value_of_register(Register::Direct));
-	printf("\"pbr\": %d, ", processor.get_value_of_register(Register::ProgramBank));
-	printf("\"e\": %d, ", processor.get_value_of_register(Register::EmulationFlag));
+	fprintf(file, "\"pc\": %d, ", (processor.get_value_of_register(Register::ProgramCounter) + pc_offset) & 65535);
+	fprintf(file, "\"s\": %d, ", processor.get_value_of_register(Register::StackPointer));
+	fprintf(file, "\"p\": %d, ", processor.get_value_of_register(Register::Flags));
+	fprintf(file, "\"a\": %d, ", processor.get_value_of_register(Register::A));
+	fprintf(file, "\"x\": %d, ", processor.get_value_of_register(Register::X));
+	fprintf(file, "\"y\": %d, ", processor.get_value_of_register(Register::Y));
+	fprintf(file, "\"dbr\": %d, ", processor.get_value_of_register(Register::DataBank));
+	fprintf(file, "\"d\": %d, ", processor.get_value_of_register(Register::Direct));
+	fprintf(file, "\"pbr\": %d, ", processor.get_value_of_register(Register::ProgramBank));
+	fprintf(file, "\"e\": %d, ", processor.get_value_of_register(Register::EmulationFlag));
 }
 
-void print_ram(const std::unordered_map<uint32_t, uint8_t> &data) {
-	printf("\"ram\": [");
+void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) {
+	fprintf(file, "\"ram\": [");
 	bool is_first = true;
 	for(const auto &pair: data) {
-		if(!is_first) printf(", ");
+		if(!is_first) fprintf(file, ", ");
 		is_first = false;
-		printf("[%d, %d]", pair.first, pair.second);
+		fprintf(file, "[%d, %d]", pair.first, pair.second);
 	}
-	printf("]");
+	fprintf(file, "]");
 }
 
 }
@@ -119,69 +129,76 @@ void print_ram(const std::unordered_map<uint32_t, uint8_t> &data) {
 
 - (void)generate {
 	BusHandler handler;
-	CPU::WDC65816::Processor<BusHandler, false> processor(handler);
-
-	// Never run the official reset procedure.
-	processor.set_power_on(false);
 
 	// Make tests repeatable, at least for any given instance of
 	// the runtime.
 	srand(65816);
 
+	NSString *const tempDir = NSTemporaryDirectory();
+	NSLog(@"Outputting to %@", tempDir);
+
 	for(int operation = 0; operation < 512; operation++) {
 		const bool is_emulated = operation & 256;
 		const uint8_t opcode = operation & 255;
 
-		for(int test = 0; test < 1; test++) {
+		NSString *const targetName = [NSString stringWithFormat:@"%@%02x.%c.json", tempDir, opcode, is_emulated ? 'e' : 'n'];
+		FILE *const target = fopen(targetName.UTF8String, "wt");
+
+		bool is_first_test = true;
+		fprintf(target, "[");
+		for(int test = 0; test < 10'000; test++) {
+			if(!is_first_test) fprintf(target, ",\n");
+			is_first_test = false;
+
 			// Ensure processor's next action is an opcode fetch.
-			processor.restart_operation_fetch();
+			handler.processor.restart_operation_fetch();
 
 			// Randomise most of the processor state...
 			using Register = CPU::MOS6502Esque::Register;
-			processor.set_value_of_register(Register::A, rand() >> 8);
-			processor.set_value_of_register(Register::Flags, rand() >> 8);
-			processor.set_value_of_register(Register::X, rand() >> 8);
-			processor.set_value_of_register(Register::Y, rand() >> 8);
-			processor.set_value_of_register(Register::ProgramCounter, rand() >> 8);
-			processor.set_value_of_register(Register::StackPointer, rand() >> 8);
-			processor.set_value_of_register(Register::DataBank, rand() >> 8);
-			processor.set_value_of_register(Register::ProgramBank, rand() >> 8);
-			processor.set_value_of_register(Register::Direct, rand() >> 8);
+			handler.processor.set_value_of_register(Register::A, rand() >> 8);
+			handler.processor.set_value_of_register(Register::Flags, rand() >> 8);
+			handler.processor.set_value_of_register(Register::X, rand() >> 8);
+			handler.processor.set_value_of_register(Register::Y, rand() >> 8);
+			handler.processor.set_value_of_register(Register::ProgramCounter, rand() >> 8);
+			handler.processor.set_value_of_register(Register::StackPointer, rand() >> 8);
+			handler.processor.set_value_of_register(Register::DataBank, rand() >> 8);
+			handler.processor.set_value_of_register(Register::ProgramBank, rand() >> 8);
+			handler.processor.set_value_of_register(Register::Direct, rand() >> 8);
 
 			// ... except for emulation mode, which is a given.
 			// And is set last to ensure proper internal state is applied.
-			processor.set_value_of_register(Register::EmulationFlag, is_emulated);
+			handler.processor.set_value_of_register(Register::EmulationFlag, is_emulated);
 
 			// Establish the opcode.
-			handler.setup(processor, opcode);
+			handler.setup(opcode);
 
 			// Dump initial state.
-			printf("{ \"name\": \"%02x %c %d\", ", opcode, is_emulated ? 'e' : 'n', test + 1);
-			printf("\"initial\": {");
-			print_registers(processor, 0);
+			fprintf(target, "{ \"name\": \"%02x %c %d\", ", opcode, is_emulated ? 'e' : 'n', test + 1);
+			fprintf(target, "\"initial\": {");
+			print_registers(target, handler.processor, 0);
 
 			// Run to the second opcode fetch.
 			handler.opcodes_remaining = 2;
 			try {
-				processor.run_for(Cycles(100));
+				handler.processor.run_for(Cycles(100));
 			} catch (const StopException &) {}
 
 			// Dump all inventions as initial memory state.
-			print_ram(handler.inventions);
+			print_ram(target, handler.inventions);
 
 			// Dump final state.
-			printf("}, \"final\": {");
-			print_registers(processor, -1);
-			print_ram(handler.ram);
-			printf("}, ");
+			fprintf(target, "}, \"final\": {");
+			print_registers(target, handler.processor, -1);
+			print_ram(target, handler.ram);
+			fprintf(target, "}, ");
 
 			// Append cycles.
-			printf("\"cycles\": [");
+			fprintf(target, "\"cycles\": [");
 
-			bool is_first = true;
+			bool is_first_cycle = true;
 			for(const auto &cycle: handler.cycles) {
-				if(!is_first) printf(",");
-				is_first = false;
+				if(!is_first_cycle) fprintf(target, ",");
+				is_first_cycle = false;
 
 				bool vda = false;
 				bool vpa = false;
@@ -206,18 +223,32 @@ void print_ram(const std::unordered_map<uint32_t, uint8_t> &data) {
 						assert(false);
 				}
 
-				printf("[%d, %d, %c%c%c%c]",
+				using ExtendedBusOutput = CPU::WDC65816::ExtendedBusOutput;
+				const bool emulation = cycle.extended_bus & ExtendedBusOutput::Emulation;
+				const bool memory_size = cycle.extended_bus & ExtendedBusOutput::MemorySize;
+				const bool index_size = cycle.extended_bus & ExtendedBusOutput::IndexSize;
+				const bool memory_lock = cycle.extended_bus & ExtendedBusOutput::MemoryLock;
+
+				fprintf(target, "[%d, %d, \"%c%c%c%c%c%c%c%c\"]",
 					cycle.address,
 					cycle.value,
 					vda ? 'd' : '-',
 					vpa ? 'p' : '-',
 					vpb ? 'v' : '-',
-					wait ? '-' : (read ? 'r' : 'w'));
+					wait ? '-' : (read ? 'r' : 'w'),
+					wait ? '-' : (emulation ? 'e' : '-'),
+					wait ? '-' : (memory_size ? 'm' : '-'),
+					wait ? '-' : (index_size ? 'i' : '-'),
+					wait ? '-' : (memory_lock ? 'l' : '-')
+				);
 			}
 
 			// Terminate object.
-			printf("]},\n");
+			fprintf(target, "]}");
 		}
+
+		fprintf(target, "]");
+		fclose(target);
 	}
 }
 

From ab0c2904899aef5769684aec58646cee0cfb8fff Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sun, 19 Jun 2022 06:58:23 -0400
Subject: [PATCH 07/31] Use 'x' instead of 'i'.

---
 OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index 6fc80612c..e134cdc1a 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -238,7 +238,7 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) {
 					wait ? '-' : (read ? 'r' : 'w'),
 					wait ? '-' : (emulation ? 'e' : '-'),
 					wait ? '-' : (memory_size ? 'm' : '-'),
-					wait ? '-' : (index_size ? 'i' : '-'),
+					wait ? '-' : (index_size ? 'x' : '-'),
 					wait ? '-' : (memory_lock ? 'l' : '-')
 				);
 			}

From ec98736bd7993e388707a38a16bcf6e645d24d11 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Tue, 21 Jun 2022 11:41:05 -0400
Subject: [PATCH 08/31] Ensure IO cycles don't produce an address of (PC+1).

---
 .../Implementation/65816Implementation.hpp    |  4 +
 .../65816/Implementation/65816Storage.cpp     | 94 +++++++++----------
 .../65816/Implementation/65816Storage.hpp     |  2 +
 3 files changed, 53 insertions(+), 47 deletions(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index 71bf18f2a..4d7ccbe61 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -99,6 +99,10 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 					perform_bus(registers_.pc | registers_.program_bank, &bus_throwaway_, MOS6502Esque::InternalOperationRead);
 				break;
 
+				case CycleFetchPreviousPCThrowaway:
+					perform_bus(((registers_.pc - 1) & 0xffff) | registers_.program_bank, &bus_throwaway_, MOS6502Esque::InternalOperationRead);
+				break;
+
 				//
 				// Data fetches and stores.
 				//
diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp
index 2978cb2b7..3ebdf76ea 100644
--- a/Processors/65816/Implementation/65816Storage.cpp
+++ b/Processors/65816/Implementation/65816Storage.cpp
@@ -387,7 +387,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 8. Accumulator; A.
 	static void accumulator(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPCThrowaway);			// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		// TODO: seriously consider a-specific versions of all relevant operations;
 		// the cost of interpreting three things here is kind of silly.
@@ -416,10 +416,10 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 	// 10a. Direct; d.
 	// (That's zero page in 6502 terms)
 	static void direct(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchIncrementPC);		// DO.
+		target(CycleFetchIncrementPC);			// DO.
 
 		target(OperationConstructDirect);
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
 		read_write(type, is8bit, target);
 	}
@@ -430,7 +430,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirect);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);				// IO.
 
 		read_modify_write(is8bit, target);
 	}
@@ -440,9 +440,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirectIndexedIndirect);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		target(CycleFetchIncrementData);				// AAL.
 		target(CycleFetchData);							// AAH.
@@ -458,7 +458,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirect);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		target(CycleFetchIncrementData);				// AAL.
 		target(CycleFetchData);							// AAH.
@@ -473,7 +473,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirect);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		target(CycleFetchIncrementData);				// AAL.
 		target(CycleFetchData);							// AAH.
@@ -490,7 +490,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirect);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		target(CycleFetchIncrementData);				// AAL.
 		target(CycleFetchIncrementData);				// AAH.
@@ -506,7 +506,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirectLong);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		target(CycleFetchIncrementData);				// AAL.
 		target(CycleFetchIncrementData);				// AAH.
@@ -522,9 +522,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirectX);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		read_write(type, is8bit, target);
 	}
@@ -534,9 +534,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirectX);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		read_modify_write(is8bit, target);
 	}
@@ -546,9 +546,9 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirectY);
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
-		target(CycleFetchPCThrowaway);					// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		read_write(type, is8bit, target);
 	}
@@ -563,20 +563,20 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	static void immediate_rep_sep(AccessType, bool, const std::function<void(MicroOp)> &target) {
 		target(CycleFetchIncrementPC);				// IDL.
-		target(CycleFetchPCThrowaway);				// "Add 1 cycle for REP and SEP"
+		target(CycleFetchPreviousPCThrowaway);		// "Add 1 cycle for REP and SEP"
 		target(OperationPerform);
 	}
 
 	// 19a. Implied; i.
 	static void implied(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
 		target(OperationPerform);
 	}
 
 	// 19b. Implied; i; XBA.
 	static void implied_xba(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPCThrowaway);		// IO.
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
 		target(OperationPerform);
 	}
 
@@ -584,8 +584,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 	// 19d. Wait for interrupt.
 	static void stp_wai(AccessType, bool, const std::function<void(MicroOp)> &target) {
 		target(OperationPerform);			// Establishes the termination condition.
-		target(CycleFetchPCThrowaway);		// IO.
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
 		target(CycleRepeatingNone);			// This will first check whether the STP/WAI exit
 											// condition has occurred; if not then it'll issue
 											// either a BusOperation::None or ::Ready and then
@@ -594,34 +594,34 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 20. Relative; r.
 	static void relative(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchIncrementPC);	// Offset.
+		target(CycleFetchIncrementPC);		// Offset.
 
-		target(OperationPerform);		// The branch instructions will all skip one or three
-										// of the next cycles, depending on the effect of
-										// the jump. It'll also calculate the correct target
-										// address, placing it into the data buffer.
+		target(OperationPerform);			// The branch instructions will all skip one or three
+											// of the next cycles, depending on the effect of
+											// the jump. It'll also calculate the correct target
+											// address, placing it into the data buffer.
 
-		target(CycleFetchPCThrowaway);	// IO.
-		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
-		target(OperationCopyDataToPC);	// Install the address that was calculated above.
+		target(OperationCopyDataToPC);			// Install the address that was calculated above.
 	}
 
 	// 21. Relative long; rl.
 	static void relative_long(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchIncrementPC);		// Offset low.
-		target(CycleFetchIncrementPC);		// Offset high.
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchIncrementPC);			// Offset low.
+		target(CycleFetchIncrementPC);			// Offset high.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
-		target(OperationPerform);			// [BRL]
+		target(OperationPerform);				// [BRL]
 	}
 
 	// 22a. Stack; s, abort/irq/nmi/res.
 	//
 	// Combined here with reset, which is the same sequence but with a different stack access.
 	static void stack_exception_impl(AccessType, bool, const std::function<void(MicroOp)> &target, MicroOp stack_op) {
-		target(CycleFetchPCThrowaway);		// IO.
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
 
 		target(OperationPrepareException);	// Populates the data buffer; if the exception is a
 											// reset then switches to the reset tail program.
@@ -654,8 +654,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22b. Stack; s, PLx.
 	static void stack_pull(AccessType, bool is8bit, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPCThrowaway);	// IO.
-		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
 		if(!is8bit) target(CyclePull);	// REG low.
 		target(CyclePull);				// REG [high].
@@ -665,7 +665,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22c. Stack; s, PHx.
 	static void stack_push(AccessType, bool is8bit, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
 		target(OperationPerform);
 
@@ -689,7 +689,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);		// DO.
 
 		target(OperationConstructDirect);
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
 
 		target(CycleFetchIncrementData);	// AAL.
 		target(CycleFetchData);				// AAH.
@@ -701,7 +701,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 	static void stack_per(AccessType, bool, const std::function<void(MicroOp)> &target) {
 		target(CycleFetchIncrementPC);		// Offset low.
 		target(CycleFetchIncrementPC);		// Offset high.
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
 
 		target(OperationConstructPER);
 
@@ -711,8 +711,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22g. Stack; s, RTI.
 	static void stack_rti(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPCThrowaway);		// IO.
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
 
 		target(CyclePull);					// P.
 		target(CyclePull);					// New PCL.
@@ -724,8 +724,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22h. Stack; s, RTS.
 	static void stack_rts(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPCThrowaway);	// IO.
-		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
 		target(CyclePull);				// PCL.
 		target(CyclePull);				// PCH.
@@ -772,7 +772,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 	// 23. Stack Relative; d, s.
 	static void stack_relative(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) {
 		target(CycleFetchIncrementPC);	// SO.
-		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
 		target(OperationConstructStackRelative);
 		read_write(type, is8bit, target);
@@ -781,7 +781,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 	// 24. Stack Relative Indirect Indexed (d, s), y.
 	static void stack_relative_indexed_indirect(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) {
 		target(CycleFetchIncrementPC);		// SO.
-		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);		// IO.
 
 		target(OperationConstructStackRelative);
 		target(CycleFetchIncrementData);	// AAL.
diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp
index 01bfe57cf..5bece0f45 100644
--- a/Processors/65816/Implementation/65816Storage.hpp
+++ b/Processors/65816/Implementation/65816Storage.hpp
@@ -13,6 +13,8 @@ enum MicroOp: uint8_t {
 	CycleFetchPC,
 	/// Fetches a byte from the program counter without incrementing it, and throws it away.
 	CycleFetchPCThrowaway,
+	/// Fetches a byte from (PC - 1), and throws it away; useful for IO cycles that immediately follow incremented PCs.
+	CycleFetchPreviousPCThrowaway,
 	/// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address.
 	CycleFetchOpcode,
 

From 7dcfa9eb65e96c504089df3e33e8eb1fd49e180d Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Tue, 21 Jun 2022 14:33:06 -0400
Subject: [PATCH 09/31] 65816: improve decimal calculations, posted IO
 addresses, read/write during redundant read-modify-write cycle.

---
 .../Implementation/65816Implementation.hpp    | 71 ++++++++++++-------
 .../65816/Implementation/65816Storage.cpp     |  4 +-
 .../65816/Implementation/65816Storage.hpp     |  5 +-
 3 files changed, 49 insertions(+), 31 deletions(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index 4d7ccbe61..9f77567c6 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -115,6 +115,13 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 					read(data_address_, data_buffer_.next_input());
 				break;
 
+				case CycleStoreOrFetchDataThrowaway:
+					if(registers_.emulation_flag) {
+						perform_bus(data_address_, data_buffer_.preview_output(), MOS6502Esque::InternalOperationWrite);
+						break;
+					}
+
+					[[fallthrough]];
 				case CycleFetchDataThrowaway:
 					perform_bus(data_address_, &bus_throwaway_, MOS6502Esque::InternalOperationRead);
 				break;
@@ -141,10 +148,6 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 					write(data_address_, data_buffer_.next_output());
 				break;
 
-				case CycleStoreDataThrowaway:
-					perform_bus(data_address_, data_buffer_.preview_output(), MOS6502Esque::InternalOperationWrite);
-				break;
-
 				case CycleStoreIncrementData:
 					write(data_address_, data_buffer_.next_output());
 					increment_data_address();
@@ -906,35 +909,43 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 
 						case SBC:
 							if(registers_.flags.decimal) {
-								// I've yet to manage to find a rational way to map this to an ADC,
-								// hence the yucky repetition of code here.
 								const uint16_t a = registers_.a.full & registers_.m_masks[1];
-								unsigned int result = 0;
-								unsigned int borrow = registers_.flags.carry ^ 1;
-								const uint16_t decimal_result = uint16_t(a - data_buffer_.value - borrow);
+								const uint16_t decimal_result = uint16_t(a - data_buffer_.value - (1 ^ registers_.flags.carry));
+								data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1];
 
-#define nibble(mask, adjustment, carry)								\
-	result += (a & mask) - (data_buffer_.value & mask) - borrow;	\
-	if(result > mask) result -= adjustment;							\
-	borrow = (result > mask) ? carry : 0;							\
-	result &= (carry - 1);
+#define begin_nibble(mask)						\
+	result += (a & mask) + (data_buffer_.value & mask);
 
+#define end_nibble(adjustment, carry)			\
+	if(result < carry) result -= adjustment;	\
+	result &= (carry | (carry - 1));
+
+#define nibble(mask, adjustment, carry)			\
+	begin_nibble(mask);							\
+	end_nibble(adjustment, carry)
+
+								unsigned int result = registers_.flags.carry;
 								nibble(0x000f, 0x0006, 0x00010);
 								nibble(0x00f0, 0x0060, 0x00100);
 								nibble(0x0f00, 0x0600, 0x01000);
-								nibble(0xf000, 0x6000, 0x10000);
+
+								begin_nibble(0xf000);
+								registers_.flags.overflow = uint8_t((( (decimal_result ^ result) & (~decimal_result ^ result) ) >> (1 + registers_.m_shift))&0x40);
+								end_nibble(0x6000, 0x10000);
 
 #undef nibble
+#undef begin_nibble
+#undef end_nibble
 
-								registers_.flags.overflow = (( (decimal_result ^ a) & (~decimal_result ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40;
 								registers_.flags.set_nz(uint16_t(result), registers_.m_shift);
-								registers_.flags.carry = ((borrow >> 16)&1)^1;
+								registers_.flags.carry = (result >> (8 + registers_.m_shift))&1;
 								LDA(result);
 
 								break;
 							}
 
 							data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1];
+
 						[[fallthrough]];
 
 						case ADC: {
@@ -942,22 +953,28 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 							const uint16_t a = registers_.a.full & registers_.m_masks[1];
 
 							if(registers_.flags.decimal) {
-								uint16_t partials = 0;
+#define begin_nibble(mask)						\
+	result += (a & mask) + (data_buffer_.value & mask);
+
+#define end_nibble(limit, adjustment, carry)	\
+	if(result >= limit) result = ((result + adjustment) & (carry - 1)) + carry;
+
+#define nibble(mask, limit, adjustment, carry)	\
+	begin_nibble(mask);	\
+	end_nibble(limit, adjustment, carry)
+
 								result = registers_.flags.carry;
-
-#define nibble(mask, limit, adjustment, carry)			\
-	result += (a & mask) + (data_buffer_.value & mask);	\
-	partials += result & mask;							\
-	if(result >= limit) result = ((result + (adjustment)) & (carry - 1)) + carry;
-
 								nibble(0x000f, 0x000a, 0x0006, 0x00010);
 								nibble(0x00f0, 0x00a0, 0x0060, 0x00100);
 								nibble(0x0f00, 0x0a00, 0x0600, 0x01000);
-								nibble(0xf000, 0xa000, 0x6000, 0x10000);
+
+								begin_nibble(0xf000);
+								registers_.flags.overflow = (( (uint16_t(result) ^ registers_.a.full) & (uint16_t(result) ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40;
+								end_nibble(0xa000, 0x6000, 0x10000);
 
 #undef nibble
-
-								registers_.flags.overflow = (( (partials ^ registers_.a.full) & (partials ^ data_buffer_.value) )  >> (1 + registers_.m_shift))&0x40;
+#undef begin_nibble
+#undef end_nibble
 
 							} else {
 								result = int(a + data_buffer_.value + registers_.flags.carry);
diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp
index 3ebdf76ea..1ad7b3ec9 100644
--- a/Processors/65816/Implementation/65816Storage.cpp
+++ b/Processors/65816/Implementation/65816Storage.cpp
@@ -195,8 +195,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		if(!is8bit)	target(CycleFetchIncrementData);	// Data low.
 		target(CycleFetchData);							// Data [high].
 
-		if(!is8bit)	target(CycleFetchDataThrowaway);	// 16-bit: reread final byte of data.
-		else target(CycleStoreDataThrowaway);			// 8-bit rewrite final byte of data.
+		target(CycleStoreOrFetchDataThrowaway);			// Native mode: reread final byte of data.
+														// Emulated mode: rewrite final byte of data.
 
 		target(OperationPerform);						// Perform operation within the data buffer.
 
diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp
index 5bece0f45..d2ead050f 100644
--- a/Processors/65816/Implementation/65816Storage.hpp
+++ b/Processors/65816/Implementation/65816Storage.hpp
@@ -39,8 +39,9 @@ enum MicroOp: uint8_t {
 
 	/// Stores a byte from the data buffer.
 	CycleStoreData,
-	/// Stores the most recent byte placed into the data buffer without removing it.
-	CycleStoreDataThrowaway,
+	/// Emulated mode: stores the most recent byte placed into the data buffer without removing it;
+	/// Native mode: performs CycleFetchDataThrowaway.
+	CycleStoreOrFetchDataThrowaway,
 	/// Stores a byte to the data address from the data buffer and increments the data address.
 	CycleStoreIncrementData,
 	/// Stores a byte to the data address from the data buffer and decrements the data address.

From 2f684ee66d0e077f1b94723e496314a468374b46 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Tue, 21 Jun 2022 21:47:18 -0400
Subject: [PATCH 10/31] Use null for values that were never loaded.

---
 .../Clock SignalTests/65816ComparativeTests.mm   | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index e134cdc1a..342bf51d9 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -10,6 +10,7 @@
 
 #import <XCTest/XCTest.h>
 #include <array>
+#include <optional>
 #include <vector>
 #include <unordered_map>
 
@@ -27,7 +28,6 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		auto &cycle = cycles.emplace_back();
 		cycle.address = address;
 		cycle.operation = operation;
-		cycle.value = 0xff;
 		cycle.extended_bus = processor.get_extended_bus_output();
 
 		// Perform the operation, and fill in the cycle's value.
@@ -47,7 +47,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 					cycle.value = *value = ram_value->second;
 				} else {
 					cycle.value = *value = uint8_t(rand() >> 8);
-					inventions[address] = ram[address] = cycle.value;
+					inventions[address] = ram[address] = *cycle.value;
 				}
 			break;
 
@@ -79,7 +79,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 	struct Cycle {
 		CPU::MOS6502Esque::BusOperation operation;
 		uint32_t address;
-		uint8_t value;
+		std::optional<uint8_t> value;
 		int extended_bus;
 	};
 	std::vector<Cycle> cycles;
@@ -229,9 +229,13 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) {
 				const bool index_size = cycle.extended_bus & ExtendedBusOutput::IndexSize;
 				const bool memory_lock = cycle.extended_bus & ExtendedBusOutput::MemoryLock;
 
-				fprintf(target, "[%d, %d, \"%c%c%c%c%c%c%c%c\"]",
-					cycle.address,
-					cycle.value,
+				fprintf(target, "[%d, ", cycle.address);
+				if(cycle.value) {
+					fprintf(target, "%d, ", *cycle.value);
+				} else {
+					fprintf(target, "null, ");
+				}
+				fprintf(target, "\"%c%c%c%c%c%c%c%c\"]",
 					vda ? 'd' : '-',
 					vpa ? 'p' : '-',
 					vpb ? 'v' : '-',

From 76767110b7c49e78dd0b718f642b28a15bc635e6 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 22 Jun 2022 15:12:08 -0400
Subject: [PATCH 11/31] Fix overflow for 8-bit calculations; essentially a
 revert for ADC.

---
 .../Implementation/65816Implementation.hpp    | 46 ++++++-------------
 1 file changed, 15 insertions(+), 31 deletions(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index 9f77567c6..d5161e64a 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -910,33 +910,25 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 						case SBC:
 							if(registers_.flags.decimal) {
 								const uint16_t a = registers_.a.full & registers_.m_masks[1];
-								const uint16_t decimal_result = uint16_t(a - data_buffer_.value - (1 ^ registers_.flags.carry));
 								data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1];
 
-#define begin_nibble(mask)						\
-	result += (a & mask) + (data_buffer_.value & mask);
+								unsigned int result = registers_.flags.carry;
+								uint16_t partials = 0;
 
-#define end_nibble(adjustment, carry)			\
-	if(result < carry) result -= adjustment;	\
+#define nibble(mask, adjustment, carry)					\
+	result += (a & mask) + (data_buffer_.value & mask);	\
+	partials += result & mask;							\
+	if(result < carry) result -= adjustment;			\
 	result &= (carry | (carry - 1));
 
-#define nibble(mask, adjustment, carry)			\
-	begin_nibble(mask);							\
-	end_nibble(adjustment, carry)
-
-								unsigned int result = registers_.flags.carry;
 								nibble(0x000f, 0x0006, 0x00010);
 								nibble(0x00f0, 0x0060, 0x00100);
 								nibble(0x0f00, 0x0600, 0x01000);
-
-								begin_nibble(0xf000);
-								registers_.flags.overflow = uint8_t((( (decimal_result ^ result) & (~decimal_result ^ result) ) >> (1 + registers_.m_shift))&0x40);
-								end_nibble(0x6000, 0x10000);
+								nibble(0xf000, 0x6000, 0x10000);
 
 #undef nibble
-#undef begin_nibble
-#undef end_nibble
 
+								registers_.flags.overflow = (( (partials ^ registers_.a.full) & (partials ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40;
 								registers_.flags.set_nz(uint16_t(result), registers_.m_shift);
 								registers_.flags.carry = (result >> (8 + registers_.m_shift))&1;
 								LDA(result);
@@ -945,7 +937,6 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 							}
 
 							data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1];
-
 						[[fallthrough]];
 
 						case ADC: {
@@ -953,29 +944,22 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 							const uint16_t a = registers_.a.full & registers_.m_masks[1];
 
 							if(registers_.flags.decimal) {
-#define begin_nibble(mask)						\
-	result += (a & mask) + (data_buffer_.value & mask);
+								uint16_t partials = 0;
+								result = registers_.flags.carry;
 
-#define end_nibble(limit, adjustment, carry)	\
+#define nibble(mask, limit, adjustment, carry)									\
+	result += (a & mask) + (data_buffer_.value & mask);							\
+	partials += result & mask;													\
 	if(result >= limit) result = ((result + adjustment) & (carry - 1)) + carry;
 
-#define nibble(mask, limit, adjustment, carry)	\
-	begin_nibble(mask);	\
-	end_nibble(limit, adjustment, carry)
-
-								result = registers_.flags.carry;
 								nibble(0x000f, 0x000a, 0x0006, 0x00010);
 								nibble(0x00f0, 0x00a0, 0x0060, 0x00100);
 								nibble(0x0f00, 0x0a00, 0x0600, 0x01000);
-
-								begin_nibble(0xf000);
-								registers_.flags.overflow = (( (uint16_t(result) ^ registers_.a.full) & (uint16_t(result) ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40;
-								end_nibble(0xa000, 0x6000, 0x10000);
+								nibble(0xf000, 0xa000, 0x6000, 0x10000);
 
 #undef nibble
-#undef begin_nibble
-#undef end_nibble
 
+								registers_.flags.overflow = (( (partials ^ registers_.a.full) & (partials ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40;
 							} else {
 								result = int(a + data_buffer_.value + registers_.flags.carry);
 								registers_.flags.overflow = (( (uint16_t(result) ^ registers_.a.full) & (uint16_t(result) ^ data_buffer_.value) ) >> (1 + registers_.m_shift))&0x40;

From 944e5ebbfa2b144027d63f56b9aabce702cadd9b Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 22 Jun 2022 15:28:11 -0400
Subject: [PATCH 12/31] Take another run at IO addresses.

---
 .../65816/Implementation/65816Storage.cpp     | 66 +++++++++----------
 1 file changed, 33 insertions(+), 33 deletions(-)

diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp
index 1ad7b3ec9..677fd496b 100644
--- a/Processors/65816/Implementation/65816Storage.cpp
+++ b/Processors/65816/Implementation/65816Storage.cpp
@@ -387,7 +387,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 8. Accumulator; A.
 	static void accumulator(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPreviousPCThrowaway);			// IO.
+		target(CycleFetchPCThrowaway);			// IO.
 
 		// TODO: seriously consider a-specific versions of all relevant operations;
 		// the cost of interpreting three things here is kind of silly.
@@ -430,7 +430,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchIncrementPC);					// DO.
 
 		target(OperationConstructDirect);
-		target(CycleFetchPreviousPCThrowaway);				// IO.
+		target(CycleFetchPreviousPCThrowaway);			// IO.
 
 		read_modify_write(is8bit, target);
 	}
@@ -569,14 +569,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 19a. Implied; i.
 	static void implied(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPCThrowaway);		// IO.
 		target(OperationPerform);
 	}
 
 	// 19b. Implied; i; XBA.
 	static void implied_xba(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPreviousPCThrowaway);		// IO.
-		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPCThrowaway);		// IO.
 		target(OperationPerform);
 	}
 
@@ -584,8 +584,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 	// 19d. Wait for interrupt.
 	static void stp_wai(AccessType, bool, const std::function<void(MicroOp)> &target) {
 		target(OperationPerform);			// Establishes the termination condition.
-		target(CycleFetchPreviousPCThrowaway);		// IO.
-		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPCThrowaway);		// IO.
 		target(CycleRepeatingNone);			// This will first check whether the STP/WAI exit
 											// condition has occurred; if not then it'll issue
 											// either a BusOperation::None or ::Ready and then
@@ -654,8 +654,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22b. Stack; s, PLx.
 	static void stack_pull(AccessType, bool is8bit, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPreviousPCThrowaway);	// IO.
-		target(CycleFetchPreviousPCThrowaway);	// IO.
+		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPCThrowaway);	// IO.
 
 		if(!is8bit) target(CyclePull);	// REG low.
 		target(CyclePull);				// REG [high].
@@ -665,7 +665,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22c. Stack; s, PHx.
 	static void stack_push(AccessType, bool is8bit, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPreviousPCThrowaway);	// IO.
+		target(CycleFetchPCThrowaway);	// IO.
 
 		target(OperationPerform);
 
@@ -686,33 +686,33 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22e. Stack; s, PEI.
 	static void stack_pei(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchIncrementPC);		// DO.
+		target(CycleFetchIncrementPC);			// DO.
 
 		target(OperationConstructDirect);
-		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
-		target(CycleFetchIncrementData);	// AAL.
-		target(CycleFetchData);				// AAH.
-		target(CyclePush);					// AAH.
-		target(CyclePush);					// AAL.
+		target(CycleFetchIncrementData);		// AAL.
+		target(CycleFetchData);					// AAH.
+		target(CyclePush);						// AAH.
+		target(CyclePush);						// AAL.
 	}
 
 	// 22f. Stack; s, PER.
 	static void stack_per(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchIncrementPC);		// Offset low.
-		target(CycleFetchIncrementPC);		// Offset high.
-		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchIncrementPC);			// Offset low.
+		target(CycleFetchIncrementPC);			// Offset high.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
 		target(OperationConstructPER);
 
-		target(CyclePush);					// AAH.
-		target(CyclePush);					// AAL.
+		target(CyclePush);						// AAH.
+		target(CyclePush);						// AAL.
 	}
 
 	// 22g. Stack; s, RTI.
 	static void stack_rti(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPreviousPCThrowaway);		// IO.
-		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchPCThrowaway);		// IO.
+		target(CycleFetchPCThrowaway);		// IO.
 
 		target(CyclePull);					// P.
 		target(CyclePull);					// New PCL.
@@ -724,8 +724,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22h. Stack; s, RTS.
 	static void stack_rts(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPreviousPCThrowaway);	// IO.
-		target(CycleFetchPreviousPCThrowaway);	// IO.
+		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPCThrowaway);	// IO.
 
 		target(CyclePull);				// PCL.
 		target(CyclePull);				// PCH.
@@ -736,8 +736,8 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22i. Stack; s, RTL.
 	static void stack_rtl(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchIncrementPC);	// IO.
-		target(CycleFetchIncrementPC);	// IO.
+		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPCThrowaway);	// IO.
 
 		target(CyclePull);				// New PCL.
 		target(CyclePull);				// New PCH.
@@ -771,7 +771,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 23. Stack Relative; d, s.
 	static void stack_relative(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchIncrementPC);	// SO.
+		target(CycleFetchIncrementPC);			// SO.
 		target(CycleFetchPreviousPCThrowaway);	// IO.
 
 		target(OperationConstructStackRelative);
@@ -780,13 +780,13 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 24. Stack Relative Indirect Indexed (d, s), y.
 	static void stack_relative_indexed_indirect(AccessType type, bool is8bit, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchIncrementPC);		// SO.
-		target(CycleFetchPreviousPCThrowaway);		// IO.
+		target(CycleFetchIncrementPC);			// SO.
+		target(CycleFetchPreviousPCThrowaway);	// IO.
 
 		target(OperationConstructStackRelative);
-		target(CycleFetchIncrementData);	// AAL.
-		target(CycleFetchData);				// AAH.
-		target(CycleFetchDataThrowaway);	// IO.
+		target(CycleFetchIncrementData);		// AAL.
+		target(CycleFetchData);					// AAH.
+		target(CycleFetchDataThrowaway);		// IO.
 
 		target(OperationConstructStackRelativeIndexedIndirect);
 		read_write(type, is8bit, target);

From a72dd96dc6e090f13fad4d442f9112e4fc6ba278 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 22 Jun 2022 15:31:30 -0400
Subject: [PATCH 13/31] Page boundary crossing is free outside of emulation
 mode.

---
 Processors/65816/Implementation/65816Implementation.hpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index d5161e64a..a96354ed5 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -835,7 +835,10 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 		data_buffer_.value = uint32_t(registers_.pc + int8_t(instruction_buffer_.value));	\
 		data_buffer_.size = 2;																\
 																							\
-		if((registers_.pc & 0xff00) == (instruction_buffer_.value & 0xff00)) {				\
+		if(																					\
+			!registers_.emulation_flag ||													\
+			(registers_.pc & 0xff00) == (instruction_buffer_.value & 0xff00)				\
+		) {																					\
 			++next_op_;																		\
 		}																					\
 	}

From ecfd17a259758955f71862754286a0df986b4217 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 22 Jun 2022 15:55:34 -0400
Subject: [PATCH 14/31] Report a 1 in the stack pointer high byte when in
 emulation mode.

It has one internally, it just wasn't previously exposed via this method.
---
 Processors/65816/Implementation/65816Base.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Processors/65816/Implementation/65816Base.cpp b/Processors/65816/Implementation/65816Base.cpp
index f61b62248..5dff89928 100644
--- a/Processors/65816/Implementation/65816Base.cpp
+++ b/Processors/65816/Implementation/65816Base.cpp
@@ -14,7 +14,10 @@ uint16_t ProcessorBase::get_value_of_register(Register r) const {
 	switch (r) {
 		case Register::ProgramCounter:			return registers_.pc;
 		case Register::LastOperationAddress:	return last_operation_pc_;
-		case Register::StackPointer:			return registers_.s.full & (registers_.emulation_flag ? 0xff : 0xffff);
+		case Register::StackPointer:
+			return
+				(registers_.s.full & (registers_.emulation_flag ? 0xff : 0xffff)) |
+				(registers_.emulation_flag ? 0x100 : 0x000);
 		case Register::Flags:					return get_flags();
 		case Register::A:						return registers_.a.full;
 		case Register::X:						return registers_.x.full;

From 65140b341d92371a931b5a52572efe51ae58be82 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 22 Jun 2022 16:43:00 -0400
Subject: [PATCH 15/31] Simplify slightly, per new S reporting rule.

---
 OSBindings/Mac/Clock SignalTests/65816kromTests.swift | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816kromTests.swift b/OSBindings/Mac/Clock SignalTests/65816kromTests.swift
index 294e6bca2..154ea68d2 100644
--- a/OSBindings/Mac/Clock SignalTests/65816kromTests.swift	
+++ b/OSBindings/Mac/Clock SignalTests/65816kromTests.swift	
@@ -84,11 +84,7 @@ class Krom65816Tests: XCTestCase {
 				cpuState += String(format: "A:%04x ", machine.value(for: .A))
 				cpuState += String(format: "X:%04x ", machine.value(for: .X))
 				cpuState += String(format: "Y:%04x ", machine.value(for: .Y))
-				if emulationFlag {
-					cpuState += String(format: "S:01%02x ", machine.value(for: .stackPointer))
-				} else {
-					cpuState += String(format: "S:%04x ", machine.value(for: .stackPointer))
-				}
+				cpuState += String(format: "S:%04x ", machine.value(for: .stackPointer))
 				cpuState += String(format: "D:%04x ", machine.value(for: .direct))
 				cpuState += String(format: "DB:%02x ", machine.value(for: .dataBank))
 

From 3112376943861d1a768d7c726dea9210daad0d09 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 11:03:37 -0400
Subject: [PATCH 16/31] Don't include DBR in direct indexed indirect.

---
 Processors/65816/Implementation/65816Implementation.hpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index a96354ed5..c503c0a44 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -345,10 +345,10 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 				continue;
 
 				case OperationConstructDirectIndexedIndirect:
-					data_address_ = registers_.data_bank + ((
+					data_address_ = (
 						((registers_.direct + registers_.x.full + instruction_buffer_.value) & registers_.e_masks[1]) +
 						(registers_.direct & registers_.e_masks[0])
-					) & 0xffff);
+					) & 0xffff;
 					data_address_increment_mask_ = 0x00'ff'ff;
 
 					if(!(registers_.direct&0xff)) {

From 5a97c0923882811791d0fea33b8e50b73f2ab021 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 11:23:00 -0400
Subject: [PATCH 17/31] Flip internal presumption on the BRK flag.

---
 Processors/6502Esque/Implementation/LazyFlags.hpp       | 2 +-
 Processors/65816/Implementation/65816Implementation.hpp | 7 +------
 2 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/Processors/6502Esque/Implementation/LazyFlags.hpp b/Processors/6502Esque/Implementation/LazyFlags.hpp
index 756d7a779..2169d8c24 100644
--- a/Processors/6502Esque/Implementation/LazyFlags.hpp
+++ b/Processors/6502Esque/Implementation/LazyFlags.hpp
@@ -64,7 +64,7 @@ struct LazyFlags {
 	}
 
 	uint8_t get() const {
-		return carry | overflow | (inverse_interrupt ^ Flag::Interrupt) | (negative_result & 0x80) | (zero_result ? 0 : Flag::Zero) | Flag::Always | decimal;
+		return carry | overflow | (inverse_interrupt ^ Flag::Interrupt) | (negative_result & 0x80) | (zero_result ? 0 : Flag::Zero) | Flag::Always | Flag::Break | decimal;
 	}
 
 	LazyFlags() {
diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index c503c0a44..3a50f3d72 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -415,7 +415,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 				case OperationPrepareException:
 					data_buffer_.value = uint32_t((registers_.pc << 8) | get_flags());
 					if(registers_.emulation_flag) {
-						if(!exception_is_interrupt_) data_buffer_.value |= Flag::Break;
+						if(exception_is_interrupt_) data_buffer_.value &= ~Flag::Break;
 						data_buffer_.size = 3;
 						registers_.data_bank = 0;
 						++next_op_;
@@ -563,11 +563,6 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 						case PHP:
 							data_buffer_.value = get_flags();
 							data_buffer_.size = 1;
-
-							if(registers_.emulation_flag) {
-								// On the 6502, the break flag is set during a PHP.
-								data_buffer_.value |= Flag::Break;
-							}
 						break;
 
 						case NOP:						break;

From 2c12a7d968cfdd2f5b7f6fac49879a6dae6513ec Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 12:12:02 -0400
Subject: [PATCH 18/31] Make absolutely sure there's no address bit 24.

---
 Processors/65816/Implementation/65816Implementation.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index 3a50f3d72..86e561445 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -9,7 +9,7 @@
 template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler, uses_ready_line>::run_for(const Cycles cycles) {
 
 #define perform_bus(address, value, operation)	\
-	bus_address_ = address;						\
+	bus_address_ = address & 0xff'ffff;			\
 	bus_value_ = value;							\
 	bus_operation_ = operation
 

From 66775b2c4ee4a989d07dd07e59106ef73167731e Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 12:46:51 -0400
Subject: [PATCH 19/31] Always consume a second cycle in 16-bit mode.

---
 .../65816/Implementation/65816Implementation.hpp | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index 86e561445..bc4ae8bc8 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -300,8 +300,12 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 					data_address_ = instruction_buffer_.value + registers_.x.full + registers_.data_bank;
 					incorrect_data_address_ = ((data_address_ & 0x00ff) | (instruction_buffer_.value & 0xff00)) + registers_.data_bank;
 
-					// If the incorrect address isn't actually incorrect, skip its usage.
-					if(operation == OperationConstructAbsoluteXRead && data_address_ == incorrect_data_address_) {
+					// "Add 1 cycle for indexing across page boundaries, or write, or X=0"
+					// (i.e. don't add 1 cycle if x = 1 and this is a read, and a page boundary wasn't crossed)
+					if(
+						operation == OperationConstructAbsoluteXRead &&
+						data_address_ == incorrect_data_address_ &&
+						registers_.mx_flags[1]) {
 						++next_op_;
 					}
 					data_address_increment_mask_ = 0xff'ff'ff;
@@ -312,8 +316,12 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 					data_address_ = instruction_buffer_.value + registers_.y.full + registers_.data_bank;
 					incorrect_data_address_ = (data_address_ & 0xff) + (instruction_buffer_.value & 0xff00) + registers_.data_bank;
 
-					// If the incorrect address isn't actually incorrect, skip its usage.
-					if(operation == OperationConstructAbsoluteYRead && data_address_ == incorrect_data_address_) {
+					// "Add 1 cycle for indexing across page boundaries, or write, or X=0"
+					// (i.e. don't add 1 cycle if x = 1 and this is a read, and a page boundary wasn't crossed)
+					if(
+						operation == OperationConstructAbsoluteYRead &&
+						data_address_ == incorrect_data_address_ &&
+						registers_.mx_flags[1]) {
 						++next_op_;
 					}
 					data_address_increment_mask_ = 0xff'ff'ff;

From 380b5141fbd19f0236119a1b79374ea17b988e34 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 13:03:26 -0400
Subject: [PATCH 20/31] Be overt about conversion wanted here.

---
 Processors/65816/Implementation/65816Implementation.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index bc4ae8bc8..3ed50cb3a 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -423,7 +423,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 				case OperationPrepareException:
 					data_buffer_.value = uint32_t((registers_.pc << 8) | get_flags());
 					if(registers_.emulation_flag) {
-						if(exception_is_interrupt_) data_buffer_.value &= ~Flag::Break;
+						if(exception_is_interrupt_) data_buffer_.value &= ~uint32_t(Flag::Break);
 						data_buffer_.size = 3;
 						registers_.data_bank = 0;
 						++next_op_;

From da552abf75e18410fc06341fbd45482b9a547144 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 15:24:51 -0400
Subject: [PATCH 21/31] Fix BIT overflow flag.

---
 Processors/65816/Implementation/65816Implementation.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index 3ed50cb3a..ec7c73ba6 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -806,7 +806,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 							assert(data_buffer_.size == 2 - m_flag());
 							registers_.flags.set_n(uint16_t(data_buffer_.value), registers_.m_shift);
 							registers_.flags.set_z(uint16_t(data_buffer_.value & registers_.a.full), registers_.m_shift);
-							registers_.flags.overflow = data_buffer_.value & Flag::Overflow;
+							registers_.flags.overflow = (data_buffer_.value >> registers_.m_shift) & Flag::Overflow;
 						break;
 
 						case BITimm:

From a23b0f5122695eb3d836988035851c92f667c296 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 20:57:47 -0400
Subject: [PATCH 22/31] Map `STA (d), y` to correct calculator.

---
 Processors/65816/Implementation/65816Storage.cpp | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp
index 677fd496b..ddd9202ec 100644
--- a/Processors/65816/Implementation/65816Storage.cpp
+++ b/Processors/65816/Implementation/65816Storage.cpp
@@ -479,7 +479,11 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 		target(CycleFetchData);							// AAH.
 
 		target(OperationCopyDataToInstruction);
-		target(OperationConstructAbsoluteYRead);
+		if(type == AccessType::Read) {
+			target(OperationConstructAbsoluteYRead);	// Calculate data address, potentially skipping the next fetch.
+		} else {
+			target(OperationConstructAbsoluteY);		// Calculate data address.
+		}
 		target(CycleFetchIncorrectDataAddress);			// IO.
 
 		read_write(type, is8bit, target);

From 2e7afb13c79eae2f66468ba3e37ed5b2b5db9f28 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 21:03:40 -0400
Subject: [PATCH 23/31] Exit gracefully upon a STP or WAI.

---
 .../Mac/Clock SignalTests/65816ComparativeTests.mm  | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index 342bf51d9..b6cab8023 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -38,6 +38,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 				--opcodes_remaining;
 				if(!opcodes_remaining) {
 					cycles.pop_back();
+					pc_overshoot = -1;
 					throw StopException();
 				}
 			case BusOperation::Read:
@@ -55,7 +56,12 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 				cycle.value = ram[address] = *value;
 			break;
 
-			default: break;
+			case BusOperation::Ready:
+			case BusOperation::None:
+				throw StopException();
+			break;
+
+			default: assert(false);
 		}
 
 		// Don't occupy any bonus time.
@@ -66,6 +72,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		ram.clear();
 		inventions.clear();
 		cycles.clear();
+		pc_overshoot = 0;
 
 		using Register = CPU::MOS6502Esque::Register;
 		const uint32_t pc =
@@ -75,6 +82,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 	}
 
 	int opcodes_remaining = 0;
+	int pc_overshoot = 0;
 
 	struct Cycle {
 		CPU::MOS6502Esque::BusOperation operation;
@@ -89,7 +97,6 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 	BusHandler() : processor(*this) {
 		// Never run the official reset procedure.
 		processor.set_power_on(false);
-
 	}
 };
 
@@ -188,7 +195,7 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) {
 
 			// Dump final state.
 			fprintf(target, "}, \"final\": {");
-			print_registers(target, handler.processor, -1);
+			print_registers(target, handler.processor, handler.pc_overshoot);
 			print_ram(target, handler.ram);
 			fprintf(target, "}, ");
 

From 4ed3b21bf37a6b6d319bfcfb048ef020b4c819e3 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 23 Jun 2022 21:58:09 -0400
Subject: [PATCH 24/31] Decimal SBC tweak: negative partial results don't cause
 carry.

---
 .../65816/Implementation/65816Implementation.hpp   | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index ec7c73ba6..4e542236f 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -918,14 +918,22 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 								const uint16_t a = registers_.a.full & registers_.m_masks[1];
 								data_buffer_.value = ~data_buffer_.value & registers_.m_masks[1];
 
-								unsigned int result = registers_.flags.carry;
+								int result = registers_.flags.carry;
 								uint16_t partials = 0;
 
 #define nibble(mask, adjustment, carry)					\
 	result += (a & mask) + (data_buffer_.value & mask);	\
 	partials += result & mask;							\
-	if(result < carry) result -= adjustment;			\
-	result &= (carry | (carry - 1));
+	result -= ((result - carry) >> 16) & adjustment;	\
+	result &= (carry & ~(result >> 31)) | (carry - 1);
+
+	// i.e. add the next nibble to that in the accumulator, with carry, and
+	// store it to result. Keep a copy for the partials.
+	//
+	// If result is less than carry, subtract adjustment.
+	//
+	// Allow onward carry if the bit immediately above this nibble is 1, and
+	// the current partial result is positive.
 
 								nibble(0x000f, 0x0006, 0x00010);
 								nibble(0x00f0, 0x0060, 0x00100);

From 069a057a945ae0a16ed938c900ea90ac5fb35e8b Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 24 Jun 2022 07:26:07 -0400
Subject: [PATCH 25/31] Resolve assumption of arithmetic shifts.

---
 Processors/65816/Implementation/65816Implementation.hpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index 4e542236f..7ac380c08 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -925,7 +925,7 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 	result += (a & mask) + (data_buffer_.value & mask);	\
 	partials += result & mask;							\
 	result -= ((result - carry) >> 16) & adjustment;	\
-	result &= (carry & ~(result >> 31)) | (carry - 1);
+	result &= (carry & ~(result >> 1)) | (carry - 1);
 
 	// i.e. add the next nibble to that in the accumulator, with carry, and
 	// store it to result. Keep a copy for the partials.

From 6c638712f3a1eeacbaa2dfabd95cc259a4c67791 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 24 Jun 2022 07:39:58 -0400
Subject: [PATCH 26/31] Attempt to capture MVP and MVN in their entirety.

---
 .../Mac/Clock SignalTests/65816ComparativeTests.mm   | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index b6cab8023..ab29c21ff 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -35,12 +35,12 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		auto ram_value = ram.find(address);
 		switch(operation) {
 			case BusOperation::ReadOpcode:
-				--opcodes_remaining;
-				if(!opcodes_remaining) {
+				if(initial_pc && *initial_pc != address) {
 					cycles.pop_back();
 					pc_overshoot = -1;
 					throw StopException();
 				}
+				initial_pc = address;
 			case BusOperation::Read:
 			case BusOperation::ReadProgram:
 			case BusOperation::ReadVector:
@@ -61,6 +61,10 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 				throw StopException();
 			break;
 
+			case BusOperation::InternalOperationRead:
+			case BusOperation::InternalOperationWrite:
+			break;
+
 			default: assert(false);
 		}
 
@@ -73,6 +77,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		inventions.clear();
 		cycles.clear();
 		pc_overshoot = 0;
+		initial_pc = std::nullopt;
 
 		using Register = CPU::MOS6502Esque::Register;
 		const uint32_t pc =
@@ -81,8 +86,8 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		inventions[pc] = ram[pc] = opcode;
 	}
 
-	int opcodes_remaining = 0;
 	int pc_overshoot = 0;
+	std::optional<uint32_t> initial_pc;
 
 	struct Cycle {
 		CPU::MOS6502Esque::BusOperation operation;
@@ -185,7 +190,6 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) {
 			print_registers(target, handler.processor, 0);
 
 			// Run to the second opcode fetch.
-			handler.opcodes_remaining = 2;
 			try {
 				handler.processor.run_for(Cycles(100));
 			} catch (const StopException &) {}

From a442077eac8faafde5270ea3821a7fae5e3d3d24 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 24 Jun 2022 10:34:43 -0400
Subject: [PATCH 27/31] Allow repetition for MVN and MVP only.

---
 OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index ab29c21ff..dd0d82e25 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -35,7 +35,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		auto ram_value = ram.find(address);
 		switch(operation) {
 			case BusOperation::ReadOpcode:
-				if(initial_pc && *initial_pc != address) {
+				if(initial_pc && (*initial_pc != address || !allow_pc_repetition)) {
 					cycles.pop_back();
 					pc_overshoot = -1;
 					throw StopException();
@@ -79,6 +79,11 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 		pc_overshoot = 0;
 		initial_pc = std::nullopt;
 
+		// For MVP or MVN, keep tracking fetches via the same location.
+		// For other instructions, don't. That's to avoid endless loops
+		// for flow control that happens to jump back to where it began.
+		allow_pc_repetition = opcode == 0x54 || opcode == 0x44;
+
 		using Register = CPU::MOS6502Esque::Register;
 		const uint32_t pc =
 			processor.get_value_of_register(Register::ProgramCounter) |
@@ -88,6 +93,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 
 	int pc_overshoot = 0;
 	std::optional<uint32_t> initial_pc;
+	bool allow_pc_repetition = false;
 
 	struct Cycle {
 		CPU::MOS6502Esque::BusOperation operation;

From 1c1ce625a767203443e193c6d28f1bed8e8a8f45 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 24 Jun 2022 10:37:39 -0400
Subject: [PATCH 28/31] Vector reads signal VDA.

---
 OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm | 2 +-
 Processors/6502Esque/6502Esque.hpp                        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index dd0d82e25..f05b7013f 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -227,7 +227,7 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) {
 					case BusOperation::Read:					read = vda = true;			break;
 					case BusOperation::ReadOpcode:				read = vda = vpa = true;	break;
 					case BusOperation::ReadProgram:				read = vpa = true;			break;
-					case BusOperation::ReadVector:				read = vpb = true;			break;
+					case BusOperation::ReadVector:				read = vpb = vda = true;	break;
 					case BusOperation::InternalOperationRead:	read = true;				break;
 
 					case BusOperation::Write:					vda = true;					break;
diff --git a/Processors/6502Esque/6502Esque.hpp b/Processors/6502Esque/6502Esque.hpp
index 8f5c75589..84edc52d2 100644
--- a/Processors/6502Esque/6502Esque.hpp
+++ b/Processors/6502Esque/6502Esque.hpp
@@ -77,7 +77,7 @@ enum BusOperation {
 	/// 65816: indicates that a read was signalled with VPA.
 	ReadProgram,
 	/// 6502: never signalled.
-	/// 65816: indicates that a read was signalled with VPB.
+	/// 65816: indicates that a read was signalled with VPB and VDA.
 	ReadVector,
 	/// 6502: never signalled.
 	/// 65816: indicates that a read was signalled, but neither VDA nor VPA were active.

From ef5ac1442fb06927819f69b95175990b519d8805 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 24 Jun 2022 13:05:32 -0400
Subject: [PATCH 29/31] Don't invent an address for STP and WAI.

---
 .../65816ComparativeTests.mm                  | 20 +++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index f05b7013f..277c00c9b 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -26,7 +26,6 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 	Cycles perform_bus_operation(CPU::MOS6502Esque::BusOperation operation, uint32_t address, uint8_t *value) {
 		// Record the basics of the operation.
 		auto &cycle = cycles.emplace_back();
-		cycle.address = address;
 		cycle.operation = operation;
 		cycle.extended_bus = processor.get_extended_bus_output();
 
@@ -41,9 +40,12 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 					throw StopException();
 				}
 				initial_pc = address;
+				[[fallthrough]];
+
 			case BusOperation::Read:
 			case BusOperation::ReadProgram:
 			case BusOperation::ReadVector:
+				cycle.address = address;
 				if(ram_value != ram.end()) {
 					cycle.value = *value = ram_value->second;
 				} else {
@@ -53,6 +55,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 			break;
 
 			case BusOperation::Write:
+				cycle.address = address;
 				cycle.value = ram[address] = *value;
 			break;
 
@@ -61,8 +64,12 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 				throw StopException();
 			break;
 
-			case BusOperation::InternalOperationRead:
 			case BusOperation::InternalOperationWrite:
+				cycle.value = *value = ram_value->second;
+				[[fallthrough]];
+
+			case BusOperation::InternalOperationRead:
+				cycle.address = address;
 			break;
 
 			default: assert(false);
@@ -97,7 +104,7 @@ struct BusHandler: public CPU::MOS6502Esque::BusHandler<uint32_t>  {
 
 	struct Cycle {
 		CPU::MOS6502Esque::BusOperation operation;
-		uint32_t address;
+		std::optional<uint32_t> address;
 		std::optional<uint8_t> value;
 		int extended_bus;
 	};
@@ -246,7 +253,12 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) {
 				const bool index_size = cycle.extended_bus & ExtendedBusOutput::IndexSize;
 				const bool memory_lock = cycle.extended_bus & ExtendedBusOutput::MemoryLock;
 
-				fprintf(target, "[%d, ", cycle.address);
+				fprintf(target, "[");
+				if(cycle.address) {
+					fprintf(target, "%d, ", *cycle.address);
+				} else {
+					fprintf(target, "null, ");
+				}
 				if(cycle.value) {
 					fprintf(target, "%d, ", *cycle.value);
 				} else {

From 4467eb1c41ba447868da6e53e19634ddb8b6f1e4 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 24 Jun 2022 14:00:03 -0400
Subject: [PATCH 30/31] Ensure relevant throwaway stack reads use the previous
 stack address.

TODO: can CycleFetchPreviousThrowaway be used more widely?
---
 .../65816/Implementation/65816Implementation.hpp   |  4 ++++
 Processors/65816/Implementation/65816Storage.cpp   | 14 +++++++-------
 Processors/65816/Implementation/65816Storage.hpp   |  4 +++-
 3 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/Processors/65816/Implementation/65816Implementation.hpp b/Processors/65816/Implementation/65816Implementation.hpp
index 7ac380c08..ea07667fd 100644
--- a/Processors/65816/Implementation/65816Implementation.hpp
+++ b/Processors/65816/Implementation/65816Implementation.hpp
@@ -103,6 +103,10 @@ template <typename BusHandler, bool uses_ready_line> void Processor<BusHandler,
 					perform_bus(((registers_.pc - 1) & 0xffff) | registers_.program_bank, &bus_throwaway_, MOS6502Esque::InternalOperationRead);
 				break;
 
+				case CycleFetchPreviousThrowaway:
+					perform_bus(bus_address_, &bus_throwaway_, MOS6502Esque::InternalOperationRead);
+				break;
+
 				//
 				// Data fetches and stores.
 				//
diff --git a/Processors/65816/Implementation/65816Storage.cpp b/Processors/65816/Implementation/65816Storage.cpp
index ddd9202ec..5249be667 100644
--- a/Processors/65816/Implementation/65816Storage.cpp
+++ b/Processors/65816/Implementation/65816Storage.cpp
@@ -320,7 +320,7 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 		target(OperationCopyPBRToData);			// Copy PBR to the data register.
 		target(CyclePush);						// PBR.
-		target(CycleAccessStack);				// IO.
+		target(CycleFetchPreviousThrowaway);	// IO.
 
 		target(CycleFetchPC);					// New PBR.
 
@@ -728,14 +728,14 @@ struct CPU::WDC65816::ProcessorStorageConstructor {
 
 	// 22h. Stack; s, RTS.
 	static void stack_rts(AccessType, bool, const std::function<void(MicroOp)> &target) {
-		target(CycleFetchPCThrowaway);	// IO.
-		target(CycleFetchPCThrowaway);	// IO.
+		target(CycleFetchPCThrowaway);			// IO.
+		target(CycleFetchPCThrowaway);			// IO.
 
-		target(CyclePull);				// PCL.
-		target(CyclePull);				// PCH.
-		target(CycleAccessStack);		// IO.
+		target(CyclePull);						// PCL.
+		target(CyclePull);						// PCH.
+		target(CycleFetchPreviousThrowaway);	// IO.
 
-		target(OperationPerform);		// [RTS]
+		target(OperationPerform);				// [RTS]
 	}
 
 	// 22i. Stack; s, RTL.
diff --git a/Processors/65816/Implementation/65816Storage.hpp b/Processors/65816/Implementation/65816Storage.hpp
index d2ead050f..bac7fda8b 100644
--- a/Processors/65816/Implementation/65816Storage.hpp
+++ b/Processors/65816/Implementation/65816Storage.hpp
@@ -15,6 +15,8 @@ enum MicroOp: uint8_t {
 	CycleFetchPCThrowaway,
 	/// Fetches a byte from (PC - 1), and throws it away; useful for IO cycles that immediately follow incremented PCs.
 	CycleFetchPreviousPCThrowaway,
+	/// Fetches from whichever address was used in the last bus cycle, and throws away the result.
+	CycleFetchPreviousThrowaway,
 	/// The same as CycleFetchIncrementPC but indicates valid program address rather than valid data address.
 	CycleFetchOpcode,
 
@@ -274,7 +276,7 @@ struct ProcessorStorage {
 		// Registers.
 		RegisterPair16 a;
 		RegisterPair16 x, y;
-		RegisterPair16  s;
+		RegisterPair16 s;
 		uint16_t pc;
 
 		// Flags aplenty.

From f2c2027a8c50f5555570fbc1d2fc02f8a5cee6ee Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 24 Jun 2022 16:50:23 -0400
Subject: [PATCH 31/31] Disable test generation for commit.

---
 OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm
index 277c00c9b..8f1d49be4 100644
--- a/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
+++ b/OSBindings/Mac/Clock SignalTests/65816ComparativeTests.mm	
@@ -295,8 +295,8 @@ void print_ram(FILE *file, const std::unordered_map<uint32_t, uint8_t> &data) {
 @implementation WDC65816ComparativeTests
 
 // A generator for tests; not intended to be a permanent fixture.
-- (void)testGenerate {
-	[[[TestGenerator alloc] init] generate];
-}
+//- (void)testGenerate {
+//	[[[TestGenerator alloc] init] generate];
+//}
 
 @end