diff --git a/Analyser/Static/Commodore/Disk.cpp b/Analyser/Static/Commodore/Disk.cpp index 3ea28b19e..c4034d1bf 100644 --- a/Analyser/Static/Commodore/Disk.cpp +++ b/Analyser/Static/Commodore/Disk.cpp @@ -19,12 +19,10 @@ using namespace Analyser::Static::Commodore; class CommodoreGCRParser: public Storage::Disk::Controller { public: - std::shared_ptr drive; - CommodoreGCRParser() : Storage::Disk::Controller(4000000), shift_register_(0), track_(1) { - drive = std::make_shared(4000000, 300, 2); - set_drive(drive); - drive->set_motor_on(true); + emplace_drive(4000000, 300, 2); + set_drive(1); + get_drive().set_motor_on(true); } struct Sector { @@ -61,6 +59,10 @@ class CommodoreGCRParser: public Storage::Disk::Controller { return get_sector(sector); } + void set_disk(const std::shared_ptr &disk) { + get_drive().set_disk(disk); + } + private: unsigned int shift_register_; int index_count_; @@ -170,7 +172,7 @@ class CommodoreGCRParser: public Storage::Disk::Controller { std::vector Analyser::Static::Commodore::GetFiles(const std::shared_ptr &disk) { std::vector files; CommodoreGCRParser parser; - parser.drive->set_disk(disk); + parser.set_disk(disk); // find any sector whatsoever to establish the current track std::shared_ptr sector; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 381ed00eb..1e319eae1 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -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 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 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); } }; diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index f1ed580bc..9e65aa52c 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -441,7 +441,8 @@ class ConcreteMachine: // Advance the relevant counters. cycles_since_audio_update_ += length; mfp_ += length; - dma_ += length; + if(dma_clocking_preference_ != ClockingHint::Preference::None) + dma_ += length; keyboard_acia_ += length; midi_acia_ += length; bus_phase_ += length; @@ -462,7 +463,7 @@ class ConcreteMachine: mfp_.flush(); } - if(dma_is_realtime_) { + if(dma_clocking_preference_ == ClockingHint::Preference::RealTime) { dma_.flush(); } @@ -531,7 +532,7 @@ class ConcreteMachine: bool may_defer_acias_ = true; bool keyboard_needs_clock_ = false; bool mfp_is_realtime_ = false; - bool dma_is_realtime_ = false; + ClockingHint::Preference dma_clocking_preference_ = ClockingHint::Preference::None; void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) final { // This is being called by one of the components; avoid any time flushing here as that's // already dealt with (and, just to be absolutely sure, to avoid recursive mania). @@ -540,7 +541,7 @@ class ConcreteMachine: (midi_acia_.last_valid()->preferred_clocking() != ClockingHint::Preference::RealTime); keyboard_needs_clock_ = ikbd_.preferred_clocking() != ClockingHint::Preference::None; mfp_is_realtime_ = mfp_.last_valid()->preferred_clocking() == ClockingHint::Preference::RealTime; - dma_is_realtime_ = dma_.last_valid()->preferred_clocking() == ClockingHint::Preference::RealTime; + dma_clocking_preference_ = dma_.last_valid()->preferred_clocking(); } // MARK: - GPIP input. diff --git a/Machines/Atari/ST/DMAController.cpp b/Machines/Atari/ST/DMAController.cpp index 8ddea26a4..1de419abc 100644 --- a/Machines/Atari/ST/DMAController.cpp +++ b/Machines/Atari/ST/DMAController.cpp @@ -126,7 +126,7 @@ void DMAController::set_floppy_drive_selection(bool drive1, bool drive2, bool si } void DMAController::set_floppy_disk(std::shared_ptr 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); } diff --git a/Machines/Atari/ST/DMAController.hpp b/Machines/Atari/ST/DMAController.hpp index 360f31077..eb4190a2a 100644 --- a/Machines/Atari/ST/DMAController.hpp +++ b/Machines/Atari/ST/DMAController.hpp @@ -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 disk, size_t drive) { + get_drive(drive).set_disk(disk); } - std::vector> drives_; } fdc_; void wd1770_did_change_output(WD::WD1770 *) final; diff --git a/Machines/Commodore/1540/Implementation/C1540.cpp b/Machines/Commodore/1540/Implementation/C1540.cpp index e7d80d77d..e97dfbbb2 100644 --- a/Machines/Commodore/1540/Implementation/C1540.cpp +++ b/Machines/Commodore/1540/Implementation/C1540.cpp @@ -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 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) { diff --git a/Machines/Commodore/1540/Implementation/C1540Base.hpp b/Machines/Commodore/1540/Implementation/C1540Base.hpp index 2c4458999..5190bed02 100644 --- a/Machines/Commodore/1540/Implementation/C1540Base.hpp +++ b/Machines/Commodore/1540/Implementation/C1540Base.hpp @@ -144,7 +144,6 @@ class MachineBase: protected: CPU::MOS6502::Processor m6502_; - std::shared_ptr drive_; uint8_t ram_[0x800]; uint8_t rom_[0x4000]; diff --git a/Machines/Electron/Plus3.cpp b/Machines/Electron/Plus3.cpp index 75e08e3ab..6d7ce340b 100644 --- a/Machines/Electron/Plus3.cpp +++ b/Machines/Electron/Plus3.cpp @@ -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 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); + }); } diff --git a/Machines/Electron/Plus3.hpp b/Machines/Electron/Plus3.hpp index ab2486c9d..9d19ce631 100644 --- a/Machines/Electron/Plus3.hpp +++ b/Machines/Electron/Plus3.hpp @@ -24,8 +24,6 @@ class Plus3 : public WD::WD1770 { private: void set_control_register(uint8_t control, uint8_t changes); - std::vector> drives_; - int selected_drive_ = 0; uint8_t last_control_ = 0; void set_motor_on(bool on); diff --git a/Machines/MSX/DiskROM.cpp b/Machines/MSX/DiskROM.cpp index 6991b4a61..f94ae2139 100644 --- a/Machines/MSX/DiskROM.cpp +++ b/Machines/MSX/DiskROM.cpp @@ -13,8 +13,7 @@ using namespace MSX; DiskROM::DiskROM(const std::vector &rom) : WD1770(P1793), rom_(rom) { - drives_[0] = std::make_shared(8000000, 300, 2); - drives_[1] = std::make_shared(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 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); + }); } diff --git a/Machines/MSX/DiskROM.hpp b/Machines/MSX/DiskROM.hpp index 5710a7f9e..8227982af 100644 --- a/Machines/MSX/DiskROM.hpp +++ b/Machines/MSX/DiskROM.hpp @@ -36,9 +36,6 @@ class DiskROM: public ROMSlotHandler, public WD::WD1770 { const std::vector &rom_; long int controller_cycles_ = 0; - size_t selected_drive_ = 0; - int selected_head_ = 0; - std::array, 2> drives_; void set_head_load_request(bool head_load) final; }; diff --git a/Machines/Oric/BD500.cpp b/Machines/Oric/BD500.cpp index 638870b33..c4b9cd942 100644 --- a/Machines/Oric/BD500.cpp +++ b/Machines/Oric/BD500.cpp @@ -14,6 +14,7 @@ BD500::BD500() : DiskController(P1793, 9000000, Storage::Disk::Drive::ReadyType: disable_basic_rom_ = true; select_paged_item(); set_is_double_density(true); + set_drive(1); } void BD500::write(int address, uint8_t value) { @@ -23,6 +24,18 @@ void BD500::write(int address, uint8_t value) { // if(address == 0x320) printf("Command %02x\n", value); WD::WD1770::write(address, value); } + + if(address == 0x031a) { + // Drive select; kudos to iss of Oricutron for figuring this one out; + // cf. http://forum.defence-force.org/viewtopic.php?f=25&p=21409#p21393 + switch(value & 0xe0) { + default: set_drive(0); break; + case 0x20: set_drive(1); break; + case 0x40: set_drive(2); break; + case 0x80: set_drive(4); break; + case 0xc0: set_drive(8); break; + } + } } uint8_t BD500::read(int address) { @@ -113,16 +126,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; } diff --git a/Machines/Oric/DiskController.hpp b/Machines/Oric/DiskController.hpp index 4bfa0e4d8..6cee28d63 100644 --- a/Machines/Oric/DiskController.hpp +++ b/Machines/Oric/DiskController.hpp @@ -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 disk, int d) { - const size_t drive = size_t(d); - if(!drives_[drive]) { - drives_[drive] = std::make_unique(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, 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; diff --git a/Machines/Oric/Jasmin.cpp b/Machines/Oric/Jasmin.cpp index 3a53180c4..8193ae87e 100644 --- a/Machines/Oric/Jasmin.cpp +++ b/Machines/Oric/Jasmin.cpp @@ -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); } diff --git a/Machines/Oric/Microdisc.cpp b/Machines/Oric/Microdisc.cpp index 11c5e2e64..73c685507 100644 --- a/Machines/Oric/Microdisc.cpp +++ b/Machines/Oric/Microdisc.cpp @@ -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. diff --git a/Machines/Oric/Video.hpp b/Machines/Oric/Video.hpp index fc4c9e046..9d066a808 100644 --- a/Machines/Oric/Video.hpp +++ b/Machines/Oric/Video.hpp @@ -44,8 +44,8 @@ class VideoOutput { int v_sync_start_position_, v_sync_end_position_, counter_period_; // Output target and device. - uint8_t *rgb_pixel_target_; - uint32_t *composite_pixel_target_; + uint8_t *rgb_pixel_target_ = nullptr; + uint32_t *composite_pixel_target_ = nullptr; uint32_t colour_forms_[8]; Outputs::Display::InputDataType data_type_; diff --git a/Storage/Disk/Controller/DiskController.cpp b/Storage/Disk/Controller/DiskController.cpp index 6afdbc9ce..2c3484767 100644 --- a/Storage/Disk/Controller/DiskController.cpp +++ b/Storage/Disk/Controller/DiskController.cpp @@ -14,9 +14,10 @@ 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_) { + empty_drive_.set_clocking_hint_observer(this); set_expected_bit_length(Time(1)); - set_drive(empty_drive_); } void Controller::set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) { @@ -24,15 +25,28 @@ void Controller::set_component_prefers_clocking(ClockingHint::Source *component, } ClockingHint::Preference Controller::preferred_clocking() { - return (!drive_ || (drive_->preferred_clocking() == ClockingHint::Preference::None)) ? ClockingHint::Preference::None : ClockingHint::Preference::RealTime; + // Nominate RealTime clocking if any drive currently wants any clocking whatsoever. + // Otherwise, ::None will do. + for(auto &drive: drives_) { + if(drive.preferred_clocking() != ClockingHint::Preference::None) { + return ClockingHint::Preference::RealTime; + } + } + if(empty_drive_.preferred_clocking() != ClockingHint::Preference::None) { + return ClockingHint::Preference::RealTime; + } + return ClockingHint::Preference::None; } void Controller::run_for(const Cycles cycles) { - if(drive_) drive_->run_for(cycles); + for(auto &drive: drives_) { + drive.run_for(cycles); + } + empty_drive_.run_for(cycles); } Drive &Controller::get_drive() { - return *drive_.get(); + return *drive_; } // MARK: - Drive::EventDelegate @@ -71,26 +85,37 @@ 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) { - 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(); - if(preferred_clocking() != former_prefernece) { - update_clocking_observer(); + // Stop receiving events from the current drive. + get_drive().set_event_delegate(nullptr); + + // TODO: a transfer of writing state, if writing? + + if(!index_mask) { + drive_ = &empty_drive_; + } else { + // TEMPORARY FIX: connect up only the first selected drive. + // TODO: at least merge events if multiple drives are selected. Some computers have + // controllers that allow this, with usually meaningless results as far as I can + // imagine. But the limit of an emulator shouldn't be the author's imagination. + size_t index = 0; + while(!(index_mask&1)) { + index_mask >>= 1; + ++index; } + drive_ = &drives_[index]; + } + + get_drive().set_event_delegate(this); + + if(preferred_clocking() != former_prefernece) { + update_clocking_observer(); } } diff --git a/Storage/Disk/Controller/DiskController.hpp b/Storage/Disk/Controller/DiskController.hpp index c7d922dd1..b3b23ebfa 100644 --- a/Storage/Disk/Controller/DiskController.hpp +++ b/Storage/Disk/Controller/DiskController.hpp @@ -49,9 +49,30 @@ 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); + void set_drive(int index_mask); + + /*! + Adds a new drive to the drive list, returning its index. + */ + template size_t emplace_drive(Args&&... args) { + drives_.emplace_back(std::forward(args)...); + drives_.back().set_clocking_hint_observer(this); + return drives_.size() - 1; + } + + /*! + Adds @c count new drives to the drive list, returning the index of the final one added. + */ + template size_t emplace_drives(size_t count, Args&&... args) { + while(count--) { + drives_.emplace_back(std::forward(args)...); + drives_.back().set_clocking_hint_observer(this); + } + return drives_.size() - 1; + } /*! Should be implemented by subclasses; communicates each bit that the PLL recognises. @@ -98,6 +119,18 @@ class Controller: */ Drive &get_drive(); + Drive &get_drive(size_t index) { + return drives_[index]; + } + + void for_all_drives(const std::function &func) { + size_t index = 0; + for(auto &drive: drives_) { + func(drive, index); + ++index; + } + } + /*! As per ClockingHint::Source. */ @@ -113,9 +146,10 @@ class Controller: DigitalPhaseLockedLoop pll_; friend DigitalPhaseLockedLoop; - std::shared_ptr drive_; - - std::shared_ptr empty_drive_; + Drive empty_drive_; + std::vector drives_; + Drive *drive_; + int drive_selection_mask_ = 0xff; // ClockingHint::Observer. void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) final; diff --git a/Storage/Disk/Drive.cpp b/Storage/Disk/Drive.cpp index 39fa5f9ae..b79f87532 100644 --- a/Storage/Disk/Drive.cpp +++ b/Storage/Disk/Drive.cpp @@ -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,9 @@ 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_); + observer_->set_led_status(drive_name_, disk_is_rotating_); } }