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:
commit
474822e83d
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
105
Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp
Normal file
105
Analyser/Dynamic/MultiMachine/Implementation/MultiProducer.cpp
Normal 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);
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
//
|
||||
// MultiCRTMachine.hpp
|
||||
// MultiProducer.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiCRTMachine_hpp
|
||||
#define MultiCRTMachine_hpp
|
||||
#ifndef MultiProducer_hpp
|
||||
#define MultiProducer_hpp
|
||||
|
||||
#include "../../../../Concurrency/AsyncTaskQueue.hpp"
|
||||
#include "../../../../Machines/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,61 +114,9 @@ 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 *)> &);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* MultiCRTMachine_hpp */
|
||||
#endif /* MultiProducer_hpp */
|
@ -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);
|
||||
|
@ -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_)) {
|
||||
|
@ -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_;
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
27
Machines/AudioProducer.hpp
Normal file
27
Machines/AudioProducer.hpp
Normal 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 */
|
@ -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 */
|
@ -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) :
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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
25
Machines/MachineTypes.hpp
Normal 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 */
|
@ -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) :
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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
104
Machines/ScanProducer.hpp
Normal 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
89
Machines/TimedMachine.hpp
Normal 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 */
|
@ -13,6 +13,7 @@
|
||||
#include "../../Reflection/Struct.hpp"
|
||||
|
||||
#include "../DynamicMachine.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
@ -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();
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 */,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user