From 15d17c12d586db943d7e25618f9539236c38c959 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Tue, 9 May 2017 21:22:01 -0400
Subject: [PATCH 1/7] Switched the 6560 to two bytes per pixel, since one isn't
 sufficient for precision and because mixing up the implementation might help
 me to figure out what's amiss.

---
 Components/6560/6560.hpp | 44 +++++++++++++++++++++-------------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp
index cd786888c..444a0dcb2 100644
--- a/Components/6560/6560.hpp
+++ b/Components/6560/6560.hpp
@@ -43,7 +43,7 @@ class Speaker: public ::Outputs::Filter<Speaker> {
 template <class T> class MOS6560 {
 	public:
 		MOS6560() :
-				crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::NTSC60, 1)),
+				crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::CRT::NTSC60, 2)),
 				speaker_(new Speaker),
 				horizontal_counter_(0),
 				vertical_counter_(0),
@@ -53,13 +53,11 @@ template <class T> class MOS6560 {
 			crt_->set_composite_sampling_function(
 				"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
 				"{"
-					"uint c = texture(texID, coordinate).r;"
-					"float y = float(c >> 4) / 4.0;"
-					"uint yC = c & 15u;"
-					"float phaseOffset = 6.283185308 * float(yC) / 16.0;"
+					"vec2 yc = texture(texID, coordinate).rg / vec2(255.0);"
+					"float phaseOffset = 6.283185308 * float(yc.y);"
 
 					"float chroma = cos(phase + phaseOffset);"
-					"return mix(y, step(yC, 14) * chroma, amplitude);"
+					"return mix(yc.x, chroma, amplitude);"
 				"}");
 
 			// default to NTSC
@@ -82,11 +80,13 @@ template <class T> class MOS6560 {
 		*/
 		void set_output_mode(OutputMode output_mode) {
 			output_mode_ = output_mode;
-			const uint8_t luminances[16] = {		// range is 0–4
-				0, 4, 1, 3, 2, 2, 1, 3,
-				2, 1, 2, 1, 2, 3, 2, 3
+			const uint8_t luminances[16] = {
+				0,		255,	91,		190,
+				134,	176,	84,		224,
+				140,	212,	200,	213,
+				205,	200,	163,	221
 			};
-			const uint8_t pal_chrominances[16] = {	// range is 0–15; 15 is a special case meaning "no chrominance"
+			const uint8_t pal_chrominances[16] = {
 				15, 15, 5, 13, 2, 10, 0, 8,
 				6, 7, 5, 13, 2, 10, 0, 8,
 			};
@@ -118,7 +118,7 @@ template <class T> class MOS6560 {
 			}
 
 			crt_->set_new_display_type((unsigned int)(timing_.cycles_per_line*4), display_type);
-//			crt_->set_visible_area(Outputs::CRT::Rect(0.1f, 0.1f, 0.8f, 0.8f));
+			crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f));
 
 //			switch(output_mode) {
 //				case OutputMode::PAL:
@@ -130,7 +130,9 @@ template <class T> class MOS6560 {
 //			}
 
 			for(int c = 0; c < 16; c++) {
-				colours_[c] = (uint8_t)((luminances[c] << 4) | chrominances[c]);
+				uint8_t *colour = (uint8_t *)&colours_[c];
+				colour[0] = luminances[c];
+				colour[1] = chrominances[c] * 15;
 			}
 		}
 
@@ -256,7 +258,7 @@ template <class T> class MOS6560 {
 
 					pixel_pointer = nullptr;
 					if(output_state_ == State::Pixels) {
-						pixel_pointer = crt_->allocate_write_area(260);
+						pixel_pointer = (uint16_t *)crt_->allocate_write_area(260);
 					}
 				}
 				cycles_in_state_++;
@@ -266,9 +268,9 @@ template <class T> class MOS6560 {
 						character_value_ = pixel_data;
 
 						if(pixel_pointer) {
-							uint8_t cell_colour = colours_[character_colour_ & 0x7];
+							uint16_t cell_colour = colours_[character_colour_ & 0x7];
 							if(!(character_colour_&0x8)) {
-								uint8_t colours[2];
+								uint16_t colours[2];
 								if(registers_.invertedCells) {
 									colours[0] = cell_colour;
 									colours[1] = registers_.backgroundColour;
@@ -285,7 +287,7 @@ template <class T> class MOS6560 {
 								pixel_pointer[6] = colours[(character_value_ >> 1)&1];
 								pixel_pointer[7] = colours[(character_value_ >> 0)&1];
 							} else {
-								uint8_t colours[4] = {registers_.backgroundColour, registers_.borderColour, cell_colour, registers_.auxiliary_colour};
+								uint16_t colours[4] = {registers_.backgroundColour, registers_.borderColour, cell_colour, registers_.auxiliary_colour};
 								pixel_pointer[0] =
 								pixel_pointer[1] = colours[(character_value_ >> 6)&3];
 								pixel_pointer[2] =
@@ -358,7 +360,7 @@ template <class T> class MOS6560 {
 				break;
 
 				case 0xf: {
-					uint8_t new_border_colour = colours_[value & 0x07];
+					uint16_t new_border_colour = colours_[value & 0x07];
 					if(this_state_ == State::Border && new_border_colour != registers_.borderColour) {
 						output_border(cycles_in_state_ * 4);
 						cycles_in_state_ = 0;
@@ -405,7 +407,7 @@ template <class T> class MOS6560 {
 			uint8_t first_column_location, first_row_location;
 			uint8_t number_of_columns, number_of_rows;
 			uint16_t character_cell_start_address, video_matrix_start_address;
-			uint8_t backgroundColour, borderColour, auxiliary_colour;
+			uint16_t backgroundColour, borderColour, auxiliary_colour;
 			bool invertedCells;
 
 			uint8_t direct_values[16];
@@ -436,11 +438,11 @@ template <class T> class MOS6560 {
 		bool is_odd_frame_, is_odd_line_;
 
 		// lookup table from 6560 colour index to appropriate PAL/NTSC value
-		uint8_t colours_[16];
+		uint16_t colours_[16];
 
-		uint8_t *pixel_pointer;
+		uint16_t *pixel_pointer;
 		void output_border(unsigned int number_of_cycles) {
-			uint8_t *colour_pointer = crt_->allocate_write_area(1);
+			uint16_t *colour_pointer = (uint16_t *)crt_->allocate_write_area(1);
 			if(colour_pointer) *colour_pointer = registers_.borderColour;
 			crt_->output_level(number_of_cycles);
 		}

From b14c8927403b98ef9037fa62415dcbdc3b3cc492 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 10 May 2017 21:29:39 -0400
Subject: [PATCH 2/7] Switched to a safer RAII approach to this lock.

---
 Outputs/CRT/Internals/CRTOpenGL.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp
index 28ec3d0e3..0b5c3f275 100644
--- a/Outputs/CRT/Internals/CRTOpenGL.cpp
+++ b/Outputs/CRT/Internals/CRTOpenGL.cpp
@@ -336,7 +336,7 @@ void OpenGLOutputBuilder::set_output_device(OutputDevice output_device) {
 }
 
 void OpenGLOutputBuilder::set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider) {
-	output_mutex_.lock();
+	std::lock_guard<std::mutex> lock_guard(output_mutex_);
 	input_frequency_ = input_frequency;
 	cycles_per_line_ = cycles_per_line;
 	height_of_display_ = height_of_display;
@@ -345,7 +345,6 @@ void OpenGLOutputBuilder::set_timing(unsigned int input_frequency, unsigned int
 	vertical_period_divider_ = vertical_period_divider;
 
 	set_timing_uniforms();
-	output_mutex_.unlock();
 }
 
 #pragma mark - Internal Configuration

From a34033122991849ce818a586d7f2c55c78e5f234 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Thu, 11 May 2017 21:31:58 -0400
Subject: [PATCH 3/7] Introduced 1-bit of saturation, returning black and white
 as black and white.

---
 Components/6560/6560.hpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp
index 444a0dcb2..fccf4e053 100644
--- a/Components/6560/6560.hpp
+++ b/Components/6560/6560.hpp
@@ -54,10 +54,10 @@ template <class T> class MOS6560 {
 				"float composite_sample(usampler2D texID, vec2 coordinate, vec2 iCoordinate, float phase, float amplitude)"
 				"{"
 					"vec2 yc = texture(texID, coordinate).rg / vec2(255.0);"
-					"float phaseOffset = 6.283185308 * float(yc.y);"
+					"float phaseOffset = 6.283185308 * 2.0 * yc.y;"
 
 					"float chroma = cos(phase + phaseOffset);"
-					"return mix(yc.x, chroma, amplitude);"
+					"return mix(yc.x, step(yc.y, 0.75) * chroma, amplitude);"
 				"}");
 
 			// default to NTSC
@@ -91,7 +91,7 @@ template <class T> class MOS6560 {
 				6, 7, 5, 13, 2, 10, 0, 8,
 			};
 			const uint8_t ntsc_chrominances[16] = {
-				15, 15, 2, 10, 4, 12, 6, 14,
+				255, 255, 2, 10, 4, 12, 6, 14,
 				0, 8, 2, 10, 4, 12, 6, 14,
 			};
 			const uint8_t *chrominances;
@@ -118,7 +118,7 @@ template <class T> class MOS6560 {
 			}
 
 			crt_->set_new_display_type((unsigned int)(timing_.cycles_per_line*4), display_type);
-			crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f));
+//			crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f));
 
 //			switch(output_mode) {
 //				case OutputMode::PAL:

From b0142cf0507737d1563a3243261a465723073ca1 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 13 May 2017 14:29:36 -0400
Subject: [PATCH 4/7] Made an updated stab at NTSC colours.

---
 Components/6560/6560.hpp | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp
index fccf4e053..575d78a15 100644
--- a/Components/6560/6560.hpp
+++ b/Components/6560/6560.hpp
@@ -57,7 +57,7 @@ template <class T> class MOS6560 {
 					"float phaseOffset = 6.283185308 * 2.0 * yc.y;"
 
 					"float chroma = cos(phase + phaseOffset);"
-					"return mix(yc.x, step(yc.y, 0.75) * chroma, amplitude);"
+					"return mix(yc.x, step(yc.y, 0.75) * 0.5 * chroma, amplitude);"
 				"}");
 
 			// default to NTSC
@@ -80,19 +80,25 @@ template <class T> class MOS6560 {
 		*/
 		void set_output_mode(OutputMode output_mode) {
 			output_mode_ = output_mode;
+
+			// Lumunances are encoded trivially: on a 0–255 scale.
 			const uint8_t luminances[16] = {
 				0,		255,	91,		190,
 				134,	176,	84,		224,
 				140,	212,	200,	213,
 				205,	200,	163,	221
 			};
+
+			// Chrominances are encoded such that 0–128 is a complete revolution of phase;
+			// anything above 191 disables the colour subcarrier. Phase is relative to the
+			// colour burst, so 0 is green.
 			const uint8_t pal_chrominances[16] = {
 				15, 15, 5, 13, 2, 10, 0, 8,
 				6, 7, 5, 13, 2, 10, 0, 8,
 			};
 			const uint8_t ntsc_chrominances[16] = {
-				255, 255, 2, 10, 4, 12, 6, 14,
-				0, 8, 2, 10, 4, 12, 6, 14,
+				255,	255,	40,		104,	64,		120,	80,		16,
+				32,		32,		40,		104,	64,		120,	80,		16,
 			};
 			const uint8_t *chrominances;
 			Outputs::CRT::DisplayType display_type;
@@ -132,7 +138,7 @@ template <class T> class MOS6560 {
 			for(int c = 0; c < 16; c++) {
 				uint8_t *colour = (uint8_t *)&colours_[c];
 				colour[0] = luminances[c];
-				colour[1] = chrominances[c] * 15;
+				colour[1] = chrominances[c];
 			}
 		}
 
@@ -297,6 +303,15 @@ template <class T> class MOS6560 {
 								pixel_pointer[6] =
 								pixel_pointer[7] = colours[(character_value_ >> 0)&3];
 							}
+
+//							pixel_pointer[0] =
+//							pixel_pointer[1] =
+//							pixel_pointer[2] =
+//							pixel_pointer[3] =
+//							pixel_pointer[4] =
+//							pixel_pointer[5] =
+//							pixel_pointer[6] =
+//							pixel_pointer[7] = colours_[8];
 							pixel_pointer += 8;
 						}
 					} else {

From 44ce7fa54ceba42387916538d1d7ecef23d522be Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 13 May 2017 21:50:09 -0400
Subject: [PATCH 5/7] Corrected luminances across the board, and PAL colours.

---
 Components/6560/6560.hpp | 30 +++++++++++++-----------------
 1 file changed, 13 insertions(+), 17 deletions(-)

diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp
index 575d78a15..ec1ba9631 100644
--- a/Components/6560/6560.hpp
+++ b/Components/6560/6560.hpp
@@ -83,22 +83,26 @@ template <class T> class MOS6560 {
 
 			// Lumunances are encoded trivially: on a 0–255 scale.
 			const uint8_t luminances[16] = {
-				0,		255,	91,		190,
-				134,	176,	84,		224,
-				140,	212,	200,	213,
-				205,	200,	163,	221
+				0,		255,	109,	189,
+				199,	144,	159,	161,
+				126,	227,	227,	207,
+				235,	173,	188,	196
 			};
 
 			// Chrominances are encoded such that 0–128 is a complete revolution of phase;
 			// anything above 191 disables the colour subcarrier. Phase is relative to the
 			// colour burst, so 0 is green.
 			const uint8_t pal_chrominances[16] = {
-				15, 15, 5, 13, 2, 10, 0, 8,
-				6, 7, 5, 13, 2, 10, 0, 8,
+				255,	255,	40,		112,
+				8,		88,		0,		56,
+				40,		48,		40,		112,
+				8,		88,		0,		56,
 			};
 			const uint8_t ntsc_chrominances[16] = {
-				255,	255,	40,		104,	64,		120,	80,		16,
-				32,		32,		40,		104,	64,		120,	80,		16,
+				255,	255,	40,		104,
+				64,		120,	80,		16,
+				32,		32,		40,		104,
+				64,		120,	80,		16,
 			};
 			const uint8_t *chrominances;
 			Outputs::CRT::DisplayType display_type;
@@ -124,7 +128,7 @@ template <class T> class MOS6560 {
 			}
 
 			crt_->set_new_display_type((unsigned int)(timing_.cycles_per_line*4), display_type);
-//			crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f));
+			crt_->set_visible_area(Outputs::CRT::Rect(0.05f, 0.05f, 0.9f, 0.9f));
 
 //			switch(output_mode) {
 //				case OutputMode::PAL:
@@ -304,14 +308,6 @@ template <class T> class MOS6560 {
 								pixel_pointer[7] = colours[(character_value_ >> 0)&3];
 							}
 
-//							pixel_pointer[0] =
-//							pixel_pointer[1] =
-//							pixel_pointer[2] =
-//							pixel_pointer[3] =
-//							pixel_pointer[4] =
-//							pixel_pointer[5] =
-//							pixel_pointer[6] =
-//							pixel_pointer[7] = colours_[8];
 							pixel_pointer += 8;
 						}
 					} else {

From c2b5a9bb1f3448522f032a2d8b8d789a833385bd Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 13 May 2017 21:50:48 -0400
Subject: [PATCH 6/7] Minor fix: given that phase is now a function of
 position, stop nudging position.

---
 Outputs/CRT/Internals/Shaders/IntermediateShader.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp
index 9c81a3421..670effaeb 100644
--- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp
+++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp
@@ -99,7 +99,7 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_shader(const std::s
 			"phaseAndAmplitudeVarying.y = 0.33;" // TODO: reinstate connection with (phaseTimeAndAmplitude.y/256.0)
 
 			// determine output position by scaling the output position according to the texture size
-			"vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(1.0)/outputTextureSize;"
+			"vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0);"
 			"gl_Position = vec4(eyePosition, 0.0, 1.0);"
 		"}", sampler_type, input_variable);
 

From e270b726b30a790d14f98c261de1c9e2eaa380e5 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sat, 13 May 2017 22:01:02 -0400
Subject: [PATCH 7/7] Tweaked blue, increased saturation.

---
 Components/6560/6560.hpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp
index ec1ba9631..0067dec43 100644
--- a/Components/6560/6560.hpp
+++ b/Components/6560/6560.hpp
@@ -57,7 +57,7 @@ template <class T> class MOS6560 {
 					"float phaseOffset = 6.283185308 * 2.0 * yc.y;"
 
 					"float chroma = cos(phase + phaseOffset);"
-					"return mix(yc.x, step(yc.y, 0.75) * 0.5 * chroma, amplitude);"
+					"return mix(yc.x, step(yc.y, 0.75) * chroma, amplitude);"
 				"}");
 
 			// default to NTSC
@@ -94,9 +94,9 @@ template <class T> class MOS6560 {
 			// colour burst, so 0 is green.
 			const uint8_t pal_chrominances[16] = {
 				255,	255,	40,		112,
-				8,		88,		0,		56,
+				8,		88,		120,	56,
 				40,		48,		40,		112,
-				8,		88,		0,		56,
+				8,		88,		120,	56,
 			};
 			const uint8_t ntsc_chrominances[16] = {
 				255,	255,	40,		104,