From e750866ab6c1b3c12bbf9a1154a848911ebc344f Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Tue, 2 Jul 2024 22:01:02 -0400
Subject: [PATCH 1/2] Remove phoney mid-BBR/BBS access.

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

diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp
index 6b6eb267a..685466f09 100644
--- a/Processors/6502/Implementation/6502Implementation.hpp
+++ b/Processors/6502/Implementation/6502Implementation.hpp
@@ -725,7 +725,7 @@ void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
 						} else {
 							scheduled_program_counter_ = operations_[size_t(OperationsSlot::DoNotBBRBBS)];
 						}
-					} break;
+					} continue;
 
 // MARK: - Transfers
 

From 2621bcc005df8fe283624253d789ac8f712c6258 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 5 Jul 2024 13:44:31 -0400
Subject: [PATCH 2/2] Switch to 5/6/7 BBS/BBR timing.

---
 .../Implementation/6502Implementation.hpp     | 15 +++++-
 .../6502/Implementation/6502Storage.cpp       | 51 ++++++++++++++-----
 .../6502/Implementation/6502Storage.hpp       |  4 +-
 3 files changed, 55 insertions(+), 15 deletions(-)

diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp
index 685466f09..223701bb8 100644
--- a/Processors/6502/Implementation/6502Implementation.hpp
+++ b/Processors/6502/Implementation/6502Implementation.hpp
@@ -708,12 +708,23 @@ void Processor<personality, T, uses_ready_line>::run_for(const Cycles cycles) {
 					continue;
 
 					case CycleFetchFromHalfUpdatedPC: {
-						uint16_t halfUpdatedPc = uint16_t(((pc_.halves.low + int8_t(operand_)) & 0xff) | (pc_.halves.high << 8));
-						throwaway_read(halfUpdatedPc);
+						uint16_t half_updated_pc = uint16_t(((pc_.halves.low + int8_t(operand_)) & 0xff) | (pc_.halves.high << 8));
+						throwaway_read(half_updated_pc);
 					} break;
 
+					case CycleFetchFromNextAddress:
+						throwaway_read(next_address_.full);
+					break;
+
 					case OperationAddSignedOperandToPC16:
+						next_address_ = pc_.full;
 						pc_.full = uint16_t(pc_.full + int8_t(operand_));
+
+						// Skip a step if 8-bit arithmetic would have been sufficient;
+						// in practise this operation is used only by BBS/BBR.
+						if(pc_.halves.high == next_address_.halves.high) {
+							++scheduled_program_counter_;
+						}
 					continue;
 
 					case OperationBBRBBS: {
diff --git a/Processors/6502/Implementation/6502Storage.cpp b/Processors/6502/Implementation/6502Storage.cpp
index 34d49afc7..a02178842 100644
--- a/Processors/6502/Implementation/6502Storage.cpp
+++ b/Processors/6502/Implementation/6502Storage.cpp
@@ -269,18 +269,35 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
 
 		/* 0x105: Do BBR or BBS. */
 		Program(
-			CycleFetchOperand,				// Fetch offset.
+			CycleFetchOperand,					// Fetch offset, and increment PC.
 			OperationIncrementPC,
-			CycleFetchFromHalfUpdatedPC,
-			OperationAddSignedOperandToPC16
+
+			OperationAddSignedOperandToPC16,	// Calculate target PC, leaving old PC in next_address_
+												// and possibly skipping the next instruction.
+
+			CycleFetchFromNextAddress,
+			CycleFetchFromNextAddress
 		),
+		// Six or seven cycles total are:
+		//	(1) operation;
+		//	(2) zero page address as operand;
+		//	(3) zero page address;
+		//	(4) duplicate of (3);
+		//	(5) further operand;
+		//	(6) read from next PC;
+		//	(7) repeat read from next PC if 16-bit arithmetic was required.
 
 		/* 0x106: Complete BBR or BBS without branching. */
 		Program(
-			CycleFetchOperand,
-			OperationIncrementPC,
-			CycleFetchFromHalfUpdatedPC
+			CycleFetchOperand,				// Fetch offset.
+			OperationIncrementPC
 		)
+		// Five cycles total are:
+		//	(1) operation;
+		//	(2) zero page address as operand;
+		//	(3) zero page address;
+		//	(4) duplicate of (3);
+		//	(5) further operand, which goes unused.
 	};
 
 	static_assert(sizeof(operations_6502) == sizeof(operations_));
@@ -408,15 +425,25 @@ ProcessorStorage::ProcessorStorage(Personality personality) {
 		// 0xc7, 0xcb, 0xcf, 0xd7, 0xdb, 0xdf,
 		// 0xe7, 0xef, 0xf7, 0xff
 		if(has_bbrbbsrmbsmb(personality)) {
-			// Add BBS and BBR. These take five cycles. My guessed breakdown is:
+			// Add BBS and BBR. These take five, six or seven cycles. First five:
 			// 1. read opcode
-			// 2. read operand
+			// 2. read first operand (i.e. zero-page address)
 			// 3. read zero page
-			// 4. read second operand
-			// 5. read from PC without top byte fixed yet
-			// ... with the caveat that (3) and (4) could be the other way around.
+			// 4. reread zero page	(presumably as a stall, to make a decision on the above)
+			// 5. read second operand (i.e. branch offset)
+			//
+			// ... and then, if the branch is taken:
+			//
+			// 6. read from where next instruction would have been
+			// 7. reread, if a further stall is necessary to cover up for a 16-bit address change.
 			for(int location = 0x0f; location <= 0xff; location += 0x10) {
-				Install(location, Program(OperationLoadAddressZeroPage, CycleFetchOperandFromAddress, OperationBBRBBS));
+				Install(location, Program(
+					OperationLoadAddressZeroPage,
+					CycleFetchOperandFromAddress,	// (cycle 3)
+					CycleFetchOperandFromAddress,	// (cycle 4)
+					OperationBBRBBS	// Branches to either OperationsSlot::DoBBRBBS, or to
+									// OperationSlot::DoNotBBRBBS, depending on data read.
+				));
 			}
 
 			// Add RMB and SMB.
diff --git a/Processors/6502/Implementation/6502Storage.hpp b/Processors/6502/Implementation/6502Storage.hpp
index 47f5c206c..fd3c9a78d 100644
--- a/Processors/6502/Implementation/6502Storage.hpp
+++ b/Processors/6502/Implementation/6502Storage.hpp
@@ -186,7 +186,9 @@ class ProcessorStorage {
 
 			CycleFetchFromHalfUpdatedPC,		// performs a throwaway read from (PC + (signed)operand).l combined with PC.h
 			CycleAddSignedOperandToPC,			// sets next_address to PC + (signed)operand. If the high byte of next_address differs from the PC, schedules a throwaway read from the half-updated PC. 65C02 specific: if the top two bytes are the same, proceeds directly to fetch-decode-execute, ignoring any pending interrupts.
-			OperationAddSignedOperandToPC16,	// adds (signed)operand into the PC
+			OperationAddSignedOperandToPC16,	// adds (signed)operand into the PC, leaving old PC in next_address_ and skipping a program step if there was no carry from low to high byte
+
+			CycleFetchFromNextAddress, 			// performs a throwaway fetch from next_address_
 
 			OperationSetFlagsFromOperand,			// sets all flags based on operand_
 			OperationSetOperandFromFlagsWithBRKSet,	// sets operand_ to the value of all flags, with the break flag set