From 7826a26c7b0ba120d609bcf115f9df57294d58ad Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 2 Jul 2021 21:42:09 -0400
Subject: [PATCH] Adds Enterprise composite video option.

While enabling more pixels on the left for RGB mode.
---
 Machines/Enterprise/Enterprise.cpp            | 21 +++++++++++++++++++
 Machines/Enterprise/Enterprise.hpp            | 20 ++++++++++++++++++
 Machines/Enterprise/Nick.cpp                  | 17 +++++++++++----
 Machines/Enterprise/Nick.hpp                  | 11 ++++++++++
 .../StaticAnalyser/CSStaticAnalyser.mm        |  1 +
 5 files changed, 66 insertions(+), 4 deletions(-)

diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp
index e1cc9b86c..97dc6ba38 100644
--- a/Machines/Enterprise/Enterprise.cpp
+++ b/Machines/Enterprise/Enterprise.cpp
@@ -64,6 +64,7 @@ namespace Enterprise {
 
 template <bool has_disk_controller> class ConcreteMachine:
 	public Activity::Source,
+	public Configurable::Device,
 	public CPU::Z80::BusHandler,
 	public Machine,
 	public MachineTypes::AudioProducer,
@@ -591,6 +592,14 @@ template <bool has_disk_controller> class ConcreteMachine:
 			return nick_.last_valid()->get_scaled_scan_status();
 		}
 
+		void set_display_type(Outputs::Display::DisplayType display_type) final {
+			nick_.last_valid()->set_display_type(display_type);
+		}
+
+		Outputs::Display::DisplayType get_display_type() const final {
+			return nick_.last_valid()->get_display_type();
+		}
+
 		// MARK: - AudioProducer
 
 		Outputs::Speaker::Speaker *get_speaker() final {
@@ -692,6 +701,18 @@ template <bool has_disk_controller> class ConcreteMachine:
 				exdos_.set_activity_observer(observer);
 			}
 		}
+
+		// MARK: - Configuration options.
+		std::unique_ptr<Reflection::Struct> get_options() final {
+			auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
+			options->output = get_video_signal_configurable();
+			return options;
+		}
+
+		void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
+			const auto options = dynamic_cast<Options *>(str.get());
+			set_video_signal_configurable(options->output);
+		}
 };
 
 }
diff --git a/Machines/Enterprise/Enterprise.hpp b/Machines/Enterprise/Enterprise.hpp
index 3a42a70f5..4970a66f1 100644
--- a/Machines/Enterprise/Enterprise.hpp
+++ b/Machines/Enterprise/Enterprise.hpp
@@ -10,16 +10,36 @@
 #define Enterprise_hpp
 
 #include "../../Analyser/Static/StaticAnalyser.hpp"
+#include "../../Configurable/Configurable.hpp"
+#include "../../Configurable/StandardOptions.hpp"
 #include "../ROMMachine.hpp"
 
 namespace Enterprise {
 
+/*!
+	@abstract Represents an Elan Enterprise.
+
+	@discussion An instance of Enterprise::Machine represents the current state of an
+	Elan Enterprise.
+*/
 class Machine {
 	public:
 		virtual ~Machine();
 
 		static Machine *Enterprise(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
 
+		/// Defines the runtime options available for an Enterprise.
+		class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
+			friend Configurable::DisplayOption<Options>;
+			public:
+				Options(Configurable::OptionsType type) :
+					Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour) {
+					if(needs_declare()) {
+						declare_display_option();
+						limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, Configurable::Display::CompositeMonochrome, -1);
+					}
+				}
+		};
 };
 
 };
diff --git a/Machines/Enterprise/Nick.cpp b/Machines/Enterprise/Nick.cpp
index 02628ead0..cb07a345a 100644
--- a/Machines/Enterprise/Nick.cpp
+++ b/Machines/Enterprise/Nick.cpp
@@ -51,7 +51,7 @@ Nick::Nick(const uint8_t *ram) :
 	ram_(ram) {
 
 	// Just use RGB for now.
-	crt_.set_display_type(Outputs::Display::DisplayType::RGB);
+	set_display_type(Outputs::Display::DisplayType::RGB);
 
 	// Crop to the centre 90% of the display.
 	crt_.set_visible_area(Outputs::Display::Rect(0.05f, 0.05f, 0.9f, 0.9f));
@@ -287,7 +287,7 @@ void Nick::run_for(Cycles duration) {
 			// the start of window 6 to the end of window 10.
 			//
 			// The first 8 palette entries also need to be fetched here.
-			while(window < 10 && window < end_window) {
+			while(window < first_pixel_window_ && window < end_window) {
 				if(window == 6) {
 					set_output_type(OutputType::ColourBurst);
 				}
@@ -303,8 +303,8 @@ void Nick::run_for(Cycles duration) {
 				add_window(1);
 			}
 
-			if(window >= 10) {
-				if(window == 10) {
+			if(window >= first_pixel_window_) {
+				if(window == first_pixel_window_) {
 					set_output_type(is_sync_or_pixels_ ? OutputType::Pixels : OutputType::Border);
 				}
 
@@ -484,6 +484,15 @@ Outputs::Display::ScanStatus Nick::get_scaled_scan_status() const {
 	return crt_.get_scaled_scan_status();
 }
 
+void Nick::set_display_type(Outputs::Display::DisplayType display_type) {
+	first_pixel_window_ = display_type == Outputs::Display::DisplayType::RGB ? 8 : 10;
+	crt_.set_display_type(display_type);
+}
+
+Outputs::Display::DisplayType Nick::get_display_type() const {
+	return crt_.get_display_type();
+}
+
 // MARK: - Specific pixel outputters.
 
 #define output1bpp(x)	\
diff --git a/Machines/Enterprise/Nick.hpp b/Machines/Enterprise/Nick.hpp
index b9ce44439..d19760859 100644
--- a/Machines/Enterprise/Nick.hpp
+++ b/Machines/Enterprise/Nick.hpp
@@ -28,6 +28,7 @@ class Nick {
 		void set_scan_target(Outputs::Display::ScanTarget *scan_target);
 		Outputs::Display::ScanStatus get_scaled_scan_status() const;
 
+		/// @returns The amount of time until the next potential change in interrupt output.
 		Cycles get_next_sequence_point() const;
 
 		/*!
@@ -38,6 +39,12 @@ class Nick {
 			return interrupt_line_;
 		}
 
+		/// Sets the type of output.
+		void set_display_type(Outputs::Display::DisplayType);
+
+		/// Gets the type of output.
+		Outputs::Display::DisplayType get_display_type() const;
+
 	private:
 		Outputs::CRT::CRT crt_;
 		const uint8_t *const ram_;
@@ -96,6 +103,10 @@ class Nick {
 		// Current palette.
 		uint16_t palette_[16]{};
 
+		// The first column with pixels on it; will be either 8 or 10 depending
+		// on whether the colour burst is meaningful to the current display type.
+		int first_pixel_window_ = 10;
+
 		// Specific outputters.
 		template <int bpp, bool is_lpixel> void output_pixel(uint16_t *target, int columns) const;
 		template <int bpp, int index_bits> void output_character(uint16_t *target, int columns) const;
diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm
index 805fff5d1..e50217f91 100644
--- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm	
+++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm	
@@ -316,6 +316,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
 		case Analyser::Machine::AtariST:		return @"CompositeOptions";
 		case Analyser::Machine::ColecoVision:	return @"CompositeOptions";
 		case Analyser::Machine::Electron:		return @"QuickLoadCompositeOptions";
+		case Analyser::Machine::Enterprise:		return @"CompositeOptions";
 		case Analyser::Machine::Macintosh:		return @"MacintoshOptions";
 		case Analyser::Machine::MasterSystem:	return @"CompositeOptions";
 		case Analyser::Machine::MSX:			return @"QuickLoadCompositeOptions";