diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 5d90db872..6b640c4e2 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -14,6 +14,7 @@ #include "DriveSpeedAccumulator.hpp" #include "Keyboard.hpp" #include "RealTimeClock.hpp" +#include "SonyDrive.hpp" #include "Video.hpp" #include "../../CRTMachine.hpp" @@ -47,7 +48,11 @@ template class ConcreteMachin iwm_(CLOCK_RATE), video_(ram_, audio_, drive_speed_accumulator_), via_(via_port_handler_), - via_port_handler_(*this, clock_, keyboard_, video_, audio_, iwm_) { + via_port_handler_(*this, clock_, keyboard_, video_, audio_, iwm_), + drives_{ + {CLOCK_RATE, model >= Analyser::Static::Macintosh::Target::Model::Mac512ke}, + {CLOCK_RATE, model >= Analyser::Static::Macintosh::Target::Model::Mac512ke} + } { // Select a ROM name and determine the proper ROM and RAM sizes // based on the machine model. @@ -85,6 +90,10 @@ template class ConcreteMachin roms[0]->resize(rom_size); Memory::PackBigEndian16(*roms[0], rom_); + // Attach the drives to the IWM. + iwm_.iwm.set_drive(0, &drives_[0]); + iwm_.iwm.set_drive(1, &drives_[1]); + // The Mac runs at 7.8336mHz. set_clock_rate(double(CLOCK_RATE)); audio_.speaker.set_input_rate(float(CLOCK_RATE)); @@ -437,6 +446,8 @@ template class ConcreteMachin bool ROM_is_overlay_ = true; int phase_ = 1; + SonyDrive drives_[2]; + uint32_t ram_mask_ = 0; uint32_t rom_mask_ = 0; uint16_t rom_[64*1024]; diff --git a/Machines/Apple/Macintosh/SonyDrive.cpp b/Machines/Apple/Macintosh/SonyDrive.cpp new file mode 100644 index 000000000..b691cc8f6 --- /dev/null +++ b/Machines/Apple/Macintosh/SonyDrive.cpp @@ -0,0 +1,32 @@ +// +// SonyDrive.cpp +// Clock Signal +// +// Created by Thomas Harte on 04/06/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#include "SonyDrive.hpp" + +using namespace Apple::Macintosh; + +SonyDrive::SonyDrive(int input_clock_rate, bool is_800k) : + Storage::Disk::Drive(static_cast(input_clock_rate), is_800k ? 2 : 1), is_800k_(is_800k) {} + +void SonyDrive::did_step(Storage::Disk::HeadPosition to_position) { + // The 800kb drive automatically selects rotation speed as a function of + // head position; the 400kb drive doesn't do so. + if(is_800k_) { + /* + Numbers below cribbed from the Kryoflux forums. + */ + 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; + } + } +} diff --git a/Machines/Apple/Macintosh/SonyDrive.hpp b/Machines/Apple/Macintosh/SonyDrive.hpp new file mode 100644 index 000000000..d6df0cd16 --- /dev/null +++ b/Machines/Apple/Macintosh/SonyDrive.hpp @@ -0,0 +1,34 @@ +// +// SonyDrive.hpp +// Clock Signal +// +// Created by Thomas Harte on 04/06/2019. +// Copyright © 2019 Thomas Harte. All rights reserved. +// + +#ifndef SonyDrive_hpp +#define SonyDrive_hpp + +#include "../../../Storage/Disk/Drive.hpp" + +namespace Apple { +namespace Macintosh { + +/*! + Models one of the Sony drives found in an original Macintosh, + specifically by providing automatic motor speed adjustment if + this is an 800kb drive. +*/ +class SonyDrive: public Storage::Disk::Drive { + public: + SonyDrive(int input_clock_rate, bool is_800k); + + private: + void did_step(Storage::Disk::HeadPosition to_position) override; + bool is_800k_; +}; + +} +} + +#endif /* SonyDrive_hpp */ diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index 99fba1182..2e6a107ee 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -36,6 +36,14 @@ Drive::Drive(unsigned int input_clock_rate, int revolutions_per_minute, int numb } } +Drive::Drive(unsigned int input_clock_rate, int number_of_heads) : Drive(input_clock_rate, 300, number_of_heads) {} + +void Drive::set_rotation_speed(float revolutions_per_minute) { + // TODO: probably I should look into + // whether doing all this with quotients is really a good idea. + rotational_multiplier_ = Time(60.0f / revolutions_per_minute); +} + Drive::~Drive() { if(disk_) disk_->flush_tracks(); } @@ -75,6 +83,9 @@ void Drive::step(HeadPosition offset) { if(head_position_ != old_head_position) { track_ = nullptr; } + + // Allow a subclass to react, if desired. + did_step(head_position_); } std::shared_ptr Drive::step_to(HeadPosition offset) { @@ -97,6 +108,10 @@ void Drive::set_head(int head) { } } +int Drive::get_head_count() { + return available_heads_; +} + Storage::Time Drive::get_time_into_track() { // `result` will initially be amount of time since the index hole was seen as a // proportion of a second; convert it into proportion of a rotation, simplify and return. diff --git a/Storage/Disk/Drive.hpp b/Storage/Disk/Drive.hpp index 02713ea37..e86bad566 100644 --- a/Storage/Disk/Drive.hpp +++ b/Storage/Disk/Drive.hpp @@ -54,6 +54,11 @@ class Drive: public ClockingHint::Source, public TimedEventLoop { */ void set_head(int head); + /*! + Gets the head count for this disk. + */ + int get_head_count(); + /*! @returns @c true if the inserted disk is read-only or no disk is inserted; @c false otherwise. */ @@ -139,7 +144,13 @@ class Drive: public ClockingHint::Source, public TimedEventLoop { */ std::shared_ptr step_to(HeadPosition offset); - void set(Time); + /*! + Alters the rotational velocity of this drive. + */ + void set_rotation_speed(float revolutions_per_minute); + + protected: + virtual void did_step(HeadPosition to_position) {} private: // Drives contain an entire disk; from that a certain track