From 74c18d7861df2439327c8304249b86d9042c6fbc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 30 Jul 2019 15:08:55 -0400 Subject: [PATCH] Attempts a full wiring up of 400kb drive speed. --- .../DiskII/MacintoshDoubleDensityDrive.cpp | 21 +++++++--- .../DiskII/MacintoshDoubleDensityDrive.hpp | 14 +++++++ .../Apple/Macintosh/DriveSpeedAccumulator.cpp | 38 +++++++++++++++++-- .../Apple/Macintosh/DriveSpeedAccumulator.hpp | 11 ++++++ Machines/Apple/Macintosh/Macintosh.cpp | 4 ++ 5 files changed, 78 insertions(+), 10 deletions(-) diff --git a/Components/DiskII/MacintoshDoubleDensityDrive.cpp b/Components/DiskII/MacintoshDoubleDensityDrive.cpp index 523217195..b41ac1570 100644 --- a/Components/DiskII/MacintoshDoubleDensityDrive.cpp +++ b/Components/DiskII/MacintoshDoubleDensityDrive.cpp @@ -23,7 +23,7 @@ DoubleDensityDrive::DoubleDensityDrive(int input_clock_rate, bool is_800k) : is_800k_(is_800k) { // Start with a valid rotation speed. if(is_800k) { - set_rotation_speed(393.3807f); + Drive::set_rotation_speed(393.3807f); } } @@ -51,15 +51,23 @@ void DoubleDensityDrive::did_step(Storage::Disk::HeadPosition to_position) { */ const int zone = to_position.as_int() >> 4; switch(zone) { - case 0: set_rotation_speed(393.3807f); break; - case 1: set_rotation_speed(429.1723f); break; - case 2: set_rotation_speed(472.1435f); break; - case 3: set_rotation_speed(524.5672f); break; - default: set_rotation_speed(590.1098f); break; + case 0: Drive::set_rotation_speed(393.3807f); break; + case 1: Drive::set_rotation_speed(429.1723f); break; + case 2: Drive::set_rotation_speed(472.1435f); break; + case 3: Drive::set_rotation_speed(524.5672f); break; + default: Drive::set_rotation_speed(590.1098f); break; } } } +void DoubleDensityDrive::set_rotation_speed(float revolutions_per_minute) { + if(!is_800k_) { + // Don't allow drive speeds to drop below 10 RPM, as a temporary sop + // to sanity. + Drive::set_rotation_speed(std::max(10.0f, revolutions_per_minute)); + } +} + // MARK: - Control input/output. void DoubleDensityDrive::set_enabled(bool) { @@ -136,6 +144,7 @@ bool DoubleDensityDrive::read() { case CA1|CA0|SEL: // Tachometer. // (arbitrary) +// printf("."); return get_tachometer(); case CA2: // Read data, lower head. diff --git a/Components/DiskII/MacintoshDoubleDensityDrive.hpp b/Components/DiskII/MacintoshDoubleDensityDrive.hpp index da409b6de..c1ac8803f 100644 --- a/Components/DiskII/MacintoshDoubleDensityDrive.hpp +++ b/Components/DiskII/MacintoshDoubleDensityDrive.hpp @@ -18,6 +18,20 @@ class DoubleDensityDrive: public IWMDrive { public: DoubleDensityDrive(int input_clock_rate, bool is_800k); + /*! + @returns @c true if this is an 800kb drive; @c false otherwise. + */ + bool is_800k() { + return is_800k_; + } + + /*! + Sets the current rotation speed of this drive only if it is a 400kb drive. + 800kb drives select their own rotation speed based on head position, + and ignore this input. + */ + void set_rotation_speed(float revolutions_per_minute); + void set_enabled(bool) override; void set_control_lines(int) override; bool read() override; diff --git a/Machines/Apple/Macintosh/DriveSpeedAccumulator.cpp b/Machines/Apple/Macintosh/DriveSpeedAccumulator.cpp index a9748449e..6ab4223a0 100644 --- a/Machines/Apple/Macintosh/DriveSpeedAccumulator.cpp +++ b/Machines/Apple/Macintosh/DriveSpeedAccumulator.cpp @@ -11,6 +11,8 @@ using namespace Apple::Macintosh; void DriveSpeedAccumulator::post_sample(uint8_t sample) { + if(!number_of_drives_) return; + // An Euler-esque approximation is used here: just collect all // the samples until there is a certain small quantity of them, // then produce a new estimate of rotation speed and start the @@ -20,9 +22,37 @@ void DriveSpeedAccumulator::post_sample(uint8_t sample) { if(sample_pointer_ == samples_.size()) { sample_pointer_ = 0; -// for(int c = 0; c < 512; c += 32) { -// printf("%u ", samples_[c]); -// } -// printf("\n"); + + // Treat 35 as a zero point and count zero crossings; then approximate + // the RPM from the frequency of those. + size_t first_crossing = 0, last_crossing = 0; + int number_of_crossings = 0; + + const uint8_t centre = 35; + bool was_over = samples_[0] > centre; + for(size_t c = 1; c < 512; ++c) { + bool is_over = samples_[c] > centre; + if(is_over != was_over) { + if(!first_crossing) first_crossing = c; + last_crossing = c; + ++number_of_crossings; + } + was_over = is_over; + } + + if(number_of_crossings) { + // The 654 multiplier here is a complete guess, based on preliminary + // observations of the values supplied and the known RPM selections of + // the 800kb drive. Updated values may be needed. + const float rotation_speed = 654.0f * float(number_of_crossings) / float(last_crossing - first_crossing); + for(int c = 0; c < number_of_drives_; ++c) { + drives_[c]->set_rotation_speed(rotation_speed); + } + } } } + +void DriveSpeedAccumulator::add_drive(Apple::Macintosh::DoubleDensityDrive *drive) { + drives_[number_of_drives_] = drive; + ++number_of_drives_; +} diff --git a/Machines/Apple/Macintosh/DriveSpeedAccumulator.hpp b/Machines/Apple/Macintosh/DriveSpeedAccumulator.hpp index 9da8fa3f1..1fab3b541 100644 --- a/Machines/Apple/Macintosh/DriveSpeedAccumulator.hpp +++ b/Machines/Apple/Macintosh/DriveSpeedAccumulator.hpp @@ -13,6 +13,8 @@ #include #include +#include "../../../Components/DiskII/MacintoshDoubleDensityDrive.hpp" + namespace Apple { namespace Macintosh { @@ -23,9 +25,18 @@ class DriveSpeedAccumulator { */ void post_sample(uint8_t sample); + /*! + Adds a connected drive. Up to two of these + can be supplied. Only Macintosh DoubleDensityDrives + are supported. + */ + void add_drive(Apple::Macintosh::DoubleDensityDrive *drive); + private: std::array samples_; std::size_t sample_pointer_ = 0; + Apple::Macintosh::DoubleDensityDrive *drives_[2] = {nullptr, nullptr}; + int number_of_drives_ = 0; }; } diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 190db046e..0146d13a2 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -116,6 +116,10 @@ template class ConcreteMachin iwm_.iwm.set_drive(0, &drives_[0]); iwm_.iwm.set_drive(1, &drives_[1]); + // If they are 400kb drives, also attach them to the drive-speed accumulator. + if(!drives_[0].is_800k()) drive_speed_accumulator_.add_drive(&drives_[0]); + if(!drives_[1].is_800k()) drive_speed_accumulator_.add_drive(&drives_[1]); + // Make sure interrupt changes from the SCC are observed. scc_.set_delegate(this);