1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-08-16 16:28:59 +00:00

Merge pull request #781 from TomHarte/NoMoreCRTMachine

Splits 'CRTMachine' into three parts: ScanProducer, AudioProducer, TimedMachine.
This commit is contained in:
Thomas Harte 2020-04-02 09:46:54 -04:00 committed by GitHub
commit 474822e83d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 727 additions and 655 deletions

View File

@ -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 <condition_variable>
#include <mutex>
using namespace Analyser::Dynamic;
MultiCRTMachine::MultiCRTMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &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<void(::CRTMachine::Machine *)> &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<decltype(machines_mutex_)> machines_lock(machines_mutex_);
std::lock_guard<std::mutex> 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<std::mutex> lock(mutex);
outstanding_machines--;
condition.notify_all();
});
}
}
std::unique_lock<std::mutex> lock(mutex);
condition.wait(lock, [&outstanding_machines] { return !outstanding_machines; });
}
void MultiCRTMachine::perform_serial(const std::function<void (::CRTMachine::Machine *)> &function) {
std::lock_guard<decltype(machines_mutex_)> 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());
}
}

View File

@ -16,7 +16,7 @@ namespace {
class MultiJoystick: public Inputs::Joystick { class MultiJoystick: public Inputs::Joystick {
public: public:
MultiJoystick(std::vector<JoystickMachine::Machine *> &machines, std::size_t index) { MultiJoystick(std::vector<MachineTypes::JoystickMachine *> &machines, std::size_t index) {
for(const auto &machine: machines) { for(const auto &machine: machines) {
const auto &joysticks = machine->get_joysticks(); const auto &joysticks = machine->get_joysticks();
if(joysticks.size() >= index) { if(joysticks.size() >= index) {
@ -67,9 +67,9 @@ class MultiJoystick: public Inputs::Joystick {
MultiJoystickMachine::MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) { MultiJoystickMachine::MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
std::size_t total_joysticks = 0; std::size_t total_joysticks = 0;
std::vector<JoystickMachine::Machine *> joystick_machines; std::vector<MachineTypes::JoystickMachine *> joystick_machines;
for(const auto &machine: machines) { for(const auto &machine: machines) {
JoystickMachine::Machine *joystick_machine = machine->joystick_machine(); auto joystick_machine = machine->joystick_machine();
if(joystick_machine) { if(joystick_machine) {
joystick_machines.push_back(joystick_machine); joystick_machines.push_back(joystick_machine);
total_joysticks = std::max(total_joysticks, joystick_machine->get_joysticks().size()); total_joysticks = std::max(total_joysticks, joystick_machine->get_joysticks().size());

View File

@ -23,7 +23,7 @@ namespace Dynamic {
Makes a static internal copy of the list of machines; makes no guarantees about the Makes a static internal copy of the list of machines; makes no guarantees about the
order of delivered messages. order of delivered messages.
*/ */
class MultiJoystickMachine: public JoystickMachine::Machine { class MultiJoystickMachine: public MachineTypes::JoystickMachine {
public: public:
MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines); MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);

View File

@ -13,7 +13,7 @@ using namespace Analyser::Dynamic;
MultiKeyboardMachine::MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) : MultiKeyboardMachine::MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) :
keyboard_(machines_) { keyboard_(machines_) {
for(const auto &machine: 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); if(keyboard_machine) machines_.push_back(keyboard_machine);
} }
} }
@ -48,7 +48,7 @@ Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() {
return keyboard_; return keyboard_;
} }
MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines) MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::MachineTypes::KeyboardMachine *> &machines)
: machines_(machines) { : machines_(machines) {
for(const auto &machine: machines_) { for(const auto &machine: machines_) {
observed_keys_.insert(machine->get_keyboard().observed_keys().begin(), machine->get_keyboard().observed_keys().end()); observed_keys_.insert(machine->get_keyboard().observed_keys().begin(), machine->get_keyboard().observed_keys().end());

View File

@ -24,13 +24,13 @@ namespace Dynamic {
Makes a static internal copy of the list of machines; makes no guarantees about the Makes a static internal copy of the list of machines; makes no guarantees about the
order of delivered messages. order of delivered messages.
*/ */
class MultiKeyboardMachine: public KeyboardMachine::Machine { class MultiKeyboardMachine: public MachineTypes::KeyboardMachine {
private: private:
std::vector<::KeyboardMachine::Machine *> machines_; std::vector<MachineTypes::KeyboardMachine *> machines_;
class MultiKeyboard: public Inputs::Keyboard { class MultiKeyboard: public Inputs::Keyboard {
public: public:
MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines); MultiKeyboard(const std::vector<MachineTypes::KeyboardMachine *> &machines);
bool set_key_pressed(Key key, char value, bool is_pressed) final; bool set_key_pressed(Key key, char value, bool is_pressed) final;
void reset_all_keys() final; void reset_all_keys() final;
@ -38,7 +38,7 @@ class MultiKeyboardMachine: public KeyboardMachine::Machine {
bool is_exclusive() final; bool is_exclusive() final;
private: private:
const std::vector<::KeyboardMachine::Machine *> &machines_; const std::vector<MachineTypes::KeyboardMachine *> &machines_;
std::set<Key> observed_keys_; std::set<Key> observed_keys_;
bool is_exclusive_ = false; bool is_exclusive_ = false;
}; };

View File

@ -12,7 +12,7 @@ using namespace Analyser::Dynamic;
MultiMediaTarget::MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) { MultiMediaTarget::MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
for(const auto &machine: 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); if(media_target) targets_.push_back(media_target);
} }
} }

View File

@ -24,7 +24,7 @@ namespace Dynamic {
Makes a static internal copy of the list of machines; makes no guarantees about the Makes a static internal copy of the list of machines; makes no guarantees about the
order of delivered messages. order of delivered messages.
*/ */
struct MultiMediaTarget: public MediaTarget::Machine { struct MultiMediaTarget: public MachineTypes::MediaTarget {
public: public:
MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines); MultiMediaTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
@ -32,7 +32,7 @@ struct MultiMediaTarget: public MediaTarget::Machine {
bool insert_media(const Analyser::Static::Media &media) final; bool insert_media(const Analyser::Static::Media &media) final;
private: private:
std::vector<MediaTarget::Machine *> targets_; std::vector<MachineTypes::MediaTarget *> targets_;
}; };
} }

View File

@ -0,0 +1,105 @@
//
// MultiProducer.cpp
// Clock Signal
//
// Created by Thomas Harte on 29/01/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiProducer.hpp"
#include <condition_variable>
#include <mutex>
using namespace Analyser::Dynamic;
// MARK: - MultiInterface
template <typename MachineType>
void MultiInterface<MachineType>::perform_parallel(const std::function<void(MachineType *)> &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<decltype(machines_mutex_)> machines_lock(machines_mutex_);
std::lock_guard<std::mutex> lock(mutex);
outstanding_machines = machines_.size();
for(std::size_t index = 0; index < machines_.size(); ++index) {
const auto machine = ::Machine::get<MachineType>(*machines_[index].get());
queues_[index].enqueue([&mutex, &condition, machine, function, &outstanding_machines]() {
if(machine) function(machine);
std::lock_guard<std::mutex> lock(mutex);
outstanding_machines--;
condition.notify_all();
});
}
}
std::unique_lock<std::mutex> lock(mutex);
condition.wait(lock, [&outstanding_machines] { return !outstanding_machines; });
}
template <typename MachineType>
void MultiInterface<MachineType>::perform_serial(const std::function<void(MachineType *)> &function) {
std::lock_guard<decltype(machines_mutex_)> machines_lock(machines_mutex_);
for(const auto &machine: machines_) {
const auto typed_machine = ::Machine::get<MachineType>(*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<decltype(machines_mutex_)> 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<decltype(machines_mutex_)> 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<decltype(machines_mutex_)> 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<std::unique_ptr<::Machine::DynamicMachine>> &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);
}

View File

@ -1,16 +1,16 @@
// //
// MultiCRTMachine.hpp // MultiProducer.hpp
// Clock Signal // Clock Signal
// //
// Created by Thomas Harte on 29/01/2018. // Created by Thomas Harte on 29/01/2018.
// Copyright 2018 Thomas Harte. All rights reserved. // Copyright 2018 Thomas Harte. All rights reserved.
// //
#ifndef MultiCRTMachine_hpp #ifndef MultiProducer_hpp
#define MultiCRTMachine_hpp #define MultiProducer_hpp
#include "../../../../Concurrency/AsyncTaskQueue.hpp" #include "../../../../Concurrency/AsyncTaskQueue.hpp"
#include "../../../../Machines/CRTMachine.hpp" #include "../../../../Machines/MachineTypes.hpp"
#include "../../../../Machines/DynamicMachine.hpp" #include "../../../../Machines/DynamicMachine.hpp"
#include "MultiSpeaker.hpp" #include "MultiSpeaker.hpp"
@ -22,6 +22,91 @@
namespace Analyser { namespace Analyser {
namespace Dynamic { namespace Dynamic {
template <typename MachineType> class MultiInterface {
public:
MultiInterface(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &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<void(MachineType *)> &);
/*!
Performs a serial for operation across all machines, performing the supplied
function on each on the calling thread.
*/
void perform_serial(const std::function<void(MachineType *)> &);
protected:
const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines_;
std::recursive_mutex &machines_mutex_;
private:
std::vector<Concurrency::AsyncTaskQueue> queues_;
};
class MultiTimedMachine: public MultiInterface<MachineTypes::TimedMachine>, 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<MachineTypes::ScanProducer>, 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<MachineTypes::AudioProducer>, public MachineTypes::AudioProducer {
public:
MultiAudioProducer(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &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. Provides a class that multiplexes the CRT machine interface to multiple machines.
@ -29,61 +114,9 @@ namespace Dynamic {
acquiring a supplied mutex. The owner should also call did_change_machine_order() acquiring a supplied mutex. The owner should also call did_change_machine_order()
if the order of machines changes. if the order of machines changes.
*/ */
class MultiCRTMachine: public CRTMachine::Machine {
public:
MultiCRTMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &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<std::unique_ptr<::Machine::DynamicMachine>> &machines_;
std::recursive_mutex &machines_mutex_;
std::vector<Concurrency::AsyncTaskQueue> 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<void(::CRTMachine::Machine *)> &);
/*!
Performs a serial for operation across all machines, performing the supplied
function on each on the calling thread.
*/
void perform_serial(const std::function<void(::CRTMachine::Machine *)> &);
};
} }
} }
#endif /* MultiCRTMachine_hpp */ #endif /* MultiProducer_hpp */

View File

@ -13,7 +13,7 @@ using namespace Analyser::Dynamic;
MultiSpeaker *MultiSpeaker::create(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) { MultiSpeaker *MultiSpeaker::create(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
std::vector<Outputs::Speaker::Speaker *> speakers; std::vector<Outputs::Speaker::Speaker *> speakers;
for(const auto &machine: machines) { 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(speaker) speakers.push_back(speaker);
} }
if(speakers.empty()) return nullptr; 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) { void MultiSpeaker::set_new_front_machine(::Machine::DynamicMachine *machine) {
{ {
std::lock_guard<std::mutex> lock_guard(front_speaker_mutex_); std::lock_guard<std::mutex> lock_guard(front_speaker_mutex_);
front_speaker_ = machine->crt_machine()->get_speaker(); front_speaker_ = machine->audio_producer()->get_speaker();
} }
if(delegate_) { if(delegate_) {
delegate_->speaker_did_change_input_clock(this); delegate_->speaker_did_change_input_clock(this);

View File

@ -16,74 +16,55 @@ using namespace Analyser::Dynamic;
MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines) : MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines) :
machines_(std::move(machines)), machines_(std::move(machines)),
configurable_(machines_), configurable_(machines_),
crt_machine_(machines_, machines_mutex_), timed_machine_(machines_, machines_mutex_),
joystick_machine_(machines), scan_producer_(machines_, machines_mutex_),
audio_producer_(machines_, machines_mutex_),
joystick_machine_(machines_),
keyboard_machine_(machines_), keyboard_machine_(machines_),
media_target_(machines_) { media_target_(machines_) {
crt_machine_.set_delegate(this); timed_machine_.set_delegate(this);
} }
Activity::Source *MultiMachine::activity_source() { Activity::Source *MultiMachine::activity_source() {
return nullptr; // TODO return nullptr; // TODO
} }
MediaTarget::Machine *MultiMachine::media_target() { #define Provider(type, name, member) \
if(has_picked_) { type *MultiMachine::name() { \
return machines_.front()->media_target(); if(has_picked_) { \
} else { return machines_.front()->name(); \
return &media_target_; } else { \
return &member; \
} \
} }
}
CRTMachine::Machine *MultiMachine::crt_machine() { Provider(Configurable::Device, configurable_device, configurable_)
if(has_picked_) { Provider(MachineTypes::TimedMachine, timed_machine, timed_machine_)
return machines_.front()->crt_machine(); Provider(MachineTypes::ScanProducer, scan_producer, scan_producer_)
} else { Provider(MachineTypes::AudioProducer, audio_producer, audio_producer_)
return &crt_machine_; 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() { MachineTypes::MouseMachine *MultiMachine::mouse_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() {
// TODO. // TODO.
return nullptr; return nullptr;
} }
Configurable::Device *MultiMachine::configurable_device() { #undef Provider
if(has_picked_) {
return machines_.front()->configurable_device();
} else {
return &configurable_;
}
}
bool MultiMachine::would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines) { bool MultiMachine::would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines) {
return return
(machines.front()->crt_machine()->get_confidence() > 0.9f) || (machines.front()->timed_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() >= 2.0f * machines[1]->timed_machine()->get_confidence());
} }
void MultiMachine::multi_crt_did_run_machines() { void MultiMachine::did_run_machines(MultiTimedMachine *) {
std::lock_guard<decltype(machines_mutex_)> machines_lock(machines_mutex_); std::lock_guard<decltype(machines_mutex_)> machines_lock(machines_mutex_);
#ifndef NDEBUG #ifndef NDEBUG
for(const auto &machine: machines_) { for(const auto &machine: machines_) {
CRTMachine::Machine *crt = machine->crt_machine(); auto timed_machine = machine->timed_machine();
LOGNBR(PADHEX(2) << crt->get_confidence() << " " << crt->debug_type() << "; "); LOGNBR(PADHEX(2) << timed_machine->get_confidence() << " " << timed_machine->debug_type() << "; ");
} }
LOGNBR(std::endl); LOGNBR(std::endl);
#endif #endif
@ -91,13 +72,14 @@ void MultiMachine::multi_crt_did_run_machines() {
DynamicMachine *front = machines_.front().get(); DynamicMachine *front = machines_.front().get();
std::stable_sort(machines_.begin(), machines_.end(), std::stable_sort(machines_.begin(), machines_.end(),
[] (const std::unique_ptr<DynamicMachine> &lhs, const std::unique_ptr<DynamicMachine> &rhs){ [] (const std::unique_ptr<DynamicMachine> &lhs, const std::unique_ptr<DynamicMachine> &rhs){
CRTMachine::Machine *lhs_crt = lhs->crt_machine(); auto lhs_timed = lhs->timed_machine();
CRTMachine::Machine *rhs_crt = rhs->crt_machine(); auto rhs_timed = rhs->timed_machine();
return lhs_crt->get_confidence() > rhs_crt->get_confidence(); return lhs_timed->get_confidence() > rhs_timed->get_confidence();
}); });
if(machines_.front().get() != front) { 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_)) { if(would_collapse(machines_)) {

View File

@ -11,8 +11,9 @@
#include "../../../Machines/DynamicMachine.hpp" #include "../../../Machines/DynamicMachine.hpp"
#include "Implementation/MultiProducer.hpp"
#include "Implementation/MultiConfigurable.hpp" #include "Implementation/MultiConfigurable.hpp"
#include "Implementation/MultiCRTMachine.hpp" #include "Implementation/MultiProducer.hpp"
#include "Implementation/MultiJoystickMachine.hpp" #include "Implementation/MultiJoystickMachine.hpp"
#include "Implementation/MultiKeyboardMachine.hpp" #include "Implementation/MultiKeyboardMachine.hpp"
#include "Implementation/MultiMediaTarget.hpp" #include "Implementation/MultiMediaTarget.hpp"
@ -38,7 +39,7 @@ namespace Dynamic {
If confidence for any machine becomes disproportionately low compared to If confidence for any machine becomes disproportionately low compared to
the others in the set, that machine stops running. 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: public:
/*! /*!
Allows a potential MultiMachine creator to enquire as to whether there's any benefit in 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; Activity::Source *activity_source() final;
Configurable::Device *configurable_device() final; Configurable::Device *configurable_device() final;
CRTMachine::Machine *crt_machine() final; MachineTypes::TimedMachine *timed_machine() final;
JoystickMachine::Machine *joystick_machine() final; MachineTypes::ScanProducer *scan_producer() final;
MouseMachine::Machine *mouse_machine() final; MachineTypes::AudioProducer *audio_producer() final;
KeyboardMachine::Machine *keyboard_machine() final; MachineTypes::JoystickMachine *joystick_machine() final;
MediaTarget::Machine *media_target() final; MachineTypes::KeyboardMachine *keyboard_machine() final;
MachineTypes::MouseMachine *mouse_machine() final;
MachineTypes::MediaTarget *media_target() final;
void *raw_pointer() final; void *raw_pointer() final;
private: private:
void multi_crt_did_run_machines() final; void did_run_machines(MultiTimedMachine *) final;
std::vector<std::unique_ptr<DynamicMachine>> machines_; std::vector<std::unique_ptr<DynamicMachine>> machines_;
std::recursive_mutex machines_mutex_; std::recursive_mutex machines_mutex_;
MultiConfigurable configurable_; MultiConfigurable configurable_;
MultiCRTMachine crt_machine_; MultiTimedMachine timed_machine_;
MultiScanProducer scan_producer_;
MultiAudioProducer audio_producer_;
MultiJoystickMachine joystick_machine_; MultiJoystickMachine joystick_machine_;
MultiKeyboardMachine keyboard_machine_; MultiKeyboardMachine keyboard_machine_;
MultiMediaTarget media_target_; MultiMediaTarget media_target_;

View File

@ -21,10 +21,7 @@
#include "../Utility/Typer.hpp" #include "../Utility/Typer.hpp"
#include "../../Activity/Source.hpp" #include "../../Activity/Source.hpp"
#include "../MediaTarget.hpp" #include "../MachineTypes.hpp"
#include "../CRTMachine.hpp"
#include "../JoystickMachine.hpp"
#include "../KeyboardMachine.hpp"
#include "../../Storage/Tape/Tape.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. The actual Amstrad CPC implementation; tying the 8255, 6845 and AY to the Z80.
*/ */
template <bool has_fdc> class ConcreteMachine: template <bool has_fdc> class ConcreteMachine:
public CRTMachine::Machine, public MachineTypes::ScanProducer,
public MediaTarget::Machine, public MachineTypes::AudioProducer,
public KeyboardMachine::MappedMachine, public MachineTypes::TimedMachine,
public MachineTypes::MediaTarget,
public MachineTypes::MappedKeyboardMachine,
public MachineTypes::JoystickMachine,
public Utility::TypeRecipient<CharacterMapper>, public Utility::TypeRecipient<CharacterMapper>,
public CPU::Z80::BusHandler, public CPU::Z80::BusHandler,
public ClockingHint::Observer, public ClockingHint::Observer,
public Configurable::Device, public Configurable::Device,
public JoystickMachine::Machine,
public Machine, public Machine,
public Activity::Source { public Activity::Source {
public: public:

View File

@ -74,13 +74,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
BIND(KeypadDelete, KeyDelete); BIND(KeypadDelete, KeyDelete);
} }
#undef BIND #undef BIND
return KeyboardMachine::MappedMachine::KeyNotMapped; return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
} }
uint16_t *CharacterMapper::sequence_for_character(char character) { uint16_t *CharacterMapper::sequence_for_character(char character) {
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define SHIFT(...) {KeyShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define X {KeyboardMachine::MappedMachine::KeyNotMapped} #define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped}
static KeySequence key_sequences[] = { static KeySequence key_sequences[] = {
/* NUL */ X, /* SOH */ X, /* NUL */ X, /* SOH */ X,
/* STX */ X, /* ETX */ X, /* STX */ X, /* ETX */ X,

View File

@ -33,7 +33,7 @@ enum Key: uint16_t {
#undef Line #undef Line
}; };
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
}; };

View File

@ -9,10 +9,7 @@
#include "AppleII.hpp" #include "AppleII.hpp"
#include "../../../Activity/Source.hpp" #include "../../../Activity/Source.hpp"
#include "../../MediaTarget.hpp" #include "../../MachineTypes.hpp"
#include "../../CRTMachine.hpp"
#include "../../JoystickMachine.hpp"
#include "../../KeyboardMachine.hpp"
#include "../../Utility/MemoryFuzzer.hpp" #include "../../Utility/MemoryFuzzer.hpp"
#include "../../Utility/StringSerialiser.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)) #define is_iie() ((model == Analyser::Static::AppleII::Target::Model::IIe) || (model == Analyser::Static::AppleII::Target::Model::EnhancedIIe))
template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine: template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public MediaTarget::Machine, public MachineTypes::ScanProducer,
public KeyboardMachine::MappedMachine, public MachineTypes::AudioProducer,
public MachineTypes::MediaTarget,
public MachineTypes::MappedKeyboardMachine,
public MachineTypes::JoystickMachine,
public CPU::MOS6502::BusHandler, public CPU::MOS6502::BusHandler,
public Inputs::Keyboard, public Inputs::Keyboard,
public Configurable::Device, public Configurable::Device,
public Apple::II::Machine, public Apple::II::Machine,
public Activity::Source, public Activity::Source,
public JoystickMachine::Machine,
public Apple::II::Card::Delegate { public Apple::II::Card::Delegate {
private: private:
struct VideoBusHandler : public Apple::II::Video::BusHandler { struct VideoBusHandler : public Apple::II::Video::BusHandler {

View File

@ -14,7 +14,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
using Key = Inputs::Keyboard::Key; using Key = Inputs::Keyboard::Key;
using MacKey = Apple::Macintosh::Key; using MacKey = Apple::Macintosh::Key;
switch(key) { switch(key) {
default: return KeyboardMachine::MappedMachine::KeyNotMapped; default: return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
#define Bind(x, y) case Key::x: return uint16_t(y) #define Bind(x, y) case Key::x: return uint16_t(y)

View File

@ -290,7 +290,7 @@ class Keyboard {
/*! /*!
Provides a mapping from idiomatic PC keys to Macintosh keys. 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; uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) final;
}; };

View File

@ -16,11 +16,8 @@
#include "RealTimeClock.hpp" #include "RealTimeClock.hpp"
#include "Video.hpp" #include "Video.hpp"
#include "../../MachineTypes.hpp"
#include "../../../Activity/Source.hpp" #include "../../../Activity/Source.hpp"
#include "../../CRTMachine.hpp"
#include "../../KeyboardMachine.hpp"
#include "../../MediaTarget.hpp"
#include "../../MouseMachine.hpp"
#include "../../../Configurable/Configurable.hpp" #include "../../../Configurable/Configurable.hpp"
#include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp" #include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp"
@ -59,11 +56,13 @@ namespace Macintosh {
template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachine: template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachine:
public Machine, public Machine,
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public MediaTarget::Machine, public MachineTypes::ScanProducer,
public MouseMachine::Machine, public MachineTypes::AudioProducer,
public MachineTypes::MediaTarget,
public MachineTypes::MouseMachine,
public MachineTypes::MappedKeyboardMachine,
public CPU::MC68000::BusHandler, public CPU::MC68000::BusHandler,
public KeyboardMachine::MappedMachine,
public Zilog::SCC::z8530::Delegate, public Zilog::SCC::z8530::Delegate,
public Activity::Source, public Activity::Source,
public Configurable::Device, public Configurable::Device,
@ -73,7 +72,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
using Target = Analyser::Static::Macintosh::Target; using Target = Analyser::Static::Macintosh::Target;
ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
KeyboardMachine::MappedMachine({ MachineTypes::MappedKeyboardMachine({
Inputs::Keyboard::Key::LeftShift, Inputs::Keyboard::Key::RightShift, Inputs::Keyboard::Key::LeftShift, Inputs::Keyboard::Key::RightShift,
Inputs::Keyboard::Key::LeftOption, Inputs::Keyboard::Key::RightOption, Inputs::Keyboard::Key::LeftOption, Inputs::Keyboard::Key::RightOption,
Inputs::Keyboard::Key::LeftMeta, Inputs::Keyboard::Key::RightMeta, Inputs::Keyboard::Key::LeftMeta, Inputs::Keyboard::Key::RightMeta,

View File

@ -11,8 +11,7 @@
#include <algorithm> #include <algorithm>
#include <cstdio> #include <cstdio>
#include "../../CRTMachine.hpp" #include "../../MachineTypes.hpp"
#include "../../JoystickMachine.hpp"
#include "../../../Analyser/Static/Atari2600/Target.hpp" #include "../../../Analyser/Static/Atari2600/Target.hpp"
@ -76,8 +75,10 @@ using Target = Analyser::Static::Atari2600::Target;
class ConcreteMachine: class ConcreteMachine:
public Machine, public Machine,
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public JoystickMachine::Machine { public MachineTypes::AudioProducer,
public MachineTypes::ScanProducer,
public MachineTypes::JoystickMachine {
public: public:
ConcreteMachine(const Target &target) : frequency_mismatch_warner_(*this) { ConcreteMachine(const Target &target) : frequency_mismatch_warner_(*this) {
const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data; const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data;

View File

@ -8,11 +8,7 @@
#include "AtariST.hpp" #include "AtariST.hpp"
#include "../../CRTMachine.hpp" #include "../../MachineTypes.hpp"
#include "../../JoystickMachine.hpp"
#include "../../KeyboardMachine.hpp"
#include "../../MouseMachine.hpp"
#include "../../MediaTarget.hpp"
#include "../../../Activity/Source.hpp" #include "../../../Activity/Source.hpp"
//#define LOG_TRACE //#define LOG_TRACE
@ -48,16 +44,18 @@ using Target = Analyser::Static::Target;
class ConcreteMachine: class ConcreteMachine:
public Atari::ST::Machine, public Atari::ST::Machine,
public CPU::MC68000::BusHandler, 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 ClockingHint::Observer,
public Motorola::ACIA::ACIA::InterruptDelegate, public Motorola::ACIA::ACIA::InterruptDelegate,
public Motorola::MFP68901::MFP68901::InterruptDelegate, public Motorola::MFP68901::MFP68901::InterruptDelegate,
public DMAController::Delegate, public DMAController::Delegate,
public MouseMachine::Machine,
public JoystickMachine::Machine,
public KeyboardMachine::MappedMachine,
public Activity::Source, public Activity::Source,
public MediaTarget::Machine,
public GI::AY38910::PortHandler, public GI::AY38910::PortHandler,
public Configurable::Device, public Configurable::Device,
public Video::RangeObserver { public Video::RangeObserver {

View File

@ -365,7 +365,7 @@ uint16_t IntelligentKeyboard::KeyboardMapper::mapped_key_for_key(Inputs::Keyboar
using Key = Inputs::Keyboard::Key; using Key = Inputs::Keyboard::Key;
using STKey = Atari::ST::Key; using STKey = Atari::ST::Key;
switch(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 Bind(x, y) case Key::x: return uint16_t(STKey::y)
#define QBind(x) case Key::x: return uint16_t(STKey::x) #define QBind(x) case Key::x: return uint16_t(STKey::x)

View File

@ -62,7 +62,7 @@ class IntelligentKeyboard:
void run_for(HalfCycles duration); void run_for(HalfCycles duration);
void set_key_state(Key key, bool is_pressed); 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; uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) final;
}; };

View File

@ -0,0 +1,27 @@
//
// 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 */

View File

@ -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 <array>
#include <cmath>
// 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<int>(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<bool()> 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 */

View File

@ -14,8 +14,7 @@
#include "../../Components/AY38910/AY38910.hpp" // For the Super Game Module. #include "../../Components/AY38910/AY38910.hpp" // For the Super Game Module.
#include "../../Components/SN76489/SN76489.hpp" #include "../../Components/SN76489/SN76489.hpp"
#include "../CRTMachine.hpp" #include "../MachineTypes.hpp"
#include "../JoystickMachine.hpp"
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../Configurable/StandardOptions.hpp" #include "../../Configurable/StandardOptions.hpp"
@ -109,9 +108,11 @@ class Joystick: public Inputs::ConcreteJoystick {
class ConcreteMachine: class ConcreteMachine:
public Machine, public Machine,
public CPU::Z80::BusHandler, public CPU::Z80::BusHandler,
public CRTMachine::Machine,
public Configurable::Device, public Configurable::Device,
public JoystickMachine::Machine { public MachineTypes::TimedMachine,
public MachineTypes::ScanProducer,
public MachineTypes::AudioProducer,
public MachineTypes::JoystickMachine {
public: public:
ConcreteMachine(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : ConcreteMachine(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :

View File

@ -76,13 +76,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
BIND(F8, KeyF8); BIND(F8, KeyF8);
} }
#undef BIND #undef BIND
return KeyboardMachine::MappedMachine::KeyNotMapped; return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
} }
uint16_t *CharacterMapper::sequence_for_character(char character) { uint16_t *CharacterMapper::sequence_for_character(char character) {
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define SHIFT(...) {KeyLShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define SHIFT(...) {KeyLShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define X {KeyboardMachine::MappedMachine::KeyNotMapped} #define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped}
static KeySequence key_sequences[] = { static KeySequence key_sequences[] = {
/* NUL */ X, /* SOH */ X, /* NUL */ X, /* SOH */ X,
/* STX */ X, /* ETX */ X, /* STX */ X, /* ETX */ X,

View File

@ -47,7 +47,7 @@ enum Key: uint16_t {
#undef key #undef key
}; };
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
}; };

View File

@ -11,10 +11,7 @@
#include "Keyboard.hpp" #include "Keyboard.hpp"
#include "../../../Activity/Source.hpp" #include "../../../Activity/Source.hpp"
#include "../../MediaTarget.hpp" #include "../../MachineTypes.hpp"
#include "../../CRTMachine.hpp"
#include "../../KeyboardMachine.hpp"
#include "../../JoystickMachine.hpp"
#include "../../../Processors/6502/6502.hpp" #include "../../../Processors/6502/6502.hpp"
#include "../../../Components/6560/6560.hpp" #include "../../../Components/6560/6560.hpp"
@ -274,10 +271,12 @@ class Joystick: public Inputs::ConcreteJoystick {
}; };
class ConcreteMachine: class ConcreteMachine:
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public MediaTarget::Machine, public MachineTypes::ScanProducer,
public KeyboardMachine::MappedMachine, public MachineTypes::AudioProducer,
public JoystickMachine::Machine, public MachineTypes::MediaTarget,
public MachineTypes::MappedKeyboardMachine,
public MachineTypes::JoystickMachine,
public Configurable::Device, public Configurable::Device,
public CPU::MOS6502::BusHandler, public CPU::MOS6502::BusHandler,
public MOS::MOS6522::IRQDelegatePortHandler::Delegate, public MOS::MOS6522::IRQDelegatePortHandler::Delegate,

View File

@ -12,13 +12,7 @@
#include "../Configurable/Configurable.hpp" #include "../Configurable/Configurable.hpp"
#include "../Activity/Source.hpp" #include "../Activity/Source.hpp"
#include "CRTMachine.hpp" #include "MachineTypes.hpp"
#include "JoystickMachine.hpp"
#include "KeyboardMachine.hpp"
#include "MediaTarget.hpp"
#include "MouseMachine.hpp"
#include "Utility/Typer.hpp"
namespace Machine { namespace Machine {
@ -31,11 +25,13 @@ struct DynamicMachine {
virtual Activity::Source *activity_source() = 0; virtual Activity::Source *activity_source() = 0;
virtual Configurable::Device *configurable_device() = 0; virtual Configurable::Device *configurable_device() = 0;
virtual CRTMachine::Machine *crt_machine() = 0; virtual MachineTypes::TimedMachine *timed_machine() = 0;
virtual JoystickMachine::Machine *joystick_machine() = 0; virtual MachineTypes::ScanProducer *scan_producer() = 0;
virtual KeyboardMachine::Machine *keyboard_machine() = 0; virtual MachineTypes::AudioProducer *audio_producer() = 0;
virtual MouseMachine::Machine *mouse_machine() = 0; virtual MachineTypes::JoystickMachine *joystick_machine() = 0;
virtual MediaTarget::Machine *media_target() = 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 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; virtual void *raw_pointer() = 0;
}; };
/*!
Provides a templateable means to access the above.
*/
template <typename MachineType> MachineType *get(DynamicMachine &);
#define SpecialisedGet(type, name) \
template <> \
inline type *get<type>(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 */ #endif /* DynamicMachine_h */

View File

@ -9,9 +9,7 @@
#include "Electron.hpp" #include "Electron.hpp"
#include "../../Activity/Source.hpp" #include "../../Activity/Source.hpp"
#include "../MediaTarget.hpp" #include "../MachineTypes.hpp"
#include "../CRTMachine.hpp"
#include "../KeyboardMachine.hpp"
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../ClockReceiver/ClockReceiver.hpp" #include "../../ClockReceiver/ClockReceiver.hpp"
@ -35,9 +33,11 @@ namespace Electron {
class ConcreteMachine: class ConcreteMachine:
public Machine, public Machine,
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public MediaTarget::Machine, public MachineTypes::ScanProducer,
public KeyboardMachine::MappedMachine, public MachineTypes::AudioProducer,
public MachineTypes::MediaTarget,
public MachineTypes::MappedKeyboardMachine,
public Configurable::Device, public Configurable::Device,
public CPU::MOS6502::BusHandler, public CPU::MOS6502::BusHandler,
public Tape::Delegate, public Tape::Delegate,

View File

@ -66,14 +66,14 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
BIND(F10, KeyF0); BIND(F10, KeyF0);
} }
#undef BIND #undef BIND
return KeyboardMachine::MappedMachine::KeyNotMapped; return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
} }
uint16_t *CharacterMapper::sequence_for_character(char character) { uint16_t *CharacterMapper::sequence_for_character(char character) {
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define SHIFT(...) {KeyShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define CTRL(...) {KeyControl, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define CTRL(...) {KeyControl, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define X {KeyboardMachine::MappedMachine::KeyNotMapped} #define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped}
static KeySequence key_sequences[] = { static KeySequence key_sequences[] = {
/* NUL */ X, /* SOH */ X, /* NUL */ X, /* SOH */ X,
/* STX */ X, /* ETX */ X, /* STX */ X, /* ETX */ X,

View File

@ -36,7 +36,7 @@ enum Key: uint16_t {
KeyBreak = 0xfffd, 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; uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) final;
}; };

View File

@ -12,9 +12,9 @@
#include "../Inputs/Joystick.hpp" #include "../Inputs/Joystick.hpp"
#include <vector> #include <vector>
namespace JoystickMachine { namespace MachineTypes {
class Machine { class JoystickMachine {
public: public:
virtual const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() = 0; virtual const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() = 0;
}; };

View File

@ -8,31 +8,31 @@
#include "KeyboardMachine.hpp" #include "KeyboardMachine.hpp"
using namespace KeyboardMachine; using namespace MachineTypes;
MappedMachine::MappedMachine(const std::set<Inputs::Keyboard::Key> &essential_modifiers) : keyboard_(essential_modifiers) { MachineTypes::MappedKeyboardMachine::MappedKeyboardMachine(const std::set<Inputs::Keyboard::Key> &essential_modifiers) : keyboard_(essential_modifiers) {
keyboard_.set_delegate(this); 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); uint16_t mapped_key = get_keyboard_mapper()->mapped_key_for_key(key);
if(mapped_key == KeyNotMapped) return false; if(mapped_key == KeyNotMapped) return false;
set_key_state(mapped_key, is_pressed); set_key_state(mapped_key, is_pressed);
return true; return true;
} }
void MappedMachine::reset_all_keys(Inputs::Keyboard *keyboard) { void MappedKeyboardMachine::reset_all_keys(Inputs::Keyboard *keyboard) {
// TODO: unify naming. // TODO: unify naming.
clear_all_keys(); clear_all_keys();
} }
Inputs::Keyboard &MappedMachine::get_keyboard() { Inputs::Keyboard &MappedKeyboardMachine::get_keyboard() {
return 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; return nullptr;
} }

View File

@ -16,7 +16,7 @@
#include "../Inputs/Keyboard.hpp" #include "../Inputs/Keyboard.hpp"
namespace KeyboardMachine { namespace MachineTypes {
/*! /*!
Covers just the actions necessary to communicate keyboard state, as a purely abstract class. 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. Describes an emulated machine which exposes a keyboard and accepts a typed string.
*/ */
class Machine: public KeyActions { class KeyboardMachine: public KeyActions {
public: public:
/*! /*!
Causes the machine to attempt to type the supplied string. 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, Provides a base class for machines that want to provide a keyboard mapper,
allowing automatic mapping from keyboard inputs to KeyActions. 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: public:
MappedMachine(const std::set<Inputs::Keyboard::Key> &essential_modifiers = {}); MappedKeyboardMachine(const std::set<Inputs::Keyboard::Key> &essential_modifiers = {});
/*! /*!
A keyboard mapper attempts to provide a physical mapping between host keys and emulated keys. A keyboard mapper attempts to provide a physical mapping between host keys and emulated keys.

View File

@ -57,5 +57,5 @@ uint16_t MSX::KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
default: break; default: break;
} }
#undef BIND #undef BIND
return KeyboardMachine::MappedMachine::KeyNotMapped; return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
} }

View File

@ -33,7 +33,7 @@ enum Key: uint16_t {
#undef Line #undef Line
}; };
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
}; };

View File

@ -33,10 +33,7 @@
#include "../../Storage/Tape/Tape.hpp" #include "../../Storage/Tape/Tape.hpp"
#include "../../Activity/Source.hpp" #include "../../Activity/Source.hpp"
#include "../CRTMachine.hpp" #include "../MachineTypes.hpp"
#include "../JoystickMachine.hpp"
#include "../MediaTarget.hpp"
#include "../KeyboardMachine.hpp"
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../Outputs/Log.hpp" #include "../../Outputs/Log.hpp"
@ -134,11 +131,13 @@ class AYPortHandler: public GI::AY38910::PortHandler {
class ConcreteMachine: class ConcreteMachine:
public Machine, public Machine,
public CPU::Z80::BusHandler, public CPU::Z80::BusHandler,
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public MediaTarget::Machine, public MachineTypes::AudioProducer,
public KeyboardMachine::MappedMachine, public MachineTypes::ScanProducer,
public MachineTypes::MediaTarget,
public MachineTypes::MappedKeyboardMachine,
public MachineTypes::JoystickMachine,
public Configurable::Device, public Configurable::Device,
public JoystickMachine::Machine,
public MemoryMap, public MemoryMap,
public ClockingHint::Observer, public ClockingHint::Observer,
public Activity::Source { public Activity::Source {

25
Machines/MachineTypes.hpp Normal file
View File

@ -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 */

View File

@ -13,9 +13,7 @@
#include "../../Components/9918/9918.hpp" #include "../../Components/9918/9918.hpp"
#include "../../Components/SN76489/SN76489.hpp" #include "../../Components/SN76489/SN76489.hpp"
#include "../CRTMachine.hpp" #include "../MachineTypes.hpp"
#include "../JoystickMachine.hpp"
#include "../KeyboardMachine.hpp"
#include "../../Configurable/Configurable.hpp" #include "../../Configurable/Configurable.hpp"
#include "../../ClockReceiver/ForceInline.hpp" #include "../../ClockReceiver/ForceInline.hpp"
@ -80,11 +78,13 @@ class Joystick: public Inputs::ConcreteJoystick {
class ConcreteMachine: class ConcreteMachine:
public Machine, public Machine,
public CPU::Z80::BusHandler, public CPU::Z80::BusHandler,
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public KeyboardMachine::Machine, public MachineTypes::ScanProducer,
public Inputs::Keyboard::Delegate, public MachineTypes::AudioProducer,
public MachineTypes::KeyboardMachine,
public MachineTypes::JoystickMachine,
public Configurable::Device, public Configurable::Device,
public JoystickMachine::Machine { public Inputs::Keyboard::Delegate {
public: public:
ConcreteMachine(const Analyser::Static::Sega::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : ConcreteMachine(const Analyser::Static::Sega::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :

View File

@ -14,12 +14,12 @@
#include <string> #include <string>
namespace MediaTarget { namespace MachineTypes {
/*! /*!
A MediaTarget::Machine is anything that can accept new media while running. A MediaTarget::Machine is anything that can accept new media while running.
*/ */
class Machine { class MediaTarget {
public: public:
/*! /*!
Requests that the machine insert @c media as a modification to current state Requests that the machine insert @c media as a modification to current state

View File

@ -11,10 +11,11 @@
#include "../Inputs/Mouse.hpp" #include "../Inputs/Mouse.hpp"
namespace MouseMachine { namespace MachineTypes {
class Machine { class MouseMachine {
public: public:
// TODO: support multiple mice?
virtual Inputs::Mouse &get_mouse() = 0; virtual Inputs::Mouse &get_mouse() = 0;
}; };

View File

@ -51,13 +51,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
} }
#undef BIND #undef BIND
return KeyboardMachine::MappedMachine::KeyNotMapped; return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
} }
uint16_t *CharacterMapper::sequence_for_character(char character) { uint16_t *CharacterMapper::sequence_for_character(char character) {
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define SHIFT(...) {KeyLeftShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define SHIFT(...) {KeyLeftShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define X {KeyboardMachine::MappedMachine::KeyNotMapped} #define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped}
static KeySequence key_sequences[] = { static KeySequence key_sequences[] = {
/* NUL */ X, /* SOH */ X, /* NUL */ X, /* SOH */ X,
/* STX */ X, /* ETX */ X, /* STX */ X, /* ETX */ X,

View File

@ -36,7 +36,7 @@ enum Key: uint16_t {
KeyJasminReset = 0xfffc, KeyJasminReset = 0xfffc,
}; };
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
}; };

View File

@ -15,9 +15,7 @@
#include "Video.hpp" #include "Video.hpp"
#include "../../Activity/Source.hpp" #include "../../Activity/Source.hpp"
#include "../MediaTarget.hpp" #include "../MachineTypes.hpp"
#include "../CRTMachine.hpp"
#include "../KeyboardMachine.hpp"
#include "../Utility/MemoryFuzzer.hpp" #include "../Utility/MemoryFuzzer.hpp"
#include "../Utility/StringSerialiser.hpp" #include "../Utility/StringSerialiser.hpp"
@ -208,9 +206,11 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
}; };
template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class ConcreteMachine: template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class ConcreteMachine:
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public MediaTarget::Machine, public MachineTypes::ScanProducer,
public KeyboardMachine::MappedMachine, public MachineTypes::AudioProducer,
public MachineTypes::MediaTarget,
public MachineTypes::MappedKeyboardMachine,
public Configurable::Device, public Configurable::Device,
public CPU::MOS6502::BusHandler, public CPU::MOS6502::BusHandler,
public MOS::MOS6522::IRQDelegatePortHandler::Delegate, public MOS::MOS6522::IRQDelegatePortHandler::Delegate,

104
Machines/ScanProducer.hpp Normal file
View File

@ -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<const TimedMachine *>(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 */

89
Machines/TimedMachine.hpp Normal file
View File

@ -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 <cmath>
namespace MachineTypes {
/*!
A timed machine is any which requires the owner to provide time-based updates,
i.e. run_for(<some number of seconds>)-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<int>(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<AudioProducer *>(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 */

View File

@ -13,6 +13,7 @@
#include "../../Reflection/Struct.hpp" #include "../../Reflection/Struct.hpp"
#include "../DynamicMachine.hpp" #include "../DynamicMachine.hpp"
#include "../ROMMachine.hpp"
#include <map> #include <map>
#include <memory> #include <memory>

View File

@ -25,33 +25,22 @@ template<typename T> class TypedDynamicMachine: public ::Machine::DynamicMachine
return *this; return *this;
} }
Activity::Source *activity_source() final { #define Provide(type, name) \
return get<Activity::Source>(); type *name() final { \
return get<type>(); \
} }
MediaTarget::Machine *media_target() final { Provide(Activity::Source, activity_source)
return get<MediaTarget::Machine>(); 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 { #undef Provide
return get<CRTMachine::Machine>();
}
JoystickMachine::Machine *joystick_machine() final {
return get<JoystickMachine::Machine>();
}
KeyboardMachine::Machine *keyboard_machine() final {
return get<KeyboardMachine::Machine>();
}
MouseMachine::Machine *mouse_machine() final {
return get<MouseMachine::Machine>();
}
Configurable::Device *configurable_device() final {
return get<Configurable::Device>();
}
void *raw_pointer() final { void *raw_pointer() final {
return get(); return get();

View File

@ -72,7 +72,7 @@ void Typer::append(const std::string &string) {
const uint16_t *Typer::sequence_for_character(char c) const { const uint16_t *Typer::sequence_for_character(char c) const {
const uint16_t *const sequence = character_mapper_.sequence_for_character(c); 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 nullptr;
} }
return sequence; return sequence;
@ -102,7 +102,7 @@ uint16_t Typer::try_type_next_character() {
} }
// If the sequence is over, stop. // If the sequence is over, stop.
if(sequence[phase_ - 2] == KeyboardMachine::MappedMachine::KeyEndSequence) { if(sequence[phase_ - 2] == MachineTypes::MappedKeyboardMachine::KeyEndSequence) {
return 0; 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) { uint16_t *CharacterMapper::table_lookup_sequence_for_character(KeySequence *sequences, std::size_t length, char character) {
std::size_t ucharacter = static_cast<std::size_t>((unsigned char)character); std::size_t ucharacter = static_cast<std::size_t>((unsigned char)character);
if(ucharacter >= (length / sizeof(KeySequence))) return nullptr; 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]; return sequences[ucharacter];
} }

View File

@ -61,7 +61,7 @@ class CharacterMapper {
*/ */
class Typer { class Typer {
public: public:
class Delegate: public KeyboardMachine::KeyActions { class Delegate: public MachineTypes::KeyActions {
public: public:
/// Informs the delegate that this typer has reached the end of its content. /// Informs the delegate that this typer has reached the end of its content.
virtual void typer_reset(Typer *typer) = 0; 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 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. which may or may not want to nominate an initial delay and typing frequency.
*/ */
template <typename CMApper> template <typename CMapper>
class TypeRecipient: public Typer::Delegate { class TypeRecipient: public Typer::Delegate {
protected: protected:
template <typename... Args> TypeRecipient(Args&&... args) : character_mapper(std::forward<Args>(args)...) {} template <typename... Args> TypeRecipient(Args&&... args) : character_mapper(std::forward<Args>(args)...) {}
@ -120,7 +120,7 @@ class TypeRecipient: public Typer::Delegate {
*/ */
bool can_type(char c) { bool can_type(char c) {
const auto sequence = character_mapper.sequence_for_character(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: private:
std::unique_ptr<Typer> previous_typer_; std::unique_ptr<Typer> previous_typer_;
CMApper character_mapper; CMapper character_mapper;
}; };
} }

View File

@ -39,15 +39,15 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
BIND(BackTick, KeyEdit); BIND(F1, KeyEdit); BIND(BackTick, KeyEdit); BIND(F1, KeyEdit);
} }
#undef BIND #undef BIND
return KeyboardMachine::MappedMachine::KeyNotMapped; return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
} }
CharacterMapper::CharacterMapper(bool is_zx81) : is_zx81_(is_zx81) {} CharacterMapper::CharacterMapper(bool is_zx81) : is_zx81_(is_zx81) {}
uint16_t *CharacterMapper::sequence_for_character(char character) { uint16_t *CharacterMapper::sequence_for_character(char character) {
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} #define SHIFT(...) {KeyShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define X {KeyboardMachine::MappedMachine::KeyNotMapped} #define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped}
static KeySequence zx81_key_sequences[] = { static KeySequence zx81_key_sequences[] = {
/* NUL */ X, /* SOH */ X, /* NUL */ X, /* SOH */ X,
/* STX */ X, /* ETX */ X, /* STX */ X, /* ETX */ X,

View File

@ -29,7 +29,7 @@ enum Key: uint16_t {
KeyBreak, KeyLeft, KeyRight, KeyUp, KeyDown, KeyEdit 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); uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
}; };

View File

@ -8,9 +8,7 @@
#include "ZX8081.hpp" #include "ZX8081.hpp"
#include "../MediaTarget.hpp" #include "../MachineTypes.hpp"
#include "../CRTMachine.hpp"
#include "../KeyboardMachine.hpp"
#include "../../Components/AY38910/AY38910.hpp" #include "../../Components/AY38910/AY38910.hpp"
#include "../../Processors/Z80/Z80.hpp" #include "../../Processors/Z80/Z80.hpp"
@ -51,9 +49,11 @@ enum ROMType: uint8_t {
}; };
template<bool is_zx81> class ConcreteMachine: template<bool is_zx81> class ConcreteMachine:
public CRTMachine::Machine, public MachineTypes::TimedMachine,
public MediaTarget::Machine, public MachineTypes::ScanProducer,
public KeyboardMachine::MappedMachine, public MachineTypes::AudioProducer,
public MachineTypes::MediaTarget,
public MachineTypes::MappedKeyboardMachine,
public Configurable::Device, public Configurable::Device,
public Utility::TypeRecipient<CharacterMapper>, public Utility::TypeRecipient<CharacterMapper>,
public CPU::Z80::BusHandler, public CPU::Z80::BusHandler,

View File

@ -770,8 +770,8 @@
4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; }; 4BB73EC21B587A5100552FC2 /* Clock_SignalUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EC11B587A5100552FC2 /* Clock_SignalUITests.swift */; };
4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; }; 4BBB70A4202011C2002FE009 /* MultiMediaTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */; };
4BBB70A5202011C2002FE009 /* 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 */; }; 4BBB70A8202014E2002FE009 /* MultiProducer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */; };
4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */; }; 4BBB70A9202014E2002FE009 /* MultiProducer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */; };
4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC951C1F368D83008F4C34 /* i8272.cpp */; }; 4BBC951E1F368D83008F4C34 /* i8272.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC951C1F368D83008F4C34 /* i8272.cpp */; };
4BBF49AF1ED2880200AB3669 /* FUSETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */; }; 4BBF49AF1ED2880200AB3669 /* FUSETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */; };
4BBFBB6C1EE8401E00C01E7A /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBFBB6A1EE8401E00C01E7A /* ZX8081.cpp */; }; 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 = "<group>"; }; 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80MemptrTests.swift; sourceTree = "<group>"; };
4B0333AD2094081A0050B93D /* AppleDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AppleDSK.cpp; sourceTree = "<group>"; }; 4B0333AD2094081A0050B93D /* AppleDSK.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AppleDSK.cpp; sourceTree = "<group>"; };
4B0333AE2094081A0050B93D /* AppleDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AppleDSK.hpp; sourceTree = "<group>"; }; 4B0333AE2094081A0050B93D /* AppleDSK.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AppleDSK.hpp; sourceTree = "<group>"; };
4B046DC31CFE651500E9E45E /* CRTMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTMachine.hpp; sourceTree = "<group>"; }; 4B046DC31CFE651500E9E45E /* ScanProducer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ScanProducer.hpp; sourceTree = "<group>"; };
4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; }; 4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; }; 4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; };
4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; }; 4B04B65622A58CB40006AB58 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
@ -1633,8 +1633,8 @@
4BBB709C2020109C002FE009 /* DynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DynamicMachine.hpp; sourceTree = "<group>"; }; 4BBB709C2020109C002FE009 /* DynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DynamicMachine.hpp; sourceTree = "<group>"; };
4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMediaTarget.hpp; sourceTree = "<group>"; }; 4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MultiMediaTarget.hpp; sourceTree = "<group>"; };
4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMediaTarget.cpp; sourceTree = "<group>"; }; 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MultiMediaTarget.cpp; sourceTree = "<group>"; };
4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiCRTMachine.cpp; sourceTree = "<group>"; }; 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MultiProducer.cpp; sourceTree = "<group>"; };
4BBB70A7202014E2002FE009 /* MultiCRTMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiCRTMachine.hpp; sourceTree = "<group>"; }; 4BBB70A7202014E2002FE009 /* MultiProducer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MultiProducer.hpp; sourceTree = "<group>"; };
4BBC951C1F368D83008F4C34 /* i8272.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = i8272.cpp; path = 8272/i8272.cpp; sourceTree = "<group>"; }; 4BBC951C1F368D83008F4C34 /* i8272.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = i8272.cpp; path = 8272/i8272.cpp; sourceTree = "<group>"; };
4BBC951D1F368D83008F4C34 /* i8272.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8272.hpp; path = 8272/i8272.hpp; sourceTree = "<group>"; }; 4BBC951D1F368D83008F4C34 /* i8272.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = i8272.hpp; path = 8272/i8272.hpp; sourceTree = "<group>"; };
4BBF49AE1ED2880200AB3669 /* FUSETests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUSETests.swift; sourceTree = "<group>"; }; 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUSETests.swift; sourceTree = "<group>"; };
@ -1652,6 +1652,9 @@
4BC131752346DE9100E4FF3D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; }; 4BC131752346DE9100E4FF3D /* StaticAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StaticAnalyser.cpp; sourceTree = "<group>"; };
4BC131782346DF2B00E4FF3D /* MSA.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MSA.cpp; sourceTree = "<group>"; }; 4BC131782346DF2B00E4FF3D /* MSA.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MSA.cpp; sourceTree = "<group>"; };
4BC131792346DF2B00E4FF3D /* MSA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSA.hpp; sourceTree = "<group>"; }; 4BC131792346DF2B00E4FF3D /* MSA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MSA.hpp; sourceTree = "<group>"; };
4BC57CD2243427C700FBC404 /* AudioProducer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = AudioProducer.hpp; sourceTree = "<group>"; };
4BC57CD32434282000FBC404 /* TimedMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TimedMachine.hpp; sourceTree = "<group>"; };
4BC57CD424342E0600FBC404 /* MachineTypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MachineTypes.hpp; sourceTree = "<group>"; };
4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000MoveTests.mm; sourceTree = "<group>"; }; 4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000MoveTests.mm; sourceTree = "<group>"; };
4BC5FC2F20CDDDEE00410AA0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/AppleIIOptions.xib"; sourceTree = SOURCE_ROOT; }; 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 = "<group>"; }; 4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
@ -3387,13 +3390,16 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */, 4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */,
4B046DC31CFE651500E9E45E /* CRTMachine.hpp */, 4BC57CD2243427C700FBC404 /* AudioProducer.hpp */,
4BBB709C2020109C002FE009 /* DynamicMachine.hpp */, 4BBB709C2020109C002FE009 /* DynamicMachine.hpp */,
4B7041271F92C26900735E45 /* JoystickMachine.hpp */, 4B7041271F92C26900735E45 /* JoystickMachine.hpp */,
4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */, 4B8E4ECD1DCE483D003716C3 /* KeyboardMachine.hpp */,
4BC57CD424342E0600FBC404 /* MachineTypes.hpp */,
4BA9C3CF1D8164A9002DDB61 /* MediaTarget.hpp */, 4BA9C3CF1D8164A9002DDB61 /* MediaTarget.hpp */,
4B92294222B04A3D00A1458F /* MouseMachine.hpp */, 4B92294222B04A3D00A1458F /* MouseMachine.hpp */,
4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */, 4BDCC5F81FB27A5E001220C5 /* ROMMachine.hpp */,
4B046DC31CFE651500E9E45E /* ScanProducer.hpp */,
4BC57CD32434282000FBC404 /* TimedMachine.hpp */,
4B38F3491F2EC12000D9235D /* AmstradCPC */, 4B38F3491F2EC12000D9235D /* AmstradCPC */,
4BCE0048227CE8CA000CA200 /* Apple */, 4BCE0048227CE8CA000CA200 /* Apple */,
4B0ACC0423775819008902D0 /* Atari */, 4B0ACC0423775819008902D0 /* Atari */,
@ -3428,13 +3434,13 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4B1B88BE202E3DB200B67DFF /* MultiConfigurable.cpp */, 4B1B88BE202E3DB200B67DFF /* MultiConfigurable.cpp */,
4BBB70A6202014E2002FE009 /* MultiCRTMachine.cpp */, 4BBB70A6202014E2002FE009 /* MultiProducer.cpp */,
4B1B88C6202E469300B67DFF /* MultiJoystickMachine.cpp */, 4B1B88C6202E469300B67DFF /* MultiJoystickMachine.cpp */,
4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */, 4B1B88B9202E2EC100B67DFF /* MultiKeyboardMachine.cpp */,
4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */, 4BBB70A3202011C2002FE009 /* MultiMediaTarget.cpp */,
4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */, 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */,
4B1B88BF202E3DB200B67DFF /* MultiConfigurable.hpp */, 4B1B88BF202E3DB200B67DFF /* MultiConfigurable.hpp */,
4BBB70A7202014E2002FE009 /* MultiCRTMachine.hpp */, 4BBB70A7202014E2002FE009 /* MultiProducer.hpp */,
4B1B88C7202E469300B67DFF /* MultiJoystickMachine.hpp */, 4B1B88C7202E469300B67DFF /* MultiJoystickMachine.hpp */,
4B1B88BA202E2EC100B67DFF /* MultiKeyboardMachine.hpp */, 4B1B88BA202E2EC100B67DFF /* MultiKeyboardMachine.hpp */,
4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */, 4BBB70A2202011C2002FE009 /* MultiMediaTarget.hpp */,
@ -4300,7 +4306,7 @@
4BB0A65E204500A900FB3688 /* StaticAnalyser.cpp in Sources */, 4BB0A65E204500A900FB3688 /* StaticAnalyser.cpp in Sources */,
4B055AC11FAE98DC0060FFFF /* MachineForTarget.cpp in Sources */, 4B055AC11FAE98DC0060FFFF /* MachineForTarget.cpp in Sources */,
4B65086122F4CFE0009C1100 /* Keyboard.cpp in Sources */, 4B65086122F4CFE0009C1100 /* Keyboard.cpp in Sources */,
4BBB70A9202014E2002FE009 /* MultiCRTMachine.cpp in Sources */, 4BBB70A9202014E2002FE009 /* MultiProducer.cpp in Sources */,
4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */, 4B6ED2F1208E2F8A0047B343 /* WOZ.cpp in Sources */,
4B055AD81FAE9B180060FFFF /* Video.cpp in Sources */, 4B055AD81FAE9B180060FFFF /* Video.cpp in Sources */,
4B89452F201967B4007DE474 /* StaticAnalyser.cpp in Sources */, 4B89452F201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
@ -4535,7 +4541,7 @@
4BB0A65B2044FD3000FB3688 /* SN76489.cpp in Sources */, 4BB0A65B2044FD3000FB3688 /* SN76489.cpp in Sources */,
4B894532201967B4007DE474 /* 6502.cpp in Sources */, 4B894532201967B4007DE474 /* 6502.cpp in Sources */,
4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */, 4BDB61EC203285AE0048AF91 /* Atari2600OptionsPanel.swift in Sources */,
4BBB70A8202014E2002FE009 /* MultiCRTMachine.cpp in Sources */, 4BBB70A8202014E2002FE009 /* MultiProducer.cpp in Sources */,
4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */, 4B8805F71DCFF6C9003085B1 /* Commodore.cpp in Sources */,
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */, 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */,
4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */, 4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */,

View File

@ -148,7 +148,7 @@ struct ActivityObserver: public Activity::Observer {
CSStaticAnalyser *_analyser; CSStaticAnalyser *_analyser;
std::unique_ptr<Machine::DynamicMachine> _machine; std::unique_ptr<Machine::DynamicMachine> _machine;
JoystickMachine::Machine *_joystickMachine; MachineTypes::JoystickMachine *_joystickMachine;
CSJoystickManager *_joystickManager; CSJoystickManager *_joystickManager;
std::bitset<65536> _depressedKeys; std::bitset<65536> _depressedKeys;
@ -255,7 +255,7 @@ struct ActivityObserver: public Activity::Observer {
- (float)idealSamplingRateFromRange:(NSRange)range { - (float)idealSamplingRateFromRange:(NSRange)range {
@synchronized(self) { @synchronized(self) {
Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker();
if(speaker) { if(speaker) {
return speaker->get_ideal_clock_rate_in_range((float)range.location, (float)(range.location + range.length)); 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 { - (BOOL)isStereo {
@synchronized(self) { @synchronized(self) {
Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker();
if(speaker) { if(speaker) {
return speaker->get_is_stereo(); 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 { - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Speaker::Delegate *)delegate sampleRate:(float)sampleRate bufferSize:(NSUInteger)bufferSize stereo:(BOOL)stereo {
@synchronized(self) { @synchronized(self) {
Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker();
if(speaker) { if(speaker) {
speaker->set_output_rate(sampleRate, (int)bufferSize, stereo); speaker->set_output_rate(sampleRate, (int)bufferSize, stereo);
speaker->set_delegate(delegate); speaker->set_delegate(delegate);
@ -362,7 +362,7 @@ struct ActivityObserver: public Activity::Observer {
- (void)setupOutputWithAspectRatio:(float)aspectRatio { - (void)setupOutputWithAspectRatio:(float)aspectRatio {
_scanTarget = std::make_unique<Outputs::Display::OpenGL::ScanTarget>(); _scanTarget = std::make_unique<Outputs::Display::OpenGL::ScanTarget>();
_machine->crt_machine()->set_scan_target(_scanTarget.get()); _machine->scan_producer()->set_scan_target(_scanTarget.get());
} }
- (void)updateViewForPixelSize:(CGSize)pixelSize { - (void)updateViewForPixelSize:(CGSize)pixelSize {
@ -379,7 +379,7 @@ struct ActivityObserver: public Activity::Observer {
} }
- (void)paste:(NSString *)paste { - (void)paste:(NSString *)paste {
KeyboardMachine::Machine *keyboardMachine = _machine->keyboard_machine(); auto keyboardMachine = _machine->keyboard_machine();
if(keyboardMachine) if(keyboardMachine)
keyboardMachine->type_string([paste UTF8String]); keyboardMachine->type_string([paste UTF8String]);
} }
@ -409,7 +409,7 @@ struct ActivityObserver: public Activity::Observer {
- (void)applyMedia:(const Analyser::Static::Media &)media { - (void)applyMedia:(const Analyser::Static::Media &)media {
@synchronized(self) { @synchronized(self) {
MediaTarget::Machine *const mediaTarget = _machine->media_target(); const auto mediaTarget = _machine->media_target();
if(mediaTarget) mediaTarget->insert_media(media); if(mediaTarget) mediaTarget->insert_media(media);
} }
} }
@ -712,7 +712,7 @@ struct ActivityObserver: public Activity::Observer {
- (void)setVolume:(float)volume { - (void)setVolume:(float)volume {
@synchronized(self) { @synchronized(self) {
Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker();
if(speaker) { if(speaker) {
return speaker->set_output_volume(volume); return speaker->set_output_volume(volume);
} }
@ -721,7 +721,7 @@ struct ActivityObserver: public Activity::Observer {
- (BOOL)hasAudioOutput { - (BOOL)hasAudioOutput {
@synchronized(self) { @synchronized(self) {
Outputs::Speaker::Speaker *speaker = _machine->crt_machine()->get_speaker(); Outputs::Speaker::Speaker *speaker = _machine->audio_producer()->get_speaker();
return speaker ? YES : NO; return speaker ? YES : NO;
} }
} }
@ -789,23 +789,23 @@ struct ActivityObserver: public Activity::Observer {
@synchronized(self) { @synchronized(self) {
// If this tick includes vsync then inspect the machine. // If this tick includes vsync then inspect the machine.
if(timeNow >= self->_syncTime && lastTime < self->_syncTime) { 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 // 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. // adjusting multiplier, then run after the split.
if(splitAndSync) { if(splitAndSync) {
self->_machine->crt_machine()->run_for((double)(self->_syncTime - lastTime) / 1e9); self->_machine->timed_machine()->run_for((double)(self->_syncTime - lastTime) / 1e9);
self->_machine->crt_machine()->set_speed_multiplier( self->_machine->timed_machine()->set_speed_multiplier(
self->_scanSynchroniser.next_speed_multiplier(self->_machine->crt_machine()->get_scan_status()) 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 // 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. // adjusting multiplier, then run after the split.
if(!splitAndSync) { if(!splitAndSync) {
self->_machine->crt_machine()->run_for((double)duration / 1e9); self->_machine->timed_machine()->run_for((double)duration / 1e9);
} }
pixelSize = self->_pixelSize; pixelSize = self->_pixelSize;
} }

View File

@ -27,8 +27,7 @@
#include "../../ClockReceiver/TimeTypes.hpp" #include "../../ClockReceiver/TimeTypes.hpp"
#include "../../ClockReceiver/ScanSynchroniser.hpp" #include "../../ClockReceiver/ScanSynchroniser.hpp"
#include "../../Machines/MediaTarget.hpp" #include "../../Machines/MachineTypes.hpp"
#include "../../Machines/CRTMachine.hpp"
#include "../../Activity/Observer.hpp" #include "../../Activity/Observer.hpp"
#include "../../Outputs/OpenGL/Primitives/Rectangle.hpp" #include "../../Outputs/OpenGL/Primitives/Rectangle.hpp"
@ -142,17 +141,18 @@ struct MachineRunner {
const auto vsync_time = vsync_time_.load(); const auto vsync_time = vsync_time_.load();
std::unique_lock<std::mutex> lock_guard(*machine_mutex); std::unique_lock<std::mutex> 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; bool split_and_sync = false;
if(last_time_ < vsync_time && time_now >= vsync_time) { 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) { if(split_and_sync) {
crt_machine->run_for(double(vsync_time - last_time_) / 1e9); timed_machine->run_for(double(vsync_time - last_time_) / 1e9);
crt_machine->set_speed_multiplier( timed_machine->set_speed_multiplier(
scan_synchroniser_.next_speed_multiplier(crt_machine->get_scan_status()) 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. // 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()); while(frame_lock_.test_and_set());
lock_guard.lock(); lock_guard.lock();
crt_machine->run_for(double(time_now - vsync_time) / 1e9); timed_machine->run_for(double(time_now - vsync_time) / 1e9);
} else { } else {
crt_machine->set_speed_multiplier(scan_synchroniser_.get_base_speed_multiplier()); timed_machine->set_speed_multiplier(scan_synchroniser_.get_base_speed_multiplier());
crt_machine->run_for(double(time_now - last_time_) / 1e9); timed_machine->run_for(double(time_now - last_time_) / 1e9);
} }
last_time_ = time_now; last_time_ = time_now;
} }
@ -777,7 +777,7 @@ int main(int argc, char *argv[]) {
} else if(volume < 0.0 || volume > 1.0) { } 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; std::cerr << "Cannot run with volume " << volume_string << "; volumes must be between 0.0 and 1.0." << std::endl;
} else { } else {
const auto speaker = machine->crt_machine()->get_speaker(); const auto speaker = machine->audio_producer()->get_speaker();
if(speaker) speaker->set_output_volume(volume); 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. // Setup output, assuming a CRT machine for now, and prepare a best-effort updater.
Outputs::Display::OpenGL::ScanTarget scan_target(target_framebuffer); 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. // For now, lie about audio output intentions.
auto speaker = machine->crt_machine()->get_speaker(); auto speaker = machine->audio_producer()->get_speaker();
if(speaker) { if(speaker) {
// Create an audio pipe. // Create an audio pipe.
SDL_AudioSpec desired_audio_spec; SDL_AudioSpec desired_audio_spec;
@ -909,7 +909,7 @@ int main(int argc, char *argv[]) {
std::vector<Uint8> hat_values_; std::vector<Uint8> hat_values_;
}; };
std::vector<SDLJoystick> joysticks; std::vector<SDLJoystick> joysticks;
JoystickMachine::Machine *const joystick_machine = machine->joystick_machine(); const auto joystick_machine = machine->joystick_machine();
if(joystick_machine) { if(joystick_machine) {
SDL_InitSubSystem(SDL_INIT_JOYSTICK); SDL_InitSubSystem(SDL_INIT_JOYSTICK);
for(int c = 0; c < SDL_NumJoysticks(); ++c) { for(int c = 0; c < SDL_NumJoysticks(); ++c) {
@ -1146,7 +1146,7 @@ int main(int argc, char *argv[]) {
} }
// Handle accumulated key states. // 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) { for (const auto &keypress: logical_keyboard ? matched_keypresses : keypresses) {
// Try to set this key on the keyboard first, if there is one. // Try to set this key on the keyboard first, if there is one.
if(keyboard_machine) { if(keyboard_machine) {