mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +00:00
Strips Controller of all capabilities now housed on the Drive.
This commit is contained in:
parent
523e1288fa
commit
0622187ddf
@ -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;
|
||||
}
|
||||
|
||||
|
@ -236,9 +236,8 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> 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];
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -147,7 +147,7 @@ class MachineBase:
|
||||
MOS::MOS6522::MOS6522<SerialPortVIA> 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();
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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> 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_++;
|
||||
}
|
||||
|
@ -7,43 +7,19 @@
|
||||
//
|
||||
|
||||
#include "DiskController.hpp"
|
||||
#include "UnformattedTrack.hpp"
|
||||
|
||||
#include "../../NumberTheory/Factors.hpp"
|
||||
#include <cassert>
|
||||
|
||||
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<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
|
||||
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> 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() {}
|
||||
|
@ -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> 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<DigitalPhaseLockedLoop> pll_;
|
||||
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);
|
||||
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();
|
||||
std::shared_ptr<Drive> 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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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<Storage::Disk::Disk> &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<Sector> 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<uint8_t> 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_++;
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user