From 99122efbbc81a3da886a962d3812e5b340a67d11 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Wed, 15 Jan 2020 23:29:52 -0500
Subject: [PATCH] Adds a slight cool-down period on end-of-rotation.

Along with the corresponding inactive transition of the ready signal.
---
 Storage/Disk/Drive.cpp | 69 +++++++++++++++++++++++++++++++-----------
 Storage/Disk/Drive.hpp |  3 ++
 2 files changed, 54 insertions(+), 18 deletions(-)

diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp
index a281c3a06..261ba711d 100644
--- a/Storage/Disk/Drive.cpp
+++ b/Storage/Disk/Drive.cpp
@@ -68,7 +68,7 @@ bool Drive::has_disk() const {
 }
 
 ClockingHint::Preference Drive::preferred_clocking() {
-	return (!motor_input_is_on_ || !has_disk_) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
+	return (!has_disk_ || (time_until_motor_transition == Cycles(0) && !disk_is_rotating_)) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
 }
 
 bool Drive::get_is_track_zero() const {
@@ -146,22 +146,29 @@ bool Drive::get_is_ready() const {
 }
 
 void Drive::set_motor_on(bool motor_is_on) {
-	if(motor_input_is_on_ != motor_is_on) {
-		motor_input_is_on_ = motor_is_on;
+	// Do nothing if the input hasn't changed.
+	if(motor_input_is_on_ == motor_is_on) return;
+	motor_input_is_on_ = motor_is_on;
 
-		if(observer_) {
-			observer_->set_drive_motor_status(drive_name_, motor_input_is_on_);
-			if(announce_motor_led_) {
-				observer_->set_led_status(drive_name_, motor_input_is_on_);
-			}
-		}
-
-		if(!motor_is_on) {
-			ready_index_count_ = 0;
-			if(disk_) disk_->flush_tracks();
-		}
-		update_clocking_observer();
+	// If this now means that the input and the actual state are in harmony,
+	// cancel any planned change and stop.
+	if(disk_is_rotating_ == motor_is_on) {
+		time_until_motor_transition = Cycles(0);
+		return;
 	}
+
+	// If this is a transition to on, start immediately.
+	// TODO: spin-up?
+	// TODO: momentum.
+	if(motor_is_on) {
+		set_disk_is_rotating(true);
+		return;
+	}
+
+	// This is a transition from on to off. Simulate momentum (ha!)
+	// by delaying the time until complete standstill.
+	if(time_until_motor_transition == Cycles(0))
+		time_until_motor_transition = get_input_clock_rate();
 }
 
 bool Drive::get_motor_on() const {
@@ -185,7 +192,16 @@ void Drive::run_for(const Cycles cycles) {
 	// Assumed: the index pulse pulses even if the drive has stopped spinning.
 	index_pulse_remaining_ = std::max(index_pulse_remaining_ - cycles, Cycles(0));
 
-	if(motor_input_is_on_) {
+	if(time_until_motor_transition > Cycles(0)) {
+		if(time_until_motor_transition > cycles) {
+			time_until_motor_transition -= cycles;
+		} else {
+			time_until_motor_transition = Cycles(0);
+			set_disk_is_rotating(!disk_is_rotating_);
+		}
+	}
+
+	if(disk_is_rotating_) {
 		if(has_disk_) {
 			Time zero(0);
 
@@ -400,6 +416,23 @@ bool Drive::is_writing() const {
 	return !is_reading_;
 }
 
+void Drive::set_disk_is_rotating(bool is_rotating) {
+	disk_is_rotating_ = is_rotating;
+
+	if(observer_) {
+		observer_->set_drive_motor_status(drive_name_, motor_input_is_on_);
+		if(announce_motor_led_) {
+			observer_->set_led_status(drive_name_, motor_input_is_on_);
+		}
+	}
+
+	if(!is_rotating) {
+		ready_index_count_ = 0;
+		if(disk_) disk_->flush_tracks();
+	}
+	update_clocking_observer();
+}
+
 void Drive::set_activity_observer(Activity::Observer *observer, const std::string &name, bool add_motor_led) {
 	observer_ = observer;
 	announce_motor_led_ = add_motor_led;
@@ -407,11 +440,11 @@ void Drive::set_activity_observer(Activity::Observer *observer, const std::strin
 		drive_name_ = name;
 
 		observer->register_drive(drive_name_);
-		observer->set_drive_motor_status(drive_name_, motor_input_is_on_);
+		observer->set_drive_motor_status(drive_name_, disk_is_rotating_);
 
 		if(add_motor_led) {
 			observer->register_led(drive_name_);
-			observer->set_led_status(drive_name_, motor_input_is_on_);
+			observer->set_led_status(drive_name_, disk_is_rotating_);
 		}
 	}
 }
diff --git a/Storage/Disk/Drive.hpp b/Storage/Disk/Drive.hpp
index d2b3f8a81..c568a399f 100644
--- a/Storage/Disk/Drive.hpp
+++ b/Storage/Disk/Drive.hpp
@@ -214,6 +214,9 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
 
 		// Motor control state.
 		bool motor_input_is_on_ = false;
+		bool disk_is_rotating_ = false;
+		Cycles time_until_motor_transition;
+		void set_disk_is_rotating(bool);
 
 		// Current state of the index pulse output.
 		Cycles index_pulse_remaining_;