1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-25 16:31:42 +00:00

Strips Controller of all capabilities now housed on the Drive.

This commit is contained in:
Thomas Harte 2017-09-10 19:23:23 -04:00
parent 523e1288fa
commit 0622187ddf
16 changed files with 124 additions and 295 deletions

View File

@ -75,7 +75,7 @@ uint8_t WD1770::get_register(int address) {
switch(status_.type) { switch(status_.type) {
case Status::One: case Status::One:
status |= status |=
(get_is_track_zero() ? Flag::TrackZero : 0) | (get_drive().get_is_track_zero() ? Flag::TrackZero : 0) |
(status_.seek_error ? Flag::SeekError : 0); (status_.seek_error ? Flag::SeekError : 0);
// TODO: index hole // TODO: index hole
break; break;
@ -91,11 +91,11 @@ uint8_t WD1770::get_register(int address) {
} }
if(!has_motor_on_line()) { 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) if(status_.type == Status::One)
status |= (head_is_loaded_ ? Flag::HeadLoaded : 0); status |= (head_is_loaded_ ? Flag::HeadLoaded : 0);
} else { } else {
status |= (get_motor_on() ? Flag::MotorOn : 0); status |= (get_drive().get_motor_on() ? Flag::MotorOn : 0);
if(status_.type == Status::One) if(status_.type == Status::One)
status |= (status_.spin_up ? Flag::SpinUp : 0); 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 LINE_LABEL INDIRECT_CONCATENATE(label, __LINE__)
#define SPIN_UP() \ #define SPIN_UP() \
set_motor_on(true); \ get_drive().set_motor_on(true); \
index_hole_count_ = 0; \ index_hole_count_ = 0; \
index_hole_count_target_ = 6; \ index_hole_count_target_ = 6; \
WAIT_FOR_EVENT(Event1770::IndexHoleTarget); \ WAIT_FOR_EVENT(Event1770::IndexHoleTarget); \
@ -178,7 +178,7 @@ void WD1770::posit_event(int new_event_type) {
// motor power-down // motor power-down
if(index_hole_count_ == 9 && !status_.busy && has_motor_on_line()) { if(index_hole_count_ == 9 && !status_.busy && has_motor_on_line()) {
set_motor_on(false); get_drive().set_motor_on(false);
} }
// head unload // head unload
@ -257,7 +257,7 @@ void WD1770::posit_event(int new_event_type) {
goto test_type1_type; goto test_type1_type;
begin_type1_spin_up: 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(); SPIN_UP();
test_type1_type: test_type1_type:
@ -280,11 +280,11 @@ void WD1770::posit_event(int new_event_type) {
if(step_direction_) track_++; else track_--; if(step_direction_) track_++; else track_--;
perform_step: perform_step:
if(!step_direction_ && get_is_track_zero()) { if(!step_direction_ && get_drive().get_is_track_zero()) {
track_ = 0; track_ = 0;
goto verify; goto verify;
} }
step(step_direction_ ? 1 : -1); get_drive().step(step_direction_ ? 1 : -1);
unsigned int time_to_wait; unsigned int time_to_wait;
switch(command_ & 3) { switch(command_ & 3) {
default: default:
@ -376,7 +376,7 @@ void WD1770::posit_event(int new_event_type) {
goto test_type2_delay; goto test_type2_delay;
begin_type2_spin_up: 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. // Perform spin up.
SPIN_UP(); SPIN_UP();
@ -386,7 +386,7 @@ void WD1770::posit_event(int new_event_type) {
WAIT_FOR_TIME(30); WAIT_FOR_TIME(30);
test_type2_write_protection: 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) { update_status([] (Status &status) {
status.write_protect = true; status.write_protect = true;
}); });
@ -541,7 +541,7 @@ void WD1770::posit_event(int new_event_type) {
}); });
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT(Event::DataWritten);
if(status_.data_request) { if(status_.data_request) {
end_writing(); get_drive().end_writing();
update_status([] (Status &status) { update_status([] (Status &status) {
status.lost_data = true; status.lost_data = true;
}); });
@ -554,7 +554,7 @@ void WD1770::posit_event(int new_event_type) {
write_crc(); write_crc();
write_byte(0xff); write_byte(0xff);
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT(Event::DataWritten);
end_writing(); get_drive().end_writing();
if(command_ & 0x10) { if(command_ & 0x10) {
sector_++; sector_++;
@ -594,7 +594,7 @@ void WD1770::posit_event(int new_event_type) {
goto type3_test_delay; goto type3_test_delay;
begin_type3_spin_up: 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(); SPIN_UP();
type3_test_delay: type3_test_delay:
@ -675,7 +675,7 @@ void WD1770::posit_event(int new_event_type) {
}); });
write_track_test_write_protect: write_track_test_write_protect:
if(get_drive_is_read_only()) { if(get_drive().get_is_read_only()) {
update_status([] (Status &status) { update_status([] (Status &status) {
status.write_protect = true; status.write_protect = true;
}); });
@ -755,11 +755,11 @@ void WD1770::posit_event(int new_event_type) {
update_status([] (Status &status) { update_status([] (Status &status) {
status.lost_data = true; status.lost_data = true;
}); });
end_writing(); get_drive().end_writing();
goto wait_for_command; goto wait_for_command;
} }
if(index_hole_count_) { if(index_hole_count_) {
end_writing(); get_drive().end_writing();
goto wait_for_command; goto wait_for_command;
} }

View File

@ -236,9 +236,8 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
active_drive_ = command_[1]&3; \ active_drive_ = command_[1]&3; \
active_head_ = (command_[1] >> 2)&1; \ active_head_ = (command_[1] >> 2)&1; \
set_drive(drives_[active_drive_].drive); \ set_drive(drives_[active_drive_].drive); \
drives_[active_drive_].drive->set_head((unsigned int)active_head_); \ get_drive().set_head((unsigned int)active_head_); \
set_is_double_density(command_[0] & 0x40); \ set_is_double_density(command_[0] & 0x40);
invalidate_track();
#define WAIT_FOR_BYTES(n) \ #define WAIT_FOR_BYTES(n) \
distance_into_section_ = 0; \ distance_into_section_ = 0; \
@ -527,7 +526,7 @@ void i8272::posit_event(int event_type) {
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT(Event::DataWritten);
if(!has_input_) { if(!has_input_) {
SetOverrun(); SetOverrun();
end_writing(); get_drive().end_writing();
goto abort; goto abort;
} }
write_byte(input_); write_byte(input_);
@ -542,7 +541,7 @@ void i8272::posit_event(int event_type) {
write_crc(); write_crc();
expects_input_ = false; expects_input_ = false;
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT(Event::DataWritten);
end_writing(); get_drive().end_writing();
if(sector_ != command_[6]) { if(sector_ != command_[6]) {
sector_++; sector_++;
@ -648,7 +647,7 @@ void i8272::posit_event(int event_type) {
switch(event_type) { switch(event_type) {
case (int)Event::IndexHole: case (int)Event::IndexHole:
SetOverrun(); SetOverrun();
end_writing(); get_drive().end_writing();
goto abort; goto abort;
break; break;
case (int)Event::DataWritten: case (int)Event::DataWritten:
@ -685,7 +684,7 @@ void i8272::posit_event(int event_type) {
WAIT_FOR_EVENT((int)Event::DataWritten | (int)Event::IndexHole); WAIT_FOR_EVENT((int)Event::DataWritten | (int)Event::IndexHole);
if(event_type != (int)Event::IndexHole) goto format_track_pad; if(event_type != (int)Event::IndexHole) goto format_track_pad;
end_writing(); get_drive().end_writing();
cylinder_ = header_[0]; cylinder_ = header_[0];
head_ = header_[1]; head_ = header_[1];

View File

@ -587,7 +587,8 @@ class FDC: public Intel::i8272::i8272 {
FDC() : i8272(bus_handler_, Cycles(8000000), 16, 300) {} FDC() : i8272(bus_handler_, Cycles(8000000), 16, 300) {}
void set_motor_on(bool on) { 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);
} }
}; };

View File

@ -91,7 +91,7 @@ void Machine::run_for(const Cycles cycles) {
m6502_.run_for(cycles); m6502_.run_for(cycles);
bool drive_motor = drive_VIA_port_handler_.get_motor_enabled(); 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) if(drive_motor)
Storage::Disk::Controller::run_for(cycles); Storage::Disk::Controller::run_for(cycles);
} }
@ -105,7 +105,7 @@ void MachineBase::mos6522_did_change_interrupt_status(void *mos6522) {
#pragma mark - Disk drive #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; shift_register_ = (shift_register_ << 1) | value;
if((shift_register_ & 0x3ff) == 0x3ff) { if((shift_register_ & 0x3ff) == 0x3ff) {
drive_VIA_port_handler_.set_sync_detected(true); drive_VIA_port_handler_.set_sync_detected(true);
@ -130,7 +130,7 @@ void MachineBase::process_index_hole() {}
#pragma mak - Drive VIA delegate #pragma mak - Drive VIA delegate
void MachineBase::drive_via_did_step_head(void *driveVIA, int direction) { 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) { void MachineBase::drive_via_did_set_data_density(void *driveVIA, int density) {

View File

@ -147,7 +147,7 @@ class MachineBase:
MOS::MOS6522::MOS6522<SerialPortVIA> serial_port_VIA_; MOS::MOS6522::MOS6522<SerialPortVIA> serial_port_VIA_;
int shift_register_, bit_window_offset_; 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(); virtual void process_index_hole();
}; };

View File

@ -42,7 +42,6 @@ void Plus3::set_control_register(uint8_t control, uint8_t changes) {
} }
} }
if(changes & 0x04) { if(changes & 0x04) {
invalidate_track();
if(drives_[0]) drives_[0]->set_head((control & 0x04) ? 1 : 0); if(drives_[0]) drives_[0]->set_head((control & 0x04) ? 1 : 0);
if(drives_[1]) drives_[1]->set_head((control & 0x04) ? 1 : 0); if(drives_[1]) drives_[1]->set_head((control & 0x04) ? 1 : 0);
} }

View File

@ -95,7 +95,7 @@ uint8_t Microdisc::get_data_request_register() {
} }
void Microdisc::set_head_load_request(bool head_load) { void Microdisc::set_head_load_request(bool head_load) {
set_motor_on(head_load); get_drive().set_motor_on(head_load);
if(head_load) { if(head_load) {
head_load_request_counter_ = 0; head_load_request_counter_ = 0;
} else { } else {

View File

@ -24,7 +24,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
CommodoreGCRParser() : Storage::Disk::Controller(4000000, 1, 300), shift_register_(0), track_(1) { CommodoreGCRParser() : Storage::Disk::Controller(4000000, 1, 300), shift_register_(0), track_(1) {
drive.reset(new Storage::Disk::Drive(4000000, 300)); drive.reset(new Storage::Disk::Drive(4000000, 300));
set_drive(drive); set_drive(drive);
set_motor_on(true); get_drive().set_motor_on(true);
} }
struct Sector { struct Sector {
@ -47,7 +47,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
int direction = difference < 0 ? -1 : 1; int direction = difference < 0 ? -1 : 1;
difference *= 2 * direction; 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; unsigned int zone = 3;
if(track >= 18) zone = 2; if(track >= 18) zone = 2;
@ -66,7 +66,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
uint8_t track_; uint8_t track_;
std::shared_ptr<Sector> sector_cache_[65536]; std::shared_ptr<Sector> 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; shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0x3ff;
bit_count_++; bit_count_++;
} }

View File

@ -7,43 +7,19 @@
// //
#include "DiskController.hpp" #include "DiskController.hpp"
#include "UnformattedTrack.hpp"
#include "../../NumberTheory/Factors.hpp" #include "../../NumberTheory/Factors.hpp"
#include <cassert>
using namespace Storage::Disk; using namespace Storage::Disk;
Controller::Controller(Cycles clock_rate, int clock_rate_multiplier, int revolutions_per_minute) : Controller::Controller(Cycles clock_rate, int clock_rate_multiplier, int revolutions_per_minute) :
clock_rate_(clock_rate.as_int() * clock_rate_multiplier), clock_rate_(clock_rate.as_int() * clock_rate_multiplier),
clock_rate_multiplier_(clock_rate_multiplier), clock_rate_multiplier_(clock_rate_multiplier),
rotational_multiplier_(60, revolutions_per_minute), empty_drive_(new Drive((unsigned int)clock_rate.as_int(), 1)) {
cycles_since_index_hole_(0),
motor_is_on_(false),
is_reading_(true),
TimedEventLoop((unsigned int)(clock_rate.as_int() * clock_rate_multiplier)) {
// seed this class with a PLL, any PLL, so that it's safe to assume non-nullptr later // seed this class with a PLL, any PLL, so that it's safe to assume non-nullptr later
Time one(1); Time one(1);
set_expected_bit_length(one); set_expected_bit_length(one);
} set_drive(empty_drive_);
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);
} }
void Controller::set_component_is_sleeping(void *component, bool is_sleeping) { 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() { bool Controller::is_sleeping() {
return !(drive_ && drive_->has_disk() && motor_is_on_); return !drive_ || drive_->is_sleeping();
} }
void Controller::run_for(const Cycles cycles) { void Controller::run_for(const Cycles cycles) {
Time zero(0); if(drive_) drive_->run_for(cycles);
}
if(drive_ && drive_->has_disk() && motor_is_on_) { Drive &Controller::get_drive() {
if(!track_) setup_track(); return *drive_.get();
}
int number_of_cycles = clock_rate_multiplier_ * cycles.as_int(); #pragma mark - Drive::EventDelegate
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);
}
cycles_since_index_hole_ += (unsigned int)cycles_to_run_for; void Controller::process_event(const Track::Event &event) {
switch(event.type) {
number_of_cycles -= cycles_to_run_for; case Track::Event::FluxTransition: pll_->add_pulse(); break;
if(is_reading_) { case Track::Event::IndexHole: process_index_hole(); break;
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));
}
} }
} }
#pragma mark - Track timed event loop void Controller::advance(const Cycles cycles) {
pll_->run_for(cycles);
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::process_next_event() void Controller::process_write_completed() {
{ // Provided for subclasses to override.
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<PCMPatchedTrack>(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
} }
#pragma mark - PLL control and delegate #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_ = bit_length;
bit_length_.simplify(); bit_length_.simplify();
cycles_per_bit_ = Storage::Time(clock_rate_) * bit_length; Time cycles_per_bit = Storage::Time(clock_rate_) * bit_length;
cycles_per_bit_.simplify(); cycles_per_bit.simplify();
// this conversion doesn't need to be exact because there's a lot of variation to be taken // 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 // 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_.reset(new DigitalPhaseLockedLoop(clocks_per_bit, 3));
pll_->set_delegate(this); pll_->set_delegate(this);
} }
void Controller::digital_phase_locked_loop_output_bit(int value) { void Controller::digital_phase_locked_loop_output_bit(int value) {
process_input_bit(value, (unsigned int)cycles_since_index_hole_); process_input_bit(value);
}
#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_;
} }
void Controller::set_drive(std::shared_ptr<Drive> drive) { void Controller::set_drive(std::shared_ptr<Drive> drive) {
if(drive_ != 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_ = drive;
drive->set_sleep_observer(this); if(drive_) {
update_sleep_observer(); 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() { void Controller::begin_writing(bool clamp_to_index_hole) {
track_ = nullptr; get_drive().begin_writing(bit_length_, clamp_to_index_hole);
if(patched_track_) {
drive_->set_track(patched_track_);
patched_track_ = nullptr;
}
} }
void Controller::process_write_completed() {}

View File

@ -13,7 +13,6 @@
#include "DigitalPhaseLockedLoop.hpp" #include "DigitalPhaseLockedLoop.hpp"
#include "PCMSegment.hpp" #include "PCMSegment.hpp"
#include "PCMPatchedTrack.hpp" #include "PCMPatchedTrack.hpp"
#include "../TimedEventLoop.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp" #include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../ClockReceiver/Sleeper.hpp" #include "../../ClockReceiver/Sleeper.hpp"
@ -30,7 +29,7 @@ namespace Disk {
TODO: communication of head size and permissible stepping extents, appropriate simulation of gain. 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: protected:
/*! /*!
Constructs a @c DiskDrive that will be run at @c clock_rate and runs its PLL at @c clock_rate*clock_rate_multiplier, 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); 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> drive); void set_drive(std::shared_ptr<Drive> 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(); virtual void process_input_bit(int value) = 0;
/*!
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;
/*! /*!
Should be implemented by subclasses; communicates that the index hole has been reached. 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(); 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 @param clamp_to_index_hole If @c true then writing will automatically be truncated by
void digital_phase_locked_loop_output_bit(int value); 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); Returns the connected drive or, if none is connected, an invented one. No guarantees are
virtual bool get_drive_is_ready(); made about the lifetime or the exclusivity of the invented drive.
bool get_drive_is_read_only(); */
Drive &get_drive();
bool is_sleeping(); bool is_sleeping();
@ -127,26 +92,17 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
std::shared_ptr<DigitalPhaseLockedLoop> pll_; std::shared_ptr<DigitalPhaseLockedLoop> pll_;
std::shared_ptr<Drive> drive_; std::shared_ptr<Drive> drive_;
std::shared_ptr<Track> track_;
int cycles_since_index_hole_;
inline void get_next_event(const Time &duration_already_passed); std::shared_ptr<Drive> empty_drive_;
Track::Event current_event_;
bool motor_is_on_;
bool is_reading_;
bool clamp_writing_to_index_hole_;
std::shared_ptr<PCMPatchedTrack> 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();
void set_component_is_sleeping(void *component, bool is_sleeping); 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);
}; };
} }

View File

@ -83,6 +83,14 @@ void Drive::set_motor_on(bool motor_is_on) {
update_sleep_observer(); 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) { void Drive::run_for(const Cycles cycles) {
Time zero(0); Time zero(0);
@ -178,6 +186,10 @@ void Drive::setup_track() {
void Drive::invalidate_track() { void Drive::invalidate_track() {
track_ = nullptr; track_ = nullptr;
if(patched_track_) {
set_track(patched_track_);
patched_track_ = nullptr;
}
} }
#pragma mark - Writing #pragma mark - Writing
@ -186,6 +198,9 @@ void Drive::begin_writing(Time bit_length, bool clamp_to_index_hole) {
is_reading_ = false; is_reading_ = false;
clamp_writing_to_index_hole_ = clamp_to_index_hole; 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_.length_of_a_bit = bit_length / rotational_multiplier_;
write_segment_.data.clear(); write_segment_.data.clear();
write_segment_.number_of_bits = 0; write_segment_.number_of_bits = 0;

View File

@ -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 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. 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. /// 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. /// Sets the current event delegate.

View File

@ -246,7 +246,7 @@ Parser::Parser(bool is_mfm) :
drive_.reset(new Storage::Disk::Drive(4000000, 300)); drive_.reset(new Storage::Disk::Drive(4000000, 300));
set_drive(drive_); set_drive(drive_);
set_motor_on(true); drive_->set_motor_on(true);
} }
Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Disk> &disk) : Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Disk> &disk) :
@ -267,7 +267,7 @@ void Parser::seek_to_track(uint8_t track) {
int direction = difference < 0 ? -1 : 1; int direction = difference < 0 ? -1 : 1;
difference *= direction; 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<Sector> Parser::get_sector(uint8_t head, uint8_t track, uint8_t
// Switch head and track if necessary. // Switch head and track if necessary.
if(head_ != head) { if(head_ != head) {
drive_->set_head(head); drive_->set_head(head);
invalidate_track();
} }
seek_to_track(track); seek_to_track(track);
int track_index = get_index(head, track, 0); int track_index = get_index(head, track, 0);
@ -315,7 +314,7 @@ std::vector<uint8_t> Parser::get_track(uint8_t track) {
return get_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; shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0xffff;
bit_count_++; bit_count_++;
} }

View File

@ -129,7 +129,7 @@ class Parser: public Storage::Disk::Controller {
bool is_mfm_; bool is_mfm_;
void seek_to_track(uint8_t track); 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(); void process_index_hole();
uint8_t get_next_byte(); uint8_t get_next_byte();

View File

@ -53,7 +53,7 @@ NumberTheory::CRC16 &MFMController::get_crc_generator() {
return 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; if(data_mode_ == DataMode::Writing) return;
shift_register_ = (shift_register_ << 1) | value; 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) { void MFMController::write_bit(int bit) {
if(is_double_density_) { if(is_double_density_) {
Controller::write_bit(!bit && !last_bit_); get_drive().write_bit(!bit && !last_bit_);
Controller::write_bit(!!bit); get_drive().write_bit(!!bit);
last_bit_ = bit; last_bit_ = bit;
} else { } else {
Controller::write_bit(true); get_drive().write_bit(true);
Controller::write_bit(!!bit); get_drive().write_bit(!!bit);
} }
} }
@ -172,7 +172,7 @@ void MFMController::write_byte(uint8_t byte) {
void MFMController::write_raw_short(uint16_t value) { void MFMController::write_raw_short(uint16_t value) {
for(int c = 0; c < 16; c++) { for(int c = 0; c < 16; c++) {
Controller::write_bit(!!((value << c)&0x8000)); get_drive().write_bit(!!((value << c)&0x8000));
} }
} }

View File

@ -146,7 +146,7 @@ class MFMController: public Controller {
private: private:
// Storage::Disk::Controller // 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_index_hole();
virtual void process_write_completed(); virtual void process_write_completed();