From f417fa82a4a9606c85b0d9b36021963e4646aefd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 1 Apr 2020 23:19:34 -0400 Subject: [PATCH 1/2] Splits 'CRTMachine' into three parts: ScanProducer, AudioProducer, TimedMachine. Simultaneously cleans up some of the naming conventions and tries to make things a bit more template-compatible. --- .../Implementation/MultiCRTMachine.cpp | 94 -------- .../Implementation/MultiJoystickMachine.cpp | 6 +- .../Implementation/MultiJoystickMachine.hpp | 2 +- .../Implementation/MultiKeyboardMachine.cpp | 4 +- .../Implementation/MultiKeyboardMachine.hpp | 8 +- .../Implementation/MultiMediaTarget.cpp | 2 +- .../Implementation/MultiMediaTarget.hpp | 4 +- .../Implementation/MultiProducer.cpp | 105 +++++++++ ...{MultiCRTMachine.hpp => MultiProducer.hpp} | 139 ++++++----- .../Implementation/MultiSpeaker.cpp | 4 +- .../Dynamic/MultiMachine/MultiMachine.cpp | 80 +++---- .../Dynamic/MultiMachine/MultiMachine.hpp | 23 +- Machines/AmstradCPC/AmstradCPC.cpp | 15 +- Machines/AmstradCPC/Keyboard.cpp | 8 +- Machines/AmstradCPC/Keyboard.hpp | 2 +- Machines/Apple/AppleII/AppleII.cpp | 15 +- Machines/Apple/Macintosh/Keyboard.cpp | 2 +- Machines/Apple/Macintosh/Keyboard.hpp | 2 +- Machines/Apple/Macintosh/Macintosh.cpp | 17 +- Machines/Atari/2600/Atari2600.cpp | 9 +- Machines/Atari/ST/AtariST.cpp | 18 +- Machines/Atari/ST/IntelligentKeyboard.cpp | 2 +- Machines/Atari/ST/IntelligentKeyboard.hpp | 2 +- Machines/AudioProducer.hpp | 29 +++ Machines/CRTMachine.hpp | 215 ------------------ Machines/ColecoVision/ColecoVision.cpp | 9 +- Machines/Commodore/Vic-20/Keyboard.cpp | 8 +- Machines/Commodore/Vic-20/Keyboard.hpp | 2 +- Machines/Commodore/Vic-20/Vic20.cpp | 15 +- Machines/DynamicMachine.hpp | 43 +++- Machines/Electron/Electron.cpp | 12 +- Machines/Electron/Keyboard.cpp | 10 +- Machines/Electron/Keyboard.hpp | 2 +- Machines/JoystickMachine.hpp | 4 +- Machines/KeyboardMachine.cpp | 14 +- Machines/KeyboardMachine.hpp | 8 +- Machines/MSX/Keyboard.cpp | 2 +- Machines/MSX/Keyboard.hpp | 2 +- Machines/MSX/MSX.cpp | 15 +- Machines/MachineTypes.hpp | 25 ++ Machines/MasterSystem/MasterSystem.cpp | 14 +- Machines/MediaTarget.hpp | 4 +- Machines/MouseMachine.hpp | 5 +- Machines/Oric/Keyboard.cpp | 8 +- Machines/Oric/Keyboard.hpp | 2 +- Machines/Oric/Oric.cpp | 12 +- Machines/ScanProducer.hpp | 104 +++++++++ Machines/TimedMachine.hpp | 89 ++++++++ Machines/Utility/MachineForTarget.hpp | 1 + Machines/Utility/TypedDynamicMachine.hpp | 37 ++- Machines/Utility/Typer.cpp | 6 +- Machines/Utility/Typer.hpp | 8 +- Machines/ZX8081/Keyboard.cpp | 8 +- Machines/ZX8081/Keyboard.hpp | 2 +- Machines/ZX8081/ZX8081.cpp | 12 +- .../Clock Signal.xcodeproj/project.pbxproj | 26 ++- .../Mac/Clock Signal/Machine/CSMachine.mm | 30 +-- OSBindings/SDL/main.cpp | 30 +-- 58 files changed, 725 insertions(+), 651 deletions(-) delete mode 100644 Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp create mode 100644 Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp rename Analyser/Dynamic/MultiMachine/Implementation/{MultiCRTMachine.hpp => MultiProducer.hpp} (60%) create mode 100644 Machines/AudioProducer.hpp delete mode 100644 Machines/CRTMachine.hpp create mode 100644 Machines/MachineTypes.hpp create mode 100644 Machines/ScanProducer.hpp create mode 100644 Machines/TimedMachine.hpp diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp deleted file mode 100644 index 6c1c4c090..000000000 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// -// MultiCRTMachine.cpp -// Clock Signal -// -// Created by Thomas Harte on 29/01/2018. -// Copyright 2018 Thomas Harte. All rights reserved. -// - -#include "MultiCRTMachine.hpp" - -#include -#include - -using namespace Analyser::Dynamic; - -MultiCRTMachine::MultiCRTMachine(const std::vector> &machines, std::recursive_mutex &machines_mutex) : - machines_(machines), machines_mutex_(machines_mutex), queues_(machines.size()) { - speaker_ = MultiSpeaker::create(machines); -} - -void MultiCRTMachine::perform_parallel(const std::function &function) { - // Apply a blunt force parallelisation of the machines; each run_for is dispatched - // to a separate queue and this queue will block until all are done. - volatile std::size_t outstanding_machines; - std::condition_variable condition; - std::mutex mutex; - { - std::lock_guard machines_lock(machines_mutex_); - std::lock_guard lock(mutex); - outstanding_machines = machines_.size(); - - for(std::size_t index = 0; index < machines_.size(); ++index) { - CRTMachine::Machine *crt_machine = machines_[index]->crt_machine(); - queues_[index].enqueue([&mutex, &condition, crt_machine, function, &outstanding_machines]() { - if(crt_machine) function(crt_machine); - - std::lock_guard lock(mutex); - outstanding_machines--; - condition.notify_all(); - }); - } - } - - std::unique_lock lock(mutex); - condition.wait(lock, [&outstanding_machines] { return !outstanding_machines; }); -} - -void MultiCRTMachine::perform_serial(const std::function &function) { - std::lock_guard machines_lock(machines_mutex_); - for(const auto &machine: machines_) { - CRTMachine::Machine *const crt_machine = machine->crt_machine(); - if(crt_machine) function(crt_machine); - } -} - -void MultiCRTMachine::set_scan_target(Outputs::Display::ScanTarget *scan_target) { - scan_target_ = scan_target; - - CRTMachine::Machine *const crt_machine = machines_.front()->crt_machine(); - if(crt_machine) crt_machine->set_scan_target(scan_target); -} - -Outputs::Display::ScanStatus MultiCRTMachine::get_scan_status() const { - CRTMachine::Machine *const crt_machine = machines_.front()->crt_machine(); - if(crt_machine) crt_machine->get_scan_status(); - - return Outputs::Display::ScanStatus(); -} - -Outputs::Speaker::Speaker *MultiCRTMachine::get_speaker() { - return speaker_; -} - -void MultiCRTMachine::run_for(Time::Seconds duration) { - perform_parallel([duration](::CRTMachine::Machine *machine) { - if(machine->get_confidence() >= 0.01f) machine->run_for(duration); - }); - - if(delegate_) delegate_->multi_crt_did_run_machines(); -} - -void MultiCRTMachine::did_change_machine_order() { - if(scan_target_) scan_target_->will_change_owner(); - - perform_serial([](::CRTMachine::Machine *machine) { - machine->set_scan_target(nullptr); - }); - CRTMachine::Machine *const crt_machine = machines_.front()->crt_machine(); - if(crt_machine) crt_machine->set_scan_target(scan_target_); - - if(speaker_) { - speaker_->set_new_front_machine(machines_.front().get()); - } -} diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp index 2749cae44..c73bd37b7 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.cpp @@ -16,7 +16,7 @@ namespace { class MultiJoystick: public Inputs::Joystick { public: - MultiJoystick(std::vector &machines, std::size_t index) { + MultiJoystick(std::vector &machines, std::size_t index) { for(const auto &machine: machines) { const auto &joysticks = machine->get_joysticks(); if(joysticks.size() >= index) { @@ -67,9 +67,9 @@ class MultiJoystick: public Inputs::Joystick { MultiJoystickMachine::MultiJoystickMachine(const std::vector> &machines) { std::size_t total_joysticks = 0; - std::vector joystick_machines; + std::vector joystick_machines; for(const auto &machine: machines) { - JoystickMachine::Machine *joystick_machine = machine->joystick_machine(); + auto joystick_machine = machine->joystick_machine(); if(joystick_machine) { joystick_machines.push_back(joystick_machine); total_joysticks = std::max(total_joysticks, joystick_machine->get_joysticks().size()); diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp index 7daf1a7da..7c5457a16 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiJoystickMachine.hpp @@ -23,7 +23,7 @@ namespace Dynamic { Makes a static internal copy of the list of machines; makes no guarantees about the order of delivered messages. */ -class MultiJoystickMachine: public JoystickMachine::Machine { +class MultiJoystickMachine: public MachineTypes::JoystickMachine { public: MultiJoystickMachine(const std::vector> &machines); diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp index c27794e76..d7ec688c6 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp @@ -13,7 +13,7 @@ using namespace Analyser::Dynamic; MultiKeyboardMachine::MultiKeyboardMachine(const std::vector> &machines) : keyboard_(machines_) { for(const auto &machine: machines) { - KeyboardMachine::Machine *keyboard_machine = machine->keyboard_machine(); + auto keyboard_machine = machine->keyboard_machine(); if(keyboard_machine) machines_.push_back(keyboard_machine); } } @@ -48,7 +48,7 @@ Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() { return keyboard_; } -MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines) +MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::MachineTypes::KeyboardMachine *> &machines) : machines_(machines) { for(const auto &machine: machines_) { observed_keys_.insert(machine->get_keyboard().observed_keys().begin(), machine->get_keyboard().observed_keys().end()); diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp index bc0803d83..723cb05a7 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp @@ -24,13 +24,13 @@ namespace Dynamic { Makes a static internal copy of the list of machines; makes no guarantees about the order of delivered messages. */ -class MultiKeyboardMachine: public KeyboardMachine::Machine { +class MultiKeyboardMachine: public MachineTypes::KeyboardMachine { private: - std::vector<::KeyboardMachine::Machine *> machines_; + std::vector machines_; class MultiKeyboard: public Inputs::Keyboard { public: - MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines); + MultiKeyboard(const std::vector &machines); bool set_key_pressed(Key key, char value, bool is_pressed) final; void reset_all_keys() final; @@ -38,7 +38,7 @@ class MultiKeyboardMachine: public KeyboardMachine::Machine { bool is_exclusive() final; private: - const std::vector<::KeyboardMachine::Machine *> &machines_; + const std::vector &machines_; std::set observed_keys_; bool is_exclusive_ = false; }; diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.cpp index 328586731..5070a9c9d 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.cpp @@ -12,7 +12,7 @@ using namespace Analyser::Dynamic; MultiMediaTarget::MultiMediaTarget(const std::vector> &machines) { for(const auto &machine: machines) { - MediaTarget::Machine *media_target = machine->media_target(); + auto media_target = machine->media_target(); if(media_target) targets_.push_back(media_target); } } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp index 14dd5afc1..274261462 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiMediaTarget.hpp @@ -24,7 +24,7 @@ namespace Dynamic { Makes a static internal copy of the list of machines; makes no guarantees about the order of delivered messages. */ -struct MultiMediaTarget: public MediaTarget::Machine { +struct MultiMediaTarget: public MachineTypes::MediaTarget { public: MultiMediaTarget(const std::vector> &machines); @@ -32,7 +32,7 @@ struct MultiMediaTarget: public MediaTarget::Machine { bool insert_media(const Analyser::Static::Media &media) final; private: - std::vector targets_; + std::vector targets_; }; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp new file mode 100644 index 000000000..93b0fb25c --- /dev/null +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp @@ -0,0 +1,105 @@ +// +// MultiCRTMachine.cpp +// Clock Signal +// +// Created by Thomas Harte on 29/01/2018. +// Copyright 2018 Thomas Harte. All rights reserved. +// + +#include "MultiProducer.hpp" + +#include +#include + +using namespace Analyser::Dynamic; + +// MARK: - MultiInterface + +template +void MultiInterface::perform_parallel(const std::function &function) { + // Apply a blunt force parallelisation of the machines; each run_for is dispatched + // to a separate queue and this queue will block until all are done. + volatile std::size_t outstanding_machines; + std::condition_variable condition; + std::mutex mutex; + { + std::lock_guard machines_lock(machines_mutex_); + std::lock_guard lock(mutex); + outstanding_machines = machines_.size(); + + for(std::size_t index = 0; index < machines_.size(); ++index) { + const auto machine = ::Machine::get(*machines_[index].get()); + queues_[index].enqueue([&mutex, &condition, machine, function, &outstanding_machines]() { + if(machine) function(machine); + + std::lock_guard lock(mutex); + outstanding_machines--; + condition.notify_all(); + }); + } + } + + std::unique_lock lock(mutex); + condition.wait(lock, [&outstanding_machines] { return !outstanding_machines; }); +} + +template +void MultiInterface::perform_serial(const std::function &function) { + std::lock_guard machines_lock(machines_mutex_); + for(const auto &machine: machines_) { + const auto typed_machine = ::Machine::get(*machine.get()); + if(typed_machine) function(typed_machine); + } +} + +// MARK: - MultiScanProducer +void MultiScanProducer::set_scan_target(Outputs::Display::ScanTarget *scan_target) { + scan_target_ = scan_target; + + std::lock_guard machines_lock(machines_mutex_); + const auto machine = machines_.front()->scan_producer(); + if(machine) machine->set_scan_target(scan_target); +} + +Outputs::Display::ScanStatus MultiScanProducer::get_scan_status() const { + std::lock_guard machines_lock(machines_mutex_); + const auto machine = machines_.front()->scan_producer(); + if(machine) return machine->get_scan_status(); + return Outputs::Display::ScanStatus(); +} + +void MultiScanProducer::did_change_machine_order() { + if(scan_target_) scan_target_->will_change_owner(); + + perform_serial([](MachineTypes::ScanProducer *machine) { + machine->set_scan_target(nullptr); + }); + std::lock_guard machines_lock(machines_mutex_); + const auto machine = machines_.front()->scan_producer(); + if(machine) machine->set_scan_target(scan_target_); +} + +// MARK: - MultiAudioProducer +MultiAudioProducer::MultiAudioProducer(const std::vector> &machines, std::recursive_mutex &machines_mutex) : MultiInterface(machines, machines_mutex) { + speaker_ = MultiSpeaker::create(machines); +} + +Outputs::Speaker::Speaker *MultiAudioProducer::get_speaker() { + return speaker_; +} + +void MultiAudioProducer::did_change_machine_order() { + if(speaker_) { + speaker_->set_new_front_machine(machines_.front().get()); + } +} + +// MARK: - MultiTimedMachine + +void MultiTimedMachine::run_for(Time::Seconds duration) { + perform_parallel([duration](::MachineTypes::TimedMachine *machine) { + if(machine->get_confidence() >= 0.01f) machine->run_for(duration); + }); + + if(delegate_) delegate_->did_run_machines(this); +} diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp similarity index 60% rename from Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp rename to Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp index 7e3535e53..e5f2a85ac 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp @@ -10,7 +10,7 @@ #define MultiCRTMachine_hpp #include "../../../../Concurrency/AsyncTaskQueue.hpp" -#include "../../../../Machines/CRTMachine.hpp" +#include "../../../../Machines/MachineTypes.hpp" #include "../../../../Machines/DynamicMachine.hpp" #include "MultiSpeaker.hpp" @@ -22,6 +22,91 @@ namespace Analyser { namespace Dynamic { +template class MultiInterface { + public: + MultiInterface(const std::vector> &machines, std::recursive_mutex &machines_mutex) : + machines_(machines), machines_mutex_(machines_mutex), queues_(machines.size()) {} + + protected: + /*! + Performs a parallel for operation across all machines, performing the supplied + function on each and returning only once all applications have completed. + + No guarantees are extended as to which thread operations will occur on. + */ + void perform_parallel(const std::function &); + + /*! + Performs a serial for operation across all machines, performing the supplied + function on each on the calling thread. + */ + void perform_serial(const std::function &); + + protected: + const std::vector> &machines_; + std::recursive_mutex &machines_mutex_; + + private: + std::vector queues_; +}; + +class MultiTimedMachine: public MultiInterface, public MachineTypes::TimedMachine { + public: + using MultiInterface::MultiInterface; + + /*! + Provides a mechanism by which a delegate can be informed each time a call to run_for has + been received. + */ + struct Delegate { + virtual void did_run_machines(MultiTimedMachine *) = 0; + }; + /// Sets @c delegate as the receiver of delegate messages. + void set_delegate(Delegate *delegate) { + delegate_ = delegate; + } + + void run_for(Time::Seconds duration) final; + + private: + void run_for(const Cycles cycles) final {} + Delegate *delegate_ = nullptr; +}; + +class MultiScanProducer: public MultiInterface, public MachineTypes::ScanProducer { + public: + using MultiInterface::MultiInterface; + + /*! + Informs the MultiScanProducer that the order of machines has changed; it + uses this as an opportunity to synthesis any CRTMachine::Machine::Delegate messages that + are necessary to bridge the gap between one machine and the next. + */ + void did_change_machine_order(); + + void set_scan_target(Outputs::Display::ScanTarget *scan_target) final; + Outputs::Display::ScanStatus get_scan_status() const final; + + private: + Outputs::Display::ScanTarget *scan_target_ = nullptr; +}; + +class MultiAudioProducer: public MultiInterface, public MachineTypes::AudioProducer { + public: + MultiAudioProducer(const std::vector> &machines, std::recursive_mutex &machines_mutex); + + /*! + Informs the MultiAudio that the order of machines has changed; it + uses this as an opportunity to switch speaker delegates as appropriate. + */ + void did_change_machine_order(); + + Outputs::Speaker::Speaker *get_speaker() final; + + private: + MultiSpeaker *speaker_ = nullptr; +}; + /*! Provides a class that multiplexes the CRT machine interface to multiple machines. @@ -29,58 +114,6 @@ namespace Dynamic { acquiring a supplied mutex. The owner should also call did_change_machine_order() if the order of machines changes. */ -class MultiCRTMachine: public CRTMachine::Machine { - public: - MultiCRTMachine(const std::vector> &machines, std::recursive_mutex &machines_mutex); - - /*! - Informs the MultiCRTMachine that the order of machines has changed; the MultiCRTMachine - uses this as an opportunity to synthesis any CRTMachine::Machine::Delegate messages that - are necessary to bridge the gap between one machine and the next. - */ - void did_change_machine_order(); - - /*! - Provides a mechanism by which a delegate can be informed each time a call to run_for has - been received. - */ - struct Delegate { - virtual void multi_crt_did_run_machines() = 0; - }; - /// Sets @c delegate as the receiver of delegate messages. - void set_delegate(Delegate *delegate) { - delegate_ = delegate; - } - - // Below is the standard CRTMachine::Machine interface; see there for documentation. - void set_scan_target(Outputs::Display::ScanTarget *scan_target) final; - Outputs::Display::ScanStatus get_scan_status() const final; - Outputs::Speaker::Speaker *get_speaker() final; - void run_for(Time::Seconds duration) final; - - private: - void run_for(const Cycles cycles) final {} - const std::vector> &machines_; - std::recursive_mutex &machines_mutex_; - std::vector queues_; - MultiSpeaker *speaker_ = nullptr; - Delegate *delegate_ = nullptr; - Outputs::Display::ScanTarget *scan_target_ = nullptr; - - /*! - Performs a parallel for operation across all machines, performing the supplied - function on each and returning only once all applications have completed. - - No guarantees are extended as to which thread operations will occur on. - */ - void perform_parallel(const std::function &); - - /*! - Performs a serial for operation across all machines, performing the supplied - function on each on the calling thread. - */ - void perform_serial(const std::function &); -}; } } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp index eb833a0c6..8164cb383 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiSpeaker.cpp @@ -13,7 +13,7 @@ using namespace Analyser::Dynamic; MultiSpeaker *MultiSpeaker::create(const std::vector> &machines) { std::vector speakers; for(const auto &machine: machines) { - Outputs::Speaker::Speaker *speaker = machine->crt_machine()->get_speaker(); + Outputs::Speaker::Speaker *speaker = machine->audio_producer()->get_speaker(); if(speaker) speakers.push_back(speaker); } if(speakers.empty()) return nullptr; @@ -85,7 +85,7 @@ void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) { void MultiSpeaker::set_new_front_machine(::Machine::DynamicMachine *machine) { { std::lock_guard lock_guard(front_speaker_mutex_); - front_speaker_ = machine->crt_machine()->get_speaker(); + front_speaker_ = machine->audio_producer()->get_speaker(); } if(delegate_) { delegate_->speaker_did_change_input_clock(this); diff --git a/Analyser/Dynamic/MultiMachine/MultiMachine.cpp b/Analyser/Dynamic/MultiMachine/MultiMachine.cpp index 4f057c3cf..f4b25b876 100644 --- a/Analyser/Dynamic/MultiMachine/MultiMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/MultiMachine.cpp @@ -16,74 +16,55 @@ using namespace Analyser::Dynamic; MultiMachine::MultiMachine(std::vector> &&machines) : machines_(std::move(machines)), configurable_(machines_), - crt_machine_(machines_, machines_mutex_), - joystick_machine_(machines), + timed_machine_(machines_, machines_mutex_), + scan_producer_(machines_, machines_mutex_), + audio_producer_(machines_, machines_mutex_), + joystick_machine_(machines_), keyboard_machine_(machines_), media_target_(machines_) { - crt_machine_.set_delegate(this); + timed_machine_.set_delegate(this); } Activity::Source *MultiMachine::activity_source() { return nullptr; // TODO } -MediaTarget::Machine *MultiMachine::media_target() { - if(has_picked_) { - return machines_.front()->media_target(); - } else { - return &media_target_; +#define Provider(type, name, member) \ + type *MultiMachine::name() { \ + if(has_picked_) { \ + return machines_.front()->name(); \ + } else { \ + return &member; \ + } \ } -} -CRTMachine::Machine *MultiMachine::crt_machine() { - if(has_picked_) { - return machines_.front()->crt_machine(); - } else { - return &crt_machine_; - } -} +Provider(Configurable::Device, configurable_device, configurable_) +Provider(MachineTypes::TimedMachine, timed_machine, timed_machine_) +Provider(MachineTypes::ScanProducer, scan_producer, scan_producer_) +Provider(MachineTypes::AudioProducer, audio_producer, audio_producer_) +Provider(MachineTypes::JoystickMachine, joystick_machine, joystick_machine_) +Provider(MachineTypes::KeyboardMachine, keyboard_machine, keyboard_machine_) +Provider(MachineTypes::MediaTarget, media_target, media_target_) -JoystickMachine::Machine *MultiMachine::joystick_machine() { - if(has_picked_) { - return machines_.front()->joystick_machine(); - } else { - return &joystick_machine_; - } -} - -KeyboardMachine::Machine *MultiMachine::keyboard_machine() { - if(has_picked_) { - return machines_.front()->keyboard_machine(); - } else { - return &keyboard_machine_; - } -} - -MouseMachine::Machine *MultiMachine::mouse_machine() { +MachineTypes::MouseMachine *MultiMachine::mouse_machine() { // TODO. return nullptr; } -Configurable::Device *MultiMachine::configurable_device() { - if(has_picked_) { - return machines_.front()->configurable_device(); - } else { - return &configurable_; - } -} +#undef Provider bool MultiMachine::would_collapse(const std::vector> &machines) { return - (machines.front()->crt_machine()->get_confidence() > 0.9f) || - (machines.front()->crt_machine()->get_confidence() >= 2.0f * machines[1]->crt_machine()->get_confidence()); + (machines.front()->timed_machine()->get_confidence() > 0.9f) || + (machines.front()->timed_machine()->get_confidence() >= 2.0f * machines[1]->timed_machine()->get_confidence()); } -void MultiMachine::multi_crt_did_run_machines() { +void MultiMachine::did_run_machines(MultiTimedMachine *) { std::lock_guard machines_lock(machines_mutex_); #ifndef NDEBUG for(const auto &machine: machines_) { - CRTMachine::Machine *crt = machine->crt_machine(); - LOGNBR(PADHEX(2) << crt->get_confidence() << " " << crt->debug_type() << "; "); + auto timed_machine = machine->timed_machine(); + LOGNBR(PADHEX(2) << timed_machine->get_confidence() << " " << timed_machine->debug_type() << "; "); } LOGNBR(std::endl); #endif @@ -91,13 +72,14 @@ void MultiMachine::multi_crt_did_run_machines() { DynamicMachine *front = machines_.front().get(); std::stable_sort(machines_.begin(), machines_.end(), [] (const std::unique_ptr &lhs, const std::unique_ptr &rhs){ - CRTMachine::Machine *lhs_crt = lhs->crt_machine(); - CRTMachine::Machine *rhs_crt = rhs->crt_machine(); - return lhs_crt->get_confidence() > rhs_crt->get_confidence(); + auto lhs_timed = lhs->timed_machine(); + auto rhs_timed = rhs->timed_machine(); + return lhs_timed->get_confidence() > rhs_timed->get_confidence(); }); if(machines_.front().get() != front) { - crt_machine_.did_change_machine_order(); + scan_producer_.did_change_machine_order(); + audio_producer_.did_change_machine_order(); } if(would_collapse(machines_)) { diff --git a/Analyser/Dynamic/MultiMachine/MultiMachine.hpp b/Analyser/Dynamic/MultiMachine/MultiMachine.hpp index 4968cbdd5..ec7e10f48 100644 --- a/Analyser/Dynamic/MultiMachine/MultiMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/MultiMachine.hpp @@ -11,8 +11,9 @@ #include "../../../Machines/DynamicMachine.hpp" +#include "Implementation/MultiProducer.hpp" #include "Implementation/MultiConfigurable.hpp" -#include "Implementation/MultiCRTMachine.hpp" +#include "Implementation/MultiProducer.hpp" #include "Implementation/MultiJoystickMachine.hpp" #include "Implementation/MultiKeyboardMachine.hpp" #include "Implementation/MultiMediaTarget.hpp" @@ -38,7 +39,7 @@ namespace Dynamic { If confidence for any machine becomes disproportionately low compared to the others in the set, that machine stops running. */ -class MultiMachine: public ::Machine::DynamicMachine, public MultiCRTMachine::Delegate { +class MultiMachine: public ::Machine::DynamicMachine, public MultiTimedMachine::Delegate { public: /*! Allows a potential MultiMachine creator to enquire as to whether there's any benefit in @@ -52,21 +53,25 @@ class MultiMachine: public ::Machine::DynamicMachine, public MultiCRTMachine::De Activity::Source *activity_source() final; Configurable::Device *configurable_device() final; - CRTMachine::Machine *crt_machine() final; - JoystickMachine::Machine *joystick_machine() final; - MouseMachine::Machine *mouse_machine() final; - KeyboardMachine::Machine *keyboard_machine() final; - MediaTarget::Machine *media_target() final; + MachineTypes::TimedMachine *timed_machine() final; + MachineTypes::ScanProducer *scan_producer() final; + MachineTypes::AudioProducer *audio_producer() final; + MachineTypes::JoystickMachine *joystick_machine() final; + MachineTypes::KeyboardMachine *keyboard_machine() final; + MachineTypes::MouseMachine *mouse_machine() final; + MachineTypes::MediaTarget *media_target() final; void *raw_pointer() final; private: - void multi_crt_did_run_machines() final; + void did_run_machines(MultiTimedMachine *) final; std::vector> machines_; std::recursive_mutex machines_mutex_; MultiConfigurable configurable_; - MultiCRTMachine crt_machine_; + MultiTimedMachine timed_machine_; + MultiScanProducer scan_producer_; + MultiAudioProducer audio_producer_; MultiJoystickMachine joystick_machine_; MultiKeyboardMachine keyboard_machine_; MultiMediaTarget media_target_; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 85910387b..1d4db3b8c 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -21,10 +21,7 @@ #include "../Utility/Typer.hpp" #include "../../Activity/Source.hpp" -#include "../MediaTarget.hpp" -#include "../CRTMachine.hpp" -#include "../JoystickMachine.hpp" -#include "../KeyboardMachine.hpp" +#include "../MachineTypes.hpp" #include "../../Storage/Tape/Tape.hpp" @@ -782,14 +779,16 @@ class i8255PortHandler : public Intel::i8255::PortHandler { The actual Amstrad CPC implementation; tying the 8255, 6845 and AY to the Z80. */ template class ConcreteMachine: - public CRTMachine::Machine, - public MediaTarget::Machine, - public KeyboardMachine::MappedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::TimedMachine, + public MachineTypes::MediaTarget, + public MachineTypes::MappedKeyboardMachine, + public MachineTypes::JoystickMachine, public Utility::TypeRecipient, public CPU::Z80::BusHandler, public ClockingHint::Observer, public Configurable::Device, - public JoystickMachine::Machine, public Machine, public Activity::Source { public: diff --git a/Machines/AmstradCPC/Keyboard.cpp b/Machines/AmstradCPC/Keyboard.cpp index 5f00ea55b..21aee331b 100644 --- a/Machines/AmstradCPC/Keyboard.cpp +++ b/Machines/AmstradCPC/Keyboard.cpp @@ -74,13 +74,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(KeypadDelete, KeyDelete); } #undef BIND - return KeyboardMachine::MappedMachine::KeyNotMapped; + return MachineTypes::MappedKeyboardMachine::KeyNotMapped; } uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define X {KeyboardMachine::MappedMachine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define SHIFT(...) {KeyShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped} static KeySequence key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/AmstradCPC/Keyboard.hpp b/Machines/AmstradCPC/Keyboard.hpp index 5aadbf5e3..b477b618a 100644 --- a/Machines/AmstradCPC/Keyboard.hpp +++ b/Machines/AmstradCPC/Keyboard.hpp @@ -33,7 +33,7 @@ enum Key: uint16_t { #undef Line }; -struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { +struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 7cff092cc..51253f364 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -9,10 +9,7 @@ #include "AppleII.hpp" #include "../../../Activity/Source.hpp" -#include "../../MediaTarget.hpp" -#include "../../CRTMachine.hpp" -#include "../../JoystickMachine.hpp" -#include "../../KeyboardMachine.hpp" +#include "../../MachineTypes.hpp" #include "../../Utility/MemoryFuzzer.hpp" #include "../../Utility/StringSerialiser.hpp" @@ -40,15 +37,17 @@ namespace II { #define is_iie() ((model == Analyser::Static::AppleII::Target::Model::IIe) || (model == Analyser::Static::AppleII::Target::Model::EnhancedIIe)) template class ConcreteMachine: - public CRTMachine::Machine, - public MediaTarget::Machine, - public KeyboardMachine::MappedMachine, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::MediaTarget, + public MachineTypes::MappedKeyboardMachine, + public MachineTypes::JoystickMachine, public CPU::MOS6502::BusHandler, public Inputs::Keyboard, public Configurable::Device, public Apple::II::Machine, public Activity::Source, - public JoystickMachine::Machine, public Apple::II::Card::Delegate { private: struct VideoBusHandler : public Apple::II::Video::BusHandler { diff --git a/Machines/Apple/Macintosh/Keyboard.cpp b/Machines/Apple/Macintosh/Keyboard.cpp index af3f3f894..c1c5a5ae6 100644 --- a/Machines/Apple/Macintosh/Keyboard.cpp +++ b/Machines/Apple/Macintosh/Keyboard.cpp @@ -14,7 +14,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { using Key = Inputs::Keyboard::Key; using MacKey = Apple::Macintosh::Key; switch(key) { - default: return KeyboardMachine::MappedMachine::KeyNotMapped; + default: return MachineTypes::MappedKeyboardMachine::KeyNotMapped; #define Bind(x, y) case Key::x: return uint16_t(y) diff --git a/Machines/Apple/Macintosh/Keyboard.hpp b/Machines/Apple/Macintosh/Keyboard.hpp index ae5bfdf7e..158776991 100644 --- a/Machines/Apple/Macintosh/Keyboard.hpp +++ b/Machines/Apple/Macintosh/Keyboard.hpp @@ -290,7 +290,7 @@ class Keyboard { /*! Provides a mapping from idiomatic PC keys to Macintosh keys. */ -class KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { +class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) final; }; diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 890db6619..03f025a4c 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -16,11 +16,8 @@ #include "RealTimeClock.hpp" #include "Video.hpp" +#include "../../MachineTypes.hpp" #include "../../../Activity/Source.hpp" -#include "../../CRTMachine.hpp" -#include "../../KeyboardMachine.hpp" -#include "../../MediaTarget.hpp" -#include "../../MouseMachine.hpp" #include "../../../Configurable/Configurable.hpp" #include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp" @@ -59,11 +56,13 @@ namespace Macintosh { template class ConcreteMachine: public Machine, - public CRTMachine::Machine, - public MediaTarget::Machine, - public MouseMachine::Machine, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::MediaTarget, + public MachineTypes::MouseMachine, + public MachineTypes::MappedKeyboardMachine, public CPU::MC68000::BusHandler, - public KeyboardMachine::MappedMachine, public Zilog::SCC::z8530::Delegate, public Activity::Source, public Configurable::Device, @@ -73,7 +72,7 @@ template class ConcreteMachin using Target = Analyser::Static::Macintosh::Target; ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : - KeyboardMachine::MappedMachine({ + MachineTypes::MappedKeyboardMachine({ Inputs::Keyboard::Key::LeftShift, Inputs::Keyboard::Key::RightShift, Inputs::Keyboard::Key::LeftOption, Inputs::Keyboard::Key::RightOption, Inputs::Keyboard::Key::LeftMeta, Inputs::Keyboard::Key::RightMeta, diff --git a/Machines/Atari/2600/Atari2600.cpp b/Machines/Atari/2600/Atari2600.cpp index 7497a4bd6..17ad8da4b 100644 --- a/Machines/Atari/2600/Atari2600.cpp +++ b/Machines/Atari/2600/Atari2600.cpp @@ -11,8 +11,7 @@ #include #include -#include "../../CRTMachine.hpp" -#include "../../JoystickMachine.hpp" +#include "../../MachineTypes.hpp" #include "../../../Analyser/Static/Atari2600/Target.hpp" @@ -76,8 +75,10 @@ using Target = Analyser::Static::Atari2600::Target; class ConcreteMachine: public Machine, - public CRTMachine::Machine, - public JoystickMachine::Machine { + public MachineTypes::TimedMachine, + public MachineTypes::AudioProducer, + public MachineTypes::ScanProducer, + public MachineTypes::JoystickMachine { public: ConcreteMachine(const Target &target) : frequency_mismatch_warner_(*this) { const std::vector &rom = target.media.cartridges.front()->get_segments().front().data; diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 504e43400..fa566ba04 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -8,11 +8,7 @@ #include "AtariST.hpp" -#include "../../CRTMachine.hpp" -#include "../../JoystickMachine.hpp" -#include "../../KeyboardMachine.hpp" -#include "../../MouseMachine.hpp" -#include "../../MediaTarget.hpp" +#include "../../MachineTypes.hpp" #include "../../../Activity/Source.hpp" //#define LOG_TRACE @@ -48,16 +44,18 @@ using Target = Analyser::Static::Target; class ConcreteMachine: public Atari::ST::Machine, public CPU::MC68000::BusHandler, - public CRTMachine::Machine, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::MouseMachine, + public MachineTypes::JoystickMachine, + public MachineTypes::MappedKeyboardMachine, + public MachineTypes::MediaTarget, public ClockingHint::Observer, public Motorola::ACIA::ACIA::InterruptDelegate, public Motorola::MFP68901::MFP68901::InterruptDelegate, public DMAController::Delegate, - public MouseMachine::Machine, - public JoystickMachine::Machine, - public KeyboardMachine::MappedMachine, public Activity::Source, - public MediaTarget::Machine, public GI::AY38910::PortHandler, public Configurable::Device, public Video::RangeObserver { diff --git a/Machines/Atari/ST/IntelligentKeyboard.cpp b/Machines/Atari/ST/IntelligentKeyboard.cpp index e9b5fbd42..8db99a9cb 100644 --- a/Machines/Atari/ST/IntelligentKeyboard.cpp +++ b/Machines/Atari/ST/IntelligentKeyboard.cpp @@ -365,7 +365,7 @@ uint16_t IntelligentKeyboard::KeyboardMapper::mapped_key_for_key(Inputs::Keyboar using Key = Inputs::Keyboard::Key; using STKey = Atari::ST::Key; switch(key) { - default: return KeyboardMachine::MappedMachine::KeyNotMapped; + default: return MachineTypes::MappedKeyboardMachine::KeyNotMapped; #define Bind(x, y) case Key::x: return uint16_t(STKey::y) #define QBind(x) case Key::x: return uint16_t(STKey::x) diff --git a/Machines/Atari/ST/IntelligentKeyboard.hpp b/Machines/Atari/ST/IntelligentKeyboard.hpp index ec02ff23d..f7fe6ba00 100644 --- a/Machines/Atari/ST/IntelligentKeyboard.hpp +++ b/Machines/Atari/ST/IntelligentKeyboard.hpp @@ -62,7 +62,7 @@ class IntelligentKeyboard: void run_for(HalfCycles duration); void set_key_state(Key key, bool is_pressed); - class KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { + class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) final; }; diff --git a/Machines/AudioProducer.hpp b/Machines/AudioProducer.hpp new file mode 100644 index 000000000..2bef3b84b --- /dev/null +++ b/Machines/AudioProducer.hpp @@ -0,0 +1,29 @@ +// +// AudioProducer.hpp +// Clock Signal +// +// Created by Thomas Harte on 31/03/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef AudioProducer_h +#define AudioProducer_h + +#include "../Outputs/Speaker/Speaker.hpp" + +namespace MachineTypes { + +/*! + An AudioProducer is any machine that **might** produce audio. This isn't always knowable statically. +*/ +class AudioProducer { + public: + /// @returns The speaker that receives this machine's output, or @c nullptr if this machine is mute. + virtual Outputs::Speaker::Speaker *get_speaker() = 0; +}; + +} + +#endif /* AudioProducer_h */ + +// virtual std::string debug_type() { return ""; } diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp deleted file mode 100644 index fc86a7379..000000000 --- a/Machines/CRTMachine.hpp +++ /dev/null @@ -1,215 +0,0 @@ -// -// CRTMachine.hpp -// Clock Signal -// -// Created by Thomas Harte on 31/05/2016. -// Copyright 2016 Thomas Harte. All rights reserved. -// - -#ifndef CRTMachine_hpp -#define CRTMachine_hpp - -#include "../Outputs/ScanTarget.hpp" -#include "../Outputs/Speaker/Speaker.hpp" -#include "../ClockReceiver/ClockReceiver.hpp" -#include "../ClockReceiver/TimeTypes.hpp" -#include "ROMMachine.hpp" - -#include "../Configurable/StandardOptions.hpp" - -#include -#include - -// TODO: rename. -namespace CRTMachine { - -/*! - A CRTMachine::Machine is a mostly-abstract base class for machines that connect to a CRT, - that optionally provide a speaker, and that nominate a clock rate and can announce to a delegate - should that clock rate change. -*/ -class Machine { - public: - /*! - Causes the machine to set up its display and, if it has one, speaker. - - The @c scan_target will receive all video output; the caller guarantees - that it is non-null. - */ - virtual void set_scan_target(Outputs::Display::ScanTarget *scan_target) = 0; - - /*! - @returns The current scan status. - */ - virtual Outputs::Display::ScanStatus get_scan_status() const { - return get_scaled_scan_status() / float(clock_rate_); - } - - /// @returns The speaker that receives this machine's output, or @c nullptr if this machine is mute. - virtual Outputs::Speaker::Speaker *get_speaker() = 0; - - /// @returns The confidence that this machine is running content it understands. - virtual float get_confidence() { return 0.5f; } - virtual std::string debug_type() { return ""; } - - /// Runs the machine for @c duration seconds. - virtual void run_for(Time::Seconds duration) { - const double cycles = (duration * clock_rate_ * speed_multiplier_) + clock_conversion_error_; - clock_conversion_error_ = std::fmod(cycles, 1.0); - run_for(Cycles(static_cast(cycles))); - } - - /*! - Sets a speed multiplier to apply to this machine; e.g. a multiplier of 1.5 will cause the - emulated machine to run 50% faster than a real machine. This speed-up is an emulation - fiction: it will apply across the system, including to the CRT. - */ - virtual void set_speed_multiplier(double multiplier) { - speed_multiplier_ = multiplier; - auto speaker = get_speaker(); - if(speaker) { - speaker->set_input_rate_multiplier(float(multiplier)); - } - } - - /*! - @returns The current speed multiplier. - */ - virtual double get_speed_multiplier() { - return speed_multiplier_; - } - - /*! - Runs for the machine for at least @c duration seconds, and then until @c condition is true. - - @returns The amount of time run for. - */ - Time::Seconds run_until(Time::Seconds minimum_duration, std::function condition) { - Time::Seconds total_runtime = minimum_duration; - run_for(minimum_duration); - while(!condition()) { - // Advance in increments of one 500th of a second until the condition - // is true; that's 1/10th of a 50Hz frame, but more like 1/8.33 of a - // 60Hz frame. Though most machines aren't exactly 50Hz or 60Hz, and some - // are arbitrary other refresh rates. So those observations are merely - // for scale. - run_for(0.002); - total_runtime += 0.002; - } - return total_runtime; - } - - enum MachineEvent: int { - /// At least one new packet of audio has been delivered to the spaker's delegate. - NewSpeakerSamplesGenerated = 1 << 0, - - /// The next vertical retrace has begun. - VerticalSync = 1 << 1, - }; - - /*! - Runs for at least @c duration seconds, and then every one of the @c events has occurred at least once since this - call to @c run_until_event. - - @param events A bitmask comprised of @c MachineEvent flags. - @returns The amount of time run for. - */ - Time::Seconds run_until(Time::Seconds minimum_duration, int events) { - // Tie up a wait-for-samples, if requested. - const Outputs::Speaker::Speaker *speaker = nullptr; - int sample_sets = 0; - if(events & MachineEvent::NewSpeakerSamplesGenerated) { - speaker = get_speaker(); - if(!speaker) events &= ~MachineEvent::NewSpeakerSamplesGenerated; - sample_sets = speaker->completed_sample_sets(); - } - - int retraces = 0; - if(events & MachineEvent::VerticalSync) { - retraces = get_scan_status().hsync_count; - } - - // Run until all requested events are satisfied. - return run_until(minimum_duration, [=]() { - return - (!(events & MachineEvent::NewSpeakerSamplesGenerated) || (sample_sets != speaker->completed_sample_sets())) && - (!(events & MachineEvent::VerticalSync) || (retraces != get_scan_status().hsync_count)); - }); - } - - protected: - /// Runs the machine for @c cycles. - virtual void run_for(const Cycles cycles) = 0; - void set_clock_rate(double clock_rate) { - clock_rate_ = clock_rate; - } - double get_clock_rate() { - return clock_rate_; - } - - virtual Outputs::Display::ScanStatus get_scaled_scan_status() const { - // This deliberately sets up an infinite loop if the user hasn't - // overridden at least one of this or get_scan_status. - // - // Most likely you want to override this, and let the base class - // throw in a divide-by-clock-rate at the end for you. - return get_scan_status(); - } - - /*! - Maps from Configurable::Display to Outputs::Display::VideoSignal and calls - @c set_display_type with the result. - */ - void set_video_signal_configurable(Configurable::Display type) { - Outputs::Display::DisplayType display_type; - switch(type) { - default: - case Configurable::Display::RGB: - display_type = Outputs::Display::DisplayType::RGB; - break; - case Configurable::Display::SVideo: - display_type = Outputs::Display::DisplayType::SVideo; - break; - case Configurable::Display::CompositeColour: - display_type = Outputs::Display::DisplayType::CompositeColour; - break; - case Configurable::Display::CompositeMonochrome: - display_type = Outputs::Display::DisplayType::CompositeMonochrome; - break; - } - set_display_type(display_type); - } - - /*! - Maps back from Outputs::Display::VideoSignal to Configurable::Display, - calling @c get_display_type for the input. - */ - Configurable::Display get_video_signal_configurable() { - switch(get_display_type()) { - default: - case Outputs::Display::DisplayType::RGB: return Configurable::Display::RGB; - case Outputs::Display::DisplayType::SVideo: return Configurable::Display::SVideo; - case Outputs::Display::DisplayType::CompositeColour: return Configurable::Display::CompositeColour; - case Outputs::Display::DisplayType::CompositeMonochrome: return Configurable::Display::CompositeMonochrome; - } - } - - /*! - Sets the display type. - */ - virtual void set_display_type(Outputs::Display::DisplayType display_type) {} - - /*! - Gets the display type. - */ - virtual Outputs::Display::DisplayType get_display_type() { return Outputs::Display::DisplayType::RGB; } - - private: - double clock_rate_ = 1.0; - double clock_conversion_error_ = 0.0; - double speed_multiplier_ = 1.0; -}; - -} - -#endif /* CRTMachine_hpp */ diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 5a773f883..152b4aee0 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -14,8 +14,7 @@ #include "../../Components/AY38910/AY38910.hpp" // For the Super Game Module. #include "../../Components/SN76489/SN76489.hpp" -#include "../CRTMachine.hpp" -#include "../JoystickMachine.hpp" +#include "../MachineTypes.hpp" #include "../../Configurable/Configurable.hpp" #include "../../Configurable/StandardOptions.hpp" @@ -109,9 +108,11 @@ class Joystick: public Inputs::ConcreteJoystick { class ConcreteMachine: public Machine, public CPU::Z80::BusHandler, - public CRTMachine::Machine, public Configurable::Device, - public JoystickMachine::Machine { + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::JoystickMachine { public: ConcreteMachine(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : diff --git a/Machines/Commodore/Vic-20/Keyboard.cpp b/Machines/Commodore/Vic-20/Keyboard.cpp index 017c47cb2..edcb75c87 100644 --- a/Machines/Commodore/Vic-20/Keyboard.cpp +++ b/Machines/Commodore/Vic-20/Keyboard.cpp @@ -76,13 +76,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(F8, KeyF8); } #undef BIND - return KeyboardMachine::MappedMachine::KeyNotMapped; + return MachineTypes::MappedKeyboardMachine::KeyNotMapped; } uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define SHIFT(...) {KeyLShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define X {KeyboardMachine::MappedMachine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define SHIFT(...) {KeyLShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped} static KeySequence key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/Commodore/Vic-20/Keyboard.hpp b/Machines/Commodore/Vic-20/Keyboard.hpp index 8e66d2278..84d54c6dd 100644 --- a/Machines/Commodore/Vic-20/Keyboard.hpp +++ b/Machines/Commodore/Vic-20/Keyboard.hpp @@ -47,7 +47,7 @@ enum Key: uint16_t { #undef key }; -struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { +struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 4595f3ef3..c0380209c 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -11,10 +11,7 @@ #include "Keyboard.hpp" #include "../../../Activity/Source.hpp" -#include "../../MediaTarget.hpp" -#include "../../CRTMachine.hpp" -#include "../../KeyboardMachine.hpp" -#include "../../JoystickMachine.hpp" +#include "../../MachineTypes.hpp" #include "../../../Processors/6502/6502.hpp" #include "../../../Components/6560/6560.hpp" @@ -274,10 +271,12 @@ class Joystick: public Inputs::ConcreteJoystick { }; class ConcreteMachine: - public CRTMachine::Machine, - public MediaTarget::Machine, - public KeyboardMachine::MappedMachine, - public JoystickMachine::Machine, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::MediaTarget, + public MachineTypes::MappedKeyboardMachine, + public MachineTypes::JoystickMachine, public Configurable::Device, public CPU::MOS6502::BusHandler, public MOS::MOS6522::IRQDelegatePortHandler::Delegate, diff --git a/Machines/DynamicMachine.hpp b/Machines/DynamicMachine.hpp index 0f888b89e..6f347c570 100644 --- a/Machines/DynamicMachine.hpp +++ b/Machines/DynamicMachine.hpp @@ -12,13 +12,7 @@ #include "../Configurable/Configurable.hpp" #include "../Activity/Source.hpp" -#include "CRTMachine.hpp" -#include "JoystickMachine.hpp" -#include "KeyboardMachine.hpp" -#include "MediaTarget.hpp" -#include "MouseMachine.hpp" - -#include "Utility/Typer.hpp" +#include "MachineTypes.hpp" namespace Machine { @@ -31,11 +25,13 @@ struct DynamicMachine { virtual Activity::Source *activity_source() = 0; virtual Configurable::Device *configurable_device() = 0; - virtual CRTMachine::Machine *crt_machine() = 0; - virtual JoystickMachine::Machine *joystick_machine() = 0; - virtual KeyboardMachine::Machine *keyboard_machine() = 0; - virtual MouseMachine::Machine *mouse_machine() = 0; - virtual MediaTarget::Machine *media_target() = 0; + virtual MachineTypes::TimedMachine *timed_machine() = 0; + virtual MachineTypes::ScanProducer *scan_producer() = 0; + virtual MachineTypes::AudioProducer *audio_producer() = 0; + virtual MachineTypes::JoystickMachine *joystick_machine() = 0; + virtual MachineTypes::KeyboardMachine *keyboard_machine() = 0; + virtual MachineTypes::MouseMachine *mouse_machine() = 0; + virtual MachineTypes::MediaTarget *media_target() = 0; /*! Provides a raw pointer to the underlying machine if and only if this dynamic machine really is @@ -50,6 +46,29 @@ struct DynamicMachine { virtual void *raw_pointer() = 0; }; +/*! + Provides a templateable means to access the above. +*/ +template MachineType *get(DynamicMachine &); + +#define SpecialisedGet(type, name) \ +template <> \ +inline type *get(DynamicMachine &machine) { \ + return machine.name(); \ +} + +SpecialisedGet(Activity::Source, activity_source) +SpecialisedGet(Configurable::Device, configurable_device) +SpecialisedGet(MachineTypes::TimedMachine, timed_machine) +SpecialisedGet(MachineTypes::ScanProducer, scan_producer) +SpecialisedGet(MachineTypes::AudioProducer, audio_producer) +SpecialisedGet(MachineTypes::JoystickMachine, joystick_machine) +SpecialisedGet(MachineTypes::KeyboardMachine, keyboard_machine) +SpecialisedGet(MachineTypes::MouseMachine, mouse_machine) +SpecialisedGet(MachineTypes::MediaTarget, media_target) + +#undef SpecialisedGet + } #endif /* DynamicMachine_h */ diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index b65ec7da9..bdb52e83d 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -9,9 +9,7 @@ #include "Electron.hpp" #include "../../Activity/Source.hpp" -#include "../MediaTarget.hpp" -#include "../CRTMachine.hpp" -#include "../KeyboardMachine.hpp" +#include "../MachineTypes.hpp" #include "../../Configurable/Configurable.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" @@ -35,9 +33,11 @@ namespace Electron { class ConcreteMachine: public Machine, - public CRTMachine::Machine, - public MediaTarget::Machine, - public KeyboardMachine::MappedMachine, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::MediaTarget, + public MachineTypes::MappedKeyboardMachine, public Configurable::Device, public CPU::MOS6502::BusHandler, public Tape::Delegate, diff --git a/Machines/Electron/Keyboard.cpp b/Machines/Electron/Keyboard.cpp index 26611d81b..e09125dad 100644 --- a/Machines/Electron/Keyboard.cpp +++ b/Machines/Electron/Keyboard.cpp @@ -66,14 +66,14 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(F10, KeyF0); } #undef BIND - return KeyboardMachine::MappedMachine::KeyNotMapped; + return MachineTypes::MappedKeyboardMachine::KeyNotMapped; } uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define CTRL(...) {KeyControl, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define X {KeyboardMachine::MappedMachine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define SHIFT(...) {KeyShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define CTRL(...) {KeyControl, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped} static KeySequence key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/Electron/Keyboard.hpp b/Machines/Electron/Keyboard.hpp index 38a5baa4f..7b95e8967 100644 --- a/Machines/Electron/Keyboard.hpp +++ b/Machines/Electron/Keyboard.hpp @@ -36,7 +36,7 @@ enum Key: uint16_t { KeyBreak = 0xfffd, }; -struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { +struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) final; }; diff --git a/Machines/JoystickMachine.hpp b/Machines/JoystickMachine.hpp index ae884cc25..4a4c754dc 100644 --- a/Machines/JoystickMachine.hpp +++ b/Machines/JoystickMachine.hpp @@ -12,9 +12,9 @@ #include "../Inputs/Joystick.hpp" #include -namespace JoystickMachine { +namespace MachineTypes { -class Machine { +class JoystickMachine { public: virtual const std::vector> &get_joysticks() = 0; }; diff --git a/Machines/KeyboardMachine.cpp b/Machines/KeyboardMachine.cpp index 1a60103d6..8c08dbddb 100644 --- a/Machines/KeyboardMachine.cpp +++ b/Machines/KeyboardMachine.cpp @@ -8,31 +8,31 @@ #include "KeyboardMachine.hpp" -using namespace KeyboardMachine; +using namespace MachineTypes; -MappedMachine::MappedMachine(const std::set &essential_modifiers) : keyboard_(essential_modifiers) { +MachineTypes::MappedKeyboardMachine::MappedKeyboardMachine(const std::set &essential_modifiers) : keyboard_(essential_modifiers) { keyboard_.set_delegate(this); } -bool MappedMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) { +bool MappedKeyboardMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) { uint16_t mapped_key = get_keyboard_mapper()->mapped_key_for_key(key); if(mapped_key == KeyNotMapped) return false; set_key_state(mapped_key, is_pressed); return true; } -void MappedMachine::reset_all_keys(Inputs::Keyboard *keyboard) { +void MappedKeyboardMachine::reset_all_keys(Inputs::Keyboard *keyboard) { // TODO: unify naming. clear_all_keys(); } -Inputs::Keyboard &MappedMachine::get_keyboard() { +Inputs::Keyboard &MappedKeyboardMachine::get_keyboard() { return keyboard_; } -void Machine::type_string(const std::string &) { +void KeyboardMachine::type_string(const std::string &) { } -MappedMachine::KeyboardMapper *MappedMachine::get_keyboard_mapper() { +MappedKeyboardMachine::KeyboardMapper *MappedKeyboardMachine::get_keyboard_mapper() { return nullptr; } diff --git a/Machines/KeyboardMachine.hpp b/Machines/KeyboardMachine.hpp index c5ee1f0e1..fc83ded7b 100644 --- a/Machines/KeyboardMachine.hpp +++ b/Machines/KeyboardMachine.hpp @@ -16,7 +16,7 @@ #include "../Inputs/Keyboard.hpp" -namespace KeyboardMachine { +namespace MachineTypes { /*! Covers just the actions necessary to communicate keyboard state, as a purely abstract class. @@ -37,7 +37,7 @@ struct KeyActions { /*! Describes an emulated machine which exposes a keyboard and accepts a typed string. */ -class Machine: public KeyActions { +class KeyboardMachine: public KeyActions { public: /*! Causes the machine to attempt to type the supplied string. @@ -107,9 +107,9 @@ class Machine: public KeyActions { Provides a base class for machines that want to provide a keyboard mapper, allowing automatic mapping from keyboard inputs to KeyActions. */ -class MappedMachine: public Inputs::Keyboard::Delegate, public Machine { +class MappedKeyboardMachine: public Inputs::Keyboard::Delegate, public KeyboardMachine { public: - MappedMachine(const std::set &essential_modifiers = {}); + MappedKeyboardMachine(const std::set &essential_modifiers = {}); /*! A keyboard mapper attempts to provide a physical mapping between host keys and emulated keys. diff --git a/Machines/MSX/Keyboard.cpp b/Machines/MSX/Keyboard.cpp index bdce3bcd1..2ca15e306 100644 --- a/Machines/MSX/Keyboard.cpp +++ b/Machines/MSX/Keyboard.cpp @@ -57,5 +57,5 @@ uint16_t MSX::KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { default: break; } #undef BIND - return KeyboardMachine::MappedMachine::KeyNotMapped; + return MachineTypes::MappedKeyboardMachine::KeyNotMapped; } diff --git a/Machines/MSX/Keyboard.hpp b/Machines/MSX/Keyboard.hpp index c9607f846..07fd35526 100644 --- a/Machines/MSX/Keyboard.hpp +++ b/Machines/MSX/Keyboard.hpp @@ -33,7 +33,7 @@ enum Key: uint16_t { #undef Line }; -struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { +struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 7fd483d8a..95c91f6ce 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -33,10 +33,7 @@ #include "../../Storage/Tape/Tape.hpp" #include "../../Activity/Source.hpp" -#include "../CRTMachine.hpp" -#include "../JoystickMachine.hpp" -#include "../MediaTarget.hpp" -#include "../KeyboardMachine.hpp" +#include "../MachineTypes.hpp" #include "../../Configurable/Configurable.hpp" #include "../../Outputs/Log.hpp" @@ -134,11 +131,13 @@ class AYPortHandler: public GI::AY38910::PortHandler { class ConcreteMachine: public Machine, public CPU::Z80::BusHandler, - public CRTMachine::Machine, - public MediaTarget::Machine, - public KeyboardMachine::MappedMachine, + public MachineTypes::TimedMachine, + public MachineTypes::AudioProducer, + public MachineTypes::ScanProducer, + public MachineTypes::MediaTarget, + public MachineTypes::MappedKeyboardMachine, + public MachineTypes::JoystickMachine, public Configurable::Device, - public JoystickMachine::Machine, public MemoryMap, public ClockingHint::Observer, public Activity::Source { diff --git a/Machines/MachineTypes.hpp b/Machines/MachineTypes.hpp new file mode 100644 index 000000000..c96a02f10 --- /dev/null +++ b/Machines/MachineTypes.hpp @@ -0,0 +1,25 @@ +// +// MachineTypes.hpp +// Clock Signal +// +// Created by Thomas Harte on 31/03/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef MachineTypes_h +#define MachineTypes_h + +// Rounds up everything in the MachineTypes namespace, all being +// optional (at least, semantically) interfaces that machines +// might implement. These header files are intended to be light, +// so including all shouldn't be a huge burden. + +#include "AudioProducer.hpp" +#include "JoystickMachine.hpp" +#include "KeyboardMachine.hpp" +#include "MediaTarget.hpp" +#include "MouseMachine.hpp" +#include "ScanProducer.hpp" +#include "TimedMachine.hpp" + +#endif /* MachineTypes_h */ diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index d4750ea37..ed46adb3b 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -13,9 +13,7 @@ #include "../../Components/9918/9918.hpp" #include "../../Components/SN76489/SN76489.hpp" -#include "../CRTMachine.hpp" -#include "../JoystickMachine.hpp" -#include "../KeyboardMachine.hpp" +#include "../MachineTypes.hpp" #include "../../Configurable/Configurable.hpp" #include "../../ClockReceiver/ForceInline.hpp" @@ -80,11 +78,13 @@ class Joystick: public Inputs::ConcreteJoystick { class ConcreteMachine: public Machine, public CPU::Z80::BusHandler, - public CRTMachine::Machine, - public KeyboardMachine::Machine, - public Inputs::Keyboard::Delegate, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::KeyboardMachine, + public MachineTypes::JoystickMachine, public Configurable::Device, - public JoystickMachine::Machine { + public Inputs::Keyboard::Delegate { public: ConcreteMachine(const Analyser::Static::Sega::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : diff --git a/Machines/MediaTarget.hpp b/Machines/MediaTarget.hpp index c07847474..70471ce41 100644 --- a/Machines/MediaTarget.hpp +++ b/Machines/MediaTarget.hpp @@ -14,12 +14,12 @@ #include -namespace MediaTarget { +namespace MachineTypes { /*! A MediaTarget::Machine is anything that can accept new media while running. */ -class Machine { +class MediaTarget { public: /*! Requests that the machine insert @c media as a modification to current state diff --git a/Machines/MouseMachine.hpp b/Machines/MouseMachine.hpp index e9e235aab..2c74599a1 100644 --- a/Machines/MouseMachine.hpp +++ b/Machines/MouseMachine.hpp @@ -11,10 +11,11 @@ #include "../Inputs/Mouse.hpp" -namespace MouseMachine { +namespace MachineTypes { -class Machine { +class MouseMachine { public: + // TODO: support multiple mice? virtual Inputs::Mouse &get_mouse() = 0; }; diff --git a/Machines/Oric/Keyboard.cpp b/Machines/Oric/Keyboard.cpp index 9593fe1b5..40661448e 100644 --- a/Machines/Oric/Keyboard.cpp +++ b/Machines/Oric/Keyboard.cpp @@ -51,13 +51,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { } #undef BIND - return KeyboardMachine::MappedMachine::KeyNotMapped; + return MachineTypes::MappedKeyboardMachine::KeyNotMapped; } uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define SHIFT(...) {KeyLeftShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define X {KeyboardMachine::MappedMachine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define SHIFT(...) {KeyLeftShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped} static KeySequence key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/Oric/Keyboard.hpp b/Machines/Oric/Keyboard.hpp index 9e4d6b394..87c20d1c1 100644 --- a/Machines/Oric/Keyboard.hpp +++ b/Machines/Oric/Keyboard.hpp @@ -36,7 +36,7 @@ enum Key: uint16_t { KeyJasminReset = 0xfffc, }; -struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { +struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 74d92136f..e7a45c4bc 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -15,9 +15,7 @@ #include "Video.hpp" #include "../../Activity/Source.hpp" -#include "../MediaTarget.hpp" -#include "../CRTMachine.hpp" -#include "../KeyboardMachine.hpp" +#include "../MachineTypes.hpp" #include "../Utility/MemoryFuzzer.hpp" #include "../Utility/StringSerialiser.hpp" @@ -208,9 +206,11 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { }; template class ConcreteMachine: - public CRTMachine::Machine, - public MediaTarget::Machine, - public KeyboardMachine::MappedMachine, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::MediaTarget, + public MachineTypes::MappedKeyboardMachine, public Configurable::Device, public CPU::MOS6502::BusHandler, public MOS::MOS6522::IRQDelegatePortHandler::Delegate, diff --git a/Machines/ScanProducer.hpp b/Machines/ScanProducer.hpp new file mode 100644 index 000000000..4393086e1 --- /dev/null +++ b/Machines/ScanProducer.hpp @@ -0,0 +1,104 @@ +// +// ScanProducer.hpp +// Clock Signal +// +// Created by Thomas Harte on 31/03/2020. +// Copyright 2020 Thomas Harte. All rights reserved. +// + +#ifndef ScanProducer_hpp +#define ScanProducer_hpp + +#include "../Outputs/ScanTarget.hpp" +#include "../Configurable/StandardOptions.hpp" + +#include "TimedMachine.hpp" + +namespace MachineTypes { + +/*! + A ScanProducer::Producer is any machine that produces video output of the form accepted + by a ScanTarget. +*/ +class ScanProducer { + public: + /*! + Causes the machine to set up its display and, if it has one, speaker. + + The @c scan_target will receive all video output; the caller guarantees + that it is non-null. + */ + virtual void set_scan_target(Outputs::Display::ScanTarget *scan_target) = 0; + + /*! + @returns The current scan status. + */ + virtual Outputs::Display::ScanStatus get_scan_status() const { + // There's an implicit assumption here that anything which produces scans + // is also a timed machine. And, also, that this function will be called infrequently. + const TimedMachine *timed_machine = dynamic_cast(this); + return get_scaled_scan_status() / float(timed_machine->get_clock_rate()); + } + + protected: + virtual Outputs::Display::ScanStatus get_scaled_scan_status() const { + // This deliberately sets up an infinite loop if the user hasn't + // overridden at least one of this or get_scan_status. + // + // Most likely you want to override this, and let the base class + // throw in a divide-by-clock-rate at the end for you. + return get_scan_status(); + } + + /*! + Maps from Configurable::Display to Outputs::Display::VideoSignal and calls + @c set_display_type with the result. + */ + void set_video_signal_configurable(Configurable::Display type) { + Outputs::Display::DisplayType display_type; + switch(type) { + default: + case Configurable::Display::RGB: + display_type = Outputs::Display::DisplayType::RGB; + break; + case Configurable::Display::SVideo: + display_type = Outputs::Display::DisplayType::SVideo; + break; + case Configurable::Display::CompositeColour: + display_type = Outputs::Display::DisplayType::CompositeColour; + break; + case Configurable::Display::CompositeMonochrome: + display_type = Outputs::Display::DisplayType::CompositeMonochrome; + break; + } + set_display_type(display_type); + } + + /*! + Maps back from Outputs::Display::VideoSignal to Configurable::Display, + calling @c get_display_type for the input. + */ + Configurable::Display get_video_signal_configurable() { + switch(get_display_type()) { + default: + case Outputs::Display::DisplayType::RGB: return Configurable::Display::RGB; + case Outputs::Display::DisplayType::SVideo: return Configurable::Display::SVideo; + case Outputs::Display::DisplayType::CompositeColour: return Configurable::Display::CompositeColour; + case Outputs::Display::DisplayType::CompositeMonochrome: return Configurable::Display::CompositeMonochrome; + } + } + + /*! + Sets the display type. + */ + virtual void set_display_type(Outputs::Display::DisplayType display_type) {} + + /*! + Gets the display type. + */ + virtual Outputs::Display::DisplayType get_display_type() { return Outputs::Display::DisplayType::RGB; } +}; + +} + +#endif /* ScanProducer_hpp */ diff --git a/Machines/TimedMachine.hpp b/Machines/TimedMachine.hpp new file mode 100644 index 000000000..b14d9a7a3 --- /dev/null +++ b/Machines/TimedMachine.hpp @@ -0,0 +1,89 @@ +// +// TimedMachine.hpp +// Clock Signal +// +// Created by Thomas Harte on 31/03/2020. +// Copyright © 2020 Thomas Harte. All rights reserved. +// + +#ifndef TimedMachine_h +#define TimedMachine_h + +#include "../ClockReceiver/ClockReceiver.hpp" +#include "../ClockReceiver/TimeTypes.hpp" + +#include "AudioProducer.hpp" +#include "ScanProducer.hpp" + +#include + +namespace MachineTypes { + +/*! + A timed machine is any which requires the owner to provide time-based updates, + i.e. run_for()-type calls. + +*/ +class TimedMachine { + public: + /// Runs the machine for @c duration seconds. + virtual void run_for(Time::Seconds duration) { + const double cycles = (duration * clock_rate_ * speed_multiplier_) + clock_conversion_error_; + clock_conversion_error_ = std::fmod(cycles, 1.0); + run_for(Cycles(static_cast(cycles))); + } + + /*! + Sets a speed multiplier to apply to this machine; e.g. a multiplier of 1.5 will cause the + emulated machine to run 50% faster than a real machine. This speed-up is an emulation + fiction: it will apply across the system, including to the CRT. + */ + virtual void set_speed_multiplier(double multiplier) { + speed_multiplier_ = multiplier; + + auto audio_producer = dynamic_cast(this); + if(!audio_producer) return; + + auto speaker = audio_producer->get_speaker(); + if(speaker) { + speaker->set_input_rate_multiplier(float(multiplier)); + } + } + + /*! + @returns The current speed multiplier. + */ + virtual double get_speed_multiplier() const { + return speed_multiplier_; + } + + /// @returns The confidence that this machine is running content it understands. + virtual float get_confidence() { return 0.5f; } + virtual std::string debug_type() { return ""; } + + protected: + /// Runs the machine for @c cycles. + virtual void run_for(const Cycles cycles) = 0; + + /// Sets this machine's clock rate. + void set_clock_rate(double clock_rate) { + clock_rate_ = clock_rate; + } + + /// Gets this machine's clock rate. + double get_clock_rate() const { + return clock_rate_; + } + + private: + // Give the ScanProducer access to this machine's clock rate. + friend class ScanProducer; + + double clock_rate_ = 1.0; + double clock_conversion_error_ = 0.0; + double speed_multiplier_ = 1.0; +}; + +} + +#endif /* TimedMachine_h */ diff --git a/Machines/Utility/MachineForTarget.hpp b/Machines/Utility/MachineForTarget.hpp index 066ef51b7..27ebe6581 100644 --- a/Machines/Utility/MachineForTarget.hpp +++ b/Machines/Utility/MachineForTarget.hpp @@ -13,6 +13,7 @@ #include "../../Reflection/Struct.hpp" #include "../DynamicMachine.hpp" +#include "../ROMMachine.hpp" #include #include diff --git a/Machines/Utility/TypedDynamicMachine.hpp b/Machines/Utility/TypedDynamicMachine.hpp index 4c1341245..d1e9d0530 100644 --- a/Machines/Utility/TypedDynamicMachine.hpp +++ b/Machines/Utility/TypedDynamicMachine.hpp @@ -25,33 +25,22 @@ template class TypedDynamicMachine: public ::Machine::DynamicMachine return *this; } - Activity::Source *activity_source() final { - return get(); +#define Provide(type, name) \ + type *name() final { \ + return get(); \ } - MediaTarget::Machine *media_target() final { - return get(); - } + Provide(Activity::Source, activity_source) + Provide(Configurable::Device, configurable_device) + Provide(MachineTypes::TimedMachine, timed_machine) + Provide(MachineTypes::ScanProducer, scan_producer) + Provide(MachineTypes::AudioProducer, audio_producer) + Provide(MachineTypes::JoystickMachine, joystick_machine) + Provide(MachineTypes::KeyboardMachine, keyboard_machine) + Provide(MachineTypes::MouseMachine, mouse_machine) + Provide(MachineTypes::MediaTarget, media_target) - CRTMachine::Machine *crt_machine() final { - return get(); - } - - JoystickMachine::Machine *joystick_machine() final { - return get(); - } - - KeyboardMachine::Machine *keyboard_machine() final { - return get(); - } - - MouseMachine::Machine *mouse_machine() final { - return get(); - } - - Configurable::Device *configurable_device() final { - return get(); - } +#undef Provide void *raw_pointer() final { return get(); diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index efdda2036..1df5d3003 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -72,7 +72,7 @@ void Typer::append(const std::string &string) { const uint16_t *Typer::sequence_for_character(char c) const { const uint16_t *const sequence = character_mapper_.sequence_for_character(c); - if(!sequence || sequence[0] == KeyboardMachine::MappedMachine::KeyNotMapped) { + if(!sequence || sequence[0] == MachineTypes::MappedKeyboardMachine::KeyNotMapped) { return nullptr; } return sequence; @@ -102,7 +102,7 @@ uint16_t Typer::try_type_next_character() { } // If the sequence is over, stop. - if(sequence[phase_ - 2] == KeyboardMachine::MappedMachine::KeyEndSequence) { + if(sequence[phase_ - 2] == MachineTypes::MappedKeyboardMachine::KeyEndSequence) { return 0; } @@ -137,7 +137,7 @@ bool Typer::type_next_character() { uint16_t *CharacterMapper::table_lookup_sequence_for_character(KeySequence *sequences, std::size_t length, char character) { std::size_t ucharacter = static_cast((unsigned char)character); if(ucharacter >= (length / sizeof(KeySequence))) return nullptr; - if(sequences[ucharacter][0] == KeyboardMachine::MappedMachine::KeyNotMapped) return nullptr; + if(sequences[ucharacter][0] == MachineTypes::MappedKeyboardMachine::KeyNotMapped) return nullptr; return sequences[ucharacter]; } diff --git a/Machines/Utility/Typer.hpp b/Machines/Utility/Typer.hpp index 867d055c8..1e9967500 100644 --- a/Machines/Utility/Typer.hpp +++ b/Machines/Utility/Typer.hpp @@ -61,7 +61,7 @@ class CharacterMapper { */ class Typer { public: - class Delegate: public KeyboardMachine::KeyActions { + class Delegate: public MachineTypes::KeyActions { public: /// Informs the delegate that this typer has reached the end of its content. virtual void typer_reset(Typer *typer) = 0; @@ -101,7 +101,7 @@ class Typer { Provides a default base class for type recipients: classes that want to attach a single typer at a time and which may or may not want to nominate an initial delay and typing frequency. */ -template +template class TypeRecipient: public Typer::Delegate { protected: template TypeRecipient(Args&&... args) : character_mapper(std::forward(args)...) {} @@ -120,7 +120,7 @@ class TypeRecipient: public Typer::Delegate { */ bool can_type(char c) { const auto sequence = character_mapper.sequence_for_character(c); - return sequence && sequence[0] != KeyboardMachine::MappedMachine::KeyNotMapped; + return sequence && sequence[0] != MachineTypes::MappedKeyboardMachine::KeyNotMapped; } /*! @@ -143,7 +143,7 @@ class TypeRecipient: public Typer::Delegate { private: std::unique_ptr previous_typer_; - CMApper character_mapper; + CMapper character_mapper; }; } diff --git a/Machines/ZX8081/Keyboard.cpp b/Machines/ZX8081/Keyboard.cpp index 2a0291d65..d4e2a5e4a 100644 --- a/Machines/ZX8081/Keyboard.cpp +++ b/Machines/ZX8081/Keyboard.cpp @@ -39,15 +39,15 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(BackTick, KeyEdit); BIND(F1, KeyEdit); } #undef BIND - return KeyboardMachine::MappedMachine::KeyNotMapped; + return MachineTypes::MappedKeyboardMachine::KeyNotMapped; } CharacterMapper::CharacterMapper(bool is_zx81) : is_zx81_(is_zx81) {} uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} -#define X {KeyboardMachine::MappedMachine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define SHIFT(...) {KeyShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence} +#define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped} static KeySequence zx81_key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/ZX8081/Keyboard.hpp b/Machines/ZX8081/Keyboard.hpp index 50fcfeda3..d4f930bf2 100644 --- a/Machines/ZX8081/Keyboard.hpp +++ b/Machines/ZX8081/Keyboard.hpp @@ -29,7 +29,7 @@ enum Key: uint16_t { KeyBreak, KeyLeft, KeyRight, KeyUp, KeyDown, KeyEdit }; -struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { +struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 6030b2a08..2dc4c92d6 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -8,9 +8,7 @@ #include "ZX8081.hpp" -#include "../MediaTarget.hpp" -#include "../CRTMachine.hpp" -#include "../KeyboardMachine.hpp" +#include "../MachineTypes.hpp" #include "../../Components/AY38910/AY38910.hpp" #include "../../Processors/Z80/Z80.hpp" @@ -51,9 +49,11 @@ enum ROMType: uint8_t { }; template class ConcreteMachine: - public CRTMachine::Machine, - public MediaTarget::Machine, - public KeyboardMachine::MappedMachine, + public MachineTypes::TimedMachine, + public MachineTypes::ScanProducer, + public MachineTypes::AudioProducer, + public MachineTypes::MediaTarget, + public MachineTypes::MappedKeyboardMachine, public Configurable::Device, public Utility::TypeRecipient, public CPU::Z80::BusHandler, diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 400a6be4f..212170c54 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -770,8 +770,8 @@ 4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; }; 4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; }; 4BBB70A5202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; }; - 4BBB70A8202014E2002FE009 /* MultiCRTMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */; }; - 4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */; }; + 4BBB70A8202014E2002FE009 /* MultiProducer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */; }; + 4BBB70A9202014E2002FE009 /* MultiProducer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */; }; 4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC951C1F368D83008F4C34 /* i8272.cpp */; }; 4BBF49AF1ED2880200AB3669 /* FUSETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */; }; 4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */; }; @@ -903,7 +903,7 @@ 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MemptrTests.swift; sourceTree = ""; }; 4B0333AD2094081A0050B93D /* AppleDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AppleDSK.cpp; sourceTree = ""; }; 4B0333AE2094081A0050B93D /* AppleDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AppleDSK.hpp; sourceTree = ""; }; - 4B046DC31CFE651500E9E45E /* CRTMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTMachine.hpp; sourceTree = ""; }; + 4B046DC31CFE651500E9E45E /* ScanProducer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ScanProducer.hpp; sourceTree = ""; }; 4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = ""; }; 4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = ""; }; 4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = ""; }; @@ -1633,8 +1633,8 @@ 4BBB709C2020109C002FE009 /* DynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DynamicMachine.hpp; sourceTree = ""; }; 4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMediaTarget.hpp; sourceTree = ""; }; 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMediaTarget.cpp; sourceTree = ""; }; - 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiCRTMachine.cpp; sourceTree = ""; }; - 4BBB70A7202014E2002FE009 /* MultiCRTMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiCRTMachine.hpp; sourceTree = ""; }; + 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiProducer.cpp; sourceTree = ""; }; + 4BBB70A7202014E2002FE009 /* MultiProducer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiProducer.hpp; sourceTree = ""; }; 4BBC951C1F368D83008F4C34 /* i8272.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = i8272.cpp; path = 8272/i8272.cpp; sourceTree = ""; }; 4BBC951D1F368D83008F4C34 /* i8272.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8272.hpp; path = 8272/i8272.hpp; sourceTree = ""; }; 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUSETests.swift; sourceTree = ""; }; @@ -1652,6 +1652,9 @@ 4BC131752346DE9100E4FF3D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = ""; }; 4BC131782346DF2B00E4FF3D /* MSA.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MSA.cpp; sourceTree = ""; }; 4BC131792346DF2B00E4FF3D /* MSA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSA.hpp; sourceTree = ""; }; + 4BC57CD2243427C700FBC404 /* AudioProducer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AudioProducer.hpp; sourceTree = ""; }; + 4BC57CD32434282000FBC404 /* TimedMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TimedMachine.hpp; sourceTree = ""; }; + 4BC57CD424342E0600FBC404 /* MachineTypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MachineTypes.hpp; sourceTree = ""; }; 4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000MoveTests.mm; sourceTree = ""; }; 4BC5FC2F20CDDDEE00410AA0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/AppleIIOptions.xib"; sourceTree = SOURCE_ROOT; }; 4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = ""; }; @@ -3387,13 +3390,16 @@ isa = PBXGroup; children = ( 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */, - 4B046DC31CFE651500E9E45E /* CRTMachine.hpp */, + 4BC57CD2243427C700FBC404 /* AudioProducer.hpp */, 4BBB709C2020109C002FE009 /* DynamicMachine.hpp */, 4B7041271F92C26900735E45 /* JoystickMachine.hpp */, 4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */, + 4BC57CD424342E0600FBC404 /* MachineTypes.hpp */, 4BA9C3CF1D8164A9002DDB61 /* MediaTarget.hpp */, 4B92294222B04A3D00A1458F /* MouseMachine.hpp */, 4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */, + 4B046DC31CFE651500E9E45E /* ScanProducer.hpp */, + 4BC57CD32434282000FBC404 /* TimedMachine.hpp */, 4B38F3491F2EC12000D9235D /* AmstradCPC */, 4BCE0048227CE8CA000CA200 /* Apple */, 4B0ACC0423775819008902D0 /* Atari */, @@ -3428,13 +3434,13 @@ isa = PBXGroup; children = ( 4B1B88BE202E3DB200B67DFF /* MultiConfigurable.cpp */, - 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */, + 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */, 4B1B88C6202E469300B67DFF /* MultiJoystickMachine.cpp */, 4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */, 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */, 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */, 4B1B88BF202E3DB200B67DFF /* MultiConfigurable.hpp */, - 4BBB70A7202014E2002FE009 /* MultiCRTMachine.hpp */, + 4BBB70A7202014E2002FE009 /* MultiProducer.hpp */, 4B1B88C7202E469300B67DFF /* MultiJoystickMachine.hpp */, 4B1B88BA202E2EC100B67DFF /* MultiKeyboardMachine.hpp */, 4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */, @@ -4300,7 +4306,7 @@ 4BB0A65E204500A900FB3688 /* StaticAnalyser.cpp in Sources */, 4B055AC11FAE98DC0060FFFF /* MachineForTarget.cpp in Sources */, 4B65086122F4CFE0009C1100 /* Keyboard.cpp in Sources */, - 4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */, + 4BBB70A9202014E2002FE009 /* MultiProducer.cpp in Sources */, 4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */, 4B055AD81FAE9B180060FFFF /* Video.cpp in Sources */, 4B89452F201967B4007DE474 /* StaticAnalyser.cpp in Sources */, @@ -4535,7 +4541,7 @@ 4BB0A65B2044FD3000FB3688 /* SN76489.cpp in Sources */, 4B894532201967B4007DE474 /* 6502.cpp in Sources */, 4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */, - 4BBB70A8202014E2002FE009 /* MultiCRTMachine.cpp in Sources */, + 4BBB70A8202014E2002FE009 /* MultiProducer.cpp in Sources */, 4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */, 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */, diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index bf23f3eed..b5d73c8dc 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -148,7 +148,7 @@ struct ActivityObserver: public Activity::Observer { CSStaticAnalyser *_analyser; std::unique_ptr _machine; - JoystickMachine::Machine *_joystickMachine; + MachineTypes::JoystickMachine *_joystickMachine; CSJoystickManager *_joystickManager; std::bitset<65536> _depressedKeys; @@ -255,7 +255,7 @@ struct ActivityObserver: public Activity::Observer { - (float)idealSamplingRateFromRange:(NSRange)range { @synchronized(self) { - Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); + Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker(); if(speaker) { return speaker->get_ideal_clock_rate_in_range((float)range.location, (float)(range.location + range.length)); } @@ -265,7 +265,7 @@ struct ActivityObserver: public Activity::Observer { - (BOOL)isStereo { @synchronized(self) { - Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); + Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker(); if(speaker) { return speaker->get_is_stereo(); } @@ -281,7 +281,7 @@ struct ActivityObserver: public Activity::Observer { - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Speaker::Delegate *)delegate sampleRate:(float)sampleRate bufferSize:(NSUInteger)bufferSize stereo:(BOOL)stereo { @synchronized(self) { - Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); + Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker(); if(speaker) { speaker->set_output_rate(sampleRate, (int)bufferSize, stereo); speaker->set_delegate(delegate); @@ -362,7 +362,7 @@ struct ActivityObserver: public Activity::Observer { - (void)setupOutputWithAspectRatio:(float)aspectRatio { _scanTarget = std::make_unique(); - _machine->crt_machine()->set_scan_target(_scanTarget.get()); + _machine->scan_producer()->set_scan_target(_scanTarget.get()); } - (void)updateViewForPixelSize:(CGSize)pixelSize { @@ -379,7 +379,7 @@ struct ActivityObserver: public Activity::Observer { } - (void)paste:(NSString *)paste { - KeyboardMachine::Machine *keyboardMachine = _machine->keyboard_machine(); + auto keyboardMachine = _machine->keyboard_machine(); if(keyboardMachine) keyboardMachine->type_string([paste UTF8String]); } @@ -409,7 +409,7 @@ struct ActivityObserver: public Activity::Observer { - (void)applyMedia:(const Analyser::Static::Media &)media { @synchronized(self) { - MediaTarget::Machine *const mediaTarget = _machine->media_target(); + const auto mediaTarget = _machine->media_target(); if(mediaTarget) mediaTarget->insert_media(media); } } @@ -712,7 +712,7 @@ struct ActivityObserver: public Activity::Observer { - (void)setVolume:(float)volume { @synchronized(self) { - Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); + Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker(); if(speaker) { return speaker->set_output_volume(volume); } @@ -721,7 +721,7 @@ struct ActivityObserver: public Activity::Observer { - (BOOL)hasAudioOutput { @synchronized(self) { - Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); + Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker(); return speaker ? YES : NO; } } @@ -789,23 +789,23 @@ struct ActivityObserver: public Activity::Observer { @synchronized(self) { // If this tick includes vsync then inspect the machine. if(timeNow >= self->_syncTime && lastTime < self->_syncTime) { - splitAndSync = self->_isSyncLocking = self->_scanSynchroniser.can_synchronise(self->_machine->crt_machine()->get_scan_status(), self->_refreshPeriod); + splitAndSync = self->_isSyncLocking = self->_scanSynchroniser.can_synchronise(self->_machine->scan_producer()->get_scan_status(), self->_refreshPeriod); // If the time window is being split, run up to the split, then check out machine speed, possibly // adjusting multiplier, then run after the split. if(splitAndSync) { - self->_machine->crt_machine()->run_for((double)(self->_syncTime - lastTime) / 1e9); - self->_machine->crt_machine()->set_speed_multiplier( - self->_scanSynchroniser.next_speed_multiplier(self->_machine->crt_machine()->get_scan_status()) + self->_machine->timed_machine()->run_for((double)(self->_syncTime - lastTime) / 1e9); + self->_machine->timed_machine()->set_speed_multiplier( + self->_scanSynchroniser.next_speed_multiplier(self->_machine->scan_producer()->get_scan_status()) ); - self->_machine->crt_machine()->run_for((double)(timeNow - self->_syncTime) / 1e9); + self->_machine->timed_machine()->run_for((double)(timeNow - self->_syncTime) / 1e9); } } // If the time window is being split, run up to the split, then check out machine speed, possibly // adjusting multiplier, then run after the split. if(!splitAndSync) { - self->_machine->crt_machine()->run_for((double)duration / 1e9); + self->_machine->timed_machine()->run_for((double)duration / 1e9); } pixelSize = self->_pixelSize; } diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 69af3aa80..8c68cbae2 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -27,8 +27,7 @@ #include "../../ClockReceiver/TimeTypes.hpp" #include "../../ClockReceiver/ScanSynchroniser.hpp" -#include "../../Machines/MediaTarget.hpp" -#include "../../Machines/CRTMachine.hpp" +#include "../../Machines/MachineTypes.hpp" #include "../../Activity/Observer.hpp" #include "../../Outputs/OpenGL/Primitives/Rectangle.hpp" @@ -142,17 +141,18 @@ struct MachineRunner { const auto vsync_time = vsync_time_.load(); std::unique_lock lock_guard(*machine_mutex); - const auto crt_machine = machine->crt_machine(); + const auto scan_producer = machine->scan_producer(); + const auto timed_machine = machine->timed_machine(); bool split_and_sync = false; if(last_time_ < vsync_time && time_now >= vsync_time) { - split_and_sync = scan_synchroniser_.can_synchronise(crt_machine->get_scan_status(), _frame_period); + split_and_sync = scan_synchroniser_.can_synchronise(scan_producer->get_scan_status(), _frame_period); } if(split_and_sync) { - crt_machine->run_for(double(vsync_time - last_time_) / 1e9); - crt_machine->set_speed_multiplier( - scan_synchroniser_.next_speed_multiplier(crt_machine->get_scan_status()) + timed_machine->run_for(double(vsync_time - last_time_) / 1e9); + timed_machine->set_speed_multiplier( + scan_synchroniser_.next_speed_multiplier(scan_producer->get_scan_status()) ); // This is a bit of an SDL ugliness; wait here until the next frame is drawn. @@ -162,10 +162,10 @@ struct MachineRunner { while(frame_lock_.test_and_set()); lock_guard.lock(); - crt_machine->run_for(double(time_now - vsync_time) / 1e9); + timed_machine->run_for(double(time_now - vsync_time) / 1e9); } else { - crt_machine->set_speed_multiplier(scan_synchroniser_.get_base_speed_multiplier()); - crt_machine->run_for(double(time_now - last_time_) / 1e9); + timed_machine->set_speed_multiplier(scan_synchroniser_.get_base_speed_multiplier()); + timed_machine->run_for(double(time_now - last_time_) / 1e9); } last_time_ = time_now; } @@ -777,7 +777,7 @@ int main(int argc, char *argv[]) { } else if(volume < 0.0 || volume > 1.0) { std::cerr << "Cannot run with volume " << volume_string << "; volumes must be between 0.0 and 1.0." << std::endl; } else { - const auto speaker = machine->crt_machine()->get_speaker(); + const auto speaker = machine->audio_producer()->get_speaker(); if(speaker) speaker->set_output_volume(volume); } } @@ -842,10 +842,10 @@ int main(int argc, char *argv[]) { // Setup output, assuming a CRT machine for now, and prepare a best-effort updater. Outputs::Display::OpenGL::ScanTarget scan_target(target_framebuffer); - machine->crt_machine()->set_scan_target(&scan_target); + machine->scan_producer()->set_scan_target(&scan_target); // For now, lie about audio output intentions. - auto speaker = machine->crt_machine()->get_speaker(); + auto speaker = machine->audio_producer()->get_speaker(); if(speaker) { // Create an audio pipe. SDL_AudioSpec desired_audio_spec; @@ -909,7 +909,7 @@ int main(int argc, char *argv[]) { std::vector hat_values_; }; std::vector joysticks; - JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); + const auto joystick_machine = machine->joystick_machine(); if(joystick_machine) { SDL_InitSubSystem(SDL_INIT_JOYSTICK); for(int c = 0; c < SDL_NumJoysticks(); ++c) { @@ -1146,7 +1146,7 @@ int main(int argc, char *argv[]) { } // Handle accumulated key states. - JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); + const auto joystick_machine = machine->joystick_machine(); for (const auto &keypress: logical_keyboard ? matched_keypresses : keypresses) { // Try to set this key on the keyboard first, if there is one. if(keyboard_machine) { From fe3942c5b3c95b53bf189eee0e1a7f80d75a2ea5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 1 Apr 2020 23:49:07 -0400 Subject: [PATCH 2/2] Updates comments. --- .../Dynamic/MultiMachine/Implementation/MultiProducer.cpp | 2 +- .../Dynamic/MultiMachine/Implementation/MultiProducer.hpp | 8 ++++---- Machines/AudioProducer.hpp | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp index 93b0fb25c..58b35e753 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp @@ -1,5 +1,5 @@ // -// MultiCRTMachine.cpp +// MultiProducer.cpp // Clock Signal // // Created by Thomas Harte on 29/01/2018. diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp index e5f2a85ac..30507fef6 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.hpp @@ -1,13 +1,13 @@ // -// MultiCRTMachine.hpp +// MultiProducer.hpp // Clock Signal // // Created by Thomas Harte on 29/01/2018. // Copyright 2018 Thomas Harte. All rights reserved. // -#ifndef MultiCRTMachine_hpp -#define MultiCRTMachine_hpp +#ifndef MultiProducer_hpp +#define MultiProducer_hpp #include "../../../../Concurrency/AsyncTaskQueue.hpp" #include "../../../../Machines/MachineTypes.hpp" @@ -119,4 +119,4 @@ class MultiAudioProducer: public MultiInterface, pu } -#endif /* MultiCRTMachine_hpp */ +#endif /* MultiProducer_hpp */ diff --git a/Machines/AudioProducer.hpp b/Machines/AudioProducer.hpp index 2bef3b84b..471275a74 100644 --- a/Machines/AudioProducer.hpp +++ b/Machines/AudioProducer.hpp @@ -25,5 +25,3 @@ class AudioProducer { } #endif /* AudioProducer_h */ - -// virtual std::string debug_type() { return ""; }