From 2f53b105bb6aa4bee8aa12701210b5ed44891d6c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 27 Jun 2021 21:02:04 -0400 Subject: [PATCH] The Enterprise is now an Activity::Source; also sketches out the owner of Dave's timed interrupt logic. --- Machines/Enterprise/Dave.cpp | 19 ++++++++++++++ Machines/Enterprise/Dave.hpp | 28 ++++++++++++++++++-- Machines/Enterprise/EXDos.cpp | 6 +++++ Machines/Enterprise/EXDos.hpp | 2 ++ Machines/Enterprise/Enterprise.cpp | 41 +++++++++++++++++++----------- 5 files changed, 79 insertions(+), 17 deletions(-) diff --git a/Machines/Enterprise/Dave.cpp b/Machines/Enterprise/Dave.cpp index 87a83afed..7d354f98c 100644 --- a/Machines/Enterprise/Dave.cpp +++ b/Machines/Enterprise/Dave.cpp @@ -10,6 +10,8 @@ using namespace Enterprise::Dave; +// MARK: - Audio generator + Audio::Audio(Concurrency::DeferringAsyncTaskQueue &audio_queue) : audio_queue_(audio_queue) {} @@ -188,3 +190,20 @@ void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { )); } } + +// MARK: - Interrupt source + +uint8_t TimedInterruptSource::get_new_interrupts() { + const uint8_t result = interrupts_; + interrupts_ = 0; + return result; +} + +void TimedInterruptSource::write(uint16_t address, uint8_t value) { + (void)address; + (void)value; +} + +void TimedInterruptSource::run_for(Cycles cycles) { + (void)cycles; +} diff --git a/Machines/Enterprise/Dave.hpp b/Machines/Enterprise/Dave.hpp index 6f9b5fdfa..3953bc211 100644 --- a/Machines/Enterprise/Dave.hpp +++ b/Machines/Enterprise/Dave.hpp @@ -11,6 +11,7 @@ #include +#include "../../ClockReceiver/ClockReceiver.hpp" #include "../../Concurrency/AsyncTaskQueue.hpp" #include "../../Numeric/LFSR.hpp" #include "../../Outputs/Speaker/Implementation/SampleSource.hpp" @@ -18,9 +19,14 @@ namespace Enterprise { namespace Dave { +enum class Interrupt: uint8_t { + VariableFrequency = 0x02, + OneHz = 0x08, + Nick = 0x20, +}; + /*! - Models a subset of Dave's behaviour; memory mapping and interrupt status - is integrated into the main Enterprise machine. + Models the audio-production subset of Dave's behaviour. */ class Audio: public Outputs::Speaker::SampleSource { public: @@ -105,6 +111,24 @@ class Audio: public Outputs::Speaker::SampleSource { uint8_t poly_state_[4]; }; +/*! + Provides Dave's timed interrupts — those that are provided at 1 kHz, + 50 Hz or according to the rate of tone generators 0 or 1, plus the fixed + 1 Hz interrupt. + +*/ +class TimedInterruptSource { + public: + void write(uint16_t address, uint8_t value); + + uint8_t get_new_interrupts(); + + void run_for(Cycles); + + private: + uint8_t interrupts_ = 0; +}; + } } diff --git a/Machines/Enterprise/EXDos.cpp b/Machines/Enterprise/EXDos.cpp index ec7673407..015626e2a 100644 --- a/Machines/Enterprise/EXDos.cpp +++ b/Machines/Enterprise/EXDos.cpp @@ -70,3 +70,9 @@ void EXDos::set_motor_on(bool on) { // writing state, so plenty of work to do in general here. get_drive().set_motor_on(on); } + +void EXDos::set_activity_observer(Activity::Observer *observer) { + 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/Enterprise/EXDos.hpp b/Machines/Enterprise/EXDos.hpp index 42d8f81c1..33553be6a 100644 --- a/Machines/Enterprise/EXDos.hpp +++ b/Machines/Enterprise/EXDos.hpp @@ -23,6 +23,8 @@ class EXDos final : public WD::WD1770 { void set_control_register(uint8_t control); uint8_t get_control_register(); + void set_activity_observer(Activity::Observer *observer); + private: bool disk_did_change_ = false; diff --git a/Machines/Enterprise/Enterprise.cpp b/Machines/Enterprise/Enterprise.cpp index 806eb0ffe..d90eb0450 100644 --- a/Machines/Enterprise/Enterprise.cpp +++ b/Machines/Enterprise/Enterprise.cpp @@ -62,6 +62,7 @@ namespace Enterprise { */ template class ConcreteMachine: + public Activity::Source, public CPU::Z80::BusHandler, public Machine, public MachineTypes::AudioProducer, @@ -86,8 +87,8 @@ template class ConcreteMachine: min_ram_slot_(min_ram_slot(target)), z80_(*this), nick_(ram_.end() - 65536), - dave_(audio_queue_), - speaker_(dave_) { + dave_audio_(audio_queue_), + speaker_(dave_audio_) { // Request a clock of 4Mhz; this'll be mapped upwards for Nick and Dave elsewhere. set_clock_rate(4'000'000); @@ -226,7 +227,7 @@ template class ConcreteMachine: const auto nick = nick_.last_valid(); const bool nick_interrupt_line = nick->get_interrupt_line(); if(nick_interrupt_line && !previous_nick_interrupt_line_) { - set_interrupt(Interrupt::Nick, nick_.last_sequence_point_overrun()); + set_interrupts(uint8_t(Dave::Interrupt::Nick), nick_.last_sequence_point_overrun()); } previous_nick_interrupt_line_ = nick_interrupt_line; } @@ -306,6 +307,9 @@ template class ConcreteMachine: const HalfCycles full_length = cycle.length + penalty; time_since_audio_update_ += full_length; advance_nick(full_length); + if(dave_timer_ += full_length) { + set_interrupts(dave_timer_.last_valid()->get_new_interrupts(), dave_timer_.last_sequence_point_overrun()); + } // The WD/etc runs at a nominal 8Mhz. if constexpr (has_disk_controller) { @@ -387,7 +391,8 @@ template class ConcreteMachine: case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf: update_audio(); - dave_.write(address, *cycle.value); + dave_audio_.write(address, *cycle.value); + dave_timer_->write(address, *cycle.value); break; case 0xb4: @@ -442,12 +447,6 @@ template class ConcreteMachine: audio_queue_.perform(); } - inline void update_audio() { - // TODO: divide by only 8, letting Dave divide itself by a further 2 or 3 - // as per its own register. - speaker_.run_for(audio_queue_, time_since_audio_update_.divide_cycles(Cycles(16))); - } - private: // MARK: - Memory layout std::array ram_{}; @@ -560,12 +559,9 @@ template class ConcreteMachine: } // MARK: - Interrupts - enum class Interrupt: uint8_t { - Nick = 0x20 - }; uint8_t interrupt_mask_ = 0x00, interrupt_state_ = 0x00; - void set_interrupt(Interrupt mask, HalfCycles offset = HalfCycles(0)) { + void set_interrupts(uint8_t mask, HalfCycles offset = HalfCycles(0)) { interrupt_state_ |= uint8_t(mask); update_interrupts(offset); } @@ -580,12 +576,27 @@ template class ConcreteMachine: // Cf. timing guesses above. Concurrency::DeferringAsyncTaskQueue audio_queue_; - Dave::Audio dave_; + Dave::Audio dave_audio_; Outputs::Speaker::LowpassSpeaker speaker_; HalfCycles time_since_audio_update_; + // The following two should both use the same divider. + JustInTimeActor dave_timer_; + inline void update_audio() { + // TODO: divide by only 8, letting Dave divide itself by a further 2 or 3 + // as per its own register. + speaker_.run_for(audio_queue_, time_since_audio_update_.divide_cycles(Cycles(16))); + } + // MARK: - EXDos card. EXDos exdos_; + + // MARK: - Activity Source + void set_activity_observer(Activity::Observer *observer) final { + if constexpr (has_disk_controller) { + exdos_.set_activity_observer(observer); + } + } }; }