mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-26 09:29:45 +00:00
Merge pull request #347 from TomHarte/DynamicAnalysis
Introduces dynamic selection of MSX MegaROM type
This commit is contained in:
commit
9c0a440c38
30
Analyser/Dynamic/ConfidenceCounter.cpp
Normal file
30
Analyser/Dynamic/ConfidenceCounter.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
//
|
||||
// ConfidenceCounter.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "ConfidenceCounter.hpp"
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
float ConfidenceCounter::get_confidence() {
|
||||
return static_cast<float>(hits_) / static_cast<float>(hits_ + misses_);
|
||||
}
|
||||
|
||||
void ConfidenceCounter::add_hit() {
|
||||
hits_++;
|
||||
}
|
||||
|
||||
void ConfidenceCounter::add_miss() {
|
||||
misses_++;
|
||||
}
|
||||
|
||||
void ConfidenceCounter::add_equivocal() {
|
||||
if(hits_ > misses_) {
|
||||
hits_++;
|
||||
misses_++;
|
||||
}
|
||||
}
|
47
Analyser/Dynamic/ConfidenceCounter.hpp
Normal file
47
Analyser/Dynamic/ConfidenceCounter.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
//
|
||||
// ConfidenceCounter.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ConfidenceCounter_hpp
|
||||
#define ConfidenceCounter_hpp
|
||||
|
||||
#include "ConfidenceSource.hpp"
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a confidence source that calculates its probability by virtual of a history of events.
|
||||
|
||||
The initial value of the confidence counter is 0.5.
|
||||
*/
|
||||
class ConfidenceCounter: public ConfidenceSource {
|
||||
public:
|
||||
/*! @returns The computed probability, based on the history of events. */
|
||||
float get_confidence() override;
|
||||
|
||||
/*! Records an event that implies this is the appropriate class — pushes probability up towards 1.0. */
|
||||
void add_hit();
|
||||
|
||||
/*! Records an event that implies this is not the appropriate class — pushes probability down towards 0.0. */
|
||||
void add_miss();
|
||||
|
||||
/*!
|
||||
Records an event that could be correct but isn't necessarily so; which can push probability
|
||||
down towards 0.5, but will never push it upwards.
|
||||
*/
|
||||
void add_equivocal();
|
||||
|
||||
private:
|
||||
int hits_ = 1;
|
||||
int misses_ = 1;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ConfidenceCounter_hpp */
|
28
Analyser/Dynamic/ConfidenceSource.hpp
Normal file
28
Analyser/Dynamic/ConfidenceSource.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// ConfidenceSource.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ConfidenceSource_hpp
|
||||
#define ConfidenceSource_hpp
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides an abstract interface through which objects can declare the probability
|
||||
that they are the proper target for their input; e.g. if an Acorn Electron is asked
|
||||
to run an Atari 2600 program then its confidence should shrink towards 0.0; if the
|
||||
program is handed to an Atari 2600 then its confidence should grow towards 1.0.
|
||||
*/
|
||||
struct ConfidenceSource {
|
||||
virtual float get_confidence() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ConfidenceSource_hpp */
|
28
Analyser/Dynamic/ConfidenceSummary.cpp
Normal file
28
Analyser/Dynamic/ConfidenceSummary.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// ConfidenceSummary.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "ConfidenceSummary.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <numeric>
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
ConfidenceSummary::ConfidenceSummary(const std::vector<ConfidenceSource *> &sources, const std::vector<float> &weights) :
|
||||
sources_(sources), weights_(weights) {
|
||||
assert(weights.size() == sources.size());
|
||||
weight_sum_ = std::accumulate(weights.begin(), weights.end(), 0.0f);
|
||||
}
|
||||
|
||||
float ConfidenceSummary::get_confidence() {
|
||||
float result = 0.0f;
|
||||
for(std::size_t index = 0; index < sources_.size(); ++index) {
|
||||
result += sources_[index]->get_confidence() * weights_[index];
|
||||
}
|
||||
return result / weight_sum_;
|
||||
}
|
46
Analyser/Dynamic/ConfidenceSummary.hpp
Normal file
46
Analyser/Dynamic/ConfidenceSummary.hpp
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// ConfidenceSummary.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 21/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ConfidenceSummary_hpp
|
||||
#define ConfidenceSummary_hpp
|
||||
|
||||
#include "ConfidenceSource.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Summaries a collection of confidence sources by calculating their weighted sum.
|
||||
*/
|
||||
class ConfidenceSummary: public ConfidenceSource {
|
||||
public:
|
||||
/*!
|
||||
Instantiates a summary that will produce the weighted sum of
|
||||
@c sources, each using the corresponding entry of @c weights.
|
||||
|
||||
Requires that @c sources and @c weights are of the same length.
|
||||
*/
|
||||
ConfidenceSummary(
|
||||
const std::vector<ConfidenceSource *> &sources,
|
||||
const std::vector<float> &weights);
|
||||
|
||||
/*! @returns The weighted sum of all sources. */
|
||||
float get_confidence() override;
|
||||
|
||||
private:
|
||||
std::vector<ConfidenceSource *> sources_;
|
||||
std::vector<float> weights_;
|
||||
float weight_sum_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* ConfidenceSummary_hpp */
|
115
Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp
Normal file
115
Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
//
|
||||
// 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::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<std::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<std::mutex> machines_lock(machines_mutex_);
|
||||
for(const auto &machine: machines_) {
|
||||
CRTMachine::Machine *crt_machine = machine->crt_machine();
|
||||
if(crt_machine) function(crt_machine);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiCRTMachine::setup_output(float aspect_ratio) {
|
||||
perform_serial([=](::CRTMachine::Machine *machine) {
|
||||
machine->setup_output(aspect_ratio);
|
||||
});
|
||||
}
|
||||
|
||||
void MultiCRTMachine::close_output() {
|
||||
perform_serial([=](::CRTMachine::Machine *machine) {
|
||||
machine->close_output();
|
||||
});
|
||||
}
|
||||
|
||||
Outputs::CRT::CRT *MultiCRTMachine::get_crt() {
|
||||
std::lock_guard<std::mutex> machines_lock(machines_mutex_);
|
||||
CRTMachine::Machine *crt_machine = machines_.front()->crt_machine();
|
||||
return crt_machine ? crt_machine->get_crt() : nullptr;
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *MultiCRTMachine::get_speaker() {
|
||||
return speaker_;
|
||||
}
|
||||
|
||||
void MultiCRTMachine::run_for(const Cycles cycles) {
|
||||
perform_parallel([=](::CRTMachine::Machine *machine) {
|
||||
if(machine->get_confidence() >= 0.01f) machine->run_for(cycles);
|
||||
});
|
||||
|
||||
if(delegate_) delegate_->multi_crt_did_run_machines();
|
||||
}
|
||||
|
||||
double MultiCRTMachine::get_clock_rate() {
|
||||
// TODO: something smarter than this? Not all clock rates will necessarily be the same.
|
||||
std::lock_guard<std::mutex> machines_lock(machines_mutex_);
|
||||
CRTMachine::Machine *crt_machine = machines_.front()->crt_machine();
|
||||
return crt_machine ? crt_machine->get_clock_rate() : 0.0;
|
||||
}
|
||||
|
||||
bool MultiCRTMachine::get_clock_is_unlimited() {
|
||||
std::lock_guard<std::mutex> machines_lock(machines_mutex_);
|
||||
CRTMachine::Machine *crt_machine = machines_.front()->crt_machine();
|
||||
return crt_machine ? crt_machine->get_clock_is_unlimited() : false;
|
||||
}
|
||||
|
||||
void MultiCRTMachine::did_change_machine_order() {
|
||||
if(speaker_) {
|
||||
speaker_->set_new_front_machine(machines_.front().get());
|
||||
}
|
||||
}
|
||||
|
||||
void MultiCRTMachine::set_delegate(::CRTMachine::Machine::Delegate *delegate) {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
void MultiCRTMachine::machine_did_change_clock_rate(Machine *machine) {
|
||||
// TODO: consider passing along.
|
||||
}
|
||||
|
||||
void MultiCRTMachine::machine_did_change_clock_is_unlimited(Machine *machine) {
|
||||
// TODO: consider passing along.
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
//
|
||||
// MultiCRTMachine.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiCRTMachine_hpp
|
||||
#define MultiCRTMachine_hpp
|
||||
|
||||
#include "../../../../Concurrency/AsyncTaskQueue.hpp"
|
||||
#include "../../../../Machines/CRTMachine.hpp"
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
|
||||
#include "MultiSpeaker.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a class that multiplexes the CRT machine interface to multiple machines.
|
||||
|
||||
Keeps a reference to the original vector of machines; will access it only after
|
||||
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 CRTMachine::Machine::Delegate {
|
||||
public:
|
||||
MultiCRTMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines, std::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 setup_output(float aspect_ratio) override;
|
||||
void close_output() override;
|
||||
Outputs::CRT::CRT *get_crt() override;
|
||||
Outputs::Speaker::Speaker *get_speaker() override;
|
||||
void run_for(const Cycles cycles) override;
|
||||
double get_clock_rate() override;
|
||||
bool get_clock_is_unlimited() override;
|
||||
void set_delegate(::CRTMachine::Machine::Delegate *delegate) override;
|
||||
|
||||
private:
|
||||
// CRTMachine::Machine::Delegate
|
||||
void machine_did_change_clock_rate(Machine *machine) override;
|
||||
void machine_did_change_clock_is_unlimited(Machine *machine) override;
|
||||
|
||||
const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines_;
|
||||
std::mutex &machines_mutex_;
|
||||
std::vector<Concurrency::AsyncTaskQueue> queues_;
|
||||
MultiSpeaker *speaker_ = nullptr;
|
||||
Delegate *delegate_ = 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 */
|
@ -0,0 +1,64 @@
|
||||
//
|
||||
// MultiConfigurable.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiConfigurable.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
MultiConfigurable::MultiConfigurable(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
||||
for(const auto &machine: machines) {
|
||||
Configurable::Device *device = machine->configurable_device();
|
||||
if(device) devices_.push_back(device);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> MultiConfigurable::get_options() {
|
||||
std::vector<std::unique_ptr<Configurable::Option>> options;
|
||||
|
||||
// Produce the list of unique options.
|
||||
for(const auto &device : devices_) {
|
||||
std::vector<std::unique_ptr<Configurable::Option>> device_options = device->get_options();
|
||||
for(auto &option : device_options) {
|
||||
if(std::find(options.begin(), options.end(), option) == options.end()) {
|
||||
options.push_back(std::move(option));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
void MultiConfigurable::set_selections(const Configurable::SelectionSet &selection_by_option) {
|
||||
for(const auto &device : devices_) {
|
||||
device->set_selections(selection_by_option);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet MultiConfigurable::get_accurate_selections() {
|
||||
Configurable::SelectionSet set;
|
||||
for(const auto &device : devices_) {
|
||||
Configurable::SelectionSet device_set = device->get_accurate_selections();
|
||||
for(auto &selection : device_set) {
|
||||
set.insert(std::move(selection));
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet MultiConfigurable::get_user_friendly_selections() {
|
||||
Configurable::SelectionSet set;
|
||||
for(const auto &device : devices_) {
|
||||
Configurable::SelectionSet device_set = device->get_user_friendly_selections();
|
||||
for(auto &selection : device_set) {
|
||||
set.insert(std::move(selection));
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
//
|
||||
// MultiConfigurable.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiConfigurable_hpp
|
||||
#define MultiConfigurable_hpp
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a class that multiplexes the configurable interface to multiple machines.
|
||||
|
||||
Makes a static internal copy of the list of machines; makes no guarantees about the
|
||||
order of delivered messages.
|
||||
*/
|
||||
class MultiConfigurable: public Configurable::Device {
|
||||
public:
|
||||
MultiConfigurable(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
|
||||
// Below is the standard Configurable::Device interface; see there for documentation.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() override;
|
||||
void set_selections(const Configurable::SelectionSet &selection_by_option) override;
|
||||
Configurable::SelectionSet get_accurate_selections() override;
|
||||
Configurable::SelectionSet get_user_friendly_selections() override;
|
||||
|
||||
private:
|
||||
std::vector<Configurable::Device *> devices_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MultiConfigurable_hpp */
|
@ -0,0 +1,29 @@
|
||||
//
|
||||
// MultiConfigurationTarget.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiConfigurationTarget.hpp"
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
MultiConfigurationTarget::MultiConfigurationTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
||||
for(const auto &machine: machines) {
|
||||
ConfigurationTarget::Machine *configuration_target = machine->configuration_target();
|
||||
if(configuration_target) targets_.push_back(configuration_target);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiConfigurationTarget::configure_as_target(const Analyser::Static::Target &target) {
|
||||
}
|
||||
|
||||
bool MultiConfigurationTarget::insert_media(const Analyser::Static::Media &media) {
|
||||
bool inserted = false;
|
||||
for(const auto &target : targets_) {
|
||||
inserted |= target->insert_media(media);
|
||||
}
|
||||
return inserted;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
//
|
||||
// MultiConfigurationTarget.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiConfigurationTarget_hpp
|
||||
#define MultiConfigurationTarget_hpp
|
||||
|
||||
#include "../../../../Machines/ConfigurationTarget.hpp"
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a class that multiplexes the configuration target interface to multiple machines.
|
||||
|
||||
Makes a static internal copy of the list of machines; makes no guarantees about the
|
||||
order of delivered messages.
|
||||
*/
|
||||
struct MultiConfigurationTarget: public ConfigurationTarget::Machine {
|
||||
public:
|
||||
MultiConfigurationTarget(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
|
||||
// Below is the standard ConfigurationTarget::Machine interface; see there for documentation.
|
||||
void configure_as_target(const Analyser::Static::Target &target) override;
|
||||
bool insert_media(const Analyser::Static::Media &media) override;
|
||||
|
||||
private:
|
||||
std::vector<ConfigurationTarget::Machine *> targets_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MultiConfigurationTarget_hpp */
|
@ -0,0 +1,22 @@
|
||||
//
|
||||
// MultiJoystickMachine.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiJoystickMachine.hpp"
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
MultiJoystickMachine::MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
||||
for(const auto &machine: machines) {
|
||||
JoystickMachine::Machine *joystick_machine = machine->joystick_machine();
|
||||
if(joystick_machine) machines_.push_back(joystick_machine);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &MultiJoystickMachine::get_joysticks() {
|
||||
return joysticks_;
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
//
|
||||
// MultiJoystickMachine.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiJoystickMachine_hpp
|
||||
#define MultiJoystickMachine_hpp
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a class that multiplexes the joystick machine interface to multiple machines.
|
||||
|
||||
Makes a static internal copy of the list of machines; makes no guarantees about the
|
||||
order of delivered messages.
|
||||
*/
|
||||
class MultiJoystickMachine: public JoystickMachine::Machine {
|
||||
public:
|
||||
MultiJoystickMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
|
||||
// Below is the standard JoystickMachine::Machine interface; see there for documentation.
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() override;
|
||||
|
||||
private:
|
||||
std::vector<JoystickMachine::Machine *> machines_;
|
||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MultiJoystickMachine_hpp */
|
@ -0,0 +1,43 @@
|
||||
//
|
||||
// MultiKeyboardMachine.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiKeyboardMachine.hpp"
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
MultiKeyboardMachine::MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
||||
for(const auto &machine: machines) {
|
||||
KeyboardMachine::Machine *keyboard_machine = machine->keyboard_machine();
|
||||
if(keyboard_machine) machines_.push_back(keyboard_machine);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::clear_all_keys() {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->clear_all_keys();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::set_key_state(uint16_t key, bool is_pressed) {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->set_key_state(key, is_pressed);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::type_string(const std::string &string) {
|
||||
for(const auto &machine: machines_) {
|
||||
machine->type_string(string);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiKeyboardMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
|
||||
for(const auto &machine: machines_) {
|
||||
uint16_t mapped_key = machine->get_keyboard_mapper()->mapped_key_for_key(key);
|
||||
if(mapped_key != KeyNotMapped) machine->set_key_state(mapped_key, is_pressed);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
//
|
||||
// MultiKeyboardMachine.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 09/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiKeyboardMachine_hpp
|
||||
#define MultiKeyboardMachine_hpp
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "../../../../Machines/KeyboardMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a class that multiplexes the keyboard machine interface to multiple machines.
|
||||
|
||||
Makes a static internal copy of the list of machines; makes no guarantees about the
|
||||
order of delivered messages.
|
||||
*/
|
||||
class MultiKeyboardMachine: public KeyboardMachine::Machine {
|
||||
public:
|
||||
MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
|
||||
// Below is the standard KeyboardMachine::Machine interface; see there for documentation.
|
||||
void clear_all_keys() override;
|
||||
void set_key_state(uint16_t key, bool is_pressed) override;
|
||||
void type_string(const std::string &) override;
|
||||
void keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) override;
|
||||
|
||||
private:
|
||||
std::vector<::KeyboardMachine::Machine *> machines_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MultiKeyboardMachine_hpp */
|
@ -0,0 +1,60 @@
|
||||
//
|
||||
// MultiSpeaker.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiSpeaker.hpp"
|
||||
|
||||
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();
|
||||
if(speaker) speakers.push_back(speaker);
|
||||
}
|
||||
if(speakers.empty()) return nullptr;
|
||||
|
||||
return new MultiSpeaker(speakers);
|
||||
}
|
||||
|
||||
MultiSpeaker::MultiSpeaker(const std::vector<Outputs::Speaker::Speaker *> &speakers) :
|
||||
speakers_(speakers), front_speaker_(speakers.front()) {
|
||||
for(const auto &speaker: speakers_) {
|
||||
speaker->set_delegate(this);
|
||||
}
|
||||
}
|
||||
|
||||
float MultiSpeaker::get_ideal_clock_rate_in_range(float minimum, float maximum) {
|
||||
float ideal = 0.0f;
|
||||
for(const auto &speaker: speakers_) {
|
||||
ideal += speaker->get_ideal_clock_rate_in_range(minimum, maximum);
|
||||
}
|
||||
|
||||
return ideal / static_cast<float>(speakers_.size());
|
||||
}
|
||||
|
||||
void MultiSpeaker::set_output_rate(float cycles_per_second, int buffer_size) {
|
||||
for(const auto &speaker: speakers_) {
|
||||
speaker->set_output_rate(cycles_per_second, buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiSpeaker::set_delegate(Outputs::Speaker::Speaker::Delegate *delegate) {
|
||||
delegate_ = delegate;
|
||||
}
|
||||
|
||||
void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) {
|
||||
std::lock_guard<std::mutex> lock_guard(front_speaker_mutex_);
|
||||
if(delegate_ && speaker == front_speaker_) {
|
||||
delegate_->speaker_did_complete_samples(this, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
//
|
||||
// MultiSpeaker.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/02/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiSpeaker_hpp
|
||||
#define MultiSpeaker_hpp
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "../../../../Outputs/Speaker/Speaker.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides a class that multiplexes calls to and from Outputs::Speaker::Speaker in order
|
||||
transparently to connect a single caller to multiple destinations.
|
||||
|
||||
Makes a static internal copy of the list of machines; expects the owner to keep it
|
||||
abreast of the current frontmost machine.
|
||||
*/
|
||||
class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker::Delegate {
|
||||
public:
|
||||
/*!
|
||||
Provides a construction mechanism that may return nullptr, in the case that all included
|
||||
machines return nullptr as their speaker.
|
||||
*/
|
||||
static MultiSpeaker *create(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||
|
||||
/// This class requires the caller to nominate changes in the frontmost machine.
|
||||
void set_new_front_machine(::Machine::DynamicMachine *machine);
|
||||
|
||||
// Below is the standard Outputs::Speaker::Speaker interface; see there for documentation.
|
||||
float get_ideal_clock_rate_in_range(float minimum, float maximum);
|
||||
void set_output_rate(float cycles_per_second, int buffer_size);
|
||||
void set_delegate(Outputs::Speaker::Speaker::Delegate *delegate);
|
||||
|
||||
private:
|
||||
void speaker_did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer);
|
||||
MultiSpeaker(const std::vector<Outputs::Speaker::Speaker *> &speakers);
|
||||
|
||||
std::vector<Outputs::Speaker::Speaker *> speakers_;
|
||||
Outputs::Speaker::Speaker *front_speaker_ = nullptr;
|
||||
Outputs::Speaker::Speaker::Delegate *delegate_ = nullptr;
|
||||
std::mutex front_speaker_mutex_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MultiSpeaker_hpp */
|
104
Analyser/Dynamic/MultiMachine/MultiMachine.cpp
Normal file
104
Analyser/Dynamic/MultiMachine/MultiMachine.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// MultiMachine.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "MultiMachine.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines) :
|
||||
machines_(std::move(machines)),
|
||||
configurable_(machines_),
|
||||
configuration_target_(machines_),
|
||||
crt_machine_(machines_, machines_mutex_),
|
||||
joystick_machine_(machines),
|
||||
keyboard_machine_(machines_) {
|
||||
crt_machine_.set_delegate(this);
|
||||
}
|
||||
|
||||
ConfigurationTarget::Machine *MultiMachine::configuration_target() {
|
||||
if(has_picked_) {
|
||||
return machines_.front()->configuration_target();
|
||||
} else {
|
||||
return &configuration_target_;
|
||||
}
|
||||
}
|
||||
|
||||
CRTMachine::Machine *MultiMachine::crt_machine() {
|
||||
if(has_picked_) {
|
||||
return machines_.front()->crt_machine();
|
||||
} else {
|
||||
return &crt_machine_;
|
||||
}
|
||||
}
|
||||
|
||||
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_;
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::Device *MultiMachine::configurable_device() {
|
||||
if(has_picked_) {
|
||||
return machines_.front()->configurable_device();
|
||||
} else {
|
||||
return &configurable_;
|
||||
}
|
||||
}
|
||||
|
||||
void MultiMachine::multi_crt_did_run_machines() {
|
||||
std::lock_guard<std::mutex> machines_lock(machines_mutex_);
|
||||
for(const auto &machine: machines_) {
|
||||
CRTMachine::Machine *crt = machine->crt_machine();
|
||||
printf("%0.2f ", crt->get_confidence());
|
||||
crt->print_type();
|
||||
printf("; ");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
if(machines_.front().get() != front) {
|
||||
crt_machine_.did_change_machine_order();
|
||||
}
|
||||
|
||||
if(
|
||||
(machines_.front()->crt_machine()->get_confidence() > 0.9f) ||
|
||||
(machines_.front()->crt_machine()->get_confidence() >= 2.0f * machines_[1]->crt_machine()->get_confidence())
|
||||
) {
|
||||
pick_first();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiMachine::pick_first() {
|
||||
has_picked_ = true;
|
||||
// machines_.erase(machines_.begin() + 1, machines_.end());
|
||||
// TODO: this isn't quite correct, because it may leak OpenGL/etc resources through failure to
|
||||
// request a close_output while the context is active.
|
||||
}
|
||||
|
||||
void *MultiMachine::raw_pointer() {
|
||||
return nullptr;
|
||||
}
|
71
Analyser/Dynamic/MultiMachine/MultiMachine.hpp
Normal file
71
Analyser/Dynamic/MultiMachine/MultiMachine.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
//
|
||||
// MultiMachine.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 28/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MultiMachine_hpp
|
||||
#define MultiMachine_hpp
|
||||
|
||||
#include "../../../Machines/DynamicMachine.hpp"
|
||||
|
||||
#include "Implementation/MultiConfigurable.hpp"
|
||||
#include "Implementation/MultiConfigurationTarget.hpp"
|
||||
#include "Implementation/MultiCRTMachine.hpp"
|
||||
#include "Implementation/MultiJoystickMachine.hpp"
|
||||
#include "Implementation/MultiKeyboardMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Dynamic {
|
||||
|
||||
/*!
|
||||
Provides the same interface as to a single machine, while multiplexing all
|
||||
underlying calls to an array of real dynamic machines.
|
||||
|
||||
Calls to crt_machine->get_crt will return that for the frontmost machine;
|
||||
anything installed as the speaker's delegate will similarly receive
|
||||
feedback only from that machine.
|
||||
|
||||
Following each crt_machine->run_for, reorders the supplied machines by
|
||||
confidence.
|
||||
|
||||
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 {
|
||||
public:
|
||||
MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines);
|
||||
|
||||
ConfigurationTarget::Machine *configuration_target() override;
|
||||
CRTMachine::Machine *crt_machine() override;
|
||||
JoystickMachine::Machine *joystick_machine() override;
|
||||
KeyboardMachine::Machine *keyboard_machine() override;
|
||||
Configurable::Device *configurable_device() override;
|
||||
void *raw_pointer() override;
|
||||
|
||||
private:
|
||||
void multi_crt_did_run_machines() override;
|
||||
|
||||
std::vector<std::unique_ptr<DynamicMachine>> machines_;
|
||||
std::mutex machines_mutex_;
|
||||
|
||||
MultiConfigurable configurable_;
|
||||
MultiConfigurationTarget configuration_target_;
|
||||
MultiCRTMachine crt_machine_;
|
||||
MultiJoystickMachine joystick_machine_;
|
||||
MultiKeyboardMachine keyboard_machine_;
|
||||
|
||||
void pick_first();
|
||||
bool has_picked_ = false;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MultiMachine_hpp */
|
26
Analyser/Machines.hpp
Normal file
26
Analyser/Machines.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Machines.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 24/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Machines_h
|
||||
#define Machines_h
|
||||
|
||||
namespace Analyser {
|
||||
|
||||
enum class Machine {
|
||||
AmstradCPC,
|
||||
Atari2600,
|
||||
Electron,
|
||||
MSX,
|
||||
Oric,
|
||||
Vic20,
|
||||
ZX8081
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* Machines_h */
|
@ -7,14 +7,16 @@
|
||||
//
|
||||
|
||||
#include "Disk.hpp"
|
||||
#include "../../Storage/Disk/Controller/DiskController.hpp"
|
||||
#include "../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "../../NumberTheory/CRC.hpp"
|
||||
|
||||
#include "../../../Storage/Disk/Controller/DiskController.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "../../../NumberTheory/CRC.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace StaticAnalyser::Acorn;
|
||||
using namespace Analyser::Static::Acorn;
|
||||
|
||||
std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
// c.f. http://beebwiki.mdfs.net/Acorn_DFS_disc_format
|
||||
std::unique_ptr<Catalogue> catalogue(new Catalogue);
|
||||
Storage::Encodings::MFM::Parser parser(false, disk);
|
||||
@ -41,9 +43,7 @@ std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha
|
||||
case 3: catalogue->bootOption = Catalogue::BootOption::ExecBOOT; break;
|
||||
}
|
||||
|
||||
// DFS files are stored contiguously, and listed in descending order of distance from track 0.
|
||||
// So iterating backwards implies the least amount of seeking.
|
||||
for(std::size_t file_offset = final_file_offset - 8; file_offset > 0; file_offset -= 8) {
|
||||
for(std::size_t file_offset = 8; file_offset < final_file_offset; file_offset += 8) {
|
||||
File new_file;
|
||||
char name[10];
|
||||
snprintf(name, 10, "%c.%.7s", names->samples[0][file_offset + 7] & 0x7f, &names->samples[0][file_offset]);
|
||||
@ -69,12 +69,12 @@ std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha
|
||||
new_file.data.insert(new_file.data.end(), next_sector->samples[0].begin(), next_sector->samples[0].begin() + length_from_sector);
|
||||
data_length -= length_from_sector;
|
||||
}
|
||||
if(!data_length) catalogue->files.push_front(new_file);
|
||||
if(!data_length) catalogue->files.push_back(new_file);
|
||||
}
|
||||
|
||||
return catalogue;
|
||||
}
|
||||
std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
std::unique_ptr<Catalogue> catalogue(new Catalogue);
|
||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||
|
@ -10,15 +10,16 @@
|
||||
#define StaticAnalyser_Acorn_Disk_hpp
|
||||
|
||||
#include "File.hpp"
|
||||
#include "../../Storage/Disk/Disk.hpp"
|
||||
#include "../../../Storage/Disk/Disk.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Acorn {
|
||||
|
||||
/// Describes a DFS- or ADFS-format catalogue(/directory) — the list of files available and the catalogue's boot option.
|
||||
struct Catalogue {
|
||||
std::string name;
|
||||
std::list<File> files;
|
||||
std::vector<File> files;
|
||||
enum class BootOption {
|
||||
None,
|
||||
LoadBOOT,
|
||||
@ -30,6 +31,7 @@ struct Catalogue {
|
||||
std::unique_ptr<Catalogue> GetDFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
std::unique_ptr<Catalogue> GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,12 @@
|
||||
#ifndef StaticAnalyser_Acorn_File_hpp
|
||||
#define StaticAnalyser_Acorn_File_hpp
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Acorn {
|
||||
|
||||
struct File {
|
||||
@ -38,9 +38,10 @@ struct File {
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
std::list<Chunk> chunks;
|
||||
std::vector<Chunk> chunks;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,11 @@
|
||||
#include "Disk.hpp"
|
||||
#include "Tape.hpp"
|
||||
|
||||
using namespace StaticAnalyser::Acorn;
|
||||
using namespace Analyser::Static::Acorn;
|
||||
|
||||
static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
AcornCartridgesFrom(const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> acorn_cartridges;
|
||||
static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
AcornCartridgesFrom(const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> acorn_cartridges;
|
||||
|
||||
for(const auto &cartridge : cartridges) {
|
||||
const auto &segments = cartridge->get_segments();
|
||||
@ -56,21 +56,21 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
return acorn_cartridges;
|
||||
}
|
||||
|
||||
void StaticAnalyser::Acorn::AddTargets(const Media &media, std::list<Target> &destination) {
|
||||
Target target;
|
||||
target.machine = Target::Electron;
|
||||
target.probability = 1.0; // TODO: a proper estimation
|
||||
target.acorn.has_dfs = false;
|
||||
target.acorn.has_adfs = false;
|
||||
target.acorn.should_shift_restart = false;
|
||||
void Analyser::Static::Acorn::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::Electron;
|
||||
target->confidence = 1.0; // TODO: a proper estimation
|
||||
target->acorn.has_dfs = false;
|
||||
target->acorn.has_adfs = false;
|
||||
target->acorn.should_shift_restart = false;
|
||||
|
||||
// strip out inappropriate cartridges
|
||||
target.media.cartridges = AcornCartridgesFrom(media.cartridges);
|
||||
target->media.cartridges = AcornCartridgesFrom(media.cartridges);
|
||||
|
||||
// if there are any tapes, attempt to get data from the first
|
||||
if(media.tapes.size() > 0) {
|
||||
std::shared_ptr<Storage::Tape::Tape> tape = media.tapes.front();
|
||||
std::list<File> files = GetFiles(tape);
|
||||
std::vector<File> files = GetFiles(tape);
|
||||
tape->reset();
|
||||
|
||||
// continue if there are any files
|
||||
@ -96,9 +96,9 @@ void StaticAnalyser::Acorn::AddTargets(const Media &media, std::list<Target> &de
|
||||
|
||||
// Inspect first file. If it's protected or doesn't look like BASIC
|
||||
// then the loading command is *RUN. Otherwise it's CHAIN"".
|
||||
target.loading_command = is_basic ? "CHAIN\"\"\n" : "*RUN\n";
|
||||
target->loading_command = is_basic ? "CHAIN\"\"\n" : "*RUN\n";
|
||||
|
||||
target.media.tapes = media.tapes;
|
||||
target->media.tapes = media.tapes;
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,18 +108,18 @@ void StaticAnalyser::Acorn::AddTargets(const Media &media, std::list<Target> &de
|
||||
dfs_catalogue = GetDFSCatalogue(disk);
|
||||
if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk);
|
||||
if(dfs_catalogue || adfs_catalogue) {
|
||||
target.media.disks = media.disks;
|
||||
target.acorn.has_dfs = !!dfs_catalogue;
|
||||
target.acorn.has_adfs = !!adfs_catalogue;
|
||||
target->media.disks = media.disks;
|
||||
target->acorn.has_dfs = !!dfs_catalogue;
|
||||
target->acorn.has_adfs = !!adfs_catalogue;
|
||||
|
||||
Catalogue::BootOption bootOption = (dfs_catalogue ?: adfs_catalogue)->bootOption;
|
||||
if(bootOption != Catalogue::BootOption::None)
|
||||
target.acorn.should_shift_restart = true;
|
||||
target->acorn.should_shift_restart = true;
|
||||
else
|
||||
target.loading_command = "*CAT\n";
|
||||
target->loading_command = "*CAT\n";
|
||||
}
|
||||
}
|
||||
|
||||
if(target.media.tapes.size() || target.media.disks.size() || target.media.cartridges.size())
|
||||
destination.push_back(target);
|
||||
if(target->media.tapes.size() || target->media.disks.size() || target->media.cartridges.size())
|
||||
destination.push_back(std::move(target));
|
||||
}
|
@ -11,11 +11,13 @@
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Acorn {
|
||||
|
||||
void AddTargets(const Media &media, std::list<Target> &destination);
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,11 @@
|
||||
#include "Tape.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include "../../NumberTheory/CRC.hpp"
|
||||
#include "../../Storage/Tape/Parsers/Acorn.hpp"
|
||||
|
||||
using namespace StaticAnalyser::Acorn;
|
||||
#include "../../../NumberTheory/CRC.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/Acorn.hpp"
|
||||
|
||||
using namespace Analyser::Static::Acorn;
|
||||
|
||||
static std::unique_ptr<File::Chunk> GetNextChunk(const std::shared_ptr<Storage::Tape::Tape> &tape, Storage::Tape::Acorn::Parser &parser) {
|
||||
std::unique_ptr<File::Chunk> new_chunk(new File::Chunk);
|
||||
@ -118,7 +119,7 @@ static std::unique_ptr<File> GetNextFile(std::deque<File::Chunk> &chunks) {
|
||||
return file;
|
||||
}
|
||||
|
||||
std::list<File> StaticAnalyser::Acorn::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<File> Analyser::Static::Acorn::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
Storage::Tape::Acorn::Parser parser;
|
||||
|
||||
// populate chunk list
|
||||
@ -131,7 +132,7 @@ std::list<File> StaticAnalyser::Acorn::GetFiles(const std::shared_ptr<Storage::T
|
||||
}
|
||||
|
||||
// decompose into file list
|
||||
std::list<File> file_list;
|
||||
std::vector<File> file_list;
|
||||
|
||||
while(chunk_list.size()) {
|
||||
std::unique_ptr<File> next_file = GetNextFile(chunk_list);
|
@ -12,13 +12,15 @@
|
||||
#include <memory>
|
||||
|
||||
#include "File.hpp"
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Acorn {
|
||||
|
||||
std::list<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,8 @@
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "../../Storage/Disk/Parsers/CPM.hpp"
|
||||
#include "../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
#include "../../../Storage/Disk/Parsers/CPM.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/MFM/Parser.hpp"
|
||||
|
||||
static bool strcmp_insensitive(const char *a, const char *b) {
|
||||
if(std::strlen(a) != std::strlen(b)) return false;
|
||||
@ -58,7 +58,7 @@ static std::string RunCommandFor(const Storage::Disk::CPM::File &file) {
|
||||
|
||||
static void InspectCatalogue(
|
||||
const Storage::Disk::CPM::Catalogue &catalogue,
|
||||
StaticAnalyser::Target &target) {
|
||||
const std::unique_ptr<Analyser::Static::Target> &target) {
|
||||
|
||||
std::vector<const Storage::Disk::CPM::File *> candidate_files;
|
||||
candidate_files.reserve(catalogue.files.size());
|
||||
@ -95,7 +95,7 @@ static void InspectCatalogue(
|
||||
|
||||
// If there's just one file, run that.
|
||||
if(candidate_files.size() == 1) {
|
||||
target.loading_command = RunCommandFor(*candidate_files[0]);
|
||||
target->loading_command = RunCommandFor(*candidate_files[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ static void InspectCatalogue(
|
||||
}
|
||||
if(basic_files == 1 || implicit_suffixed_files == 1) {
|
||||
std::size_t selected_file = (basic_files == 1) ? last_basic_file : last_implicit_suffixed_file;
|
||||
target.loading_command = RunCommandFor(*candidate_files[selected_file]);
|
||||
target->loading_command = RunCommandFor(*candidate_files[selected_file]);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -143,17 +143,17 @@ static void InspectCatalogue(
|
||||
if(name_counts.size() == 2) {
|
||||
for(auto &pair : name_counts) {
|
||||
if(pair.second == 1) {
|
||||
target.loading_command = RunCommandFor(*candidate_files[indices_by_name[pair.first]]);
|
||||
target->loading_command = RunCommandFor(*candidate_files[indices_by_name[pair.first]]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Desperation.
|
||||
target.loading_command = "cat\n";
|
||||
target->loading_command = "cat\n";
|
||||
}
|
||||
|
||||
static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, StaticAnalyser::Target &target) {
|
||||
static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, const std::unique_ptr<Analyser::Static::Target> &target) {
|
||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||
Storage::Encodings::MFM::Sector *boot_sector = parser.get_sector(0, 0, 0x41);
|
||||
if(boot_sector != nullptr && !boot_sector->samples.empty()) {
|
||||
@ -169,7 +169,7 @@ static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, St
|
||||
|
||||
// This is a system disk, then launch it as though it were CP/M.
|
||||
if(!matched) {
|
||||
target.loading_command = "|cpm\n";
|
||||
target->loading_command = "|cpm\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -177,24 +177,24 @@ static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, St
|
||||
return false;
|
||||
}
|
||||
|
||||
void StaticAnalyser::AmstradCPC::AddTargets(const Media &media, std::list<Target> &destination) {
|
||||
Target target;
|
||||
target.machine = Target::AmstradCPC;
|
||||
target.probability = 1.0;
|
||||
target.media.disks = media.disks;
|
||||
target.media.tapes = media.tapes;
|
||||
target.media.cartridges = media.cartridges;
|
||||
void Analyser::Static::AmstradCPC::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::AmstradCPC;
|
||||
target->confidence = 1.0;
|
||||
target->media.disks = media.disks;
|
||||
target->media.tapes = media.tapes;
|
||||
target->media.cartridges = media.cartridges;
|
||||
|
||||
target.amstradcpc.model = AmstradCPCModel::CPC6128;
|
||||
target->amstradcpc.model = AmstradCPCModel::CPC6128;
|
||||
|
||||
if(!target.media.tapes.empty()) {
|
||||
if(!target->media.tapes.empty()) {
|
||||
// Ugliness flows here: assume the CPC isn't smart enough to pause between pressing
|
||||
// enter and responding to the follow-on prompt to press a key, so just type for
|
||||
// a while. Yuck!
|
||||
target.loading_command = "|tape\nrun\"\n1234567890";
|
||||
target->loading_command = "|tape\nrun\"\n1234567890";
|
||||
}
|
||||
|
||||
if(!target.media.disks.empty()) {
|
||||
if(!target->media.disks.empty()) {
|
||||
Storage::Disk::CPM::ParameterBlock data_format;
|
||||
data_format.sectors_per_track = 9;
|
||||
data_format.tracks = 40;
|
||||
@ -203,11 +203,11 @@ void StaticAnalyser::AmstradCPC::AddTargets(const Media &media, std::list<Target
|
||||
data_format.catalogue_allocation_bitmap = 0xc000;
|
||||
data_format.reserved_tracks = 0;
|
||||
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(target.media.disks.front(), data_format);
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> data_catalogue = Storage::Disk::CPM::GetCatalogue(target->media.disks.front(), data_format);
|
||||
if(data_catalogue) {
|
||||
InspectCatalogue(*data_catalogue, target);
|
||||
} else {
|
||||
if(!CheckBootSector(target.media.disks.front(), target)) {
|
||||
if(!CheckBootSector(target->media.disks.front(), target)) {
|
||||
Storage::Disk::CPM::ParameterBlock system_format;
|
||||
system_format.sectors_per_track = 9;
|
||||
system_format.tracks = 40;
|
||||
@ -216,7 +216,7 @@ void StaticAnalyser::AmstradCPC::AddTargets(const Media &media, std::list<Target
|
||||
system_format.catalogue_allocation_bitmap = 0xc000;
|
||||
system_format.reserved_tracks = 2;
|
||||
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(target.media.disks.front(), system_format);
|
||||
std::unique_ptr<Storage::Disk::CPM::Catalogue> system_catalogue = Storage::Disk::CPM::GetCatalogue(target->media.disks.front(), system_format);
|
||||
if(system_catalogue) {
|
||||
InspectCatalogue(*system_catalogue, target);
|
||||
}
|
||||
@ -224,5 +224,5 @@ void StaticAnalyser::AmstradCPC::AddTargets(const Media &media, std::list<Target
|
||||
}
|
||||
}
|
||||
|
||||
destination.push_back(target);
|
||||
destination.push_back(std::move(target));
|
||||
}
|
@ -11,11 +11,13 @@
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace AmstradCPC {
|
||||
|
||||
void AddTargets(const Media &media, std::list<Target> &destination);
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@
|
||||
|
||||
#include "../Disassembler/6502.hpp"
|
||||
|
||||
using namespace StaticAnalyser::Atari;
|
||||
using namespace Analyser::Static::Atari;
|
||||
|
||||
static void DeterminePagingFor2kCartridge(StaticAnalyser::Target &target, const Storage::Cartridge::Cartridge::Segment &segment) {
|
||||
static void DeterminePagingFor2kCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment) {
|
||||
// if this is a 2kb cartridge then it's definitely either unpaged or a CommaVid
|
||||
uint16_t entry_address, break_address;
|
||||
|
||||
@ -26,17 +26,17 @@ static void DeterminePagingFor2kCartridge(StaticAnalyser::Target &target, const
|
||||
address &= 0x1fff;
|
||||
return static_cast<std::size_t>(address - 0x1800);
|
||||
};
|
||||
StaticAnalyser::MOS6502::Disassembly high_location_disassembly =
|
||||
StaticAnalyser::MOS6502::Disassemble(segment.data, high_location_mapper, {entry_address, break_address});
|
||||
Analyser::Static::MOS6502::Disassembly high_location_disassembly =
|
||||
Analyser::Static::MOS6502::Disassemble(segment.data, high_location_mapper, {entry_address, break_address});
|
||||
|
||||
// assume that any kind of store that looks likely to be intended for large amounts of memory implies
|
||||
// large amounts of memory
|
||||
bool has_wide_area_store = false;
|
||||
for(std::map<uint16_t, StaticAnalyser::MOS6502::Instruction>::value_type &entry : high_location_disassembly.instructions_by_address) {
|
||||
if(entry.second.operation == StaticAnalyser::MOS6502::Instruction::STA) {
|
||||
has_wide_area_store |= entry.second.addressing_mode == StaticAnalyser::MOS6502::Instruction::Indirect;
|
||||
has_wide_area_store |= entry.second.addressing_mode == StaticAnalyser::MOS6502::Instruction::IndexedIndirectX;
|
||||
has_wide_area_store |= entry.second.addressing_mode == StaticAnalyser::MOS6502::Instruction::IndirectIndexedY;
|
||||
for(std::map<uint16_t, Analyser::Static::MOS6502::Instruction>::value_type &entry : high_location_disassembly.instructions_by_address) {
|
||||
if(entry.second.operation == Analyser::Static::MOS6502::Instruction::STA) {
|
||||
has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::Indirect;
|
||||
has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::IndexedIndirectX;
|
||||
has_wide_area_store |= entry.second.addressing_mode == Analyser::Static::MOS6502::Instruction::IndirectIndexedY;
|
||||
|
||||
if(has_wide_area_store) break;
|
||||
}
|
||||
@ -46,10 +46,10 @@ static void DeterminePagingFor2kCartridge(StaticAnalyser::Target &target, const
|
||||
// caveat: false positives aren't likely to be problematic; a false positive is a 2KB ROM that always addresses
|
||||
// itself so as to land in ROM even if mapped as a CommaVid and this code is on the fence as to whether it
|
||||
// attempts to modify itself but it probably doesn't
|
||||
if(has_wide_area_store) target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::CommaVid;
|
||||
if(has_wide_area_store) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::CommaVid;
|
||||
}
|
||||
|
||||
static void DeterminePagingFor8kCartridge(StaticAnalyser::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const StaticAnalyser::MOS6502::Disassembly &disassembly) {
|
||||
static void DeterminePagingFor8kCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
// Activision stack titles have their vectors at the top of the low 4k, not the top, and
|
||||
// always list 0xf000 as both vectors; they do not repeat them, and, inexplicably, they all
|
||||
// issue an SEI as their first instruction (maybe some sort of relic of the development environment?)
|
||||
@ -58,12 +58,12 @@ static void DeterminePagingFor8kCartridge(StaticAnalyser::Target &target, const
|
||||
(segment.data[8191] != 0xf0 || segment.data[8189] != 0xf0 || segment.data[8190] != 0x00 || segment.data[8188] != 0x00) &&
|
||||
segment.data[0] == 0x78
|
||||
) {
|
||||
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::ActivisionStack;
|
||||
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::ActivisionStack;
|
||||
return;
|
||||
}
|
||||
|
||||
// make an assumption that this is the Atari paging model
|
||||
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::Atari8k;
|
||||
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Atari8k;
|
||||
|
||||
std::set<uint16_t> internal_accesses;
|
||||
internal_accesses.insert(disassembly.internal_stores.begin(), disassembly.internal_stores.end());
|
||||
@ -83,13 +83,13 @@ static void DeterminePagingFor8kCartridge(StaticAnalyser::Target &target, const
|
||||
tigervision_access_count += masked_address == 0x3f;
|
||||
}
|
||||
|
||||
if(parker_access_count > atari_access_count) target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::ParkerBros;
|
||||
else if(tigervision_access_count > atari_access_count) target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::Tigervision;
|
||||
if(parker_access_count > atari_access_count) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::ParkerBros;
|
||||
else if(tigervision_access_count > atari_access_count) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Tigervision;
|
||||
}
|
||||
|
||||
static void DeterminePagingFor16kCartridge(StaticAnalyser::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const StaticAnalyser::MOS6502::Disassembly &disassembly) {
|
||||
static void DeterminePagingFor16kCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
// make an assumption that this is the Atari paging model
|
||||
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::Atari16k;
|
||||
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Atari16k;
|
||||
|
||||
std::set<uint16_t> internal_accesses;
|
||||
internal_accesses.insert(disassembly.internal_stores.begin(), disassembly.internal_stores.end());
|
||||
@ -104,17 +104,17 @@ static void DeterminePagingFor16kCartridge(StaticAnalyser::Target &target, const
|
||||
mnetwork_access_count += masked_address >= 0x1fe0 && masked_address < 0x1ffb;
|
||||
}
|
||||
|
||||
if(mnetwork_access_count > atari_access_count) target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::MNetwork;
|
||||
if(mnetwork_access_count > atari_access_count) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::MNetwork;
|
||||
}
|
||||
|
||||
static void DeterminePagingFor64kCartridge(StaticAnalyser::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const StaticAnalyser::MOS6502::Disassembly &disassembly) {
|
||||
static void DeterminePagingFor64kCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment, const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
// make an assumption that this is a Tigervision if there is a write to 3F
|
||||
target.atari.paging_model =
|
||||
(disassembly.external_stores.find(0x3f) != disassembly.external_stores.end()) ?
|
||||
StaticAnalyser::Atari2600PagingModel::Tigervision : StaticAnalyser::Atari2600PagingModel::MegaBoy;
|
||||
Analyser::Static::Atari2600PagingModel::Tigervision : Analyser::Static::Atari2600PagingModel::MegaBoy;
|
||||
}
|
||||
|
||||
static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const Storage::Cartridge::Cartridge::Segment &segment) {
|
||||
static void DeterminePagingForCartridge(Analyser::Static::Target &target, const Storage::Cartridge::Cartridge::Segment &segment) {
|
||||
if(segment.data.size() == 2048) {
|
||||
DeterminePagingFor2kCartridge(target, segment);
|
||||
return;
|
||||
@ -131,23 +131,23 @@ static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const St
|
||||
};
|
||||
|
||||
std::vector<uint8_t> final_4k(segment.data.end() - 4096, segment.data.end());
|
||||
StaticAnalyser::MOS6502::Disassembly disassembly = StaticAnalyser::MOS6502::Disassemble(final_4k, address_mapper, {entry_address, break_address});
|
||||
Analyser::Static::MOS6502::Disassembly disassembly = Analyser::Static::MOS6502::Disassemble(final_4k, address_mapper, {entry_address, break_address});
|
||||
|
||||
switch(segment.data.size()) {
|
||||
case 8192:
|
||||
DeterminePagingFor8kCartridge(target, segment, disassembly);
|
||||
break;
|
||||
case 10495:
|
||||
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::Pitfall2;
|
||||
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Pitfall2;
|
||||
break;
|
||||
case 12288:
|
||||
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::CBSRamPlus;
|
||||
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::CBSRamPlus;
|
||||
break;
|
||||
case 16384:
|
||||
DeterminePagingFor16kCartridge(target, segment, disassembly);
|
||||
break;
|
||||
case 32768:
|
||||
target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::Atari32k;
|
||||
target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Atari32k;
|
||||
break;
|
||||
case 65536:
|
||||
DeterminePagingFor64kCartridge(target, segment, disassembly);
|
||||
@ -159,8 +159,8 @@ static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const St
|
||||
// check for a Super Chip. Atari ROM images [almost] always have the same value stored over RAM
|
||||
// regions; when they don't they at least seem to have the first 128 bytes be the same as the
|
||||
// next 128 bytes. So check for that.
|
||||
if( target.atari.paging_model != StaticAnalyser::Atari2600PagingModel::CBSRamPlus &&
|
||||
target.atari.paging_model != StaticAnalyser::Atari2600PagingModel::MNetwork) {
|
||||
if( target.atari.paging_model != Analyser::Static::Atari2600PagingModel::CBSRamPlus &&
|
||||
target.atari.paging_model != Analyser::Static::Atari2600PagingModel::MNetwork) {
|
||||
bool has_superchip = true;
|
||||
for(std::size_t address = 0; address < 128; address++) {
|
||||
if(segment.data[address] != segment.data[address+128]) {
|
||||
@ -172,20 +172,20 @@ static void DeterminePagingForCartridge(StaticAnalyser::Target &target, const St
|
||||
}
|
||||
|
||||
// check for a Tigervision or Tigervision-esque scheme
|
||||
if(target.atari.paging_model == StaticAnalyser::Atari2600PagingModel::None && segment.data.size() > 4096) {
|
||||
if(target.atari.paging_model == Analyser::Static::Atari2600PagingModel::None && segment.data.size() > 4096) {
|
||||
bool looks_like_tigervision = disassembly.external_stores.find(0x3f) != disassembly.external_stores.end();
|
||||
if(looks_like_tigervision) target.atari.paging_model = StaticAnalyser::Atari2600PagingModel::Tigervision;
|
||||
if(looks_like_tigervision) target.atari.paging_model = Analyser::Static::Atari2600PagingModel::Tigervision;
|
||||
}
|
||||
}
|
||||
|
||||
void StaticAnalyser::Atari::AddTargets(const Media &media, std::list<Target> &destination) {
|
||||
void Analyser::Static::Atari::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
|
||||
// TODO: sanity checking; is this image really for an Atari 2600.
|
||||
Target target;
|
||||
target.machine = Target::Atari2600;
|
||||
target.probability = 1.0;
|
||||
target.media.cartridges = media.cartridges;
|
||||
target.atari.paging_model = Atari2600PagingModel::None;
|
||||
target.atari.uses_superchip = false;
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::Atari2600;
|
||||
target->confidence = 1.0;
|
||||
target->media.cartridges = media.cartridges;
|
||||
target->atari.paging_model = Atari2600PagingModel::None;
|
||||
target->atari.uses_superchip = false;
|
||||
|
||||
// try to figure out the paging scheme
|
||||
if(!media.cartridges.empty()) {
|
||||
@ -193,9 +193,9 @@ void StaticAnalyser::Atari::AddTargets(const Media &media, std::list<Target> &de
|
||||
|
||||
if(segments.size() == 1) {
|
||||
const Storage::Cartridge::Cartridge::Segment &segment = segments.front();
|
||||
DeterminePagingForCartridge(target, segment);
|
||||
DeterminePagingForCartridge(*target, segment);
|
||||
}
|
||||
}
|
||||
|
||||
destination.push_back(target);
|
||||
destination.push_back(std::move(target));
|
||||
}
|
@ -11,11 +11,13 @@
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Atari {
|
||||
|
||||
void AddTargets(const Media &media, std::list<Target> &destination);
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,15 +7,15 @@
|
||||
//
|
||||
|
||||
#include "Disk.hpp"
|
||||
#include "../../Storage/Disk/Controller/DiskController.hpp"
|
||||
#include "../../Storage/Disk/Encodings/CommodoreGCR.hpp"
|
||||
#include "../../Storage/Data/Commodore.hpp"
|
||||
#include "../../../Storage/Disk/Controller/DiskController.hpp"
|
||||
#include "../../../Storage/Disk/Encodings/CommodoreGCR.hpp"
|
||||
#include "../../../Storage/Data/Commodore.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
using namespace StaticAnalyser::Commodore;
|
||||
using namespace Analyser::Static::Commodore;
|
||||
|
||||
class CommodoreGCRParser: public Storage::Disk::Controller {
|
||||
public:
|
||||
@ -165,8 +165,8 @@ class CommodoreGCRParser: public Storage::Disk::Controller {
|
||||
}
|
||||
};
|
||||
|
||||
std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
std::list<File> files;
|
||||
std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
std::vector<File> files;
|
||||
CommodoreGCRParser parser;
|
||||
parser.drive->set_disk(disk);
|
||||
|
@ -9,17 +9,19 @@
|
||||
#ifndef StaticAnalyser_Commodore_Disk_hpp
|
||||
#define StaticAnalyser_Commodore_Disk_hpp
|
||||
|
||||
#include "../../Storage/Disk/Disk.hpp"
|
||||
#include "../../../Storage/Disk/Disk.hpp"
|
||||
#include "File.hpp"
|
||||
#include <list>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
#include <vector>
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Commodore {
|
||||
|
||||
std::list<File> GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* Disk_hpp */
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "File.hpp"
|
||||
|
||||
bool StaticAnalyser::Commodore::File::is_basic() {
|
||||
bool Analyser::Static::Commodore::File::is_basic() {
|
||||
// BASIC files are always relocatable (?)
|
||||
if(type != File::RelocatableProgram) return false;
|
||||
|
@ -12,7 +12,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Commodore {
|
||||
|
||||
struct File {
|
||||
@ -34,6 +35,7 @@ struct File {
|
||||
bool is_basic();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,18 +8,18 @@
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "Disk.hpp"
|
||||
#include "File.hpp"
|
||||
#include "Tape.hpp"
|
||||
#include "Disk.hpp"
|
||||
#include "../../Storage/Cartridge/Encodings/CommodoreROM.hpp"
|
||||
#include "../../../Storage/Cartridge/Encodings/CommodoreROM.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace StaticAnalyser::Commodore;
|
||||
using namespace Analyser::Static::Commodore;
|
||||
|
||||
static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
Vic20CartridgesFrom(const std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> vic20_cartridges;
|
||||
static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
Vic20CartridgesFrom(const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> vic20_cartridges;
|
||||
|
||||
for(const auto &cartridge : cartridges) {
|
||||
const auto &segments = cartridge->get_segments();
|
||||
@ -38,42 +38,42 @@ static std::list<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
return vic20_cartridges;
|
||||
}
|
||||
|
||||
void StaticAnalyser::Commodore::AddTargets(const Media &media, std::list<Target> &destination) {
|
||||
Target target;
|
||||
target.machine = Target::Vic20; // TODO: machine estimation
|
||||
target.probability = 1.0; // TODO: a proper estimation
|
||||
void Analyser::Static::Commodore::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::Vic20; // TODO: machine estimation
|
||||
target->confidence = 1.0; // TODO: a proper estimation
|
||||
|
||||
int device = 0;
|
||||
std::list<File> files;
|
||||
std::vector<File> files;
|
||||
bool is_disk = false;
|
||||
|
||||
// strip out inappropriate cartridges
|
||||
target.media.cartridges = Vic20CartridgesFrom(media.cartridges);
|
||||
target->media.cartridges = Vic20CartridgesFrom(media.cartridges);
|
||||
|
||||
// check disks
|
||||
for(auto &disk : media.disks) {
|
||||
std::list<File> disk_files = GetFiles(disk);
|
||||
std::vector<File> disk_files = GetFiles(disk);
|
||||
if(!disk_files.empty()) {
|
||||
is_disk = true;
|
||||
files.splice(files.end(), disk_files);
|
||||
target.media.disks.push_back(disk);
|
||||
files.insert(files.end(), disk_files.begin(), disk_files.end());
|
||||
target->media.disks.push_back(disk);
|
||||
if(!device) device = 8;
|
||||
}
|
||||
}
|
||||
|
||||
// check tapes
|
||||
for(auto &tape : media.tapes) {
|
||||
std::list<File> tape_files = GetFiles(tape);
|
||||
std::vector<File> tape_files = GetFiles(tape);
|
||||
tape->reset();
|
||||
if(!tape_files.empty()) {
|
||||
files.splice(files.end(), tape_files);
|
||||
target.media.tapes.push_back(tape);
|
||||
files.insert(files.end(), tape_files.begin(), tape_files.end());
|
||||
target->media.tapes.push_back(tape);
|
||||
if(!device) device = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(!files.empty()) {
|
||||
target.vic20.memory_model = Vic20MemoryModel::Unexpanded;
|
||||
target->vic20.memory_model = Vic20MemoryModel::Unexpanded;
|
||||
std::ostringstream string_stream;
|
||||
string_stream << "LOAD\"" << (is_disk ? "*" : "") << "\"," << device << ",";
|
||||
if(files.front().is_basic()) {
|
||||
@ -82,17 +82,17 @@ void StaticAnalyser::Commodore::AddTargets(const Media &media, std::list<Target>
|
||||
string_stream << "1";
|
||||
}
|
||||
string_stream << "\nRUN\n";
|
||||
target.loading_command = string_stream.str();
|
||||
target->loading_command = string_stream.str();
|
||||
|
||||
// make a first guess based on loading address
|
||||
switch(files.front().starting_address) {
|
||||
case 0x1001:
|
||||
default: break;
|
||||
case 0x1201:
|
||||
target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
target->vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
break;
|
||||
case 0x0401:
|
||||
target.vic20.memory_model = Vic20MemoryModel::EightKB;
|
||||
target->vic20.memory_model = Vic20MemoryModel::EightKB;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -108,9 +108,9 @@ void StaticAnalyser::Commodore::AddTargets(const Media &media, std::list<Target>
|
||||
// An unexpanded machine has 3583 bytes free for BASIC;
|
||||
// a 3kb expanded machine has 6655 bytes free.
|
||||
if(file_size > 6655)
|
||||
target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && file_size > 3583)
|
||||
target.vic20.memory_model = Vic20MemoryModel::EightKB;
|
||||
target->vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
else if(target->vic20.memory_model == Vic20MemoryModel::Unexpanded && file_size > 3583)
|
||||
target->vic20.memory_model = Vic20MemoryModel::EightKB;
|
||||
}
|
||||
else
|
||||
{*/
|
||||
@ -129,13 +129,13 @@ void StaticAnalyser::Commodore::AddTargets(const Media &media, std::list<Target>
|
||||
// If anything above the 8kb mark is touched, mark as a 32kb machine; otherwise if the
|
||||
// region 0x0400 to 0x1000 is touched and this is an unexpanded machine, mark as 3kb.
|
||||
if(starting_address + file_size > 0x2000)
|
||||
target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
else if(target.vic20.memory_model == Vic20MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400))
|
||||
target.vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
target->vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
else if(target->vic20.memory_model == Vic20MemoryModel::Unexpanded && !(starting_address >= 0x1000 || starting_address+file_size < 0x0400))
|
||||
target->vic20.memory_model = Vic20MemoryModel::ThirtyTwoKB;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
if(!target.media.tapes.empty() || !target.media.cartridges.empty() || !target.media.disks.empty())
|
||||
destination.push_back(target);
|
||||
if(!target->media.empty())
|
||||
destination.push_back(std::move(target));
|
||||
}
|
@ -11,11 +11,13 @@
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Commodore {
|
||||
|
||||
void AddTargets(const Media &media, std::list<Target> &destination);
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
#include "../../Storage/Tape/Parsers/Commodore.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/Commodore.hpp"
|
||||
|
||||
using namespace StaticAnalyser::Commodore;
|
||||
using namespace Analyser::Static::Commodore;
|
||||
|
||||
std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<File> Analyser::Static::Commodore::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
Storage::Tape::Commodore::Parser parser;
|
||||
std::list<File> file_list;
|
||||
std::vector<File> file_list;
|
||||
|
||||
std::unique_ptr<Storage::Tape::Commodore::Header> header = parser.get_next_header(tape);
|
||||
|
@ -9,15 +9,16 @@
|
||||
#ifndef StaticAnalyser_Commodore_Tape_hpp
|
||||
#define StaticAnalyser_Commodore_Tape_hpp
|
||||
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
#include "File.hpp"
|
||||
#include <list>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Commodore {
|
||||
|
||||
std::list<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,10 @@
|
||||
|
||||
#include "Kernel.hpp"
|
||||
|
||||
using namespace StaticAnalyser::MOS6502;
|
||||
using namespace Analyser::Static::MOS6502;
|
||||
namespace {
|
||||
|
||||
using PartialDisassembly = StaticAnalyser::Disassembly::PartialDisassembly<Disassembly, uint16_t>;
|
||||
using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Disassembly, uint16_t>;
|
||||
|
||||
struct MOS6502Disassembler {
|
||||
|
||||
@ -312,9 +312,9 @@ static void AddToDisassembly(PartialDisassembly &disassembly, const std::vector<
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
Disassembly StaticAnalyser::MOS6502::Disassemble(
|
||||
Disassembly Analyser::Static::MOS6502::Disassemble(
|
||||
const std::vector<uint8_t> &memory,
|
||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||
std::vector<uint16_t> entry_points) {
|
||||
return StaticAnalyser::Disassembly::Disassemble<Disassembly, uint16_t, MOS6502Disassembler>(memory, address_mapper, entry_points);
|
||||
return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, MOS6502Disassembler>(memory, address_mapper, entry_points);
|
||||
}
|
@ -16,7 +16,8 @@
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace MOS6502 {
|
||||
|
||||
/*!
|
||||
@ -95,5 +96,6 @@ Disassembly Disassemble(
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Disassembler6502_hpp */
|
@ -11,7 +11,8 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Disassembler {
|
||||
|
||||
/*!
|
||||
@ -24,6 +25,7 @@ template <typename T> std::function<std::size_t(T)> OffsetMapper(T start_address
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,8 @@
|
||||
#ifndef Kernel_hpp
|
||||
#define Kernel_hpp
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Disassembly {
|
||||
|
||||
template <typename D, typename S> struct PartialDisassembly {
|
||||
@ -44,6 +45,7 @@ template <typename D, typename S, typename Disassembler> D Disassemble(
|
||||
return partial_disassembly.disassembly;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,10 @@
|
||||
|
||||
#include "Kernel.hpp"
|
||||
|
||||
using namespace StaticAnalyser::Z80;
|
||||
using namespace Analyser::Static::Z80;
|
||||
namespace {
|
||||
|
||||
using PartialDisassembly = StaticAnalyser::Disassembly::PartialDisassembly<Disassembly, uint16_t>;
|
||||
using PartialDisassembly = Analyser::Static::Disassembly::PartialDisassembly<Disassembly, uint16_t>;
|
||||
|
||||
class Accessor {
|
||||
public:
|
||||
@ -611,9 +611,9 @@ struct Z80Disassembler {
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
Disassembly StaticAnalyser::Z80::Disassemble(
|
||||
Disassembly Analyser::Static::Z80::Disassemble(
|
||||
const std::vector<uint8_t> &memory,
|
||||
const std::function<std::size_t(uint16_t)> &address_mapper,
|
||||
std::vector<uint16_t> entry_points) {
|
||||
return StaticAnalyser::Disassembly::Disassemble<Disassembly, uint16_t, Z80Disassembler>(memory, address_mapper, entry_points);
|
||||
return Analyser::Static::Disassembly::Disassemble<Disassembly, uint16_t, Z80Disassembler>(memory, address_mapper, entry_points);
|
||||
}
|
@ -15,7 +15,8 @@
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Z80 {
|
||||
|
||||
struct Instruction {
|
||||
@ -84,5 +85,6 @@ Disassembly Disassemble(
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* StaticAnalyser_Disassembler_Z80_hpp */
|
40
Analyser/Static/MSX/Cartridge.hpp
Normal file
40
Analyser/Static/MSX/Cartridge.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
//
|
||||
// Cartridge.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Cartridge_hpp
|
||||
#define Cartridge_hpp
|
||||
|
||||
#include "../../../Storage/Cartridge/Cartridge.hpp"
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace MSX {
|
||||
|
||||
/*!
|
||||
Extends the base cartridge class by adding a (guess at) the banking scheme.
|
||||
*/
|
||||
struct Cartridge: public ::Storage::Cartridge::Cartridge {
|
||||
enum Type {
|
||||
None,
|
||||
Konami,
|
||||
KonamiWithSCC,
|
||||
ASCII8kb,
|
||||
ASCII16kb,
|
||||
FMPac
|
||||
};
|
||||
const Type type;
|
||||
|
||||
Cartridge(const std::vector<Segment> &segments, Type type) :
|
||||
Storage::Cartridge::Cartridge(segments), type(type) {}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Cartridge_hpp */
|
292
Analyser/Static/MSX/StaticAnalyser.cpp
Normal file
292
Analyser/Static/MSX/StaticAnalyser.cpp
Normal file
@ -0,0 +1,292 @@
|
||||
//
|
||||
// StaticAnalyser.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/11/2017.
|
||||
// Copyright © 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include "Cartridge.hpp"
|
||||
#include "Tape.hpp"
|
||||
#include "../Disassembler/Z80.hpp"
|
||||
#include "../Disassembler/AddressMapper.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
static std::unique_ptr<Analyser::Static::Target> CartridgeTarget(
|
||||
const Storage::Cartridge::Cartridge::Segment &segment,
|
||||
uint16_t start_address,
|
||||
Analyser::Static::MSX::Cartridge::Type type,
|
||||
float confidence) {
|
||||
|
||||
// Size down to a multiple of 8kb in size and apply the start address.
|
||||
std::vector<Storage::Cartridge::Cartridge::Segment> output_segments;
|
||||
if(segment.data.size() & 0x1fff) {
|
||||
std::vector<uint8_t> truncated_data;
|
||||
std::vector<uint8_t>::difference_type truncated_size = static_cast<std::vector<uint8_t>::difference_type>(segment.data.size()) & ~0x1fff;
|
||||
truncated_data.insert(truncated_data.begin(), segment.data.begin(), segment.data.begin() + truncated_size);
|
||||
output_segments.emplace_back(start_address, truncated_data);
|
||||
} else {
|
||||
output_segments.emplace_back(start_address, segment.data);
|
||||
}
|
||||
|
||||
std::unique_ptr<Analyser::Static::Target> target(new Analyser::Static::Target);
|
||||
target->machine = Analyser::Machine::MSX;
|
||||
target->confidence = confidence;
|
||||
|
||||
if(type == Analyser::Static::MSX::Cartridge::Type::None) {
|
||||
target->media.cartridges.emplace_back(new Storage::Cartridge::Cartridge(output_segments));
|
||||
} else {
|
||||
target->media.cartridges.emplace_back(new Analyser::Static::MSX::Cartridge(output_segments, type));
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/*
|
||||
Expected standard cartridge format:
|
||||
|
||||
DEFB "AB" ; expansion ROM header
|
||||
DEFW initcode ; start of the init code, 0 if no initcode
|
||||
DEFW callstat; pointer to CALL statement handler, 0 if no such handler
|
||||
DEFW device; pointer to expansion device handler, 0 if no such handler
|
||||
DEFW basic ; pointer to the start of a tokenized basicprogram, 0 if no basicprogram
|
||||
DEFS 6,0 ; room reserved for future extensions
|
||||
|
||||
MSX cartridges often include banking hardware; those games were marketed as MegaROMs. The file
|
||||
format that the MSX community has decided upon doesn't retain the type of hardware included, so
|
||||
this analyser has to guess.
|
||||
|
||||
(additional audio hardware is also sometimes included, but it's implied by the banking hardware)
|
||||
*/
|
||||
static std::vector<std::unique_ptr<Analyser::Static::Target>> CartridgeTargetsFrom(
|
||||
const std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> &cartridges) {
|
||||
// No cartridges implies no targets.
|
||||
if(cartridges.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Analyser::Static::Target>> targets;
|
||||
for(const auto &cartridge : cartridges) {
|
||||
const auto &segments = cartridge->get_segments();
|
||||
|
||||
// Only one mapped item is allowed.
|
||||
if(segments.size() != 1) continue;
|
||||
|
||||
// Which must be no more than 63 bytes larger than a multiple of 8 kb in size.
|
||||
Storage::Cartridge::Cartridge::Segment segment = segments.front();
|
||||
const size_t data_size = segment.data.size();
|
||||
if(data_size < 0x2000 || (data_size & 0x1fff) > 64) continue;
|
||||
|
||||
// Check for a ROM header at address 0; if it's not found then try 0x4000
|
||||
// and adjust the start address;
|
||||
uint16_t start_address = 0;
|
||||
bool found_start = false;
|
||||
if(segment.data[0] == 0x41 && segment.data[1] == 0x42) {
|
||||
start_address = 0x4000;
|
||||
found_start = true;
|
||||
} else if(segment.data.size() >= 0x8000 && segment.data[0x4000] == 0x41 && segment.data[0x4001] == 0x42) {
|
||||
start_address = 0;
|
||||
found_start = true;
|
||||
}
|
||||
|
||||
// Reject cartridge if the ROM header wasn't found.
|
||||
if(!found_start) continue;
|
||||
|
||||
uint16_t init_address = static_cast<uint16_t>(segment.data[2] | (segment.data[3] << 8));
|
||||
// TODO: check for a rational init address?
|
||||
|
||||
// If this ROM is less than 48kb in size then it's an ordinary ROM. Just emplace it and move on.
|
||||
if(data_size <= 0xc000) {
|
||||
targets.emplace_back(CartridgeTarget(segment, start_address, Analyser::Static::MSX::Cartridge::Type::None, 1.0));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this ROM is greater than 48kb in size then some sort of MegaROM scheme must
|
||||
// be at play; disassemble to try to figure it out.
|
||||
std::vector<uint8_t> first_8k;
|
||||
first_8k.insert(first_8k.begin(), segment.data.begin(), segment.data.begin() + 8192);
|
||||
Analyser::Static::Z80::Disassembly disassembly =
|
||||
Analyser::Static::Z80::Disassemble(
|
||||
first_8k,
|
||||
Analyser::Static::Disassembler::OffsetMapper(start_address),
|
||||
{ init_address }
|
||||
);
|
||||
|
||||
// // Look for a indirect store followed by an unconditional JP or CALL into another
|
||||
// // segment, that's a fairly explicit sign where found.
|
||||
using Instruction = Analyser::Static::Z80::Instruction;
|
||||
std::map<uint16_t, Instruction> &instructions = disassembly.instructions_by_address;
|
||||
bool is_ascii = false;
|
||||
// auto iterator = instructions.begin();
|
||||
// while(iterator != instructions.end()) {
|
||||
// auto next_iterator = iterator;
|
||||
// next_iterator++;
|
||||
// if(next_iterator == instructions.end()) break;
|
||||
//
|
||||
// if( iterator->second.operation == Instruction::Operation::LD &&
|
||||
// iterator->second.destination == Instruction::Location::Operand_Indirect &&
|
||||
// (
|
||||
// iterator->second.operand == 0x5000 ||
|
||||
// iterator->second.operand == 0x6000 ||
|
||||
// iterator->second.operand == 0x6800 ||
|
||||
// iterator->second.operand == 0x7000 ||
|
||||
// iterator->second.operand == 0x77ff ||
|
||||
// iterator->second.operand == 0x7800 ||
|
||||
// iterator->second.operand == 0x8000 ||
|
||||
// iterator->second.operand == 0x9000 ||
|
||||
// iterator->second.operand == 0xa000
|
||||
// ) &&
|
||||
// (
|
||||
// next_iterator->second.operation == Instruction::Operation::CALL ||
|
||||
// next_iterator->second.operation == Instruction::Operation::JP
|
||||
// ) &&
|
||||
// ((next_iterator->second.operand >> 13) != (0x4000 >> 13))
|
||||
// ) {
|
||||
// const uint16_t address = static_cast<uint16_t>(next_iterator->second.operand);
|
||||
// switch(iterator->second.operand) {
|
||||
// case 0x6000:
|
||||
// if(address >= 0x6000 && address < 0x8000) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
||||
// }
|
||||
// break;
|
||||
// case 0x6800:
|
||||
// if(address >= 0x6000 && address < 0x6800) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::ASCII8kb;
|
||||
// }
|
||||
// break;
|
||||
// case 0x7000:
|
||||
// if(address >= 0x6000 && address < 0x8000) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
||||
// }
|
||||
// if(address >= 0x7000 && address < 0x7800) {
|
||||
// is_ascii = true;
|
||||
// }
|
||||
// break;
|
||||
// case 0x77ff:
|
||||
// if(address >= 0x7000 && address < 0x7800) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::ASCII16kb;
|
||||
// }
|
||||
// break;
|
||||
// case 0x7800:
|
||||
// if(address >= 0xa000 && address < 0xc000) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::ASCII8kb;
|
||||
// }
|
||||
// break;
|
||||
// case 0x8000:
|
||||
// if(address >= 0x8000 && address < 0xa000) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
||||
// }
|
||||
// break;
|
||||
// case 0x9000:
|
||||
// if(address >= 0x8000 && address < 0xa000) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
||||
// }
|
||||
// break;
|
||||
// case 0xa000:
|
||||
// if(address >= 0xa000 && address < 0xc000) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::Konami;
|
||||
// }
|
||||
// break;
|
||||
// case 0xb000:
|
||||
// if(address >= 0xa000 && address < 0xc000) {
|
||||
// target.msx.cartridge_type = Analyser::Static::MSXCartridgeType::KonamiWithSCC;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// iterator = next_iterator;
|
||||
|
||||
// Look for LD (nnnn), A instructions, and collate those addresses.
|
||||
std::map<uint16_t, int> address_counts;
|
||||
for(const auto &instruction_pair : instructions) {
|
||||
if( instruction_pair.second.operation == Instruction::Operation::LD &&
|
||||
instruction_pair.second.destination == Instruction::Location::Operand_Indirect &&
|
||||
instruction_pair.second.source == Instruction::Location::A) {
|
||||
address_counts[static_cast<uint16_t>(instruction_pair.second.operand)]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Weight confidences by number of observed hits.
|
||||
float total_hits =
|
||||
static_cast<float>(
|
||||
address_counts[0x6000] + address_counts[0x6800] +
|
||||
address_counts[0x7000] + address_counts[0x7800] +
|
||||
address_counts[0x77ff] + address_counts[0x8000] +
|
||||
address_counts[0xa000] + address_counts[0x5000] +
|
||||
address_counts[0x9000] + address_counts[0xb000]
|
||||
);
|
||||
|
||||
targets.push_back(CartridgeTarget(
|
||||
segment,
|
||||
start_address,
|
||||
Analyser::Static::MSX::Cartridge::ASCII8kb,
|
||||
static_cast<float>( address_counts[0x6000] +
|
||||
address_counts[0x6800] +
|
||||
address_counts[0x7000] +
|
||||
address_counts[0x7800]) / total_hits));
|
||||
targets.push_back(CartridgeTarget(
|
||||
segment,
|
||||
start_address,
|
||||
Analyser::Static::MSX::Cartridge::ASCII16kb,
|
||||
static_cast<float>( address_counts[0x6000] +
|
||||
address_counts[0x7000] +
|
||||
address_counts[0x77ff]) / total_hits));
|
||||
if(!is_ascii) {
|
||||
targets.push_back(CartridgeTarget(
|
||||
segment,
|
||||
start_address,
|
||||
Analyser::Static::MSX::Cartridge::Konami,
|
||||
static_cast<float>( address_counts[0x6000] +
|
||||
address_counts[0x8000] +
|
||||
address_counts[0xa000]) / total_hits));
|
||||
}
|
||||
if(!is_ascii) {
|
||||
targets.push_back(CartridgeTarget(
|
||||
segment,
|
||||
start_address,
|
||||
Analyser::Static::MSX::Cartridge::KonamiWithSCC,
|
||||
static_cast<float>( address_counts[0x5000] +
|
||||
address_counts[0x7000] +
|
||||
address_counts[0x9000] +
|
||||
address_counts[0xb000]) / total_hits));
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
void Analyser::Static::MSX::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
|
||||
// Append targets for any cartridges that look correct.
|
||||
std::vector<std::unique_ptr<Target>> cartridge_targets = CartridgeTargetsFrom(media.cartridges);
|
||||
std::move(cartridge_targets.begin(), cartridge_targets.end(), std::back_inserter(destination));
|
||||
|
||||
// Consider building a target for disks and/or tapes.
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
|
||||
// Check tapes for loadable files.
|
||||
for(const auto &tape : media.tapes) {
|
||||
std::vector<File> files_on_tape = GetFiles(tape);
|
||||
if(!files_on_tape.empty()) {
|
||||
switch(files_on_tape.front().type) {
|
||||
case File::Type::ASCII: target->loading_command = "RUN\"CAS:\r"; break;
|
||||
case File::Type::TokenisedBASIC: target->loading_command = "CLOAD\rRUN\r"; break;
|
||||
case File::Type::Binary: target->loading_command = "BLOAD\"CAS:\",R\r"; break;
|
||||
default: break;
|
||||
}
|
||||
target->media.tapes.push_back(tape);
|
||||
}
|
||||
}
|
||||
|
||||
// Blindly accept disks for now.
|
||||
target->media.disks = media.disks;
|
||||
|
||||
if(!target->media.empty()) {
|
||||
target->machine = Machine::MSX;
|
||||
target->confidence = 1.0;
|
||||
destination.push_back(std::move(target));
|
||||
}
|
||||
}
|
@ -11,11 +11,13 @@
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace MSX {
|
||||
|
||||
void AddTargets(const Media &media, std::list<Target> &destination);
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
#include "Tape.hpp"
|
||||
|
||||
#include "../../Storage/Tape/Parsers/MSX.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/MSX.hpp"
|
||||
|
||||
using namespace StaticAnalyser::MSX;
|
||||
using namespace Analyser::Static::MSX;
|
||||
|
||||
File::File(File &&rhs) :
|
||||
name(std::move(rhs.name)),
|
||||
@ -24,7 +24,7 @@ File::File() :
|
||||
starting_address(0),
|
||||
entry_address(0) {} // For the sake of initialising in a defined state.
|
||||
|
||||
std::vector<File> StaticAnalyser::MSX::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<File> Analyser::Static::MSX::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<File> files;
|
||||
|
||||
Storage::Tape::BinaryTapePlayer tape_player(1000000);
|
@ -9,12 +9,13 @@
|
||||
#ifndef StaticAnalyser_MSX_Tape_hpp
|
||||
#define StaticAnalyser_MSX_Tape_hpp
|
||||
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace MSX {
|
||||
|
||||
struct File {
|
||||
@ -36,6 +37,7 @@ struct File {
|
||||
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
#include "../Disassembler/6502.hpp"
|
||||
#include "../Disassembler/AddressMapper.hpp"
|
||||
|
||||
using namespace StaticAnalyser::Oric;
|
||||
using namespace Analyser::Static::Oric;
|
||||
|
||||
static int Score(const StaticAnalyser::MOS6502::Disassembly &disassembly, const std::set<uint16_t> &rom_functions, const std::set<uint16_t> &variable_locations) {
|
||||
static int Score(const Analyser::Static::MOS6502::Disassembly &disassembly, const std::set<uint16_t> &rom_functions, const std::set<uint16_t> &variable_locations) {
|
||||
int score = 0;
|
||||
|
||||
for(auto address : disassembly.outward_calls) score += (rom_functions.find(address) != rom_functions.end()) ? 1 : -1;
|
||||
@ -24,7 +24,7 @@ static int Score(const StaticAnalyser::MOS6502::Disassembly &disassembly, const
|
||||
return score;
|
||||
}
|
||||
|
||||
static int Basic10Score(const StaticAnalyser::MOS6502::Disassembly &disassembly) {
|
||||
static int Basic10Score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
std::set<uint16_t> rom_functions = {
|
||||
0x0228, 0x022b,
|
||||
0xc3ca, 0xc3f8, 0xc448, 0xc47c, 0xc4b5, 0xc4e3, 0xc4e0, 0xc524, 0xc56f, 0xc5a2, 0xc5f8, 0xc60a, 0xc6a5, 0xc6de, 0xc719, 0xc738,
|
||||
@ -48,7 +48,7 @@ static int Basic10Score(const StaticAnalyser::MOS6502::Disassembly &disassembly)
|
||||
return Score(disassembly, rom_functions, variable_locations);
|
||||
}
|
||||
|
||||
static int Basic11Score(const StaticAnalyser::MOS6502::Disassembly &disassembly) {
|
||||
static int Basic11Score(const Analyser::Static::MOS6502::Disassembly &disassembly) {
|
||||
std::set<uint16_t> rom_functions = {
|
||||
0x0238, 0x023b, 0x023e, 0x0241, 0x0244, 0x0247,
|
||||
0xc3c6, 0xc3f4, 0xc444, 0xc47c, 0xc4a8, 0xc4d3, 0xc4e0, 0xc524, 0xc55f, 0xc592, 0xc5e8, 0xc5fa, 0xc692, 0xc6b3, 0xc6ee, 0xc70d,
|
||||
@ -73,23 +73,23 @@ static int Basic11Score(const StaticAnalyser::MOS6502::Disassembly &disassembly)
|
||||
return Score(disassembly, rom_functions, variable_locations);
|
||||
}
|
||||
|
||||
void StaticAnalyser::Oric::AddTargets(const Media &media, std::list<Target> &destination) {
|
||||
Target target;
|
||||
target.machine = Target::Oric;
|
||||
target.probability = 1.0;
|
||||
void Analyser::Static::Oric::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination) {
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::Oric;
|
||||
target->confidence = 1.0;
|
||||
|
||||
int basic10_votes = 0;
|
||||
int basic11_votes = 0;
|
||||
|
||||
for(auto &tape : media.tapes) {
|
||||
std::list<File> tape_files = GetFiles(tape);
|
||||
std::vector<File> tape_files = GetFiles(tape);
|
||||
tape->reset();
|
||||
if(tape_files.size()) {
|
||||
for(auto file : tape_files) {
|
||||
if(file.data_type == File::MachineCode) {
|
||||
std::vector<uint16_t> entry_points = {file.starting_address};
|
||||
StaticAnalyser::MOS6502::Disassembly disassembly =
|
||||
StaticAnalyser::MOS6502::Disassemble(file.data, StaticAnalyser::Disassembler::OffsetMapper(file.starting_address), entry_points);
|
||||
Analyser::Static::MOS6502::Disassembly disassembly =
|
||||
Analyser::Static::MOS6502::Disassemble(file.data, Analyser::Static::Disassembler::OffsetMapper(file.starting_address), entry_points);
|
||||
|
||||
int basic10_score = Basic10Score(disassembly);
|
||||
int basic11_score = Basic11Score(disassembly);
|
||||
@ -97,23 +97,23 @@ void StaticAnalyser::Oric::AddTargets(const Media &media, std::list<Target> &des
|
||||
}
|
||||
}
|
||||
|
||||
target.media.tapes.push_back(tape);
|
||||
target.loading_command = "CLOAD\"\"\n";
|
||||
target->media.tapes.push_back(tape);
|
||||
target->loading_command = "CLOAD\"\"\n";
|
||||
}
|
||||
}
|
||||
|
||||
// trust that any disk supplied can be handled by the Microdisc. TODO: check.
|
||||
if(!media.disks.empty()) {
|
||||
target.oric.has_microdisc = true;
|
||||
target.media.disks = media.disks;
|
||||
target->oric.has_microdisc = true;
|
||||
target->media.disks = media.disks;
|
||||
} else {
|
||||
target.oric.has_microdisc = false;
|
||||
target->oric.has_microdisc = false;
|
||||
}
|
||||
|
||||
// TODO: really this should add two targets if not all votes agree
|
||||
target.oric.use_atmos_rom = basic11_votes >= basic10_votes;
|
||||
if(target.oric.has_microdisc) target.oric.use_atmos_rom = true;
|
||||
target->oric.use_atmos_rom = basic11_votes >= basic10_votes;
|
||||
if(target->oric.has_microdisc) target->oric.use_atmos_rom = true;
|
||||
|
||||
if(target.media.tapes.size() || target.media.disks.size() || target.media.cartridges.size())
|
||||
destination.push_back(target);
|
||||
if(target->media.tapes.size() || target->media.disks.size() || target->media.cartridges.size())
|
||||
destination.push_back(std::move(target));
|
||||
}
|
@ -11,13 +11,14 @@
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Oric {
|
||||
|
||||
void AddTargets(const Media &media, std::list<Target> &destination);
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* StaticAnalyser_hpp */
|
@ -7,12 +7,12 @@
|
||||
//
|
||||
|
||||
#include "Tape.hpp"
|
||||
#include "../../Storage/Tape/Parsers/Oric.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/Oric.hpp"
|
||||
|
||||
using namespace StaticAnalyser::Oric;
|
||||
using namespace Analyser::Static::Oric;
|
||||
|
||||
std::list<File> StaticAnalyser::Oric::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::list<File> files;
|
||||
std::vector<File> Analyser::Static::Oric::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<File> files;
|
||||
Storage::Tape::Oric::Parser parser;
|
||||
|
||||
while(!tape->is_at_end()) {
|
@ -9,12 +9,13 @@
|
||||
#ifndef StaticAnalyser_Oric_Tape_hpp
|
||||
#define StaticAnalyser_Oric_Tape_hpp
|
||||
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include <list>
|
||||
#include "../../../Storage/Tape/Tape.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Oric {
|
||||
|
||||
struct File {
|
||||
@ -30,8 +31,9 @@ struct File {
|
||||
std::vector<uint8_t> data;
|
||||
};
|
||||
|
||||
std::list<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
std::vector<File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "StaticAnalyser.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
@ -21,34 +22,34 @@
|
||||
#include "ZX8081/StaticAnalyser.hpp"
|
||||
|
||||
// Cartridges
|
||||
#include "../Storage/Cartridge/Formats/BinaryDump.hpp"
|
||||
#include "../Storage/Cartridge/Formats/PRG.hpp"
|
||||
#include "../../Storage/Cartridge/Formats/BinaryDump.hpp"
|
||||
#include "../../Storage/Cartridge/Formats/PRG.hpp"
|
||||
|
||||
// Disks
|
||||
#include "../Storage/Disk/DiskImage/Formats/AcornADF.hpp"
|
||||
#include "../Storage/Disk/DiskImage/Formats/CPCDSK.hpp"
|
||||
#include "../Storage/Disk/DiskImage/Formats/D64.hpp"
|
||||
#include "../Storage/Disk/DiskImage/Formats/G64.hpp"
|
||||
#include "../Storage/Disk/DiskImage/Formats/DMK.hpp"
|
||||
#include "../Storage/Disk/DiskImage/Formats/HFE.hpp"
|
||||
#include "../Storage/Disk/DiskImage/Formats/MSXDSK.hpp"
|
||||
#include "../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp"
|
||||
#include "../Storage/Disk/DiskImage/Formats/SSD.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/AcornADF.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/CPCDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/D64.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/G64.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/DMK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/HFE.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/MSXDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/OricMFMDSK.hpp"
|
||||
#include "../../Storage/Disk/DiskImage/Formats/SSD.hpp"
|
||||
|
||||
// Tapes
|
||||
#include "../Storage/Tape/Formats/CAS.hpp"
|
||||
#include "../Storage/Tape/Formats/CommodoreTAP.hpp"
|
||||
#include "../Storage/Tape/Formats/CSW.hpp"
|
||||
#include "../Storage/Tape/Formats/OricTAP.hpp"
|
||||
#include "../Storage/Tape/Formats/TapePRG.hpp"
|
||||
#include "../Storage/Tape/Formats/TapeUEF.hpp"
|
||||
#include "../Storage/Tape/Formats/TZX.hpp"
|
||||
#include "../Storage/Tape/Formats/ZX80O81P.hpp"
|
||||
#include "../../Storage/Tape/Formats/CAS.hpp"
|
||||
#include "../../Storage/Tape/Formats/CommodoreTAP.hpp"
|
||||
#include "../../Storage/Tape/Formats/CSW.hpp"
|
||||
#include "../../Storage/Tape/Formats/OricTAP.hpp"
|
||||
#include "../../Storage/Tape/Formats/TapePRG.hpp"
|
||||
#include "../../Storage/Tape/Formats/TapeUEF.hpp"
|
||||
#include "../../Storage/Tape/Formats/TZX.hpp"
|
||||
#include "../../Storage/Tape/Formats/ZX80O81P.hpp"
|
||||
|
||||
// Target Platform Types
|
||||
#include "../Storage/TargetPlatforms.hpp"
|
||||
#include "../../Storage/TargetPlatforms.hpp"
|
||||
|
||||
using namespace StaticAnalyser;
|
||||
using namespace Analyser::Static;
|
||||
|
||||
static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType &potential_platforms) {
|
||||
// Get the extension, if any; it will be assumed that extensions are reliable, so an extension is a broad-phase
|
||||
@ -88,8 +89,8 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
|
||||
Format("adf", result.disks, Disk::DiskImageHolder<Storage::Disk::AcornADF>, TargetPlatform::Acorn) // ADF
|
||||
Format("bin", result.cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
|
||||
Format("cas", result.tapes, Tape::CAS, TargetPlatform::MSX) // CAS
|
||||
Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT
|
||||
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
|
||||
Format("cdt", result.tapes, Tape::TZX, TargetPlatform::AmstradCPC) // CDT
|
||||
Format("csw", result.tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
|
||||
Format("d64", result.disks, Disk::DiskImageHolder<Storage::Disk::D64>, TargetPlatform::Commodore) // D64
|
||||
Format("dmk", result.disks, Disk::DiskImageHolder<Storage::Disk::DMK>, TargetPlatform::MSX) // DMK
|
||||
Format("dsd", result.disks, Disk::DiskImageHolder<Storage::Disk::SSD>, TargetPlatform::Acorn) // DSD
|
||||
@ -134,13 +135,13 @@ static Media GetMediaAndPlatforms(const char *file_name, TargetPlatform::IntType
|
||||
return result;
|
||||
}
|
||||
|
||||
Media StaticAnalyser::GetMedia(const char *file_name) {
|
||||
Media Analyser::Static::GetMedia(const char *file_name) {
|
||||
TargetPlatform::IntType throwaway;
|
||||
return GetMediaAndPlatforms(file_name, throwaway);
|
||||
}
|
||||
|
||||
std::list<Target> StaticAnalyser::GetTargets(const char *file_name) {
|
||||
std::list<Target> targets;
|
||||
std::vector<std::unique_ptr<Target>> Analyser::Static::GetTargets(const char *file_name) {
|
||||
std::vector<std::unique_ptr<Target>> targets;
|
||||
|
||||
// Collect all disks, tapes and ROMs as can be extrapolated from this file, forming the
|
||||
// union of all platforms this file might be a target for.
|
||||
@ -158,11 +159,18 @@ std::list<Target> StaticAnalyser::GetTargets(const char *file_name) {
|
||||
if(potential_platforms & TargetPlatform::ZX8081) ZX8081::AddTargets(media, targets, potential_platforms);
|
||||
|
||||
// Reset any tapes to their initial position
|
||||
for(auto target : targets) {
|
||||
for(auto tape : media.tapes) {
|
||||
for(auto &target : targets) {
|
||||
for(auto &tape : target->media.tapes) {
|
||||
tape->reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by initial confidence. Use a stable sort in case any of the machine-specific analysers
|
||||
// picked their insertion order carefully.
|
||||
std::stable_sort(targets.begin(), targets.end(),
|
||||
[] (const std::unique_ptr<Target> &a, const std::unique_ptr<Target> &b) {
|
||||
return a->confidence > b->confidence;
|
||||
});
|
||||
|
||||
return targets;
|
||||
}
|
@ -9,15 +9,17 @@
|
||||
#ifndef StaticAnalyser_hpp
|
||||
#define StaticAnalyser_hpp
|
||||
|
||||
#include "../Storage/Tape/Tape.hpp"
|
||||
#include "../Storage/Disk/Disk.hpp"
|
||||
#include "../Storage/Cartridge/Cartridge.hpp"
|
||||
#include "../Machines.hpp"
|
||||
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
#include "../../Storage/Disk/Disk.hpp"
|
||||
#include "../../Storage/Cartridge/Cartridge.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
|
||||
enum class Vic20MemoryModel {
|
||||
Unexpanded,
|
||||
@ -40,15 +42,6 @@ enum class Atari2600PagingModel {
|
||||
Pitfall2
|
||||
};
|
||||
|
||||
enum class MSXCartridgeType {
|
||||
None,
|
||||
Konami,
|
||||
KonamiWithSCC,
|
||||
ASCII8kb,
|
||||
ASCII16kb,
|
||||
FMPac
|
||||
};
|
||||
|
||||
enum class ZX8081MemoryModel {
|
||||
Unexpanded,
|
||||
SixteenKB,
|
||||
@ -65,9 +58,9 @@ enum class AmstradCPCModel {
|
||||
A list of disks, tapes and cartridges.
|
||||
*/
|
||||
struct Media {
|
||||
std::list<std::shared_ptr<Storage::Disk::Disk>> disks;
|
||||
std::list<std::shared_ptr<Storage::Tape::Tape>> tapes;
|
||||
std::list<std::shared_ptr<Storage::Cartridge::Cartridge>> cartridges;
|
||||
std::vector<std::shared_ptr<Storage::Disk::Disk>> disks;
|
||||
std::vector<std::shared_ptr<Storage::Tape::Tape>> tapes;
|
||||
std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>> cartridges;
|
||||
|
||||
bool empty() const {
|
||||
return disks.empty() && tapes.empty() && cartridges.empty();
|
||||
@ -79,16 +72,11 @@ struct Media {
|
||||
and instructions on how to launch the software attached, plus a measure of confidence in this target's correctness.
|
||||
*/
|
||||
struct Target {
|
||||
enum Machine {
|
||||
AmstradCPC,
|
||||
Atari2600,
|
||||
Electron,
|
||||
MSX,
|
||||
Oric,
|
||||
Vic20,
|
||||
ZX8081
|
||||
} machine;
|
||||
float probability;
|
||||
Machine machine;
|
||||
Media media;
|
||||
|
||||
float confidence;
|
||||
std::string loading_command;
|
||||
|
||||
// TODO: this is too C-like a solution; make Target a base class and
|
||||
// turn the following into information held by more specific subclasses.
|
||||
@ -122,14 +110,7 @@ struct Target {
|
||||
struct {
|
||||
AmstradCPCModel model;
|
||||
} amstradcpc;
|
||||
|
||||
struct {
|
||||
MSXCartridgeType cartridge_type;
|
||||
} msx;
|
||||
};
|
||||
|
||||
std::string loading_command;
|
||||
Media media;
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -137,13 +118,14 @@ struct Target {
|
||||
|
||||
@returns The list of potential targets, sorted from most to least probable.
|
||||
*/
|
||||
std::list<Target> GetTargets(const char *file_name);
|
||||
std::vector<std::unique_ptr<Target>> GetTargets(const char *file_name);
|
||||
|
||||
/*!
|
||||
Inspects the supplied file and determines the media included.
|
||||
*/
|
||||
Media GetMedia(const char *file_name);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* StaticAnalyser_hpp */
|
@ -11,7 +11,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../../Storage/Tape/Parsers/ZX8081.hpp"
|
||||
#include "../../../Storage/Tape/Parsers/ZX8081.hpp"
|
||||
|
||||
static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape) {
|
||||
std::vector<Storage::Data::ZX8081::File> files;
|
||||
@ -27,41 +27,41 @@ static std::vector<Storage::Data::ZX8081::File> GetFiles(const std::shared_ptr<S
|
||||
return files;
|
||||
}
|
||||
|
||||
void StaticAnalyser::ZX8081::AddTargets(const Media &media, std::list<Target> &destination, TargetPlatform::IntType potential_platforms) {
|
||||
void Analyser::Static::ZX8081::AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination, TargetPlatform::IntType potential_platforms) {
|
||||
if(!media.tapes.empty()) {
|
||||
std::vector<Storage::Data::ZX8081::File> files = GetFiles(media.tapes.front());
|
||||
media.tapes.front()->reset();
|
||||
if(!files.empty()) {
|
||||
StaticAnalyser::Target target;
|
||||
target.machine = Target::ZX8081;
|
||||
std::unique_ptr<Target> target(new Target);
|
||||
target->machine = Machine::ZX8081;
|
||||
|
||||
// Guess the machine type from the file only if it isn't already known.
|
||||
switch(potential_platforms & (TargetPlatform::ZX80 | TargetPlatform::ZX81)) {
|
||||
default:
|
||||
target.zx8081.isZX81 = files.front().isZX81;
|
||||
target->zx8081.isZX81 = files.front().isZX81;
|
||||
break;
|
||||
|
||||
case TargetPlatform::ZX80: target.zx8081.isZX81 = false; break;
|
||||
case TargetPlatform::ZX81: target.zx8081.isZX81 = true; break;
|
||||
case TargetPlatform::ZX80: target->zx8081.isZX81 = false; break;
|
||||
case TargetPlatform::ZX81: target->zx8081.isZX81 = true; break;
|
||||
}
|
||||
|
||||
/*if(files.front().data.size() > 16384) {
|
||||
target.zx8081.memory_model = ZX8081MemoryModel::SixtyFourKB;
|
||||
target->zx8081.memory_model = ZX8081MemoryModel::SixtyFourKB;
|
||||
} else*/ if(files.front().data.size() > 1024) {
|
||||
target.zx8081.memory_model = ZX8081MemoryModel::SixteenKB;
|
||||
target->zx8081.memory_model = ZX8081MemoryModel::SixteenKB;
|
||||
} else {
|
||||
target.zx8081.memory_model = ZX8081MemoryModel::Unexpanded;
|
||||
target->zx8081.memory_model = ZX8081MemoryModel::Unexpanded;
|
||||
}
|
||||
target.media.tapes = media.tapes;
|
||||
target->media.tapes = media.tapes;
|
||||
|
||||
// TODO: how to run software once loaded? Might require a BASIC detokeniser.
|
||||
if(target.zx8081.isZX81) {
|
||||
target.loading_command = "J\"\"\n";
|
||||
if(target->zx8081.isZX81) {
|
||||
target->loading_command = "J\"\"\n";
|
||||
} else {
|
||||
target.loading_command = "W\n";
|
||||
target->loading_command = "W\n";
|
||||
}
|
||||
|
||||
destination.push_back(target);
|
||||
destination.push_back(std::move(target));
|
||||
}
|
||||
}
|
||||
}
|
@ -10,13 +10,15 @@
|
||||
#define StaticAnalyser_ZX8081_StaticAnalyser_hpp
|
||||
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include "../../Storage/TargetPlatforms.hpp"
|
||||
#include "../../../Storage/TargetPlatforms.hpp"
|
||||
|
||||
namespace StaticAnalyser {
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace ZX8081 {
|
||||
|
||||
void AddTargets(const Media &media, std::list<Target> &destination, TargetPlatform::IntType potential_platforms);
|
||||
void AddTargets(const Media &media, std::vector<std::unique_ptr<Target>> &destination, TargetPlatform::IntType potential_platforms);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,10 @@ struct Option {
|
||||
virtual ~Option() {}
|
||||
|
||||
Option(const std::string &long_name, const std::string &short_name) : long_name(long_name), short_name(short_name) {}
|
||||
|
||||
virtual bool operator==(const Option &rhs) {
|
||||
return long_name == rhs.long_name && short_name == rhs.short_name;
|
||||
}
|
||||
};
|
||||
|
||||
struct BooleanOption: public Option {
|
||||
@ -35,6 +39,12 @@ struct BooleanOption: public Option {
|
||||
struct ListOption: public Option {
|
||||
std::vector<std::string> options;
|
||||
ListOption(const std::string &long_name, const std::string &short_name, const std::vector<std::string> &options) : Option(long_name, short_name), options(options) {}
|
||||
|
||||
virtual bool operator==(const Option &rhs) {
|
||||
const ListOption *list_rhs = dynamic_cast<const ListOption *>(&rhs);
|
||||
if(!list_rhs) return false;
|
||||
return long_name == rhs.long_name && short_name == rhs.short_name && options == list_rhs->options;
|
||||
}
|
||||
};
|
||||
|
||||
struct BooleanSelection;
|
||||
|
@ -867,19 +867,19 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
/// The ConfigurationTarget entry point; should configure this meachine as described by @c target.
|
||||
void configure_as_target(const StaticAnalyser::Target &target) override final {
|
||||
void configure_as_target(const Analyser::Static::Target &target) override final {
|
||||
switch(target.amstradcpc.model) {
|
||||
case StaticAnalyser::AmstradCPCModel::CPC464:
|
||||
case Analyser::Static::AmstradCPCModel::CPC464:
|
||||
rom_model_ = ROMType::OS464;
|
||||
has_128k_ = false;
|
||||
has_fdc_ = false;
|
||||
break;
|
||||
case StaticAnalyser::AmstradCPCModel::CPC664:
|
||||
case Analyser::Static::AmstradCPCModel::CPC664:
|
||||
rom_model_ = ROMType::OS664;
|
||||
has_128k_ = false;
|
||||
has_fdc_ = true;
|
||||
break;
|
||||
case StaticAnalyser::AmstradCPCModel::CPC6128:
|
||||
case Analyser::Static::AmstradCPCModel::CPC6128:
|
||||
rom_model_ = ROMType::OS6128;
|
||||
has_128k_ = true;
|
||||
has_fdc_ = true;
|
||||
@ -908,7 +908,7 @@ class ConcreteMachine:
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
bool insert_media(const StaticAnalyser::Media &media) override final {
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
// If there are any tapes supplied, use the first of them.
|
||||
if(!media.tapes.empty()) {
|
||||
tape_player_.set_tape(media.tapes.front());
|
||||
@ -976,8 +976,8 @@ class ConcreteMachine:
|
||||
key_state_.clear_all_keys();
|
||||
}
|
||||
|
||||
KeyboardMapper &get_keyboard_mapper() override {
|
||||
return keyboard_mapper_;
|
||||
KeyboardMapper *get_keyboard_mapper() override {
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -72,34 +72,34 @@ class ConcreteMachine:
|
||||
close_output();
|
||||
}
|
||||
|
||||
void configure_as_target(const StaticAnalyser::Target &target) override {
|
||||
void configure_as_target(const Analyser::Static::Target &target) override {
|
||||
const std::vector<uint8_t> &rom = target.media.cartridges.front()->get_segments().front().data;
|
||||
switch(target.atari.paging_model) {
|
||||
case StaticAnalyser::Atari2600PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge<Cartridge::ActivisionStack>(rom)); break;
|
||||
case StaticAnalyser::Atari2600PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge<Cartridge::CBSRAMPlus>(rom)); break;
|
||||
case StaticAnalyser::Atari2600PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge<Cartridge::CommaVid>(rom)); break;
|
||||
case StaticAnalyser::Atari2600PagingModel::MegaBoy: bus_.reset(new Cartridge::Cartridge<Cartridge::MegaBoy>(rom)); break;
|
||||
case StaticAnalyser::Atari2600PagingModel::MNetwork: bus_.reset(new Cartridge::Cartridge<Cartridge::MNetwork>(rom)); break;
|
||||
case StaticAnalyser::Atari2600PagingModel::None: bus_.reset(new Cartridge::Cartridge<Cartridge::Unpaged>(rom)); break;
|
||||
case StaticAnalyser::Atari2600PagingModel::ParkerBros: bus_.reset(new Cartridge::Cartridge<Cartridge::ParkerBros>(rom)); break;
|
||||
case StaticAnalyser::Atari2600PagingModel::Pitfall2: bus_.reset(new Cartridge::Cartridge<Cartridge::Pitfall2>(rom)); break;
|
||||
case StaticAnalyser::Atari2600PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge<Cartridge::Tigervision>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::ActivisionStack: bus_.reset(new Cartridge::Cartridge<Cartridge::ActivisionStack>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::CBSRamPlus: bus_.reset(new Cartridge::Cartridge<Cartridge::CBSRAMPlus>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::CommaVid: bus_.reset(new Cartridge::Cartridge<Cartridge::CommaVid>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::MegaBoy: bus_.reset(new Cartridge::Cartridge<Cartridge::MegaBoy>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::MNetwork: bus_.reset(new Cartridge::Cartridge<Cartridge::MNetwork>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::None: bus_.reset(new Cartridge::Cartridge<Cartridge::Unpaged>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::ParkerBros: bus_.reset(new Cartridge::Cartridge<Cartridge::ParkerBros>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::Pitfall2: bus_.reset(new Cartridge::Cartridge<Cartridge::Pitfall2>(rom)); break;
|
||||
case Analyser::Static::Atari2600PagingModel::Tigervision: bus_.reset(new Cartridge::Cartridge<Cartridge::Tigervision>(rom)); break;
|
||||
|
||||
case StaticAnalyser::Atari2600PagingModel::Atari8k:
|
||||
case Analyser::Static::Atari2600PagingModel::Atari8k:
|
||||
if(target.atari.uses_superchip) {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8kSuperChip>(rom));
|
||||
} else {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari8k>(rom));
|
||||
}
|
||||
break;
|
||||
case StaticAnalyser::Atari2600PagingModel::Atari16k:
|
||||
case Analyser::Static::Atari2600PagingModel::Atari16k:
|
||||
if(target.atari.uses_superchip) {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16kSuperChip>(rom));
|
||||
} else {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari16k>(rom));
|
||||
}
|
||||
break;
|
||||
case StaticAnalyser::Atari2600PagingModel::Atari32k:
|
||||
case Analyser::Static::Atari2600PagingModel::Atari32k:
|
||||
if(target.atari.uses_superchip) {
|
||||
bus_.reset(new Cartridge::Cartridge<Cartridge::Atari32kSuperChip>(rom));
|
||||
} else {
|
||||
@ -112,7 +112,7 @@ class ConcreteMachine:
|
||||
joysticks_.emplace_back(new Joystick(bus_.get(), 4, 1));
|
||||
}
|
||||
|
||||
bool insert_media(const StaticAnalyser::Media &media) override {
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -130,6 +130,18 @@ class ConcreteMachine:
|
||||
}
|
||||
}
|
||||
|
||||
bool get_switch_is_enabled(Atari2600Switch input) override {
|
||||
uint8_t port_input = bus_->mos6532_.get_port_input(1);
|
||||
switch(input) {
|
||||
case Atari2600SwitchReset: return !!(port_input & 0x01);
|
||||
case Atari2600SwitchSelect: return !!(port_input & 0x02);
|
||||
case Atari2600SwitchColour: return !!(port_input & 0x08);
|
||||
case Atari2600SwitchLeftPlayerDifficulty: return !!(port_input & 0x40);
|
||||
case Atari2600SwitchRightPlayerDifficulty: return !!(port_input & 0x80);
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void set_reset_switch(bool state) override {
|
||||
bus_->set_reset_line(state);
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ class Machine:
|
||||
/// Sets the switch @c input to @c state.
|
||||
virtual void set_switch_is_enabled(Atari2600Switch input, bool state) = 0;
|
||||
|
||||
/// Gets the state of switch @c input.
|
||||
virtual bool get_switch_is_enabled(Atari2600Switch input) = 0;
|
||||
|
||||
// Presses or releases the reset button.
|
||||
virtual void set_reset_switch(bool state) = 0;
|
||||
};
|
||||
|
@ -44,11 +44,15 @@ class Machine: public ROMMachine::Machine {
|
||||
/// Runs the machine for @c cycles.
|
||||
virtual void run_for(const Cycles cycles) = 0;
|
||||
|
||||
/// @returns The confidence that this machine is running content it understands.
|
||||
virtual float get_confidence() { return 0.5f; }
|
||||
virtual void print_type() {}
|
||||
|
||||
// TODO: sever the clock-rate stuff.
|
||||
double get_clock_rate() {
|
||||
virtual double get_clock_rate() {
|
||||
return clock_rate_;
|
||||
}
|
||||
bool get_clock_is_unlimited() {
|
||||
virtual bool get_clock_is_unlimited() {
|
||||
return clock_is_unlimited_;
|
||||
}
|
||||
class Delegate {
|
||||
@ -56,7 +60,7 @@ class Machine: public ROMMachine::Machine {
|
||||
virtual void machine_did_change_clock_rate(Machine *machine) = 0;
|
||||
virtual void machine_did_change_clock_is_unlimited(Machine *machine) = 0;
|
||||
};
|
||||
void set_delegate(Delegate *delegate) { delegate_ = delegate; }
|
||||
virtual void set_delegate(Delegate *delegate) { delegate_ = delegate; }
|
||||
|
||||
protected:
|
||||
void set_clock_rate(double clock_rate) {
|
||||
|
@ -346,19 +346,19 @@ class ConcreteMachine:
|
||||
return true;
|
||||
}
|
||||
|
||||
void configure_as_target(const StaticAnalyser::Target &target) override final {
|
||||
void configure_as_target(const Analyser::Static::Target &target) override final {
|
||||
if(target.loading_command.length()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
|
||||
switch(target.vic20.memory_model) {
|
||||
case StaticAnalyser::Vic20MemoryModel::Unexpanded:
|
||||
case Analyser::Static::Vic20MemoryModel::Unexpanded:
|
||||
set_memory_size(Default);
|
||||
break;
|
||||
case StaticAnalyser::Vic20MemoryModel::EightKB:
|
||||
case Analyser::Static::Vic20MemoryModel::EightKB:
|
||||
set_memory_size(ThreeKB);
|
||||
break;
|
||||
case StaticAnalyser::Vic20MemoryModel::ThirtyTwoKB:
|
||||
case Analyser::Static::Vic20MemoryModel::ThirtyTwoKB:
|
||||
set_memory_size(ThirtyTwoKB);
|
||||
break;
|
||||
}
|
||||
@ -377,7 +377,7 @@ class ConcreteMachine:
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
bool insert_media(const StaticAnalyser::Media &media) override final {
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
if(!media.tapes.empty()) {
|
||||
tape_->set_tape(media.tapes.front());
|
||||
}
|
||||
@ -662,8 +662,8 @@ class ConcreteMachine:
|
||||
keyboard_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !tape->get_input());
|
||||
}
|
||||
|
||||
KeyboardMapper &get_keyboard_mapper() override {
|
||||
return keyboard_mapper_;
|
||||
KeyboardMapper *get_keyboard_mapper() override {
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
|
@ -9,7 +9,7 @@
|
||||
#ifndef ConfigurationTarget_hpp
|
||||
#define ConfigurationTarget_hpp
|
||||
|
||||
#include "../StaticAnalyser/StaticAnalyser.hpp"
|
||||
#include "../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../Configurable/Configurable.hpp"
|
||||
|
||||
#include <string>
|
||||
@ -17,20 +17,20 @@
|
||||
namespace ConfigurationTarget {
|
||||
|
||||
/*!
|
||||
A ConfigurationTarget::Machine is anything that can accept a StaticAnalyser::Target
|
||||
A ConfigurationTarget::Machine is anything that can accept a Analyser::Static::Target
|
||||
and configure itself appropriately, or accept a list of media subsequently to insert.
|
||||
*/
|
||||
class Machine {
|
||||
public:
|
||||
/// Instructs the machine to configure itself as described by @c target and insert the included media.
|
||||
virtual void configure_as_target(const StaticAnalyser::Target &target) = 0;
|
||||
virtual void configure_as_target(const Analyser::Static::Target &target) = 0;
|
||||
|
||||
/*!
|
||||
Requests that the machine insert @c media as a modification to current state
|
||||
|
||||
@returns @c true if any media was inserted; @c false otherwise.
|
||||
*/
|
||||
virtual bool insert_media(const StaticAnalyser::Media &media) = 0;
|
||||
virtual bool insert_media(const Analyser::Static::Media &media) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
48
Machines/DynamicMachine.hpp
Normal file
48
Machines/DynamicMachine.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// DynamicMachine.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 29/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef DynamicMachine_h
|
||||
#define DynamicMachine_h
|
||||
|
||||
#include "../Configurable/Configurable.hpp"
|
||||
#include "ConfigurationTarget.hpp"
|
||||
#include "CRTMachine.hpp"
|
||||
#include "JoystickMachine.hpp"
|
||||
#include "KeyboardMachine.hpp"
|
||||
#include "Utility/Typer.hpp"
|
||||
|
||||
namespace Machine {
|
||||
|
||||
/*!
|
||||
Provides the structure for owning a machine and dynamically casting it as desired without knowledge of
|
||||
the machine's parent class or, therefore, the need to establish a common one.
|
||||
*/
|
||||
struct DynamicMachine {
|
||||
virtual ~DynamicMachine() {}
|
||||
virtual ConfigurationTarget::Machine *configuration_target() = 0;
|
||||
virtual CRTMachine::Machine *crt_machine() = 0;
|
||||
virtual JoystickMachine::Machine *joystick_machine() = 0;
|
||||
virtual KeyboardMachine::Machine *keyboard_machine() = 0;
|
||||
virtual Configurable::Device *configurable_device() = 0;
|
||||
|
||||
/*!
|
||||
Provides a raw pointer to the underlying machine if and only if this dynamic machine really is
|
||||
only a single machine.
|
||||
|
||||
Very unsafe. Very temporary.
|
||||
|
||||
TODO: eliminate in favour of introspection for machine-specific inputs. This is here temporarily
|
||||
only to permit continuity of certain features in the Mac port that have not yet made their way
|
||||
to the SDL/console port.
|
||||
*/
|
||||
virtual void *raw_pointer() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* DynamicMachine_h */
|
@ -119,7 +119,7 @@ class ConcreteMachine:
|
||||
use_fast_tape_hack_ = activate;
|
||||
}
|
||||
|
||||
void configure_as_target(const StaticAnalyser::Target &target) override final {
|
||||
void configure_as_target(const Analyser::Static::Target &target) override final {
|
||||
if(target.loading_command.length()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
@ -143,7 +143,7 @@ class ConcreteMachine:
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
bool insert_media(const StaticAnalyser::Media &media) override final {
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
if(!media.tapes.empty()) {
|
||||
tape_.set_tape(media.tapes.front());
|
||||
}
|
||||
@ -425,8 +425,8 @@ class ConcreteMachine:
|
||||
Utility::TypeRecipient::add_typer(string, std::move(mapper));
|
||||
}
|
||||
|
||||
KeyboardMapper &get_keyboard_mapper() override {
|
||||
return keyboard_mapper_;
|
||||
KeyboardMapper *get_keyboard_mapper() override {
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
|
@ -15,7 +15,7 @@ Machine::Machine() {
|
||||
}
|
||||
|
||||
void Machine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
|
||||
uint16_t mapped_key = get_keyboard_mapper().mapped_key_for_key(key);
|
||||
uint16_t mapped_key = get_keyboard_mapper()->mapped_key_for_key(key);
|
||||
if(mapped_key != KeyNotMapped) set_key_state(mapped_key, is_pressed);
|
||||
}
|
||||
|
||||
@ -30,3 +30,7 @@ Inputs::Keyboard &Machine::get_keyboard() {
|
||||
|
||||
void Machine::type_string(const std::string &) {
|
||||
}
|
||||
|
||||
Machine::KeyboardMapper *Machine::get_keyboard_mapper() {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -61,12 +61,11 @@ class Machine: public Inputs::Keyboard::Delegate {
|
||||
*/
|
||||
static const uint16_t KeyNotMapped = 0xfffe;
|
||||
|
||||
protected:
|
||||
/*!
|
||||
Allows individual machines to provide the mapping between host keys
|
||||
as per Inputs::Keyboard and their native scheme.
|
||||
*/
|
||||
virtual KeyboardMapper &get_keyboard_mapper() = 0;
|
||||
virtual KeyboardMapper *get_keyboard_mapper();
|
||||
|
||||
private:
|
||||
void keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) override;
|
||||
|
@ -19,18 +19,31 @@ class ASCII16kbROMSlotHandler: public ROMSlotHandler {
|
||||
ASCII16kbROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) override {
|
||||
// printf("A16 %04x ", address);
|
||||
switch(address >> 11) {
|
||||
default: break;
|
||||
default:
|
||||
if(pc_is_outside_bios) confidence_counter_.add_miss();
|
||||
break;
|
||||
case 0xc:
|
||||
map_.map(slot_, value * 8192, 0x4000, 0x4000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x6000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x4000, 0x4000, 0x4000);
|
||||
break;
|
||||
case 0xe:
|
||||
map_.map(slot_, value * 8192, 0x8000, 0x4000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x7000 || address == 0x77ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x4000, 0x8000, 0x4000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void print_type() override {
|
||||
printf("A16");
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
|
@ -19,24 +19,43 @@ class ASCII8kbROMSlotHandler: public ROMSlotHandler {
|
||||
ASCII8kbROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) override {
|
||||
// printf("A8 %04x ", address);
|
||||
switch(address >> 11) {
|
||||
default: break;
|
||||
default:
|
||||
if(pc_is_outside_bios) confidence_counter_.add_miss();
|
||||
break;
|
||||
case 0xc:
|
||||
map_.map(slot_, value * 8192, 0x4000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x6000 || address == 0x60ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x4000, 0x2000);
|
||||
break;
|
||||
case 0xd:
|
||||
map_.map(slot_, value * 8192, 0x6000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x6800 || address == 0x68ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x6000, 0x2000);
|
||||
break;
|
||||
case 0xe:
|
||||
map_.map(slot_, value * 8192, 0x8000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x7000 || address == 0x70ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x8000, 0x2000);
|
||||
break;
|
||||
case 0xf:
|
||||
map_.map(slot_, value * 8192, 0xa000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x7800 || address == 0x78ff) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void print_type() override {
|
||||
printf("A8");
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
|
@ -19,21 +19,36 @@ class KonamiROMSlotHandler: public ROMSlotHandler {
|
||||
KonamiROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
||||
map_(map), slot_(slot) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value) {
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) override {
|
||||
// printf("K %04x[%c]\n", address, pc_is_outside_bios ? '.' : '+');
|
||||
switch(address >> 13) {
|
||||
default: break;
|
||||
default:
|
||||
if(pc_is_outside_bios) confidence_counter_.add_miss();
|
||||
break;
|
||||
case 3:
|
||||
map_.map(slot_, value * 8192, 0x6000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x6000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x6000, 0x2000);
|
||||
break;
|
||||
case 4:
|
||||
map_.map(slot_, value * 8192, 0x8000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x8000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x8000, 0x2000);
|
||||
break;
|
||||
case 5:
|
||||
map_.map(slot_, value * 8192, 0xa000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0xa000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void print_type() override {
|
||||
printf("K");
|
||||
}
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
|
@ -20,40 +20,66 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
||||
KonamiWithSCCROMSlotHandler(MSX::MemoryMap &map, int slot, Konami::SCC &scc) :
|
||||
map_(map), slot_(slot), scc_(scc) {}
|
||||
|
||||
void write(uint16_t address, uint8_t value) override {
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) override {
|
||||
// printf("KSCC %04x ", address);
|
||||
switch(address >> 11) {
|
||||
default: break;
|
||||
default:
|
||||
if(pc_is_outside_bios) confidence_counter_.add_miss();
|
||||
break;
|
||||
case 0x0a:
|
||||
map_.map(slot_, value * 8192, 0x4000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x5000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x4000, 0x2000);
|
||||
break;
|
||||
case 0x0e:
|
||||
map_.map(slot_, value * 8192, 0x6000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x7000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0x6000, 0x2000);
|
||||
break;
|
||||
case 0x12:
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0x9000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
if((value&0x3f) == 0x3f) {
|
||||
scc_is_visible_ = true;
|
||||
map_.unmap(slot_, 0x8000, 0x2000);
|
||||
} else {
|
||||
scc_is_visible_ = false;
|
||||
map_.map(slot_, value * 8192, 0x8000, 0x2000);
|
||||
map_.map(slot_, value * 0x2000, 0x8000, 0x2000);
|
||||
}
|
||||
break;
|
||||
case 0x13:
|
||||
if(scc_is_visible_) scc_.write(address, value);
|
||||
if(scc_is_visible_) {
|
||||
if(pc_is_outside_bios) confidence_counter_.add_hit();
|
||||
scc_.write(address, value);
|
||||
} else {
|
||||
if(pc_is_outside_bios) confidence_counter_.add_miss();
|
||||
}
|
||||
break;
|
||||
case 0x16:
|
||||
map_.map(slot_, value * 8192, 0xa000, 0x2000);
|
||||
if(pc_is_outside_bios) {
|
||||
if(address == 0xb000) confidence_counter_.add_hit(); else confidence_counter_.add_equivocal();
|
||||
}
|
||||
map_.map(slot_, value * 0x2000, 0xa000, 0x2000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t read(uint16_t address) override {
|
||||
if(scc_is_visible_ && address >= 0x9800 && address < 0xa000) {
|
||||
confidence_counter_.add_hit();
|
||||
return scc_.read(address);
|
||||
}
|
||||
confidence_counter_.add_miss();
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
virtual void print_type() override {
|
||||
printf("KSCC");
|
||||
}
|
||||
|
||||
private:
|
||||
MSX::MemoryMap &map_;
|
||||
int slot_;
|
||||
|
@ -16,7 +16,7 @@ DiskROM::DiskROM(const std::vector<uint8_t> &rom) :
|
||||
set_is_double_density(true);
|
||||
}
|
||||
|
||||
void DiskROM::write(uint16_t address, uint8_t value) {
|
||||
void DiskROM::write(uint16_t address, uint8_t value, bool pc_is_outside_bios) {
|
||||
switch(address) {
|
||||
case 0x7ff8: case 0x7ff9: case 0x7ffa: case 0x7ffb:
|
||||
set_register(address, value);
|
||||
|
@ -22,7 +22,7 @@ class DiskROM: public ROMSlotHandler, public WD::WD1770 {
|
||||
public:
|
||||
DiskROM(const std::vector<uint8_t> &rom);
|
||||
|
||||
void write(uint16_t address, uint8_t value) override;
|
||||
void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) override;
|
||||
uint8_t read(uint16_t address) override;
|
||||
void run_for(HalfCycles half_cycles) override;
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "Keyboard.hpp"
|
||||
#include "ROMSlotHandler.hpp"
|
||||
|
||||
#include "../../Analyser/Static/MSX/Cartridge.hpp"
|
||||
#include "Cartridges/ASCII8kb.hpp"
|
||||
#include "Cartridges/ASCII16kb.hpp"
|
||||
#include "Cartridges/Konami.hpp"
|
||||
@ -156,7 +157,21 @@ class ConcreteMachine:
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
void configure_as_target(const StaticAnalyser::Target &target) override {
|
||||
float get_confidence() override {
|
||||
if(performed_unmapped_access_ || pc_zero_accesses_ > 1) return 0.0f;
|
||||
if(memory_slots_[1].handler) {
|
||||
return memory_slots_[1].handler->get_confidence();
|
||||
}
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
void print_type() override {
|
||||
if(memory_slots_[1].handler) {
|
||||
memory_slots_[1].handler->print_type();
|
||||
}
|
||||
}
|
||||
|
||||
void configure_as_target(const Analyser::Static::Target &target) override {
|
||||
// Add a disk cartridge if any disks were supplied.
|
||||
if(!target.media.disks.empty()) {
|
||||
map(2, 0, 0x4000, 0x2000);
|
||||
@ -171,30 +186,32 @@ class ConcreteMachine:
|
||||
if(target.loading_command.length()) {
|
||||
type_string(target.loading_command);
|
||||
}
|
||||
|
||||
// Attach the hardware necessary for a game cartridge, if any.
|
||||
switch(target.msx.cartridge_type) {
|
||||
default: break;
|
||||
case StaticAnalyser::MSXCartridgeType::Konami:
|
||||
memory_slots_[1].set_handler(new Cartridge::KonamiROMSlotHandler(*this, 1));
|
||||
break;
|
||||
case StaticAnalyser::MSXCartridgeType::KonamiWithSCC:
|
||||
memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1, scc_));
|
||||
break;
|
||||
case StaticAnalyser::MSXCartridgeType::ASCII8kb:
|
||||
memory_slots_[1].set_handler(new Cartridge::ASCII8kbROMSlotHandler(*this, 1));
|
||||
break;
|
||||
case StaticAnalyser::MSXCartridgeType::ASCII16kb:
|
||||
memory_slots_[1].set_handler(new Cartridge::ASCII16kbROMSlotHandler(*this, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool insert_media(const StaticAnalyser::Media &media) override {
|
||||
bool insert_media(const Analyser::Static::Media &media) override {
|
||||
if(!media.cartridges.empty()) {
|
||||
const auto &segment = media.cartridges.front()->get_segments().front();
|
||||
memory_slots_[1].source = segment.data;
|
||||
map(1, 0, static_cast<uint16_t>(segment.start_address), std::min(segment.data.size(), 65536 - segment.start_address));
|
||||
|
||||
auto msx_cartridge = dynamic_cast<Analyser::Static::MSX::Cartridge *>(media.cartridges.front().get());
|
||||
if(msx_cartridge) {
|
||||
switch(msx_cartridge->type) {
|
||||
default: break;
|
||||
case Analyser::Static::MSX::Cartridge::Konami:
|
||||
memory_slots_[1].set_handler(new Cartridge::KonamiROMSlotHandler(*this, 1));
|
||||
break;
|
||||
case Analyser::Static::MSX::Cartridge::KonamiWithSCC:
|
||||
memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1, scc_));
|
||||
break;
|
||||
case Analyser::Static::MSX::Cartridge::ASCII8kb:
|
||||
memory_slots_[1].set_handler(new Cartridge::ASCII8kbROMSlotHandler(*this, 1));
|
||||
break;
|
||||
case Analyser::Static::MSX::Cartridge::ASCII16kb:
|
||||
memory_slots_[1].set_handler(new Cartridge::ASCII16kbROMSlotHandler(*this, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!media.tapes.empty()) {
|
||||
@ -324,6 +341,14 @@ class ConcreteMachine:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!address) {
|
||||
pc_zero_accesses_++;
|
||||
}
|
||||
if(read_pointers_[address >> 13] == unpopulated_) {
|
||||
performed_unmapped_access_ = true;
|
||||
}
|
||||
pc_address_ = address; // This is retained so as to be able to name the source of an access to cartridge handlers.
|
||||
case CPU::Z80::PartialMachineCycle::Read:
|
||||
if(read_pointers_[address >> 13]) {
|
||||
*cycle.value = read_pointers_[address >> 13][address & 8191];
|
||||
@ -341,7 +366,7 @@ class ConcreteMachine:
|
||||
if(memory_slots_[slot_hit].handler) {
|
||||
update_audio();
|
||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
||||
memory_slots_[slot_hit].handler->write(address, *cycle.value);
|
||||
memory_slots_[slot_hit].handler->write(address, *cycle.value, read_pointers_[pc_address_ >> 13] != memory_slots_[0].read_pointers[pc_address_ >> 13]);
|
||||
}
|
||||
} break;
|
||||
|
||||
@ -505,8 +530,8 @@ class ConcreteMachine:
|
||||
if(is_pressed) key_states_[line] &= ~mask; else key_states_[line] |= mask;
|
||||
}
|
||||
|
||||
KeyboardMapper &get_keyboard_mapper() override {
|
||||
return keyboard_mapper_;
|
||||
KeyboardMapper *get_keyboard_mapper() override {
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
@ -637,6 +662,10 @@ class ConcreteMachine:
|
||||
std::string input_text_;
|
||||
|
||||
MSX::KeyboardMapper keyboard_mapper_;
|
||||
|
||||
int pc_zero_accesses_ = 0;
|
||||
bool performed_unmapped_access_ = false;
|
||||
uint16_t pc_address_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define ROMSlotHandler_hpp
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../Analyser/Dynamic/ConfidenceCounter.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@ -46,7 +47,7 @@ class ROMSlotHandler {
|
||||
virtual void run_for(HalfCycles half_cycles) {}
|
||||
|
||||
/*! Announces an attempt to write @c value to @c address. */
|
||||
virtual void write(uint16_t address, uint8_t value) = 0;
|
||||
virtual void write(uint16_t address, uint8_t value, bool pc_is_outside_bios) = 0;
|
||||
|
||||
/*! Seeks the result of a read at @c address; this is used only if the area is unmapped. */
|
||||
virtual uint8_t read(uint16_t address) { return 0xff; }
|
||||
@ -57,12 +58,22 @@ class ROMSlotHandler {
|
||||
/// Empty causes all out-of-bounds accesses to read a vacant bus.
|
||||
Empty
|
||||
};
|
||||
/*!
|
||||
Returns the wrapping strategy to apply to mapping requests from this ROM slot.
|
||||
*/
|
||||
|
||||
/*! @returns The wrapping strategy to apply to mapping requests from this ROM slot. */
|
||||
virtual WrappingStrategy wrapping_strategy() const {
|
||||
return WrappingStrategy::Repeat;
|
||||
}
|
||||
|
||||
/*! @returns The probability that this handler is correct for the data it owns. */
|
||||
float get_confidence() {
|
||||
return confidence_counter_.get_confidence();
|
||||
}
|
||||
|
||||
virtual void print_type() {
|
||||
}
|
||||
|
||||
protected:
|
||||
Analyser::Dynamic::ConfidenceCounter confidence_counter_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// to satisfy ConfigurationTarget::Machine
|
||||
void configure_as_target(const StaticAnalyser::Target &target) override final {
|
||||
void configure_as_target(const Analyser::Static::Target &target) override final {
|
||||
if(target.oric.has_microdisc) {
|
||||
microdisc_is_enabled_ = true;
|
||||
microdisc_did_change_paging_flags(µdisc_);
|
||||
@ -284,7 +284,7 @@ class ConcreteMachine:
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
bool insert_media(const StaticAnalyser::Media &media) override final {
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
if(media.tapes.size()) {
|
||||
tape_player_.set_tape(media.tapes.front());
|
||||
}
|
||||
@ -432,8 +432,8 @@ class ConcreteMachine:
|
||||
set_interrupt_line();
|
||||
}
|
||||
|
||||
KeyboardMapper &get_keyboard_mapper() override {
|
||||
return keyboard_mapper_;
|
||||
KeyboardMapper *get_keyboard_mapper() override {
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
|
@ -15,11 +15,13 @@
|
||||
|
||||
namespace ROMMachine {
|
||||
|
||||
typedef std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> ROMFetcher;
|
||||
|
||||
struct Machine {
|
||||
/*!
|
||||
Provides the machine with a way to obtain such ROMs as it needs.
|
||||
*/
|
||||
virtual bool set_rom_fetcher(const std::function<std::vector<std::unique_ptr<std::vector<uint8_t>>>(const std::string &machine, const std::vector<std::string> &names)> &rom_with_name) { return true; }
|
||||
virtual bool set_rom_fetcher(const ROMFetcher &rom_with_name) { return true; }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,45 +16,97 @@
|
||||
#include "../Oric/Oric.hpp"
|
||||
#include "../ZX8081/ZX8081.hpp"
|
||||
|
||||
#include "../../Analyser/Dynamic/MultiMachine/MultiMachine.hpp"
|
||||
#include "TypedDynamicMachine.hpp"
|
||||
|
||||
::Machine::DynamicMachine *::Machine::MachineForTarget(const StaticAnalyser::Target &target) {
|
||||
switch(target.machine) {
|
||||
case StaticAnalyser::Target::AmstradCPC: return new TypedDynamicMachine<AmstradCPC::Machine>(AmstradCPC::Machine::AmstradCPC());
|
||||
case StaticAnalyser::Target::Atari2600: return new TypedDynamicMachine<Atari2600::Machine>(Atari2600::Machine::Atari2600());
|
||||
case StaticAnalyser::Target::Electron: return new TypedDynamicMachine<Electron::Machine>(Electron::Machine::Electron());
|
||||
case StaticAnalyser::Target::MSX: return new TypedDynamicMachine<MSX::Machine>(MSX::Machine::MSX());
|
||||
case StaticAnalyser::Target::Oric: return new TypedDynamicMachine<Oric::Machine>(Oric::Machine::Oric());
|
||||
case StaticAnalyser::Target::Vic20: return new TypedDynamicMachine<Commodore::Vic20::Machine>(Commodore::Vic20::Machine::Vic20());
|
||||
case StaticAnalyser::Target::ZX8081: return new TypedDynamicMachine<ZX8081::Machine>(ZX8081::Machine::ZX8081(target));
|
||||
namespace {
|
||||
|
||||
default: return nullptr;
|
||||
::Machine::DynamicMachine *MachineForTarget(const Analyser::Static::Target &target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::Error &error) {
|
||||
error = Machine::Error::None;
|
||||
::Machine::DynamicMachine *machine = nullptr;
|
||||
switch(target.machine) {
|
||||
case Analyser::Machine::AmstradCPC: machine = new Machine::TypedDynamicMachine<AmstradCPC::Machine>(AmstradCPC::Machine::AmstradCPC()); break;
|
||||
case Analyser::Machine::Atari2600: machine = new Machine::TypedDynamicMachine<Atari2600::Machine>(Atari2600::Machine::Atari2600()); break;
|
||||
case Analyser::Machine::Electron: machine = new Machine::TypedDynamicMachine<Electron::Machine>(Electron::Machine::Electron()); break;
|
||||
case Analyser::Machine::MSX: machine = new Machine::TypedDynamicMachine<MSX::Machine>(MSX::Machine::MSX()); break;
|
||||
case Analyser::Machine::Oric: machine = new Machine::TypedDynamicMachine<Oric::Machine>(Oric::Machine::Oric()); break;
|
||||
case Analyser::Machine::Vic20: machine = new Machine::TypedDynamicMachine<Commodore::Vic20::Machine>(Commodore::Vic20::Machine::Vic20()); break;
|
||||
case Analyser::Machine::ZX8081: machine = new Machine::TypedDynamicMachine<ZX8081::Machine>(ZX8081::Machine::ZX8081(target)); break;
|
||||
|
||||
default:
|
||||
error = Machine::Error::UnknownMachine;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: this shouldn't depend on CRT machine's inclusion of ROM machine.
|
||||
CRTMachine::Machine *crt_machine = machine->crt_machine();
|
||||
if(crt_machine) {
|
||||
if(!machine->crt_machine()->set_rom_fetcher(rom_fetcher)) {
|
||||
delete machine;
|
||||
error = Machine::Error::MissingROM;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurationTarget::Machine *configuration_target = machine->configuration_target();
|
||||
if(configuration_target) {
|
||||
machine->configuration_target()->configure_as_target(target);
|
||||
}
|
||||
|
||||
return machine;
|
||||
}
|
||||
|
||||
std::string Machine::ShortNameForTargetMachine(const StaticAnalyser::Target::Machine machine) {
|
||||
}
|
||||
|
||||
::Machine::DynamicMachine *::Machine::MachineForTargets(const std::vector<std::unique_ptr<Analyser::Static::Target>> &targets, const ROMMachine::ROMFetcher &rom_fetcher, Error &error) {
|
||||
// Zero targets implies no machine.
|
||||
if(targets.empty()) {
|
||||
error = Error::NoTargets;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If there's more than one target, get all the machines and combine them into a multimachine.
|
||||
if(targets.size() > 1) {
|
||||
std::vector<std::unique_ptr<Machine::DynamicMachine>> machines;
|
||||
for(const auto &target: targets) {
|
||||
machines.emplace_back(MachineForTarget(*target, rom_fetcher, error));
|
||||
|
||||
// Exit early if any errors have occurred.
|
||||
if(error != Error::None) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return new Analyser::Dynamic::MultiMachine(std::move(machines));
|
||||
}
|
||||
|
||||
// There's definitely exactly one target.
|
||||
return MachineForTarget(*targets.front(), rom_fetcher, error);
|
||||
}
|
||||
|
||||
std::string Machine::ShortNameForTargetMachine(const Analyser::Machine machine) {
|
||||
switch(machine) {
|
||||
case StaticAnalyser::Target::AmstradCPC: return "AmstradCPC";
|
||||
case StaticAnalyser::Target::Atari2600: return "Atari2600";
|
||||
case StaticAnalyser::Target::Electron: return "Electron";
|
||||
case StaticAnalyser::Target::MSX: return "MSX";
|
||||
case StaticAnalyser::Target::Oric: return "Oric";
|
||||
case StaticAnalyser::Target::Vic20: return "Vic20";
|
||||
case StaticAnalyser::Target::ZX8081: return "ZX8081";
|
||||
case Analyser::Machine::AmstradCPC: return "AmstradCPC";
|
||||
case Analyser::Machine::Atari2600: return "Atari2600";
|
||||
case Analyser::Machine::Electron: return "Electron";
|
||||
case Analyser::Machine::MSX: return "MSX";
|
||||
case Analyser::Machine::Oric: return "Oric";
|
||||
case Analyser::Machine::Vic20: return "Vic20";
|
||||
case Analyser::Machine::ZX8081: return "ZX8081";
|
||||
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string Machine::LongNameForTargetMachine(StaticAnalyser::Target::Machine machine) {
|
||||
std::string Machine::LongNameForTargetMachine(Analyser::Machine machine) {
|
||||
switch(machine) {
|
||||
case StaticAnalyser::Target::AmstradCPC: return "Amstrad CPC";
|
||||
case StaticAnalyser::Target::Atari2600: return "Atari 2600";
|
||||
case StaticAnalyser::Target::Electron: return "Acorn Electron";
|
||||
case StaticAnalyser::Target::MSX: return "MSX";
|
||||
case StaticAnalyser::Target::Oric: return "Oric";
|
||||
case StaticAnalyser::Target::Vic20: return "Vic 20";
|
||||
case StaticAnalyser::Target::ZX8081: return "ZX80/81";
|
||||
case Analyser::Machine::AmstradCPC: return "Amstrad CPC";
|
||||
case Analyser::Machine::Atari2600: return "Atari 2600";
|
||||
case Analyser::Machine::Electron: return "Acorn Electron";
|
||||
case Analyser::Machine::MSX: return "MSX";
|
||||
case Analyser::Machine::Oric: return "Oric";
|
||||
case Analyser::Machine::Vic20: return "Vic 20";
|
||||
case Analyser::Machine::ZX8081: return "ZX80/81";
|
||||
|
||||
default: return "";
|
||||
}
|
||||
@ -63,11 +115,11 @@ std::string Machine::LongNameForTargetMachine(StaticAnalyser::Target::Machine ma
|
||||
std::map<std::string, std::vector<std::unique_ptr<Configurable::Option>>> Machine::AllOptionsByMachineName() {
|
||||
std::map<std::string, std::vector<std::unique_ptr<Configurable::Option>>> options;
|
||||
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(StaticAnalyser::Target::Electron), Electron::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(StaticAnalyser::Target::MSX), MSX::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(StaticAnalyser::Target::Oric), Oric::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(StaticAnalyser::Target::Vic20), Commodore::Vic20::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(StaticAnalyser::Target::ZX8081), ZX8081::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Electron), Electron::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::MSX), MSX::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Oric), Oric::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Vic20), Commodore::Vic20::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::ZX8081), ZX8081::get_options()));
|
||||
|
||||
return options;
|
||||
}
|
||||
|
@ -9,52 +9,42 @@
|
||||
#ifndef MachineForTarget_hpp
|
||||
#define MachineForTarget_hpp
|
||||
|
||||
#include "../../StaticAnalyser/StaticAnalyser.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../JoystickMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
#include "Typer.hpp"
|
||||
#include "../DynamicMachine.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Machine {
|
||||
|
||||
/*!
|
||||
Provides the structure for owning a machine and dynamically casting it as desired without knowledge of
|
||||
the machine's parent class or, therefore, the need to establish a common one.
|
||||
*/
|
||||
struct DynamicMachine {
|
||||
virtual ConfigurationTarget::Machine *configuration_target() = 0;
|
||||
virtual CRTMachine::Machine *crt_machine() = 0;
|
||||
virtual JoystickMachine::Machine *joystick_machine() = 0;
|
||||
virtual KeyboardMachine::Machine *keyboard_machine() = 0;
|
||||
virtual Configurable::Device *configurable_device() = 0;
|
||||
virtual Utility::TypeRecipient *type_recipient() = 0;
|
||||
enum class Error {
|
||||
None,
|
||||
UnknownMachine,
|
||||
MissingROM,
|
||||
NoTargets
|
||||
};
|
||||
|
||||
/*!
|
||||
Allocates an instance of DynamicMachine holding a machine that can
|
||||
receive the supplied target. The machine has been allocated on the heap.
|
||||
It is the caller's responsibility to delete the class when finished.
|
||||
receive the supplied static analyser result. The machine has been allocated
|
||||
on the heap. It is the caller's responsibility to delete the class when finished.
|
||||
*/
|
||||
DynamicMachine *MachineForTarget(const StaticAnalyser::Target &target);
|
||||
DynamicMachine *MachineForTargets(const std::vector<std::unique_ptr<Analyser::Static::Target>> &targets, const ::ROMMachine::ROMFetcher &rom_fetcher, Error &error);
|
||||
|
||||
/*!
|
||||
Returns a short string name for the machine identified by the target,
|
||||
which is guaranteed not to have any spaces or other potentially
|
||||
filesystem-bothering contents.
|
||||
*/
|
||||
std::string ShortNameForTargetMachine(const StaticAnalyser::Target::Machine target);
|
||||
std::string ShortNameForTargetMachine(const Analyser::Machine target);
|
||||
|
||||
/*!
|
||||
Returns a long string name for the machine identified by the target,
|
||||
usable for presentation to a human.
|
||||
*/
|
||||
std::string LongNameForTargetMachine(const StaticAnalyser::Target::Machine target);
|
||||
std::string LongNameForTargetMachine(const Analyser::Machine target);
|
||||
|
||||
/*!
|
||||
Returns a map from machine name to the list of options that machine
|
||||
|
@ -45,8 +45,8 @@ template<typename T> class TypedDynamicMachine: public ::Machine::DynamicMachine
|
||||
return get<Configurable::Device>();
|
||||
}
|
||||
|
||||
Utility::TypeRecipient *type_recipient() override {
|
||||
return get<Utility::TypeRecipient>();
|
||||
void *raw_pointer() override {
|
||||
return get();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -238,7 +238,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
z80_.run_for(cycles);
|
||||
}
|
||||
|
||||
void configure_as_target(const StaticAnalyser::Target &target) override final {
|
||||
void configure_as_target(const Analyser::Static::Target &target) override final {
|
||||
is_zx81_ = target.zx8081.isZX81;
|
||||
if(is_zx81_) {
|
||||
rom_ = zx81_rom_;
|
||||
@ -260,17 +260,17 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
rom_mask_ = static_cast<uint16_t>(rom_.size() - 1);
|
||||
|
||||
switch(target.zx8081.memory_model) {
|
||||
case StaticAnalyser::ZX8081MemoryModel::Unexpanded:
|
||||
case Analyser::Static::ZX8081MemoryModel::Unexpanded:
|
||||
ram_.resize(1024);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 1023;
|
||||
break;
|
||||
case StaticAnalyser::ZX8081MemoryModel::SixteenKB:
|
||||
case Analyser::Static::ZX8081MemoryModel::SixteenKB:
|
||||
ram_.resize(16384);
|
||||
ram_base_ = 16384;
|
||||
ram_mask_ = 16383;
|
||||
break;
|
||||
case StaticAnalyser::ZX8081MemoryModel::SixtyFourKB:
|
||||
case Analyser::Static::ZX8081MemoryModel::SixtyFourKB:
|
||||
ram_.resize(65536);
|
||||
ram_base_ = 8192;
|
||||
ram_mask_ = 65535;
|
||||
@ -285,7 +285,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
insert_media(target.media);
|
||||
}
|
||||
|
||||
bool insert_media(const StaticAnalyser::Media &media) override final {
|
||||
bool insert_media(const Analyser::Static::Media &media) override final {
|
||||
if(!media.tapes.empty()) {
|
||||
tape_player_.set_tape(media.tapes.front());
|
||||
}
|
||||
@ -341,16 +341,21 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
tape_player_.set_motor_control(false);
|
||||
}
|
||||
}
|
||||
|
||||
void set_tape_is_playing(bool is_playing) override final {
|
||||
tape_player_.set_motor_control(is_playing);
|
||||
}
|
||||
|
||||
bool get_tape_is_playing() override final {
|
||||
return tape_player_.get_motor_control();
|
||||
}
|
||||
|
||||
// MARK: - Typer timing
|
||||
HalfCycles get_typer_delay() override final { return Cycles(7000000); }
|
||||
HalfCycles get_typer_frequency() override final { return Cycles(390000); }
|
||||
|
||||
KeyboardMapper &get_keyboard_mapper() override {
|
||||
return keyboard_mapper_;
|
||||
KeyboardMapper *get_keyboard_mapper() override {
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
@ -442,7 +447,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
using namespace ZX8081;
|
||||
|
||||
// See header; constructs and returns an instance of the ZX80 or 81.
|
||||
Machine *Machine::ZX8081(const StaticAnalyser::Target &target_hint) {
|
||||
Machine *Machine::ZX8081(const Analyser::Static::Target &target_hint) {
|
||||
// Instantiate the correct type of machine.
|
||||
if(target_hint.zx8081.isZX81)
|
||||
return new ZX8081::ConcreteMachine<true>();
|
||||
|
@ -27,9 +27,10 @@ class Machine:
|
||||
public:
|
||||
virtual ~Machine();
|
||||
|
||||
static Machine *ZX8081(const StaticAnalyser::Target &target_hint);
|
||||
static Machine *ZX8081(const Analyser::Static::Target &target_hint);
|
||||
|
||||
virtual void set_tape_is_playing(bool is_playing) = 0;
|
||||
virtual bool get_tape_is_playing() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -79,6 +79,7 @@
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
stopOnEveryThreadSanitizerIssue = "YES"
|
||||
stopOnEveryUBSanitizerIssue = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "NO">
|
||||
|
@ -25,8 +25,7 @@ static NSLock *CSAudioQueueDeallocLock;
|
||||
@implementation CSWeakAudioQueuePointer
|
||||
@end
|
||||
|
||||
@implementation CSAudioQueue
|
||||
{
|
||||
@implementation CSAudioQueue {
|
||||
AudioQueueRef _audioQueue;
|
||||
AudioQueueBufferRef _storedBuffers[NumberOfStoredAudioQueueBuffer];
|
||||
NSLock *_storedBuffersLock;
|
||||
@ -38,13 +37,10 @@ static NSLock *CSAudioQueueDeallocLock;
|
||||
/*!
|
||||
@returns @c YES if the queue is running dry; @c NO otherwise.
|
||||
*/
|
||||
- (BOOL)audioQueue:(AudioQueueRef)theAudioQueue didCallbackWithBuffer:(AudioQueueBufferRef)buffer
|
||||
{
|
||||
- (BOOL)audioQueue:(AudioQueueRef)theAudioQueue didCallbackWithBuffer:(AudioQueueBufferRef)buffer {
|
||||
[_storedBuffersLock lock];
|
||||
for(int c = 0; c < NumberOfStoredAudioQueueBuffer; c++)
|
||||
{
|
||||
if(!_storedBuffers[c] || buffer->mAudioDataBytesCapacity > _storedBuffers[c]->mAudioDataBytesCapacity)
|
||||
{
|
||||
for(int c = 0; c < NumberOfStoredAudioQueueBuffer; c++) {
|
||||
if(!_storedBuffers[c] || buffer->mAudioDataBytesCapacity > _storedBuffers[c]->mAudioDataBytesCapacity) {
|
||||
if(_storedBuffers[c]) AudioQueueFreeBuffer(_audioQueue, _storedBuffers[c]);
|
||||
_storedBuffers[c] = buffer;
|
||||
[_storedBuffersLock unlock];
|
||||
@ -59,12 +55,10 @@ static NSLock *CSAudioQueueDeallocLock;
|
||||
static void audioOutputCallback(
|
||||
void *inUserData,
|
||||
AudioQueueRef inAQ,
|
||||
AudioQueueBufferRef inBuffer)
|
||||
{
|
||||
AudioQueueBufferRef inBuffer) {
|
||||
// Pull the delegate call for audio queue running dry outside of the locked region, to allow non-deadlocking
|
||||
// lifecycle -dealloc events to result from it.
|
||||
if([CSAudioQueueDeallocLock tryLock])
|
||||
{
|
||||
if([CSAudioQueueDeallocLock tryLock]) {
|
||||
CSAudioQueue *queue = ((__bridge CSWeakAudioQueuePointer *)inUserData).queue;
|
||||
BOOL isRunningDry = NO;
|
||||
isRunningDry = [queue audioQueue:inAQ didCallbackWithBuffer:inBuffer];
|
||||
@ -76,14 +70,11 @@ static void audioOutputCallback(
|
||||
|
||||
#pragma mark - Standard object lifecycle
|
||||
|
||||
- (instancetype)initWithSamplingRate:(Float64)samplingRate
|
||||
{
|
||||
- (instancetype)initWithSamplingRate:(Float64)samplingRate {
|
||||
self = [super init];
|
||||
|
||||
if(self)
|
||||
{
|
||||
if(!CSAudioQueueDeallocLock)
|
||||
{
|
||||
if(self) {
|
||||
if(!CSAudioQueueDeallocLock) {
|
||||
CSAudioQueueDeallocLock = [[NSLock alloc] init];
|
||||
}
|
||||
_storedBuffersLock = [[NSLock alloc] init];
|
||||
@ -122,8 +113,7 @@ static void audioOutputCallback(
|
||||
NULL,
|
||||
kCFRunLoopCommonModes,
|
||||
0,
|
||||
&_audioQueue))
|
||||
{
|
||||
&_audioQueue)) {
|
||||
AudioQueueStart(_audioQueue, NULL);
|
||||
}
|
||||
}
|
||||
@ -131,16 +121,13 @@ static void audioOutputCallback(
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
- (instancetype)init {
|
||||
return [self initWithSamplingRate:[[self class] preferredSamplingRate]];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[CSAudioQueueDeallocLock lock];
|
||||
if(_audioQueue)
|
||||
{
|
||||
if(_audioQueue) {
|
||||
AudioQueueDispose(_audioQueue, true);
|
||||
_audioQueue = NULL;
|
||||
}
|
||||
@ -167,15 +154,12 @@ static void audioOutputCallback(
|
||||
|
||||
#pragma mark - Audio enqueuer
|
||||
|
||||
- (void)enqueueAudioBuffer:(const int16_t *)buffer numberOfSamples:(size_t)lengthInSamples
|
||||
{
|
||||
- (void)enqueueAudioBuffer:(const int16_t *)buffer numberOfSamples:(size_t)lengthInSamples {
|
||||
size_t bufferBytes = lengthInSamples * sizeof(int16_t);
|
||||
|
||||
[_storedBuffersLock lock];
|
||||
for(int c = 0; c < NumberOfStoredAudioQueueBuffer; c++)
|
||||
{
|
||||
if(_storedBuffers[c] && _storedBuffers[c]->mAudioDataBytesCapacity >= bufferBytes)
|
||||
{
|
||||
for(int c = 0; c < NumberOfStoredAudioQueueBuffer; c++) {
|
||||
if(_storedBuffers[c] && _storedBuffers[c]->mAudioDataBytesCapacity >= bufferBytes) {
|
||||
memcpy(_storedBuffers[c]->mAudioData, buffer, bufferBytes);
|
||||
_storedBuffers[c]->mAudioDataByteSize = (UInt32)bufferBytes;
|
||||
|
||||
@ -197,8 +181,7 @@ static void audioOutputCallback(
|
||||
|
||||
#pragma mark - Sampling Rate getters
|
||||
|
||||
+ (AudioDeviceID)defaultOutputDevice
|
||||
{
|
||||
+ (AudioDeviceID)defaultOutputDevice {
|
||||
AudioObjectPropertyAddress address;
|
||||
address.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||
address.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
@ -209,8 +192,7 @@ static void audioOutputCallback(
|
||||
return AudioObjectGetPropertyData(kAudioObjectSystemObject, &address, sizeof(AudioObjectPropertyAddress), NULL, &size, &deviceID) ? 0 : deviceID;
|
||||
}
|
||||
|
||||
+ (Float64)preferredSamplingRate
|
||||
{
|
||||
+ (Float64)preferredSamplingRate {
|
||||
AudioObjectPropertyAddress address;
|
||||
address.mSelector = kAudioDevicePropertyNominalSampleRate;
|
||||
address.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
|
@ -6,8 +6,6 @@
|
||||
#import "CSFastLoading.h"
|
||||
|
||||
#import "CSAtari2600.h"
|
||||
#import "CSElectron.h"
|
||||
#import "CSOric.h"
|
||||
#import "CSVic20.h"
|
||||
#import "CSZX8081.h"
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
class Atari2600OptionsPanel: MachinePanel {
|
||||
var atari2600: CSAtari2600! {
|
||||
get {
|
||||
return self.machine as! CSAtari2600
|
||||
return self.machine.atari2600
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,10 @@ class MachineDocument:
|
||||
CSBestEffortUpdaterDelegate,
|
||||
CSAudioQueueDelegate
|
||||
{
|
||||
lazy var actionLock = NSLock()
|
||||
lazy var drawLock = NSLock()
|
||||
fileprivate let actionLock = NSLock()
|
||||
fileprivate let drawLock = NSLock()
|
||||
fileprivate let bestEffortLock = NSLock()
|
||||
|
||||
var machine: CSMachine!
|
||||
var name: String! {
|
||||
get {
|
||||
@ -77,7 +79,9 @@ class MachineDocument:
|
||||
}
|
||||
|
||||
func machineDidChangeClockIsUnlimited(_ machine: CSMachine!) {
|
||||
bestEffortLock.lock()
|
||||
self.bestEffortUpdater?.runAsUnlimited = machine.clockIsUnlimited
|
||||
bestEffortLock.unlock()
|
||||
}
|
||||
|
||||
fileprivate func setupClockRate() {
|
||||
@ -91,20 +95,24 @@ class MachineDocument:
|
||||
self.machine.setAudioSamplingRate(selectedSamplingRate, bufferSize:audioQueue.preferredBufferSize)
|
||||
}
|
||||
|
||||
bestEffortLock.lock()
|
||||
self.bestEffortUpdater?.clockRate = self.machine.clockRate
|
||||
bestEffortLock.unlock()
|
||||
}
|
||||
|
||||
override func close() {
|
||||
optionsPanel?.setIsVisible(false)
|
||||
optionsPanel = nil
|
||||
|
||||
openGLView.delegate = nil
|
||||
bestEffortLock.lock()
|
||||
bestEffortUpdater!.delegate = nil
|
||||
bestEffortUpdater = nil
|
||||
bestEffortLock.unlock()
|
||||
|
||||
actionLock.lock()
|
||||
drawLock.lock()
|
||||
machine = nil
|
||||
openGLView.delegate = nil
|
||||
openGLView.invalidate()
|
||||
actionLock.unlock()
|
||||
drawLock.unlock()
|
||||
@ -114,10 +122,9 @@ class MachineDocument:
|
||||
|
||||
// MARK: configuring
|
||||
func configureAs(_ analysis: CSStaticAnalyser) {
|
||||
if let machine = analysis.newMachine() {
|
||||
if let machine = CSMachine(analyser: analysis) {
|
||||
self.machine = machine
|
||||
}
|
||||
analysis.apply(to: self.machine)
|
||||
|
||||
if let optionsPanelNibName = analysis.optionsPanelNibName {
|
||||
Bundle.main.loadNibNamed(NSNib.Name(rawValue: optionsPanelNibName), owner: self, topLevelObjects: nil)
|
||||
@ -149,6 +156,7 @@ class MachineDocument:
|
||||
}
|
||||
|
||||
func runForNumberOfCycles(_ numberOfCycles: Int32) {
|
||||
bestEffortLock.lock()
|
||||
if let bestEffortUpdater = bestEffortUpdater {
|
||||
let cyclesToRunFor = min(numberOfCycles, Int32(bestEffortUpdater.clockRate / 10))
|
||||
if actionLock.try() {
|
||||
@ -156,15 +164,19 @@ class MachineDocument:
|
||||
actionLock.unlock()
|
||||
}
|
||||
}
|
||||
bestEffortLock.unlock()
|
||||
}
|
||||
|
||||
// MARK: CSAudioQueueDelegate
|
||||
final func audioQueueIsRunningDry(_ audioQueue: CSAudioQueue) {
|
||||
bestEffortLock.lock()
|
||||
bestEffortUpdater?.update()
|
||||
bestEffortLock.unlock()
|
||||
}
|
||||
|
||||
// MARK: CSOpenGLViewDelegate
|
||||
final func openGLView(_ view: CSOpenGLView, drawViewOnlyIfDirty onlyIfDirty: Bool) {
|
||||
bestEffortLock.lock()
|
||||
if let bestEffortUpdater = bestEffortUpdater {
|
||||
bestEffortUpdater.update()
|
||||
if drawLock.try() {
|
||||
@ -172,6 +184,7 @@ class MachineDocument:
|
||||
drawLock.unlock()
|
||||
}
|
||||
}
|
||||
bestEffortLock.unlock()
|
||||
}
|
||||
|
||||
final func openGLView(_ view: CSOpenGLView, didReceiveFileAt URL: URL) {
|
||||
|
@ -9,7 +9,7 @@
|
||||
class ZX8081OptionsPanel: MachinePanel {
|
||||
var zx8081: CSZX8081! {
|
||||
get {
|
||||
return self.machine as! CSZX8081
|
||||
return self.machine.zx8081
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ class ZX8081OptionsPanel: MachinePanel {
|
||||
let isEnabled = sender.state == .on
|
||||
UserDefaults.standard.set(isEnabled, forKey: self.automaticTapeMotorControlDefaultsKey)
|
||||
self.playOrPauseTapeButton.isEnabled = !isEnabled
|
||||
self.zx8081.useAutomaticTapeMotorControl = isEnabled
|
||||
self.machine.useAutomaticTapeMotorControl = isEnabled
|
||||
}
|
||||
|
||||
@IBOutlet var playOrPauseTapeButton: NSButton!
|
||||
@ -44,6 +44,6 @@ class ZX8081OptionsPanel: MachinePanel {
|
||||
let automaticTapeMotorControlIsEnabled = standardUserDefaults.bool(forKey: self.automaticTapeMotorControlDefaultsKey)
|
||||
self.automaticTapeMotorControlButton.state = automaticTapeMotorControlIsEnabled ? .on : .off
|
||||
self.playOrPauseTapeButton.isEnabled = !automaticTapeMotorControlIsEnabled
|
||||
self.zx8081.useAutomaticTapeMotorControl = automaticTapeMotorControlIsEnabled
|
||||
self.machine.useAutomaticTapeMotorControl = automaticTapeMotorControlIsEnabled
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
//
|
||||
// CSMachine+Subclassing.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 04/01/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CSMachine.h"
|
||||
#include "CRTMachine.hpp"
|
||||
|
||||
@interface CSMachine (Subclassing)
|
||||
|
||||
- (void)setupOutputWithAspectRatio:(float)aspectRatio;
|
||||
|
||||
@end
|
@ -12,7 +12,6 @@
|
||||
|
||||
@interface CSMachine(Target)
|
||||
|
||||
- (void)applyTarget:(const StaticAnalyser::Target &)target;
|
||||
- (void)applyMedia:(const StaticAnalyser::Media &)media;
|
||||
- (void)applyMedia:(const Analyser::Static::Media &)media;
|
||||
|
||||
@end
|
||||
|
@ -7,8 +7,10 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "CSOpenGLView.h"
|
||||
|
||||
#import "CSAudioQueue.h"
|
||||
#import "CSOpenGLView.h"
|
||||
#import "CSStaticAnalyser.h"
|
||||
|
||||
@class CSMachine;
|
||||
@protocol CSMachineDelegate
|
||||
@ -16,16 +18,19 @@
|
||||
- (void)machineDidChangeClockIsUnlimited:(CSMachine *)machine;
|
||||
@end
|
||||
|
||||
// Deliberately low; to ensure CSMachine has been declared as an @class already.
|
||||
#import "CSAtari2600.h"
|
||||
#import "CSZX8081.h"
|
||||
|
||||
@interface CSMachine : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
/*!
|
||||
Initialises an instance of CSMachine.
|
||||
|
||||
@param machine The pointer to an instance of @c Machine::DynamicMachine . C++ type is omitted because
|
||||
this header is visible to Swift, and the designated initialiser cannot be placed into a category.
|
||||
@param result The CSStaticAnalyser result that describes the machine needed.
|
||||
*/
|
||||
- (instancetype)initWithMachine:(void *)machine NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)runForNumberOfCycles:(int)numberOfCycles;
|
||||
|
||||
@ -53,4 +58,8 @@
|
||||
@property (nonatomic, assign) BOOL useCompositeOutput;
|
||||
@property (nonatomic, assign) BOOL useAutomaticTapeMotorControl;
|
||||
|
||||
// Special-case accessors; undefined behaviour if accessed for a machine not of the corresponding type.
|
||||
@property (nonatomic, readonly) CSAtari2600 *atari2600;
|
||||
@property (nonatomic, readonly) CSZX8081 *zx8081;
|
||||
|
||||
@end
|
||||
|
@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
#import "CSMachine.h"
|
||||
#import "CSMachine+Subclassing.h"
|
||||
#import "CSMachine+Target.h"
|
||||
|
||||
#include "CSROMFetcher.hpp"
|
||||
@ -20,6 +19,7 @@
|
||||
#include "StandardOptions.hpp"
|
||||
#include "Typer.hpp"
|
||||
|
||||
#import "CSStaticAnalyser+TargetVector.h"
|
||||
#import "NSBundle+DataResource.h"
|
||||
#import "NSData+StdVector.h"
|
||||
|
||||
@ -61,13 +61,20 @@ struct MachineDelegate: CRTMachine::Machine::Delegate, public LockProtectedDeleg
|
||||
SpeakerDelegate _speakerDelegate;
|
||||
MachineDelegate _machineDelegate;
|
||||
NSLock *_delegateMachineAccessLock;
|
||||
Machine::DynamicMachine *_machine;
|
||||
|
||||
CSStaticAnalyser *_analyser;
|
||||
std::unique_ptr<Machine::DynamicMachine> _machine;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMachine:(void *)machine {
|
||||
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
_machine = (Machine::DynamicMachine *)machine;
|
||||
_analyser = result;
|
||||
|
||||
Machine::Error error;
|
||||
_machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(), error));
|
||||
if(!_machine) return nil;
|
||||
|
||||
_delegateMachineAccessLock = [[NSLock alloc] init];
|
||||
|
||||
_machineDelegate.machine = self;
|
||||
@ -76,7 +83,6 @@ struct MachineDelegate: CRTMachine::Machine::Delegate, public LockProtectedDeleg
|
||||
_speakerDelegate.machineAccessLock = _delegateMachineAccessLock;
|
||||
|
||||
_machine->crt_machine()->set_delegate(&_machineDelegate);
|
||||
CSApplyROMFetcher(*_machine->crt_machine());
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -174,19 +180,12 @@ struct MachineDelegate: CRTMachine::Machine::Delegate, public LockProtectedDeleg
|
||||
}
|
||||
|
||||
- (void)paste:(NSString *)paste {
|
||||
KeyboardMachine::Machine *keyboardMachine = _machine->type_recipient();
|
||||
KeyboardMachine::Machine *keyboardMachine = _machine->keyboard_machine();
|
||||
if(keyboardMachine)
|
||||
keyboardMachine->type_string([paste UTF8String]);
|
||||
}
|
||||
|
||||
- (void)applyTarget:(const StaticAnalyser::Target &)target {
|
||||
@synchronized(self) {
|
||||
ConfigurationTarget::Machine *const configurationTarget = _machine->configuration_target();
|
||||
if(configurationTarget) configurationTarget->configure_as_target(target);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applyMedia:(const StaticAnalyser::Media &)media {
|
||||
- (void)applyMedia:(const Analyser::Static::Media &)media {
|
||||
@synchronized(self) {
|
||||
ConfigurationTarget::Machine *const configurationTarget = _machine->configuration_target();
|
||||
if(configurationTarget) configurationTarget->insert_media(media);
|
||||
@ -336,4 +335,20 @@ struct MachineDelegate: CRTMachine::Machine::Delegate, public LockProtectedDeleg
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)userDefaultsPrefix {
|
||||
// Assumes that the first machine in the targets list is the source of user defaults.
|
||||
std::string name = Machine::ShortNameForTargetMachine(_analyser.targets.front()->machine);
|
||||
return [[NSString stringWithUTF8String:name.c_str()] lowercaseString];
|
||||
}
|
||||
|
||||
#pragma mark - Special machines
|
||||
|
||||
- (CSAtari2600 *)atari2600 {
|
||||
return [[CSAtari2600 alloc] initWithAtari2600:_machine->raw_pointer() owner:self];
|
||||
}
|
||||
|
||||
- (CSZX8081 *)zx8081 {
|
||||
return [[CSZX8081 alloc] initWithZX8081:_machine->raw_pointer() owner:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -8,4 +8,4 @@
|
||||
|
||||
#include "ROMMachine.hpp"
|
||||
|
||||
void CSApplyROMFetcher(ROMMachine::Machine &rom_machine);
|
||||
ROMMachine::ROMFetcher CSROMFetcher();
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
void CSApplyROMFetcher(ROMMachine::Machine &rom_machine) {
|
||||
rom_machine.set_rom_fetcher( [] (const std::string &machine, const std::vector<std::string> &names) -> std::vector<std::unique_ptr<std::vector<std::uint8_t>>> {
|
||||
ROMMachine::ROMFetcher CSROMFetcher() {
|
||||
return [] (const std::string &machine, const std::vector<std::string> &names) -> std::vector<std::unique_ptr<std::vector<std::uint8_t>>> {
|
||||
NSString *subDirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:machine.c_str()]];
|
||||
std::vector<std::unique_ptr<std::vector<std::uint8_t>>> results;
|
||||
for(auto &name: names) {
|
||||
@ -31,5 +31,5 @@ void CSApplyROMFetcher(ROMMachine::Machine &rom_machine) {
|
||||
}
|
||||
|
||||
return results;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
//
|
||||
// CSStaticAnalyser+ResultVector.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 24/01/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CSStaticAnalyser.h"
|
||||
|
||||
@interface CSStaticAnalyser (ResultVector)
|
||||
|
||||
- (std::vector<std::unique_ptr<Analyser::Static::Target>> &)targets;
|
||||
|
||||
@end
|
@ -15,12 +15,8 @@
|
||||
- (instancetype)initWithFileAtURL:(NSURL *)url;
|
||||
|
||||
@property(nonatomic, readonly) NSString *optionsPanelNibName;
|
||||
- (CSMachine *)newMachine;
|
||||
|
||||
@property(nonatomic, readonly) NSString *displayName;
|
||||
|
||||
- (void)applyToMachine:(CSMachine *)machine;
|
||||
|
||||
@end
|
||||
|
||||
@interface CSMediaSet : NSObject
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user