diff --git a/Components/DiskII/IWM.cpp b/Components/DiskII/IWM.cpp index 5e2861911..385d53bdb 100644 --- a/Components/DiskII/IWM.cpp +++ b/Components/DiskII/IWM.cpp @@ -105,7 +105,7 @@ uint8_t IWM::read(int address) { bit 7: 1 = write data buffer ready for data. */ LOG("Reading write handshake"); - return 0x1f | 0x80 | 0x40; + return 0x1f | write_handshake_; } return 0xff; @@ -143,7 +143,7 @@ void IWM::write(int address, uint8_t input) { break; case Q7|Q6|ENABLE: // Write data register. - LOG("Data register write\n"); + LOG("Data register write"); break; } } @@ -233,24 +233,34 @@ void IWM::run_for(const Cycles cycles) { // } // Activity otherwise depends on mode and motor state. - const bool run_disk = drives_[active_drive_]; // drive_motor_on_ && int integer_cycles = cycles.as_int(); switch(state_ & (Q6 | Q7 | ENABLE)) { case 0: case ENABLE: // i.e. read mode. - while(integer_cycles--) { - if(run_disk) { + if(drive_is_rotating_[active_drive_]) { + while(integer_cycles--) { drives_[active_drive_]->run_for(Cycles(1)); + ++cycles_since_shift_; + if(cycles_since_shift_ == bit_length_ + Cycles(2)) { + propose_shift(0); + } } - ++cycles_since_shift_; - if(cycles_since_shift_ == bit_length_ + Cycles(2)) { + } else { + while(cycles_since_shift_ + integer_cycles >= bit_length_ + Cycles(2)) { propose_shift(0); + integer_cycles -= bit_length_.as_int() + 2 - cycles_since_shift_.as_int(); } + cycles_since_shift_ += Cycles(integer_cycles); } break; + case Q6|Q7: + case Q6|Q7|ENABLE: // write mode? + printf("IWM write mode?\n"); + break; + default: - if(run_disk) drives_[active_drive_]->run_for(cycles); + if(drive_is_rotating_[active_drive_]) drives_[active_drive_]->run_for(cycles); break; } } @@ -278,4 +288,15 @@ void IWM::propose_shift(uint8_t bit) { void IWM::set_drive(int slot, IWMDrive *drive) { drives_[slot] = drive; drive->set_event_delegate(this); + drive->set_clocking_hint_observer(this); +} + +void IWM::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) { + const bool is_rotating = clocking != ClockingHint::Preference::None; + + if(component == static_cast(drives_[0])) { + drive_is_rotating_[0] = is_rotating; + } else { + drive_is_rotating_[1] = is_rotating; + } } diff --git a/Components/DiskII/IWM.hpp b/Components/DiskII/IWM.hpp index b26fe142c..c9dee5219 100644 --- a/Components/DiskII/IWM.hpp +++ b/Components/DiskII/IWM.hpp @@ -10,6 +10,7 @@ #define IWM_hpp #include "../../ClockReceiver/ClockReceiver.hpp" +#include "../../ClockReceiver/ClockingHintSource.hpp" #include "../../Storage/Disk/Drive.hpp" #include @@ -37,11 +38,11 @@ struct IWMDrive: public Storage::Disk::Drive { virtual void set_enabled(bool) = 0; virtual void set_control_lines(int) = 0; virtual bool read() = 0; - virtual void write(bool value) = 0; }; class IWM: - public Storage::Disk::Drive::EventDelegate { + public Storage::Disk::Drive::EventDelegate, + public ClockingHint::Observer { public: IWM(int clock_rate); @@ -81,8 +82,12 @@ class IWM: int active_drive_ = 0; IWMDrive *drives_[2] = {nullptr, nullptr}; + bool drive_is_rotating_[2] = {false, false}; + + void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) override; Cycles cycles_until_motor_off_; + uint8_t write_handshake_ = 0xc0; void access(int address); diff --git a/Components/DiskII/MacintoshDoubleDensityDrive.cpp b/Components/DiskII/MacintoshDoubleDensityDrive.cpp index c47399503..7db79c2bc 100644 --- a/Components/DiskII/MacintoshDoubleDensityDrive.cpp +++ b/Components/DiskII/MacintoshDoubleDensityDrive.cpp @@ -8,6 +8,14 @@ #include "MacintoshDoubleDensityDrive.hpp" +/* + Sources used pervasively: + + http://members.iinet.net.au/~kalandi/apple/AUG/1991/11%20NOV.DEC/DISK.STUFF.html + Apple Guide to the Macintosh Family Hardware + Inside Macintosh III +*/ + using namespace Apple::Macintosh; DoubleDensityDrive::DoubleDensityDrive(int input_clock_rate, bool is_800k) : @@ -65,27 +73,27 @@ void DoubleDensityDrive::set_control_lines(int lines) { if((old_state ^ control_state_) & control_state_ & Line::LSTRB) { switch(control_state_ & (Line::CA1 | Line::CA0 | Line::SEL)) { default: -// LOG("Unhandled LSTRB"); break; - case 0: -// LOG("LSTRB Set stepping direction: " << int(state_ & CA2)); + case 0: // Set step direction — CA2 set => step outward. step_direction_ = (control_state_ & Line::CA2) ? -1 : 1; break; - case Line::CA1: -// LOG("LSTRB Motor"); + case Line::CA1: // Set drive motor — CA2 set => motor off. set_motor_on(!(control_state_ & Line::CA2)); break; - case Line::CA0: -// LOG("LSTRB Step"); - step(Storage::Disk::HeadPosition(step_direction_)); + case Line::CA0: // Initiate a step, if CA2 is clear. + if(!(control_state_ & Line::CA2)) + step(Storage::Disk::HeadPosition(step_direction_)); break; - case Line::CA1 | Line::CA0: -// LOG("LSTRB Eject disk"); - set_disk(nullptr); + case Line::SEL: // Reset has-been-ejected flag (if CA2 is set?) + break; + + case Line::CA1 | Line::CA0: // Eject the disk if CA2 is set. + if(control_state_ & Line::CA2) + set_disk(nullptr); // TODO: should probably trigger the disk has been ejected bit? break; } } @@ -94,75 +102,64 @@ void DoubleDensityDrive::set_control_lines(int lines) { bool DoubleDensityDrive::read() { switch(control_state_ & (CA2 | CA1 | CA0 | SEL)) { default: -// LOG("unknown)"); return false; - // Possible other meanings: - // B = ready (0 = ready) - // C = disk switched? - // - // {CA1,CA0,SEL,CA2} - case 0: // Head step direction. // (0 = inward) -// LOG("head step direction)"); return step_direction_ <= 0; case SEL: // Disk in place. // (0 = disk present) -// LOG("disk in place)"); return !has_disk(); case CA0: // Disk head step completed. // (0 = still stepping) -// LOG("head stepping)"); return true; // TODO: stepping delay. But at the main Drive level. case CA0|SEL: // Disk locked. // (0 = write protected) -// LOG("disk locked)"); return !get_is_read_only(); case CA1: // Disk motor running. // (0 = motor on) -// LOG("disk motor running)"); return !get_motor_on(); case CA1|SEL: // Head at track 0. // (0 = at track 0) -// LOG("head at track 0)"); + // "This bit becomes valid beginning 12 msec after the step that places the head at track 0." return !get_is_track_zero(); + case CA1|CA0: // Disk has been ejected. + // (0 = user has ejected disk) + return false; + case CA1|CA0|SEL: // Tachometer. // (arbitrary) return get_tachometer(); case CA2: // Read data, lower head. -// LOG("data, lower head)\n"); set_head(0); - return false;; + return false; case CA2|SEL: // Read data, upper head. -// LOG("data, upper head)\n"); set_head(1); return false; case CA2|CA1: // Single- or double-sided drive. // (0 = single sided) -// LOG("single- or double-sided drive)"); return get_head_count() != 1; case CA2|CA1|CA0: // "Present/HD" (per the Mac Plus ROM) // (0 = ??HD??) -// LOG("present/HD)"); + // + // Alternative explanation: "Disk ready for reading?" + // (0 = ready) return false; case CA2|CA1|CA0|SEL: // Drive installed. // (0 = present, 1 = missing) -// LOG("drive installed)"); + // + // TODO: why do I need to return this the wrong way around for the Mac Plus? return true; } } - -void DoubleDensityDrive::write(bool value) { -} diff --git a/Components/DiskII/MacintoshDoubleDensityDrive.hpp b/Components/DiskII/MacintoshDoubleDensityDrive.hpp index 7e070b7db..00665dcae 100644 --- a/Components/DiskII/MacintoshDoubleDensityDrive.hpp +++ b/Components/DiskII/MacintoshDoubleDensityDrive.hpp @@ -21,7 +21,6 @@ class DoubleDensityDrive: public IWMDrive { void set_enabled(bool) override; void set_control_lines(int) override; bool read() override; - void write(bool value) override; private: // To receive the proper notifications from Storage::Disk::Drive.