1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +00:00

Merge pull request #634 from TomHarte/OpenCrash

Resolves a potential crash at launch on the Mac
This commit is contained in:
Thomas Harte 2019-07-31 14:11:58 -04:00 committed by GitHub
commit 1ae3799aee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 11 deletions

View File

@ -23,13 +23,14 @@ 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);
}
}
// MARK: - Speed Selection
void DoubleDensityDrive::did_step(Storage::Disk::HeadPosition to_position) {
// printf("At track %d\n", to_position.as_int());
// The 800kb drive automatically selects rotation speed as a function of
// head position; the 400kb drive doesn't do so.
if(is_800k_) {
@ -51,15 +52,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) {

View File

@ -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;

View File

@ -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,31 @@ 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 33 as a zero point and count zero crossings; then approximate
// the RPM from the frequency of those.
int samples_over = 0;
int sum = 0;
const uint8_t centre = 33;
for(size_t c = 0; c < 512; ++c) {
if(samples_[c] > centre) ++ samples_over;
sum += samples_[c];
}
// TODO: if the above is the correct test, do it on sample receipt rather than
// bothering with an intermediate buffer.
// The below fits for a function like `a + bc`.
const float rotation_speed = (float(sum) * 0.052896440564137f) - 259.0f;
for(int c = 0; c < number_of_drives_; ++c) {
drives_[c]->set_rotation_speed(rotation_speed);
}
// printf("RPM: %0.2f (%d over; %d sum)\n", rotation_speed, samples_over, sum);
}
}
void DriveSpeedAccumulator::add_drive(Apple::Macintosh::DoubleDensityDrive *drive) {
drives_[number_of_drives_] = drive;
++number_of_drives_;
}

View File

@ -13,6 +13,8 @@
#include <cstddef>
#include <cstdint>
#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<uint8_t, 512> samples_;
std::size_t sample_pointer_ = 0;
Apple::Macintosh::DoubleDensityDrive *drives_[2] = {nullptr, nullptr};
int number_of_drives_ = 0;
};
}

View File

@ -116,6 +116,10 @@ template <Analyser::Static::Macintosh::Target::Model model> 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);

View File

@ -56,7 +56,16 @@ class MachineDocument:
override func windowControllerDidLoadNib(_ aController: NSWindowController) {
super.windowControllerDidLoadNib(aController)
aController.window?.contentAspectRatio = self.aspectRatio()
setupMachineOutput()
if self.machine != nil {
setupMachineOutput()
} else {
// This is somewhat of a desperate workaround; just having loaded the Nib doesn't
// mean that the window is visible yet, but presenting the ROM import sheet before
// the window is visible will result in it being free floating.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(500)) {
self.configureAs(self.selectedMachine!)
}
}
}
// Attempting to show a sheet before the window is visible (such as when the NIB is loaded) results in
@ -340,6 +349,11 @@ class MachineDocument:
@IBOutlet var romReceiverView: CSROMReceiverView?
private var romRequestBaseText = ""
func requestRoms() {
// Don't act yet if there's no window controller yet.
if self.windowControllers.count == 0 {
return
}
// Load the ROM requester dialogue.
Bundle.main.loadNibNamed("ROMRequester", owner: self, topLevelObjects: nil)
self.romReceiverView!.delegate = self