mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-06 13:31:55 +00:00
Merge pull request #749 from TomHarte/DiskIndicator
Moves ownership of drives inside Disk::Controller.
This commit is contained in:
commit
0c1c5a0ab8
@ -19,12 +19,10 @@ using namespace Analyser::Static::Commodore;
|
||||
|
||||
class CommodoreGCRParser: public Storage::Disk::Controller {
|
||||
public:
|
||||
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);
|
||||
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<Storage::Disk::Disk> &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<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
std::vector<File> files;
|
||||
CommodoreGCRParser parser;
|
||||
parser.drive->set_disk(disk);
|
||||
parser.set_disk(disk);
|
||||
|
||||
// find any sector whatsoever to establish the current track
|
||||
std::shared_ptr<CommodoreGCRParser::Sector> sector;
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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> drive);
|
||||
void set_drive(int index_mask);
|
||||
|
||||
/*!
|
||||
Adds a new drive to the drive list, returning its index.
|
||||
*/
|
||||
template<typename... Args> size_t emplace_drive(Args&&... args) {
|
||||
drives_.emplace_back(std::forward<Args>(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<typename... Args> size_t emplace_drives(size_t count, Args&&... args) {
|
||||
while(count--) {
|
||||
drives_.emplace_back(std::forward<Args>(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<void(Drive &, size_t)> &func) {
|
||||
size_t index = 0;
|
||||
for(auto &drive: drives_) {
|
||||
func(drive, index);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
As per ClockingHint::Source.
|
||||
*/
|
||||
@ -113,9 +146,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;
|
||||
|
@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user