From 7e319374b634f99868d970fbdae1ec970cc4ea66 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 11 May 2023 23:49:12 -0400
Subject: [PATCH] Consolidate StandardTiming into LineLayout.

---
 Components/9918/9918.hpp                      |  2 +
 Components/9918/Implementation/9918.cpp       | 44 +++++++++----------
 .../9918/Implementation/ClockConverter.hpp    | 30 ++++++-------
 3 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/Components/9918/9918.hpp b/Components/9918/9918.hpp
index d49a33fad..0789533a3 100644
--- a/Components/9918/9918.hpp
+++ b/Components/9918/9918.hpp
@@ -18,6 +18,8 @@ namespace TI::TMS {
 
 enum Personality {
 	TMS9918A,	// includes the 9928 and 9929; set TV standard and output device as desired.
+
+	// Yamaha extensions.
 	V9938,
 	V9958,
 
diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp
index bb59af32b..25595f79a 100644
--- a/Components/9918/Implementation/9918.cpp
+++ b/Components/9918/Implementation/9918.cpp
@@ -35,7 +35,7 @@ Base<personality>::Base() :
 
 		// "For a line interrupt, /INT is pulled low 608 mclks into the appropriate scanline relative to pixel 0.
 		// This is 3 mclks before the rising edge of /HSYNC which starts the next scanline."
-		mode_timing_.line_interrupt_position = (LineLayout<personality>::EndOfLeftBorder + 304) % Timing<personality>::CyclesPerLine;
+		mode_timing_.line_interrupt_position = (LineLayout<personality>::EndOfLeftBorder + 304) % LineLayout<personality>::CyclesPerLine;
 
 		// For a frame interrupt, /INT is pulled low 607 mclks into scanline 192 (of scanlines 0 through 261) relative to pixel 0.
 		// This is 4 mclks before the rising edge of /HSYNC which starts the next scanline.
@@ -197,7 +197,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
 		if(fetch_cycles_pool) {
 			// Determine how much writing to do; at the absolute most go to the end of this line.
 			const int fetch_cycles = std::min(
-				Timing<personality>::CyclesPerLine - this->fetch_pointer_.column,
+				LineLayout<personality>::CyclesPerLine - this->fetch_pointer_.column,
 				fetch_cycles_pool
 			);
 			const int end_column = this->fetch_pointer_.column + fetch_cycles;
@@ -319,7 +319,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
 			fetch_cycles_pool -= fetch_cycles;
 
 			// Check for end of line.
-			if(this->fetch_pointer_.column == Timing<personality>::CyclesPerLine) {
+			if(this->fetch_pointer_.column == LineLayout<personality>::CyclesPerLine) {
 				this->fetch_pointer_.column = 0;
 				this->fetch_pointer_.row = (this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines;
 
@@ -341,13 +341,13 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
 				this->minimum_access_column_ =
 					std::max(
 						0,
-						this->minimum_access_column_ - Timing<personality>::CyclesPerLine
+						this->minimum_access_column_ - LineLayout<personality>::CyclesPerLine
 					);
 				if constexpr (is_yamaha_vdp(personality)) {
 					Storage<personality>::minimum_command_column_ =
 						std::max(
 							0,
-							Storage<personality>::minimum_command_column_ - Timing<personality>::CyclesPerLine
+							Storage<personality>::minimum_command_column_ - LineLayout<personality>::CyclesPerLine
 						);
 				}
 
@@ -457,7 +457,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
 		if(output_cycles_pool) {
 			// Determine how much time has passed in the remainder of this line, and proceed.
 			const int target_output_cycles = std::min(
-				Timing<personality>::CyclesPerLine - this->output_pointer_.column,
+				LineLayout<personality>::CyclesPerLine - this->output_pointer_.column,
 				output_cycles_pool
 			);
 			int output_cycles_performed = 0;
@@ -522,8 +522,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
 				};
 
 				const auto right_blank = [&]() {
-					if(end_column == Timing<personality>::CyclesPerLine) {
-						output_blank(Timing<personality>::CyclesPerLine - LineLayout<personality>::EndOfRightBorder);
+					if(end_column == LineLayout<personality>::CyclesPerLine) {
+						output_blank(LineLayout<personality>::CyclesPerLine - LineLayout<personality>::EndOfRightBorder);
 					}
 				};
 
@@ -534,8 +534,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
 					) {
 						// Vertical sync.
 						// TODO: the Yamaha and Mega Drive both support interlaced video.
-						if(end_column == Timing<personality>::CyclesPerLine) {
-							output_sync(Timing<personality>::CyclesPerLine);
+						if(end_column == LineLayout<personality>::CyclesPerLine) {
+							output_sync(LineLayout<personality>::CyclesPerLine);
 						}
 					} else {
 						left_blank();
@@ -610,14 +610,14 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
 				// Advance time.
 				// -------------
 				this->output_pointer_.column = end_column;
-				if(end_column == Timing<personality>::CyclesPerLine) {
+				if(end_column == LineLayout<personality>::CyclesPerLine) {
 					// Advance line buffer.
 					this->advance(this->draw_line_buffer_);
 				}
 			}
 
 			output_cycles_pool -= target_output_cycles;
-			if(this->output_pointer_.column == Timing<personality>::CyclesPerLine) {
+			if(this->output_pointer_.column == LineLayout<personality>::CyclesPerLine) {
 				this->output_pointer_.column = 0;
 				this->output_pointer_.row = (this->output_pointer_.row + 1) % this->mode_timing_.total_lines;
 			}
@@ -685,7 +685,7 @@ void Base<personality>::write_vram(uint8_t value) {
 	// Enqueue the write to occur at the next available slot.
 	read_ahead_buffer_ = value;
 	queued_access_ = MemoryAccess::Write;
-	minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay;
+	minimum_access_column_ = fetch_pointer_.column + LineLayout<personality>::VRAMAccessDelay;
 }
 
 template <Personality personality>
@@ -1034,7 +1034,7 @@ void Base<personality>::write_register(uint8_t value) {
 			// A read request is enqueued upon setting the address; conversely a write
 			// won't be enqueued unless and until some actual data is supplied.
 			queued_access_ = MemoryAccess::Read;
-			minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay;
+			minimum_access_column_ = fetch_pointer_.column + LineLayout<personality>::VRAMAccessDelay;
 		}
 
 		if constexpr (is_sega_vdp(personality)) {
@@ -1242,11 +1242,11 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const {
 	if(get_interrupt_line()) return HalfCycles::max();
 
 	// Calculate the amount of time until the next end-of-frame interrupt.
-	const int frame_length = Timing<personality>::CyclesPerLine * this->mode_timing_.total_lines;
+	const int frame_length = LineLayout<personality>::CyclesPerLine * this->mode_timing_.total_lines;
 	int time_until_frame_interrupt =
 		(
-			((this->mode_timing_.end_of_frame_interrupt_position.row * Timing<personality>::CyclesPerLine) + this->mode_timing_.end_of_frame_interrupt_position.column + frame_length) -
-			((this->fetch_pointer_.row * Timing<personality>::CyclesPerLine) + this->fetch_pointer_.column)
+			((this->mode_timing_.end_of_frame_interrupt_position.row * LineLayout<personality>::CyclesPerLine) + this->mode_timing_.end_of_frame_interrupt_position.column + frame_length) -
+			((this->fetch_pointer_.row * LineLayout<personality>::CyclesPerLine) + this->fetch_pointer_.column)
 		) % frame_length;
 	if(!time_until_frame_interrupt) time_until_frame_interrupt = frame_length;
 
@@ -1260,7 +1260,7 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const {
 	int cycles_to_next_interrupt_threshold = this->mode_timing_.line_interrupt_position - this->fetch_pointer_.column;
 	int line_of_next_interrupt_threshold = this->fetch_pointer_.row;
 	if(cycles_to_next_interrupt_threshold <= 0) {
-		cycles_to_next_interrupt_threshold += Timing<personality>::CyclesPerLine;
+		cycles_to_next_interrupt_threshold += LineLayout<personality>::CyclesPerLine;
 		++line_of_next_interrupt_threshold;
 	}
 
@@ -1291,7 +1291,7 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const {
 	// Figure out the number of internal cycles until the next line interrupt, which is the amount
 	// of time to the next tick over and then next_line_interrupt_row - row_ lines further.
 	const int lines_until_interrupt = (next_line_interrupt_row - line_of_next_interrupt_threshold + this->mode_timing_.total_lines) % this->mode_timing_.total_lines;
-	const int local_cycles_until_line_interrupt = cycles_to_next_interrupt_threshold + lines_until_interrupt * Timing<personality>::CyclesPerLine;
+	const int local_cycles_until_line_interrupt = cycles_to_next_interrupt_threshold + lines_until_interrupt * LineLayout<personality>::CyclesPerLine;
 	if(!this->generate_interrupts_) return this->clock_converter_.half_cycles_before_internal_cycles(local_cycles_until_line_interrupt);
 
 	// Return whichever interrupt is closer.
@@ -1305,7 +1305,7 @@ HalfCycles TMS9918<personality>::get_time_until_line(int line) {
 	int cycles_to_next_interrupt_threshold = this->mode_timing_.line_interrupt_position - this->fetch_pointer_.column;
 	int line_of_next_interrupt_threshold = this->fetch_pointer_.row;
 	if(cycles_to_next_interrupt_threshold <= 0) {
-		cycles_to_next_interrupt_threshold += Timing<personality>::CyclesPerLine;
+		cycles_to_next_interrupt_threshold += LineLayout<personality>::CyclesPerLine;
 		++line_of_next_interrupt_threshold;
 	}
 
@@ -1313,7 +1313,7 @@ HalfCycles TMS9918<personality>::get_time_until_line(int line) {
 		line += this->mode_timing_.total_lines;
 	}
 
-	return this->clock_converter_.half_cycles_before_internal_cycles(cycles_to_next_interrupt_threshold + (line - line_of_next_interrupt_threshold)*Timing<personality>::CyclesPerLine);
+	return this->clock_converter_.half_cycles_before_internal_cycles(cycles_to_next_interrupt_threshold + (line - line_of_next_interrupt_threshold)*LineLayout<personality>::CyclesPerLine);
 }
 
 template <Personality personality>
@@ -1329,7 +1329,7 @@ template <Personality personality>uint8_t TMS9918<personality>::get_latched_hori
 	// which counts the 256 pixels as items 0–255, starts
 	// counting at -48, and returns only the top 8 bits of the number.
 	int public_counter = this->latched_column_ - LineLayout<personality>::EndOfLeftBorder;
-	if(public_counter < -46) public_counter += Timing<personality>::CyclesPerLine;
+	if(public_counter < -46) public_counter += LineLayout<personality>::CyclesPerLine;
 	return uint8_t(public_counter >> 1);
 }
 
diff --git a/Components/9918/Implementation/ClockConverter.hpp b/Components/9918/Implementation/ClockConverter.hpp
index 9e115d935..9c61bbb96 100644
--- a/Components/9918/Implementation/ClockConverter.hpp
+++ b/Components/9918/Implementation/ClockConverter.hpp
@@ -51,20 +51,6 @@ template <Personality personality, Clock clock> constexpr int from_internal(int
 	return length * clock_rate<personality, clock>() / clock_rate<personality, Clock::Internal>();
 }
 
-/// Provides default timing measurements that duplicate the layout of a TMS9928's line,
-/// scaled to the clock rate specified.
-template <Personality personality> struct StandardTiming {
-	/// The total number of internal cycles per line of output.
-	constexpr static int CyclesPerLine = clock_rate<personality, Clock::Internal>();
-
-	/// The number of internal cycles that must elapse between a request to read or write and
-	/// it becoming a candidate for action.
-	constexpr static int VRAMAccessDelay = 6;
-};
-
-/// Provides concrete, specific timing for the nominated personality.
-template <Personality personality> struct Timing: public StandardTiming<personality> {};
-
 /*!
 	Provides a [potentially-]stateful conversion between the external and internal clocks.
 	Unlike the other clock conversions, this one may be non-integral, requiring that
@@ -174,9 +160,15 @@ template <Personality personality> struct LineLayout<personality, std::enable_if
 	constexpr static int EndOfLeftBorder	= 63;
 	constexpr static int EndOfPixels		= 319;
 	constexpr static int EndOfRightBorder	= 334;
-
+	
+	constexpr static int CyclesPerLine 		= 342;
+	
 	constexpr static int TextModeEndOfLeftBorder	= 69;
 	constexpr static int TextModeEndOfPixels		= 309;
+	
+	/// The number of internal cycles that must elapse between a request to read or write and
+	/// it becoming a candidate for action.
+	constexpr static int VRAMAccessDelay = 6;
 };
 
 template <Personality personality> struct LineLayout<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
@@ -187,9 +179,15 @@ template <Personality personality> struct LineLayout<personality, std::enable_if
 	constexpr static int EndOfLeftBorder	= 258;
 	constexpr static int EndOfPixels		= 1282;
 	constexpr static int EndOfRightBorder	= 1341;
-
+	
+	constexpr static int CyclesPerLine 		= 1368;
+	
 	constexpr static int TextModeEndOfLeftBorder	= 294;
 	constexpr static int TextModeEndOfPixels		= 1254;
+	
+	/// The number of internal cycles that must elapse between a request to read or write and
+	/// it becoming a candidate for action.
+	constexpr static int VRAMAccessDelay = 16;
 };
 
 }