From 20cab08f8f480deea45af737b4d4a942b00b123a Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 23 Jan 2016 17:44:34 -0500
Subject: [PATCH] Added a two-slot buffer of scans and a comon dispatch
 mechanism.

---
 Outputs/CRT.cpp | 75 +++++++++++++++++++++++++++++++------------------
 Outputs/CRT.hpp | 19 ++++++++++++-
 2 files changed, 66 insertions(+), 28 deletions(-)

diff --git a/Outputs/CRT.cpp b/Outputs/CRT.cpp
index 307be6caa..9e5b73204 100644
--- a/Outputs/CRT.cpp
+++ b/Outputs/CRT.cpp
@@ -64,7 +64,16 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di
 	}
 }
 
-CRT::CRT(unsigned int cycles_per_line, unsigned int height_of_display, unsigned int number_of_buffers, ...)
+CRT::CRT(unsigned int cycles_per_line, unsigned int height_of_display, unsigned int number_of_buffers, ...) :
+	_next_scan(0),
+	_frames_with_delegate(0),
+	_frame_read_pointer(0),
+	_horizontal_counter(0),
+	_sync_capacitor_charge_level(0),
+	_is_receiving_sync(false),
+	_is_in_hsync(false),
+	_is_in_vsync(false),
+	_rasterPosition({.x = 0, .y = 0})
 {
 	set_new_timing(cycles_per_line, height_of_display);
 
@@ -79,24 +88,10 @@ CRT::CRT(unsigned int cycles_per_line, unsigned int height_of_display, unsigned
 		_frame_builders[frame] = new CRTFrameBuilder(bufferWidth, bufferHeight, number_of_buffers, va);
 		va_end(va);
 	}
-	_frames_with_delegate = 0;
-	_frame_read_pointer = 0;
 	_current_frame_builder = _frame_builders[0];
 
-	// reset raster position
-	_rasterPosition.x = _rasterPosition.y = 0;
-
 	// reset flywheel sync
 	_expected_next_hsync = _cycles_per_line;
-	_horizontal_counter = 0;
-
-	// reset the vertical charge capacitor
-	_sync_capacitor_charge_level = 0;
-
-	// start off not in horizontal sync, not receiving a sync signal
-	_is_receiving_sync = false;
-	_is_in_hsync = false;
-	_is_in_vsync = false;
 }
 
 CRT::~CRT()
@@ -349,35 +344,61 @@ void CRT::set_delegate(Delegate *delegate)
 
 #pragma mark - stream feeding methods
 
+void CRT::output_scan(Scan *scan)
+{
+	bool this_is_sync = (scan->type == Type::Sync);
+	bool hsync_requested = !_is_receiving_sync && this_is_sync;
+	bool vsync_requested = _is_receiving_sync;
+	_is_receiving_sync = this_is_sync;
+
+	advance_cycles(scan->number_of_cycles, scan->source_divider, hsync_requested, vsync_requested, this_is_sync, scan->type);
+
+	_next_scan ^= 1;
+}
+
 /*
 	These all merely channel into advance_cycles, supplying appropriate arguments
 */
 void CRT::output_sync(unsigned int number_of_cycles)
 {
-	bool _hsync_requested = !_is_receiving_sync;	// ensure this really is edge triggered; someone calling output_sync twice in succession shouldn't trigger it twice
-	_is_receiving_sync = true;
-	advance_cycles(number_of_cycles, 1, _hsync_requested, false, true, Type::Sync);
+	_scans[_next_scan].type = Type::Sync;
+	_scans[_next_scan].number_of_cycles = number_of_cycles;
+	output_scan(&_scans[_next_scan]);
 }
 
 void CRT::output_blank(unsigned int number_of_cycles)
 {
-	bool _vsync_requested = _is_receiving_sync;
-	_is_receiving_sync = false;
-	advance_cycles(number_of_cycles, 1, false, _vsync_requested, false, Type::Blank);
+	_scans[_next_scan].type = Type::Blank;
+	_scans[_next_scan].number_of_cycles = number_of_cycles;
+	output_scan(&_scans[_next_scan]);
 }
 
 void CRT::output_level(unsigned int number_of_cycles)
 {
-	bool _vsync_requested = _is_receiving_sync;
-	_is_receiving_sync = false;
-	advance_cycles(number_of_cycles, 1, false, _vsync_requested, false, Type::Level);
+	_scans[_next_scan].type = Type::Level;
+	_scans[_next_scan].number_of_cycles = number_of_cycles;
+	_scans[_next_scan].tex_x = _current_frame_builder ? _current_frame_builder->_write_x_position : 0;
+	_scans[_next_scan].tex_y = _current_frame_builder ? _current_frame_builder->_write_y_position : 0;
+	output_scan(&_scans[_next_scan]);
+}
+
+void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t magnitude)
+{
+	_scans[_next_scan].type = Type::ColourBurst;
+	_scans[_next_scan].number_of_cycles = number_of_cycles;
+	_scans[_next_scan].phase = phase;
+	_scans[_next_scan].magnitude = magnitude;
+	output_scan(&_scans[_next_scan]);
 }
 
 void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider)
 {
-	bool _vsync_requested = _is_receiving_sync;
-	_is_receiving_sync = false;
-	advance_cycles(number_of_cycles, source_divider, false, _vsync_requested, false, Type::Data);
+	_scans[_next_scan].type = Type::Data;
+	_scans[_next_scan].number_of_cycles = number_of_cycles;
+	_scans[_next_scan].tex_x = _current_frame_builder ? _current_frame_builder->_write_x_position : 0;
+	_scans[_next_scan].tex_y = _current_frame_builder ? _current_frame_builder->_write_y_position : 0;
+	_scans[_next_scan].source_divider = source_divider;
+	output_scan(&_scans[_next_scan]);
 }
 
 #pragma mark - Buffer supply
diff --git a/Outputs/CRT.hpp b/Outputs/CRT.hpp
index af87c4f29..865e9dda7 100644
--- a/Outputs/CRT.hpp
+++ b/Outputs/CRT.hpp
@@ -144,7 +144,7 @@ class CRT {
 
 		// the outer entry point for dispatching output_sync, output_blank, output_level and output_data
 		enum Type {
-			Sync, Level, Data, Blank
+			Sync, Level, Data, Blank, ColourBurst
 		} type;
 		void advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, bool vsync_charging, Type type);
 
@@ -157,6 +157,23 @@ class CRT {
 		};
 		SyncEvent get_next_vertical_sync_event(bool vsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced);
 		SyncEvent get_next_horizontal_sync_event(bool hsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced);
+
+		// each call to output_* generates a scan. A two-slot queue for scans allows edge extensions.
+		struct Scan {
+			Type type;
+			unsigned int number_of_cycles;
+			union {
+				struct {
+					unsigned int source_divider;
+					uint16_t tex_x, tex_y;
+				};
+				struct {
+					uint8_t phase, magnitude;
+				};
+			};
+		} _scans[2];
+		int _next_scan;
+		void output_scan(Scan *scan);
 };
 
 }