1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-27 01:29:31 +00:00

Attempts to pull drive ownership into DiskController.

For the sake of being more intelligent as to drive clocking, hopefully. And, eventually, to support multiple drive selection.
This commit is contained in:
Thomas Harte 2020-02-11 21:59:13 -05:00
parent 654f5b0478
commit 05bcd73f82
17 changed files with 157 additions and 138 deletions

View File

@ -22,8 +22,8 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
std::shared_ptr<Storage::Disk::Drive> drive;
CommodoreGCRParser() : Storage::Disk::Controller(4000000), shift_register_(0), track_(1) {
drive = std::make_shared<Storage::Disk::Drive>(4000000, 300, 2);
set_drive(drive);
emplace_drive(4000000, 300, 2);
set_drive(1);
drive->set_motor_on(true);
}

View File

@ -661,17 +661,15 @@ class KeyboardState: public GI::AY38910::PortHandler {
class FDC: public Intel::i8272::i8272 {
private:
Intel::i8272::BusHandler bus_handler_;
std::shared_ptr<Storage::Disk::Drive> drive_;
public:
FDC() :
i8272(bus_handler_, Cycles(8000000)),
drive_(new Storage::Disk::Drive(8000000, 300, 1)) {
set_drive(drive_);
FDC() : i8272(bus_handler_, Cycles(8000000)) {
emplace_drive(8000000, 300, 1);
set_drive(1);
}
void set_motor_on(bool on) {
drive_->set_motor_on(on);
get_drive().set_motor_on(on);
}
void select_drive(int c) {
@ -679,11 +677,11 @@ class FDC: public Intel::i8272::i8272 {
}
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
drive_->set_disk(disk);
get_drive().set_disk(disk);
}
void set_activity_observer(Activity::Observer *observer) {
drive_->set_activity_observer(observer, "Drive 1", true);
get_drive().set_activity_observer(observer, "Drive 1", true);
}
};

View File

@ -126,7 +126,7 @@ void DMAController::set_floppy_drive_selection(bool drive1, bool drive2, bool si
}
void DMAController::set_floppy_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
fdc_.drives_[drive]->set_disk(disk);
fdc_.set_disk(disk, drive);
}
void DMAController::run_for(HalfCycles duration) {
@ -256,6 +256,5 @@ ClockingHint::Preference DMAController::preferred_clocking() {
}
void DMAController::set_activity_observer(Activity::Observer *observer) {
fdc_.drives_[0]->set_activity_observer(observer, "Internal", true);
fdc_.drives_[1]->set_activity_observer(observer, "External", true);
fdc_.set_activity_observer(observer);
}

View File

@ -56,30 +56,36 @@ class DMAController: public WD::WD1770::Delegate, public ClockingHint::Source, p
HalfCycles running_time_;
struct WD1772: public WD::WD1770 {
WD1772(): WD::WD1770(WD::WD1770::P1772) {
drives_.emplace_back(new Storage::Disk::Drive(8000000, 300, 2));
drives_.emplace_back(new Storage::Disk::Drive(8000000, 300, 2));
set_drive(drives_[0]);
emplace_drives(2, 8000000, 300, 2);
set_is_double_density(true); // TODO: is this selectable on the ST?
}
void set_motor_on(bool motor_on) final {
drives_[0]->set_motor_on(motor_on);
drives_[1]->set_motor_on(motor_on);
for_all_drives([motor_on] (Storage::Disk::Drive &drive, size_t) {
drive.set_motor_on(motor_on);
});
}
void set_floppy_drive_selection(bool drive1, bool drive2, bool side2) {
// TODO: handle no drives and/or both drives selected.
if(drive1) {
set_drive(drives_[0]);
} else {
set_drive(drives_[1]);
}
set_drive(
(drive1 ? 1 : 0) |
(drive2 ? 2 : 0)
);
drives_[0]->set_head(side2);
drives_[1]->set_head(side2);
for_all_drives([side2] (Storage::Disk::Drive &drive, size_t) {
drive.set_head(side2);
});
}
void set_activity_observer(Activity::Observer *observer) {
get_drive(0).set_activity_observer(observer, "Internal", true);
get_drive(1).set_activity_observer(observer, "External", true);
}
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
get_drive(drive).set_disk(disk);
}
std::vector<std::shared_ptr<Storage::Disk::Drive>> drives_;
} fdc_;
void wd1770_did_change_output(WD::WD1770 *) final;

View File

@ -19,7 +19,6 @@ using namespace Commodore::C1540;
MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &rom_fetcher) :
Storage::Disk::Controller(1000000),
m6502_(*this),
drive_(new Storage::Disk::Drive(1000000, 300, 2)),
serial_port_VIA_port_handler_(new SerialPortVIA(serial_port_VIA_)),
serial_port_(new SerialPort),
drive_VIA_(drive_VIA_port_handler_),
@ -37,7 +36,8 @@ MachineBase::MachineBase(Personality personality, const ROMMachine::ROMFetcher &
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(3));
// attach the only drive there is
set_drive(drive_);
emplace_drive(1000000, 300, 2);
set_drive(1);
std::string device_name;
uint32_t crc = 0;
@ -103,21 +103,21 @@ Cycles MachineBase::perform_bus_operation(CPU::MOS6502::BusOperation operation,
}
void Machine::set_disk(std::shared_ptr<Storage::Disk::Disk> disk) {
drive_->set_disk(disk);
get_drive().set_disk(disk);
}
void Machine::run_for(const Cycles cycles) {
m6502_.run_for(cycles);
bool drive_motor = drive_VIA_port_handler_.get_motor_enabled();
drive_->set_motor_on(drive_motor);
const bool drive_motor = drive_VIA_port_handler_.get_motor_enabled();
get_drive().set_motor_on(drive_motor);
if(drive_motor)
Storage::Disk::Controller::run_for(cycles);
}
void MachineBase::set_activity_observer(Activity::Observer *observer) {
drive_VIA_.bus_handler().set_activity_observer(observer);
drive_->set_activity_observer(observer, "Drive", false);
get_drive().set_activity_observer(observer, "Drive", false);
}
// MARK: - 6522 delegate
@ -154,7 +154,7 @@ void MachineBase::process_index_hole() {}
// MARK: - Drive VIA delegate
void MachineBase::drive_via_did_step_head(void *driveVIA, int direction) {
drive_->step(Storage::Disk::HeadPosition(direction, 2));
get_drive().step(Storage::Disk::HeadPosition(direction, 2));
}
void MachineBase::drive_via_did_set_data_density(void *driveVIA, int density) {

View File

@ -144,7 +144,6 @@ class MachineBase:
protected:
CPU::MOS6502::Processor<CPU::MOS6502::Personality::P6502, MachineBase, false> m6502_;
std::shared_ptr<Storage::Disk::Drive> drive_;
uint8_t ram_[0x800];
uint8_t rom_[0x4000];

View File

@ -11,13 +11,12 @@
using namespace Electron;
Plus3::Plus3() : WD1770(P1770) {
drives_.emplace_back(new Storage::Disk::Drive(8000000, 300, 2));
drives_.emplace_back(new Storage::Disk::Drive(8000000, 300, 2));
emplace_drives(2, 8000000, 300, 2);
set_control_register(last_control_, 0xff);
}
void Plus3::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
drives_[drive]->set_disk(disk);
get_drive(drive).set_disk(disk);
}
void Plus3::set_control_register(uint8_t control) {
@ -33,16 +32,15 @@ void Plus3::set_control_register(uint8_t control) {
void Plus3::set_control_register(uint8_t control, uint8_t changes) {
if(changes&3) {
switch(control&3) {
case 0: selected_drive_ = -1; set_drive(nullptr); break;
default: selected_drive_ = 0; set_drive(drives_[0]); break;
case 2: selected_drive_ = 1; set_drive(drives_[1]); break;
}
set_drive(control&3);
}
// Select the side on both drives at once.
if(changes & 0x04) {
drives_[0]->set_head((control & 0x04) ? 1 : 0);
drives_[1]->set_head((control & 0x04) ? 1 : 0);
get_drive(0).set_head((control & 0x04) ? 1 : 0);
get_drive(1).set_head((control & 0x04) ? 1 : 0);
}
if(changes & 0x08) set_is_double_density(!(control & 0x08));
}
@ -53,9 +51,7 @@ void Plus3::set_motor_on(bool on) {
}
void Plus3::set_activity_observer(Activity::Observer *observer) {
size_t index = 0;
for(const auto &drive: drives_) {
drive->set_activity_observer(observer, "Drive " + std::to_string(index+1), true);
++index;
}
for_all_drives([observer] (Storage::Disk::Drive &drive, size_t index) {
drive.set_activity_observer(observer, "Drive " + std::to_string(index+1), true);
});
}

View File

@ -24,8 +24,6 @@ class Plus3 : public WD::WD1770 {
private:
void set_control_register(uint8_t control, uint8_t changes);
std::vector<std::shared_ptr<Storage::Disk::Drive>> drives_;
int selected_drive_ = 0;
uint8_t last_control_ = 0;
void set_motor_on(bool on);

View File

@ -13,8 +13,7 @@ using namespace MSX;
DiskROM::DiskROM(const std::vector<uint8_t> &rom) :
WD1770(P1793),
rom_(rom) {
drives_[0] = std::make_shared<Storage::Disk::Drive>(8000000, 300, 2);
drives_[1] = std::make_shared<Storage::Disk::Drive>(8000000, 300, 2);
emplace_drives(2, 8000000, 300, 2);
set_is_double_density(true);
}
@ -23,18 +22,19 @@ void DiskROM::write(uint16_t address, uint8_t value, bool pc_is_outside_bios) {
case 0x7ff8: case 0x7ff9: case 0x7ffa: case 0x7ffb:
WD::WD1770::write(address, value);
break;
case 0x7ffc:
selected_head_ = value & 1;
drives_[0]->set_head(selected_head_);
drives_[1]->set_head(selected_head_);
break;
case 0x7ffc: {
const int selected_head = value & 1;
for_all_drives([selected_head] (Storage::Disk::Drive &drive, size_t index) {
drive.set_head(selected_head);
});
} break;
case 0x7ffd: {
selected_drive_ = value & 1;
set_drive(drives_[selected_drive_]);
set_drive(1 << (value & 1));
bool drive_motor = !!(value & 0x80);
drives_[0]->set_motor_on(drive_motor);
drives_[1]->set_motor_on(drive_motor);
const bool drive_motor = value & 0x80;
for_all_drives([drive_motor] (Storage::Disk::Drive &drive, size_t index) {
drive.set_motor_on(drive_motor);
});
} break;
}
}
@ -59,7 +59,7 @@ void DiskROM::run_for(HalfCycles half_cycles) {
}
void DiskROM::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
drives_[drive]->set_disk(disk);
get_drive(drive).set_disk(disk);
}
void DiskROM::set_head_load_request(bool head_load) {
@ -68,9 +68,7 @@ void DiskROM::set_head_load_request(bool head_load) {
}
void DiskROM::set_activity_observer(Activity::Observer *observer) {
size_t c = 1;
for(auto &drive: drives_) {
drive->set_activity_observer(observer, "Drive " + std::to_string(c), true);
++c;
}
for_all_drives([observer] (Storage::Disk::Drive &drive, size_t index) {
drive.set_activity_observer(observer, "Drive " + std::to_string(index), true);
});
}

View File

@ -36,9 +36,6 @@ class DiskROM: public ROMSlotHandler, public WD::WD1770 {
const std::vector<uint8_t> &rom_;
long int controller_cycles_ = 0;
size_t selected_drive_ = 0;
int selected_head_ = 0;
std::array<std::shared_ptr<Storage::Disk::Drive>, 2> drives_;
void set_head_load_request(bool head_load) final;
};

View File

@ -113,16 +113,16 @@ void BD500::access(int address) {
void BD500::set_head_load_request(bool head_load) {
// Turn all motors on or off; if off then unload the head instantly.
is_loading_head_ |= head_load;
for(auto &drive : drives_) {
if(drive) drive->set_motor_on(head_load);
}
for_all_drives([head_load] (Storage::Disk::Drive &drive, size_t) {
drive.set_motor_on(head_load);
});
if(!head_load) set_head_loaded(false);
}
void BD500::run_for(const Cycles cycles) {
// If a head load is in progress and the selected drive is now ready,
// declare head loaded.
if(is_loading_head_ && drives_[selected_drive_] && drives_[selected_drive_]->get_is_ready()) {
if(is_loading_head_ && get_drive().get_is_ready()) {
set_head_loaded(true);
is_loading_head_ = false;
}

View File

@ -14,15 +14,13 @@ namespace Oric {
class DiskController: public WD::WD1770 {
public:
DiskController(WD::WD1770::Personality personality, int clock_rate, Storage::Disk::Drive::ReadyType ready_type) :
WD::WD1770(personality), clock_rate_(clock_rate), ready_type_(ready_type) {}
WD::WD1770(personality), clock_rate_(clock_rate), ready_type_(ready_type) {
emplace_drives(4, clock_rate_, 300, 2, ready_type_);
// TODO: don't assume four drives?
}
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int d) {
const size_t drive = size_t(d);
if(!drives_[drive]) {
drives_[drive] = std::make_unique<Storage::Disk::Drive>(clock_rate_, 300, 2, ready_type_);
if(drive == selected_drive_) set_drive(drives_[drive]);
}
drives_[drive]->set_disk(disk);
get_drive(size_t(d)).set_disk(disk);
}
enum class PagedItem {
@ -44,14 +42,6 @@ class DiskController: public WD::WD1770 {
}
protected:
std::array<std::shared_ptr<Storage::Disk::Drive>, 4> drives_;
size_t selected_drive_ = 0;
void select_drive(size_t drive) {
if(drive != selected_drive_) {
selected_drive_ = drive;
set_drive(drives_[selected_drive_]);
}
}
Delegate *delegate_ = nullptr;
bool enable_overlay_ram_ = false;

View File

@ -20,11 +20,12 @@ Jasmin::Jasmin() : DiskController(P1770, 8000000, Storage::Disk::Drive::ReadyTyp
void Jasmin::write(int address, uint8_t value) {
switch(address) {
// Set side.
case 0x3f8:
for(auto &drive : drives_) {
if(drive) drive->set_head(value & 1);
}
break;
case 0x3f8: {
const int head = value & 1;
for_all_drives([head] (Storage::Disk::Drive &drive, size_t) {
drive.set_head(head);
});
} break;
case 0x3f9:
/* TODO: reset. */
@ -42,11 +43,11 @@ void Jasmin::write(int address, uint8_t value) {
select_paged_item();
break;
case 0x3fc: case 0x3fd: case 0x3fe: case 0x3ff: {
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(false);
select_drive(size_t(address - 0x3fc));
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(motor_on_);
} break;
case 0x3fc: case 0x3fd: case 0x3fe: case 0x3ff:
get_drive().set_motor_on(false);
set_drive(1 << (address - 0x3fc));
get_drive().set_motor_on(motor_on_);
break;
default:
return WD::WD1770::write(address, value);
@ -55,7 +56,7 @@ void Jasmin::write(int address, uint8_t value) {
void Jasmin::set_motor_on(bool on) {
motor_on_ = on;
if(drives_[selected_drive_]) drives_[selected_drive_]->set_motor_on(motor_on_);
get_drive().set_motor_on(motor_on_);
if(observer_) {
observer_->set_led_status("Jasmin", on);
}

View File

@ -33,16 +33,15 @@ void Microdisc::set_control_register(uint8_t control, uint8_t changes) {
// b65: drive select
if((changes >> 5)&3) {
selected_drive_ = (control >> 5)&3;
set_drive(drives_[selected_drive_]);
set_drive(1 << (control >> 5)&3);
}
// b4: side select
if(changes & 0x10) {
const int head = (control & 0x10) ? 1 : 0;
for(auto &drive : drives_) {
if(drive) drive->set_head(head);
}
for_all_drives([head] (Storage::Disk::Drive &drive, size_t) {
drive.set_head(head);
});
}
// b3: double density select (0 = double)
@ -86,9 +85,9 @@ void Microdisc::set_head_load_request(bool head_load) {
// The drive motors (at present: I believe **all drive motors** regardless of the selected drive) receive
// the current head load request state.
for(auto &drive : drives_) {
if(drive) drive->set_motor_on(head_load);
}
for_all_drives([head_load] (Storage::Disk::Drive &drive, size_t) {
drive.set_motor_on(head_load);
});
// A request to load the head results in a delay until the head is confirmed loaded. This delay is handled
// in ::run_for. A request to unload the head results in an instant answer that the head is unloaded.

View File

@ -14,9 +14,9 @@ Controller::Controller(Cycles clock_rate) :
clock_rate_multiplier_(128000000 / clock_rate.as_integral()),
clock_rate_(clock_rate.as_integral() * clock_rate_multiplier_),
pll_(100, *this),
empty_drive_(new Drive(int(clock_rate.as_integral()), 1, 1)) {
empty_drive_(int(clock_rate.as_integral()), 1, 1),
drive_(&empty_drive_) {
set_expected_bit_length(Time(1));
set_drive(empty_drive_);
}
void Controller::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) {
@ -32,7 +32,7 @@ void Controller::run_for(const Cycles cycles) {
}
Drive &Controller::get_drive() {
return *drive_.get();
return *drive_;
}
// MARK: - Drive::EventDelegate
@ -71,26 +71,33 @@ void Controller::digital_phase_locked_loop_output_bit(int value) {
if(is_reading_) process_input_bit(value);
}
void Controller::set_drive(std::shared_ptr<Drive> drive) {
if(drive_ != drive) {
ClockingHint::Preference former_prefernece = preferred_clocking();
// invalidate_track();
void Controller::set_drive(int index_mask) {
if(drive_selection_mask_ == index_mask) {
return;
}
if(drive_) {
drive_->set_event_delegate(nullptr);
drive_->set_clocking_hint_observer(nullptr);
}
drive_ = drive;
if(drive_) {
drive_->set_event_delegate(this);
drive_->set_clocking_hint_observer(this);
} else {
drive_ = empty_drive_;
}
ClockingHint::Preference former_prefernece = preferred_clocking();
// invalidate_track();
if(preferred_clocking() != former_prefernece) {
update_clocking_observer();
get_drive().set_event_delegate(nullptr);
get_drive().set_clocking_hint_observer(nullptr);
if(!index_mask) {
drive_ = &empty_drive_;
} else {
size_t index = 0;
while(!(index_mask&1)) {
index_mask >>= 1;
++index;
}
drive_ = &drives_[index];
}
get_drive().set_event_delegate(this);
get_drive().set_clocking_hint_observer(this);
if(preferred_clocking() != former_prefernece) {
update_clocking_observer();
}
}

View File

@ -49,9 +49,25 @@ class Controller:
void run_for(const Cycles cycles);
/*!
Sets the current drive. This drive is the one the PLL listens to.
Sets the current drive(s). Normally this will be exactly one, but some machines allow
zero or multiple drives to be attached, with useless results.
*/
void set_drive(std::shared_ptr<Drive> drive);
void set_drive(int index_mask);
/*!
Adds a new drive to the attached list, returning its index.
*/
template<typename... Args> size_t emplace_drive(Args&&... args) {
drives_.emplace_back(std::forward<Args>(args)...);
return drives_.size() - 1;
}
template<typename... Args> size_t emplace_drives(size_t count, Args&&... args) {
while(count--) {
drives_.emplace_back(std::forward<Args>(args)...);
}
return drives_.size() - 1;
}
/*!
Should be implemented by subclasses; communicates each bit that the PLL recognises.
@ -98,6 +114,18 @@ class Controller:
*/
Drive &get_drive();
Drive &get_drive(size_t index) {
return drives_[index];
}
void for_all_drives(const std::function<void(Drive &, size_t)> &func) {
size_t index = 0;
for(auto &drive: drives_) {
func(drive, index);
++index;
}
}
/*!
As per ClockingHint::Source.
*/
@ -113,9 +141,10 @@ class Controller:
DigitalPhaseLockedLoop<Controller> pll_;
friend DigitalPhaseLockedLoop<Controller>;
std::shared_ptr<Drive> drive_;
std::shared_ptr<Drive> empty_drive_;
Drive empty_drive_;
std::vector<Drive> drives_;
Drive *drive_;
int drive_selection_mask_ = 0xff;
// ClockingHint::Observer.
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) final;

View File

@ -170,6 +170,7 @@ void Drive::set_motor_on(bool motor_is_on) {
// TODO: momentum.
if(motor_is_on) {
set_disk_is_rotating(true);
time_until_motor_transition = Cycles(0);
return;
}
@ -431,9 +432,10 @@ void Drive::set_disk_is_rotating(bool is_rotating) {
disk_is_rotating_ = is_rotating;
if(observer_) {
observer_->set_drive_motor_status(drive_name_, motor_input_is_on_);
observer_->set_drive_motor_status(drive_name_, disk_is_rotating_);
if(announce_motor_led_) {
observer_->set_led_status(drive_name_, motor_input_is_on_);
printf("LED set: %s %d\n", drive_name_.c_str(), int(disk_is_rotating_));
observer_->set_led_status(drive_name_, disk_is_rotating_);
}
}