From e88a51e75e4fe5ecaa0b71c6a5f039c6008da201 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 20 Aug 2017 12:05:00 -0400 Subject: [PATCH] Worked logic all the way down to the CPC. If the 8272 announces that it is asleep, it is now no longer clocked. Also very slightly cut down on IRQ line chatter to the Z80. --- Components/8272/i8272.cpp | 9 ++++++++- Components/8272/i8272.hpp | 2 ++ Machines/AmstradCPC/AmstradCPC.cpp | 24 ++++++++++++++++++++---- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 926879f1d..664bf54b8 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -88,6 +88,10 @@ i8272::i8272(BusHandler &bus_handler, Cycles clock_rate, int clock_rate_multipli posit_event((int)Event8272::CommandByte); } +bool i8272::is_sleeping() { + return is_sleeping_ && Storage::Disk::MFMController::is_sleeping(); +} + void i8272::run_for(Cycles cycles) { Storage::Disk::MFMController::run_for(cycles); @@ -154,6 +158,7 @@ void i8272::run_for(Cycles cycles) { } is_sleeping_ = !delay_time_ && !drives_seeking_ && !head_timers_running_; + if(is_sleeping_) update_sleep_observer(); } void i8272::set_register(int address, uint8_t value) { @@ -198,7 +203,7 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { #define MS_TO_CYCLES(x) x * 8000 #define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = (int)mask; return; case __LINE__: -#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = (int)Event8272::Timer; delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; case __LINE__: if(delay_time_) return; +#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = (int)Event8272::Timer; delay_time_ = MS_TO_CYCLES(ms); is_sleeping_ = false; update_sleep_observer(); case __LINE__: if(delay_time_) return; #define PASTE(x, y) x##y #define CONCAT(x, y) PASTE(x, y) @@ -257,6 +262,7 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { if(drives_[active_drive_].head_unload_delay[active_head_] == 0) { \ head_timers_running_++; \ is_sleeping_ = false; \ + update_sleep_observer(); \ } \ drives_[active_drive_].head_unload_delay[active_head_] = MS_TO_CYCLES(head_unload_time_);\ } @@ -711,6 +717,7 @@ void i8272::posit_event(int event_type) { if(drives_[drive].phase != Drive::Seeking) { drives_seeking_++; is_sleeping_ = false; + update_sleep_observer(); } // Set currently seeking, with a step to occur right now (yes, it sounds like jamming these diff --git a/Components/8272/i8272.hpp b/Components/8272/i8272.hpp index 9c216aa7c..6c31a9517 100644 --- a/Components/8272/i8272.hpp +++ b/Components/8272/i8272.hpp @@ -41,6 +41,8 @@ class i8272: public Storage::Disk::MFMController { void set_disk(std::shared_ptr disk, int drive); + bool is_sleeping(); + private: // The bus handler, for interrupt and DMA-driven usage. BusHandler &bus_handler_; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 3fdded550..593393946 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -76,7 +76,12 @@ class InterruptTimer { /// @returns @c true if an interrupt is currently requested; @c false otherwise. inline bool get_request() { - return interrupt_request_; + return last_interrupt_request_ = interrupt_request_; + } + + /// Asks whether the interrupt status has changed. + inline bool request_has_changed() { + return last_interrupt_request_ != interrupt_request_; } /// Resets the timer. @@ -88,6 +93,7 @@ class InterruptTimer { private: int reset_counter_; bool interrupt_request_; + bool last_interrupt_request_; int timer_; }; @@ -659,6 +665,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler { class ConcreteMachine: public Utility::TypeRecipient, public CPU::Z80::BusHandler, + public Sleeper::SleepObserver, public Machine { public: ConcreteMachine() : @@ -674,6 +681,10 @@ class ConcreteMachine: // ensure memory starts in a random state Memory::Fuzz(ram_, sizeof(ram_)); + + // register this class as the sleep observer for the FDC + fdc_.set_sleep_observer(this); + fdc_is_sleeping_ = fdc_.is_sleeping(); } /// The entry point for performing a partial Z80 machine cycle. @@ -689,7 +700,7 @@ class ConcreteMachine: crtc_counter_ += cycle.length; Cycles crtc_cycles = crtc_counter_.divide_cycles(Cycles(4)); if(crtc_cycles > Cycles(0)) crtc_.run_for(crtc_cycles); - z80_.set_interrupt_line(interrupt_timer_.get_request()); + if(interrupt_timer_.request_has_changed()) z80_.set_interrupt_line(interrupt_timer_.get_request()); // TODO (in the player, not here): adapt it to accept an input clock rate and // run_for as HalfCycles @@ -699,7 +710,7 @@ class ConcreteMachine: ay_.run_for(cycle.length); // Clock the FDC, if connected, using a lazy scale by two - if(has_fdc_) fdc_.run_for(Cycles(cycle.length.as_int())); + if(has_fdc_ && !fdc_is_sleeping_) fdc_.run_for(Cycles(cycle.length.as_int())); // Update typing activity if(typer_) typer_->run_for(cycle.length); @@ -902,6 +913,11 @@ class ConcreteMachine: roms_[(int)type] = data; } + void set_component_is_sleeping(void *component, bool is_sleeping) { + // The FDC is the only thing this registers with as a sleep observer. + fdc_is_sleeping_ = is_sleeping; + } + #pragma mark - Keyboard void set_typer_for_string(const char *string) { @@ -995,7 +1011,7 @@ class ConcreteMachine: std::vector roms_[7]; int rom_model_; - bool has_fdc_; + bool has_fdc_, fdc_is_sleeping_; bool has_128k_; bool upper_rom_is_paged_; int upper_rom_;