From 7b7beb13a3129a17326539b7b8b6d8dcdfdaf935 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 17 May 2018 21:39:11 -0400 Subject: [PATCH] Eliminates the fiction of setting and getting registers. The Disk II seems lower level than that; it will read the data bus whenever it likes, it is the programmer's responsibility to keep up with that. It also reserves the right not to load the bus regardless of whether it receives a read or write access. --- Components/DiskII/DiskII.cpp | 60 +++++---------------- Components/DiskII/DiskII.hpp | 41 ++++++++++++-- Machines/AppleII/DiskIICard.cpp | 8 +-- Machines/Oric/Oric.cpp | 23 ++++---- Storage/Disk/DiskImage/Formats/AppleDSK.hpp | 3 ++ 5 files changed, 71 insertions(+), 64 deletions(-) diff --git a/Components/DiskII/DiskII.cpp b/Components/DiskII/DiskII.cpp index 8669829fd..6a4b90b60 100644 --- a/Components/DiskII/DiskII.cpp +++ b/Components/DiskII/DiskII.cpp @@ -39,11 +39,9 @@ void DiskII::set_control(Control control, bool on) { case Control::Motor: motor_is_enabled_ = on; drives_[active_drive_].set_motor_on(on); - break; + return; } -// printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off"); - // If the stepper magnet selections have changed, and any is on, see how // that moves the head. if(previous_stepper_mask ^ stepper_mask_ && stepper_mask_) { @@ -63,14 +61,7 @@ void DiskII::set_control(Control control, bool on) { } } -void DiskII::set_mode(Mode mode) { -// printf("Set mode %d\n", mode); - inputs_ = (inputs_ & ~input_mode) | ((mode == Mode::Write) ? input_mode : 0); - set_controller_can_sleep(); -} - void DiskII::select_drive(int drive) { -// printf("Select drive %d\n", drive); if((drive&1) == active_drive_) return; drives_[active_drive_].set_event_delegate(this); @@ -81,20 +72,6 @@ void DiskII::select_drive(int drive) { drives_[active_drive_].set_motor_on(motor_is_enabled_); } -void DiskII::set_data_register(uint8_t value) { -// printf("Set data register (?)\n"); - inputs_ |= input_command; - shift_register_ = value; - set_controller_can_sleep(); -} - -uint8_t DiskII::get_shift_register() { -// if(shift_register_ & 0x80) printf("[%02x] ", shift_register_); - inputs_ &= ~input_command; - set_controller_can_sleep(); - return shift_register_; -} - void DiskII::run_for(const Cycles cycles) { if(is_sleeping()) return; @@ -124,11 +101,7 @@ void DiskII::run_for(const Cycles cycles) { return; } break; - case 0xb: - // load data register from data bus... - printf("TODO\n"); - // shift_register_ = data_register_; - break; // load + case 0xb: shift_register_ = data_input_; break; // load data register from data bus } // TODO: surely there's a less heavyweight solution than this? @@ -221,15 +194,11 @@ bool DiskII::is_sleeping() { return controller_can_sleep_ && drive_is_sleeping_[0] && drive_is_sleeping_[1]; } -void DiskII::set_register(int address, uint8_t value) { - trigger_address(address, value); +void DiskII::set_data_input(uint8_t input) { + data_input_ = input; } -uint8_t DiskII::get_register(int address) { - return trigger_address(address, 0xff); -} - -uint8_t DiskII::trigger_address(int address, uint8_t value) { +int DiskII::read_address(int address) { switch(address & 0xf) { default: case 0x0: set_control(Control::P0, false); break; @@ -241,22 +210,19 @@ uint8_t DiskII::trigger_address(int address, uint8_t value) { case 0x6: set_control(Control::P3, false); break; case 0x7: set_control(Control::P3, true); break; - case 0x8: set_control(Control::Motor, false); break; + case 0x8: + shift_register_ = 0; + set_control(Control::Motor, false); + break; case 0x9: set_control(Control::Motor, true); break; case 0xa: select_drive(0); break; case 0xb: select_drive(1); break; - case 0xc: - inputs_ &= ~input_command; - break; - case 0xd: set_data_register(value); break; - - case 0xe: - set_mode(Mode::Read); - break; -// return shift_register_; - case 0xf: set_mode(Mode::Write); break; + case 0xc: inputs_ &= ~input_command; break; + case 0xd: inputs_ |= input_command; break; + case 0xe: inputs_ &= ~input_mode; break; + case 0xf: inputs_ |= input_mode; break; } set_controller_can_sleep(); return (address & 1) ? 0xff : shift_register_; diff --git a/Components/DiskII/DiskII.hpp b/Components/DiskII/DiskII.hpp index 14a714b71..f85a5ab49 100644 --- a/Components/DiskII/DiskII.hpp +++ b/Components/DiskII/DiskII.hpp @@ -33,15 +33,48 @@ class DiskII: public: DiskII(); - void set_register(int address, uint8_t value); - uint8_t get_register(int address); + /// Sets the current external value of the data bus. + void set_data_input(uint8_t input); + /*! + Submits an access to address @c address. + + @returns The 8-bit value loaded to the data bus by the DiskII if any; + @c DidNotLoad otherwise. + */ + int read_address(int address); + + /*! + The value returned by @c read_address if accessing that address + didn't cause the disk II to place anything onto the bus. + */ + const int DidNotLoad = -1; + + /// Advances the controller by @c cycles. void run_for(const Cycles cycles); + + /*! + Supplies the image of the state machine (i.e. P6) ROM, + which dictates how the Disk II will respond to input. + + To reduce processing costs, some assumptions are made by + the implementation as to the content of this ROM. + Including: + + If Q6 is set and Q7 is reset, the controller is testing + for write protect. If and when the shift register has + become full with the state of the write protect value, + no further processing is required. + */ void set_state_machine(const std::vector &); + /// Inserts @c disk into the drive @c drive. void set_disk(const std::shared_ptr &disk, int drive); + + // As per Sleeper. bool is_sleeping() override; + // The Disk II functions as a potential target for @c Activity::Sources. void set_activity_observer(Activity::Observer *observer); private: @@ -55,8 +88,6 @@ class DiskII: void set_control(Control control, bool on); void set_mode(Mode mode); void select_drive(int drive); - void set_data_register(uint8_t value); - uint8_t get_shift_register(); uint8_t trigger_address(int address, uint8_t value); void process_event(const Storage::Disk::Track::Event &event) override; @@ -78,6 +109,8 @@ class DiskII: bool motor_is_enabled_ = false; void set_controller_can_sleep(); + + uint8_t data_input_ = 0; }; } diff --git a/Machines/AppleII/DiskIICard.cpp b/Machines/AppleII/DiskIICard.cpp index e1447d6cc..6a0fb6c0f 100644 --- a/Machines/AppleII/DiskIICard.cpp +++ b/Machines/AppleII/DiskIICard.cpp @@ -25,10 +25,12 @@ void DiskIICard::perform_bus_operation(CPU::MOS6502::BusOperation operation, uin if(address < 0x100) { if(isReadOperation(operation)) *value = boot_[address]; } else { + // TODO: data input really shouldn't happen only upon a write. + diskii_.set_data_input(*value); + const int disk_value = diskii_.read_address(address); if(isReadOperation(operation)) { - *value = diskii_.get_register(address); - } else { - diskii_.set_register(address, *value); + if(disk_value != diskii_.DidNotLoad) + *value = static_cast(disk_value); } } } diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 43d1dfcc7..b37124de4 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -405,9 +405,8 @@ template class Co } } } else { - update_diskii(); - if(isReadOperation(operation)) *value = diskii_.get_register(address); - else diskii_.set_register(address, *value); + const int disk_value = diskii_.read_address(address); + if(isReadOperation(operation) && disk_value != diskii_.DidNotLoad) *value = static_cast(disk_value); } break; } @@ -436,8 +435,13 @@ template class Co tape_player_.run_for(Cycles(1)); switch(disk_interface) { default: break; - case Analyser::Static::Oric::Target::DiskInterface::Microdisc: microdisc_.run_for(Cycles(8)); break; - case Analyser::Static::Oric::Target::DiskInterface::Pravetz: cycles_since_diskii_update_ += 2; break; + case Analyser::Static::Oric::Target::DiskInterface::Microdisc: + microdisc_.run_for(Cycles(8)); + break; + case Analyser::Static::Oric::Target::DiskInterface::Pravetz: + diskii_.set_data_input(*value); + diskii_.run_for(Cycles(2)); + break; } cycles_since_video_update_++; return Cycles(1); @@ -446,7 +450,6 @@ template class Co forceinline void flush() { update_video(); via_port_handler_.flush(); - if(disk_interface == Analyser::Static::Oric::Target::DiskInterface::Pravetz) update_diskii(); } // to satisfy CRTMachine::Machine @@ -605,10 +608,10 @@ template class Co Apple::DiskII diskii_; std::vector pravetz_rom_; std::size_t pravetz_rom_base_pointer_ = 0; - Cycles cycles_since_diskii_update_; - void update_diskii() { - diskii_.run_for(cycles_since_diskii_update_.flush()); - } +// Cycles cycles_since_diskii_update_; +// void update_diskii() { +// diskii_.run_for(cycles_since_diskii_update_.flush()); +// } // Overlay RAM uint16_t ram_top_ = basic_visible_ram_top_; diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp index 3e5ad3321..fb17f359e 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.hpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.hpp @@ -35,6 +35,9 @@ class AppleDSK: public DiskImage { HeadPosition get_maximum_head_position() override; std::shared_ptr get_track_at_position(Track::Address address) override; + // TEST! + bool get_is_read_only() override { return false; } + private: Storage::FileHolder file_; int sectors_per_track_ = 16;