From 3cb6bbf771d06e66b871ac7c9bc2c06eed52365c Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 12 Feb 2020 22:28:42 -0500
Subject: [PATCH] Uses the union of all drive statuses to determine
 Drive::Controller's preferred clocking.

---
 Storage/Disk/Controller/DiskController.cpp | 28 ++++++++++++++++++----
 Storage/Disk/Controller/DiskController.hpp |  7 +++++-
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/Storage/Disk/Controller/DiskController.cpp b/Storage/Disk/Controller/DiskController.cpp
index 73ce78d74..2c3484767 100644
--- a/Storage/Disk/Controller/DiskController.cpp
+++ b/Storage/Disk/Controller/DiskController.cpp
@@ -16,6 +16,7 @@ Controller::Controller(Cycles clock_rate) :
 		pll_(100, *this),
 		empty_drive_(int(clock_rate.as_integral()), 1, 1),
 		drive_(&empty_drive_) {
+	empty_drive_.set_clocking_hint_observer(this);
 	set_expected_bit_length(Time(1));
 }
 
@@ -24,11 +25,24 @@ void Controller::set_component_prefers_clocking(ClockingHint::Source *component,
 }
 
 ClockingHint::Preference Controller::preferred_clocking() {
-	return (!drive_ || (drive_->preferred_clocking() == ClockingHint::Preference::None)) ? ClockingHint::Preference::None : ClockingHint::Preference::RealTime;
+	// Nominate RealTime clocking if any drive currently wants any clocking whatsoever.
+	// Otherwise, ::None will do.
+	for(auto &drive: drives_) {
+		if(drive.preferred_clocking() != ClockingHint::Preference::None) {
+			return ClockingHint::Preference::RealTime;
+		}
+	}
+	if(empty_drive_.preferred_clocking() != ClockingHint::Preference::None) {
+		return ClockingHint::Preference::RealTime;
+	}
+	return ClockingHint::Preference::None;
 }
 
 void Controller::run_for(const Cycles cycles) {
-	if(drive_) drive_->run_for(cycles);
+	for(auto &drive: drives_) {
+		drive.run_for(cycles);
+	}
+	empty_drive_.run_for(cycles);
 }
 
 Drive &Controller::get_drive() {
@@ -77,14 +91,19 @@ void Controller::set_drive(int index_mask) {
 	}
 
 	ClockingHint::Preference former_prefernece = preferred_clocking();
-//	invalidate_track();
 
+	// Stop receiving events from the current drive.
 	get_drive().set_event_delegate(nullptr);
-	get_drive().set_clocking_hint_observer(nullptr);
+
+	// TODO: a transfer of writing state, if writing?
 
 	if(!index_mask) {
 		drive_ = &empty_drive_;
 	} else {
+		// TEMPORARY FIX: connect up only the first selected drive.
+		// TODO: at least merge events if multiple drives are selected. Some computers have
+		// controllers that allow this, with usually meaningless results as far as I can
+		// imagine. But the limit of an emulator shouldn't be the author's imagination.
 		size_t index = 0;
 		while(!(index_mask&1)) {
 			index_mask >>= 1;
@@ -94,7 +113,6 @@ void Controller::set_drive(int index_mask) {
 	}
 
 	get_drive().set_event_delegate(this);
-	get_drive().set_clocking_hint_observer(this);
 
 	if(preferred_clocking() != former_prefernece) {
 		update_clocking_observer();
diff --git a/Storage/Disk/Controller/DiskController.hpp b/Storage/Disk/Controller/DiskController.hpp
index 9a495fb19..b3b23ebfa 100644
--- a/Storage/Disk/Controller/DiskController.hpp
+++ b/Storage/Disk/Controller/DiskController.hpp
@@ -55,16 +55,21 @@ class Controller:
 		void set_drive(int index_mask);
 
 		/*!
-			Adds a new drive to the attached list, returning its index.
+			Adds a new drive to the drive list, returning its index.
 		*/
 		template<typename... Args> size_t emplace_drive(Args&&... args) {
 			drives_.emplace_back(std::forward<Args>(args)...);
+			drives_.back().set_clocking_hint_observer(this);
 			return drives_.size() - 1;
 		}
 
+		/*!
+			Adds @c count new drives to the drive list, returning the index of the final one added.
+		*/
 		template<typename... Args> size_t emplace_drives(size_t count, Args&&... args) {
 			while(count--) {
 				drives_.emplace_back(std::forward<Args>(args)...);
+				drives_.back().set_clocking_hint_observer(this);
 			}
 			return drives_.size() - 1;
 		}