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:
parent
523e1288fa
commit
0622187ddf
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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];
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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_++;
|
||||||
}
|
}
|
||||||
|
@ -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() {}
|
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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_++;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user