1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-25 18:30:07 +00:00

Splits 'CRTMachine' into three parts: ScanProducer, AudioProducer, TimedMachine.

Simultaneously cleans up some of the naming conventions and tries to make things a bit more template-compatible.
This commit is contained in:
Thomas Harte 2020-04-01 23:19:34 -04:00
parent c4b114133a
commit f417fa82a4
58 changed files with 725 additions and 651 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 {
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) {
const auto &joysticks = machine->get_joysticks();
if(joysticks.size() >= index) {
@ -67,9 +67,9 @@ class MultiJoystick: public Inputs::Joystick {
MultiJoystickMachine::MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
std::size_t total_joysticks = 0;
std::vector<JoystickMachine::Machine *> joystick_machines;
std::vector<MachineTypes::JoystickMachine *> joystick_machines;
for(const auto &machine: machines) {
JoystickMachine::Machine *joystick_machine = machine->joystick_machine();
auto joystick_machine = machine->joystick_machine();
if(joystick_machine) {
joystick_machines.push_back(joystick_machine);
total_joysticks = std::max(total_joysticks, joystick_machine->get_joysticks().size());

View File

@ -23,7 +23,7 @@ namespace Dynamic {
Makes a static internal copy of the list of machines; makes no guarantees about the
order of delivered messages.
*/
class MultiJoystickMachine: public JoystickMachine::Machine {
class MultiJoystickMachine: public MachineTypes::JoystickMachine {
public:
MultiJoystickMachine(const std::vector<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) :
keyboard_(machines_) {
for(const auto &machine: machines) {
KeyboardMachine::Machine *keyboard_machine = machine->keyboard_machine();
auto keyboard_machine = machine->keyboard_machine();
if(keyboard_machine) machines_.push_back(keyboard_machine);
}
}
@ -48,7 +48,7 @@ Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() {
return keyboard_;
}
MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines)
MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::MachineTypes::KeyboardMachine *> &machines)
: machines_(machines) {
for(const auto &machine: machines_) {
observed_keys_.insert(machine->get_keyboard().observed_keys().begin(), machine->get_keyboard().observed_keys().end());

View File

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

View File

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

View File

@ -0,0 +1,105 @@
//
// MultiCRTMachine.cpp
// Clock Signal
//
// Created by Thomas Harte on 29/01/2018.
// Copyright 2018 Thomas Harte. All rights reserved.
//
#include "MultiProducer.hpp"
#include <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

@ -10,7 +10,7 @@
#define MultiCRTMachine_hpp
#include "../../../../Concurrency/AsyncTaskQueue.hpp"
#include "../../../../Machines/CRTMachine.hpp"
#include "../../../../Machines/MachineTypes.hpp"
#include "../../../../Machines/DynamicMachine.hpp"
#include "MultiSpeaker.hpp"
@ -22,6 +22,91 @@
namespace Analyser {
namespace Dynamic {
template <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.
@ -29,58 +114,6 @@ namespace Dynamic {
acquiring a supplied mutex. The owner should also call did_change_machine_order()
if the order of machines changes.
*/
class MultiCRTMachine: public CRTMachine::Machine {
public:
MultiCRTMachine(const std::vector<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 *)> &);
};
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -290,7 +290,7 @@ class Keyboard {
/*!
Provides a mapping from idiomatic PC keys to Macintosh keys.
*/
class KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) final;
};

View File

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

View File

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

View File

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

View File

@ -365,7 +365,7 @@ uint16_t IntelligentKeyboard::KeyboardMapper::mapped_key_for_key(Inputs::Keyboar
using Key = Inputs::Keyboard::Key;
using STKey = Atari::ST::Key;
switch(key) {
default: return KeyboardMachine::MappedMachine::KeyNotMapped;
default: return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
#define Bind(x, y) case Key::x: return uint16_t(STKey::y)
#define QBind(x) case Key::x: return uint16_t(STKey::x)

View File

@ -62,7 +62,7 @@ class IntelligentKeyboard:
void run_for(HalfCycles duration);
void set_key_state(Key key, bool is_pressed);
class KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) final;
};

View File

@ -0,0 +1,29 @@
//
// AudioProducer.hpp
// Clock Signal
//
// Created by Thomas Harte on 31/03/2020.
// Copyright © 2020 Thomas Harte. All rights reserved.
//
#ifndef AudioProducer_h
#define AudioProducer_h
#include "../Outputs/Speaker/Speaker.hpp"
namespace MachineTypes {
/*!
An AudioProducer is any machine that **might** produce audio. This isn't always knowable statically.
*/
class AudioProducer {
public:
/// @returns The speaker that receives this machine's output, or @c nullptr if this machine is mute.
virtual Outputs::Speaker::Speaker *get_speaker() = 0;
};
}
#endif /* AudioProducer_h */
// virtual std::string debug_type() { return ""; }

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/SN76489/SN76489.hpp"
#include "../CRTMachine.hpp"
#include "../JoystickMachine.hpp"
#include "../MachineTypes.hpp"
#include "../../Configurable/Configurable.hpp"
#include "../../Configurable/StandardOptions.hpp"
@ -109,9 +108,11 @@ class Joystick: public Inputs::ConcreteJoystick {
class ConcreteMachine:
public Machine,
public CPU::Z80::BusHandler,
public CRTMachine::Machine,
public Configurable::Device,
public JoystickMachine::Machine {
public MachineTypes::TimedMachine,
public MachineTypes::ScanProducer,
public MachineTypes::AudioProducer,
public MachineTypes::JoystickMachine {
public:
ConcreteMachine(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :

View File

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

View File

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

View File

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

View File

@ -12,13 +12,7 @@
#include "../Configurable/Configurable.hpp"
#include "../Activity/Source.hpp"
#include "CRTMachine.hpp"
#include "JoystickMachine.hpp"
#include "KeyboardMachine.hpp"
#include "MediaTarget.hpp"
#include "MouseMachine.hpp"
#include "Utility/Typer.hpp"
#include "MachineTypes.hpp"
namespace Machine {
@ -31,11 +25,13 @@ struct DynamicMachine {
virtual Activity::Source *activity_source() = 0;
virtual Configurable::Device *configurable_device() = 0;
virtual CRTMachine::Machine *crt_machine() = 0;
virtual JoystickMachine::Machine *joystick_machine() = 0;
virtual KeyboardMachine::Machine *keyboard_machine() = 0;
virtual MouseMachine::Machine *mouse_machine() = 0;
virtual MediaTarget::Machine *media_target() = 0;
virtual MachineTypes::TimedMachine *timed_machine() = 0;
virtual MachineTypes::ScanProducer *scan_producer() = 0;
virtual MachineTypes::AudioProducer *audio_producer() = 0;
virtual MachineTypes::JoystickMachine *joystick_machine() = 0;
virtual MachineTypes::KeyboardMachine *keyboard_machine() = 0;
virtual MachineTypes::MouseMachine *mouse_machine() = 0;
virtual MachineTypes::MediaTarget *media_target() = 0;
/*!
Provides a raw pointer to the underlying machine if and only if this dynamic machine really is
@ -50,6 +46,29 @@ struct DynamicMachine {
virtual void *raw_pointer() = 0;
};
/*!
Provides a templateable means to access the above.
*/
template <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 */

View File

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

View File

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

View File

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

View File

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

View File

@ -8,31 +8,31 @@
#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);
}
bool MappedMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
bool MappedKeyboardMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
uint16_t mapped_key = get_keyboard_mapper()->mapped_key_for_key(key);
if(mapped_key == KeyNotMapped) return false;
set_key_state(mapped_key, is_pressed);
return true;
}
void MappedMachine::reset_all_keys(Inputs::Keyboard *keyboard) {
void MappedKeyboardMachine::reset_all_keys(Inputs::Keyboard *keyboard) {
// TODO: unify naming.
clear_all_keys();
}
Inputs::Keyboard &MappedMachine::get_keyboard() {
Inputs::Keyboard &MappedKeyboardMachine::get_keyboard() {
return keyboard_;
}
void Machine::type_string(const std::string &) {
void KeyboardMachine::type_string(const std::string &) {
}
MappedMachine::KeyboardMapper *MappedMachine::get_keyboard_mapper() {
MappedKeyboardMachine::KeyboardMapper *MappedKeyboardMachine::get_keyboard_mapper() {
return nullptr;
}

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 "../DynamicMachine.hpp"
#include "../ROMMachine.hpp"
#include <map>
#include <memory>

View File

@ -25,33 +25,22 @@ template<typename T> class TypedDynamicMachine: public ::Machine::DynamicMachine
return *this;
}
Activity::Source *activity_source() final {
return get<Activity::Source>();
#define Provide(type, name) \
type *name() final { \
return get<type>(); \
}
MediaTarget::Machine *media_target() final {
return get<MediaTarget::Machine>();
}
Provide(Activity::Source, activity_source)
Provide(Configurable::Device, configurable_device)
Provide(MachineTypes::TimedMachine, timed_machine)
Provide(MachineTypes::ScanProducer, scan_producer)
Provide(MachineTypes::AudioProducer, audio_producer)
Provide(MachineTypes::JoystickMachine, joystick_machine)
Provide(MachineTypes::KeyboardMachine, keyboard_machine)
Provide(MachineTypes::MouseMachine, mouse_machine)
Provide(MachineTypes::MediaTarget, media_target)
CRTMachine::Machine *crt_machine() final {
return get<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>();
}
#undef Provide
void *raw_pointer() final {
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 *const sequence = character_mapper_.sequence_for_character(c);
if(!sequence || sequence[0] == KeyboardMachine::MappedMachine::KeyNotMapped) {
if(!sequence || sequence[0] == MachineTypes::MappedKeyboardMachine::KeyNotMapped) {
return nullptr;
}
return sequence;
@ -102,7 +102,7 @@ uint16_t Typer::try_type_next_character() {
}
// If the sequence is over, stop.
if(sequence[phase_ - 2] == KeyboardMachine::MappedMachine::KeyEndSequence) {
if(sequence[phase_ - 2] == MachineTypes::MappedKeyboardMachine::KeyEndSequence) {
return 0;
}
@ -137,7 +137,7 @@ bool Typer::type_next_character() {
uint16_t *CharacterMapper::table_lookup_sequence_for_character(KeySequence *sequences, std::size_t length, char character) {
std::size_t ucharacter = static_cast<std::size_t>((unsigned char)character);
if(ucharacter >= (length / sizeof(KeySequence))) return nullptr;
if(sequences[ucharacter][0] == KeyboardMachine::MappedMachine::KeyNotMapped) return nullptr;
if(sequences[ucharacter][0] == MachineTypes::MappedKeyboardMachine::KeyNotMapped) return nullptr;
return sequences[ucharacter];
}

View File

@ -61,7 +61,7 @@ class CharacterMapper {
*/
class Typer {
public:
class Delegate: public KeyboardMachine::KeyActions {
class Delegate: public MachineTypes::KeyActions {
public:
/// Informs the delegate that this typer has reached the end of its content.
virtual void typer_reset(Typer *typer) = 0;
@ -101,7 +101,7 @@ class Typer {
Provides a default base class for type recipients: classes that want to attach a single typer at a time and
which may or may not want to nominate an initial delay and typing frequency.
*/
template <typename CMApper>
template <typename CMapper>
class TypeRecipient: public Typer::Delegate {
protected:
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) {
const auto sequence = character_mapper.sequence_for_character(c);
return sequence && sequence[0] != KeyboardMachine::MappedMachine::KeyNotMapped;
return sequence && sequence[0] != MachineTypes::MappedKeyboardMachine::KeyNotMapped;
}
/*!
@ -143,7 +143,7 @@ class TypeRecipient: public Typer::Delegate {
private:
std::unique_ptr<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);
}
#undef BIND
return KeyboardMachine::MappedMachine::KeyNotMapped;
return MachineTypes::MappedKeyboardMachine::KeyNotMapped;
}
CharacterMapper::CharacterMapper(bool is_zx81) : is_zx81_(is_zx81) {}
uint16_t *CharacterMapper::sequence_for_character(char character) {
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
#define X {KeyboardMachine::MappedMachine::KeyNotMapped}
#define KEYS(...) {__VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define SHIFT(...) {KeyShift, __VA_ARGS__, MachineTypes::MappedKeyboardMachine::KeyEndSequence}
#define X {MachineTypes::MappedKeyboardMachine::KeyNotMapped}
static KeySequence zx81_key_sequences[] = {
/* NUL */ X, /* SOH */ X,
/* STX */ X, /* ETX */ X,

View File

@ -29,7 +29,7 @@ enum Key: uint16_t {
KeyBreak, KeyLeft, KeyRight, KeyUp, KeyDown, KeyEdit
};
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
struct KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
};

View File

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

View File

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

View File

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

View File

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