From 0622187ddf4fc01390a65077b3dcd663959b82ea Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 10 Sep 2017 19:23:23 -0400 Subject: [PATCH] Strips Controller of all capabilities now housed on the Drive. --- Components/1770/1770.cpp | 32 +-- Components/8272/i8272.cpp | 13 +- Machines/AmstradCPC/AmstradCPC.cpp | 3 +- .../Commodore/1540/Implementation/C1540.cpp | 6 +- .../1540/Implementation/C1540Base.hpp | 2 +- Machines/Electron/Plus3.cpp | 1 - Machines/Oric/Microdisc.cpp | 2 +- StaticAnalyser/Commodore/Disk.cpp | 6 +- Storage/Disk/DiskController.cpp | 222 ++++-------------- Storage/Disk/DiskController.hpp | 90 ++----- Storage/Disk/Drive.cpp | 15 ++ Storage/Disk/Drive.hpp | 4 +- Storage/Disk/Encodings/MFM.cpp | 7 +- Storage/Disk/Encodings/MFM.hpp | 2 +- Storage/Disk/MFMDiskController.cpp | 12 +- Storage/Disk/MFMDiskController.hpp | 2 +- 16 files changed, 124 insertions(+), 295 deletions(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index 45545f7ef..5852d0b4e 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -75,7 +75,7 @@ uint8_t WD1770::get_register(int address) { switch(status_.type) { case Status::One: status |= - (get_is_track_zero() ? Flag::TrackZero : 0) | + (get_drive().get_is_track_zero() ? Flag::TrackZero : 0) | (status_.seek_error ? Flag::SeekError : 0); // TODO: index hole break; @@ -91,11 +91,11 @@ uint8_t WD1770::get_register(int address) { } if(!has_motor_on_line()) { - status |= get_drive_is_ready() ? 0 : Flag::NotReady; + status |= get_drive().get_is_ready() ? 0 : Flag::NotReady; if(status_.type == Status::One) status |= (head_is_loaded_ ? Flag::HeadLoaded : 0); } else { - status |= (get_motor_on() ? Flag::MotorOn : 0); + status |= (get_drive().get_motor_on() ? Flag::MotorOn : 0); if(status_.type == Status::One) status |= (status_.spin_up ? Flag::SpinUp : 0); } @@ -145,7 +145,7 @@ void WD1770::run_for(const Cycles cycles) { #define LINE_LABEL INDIRECT_CONCATENATE(label, __LINE__) #define SPIN_UP() \ - set_motor_on(true); \ + get_drive().set_motor_on(true); \ index_hole_count_ = 0; \ index_hole_count_target_ = 6; \ WAIT_FOR_EVENT(Event1770::IndexHoleTarget); \ @@ -178,7 +178,7 @@ void WD1770::posit_event(int new_event_type) { // motor power-down if(index_hole_count_ == 9 && !status_.busy && has_motor_on_line()) { - set_motor_on(false); + get_drive().set_motor_on(false); } // head unload @@ -257,7 +257,7 @@ void WD1770::posit_event(int new_event_type) { goto test_type1_type; begin_type1_spin_up: - if((command_&0x08) || get_motor_on()) goto test_type1_type; + if((command_&0x08) || get_drive().get_motor_on()) goto test_type1_type; SPIN_UP(); test_type1_type: @@ -280,11 +280,11 @@ void WD1770::posit_event(int new_event_type) { if(step_direction_) track_++; else track_--; perform_step: - if(!step_direction_ && get_is_track_zero()) { + if(!step_direction_ && get_drive().get_is_track_zero()) { track_ = 0; goto verify; } - step(step_direction_ ? 1 : -1); + get_drive().step(step_direction_ ? 1 : -1); unsigned int time_to_wait; switch(command_ & 3) { default: @@ -376,7 +376,7 @@ void WD1770::posit_event(int new_event_type) { goto test_type2_delay; begin_type2_spin_up: - if(get_motor_on()) goto test_type2_delay; + if(get_drive().get_motor_on()) goto test_type2_delay; // Perform spin up. SPIN_UP(); @@ -386,7 +386,7 @@ void WD1770::posit_event(int new_event_type) { WAIT_FOR_TIME(30); test_type2_write_protection: - if(command_&0x20 && get_drive_is_read_only()) { + if(command_&0x20 && get_drive().get_is_read_only()) { update_status([] (Status &status) { status.write_protect = true; }); @@ -541,7 +541,7 @@ void WD1770::posit_event(int new_event_type) { }); WAIT_FOR_EVENT(Event::DataWritten); if(status_.data_request) { - end_writing(); + get_drive().end_writing(); update_status([] (Status &status) { status.lost_data = true; }); @@ -554,7 +554,7 @@ void WD1770::posit_event(int new_event_type) { write_crc(); write_byte(0xff); WAIT_FOR_EVENT(Event::DataWritten); - end_writing(); + get_drive().end_writing(); if(command_ & 0x10) { sector_++; @@ -594,7 +594,7 @@ void WD1770::posit_event(int new_event_type) { goto type3_test_delay; begin_type3_spin_up: - if((command_&0x08) || get_motor_on()) goto type3_test_delay; + if((command_&0x08) || get_drive().get_motor_on()) goto type3_test_delay; SPIN_UP(); type3_test_delay: @@ -675,7 +675,7 @@ void WD1770::posit_event(int new_event_type) { }); write_track_test_write_protect: - if(get_drive_is_read_only()) { + if(get_drive().get_is_read_only()) { update_status([] (Status &status) { status.write_protect = true; }); @@ -755,11 +755,11 @@ void WD1770::posit_event(int new_event_type) { update_status([] (Status &status) { status.lost_data = true; }); - end_writing(); + get_drive().end_writing(); goto wait_for_command; } if(index_hole_count_) { - end_writing(); + get_drive().end_writing(); goto wait_for_command; } diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 664bf54b8..437533249 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -236,9 +236,8 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { active_drive_ = command_[1]&3; \ active_head_ = (command_[1] >> 2)&1; \ set_drive(drives_[active_drive_].drive); \ - drives_[active_drive_].drive->set_head((unsigned int)active_head_); \ - set_is_double_density(command_[0] & 0x40); \ - invalidate_track(); + get_drive().set_head((unsigned int)active_head_); \ + set_is_double_density(command_[0] & 0x40); #define WAIT_FOR_BYTES(n) \ distance_into_section_ = 0; \ @@ -527,7 +526,7 @@ void i8272::posit_event(int event_type) { WAIT_FOR_EVENT(Event::DataWritten); if(!has_input_) { SetOverrun(); - end_writing(); + get_drive().end_writing(); goto abort; } write_byte(input_); @@ -542,7 +541,7 @@ void i8272::posit_event(int event_type) { write_crc(); expects_input_ = false; WAIT_FOR_EVENT(Event::DataWritten); - end_writing(); + get_drive().end_writing(); if(sector_ != command_[6]) { sector_++; @@ -648,7 +647,7 @@ void i8272::posit_event(int event_type) { switch(event_type) { case (int)Event::IndexHole: SetOverrun(); - end_writing(); + get_drive().end_writing(); goto abort; break; case (int)Event::DataWritten: @@ -685,7 +684,7 @@ void i8272::posit_event(int event_type) { WAIT_FOR_EVENT((int)Event::DataWritten | (int)Event::IndexHole); if(event_type != (int)Event::IndexHole) goto format_track_pad; - end_writing(); + get_drive().end_writing(); cylinder_ = header_[0]; head_ = header_[1]; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 0244ddb41..34cf8d650 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -587,7 +587,8 @@ class FDC: public Intel::i8272::i8272 { FDC() : i8272(bus_handler_, Cycles(8000000), 16, 300) {} void set_motor_on(bool on) { - Intel::i8272::i8272::set_motor_on(on); + // TODO: should set all motors on, not just the one active drive. + get_drive().set_motor_on(on); } }; diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index 6f3ded472..abe95e663 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -91,7 +91,7 @@ void Machine::run_for(const Cycles cycles) { m6502_.run_for(cycles); bool drive_motor = drive_VIA_port_handler_.get_motor_enabled(); - set_motor_on(drive_motor); + get_drive().set_motor_on(drive_motor); if(drive_motor) Storage::Disk::Controller::run_for(cycles); } @@ -105,7 +105,7 @@ void MachineBase::mos6522_did_change_interrupt_status(void *mos6522) { #pragma mark - Disk drive -void MachineBase::process_input_bit(int value, unsigned int cycles_since_index_hole) { +void MachineBase::process_input_bit(int value) { shift_register_ = (shift_register_ << 1) | value; if((shift_register_ & 0x3ff) == 0x3ff) { drive_VIA_port_handler_.set_sync_detected(true); @@ -130,7 +130,7 @@ void MachineBase::process_index_hole() {} #pragma mak - Drive VIA delegate void MachineBase::drive_via_did_step_head(void *driveVIA, int direction) { - step(direction); + get_drive().step(direction); } void MachineBase::drive_via_did_set_data_density(void *driveVIA, int density) { diff --git a/Machines/Commodore/1540/Implementation/C1540Base.hpp b/Machines/Commodore/1540/Implementation/C1540Base.hpp index d08165ea7..f96d4b464 100644 --- a/Machines/Commodore/1540/Implementation/C1540Base.hpp +++ b/Machines/Commodore/1540/Implementation/C1540Base.hpp @@ -147,7 +147,7 @@ class MachineBase: MOS::MOS6522::MOS6522 serial_port_VIA_; int shift_register_, bit_window_offset_; - virtual void process_input_bit(int value, unsigned int cycles_since_index_hole); + virtual void process_input_bit(int value); virtual void process_index_hole(); }; diff --git a/Machines/Electron/Plus3.cpp b/Machines/Electron/Plus3.cpp index e0e12bb61..d79057839 100644 --- a/Machines/Electron/Plus3.cpp +++ b/Machines/Electron/Plus3.cpp @@ -42,7 +42,6 @@ void Plus3::set_control_register(uint8_t control, uint8_t changes) { } } if(changes & 0x04) { - invalidate_track(); if(drives_[0]) drives_[0]->set_head((control & 0x04) ? 1 : 0); if(drives_[1]) drives_[1]->set_head((control & 0x04) ? 1 : 0); } diff --git a/Machines/Oric/Microdisc.cpp b/Machines/Oric/Microdisc.cpp index 90c82cc48..d04b5c832 100644 --- a/Machines/Oric/Microdisc.cpp +++ b/Machines/Oric/Microdisc.cpp @@ -95,7 +95,7 @@ uint8_t Microdisc::get_data_request_register() { } void Microdisc::set_head_load_request(bool head_load) { - set_motor_on(head_load); + get_drive().set_motor_on(head_load); if(head_load) { head_load_request_counter_ = 0; } else { diff --git a/StaticAnalyser/Commodore/Disk.cpp b/StaticAnalyser/Commodore/Disk.cpp index 63221d758..d9349b762 100644 --- a/StaticAnalyser/Commodore/Disk.cpp +++ b/StaticAnalyser/Commodore/Disk.cpp @@ -24,7 +24,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller { CommodoreGCRParser() : Storage::Disk::Controller(4000000, 1, 300), shift_register_(0), track_(1) { drive.reset(new Storage::Disk::Drive(4000000, 300)); set_drive(drive); - set_motor_on(true); + get_drive().set_motor_on(true); } struct Sector { @@ -47,7 +47,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller { int direction = difference < 0 ? -1 : 1; difference *= 2 * direction; - for(int c = 0; c < difference; c++) step(direction); + for(int c = 0; c < difference; c++) get_drive().step(direction); unsigned int zone = 3; if(track >= 18) zone = 2; @@ -66,7 +66,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller { uint8_t track_; std::shared_ptr sector_cache_[65536]; - void process_input_bit(int value, unsigned int cycles_since_index_hole) { + void process_input_bit(int value) { shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0x3ff; bit_count_++; } diff --git a/Storage/Disk/DiskController.cpp b/Storage/Disk/DiskController.cpp index 772750e88..4b2ee7a9e 100644 --- a/Storage/Disk/DiskController.cpp +++ b/Storage/Disk/DiskController.cpp @@ -7,43 +7,19 @@ // #include "DiskController.hpp" -#include "UnformattedTrack.hpp" + #include "../../NumberTheory/Factors.hpp" -#include using namespace Storage::Disk; Controller::Controller(Cycles clock_rate, int clock_rate_multiplier, int revolutions_per_minute) : clock_rate_(clock_rate.as_int() * clock_rate_multiplier), clock_rate_multiplier_(clock_rate_multiplier), - rotational_multiplier_(60, revolutions_per_minute), - - cycles_since_index_hole_(0), - motor_is_on_(false), - - is_reading_(true), - - TimedEventLoop((unsigned int)(clock_rate.as_int() * clock_rate_multiplier)) { + empty_drive_(new Drive((unsigned int)clock_rate.as_int(), 1)) { // seed this class with a PLL, any PLL, so that it's safe to assume non-nullptr later Time one(1); set_expected_bit_length(one); -} - -void Controller::setup_track() { - track_ = drive_->get_track(); - if(!track_) { - track_.reset(new UnformattedTrack); - } - - Time offset; - Time track_time_now = get_time_into_track(); - assert(track_time_now >= Time(0) && current_event_.length <= Time(1)); - - Time time_found = track_->seek_to(track_time_now); - assert(time_found >= Time(0) && time_found <= track_time_now); - offset = track_time_now - time_found; - - get_next_event(offset); + set_drive(empty_drive_); } void Controller::set_component_is_sleeping(void *component, bool is_sleeping) { @@ -51,125 +27,32 @@ void Controller::set_component_is_sleeping(void *component, bool is_sleeping) { } bool Controller::is_sleeping() { - return !(drive_ && drive_->has_disk() && motor_is_on_); + return !drive_ || drive_->is_sleeping(); } void Controller::run_for(const Cycles cycles) { - Time zero(0); + if(drive_) drive_->run_for(cycles); +} - if(drive_ && drive_->has_disk() && motor_is_on_) { - if(!track_) setup_track(); +Drive &Controller::get_drive() { + return *drive_.get(); +} - int number_of_cycles = clock_rate_multiplier_ * cycles.as_int(); - while(number_of_cycles) { - int cycles_until_next_event = (int)get_cycles_until_next_event(); - int cycles_to_run_for = std::min(cycles_until_next_event, number_of_cycles); - if(!is_reading_ && cycles_until_bits_written_ > zero) { - int write_cycles_target = (int)cycles_until_bits_written_.get_unsigned_int(); - if(cycles_until_bits_written_.length % cycles_until_bits_written_.clock_rate) write_cycles_target++; - cycles_to_run_for = std::min(cycles_to_run_for, write_cycles_target); - } +#pragma mark - Drive::EventDelegate - cycles_since_index_hole_ += (unsigned int)cycles_to_run_for; - - number_of_cycles -= cycles_to_run_for; - if(is_reading_) { - pll_->run_for(Cycles(cycles_to_run_for)); - } else { - if(cycles_until_bits_written_ > zero) { - Storage::Time cycles_to_run_for_time(cycles_to_run_for); - if(cycles_until_bits_written_ <= cycles_to_run_for_time) { - process_write_completed(); - if(cycles_until_bits_written_ <= cycles_to_run_for_time) - cycles_until_bits_written_.set_zero(); - else - cycles_until_bits_written_ -= cycles_to_run_for_time; - } else { - cycles_until_bits_written_ -= cycles_to_run_for_time; - } - } - } - TimedEventLoop::run_for(Cycles(cycles_to_run_for)); - } +void Controller::process_event(const Track::Event &event) { + switch(event.type) { + case Track::Event::FluxTransition: pll_->add_pulse(); break; + case Track::Event::IndexHole: process_index_hole(); break; } } -#pragma mark - Track timed event loop - -void Controller::get_next_event(const Time &duration_already_passed) { - if(track_) { - current_event_ = track_->get_next_event(); - } else { - current_event_.length.length = 1; - current_event_.length.clock_rate = 1; - current_event_.type = Track::Event::IndexHole; - } - - // divide interval, which is in terms of a single rotation of the disk, by rotation speed to - // convert it into revolutions per second; this is achieved by multiplying by rotational_multiplier_ - assert(current_event_.length <= Time(1) && current_event_.length >= Time(0)); - Time interval = (current_event_.length - duration_already_passed) * rotational_multiplier_; - set_next_event_time_interval(interval); +void Controller::advance(const Cycles cycles) { + pll_->run_for(cycles); } -void Controller::process_next_event() -{ - switch(current_event_.type) { - case Track::Event::FluxTransition: - if(is_reading_) pll_->add_pulse(); - break; - case Track::Event::IndexHole: -// printf("%p %d [/%d = %d]\n", this, cycles_since_index_hole_, clock_rate_multiplier_, cycles_since_index_hole_ / clock_rate_multiplier_); - cycles_since_index_hole_ = 0; - process_index_hole(); - break; - } - get_next_event(Time(0)); -} - -Storage::Time Controller::get_time_into_track() { - // this is proportion of a second - Time result(cycles_since_index_hole_, 8000000 * clock_rate_multiplier_); - result /= rotational_multiplier_; - result.simplify(); - return result; -} - -#pragma mark - Writing - -void Controller::begin_writing(bool clamp_to_index_hole) { - is_reading_ = false; - clamp_writing_to_index_hole_ = clamp_to_index_hole; - - write_segment_.length_of_a_bit = bit_length_ / rotational_multiplier_; - write_segment_.data.clear(); - write_segment_.number_of_bits = 0; - - write_start_time_ = get_time_into_track(); -} - -void Controller::write_bit(bool value) { - bool needs_new_byte = !(write_segment_.number_of_bits&7); - if(needs_new_byte) write_segment_.data.push_back(0); - if(value) write_segment_.data[write_segment_.number_of_bits >> 3] |= 0x80 >> (write_segment_.number_of_bits & 7); - write_segment_.number_of_bits++; - - cycles_until_bits_written_ += cycles_per_bit_; -} - -void Controller::end_writing() { - is_reading_ = true; - - if(!patched_track_) { - // Avoid creating a new patched track if this one is already patched - patched_track_ = std::dynamic_pointer_cast(track_); - if(!patched_track_) { - patched_track_.reset(new PCMPatchedTrack(track_)); - } - } - patched_track_->add_segment(write_start_time_, write_segment_, clamp_writing_to_index_hole_); - cycles_since_index_hole_ %= 8000000 * clock_rate_multiplier_; - invalidate_track(); // TEMPORARY: to force a seek +void Controller::process_write_completed() { + // Provided for subclasses to override. } #pragma mark - PLL control and delegate @@ -178,66 +61,43 @@ void Controller::set_expected_bit_length(Time bit_length) { bit_length_ = bit_length; bit_length_.simplify(); - cycles_per_bit_ = Storage::Time(clock_rate_) * bit_length; - cycles_per_bit_.simplify(); + Time cycles_per_bit = Storage::Time(clock_rate_) * bit_length; + cycles_per_bit.simplify(); // this conversion doesn't need to be exact because there's a lot of variation to be taken // account of in rotation speed, air turbulence, etc, so a direct conversion will do - int clocks_per_bit = (int)cycles_per_bit_.get_unsigned_int(); + int clocks_per_bit = (int)cycles_per_bit.get_unsigned_int(); pll_.reset(new DigitalPhaseLockedLoop(clocks_per_bit, 3)); pll_->set_delegate(this); } void Controller::digital_phase_locked_loop_output_bit(int value) { - process_input_bit(value, (unsigned int)cycles_since_index_hole_); -} - -#pragma mark - Drive actions - -bool Controller::get_is_track_zero() { - if(!drive_) return false; - return drive_->get_is_track_zero(); -} - -bool Controller::get_drive_is_ready() { - if(!drive_) return false; - return drive_->has_disk(); -} - -bool Controller::get_drive_is_read_only() { - if(!drive_) return false; - return drive_->get_is_read_only(); -} - -void Controller::step(int direction) { - invalidate_track(); - if(drive_) drive_->step(direction); -} - -void Controller::set_motor_on(bool motor_on) { - motor_is_on_ = motor_on; - update_sleep_observer(); -} - -bool Controller::get_motor_on() { - return motor_is_on_; + process_input_bit(value); } void Controller::set_drive(std::shared_ptr drive) { if(drive_ != drive) { - invalidate_track(); + bool was_sleeping = is_sleeping(); +// invalidate_track(); + + if(drive_) { + drive_->set_event_delegate(nullptr); + drive_->set_sleep_observer(nullptr); + } drive_ = drive; - drive->set_sleep_observer(this); - update_sleep_observer(); + if(drive_) { + drive_->set_event_delegate(this); + drive_->set_sleep_observer(this); + } else { + drive_ = empty_drive_; + } + + if(is_sleeping() != was_sleeping) { + update_sleep_observer(); + } } } -void Controller::invalidate_track() { - track_ = nullptr; - if(patched_track_) { - drive_->set_track(patched_track_); - patched_track_ = nullptr; - } +void Controller::begin_writing(bool clamp_to_index_hole) { + get_drive().begin_writing(bit_length_, clamp_to_index_hole); } - -void Controller::process_write_completed() {} diff --git a/Storage/Disk/DiskController.hpp b/Storage/Disk/DiskController.hpp index 4d35eaecc..2f6585729 100644 --- a/Storage/Disk/DiskController.hpp +++ b/Storage/Disk/DiskController.hpp @@ -13,7 +13,6 @@ #include "DigitalPhaseLockedLoop.hpp" #include "PCMSegment.hpp" #include "PCMPatchedTrack.hpp" -#include "../TimedEventLoop.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" #include "../../ClockReceiver/Sleeper.hpp" @@ -30,7 +29,7 @@ namespace Disk { TODO: communication of head size and permissible stepping extents, appropriate simulation of gain. */ -class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop, public Sleeper, public Sleeper::SleepObserver { +class Controller: public DigitalPhaseLockedLoop::Delegate, public Drive::EventDelegate, public Sleeper, public Sleeper::SleepObserver { protected: /*! Constructs a @c DiskDrive that will be run at @c clock_rate and runs its PLL at @c clock_rate*clock_rate_multiplier, @@ -49,51 +48,14 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop void run_for(const Cycles cycles); /*! - Sets the current drive. + Sets the current drive. This drive is the one the PLL listens to. */ void set_drive(std::shared_ptr drive); /*! - Announces that the track the drive sees is about to change for a reason unknownt to the controller. + Should be implemented by subclasses; communicates each bit that the PLL recognises. */ - void invalidate_track(); - - /*! - Enables or disables the disk motor. - */ - void set_motor_on(bool motor_on); - - /*! - @returns @c true if the motor is on; @c false otherwise. - */ - bool get_motor_on(); - - /*! - Begins write mode, initiating a PCM sampled region of data. Bits should be written via - @c write_bit. They will be written with the length set via @c set_expected_bit_length. - It is acceptable to supply a backlog of bits. Flux transition events will not be reported - while writing. - - @param clamp_to_index_hole If @c true then writing will automatically be truncated by - the index hole. Writing will continue over the index hole otherwise. - */ - void begin_writing(bool clamp_to_index_hole); - - /*! - Writes the bit @c value as the next in the PCM stream initiated by @c begin_writing. - */ - void write_bit(bool value); - - /*! - Ends write mode, switching back to read mode. The drive will stop overwriting events. - */ - void end_writing(); - - /*! - Should be implemented by subclasses; communicates each bit that the PLL recognises, also specifying - the amount of time since the index hole was last seen. - */ - virtual void process_input_bit(int value, unsigned int cycles_since_index_hole) = 0; + virtual void process_input_bit(int value) = 0; /*! Should be implemented by subclasses; communicates that the index hole has been reached. @@ -106,16 +68,19 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop */ virtual void process_write_completed(); - // for TimedEventLoop - virtual void process_next_event(); + /*! + Puts the drive returned by get_drive() into write mode, supplying the current bit length. - // to satisfy DigitalPhaseLockedLoop::Delegate - void digital_phase_locked_loop_output_bit(int value); + @param clamp_to_index_hole If @c true then writing will automatically be truncated by + the index hole. Writing will continue over the index hole otherwise. + */ + void begin_writing(bool clamp_to_index_hole); - bool get_is_track_zero(); - void step(int direction); - virtual bool get_drive_is_ready(); - bool get_drive_is_read_only(); + /*! + Returns the connected drive or, if none is connected, an invented one. No guarantees are + made about the lifetime or the exclusivity of the invented drive. + */ + Drive &get_drive(); bool is_sleeping(); @@ -127,26 +92,17 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop std::shared_ptr pll_; std::shared_ptr drive_; - std::shared_ptr track_; - int cycles_since_index_hole_; - inline void get_next_event(const Time &duration_already_passed); - Track::Event current_event_; - bool motor_is_on_; - - bool is_reading_; - bool clamp_writing_to_index_hole_; - std::shared_ptr patched_track_; - PCMSegment write_segment_; - Time write_start_time_; - - Time cycles_until_bits_written_; - Time cycles_per_bit_; - - void setup_track(); - Time get_time_into_track(); + std::shared_ptr empty_drive_; void set_component_is_sleeping(void *component, bool is_sleeping); + + // for Drive::EventDelegate + void process_event(const Track::Event &event); + void advance(const Cycles cycles); + + // to satisfy DigitalPhaseLockedLoop::Delegate + void digital_phase_locked_loop_output_bit(int value); }; } diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index 47f5d1750..6e12dc70f 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -83,6 +83,14 @@ void Drive::set_motor_on(bool motor_is_on) { update_sleep_observer(); } +bool Drive::get_motor_on() { + return motor_is_on_; +} + +void Drive::set_event_delegate(Storage::Disk::Drive::EventDelegate *delegate) { + event_delegate_ = delegate; +} + void Drive::run_for(const Cycles cycles) { Time zero(0); @@ -178,6 +186,10 @@ void Drive::setup_track() { void Drive::invalidate_track() { track_ = nullptr; + if(patched_track_) { + set_track(patched_track_); + patched_track_ = nullptr; + } } #pragma mark - Writing @@ -186,6 +198,9 @@ void Drive::begin_writing(Time bit_length, bool clamp_to_index_hole) { is_reading_ = false; clamp_writing_to_index_hole_ = clamp_to_index_hole; + cycles_per_bit_ = Storage::Time(get_input_clock_rate()) * bit_length; + cycles_per_bit_.simplify(); + write_segment_.length_of_a_bit = bit_length / rotational_multiplier_; write_segment_.data.clear(); write_segment_.number_of_bits = 0; diff --git a/Storage/Disk/Drive.hpp b/Storage/Disk/Drive.hpp index 97f12ed49..b11d23fb6 100644 --- a/Storage/Disk/Drive.hpp +++ b/Storage/Disk/Drive.hpp @@ -109,10 +109,10 @@ class Drive: public Sleeper, public TimedEventLoop { If the drive is in write mode, announces that all queued bits have now been written. If the controller provides further bits now then there will be no gap in written data. */ - virtual void process_write_completed() {} + virtual void process_write_completed() = 0; /// Informs the delegate of the passing of @c cycles. - virtual void advance(const Cycles cycles) {} + virtual void advance(const Cycles cycles) = 0; }; /// Sets the current event delegate. diff --git a/Storage/Disk/Encodings/MFM.cpp b/Storage/Disk/Encodings/MFM.cpp index d9544f3b5..6a03d5ab2 100644 --- a/Storage/Disk/Encodings/MFM.cpp +++ b/Storage/Disk/Encodings/MFM.cpp @@ -246,7 +246,7 @@ Parser::Parser(bool is_mfm) : drive_.reset(new Storage::Disk::Drive(4000000, 300)); set_drive(drive_); - set_motor_on(true); + drive_->set_motor_on(true); } Parser::Parser(bool is_mfm, const std::shared_ptr &disk) : @@ -267,7 +267,7 @@ void Parser::seek_to_track(uint8_t track) { int direction = difference < 0 ? -1 : 1; difference *= direction; - for(int c = 0; c < difference; c++) step(direction); + for(int c = 0; c < difference; c++) drive_->step(direction); } } @@ -275,7 +275,6 @@ std::shared_ptr Parser::get_sector(uint8_t head, uint8_t track, uint8_t // Switch head and track if necessary. if(head_ != head) { drive_->set_head(head); - invalidate_track(); } seek_to_track(track); int track_index = get_index(head, track, 0); @@ -315,7 +314,7 @@ std::vector Parser::get_track(uint8_t track) { return get_track(); } -void Parser::process_input_bit(int value, unsigned int cycles_since_index_hole) { +void Parser::process_input_bit(int value) { shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0xffff; bit_count_++; } diff --git a/Storage/Disk/Encodings/MFM.hpp b/Storage/Disk/Encodings/MFM.hpp index 2ae651555..be521f2a2 100644 --- a/Storage/Disk/Encodings/MFM.hpp +++ b/Storage/Disk/Encodings/MFM.hpp @@ -129,7 +129,7 @@ class Parser: public Storage::Disk::Controller { bool is_mfm_; void seek_to_track(uint8_t track); - void process_input_bit(int value, unsigned int cycles_since_index_hole); + void process_input_bit(int value); void process_index_hole(); uint8_t get_next_byte(); diff --git a/Storage/Disk/MFMDiskController.cpp b/Storage/Disk/MFMDiskController.cpp index 1ae81df9a..63d3e2ac0 100644 --- a/Storage/Disk/MFMDiskController.cpp +++ b/Storage/Disk/MFMDiskController.cpp @@ -53,7 +53,7 @@ NumberTheory::CRC16 &MFMController::get_crc_generator() { return crc_generator_; } -void MFMController::process_input_bit(int value, unsigned int cycles_since_index_hole) { +void MFMController::process_input_bit(int value) { if(data_mode_ == DataMode::Writing) return; shift_register_ = (shift_register_ << 1) | value; @@ -156,12 +156,12 @@ void MFMController::process_input_bit(int value, unsigned int cycles_since_index void MFMController::write_bit(int bit) { if(is_double_density_) { - Controller::write_bit(!bit && !last_bit_); - Controller::write_bit(!!bit); + get_drive().write_bit(!bit && !last_bit_); + get_drive().write_bit(!!bit); last_bit_ = bit; } else { - Controller::write_bit(true); - Controller::write_bit(!!bit); + get_drive().write_bit(true); + get_drive().write_bit(!!bit); } } @@ -172,7 +172,7 @@ void MFMController::write_byte(uint8_t byte) { void MFMController::write_raw_short(uint16_t value) { for(int c = 0; c < 16; c++) { - Controller::write_bit(!!((value << c)&0x8000)); + get_drive().write_bit(!!((value << c)&0x8000)); } } diff --git a/Storage/Disk/MFMDiskController.hpp b/Storage/Disk/MFMDiskController.hpp index 206246ba2..bcd96854c 100644 --- a/Storage/Disk/MFMDiskController.hpp +++ b/Storage/Disk/MFMDiskController.hpp @@ -146,7 +146,7 @@ class MFMController: public Controller { private: // Storage::Disk::Controller - virtual void process_input_bit(int value, unsigned int cycles_since_index_hole); + virtual void process_input_bit(int value); virtual void process_index_hole(); virtual void process_write_completed();