mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-05 04:37:41 +00:00
Merge pull request #772 from TomHarte/ReflectiveEnum
Endeavours to bring introspection to machine selection options.
This commit is contained in:
commit
129bc485bf
@ -12,6 +12,81 @@
|
||||
|
||||
using namespace Analyser::Dynamic;
|
||||
|
||||
namespace {
|
||||
|
||||
class MultiStruct: public Reflection::Struct {
|
||||
public:
|
||||
MultiStruct(const std::vector<Configurable::Device *> &devices) : devices_(devices) {
|
||||
for(auto device: devices) {
|
||||
options_.emplace_back(device->get_options());
|
||||
}
|
||||
}
|
||||
|
||||
void apply() {
|
||||
auto options = options_.begin();
|
||||
for(auto device: devices_) {
|
||||
device->set_options(*options);
|
||||
++options;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> all_keys() final {
|
||||
std::set<std::string> keys;
|
||||
for(auto &options: options_) {
|
||||
const auto new_keys = options->all_keys();
|
||||
keys.insert(new_keys.begin(), new_keys.end());
|
||||
}
|
||||
return std::vector<std::string>(keys.begin(), keys.end());
|
||||
}
|
||||
|
||||
std::vector<std::string> values_for(const std::string &name) final {
|
||||
std::set<std::string> values;
|
||||
for(auto &options: options_) {
|
||||
const auto new_values = options->values_for(name);
|
||||
values.insert(new_values.begin(), new_values.end());
|
||||
}
|
||||
return std::vector<std::string>(values.begin(), values.end());
|
||||
}
|
||||
|
||||
const std::type_info *type_of(const std::string &name) final {
|
||||
for(auto &options: options_) {
|
||||
auto info = options->type_of(name);
|
||||
if(info) return info;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const void *get(const std::string &name) final {
|
||||
for(auto &options: options_) {
|
||||
auto value = options->get(name);
|
||||
if(value) return value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void set(const std::string &name, const void *value) final {
|
||||
const auto safe_type = type_of(name);
|
||||
if(!safe_type) return;
|
||||
|
||||
// Set this property only where the child's type is the same as that
|
||||
// which was returned from here for type_of.
|
||||
for(auto &options: options_) {
|
||||
const auto type = options->type_of(name);
|
||||
if(!type) continue;
|
||||
|
||||
if(*type == *safe_type) {
|
||||
options->set(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<Configurable::Device *> &devices_;
|
||||
std::vector<std::unique_ptr<Reflection::Struct>> options_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
MultiConfigurable::MultiConfigurable(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
||||
for(const auto &machine: machines) {
|
||||
Configurable::Device *device = machine->configurable_device();
|
||||
@ -19,46 +94,11 @@ MultiConfigurable::MultiConfigurable(const std::vector<std::unique_ptr<::Machine
|
||||
}
|
||||
}
|
||||
|
||||
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_options(const std::unique_ptr<Reflection::Struct> &str) {
|
||||
const auto options = dynamic_cast<MultiStruct *>(str.get());
|
||||
options->apply();
|
||||
}
|
||||
|
||||
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;
|
||||
std::unique_ptr<Reflection::Struct> MultiConfigurable::get_options() {
|
||||
return std::make_unique<MultiStruct>(devices_);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define MultiConfigurable_hpp
|
||||
|
||||
#include "../../../../Machines/DynamicMachine.hpp"
|
||||
#include "../../../../Configurable/Configurable.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -28,10 +29,8 @@ class MultiConfigurable: public Configurable::Device {
|
||||
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() final;
|
||||
void set_selections(const Configurable::SelectionSet &selection_by_option) final;
|
||||
Configurable::SelectionSet get_accurate_selections() final;
|
||||
Configurable::SelectionSet get_user_friendly_selections() final;
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &options) final;
|
||||
std::unique_ptr<Reflection::Struct> get_options() final;
|
||||
|
||||
private:
|
||||
std::vector<Configurable::Device *> devices_;
|
||||
|
@ -59,7 +59,6 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Machine::Electron;
|
||||
target->confidence = 0.5; // TODO: a proper estimation
|
||||
target->has_dfs = false;
|
||||
target->has_adfs = false;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef Analyser_Static_Acorn_Target_h
|
||||
#define Analyser_Static_Acorn_Target_h
|
||||
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
@ -16,11 +17,18 @@ namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Acorn {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
bool has_adfs = false;
|
||||
bool has_dfs = false;
|
||||
bool should_shift_restart = false;
|
||||
std::string loading_command;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Electron) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(has_adfs);
|
||||
DeclareField(has_dfs);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -182,7 +182,6 @@ static bool CheckBootSector(const std::shared_ptr<Storage::Disk::Disk> &disk, co
|
||||
Analyser::Static::TargetList Analyser::Static::AmstradCPC::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
TargetList destination;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Machine::AmstradCPC;
|
||||
target->confidence = 0.5;
|
||||
|
||||
target->model = Target::Model::CPC6128;
|
||||
|
@ -9,6 +9,8 @@
|
||||
#ifndef Analyser_Static_AmstradCPC_Target_h
|
||||
#define Analyser_Static_AmstradCPC_Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
@ -16,15 +18,17 @@ namespace Analyser {
|
||||
namespace Static {
|
||||
namespace AmstradCPC {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
enum class Model {
|
||||
CPC464,
|
||||
CPC664,
|
||||
CPC6128
|
||||
};
|
||||
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(Model, CPC464, CPC664, CPC6128);
|
||||
Model model = Model::CPC464;
|
||||
std::string loading_command;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::AmstradCPC) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
AnnounceEnum(Model);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::AppleII::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Machine::AppleII;
|
||||
target->media = media;
|
||||
|
||||
if(!target->media.disks.empty())
|
||||
|
@ -9,27 +9,38 @@
|
||||
#ifndef Target_h
|
||||
#define Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace AppleII {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
enum class Model {
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(Model,
|
||||
II,
|
||||
IIplus,
|
||||
IIe,
|
||||
EnhancedIIe
|
||||
};
|
||||
enum class DiskController {
|
||||
);
|
||||
ReflectableEnum(DiskController,
|
||||
None,
|
||||
SixteenSector,
|
||||
ThirteenSector
|
||||
};
|
||||
);
|
||||
|
||||
Model model = Model::IIe;
|
||||
DiskController disk_controller = DiskController::None;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::AppleII) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
DeclareField(disk_controller);
|
||||
AnnounceEnum(Model);
|
||||
AnnounceEnum(DiskController);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -184,7 +184,6 @@ static void DeterminePagingForCartridge(Target &target, const Storage::Cartridge
|
||||
Analyser::Static::TargetList Analyser::Static::Atari2600::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
// TODO: sanity checking; is this image really for an Atari 2600?
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Machine::Atari2600;
|
||||
target->confidence = 0.5;
|
||||
target->media.cartridges = media.cartridges;
|
||||
target->paging_model = Target::PagingModel::None;
|
||||
|
@ -34,6 +34,8 @@ struct Target: public ::Analyser::Static::Target {
|
||||
// TODO: shouldn't these be properties of the cartridge?
|
||||
PagingModel paging_model = PagingModel::None;
|
||||
bool uses_superchip = false;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Atari2600) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,9 +16,8 @@ Analyser::Static::TargetList Analyser::Static::AtariST::GetTargets(const Media &
|
||||
// As there is at least one usable media image, wave it through.
|
||||
Analyser::Static::TargetList targets;
|
||||
|
||||
using Target = Analyser::Static::Target;
|
||||
auto *target = new Target;
|
||||
target->machine = Analyser::Machine::AtariST;
|
||||
using Target = Analyser::Static::AtariST::Target;
|
||||
auto *target = new Target();
|
||||
target->media = media;
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(target));
|
||||
|
||||
|
@ -9,11 +9,15 @@
|
||||
#ifndef Analyser_Static_AtariST_Target_h
|
||||
#define Analyser_Static_AtariST_Target_h
|
||||
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace AtariST {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
Target() : Analyser::Static::Target(Machine::AtariST) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -54,8 +54,7 @@ static std::vector<std::shared_ptr<Storage::Cartridge::Cartridge>>
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Coleco::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
TargetList targets;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Machine::ColecoVision;
|
||||
auto target = std::make_unique<Target>(Machine::ColecoVision);
|
||||
target->confidence = 1.0f - 1.0f / 32768.0f;
|
||||
target->media.cartridges = ColecoCartridgesFrom(media.cartridges);
|
||||
if(!target->media.empty())
|
||||
|
@ -9,6 +9,8 @@
|
||||
#ifndef Analyser_Static_Commodore_Target_h
|
||||
#define Analyser_Static_Commodore_Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
@ -16,20 +18,20 @@ namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Commodore {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
enum class MemoryModel {
|
||||
Unexpanded,
|
||||
EightKB,
|
||||
ThirtyTwoKB
|
||||
};
|
||||
|
||||
enum class Region {
|
||||
ReflectableEnum(Region,
|
||||
American,
|
||||
Danish,
|
||||
Japanese,
|
||||
European,
|
||||
Swedish
|
||||
};
|
||||
);
|
||||
|
||||
/// Maps from a named memory model to a bank enabled/disabled set.
|
||||
void set_memory_model(MemoryModel memory_model) {
|
||||
@ -54,6 +56,19 @@ struct Target: public ::Analyser::Static::Target {
|
||||
Region region = Region::European;
|
||||
bool has_c1540 = false;
|
||||
std::string loading_command;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Vic20) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(enabled_ram.bank0);
|
||||
DeclareField(enabled_ram.bank1);
|
||||
DeclareField(enabled_ram.bank2);
|
||||
DeclareField(enabled_ram.bank3);
|
||||
DeclareField(enabled_ram.bank5);
|
||||
DeclareField(region);
|
||||
DeclareField(has_c1540);
|
||||
AnnounceEnum(Region);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ namespace {
|
||||
Analyser::Static::Target *AppleTarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
auto *target = new Target;
|
||||
target->machine = Analyser::Machine::AppleII;
|
||||
|
||||
if(sector_zero && sector_zero->encoding == Storage::Encodings::AppleGCR::Sector::Encoding::FiveAndThree) {
|
||||
target->disk_controller = Target::DiskController::ThirteenSector;
|
||||
@ -35,7 +34,6 @@ Analyser::Static::Target *AppleTarget(const Storage::Encodings::AppleGCR::Sector
|
||||
Analyser::Static::Target *OricTarget(const Storage::Encodings::AppleGCR::Sector *sector_zero) {
|
||||
using Target = Analyser::Static::Oric::Target;
|
||||
auto *target = new Target;
|
||||
target->machine = Analyser::Machine::Oric;
|
||||
target->rom = Target::ROM::Pravetz;
|
||||
target->disk_interface = Target::DiskInterface::Pravetz;
|
||||
target->loading_command = "CALL 800\n";
|
||||
|
@ -35,7 +35,6 @@ static std::unique_ptr<Analyser::Static::Target> CartridgeTarget(
|
||||
}
|
||||
|
||||
auto target = std::make_unique<Analyser::Static::MSX::Target>();
|
||||
target->machine = Analyser::Machine::MSX;
|
||||
target->confidence = confidence;
|
||||
|
||||
if(type == Analyser::Static::MSX::Cartridge::Type::None) {
|
||||
@ -295,7 +294,6 @@ Analyser::Static::TargetList Analyser::Static::MSX::GetTargets(const Media &medi
|
||||
target->has_disk_drive = !media.disks.empty();
|
||||
|
||||
if(!target->media.empty()) {
|
||||
target->machine = Machine::MSX;
|
||||
target->confidence = 0.5;
|
||||
destination.push_back(std::move(target));
|
||||
}
|
||||
|
@ -9,6 +9,8 @@
|
||||
#ifndef Analyser_Static_MSX_Target_h
|
||||
#define Analyser_Static_MSX_Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
@ -16,15 +18,24 @@ namespace Analyser {
|
||||
namespace Static {
|
||||
namespace MSX {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
bool has_disk_drive = false;
|
||||
std::string loading_command;
|
||||
|
||||
enum class Region {
|
||||
ReflectableEnum(Region,
|
||||
Japan,
|
||||
USA,
|
||||
Europe
|
||||
} region = Region::USA;
|
||||
);
|
||||
Region region = Region::USA;
|
||||
|
||||
Target(): Analyser::Static::Target(Machine::MSX) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(has_disk_drive);
|
||||
DeclareField(region);
|
||||
AnnounceEnum(Region);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ Analyser::Static::TargetList Analyser::Static::Macintosh::GetTargets(const Media
|
||||
|
||||
using Target = Analyser::Static::Macintosh::Target;
|
||||
auto *target = new Target;
|
||||
target->machine = Analyser::Machine::Macintosh;
|
||||
target->media = media;
|
||||
targets.push_back(std::unique_ptr<Analyser::Static::Target>(target));
|
||||
|
||||
|
@ -9,19 +9,25 @@
|
||||
#ifndef Analyser_Static_Macintosh_Target_h
|
||||
#define Analyser_Static_Macintosh_Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Macintosh {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
enum class Model {
|
||||
Mac128k,
|
||||
Mac512k,
|
||||
Mac512ke,
|
||||
MacPlus
|
||||
};
|
||||
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(Model, Mac128k, Mac512k, Mac512ke, MacPlus);
|
||||
Model model = Model::MacPlus;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::Macintosh) {
|
||||
// Boilerplate for declaring fields and potential values.
|
||||
if(needs_declare()) {
|
||||
DeclareField(model);
|
||||
AnnounceEnum(Model);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -147,7 +147,6 @@ bool is_bd500(Storage::Encodings::MFM::Parser &parser) {
|
||||
|
||||
Analyser::Static::TargetList Analyser::Static::Oric::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) {
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Machine::Oric;
|
||||
target->confidence = 0.5;
|
||||
|
||||
int basic10_votes = 0;
|
||||
|
@ -9,6 +9,8 @@
|
||||
#ifndef Analyser_Static_Oric_Target_h
|
||||
#define Analyser_Static_Oric_Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
@ -16,25 +18,34 @@ namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Oric {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
enum class ROM {
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(ROM,
|
||||
BASIC10,
|
||||
BASIC11,
|
||||
Pravetz
|
||||
};
|
||||
);
|
||||
|
||||
enum class DiskInterface {
|
||||
ReflectableEnum(DiskInterface,
|
||||
None,
|
||||
Microdisc,
|
||||
Pravetz,
|
||||
Jasmin,
|
||||
BD500,
|
||||
None
|
||||
};
|
||||
BD500
|
||||
);
|
||||
|
||||
ROM rom = ROM::BASIC11;
|
||||
DiskInterface disk_interface = DiskInterface::None;
|
||||
std::string loading_command;
|
||||
bool should_start_jasmin = false;
|
||||
|
||||
Target(): Analyser::Static::Target(Machine::Oric) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(rom);
|
||||
DeclareField(disk_interface);
|
||||
AnnounceEnum(ROM);
|
||||
AnnounceEnum(DiskInterface);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &med
|
||||
TargetList targets;
|
||||
auto target = std::make_unique<Target>();
|
||||
|
||||
target->machine = Machine::MasterSystem;
|
||||
|
||||
// Files named .sg are treated as for the SG1000; otherwise assume a Master System.
|
||||
if(file_name.size() >= 2 && *(file_name.end() - 2) == 's' && *(file_name.end() - 1) == 'g') {
|
||||
target->model = Target::Model::SG1000;
|
||||
|
@ -9,23 +9,27 @@
|
||||
#ifndef Analyser_Static_Sega_Target_h
|
||||
#define Analyser_Static_Sega_Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
|
||||
namespace Analyser {
|
||||
namespace Static {
|
||||
namespace Sega {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
enum class Model {
|
||||
SG1000,
|
||||
MasterSystem,
|
||||
MasterSystem2,
|
||||
};
|
||||
|
||||
enum class Region {
|
||||
ReflectableEnum(Region,
|
||||
Japan,
|
||||
USA,
|
||||
Europe,
|
||||
Brazil
|
||||
};
|
||||
);
|
||||
|
||||
enum class PagingScheme {
|
||||
Sega,
|
||||
@ -35,6 +39,13 @@ struct Target: public ::Analyser::Static::Target {
|
||||
Model model = Model::MasterSystem;
|
||||
Region region = Region::Japan;
|
||||
PagingScheme paging_scheme = PagingScheme::Sega;
|
||||
|
||||
Target() : Analyser::Static::Target(Machine::MasterSystem) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(region);
|
||||
AnnounceEnum(Region);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define is_master_system(v) v >= Analyser::Static::Sega::Target::Model::MasterSystem
|
||||
|
@ -35,6 +35,16 @@ struct Media {
|
||||
bool empty() const {
|
||||
return disks.empty() && tapes.empty() && cartridges.empty() && mass_storage_devices.empty();
|
||||
}
|
||||
|
||||
Media &operator +=(const Media &rhs) {
|
||||
#define append(name) name.insert(name.end(), rhs.name.begin(), rhs.name.end());
|
||||
append(disks);
|
||||
append(tapes);
|
||||
append(cartridges);
|
||||
append(mass_storage_devices);
|
||||
#undef append
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -42,11 +52,12 @@ struct Media {
|
||||
and instructions on how to launch the software attached, plus a measure of confidence in this target's correctness.
|
||||
*/
|
||||
struct Target {
|
||||
Target(Machine machine) : machine(machine) {}
|
||||
virtual ~Target() {}
|
||||
|
||||
Machine machine;
|
||||
Media media;
|
||||
float confidence;
|
||||
float confidence = 0.0f;
|
||||
};
|
||||
typedef std::vector<std::unique_ptr<Target>> TargetList;
|
||||
|
||||
|
@ -9,6 +9,8 @@
|
||||
#ifndef Analyser_Static_ZX8081_Target_h
|
||||
#define Analyser_Static_ZX8081_Target_h
|
||||
|
||||
#include "../../../Reflection/Enum.hpp"
|
||||
#include "../../../Reflection/Struct.hpp"
|
||||
#include "../StaticAnalyser.hpp"
|
||||
#include <string>
|
||||
|
||||
@ -16,17 +18,26 @@ namespace Analyser {
|
||||
namespace Static {
|
||||
namespace ZX8081 {
|
||||
|
||||
struct Target: public ::Analyser::Static::Target {
|
||||
enum class MemoryModel {
|
||||
struct Target: public ::Analyser::Static::Target, public Reflection::StructImpl<Target> {
|
||||
ReflectableEnum(MemoryModel,
|
||||
Unexpanded,
|
||||
SixteenKB,
|
||||
SixtyFourKB
|
||||
};
|
||||
);
|
||||
|
||||
MemoryModel memory_model = MemoryModel::Unexpanded;
|
||||
bool is_ZX81 = false;
|
||||
bool ZX80_uses_ZX81_ROM = false;
|
||||
std::string loading_command;
|
||||
|
||||
Target(): Analyser::Static::Target(Machine::ZX8081) {
|
||||
if(needs_declare()) {
|
||||
DeclareField(memory_model);
|
||||
DeclareField(is_ZX81);
|
||||
DeclareField(ZX80_uses_ZX81_ROM);
|
||||
AnnounceEnum(MemoryModel);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ template <class BusHandler> class MOS6560 {
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); }
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const { return crt_.get_scaled_scan_status() / 4.0f; }
|
||||
void set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); }
|
||||
Outputs::Display::DisplayType get_display_type() { return crt_.get_display_type(); }
|
||||
Outputs::Speaker::Speaker *get_speaker() { return &speaker_; }
|
||||
|
||||
void set_high_frequency_cutoff(float cutoff) {
|
||||
|
@ -129,6 +129,10 @@ void TMS9918::set_display_type(Outputs::Display::DisplayType display_type) {
|
||||
crt_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType TMS9918::get_display_type() {
|
||||
return crt_.get_display_type();
|
||||
}
|
||||
|
||||
void Base::LineBuffer::reset_sprite_collection() {
|
||||
sprites_stopped = false;
|
||||
active_sprite_slot = 0;
|
||||
|
@ -50,6 +50,9 @@ class TMS9918: public Base {
|
||||
/*! Sets the type of display the CRT will request. */
|
||||
void set_display_type(Outputs::Display::DisplayType);
|
||||
|
||||
/*! Gets the type of display the CRT will request. */
|
||||
Outputs::Display::DisplayType get_display_type();
|
||||
|
||||
/*!
|
||||
Runs the VCP for the number of cycles indicate; it is an implicit assumption of the code
|
||||
that the input clock rate is 3579545 Hz, the NTSC colour clock rate.
|
||||
|
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Configurable.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 18/11/2017.
|
||||
// Copyright 2017 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Configurable.hpp"
|
||||
|
||||
using namespace Configurable;
|
||||
|
||||
ListSelection *BooleanSelection::list_selection() {
|
||||
return new ListSelection(value ? "yes" : "no");
|
||||
}
|
||||
|
||||
ListSelection *ListSelection::list_selection() {
|
||||
return new ListSelection(value);
|
||||
}
|
||||
|
||||
BooleanSelection *ListSelection::boolean_selection() {
|
||||
return new BooleanSelection(value != "no" && value != "n" && value != "false" && value != "f");
|
||||
}
|
||||
|
||||
BooleanSelection *BooleanSelection::boolean_selection() {
|
||||
return new BooleanSelection(value);
|
||||
}
|
@ -9,89 +9,37 @@
|
||||
#ifndef Configurable_h
|
||||
#define Configurable_h
|
||||
|
||||
#include <map>
|
||||
#include "../Reflection/Struct.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Configurable {
|
||||
|
||||
/*!
|
||||
The Option class hierarchy provides a way for components, machines, etc, to provide a named
|
||||
list of typed options to which they can respond.
|
||||
*/
|
||||
struct Option {
|
||||
std::string long_name;
|
||||
std::string short_name;
|
||||
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 {
|
||||
BooleanOption(const std::string &long_name, const std::string &short_name) : Option(long_name, short_name) {}
|
||||
};
|
||||
|
||||
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;
|
||||
struct ListSelection;
|
||||
|
||||
/*!
|
||||
Selections are responses to Options.
|
||||
*/
|
||||
struct Selection {
|
||||
virtual ~Selection() {}
|
||||
virtual ListSelection *list_selection() = 0;
|
||||
virtual BooleanSelection *boolean_selection() = 0;
|
||||
};
|
||||
|
||||
struct BooleanSelection: public Selection {
|
||||
bool value;
|
||||
|
||||
ListSelection *list_selection();
|
||||
BooleanSelection *boolean_selection();
|
||||
BooleanSelection(bool value) : value(value) {}
|
||||
};
|
||||
|
||||
struct ListSelection: public Selection {
|
||||
std::string value;
|
||||
|
||||
ListSelection *list_selection();
|
||||
BooleanSelection *boolean_selection();
|
||||
ListSelection(const std::string value) : value(value) {}
|
||||
};
|
||||
|
||||
using SelectionSet = std::map<std::string, std::unique_ptr<Selection>>;
|
||||
|
||||
/*!
|
||||
A Configuratble provides the options that it responds to and allows selections to be set.
|
||||
A Configurable::Device provides a reflective struct listing the available runtime options for this machine.
|
||||
You can ordinarily either get or set a machine's current options, or else construct a new instance of
|
||||
its options with one of the OptionsTypes defined below.
|
||||
*/
|
||||
struct Device {
|
||||
virtual std::vector<std::unique_ptr<Option>> get_options() = 0;
|
||||
virtual void set_selections(const SelectionSet &selection_by_option) = 0;
|
||||
virtual SelectionSet get_accurate_selections() = 0;
|
||||
virtual SelectionSet get_user_friendly_selections() = 0;
|
||||
/// Sets the current options. The caller must ensure that the object passed in is either an instance of the machine's
|
||||
/// Options struct, or else was previously returned by get_options.
|
||||
virtual void set_options(const std::unique_ptr<Reflection::Struct> &options) = 0;
|
||||
|
||||
/// @returns An options object
|
||||
virtual std::unique_ptr<Reflection::Struct> get_options() = 0;
|
||||
};
|
||||
|
||||
template <typename T> T *selection(const Configurable::SelectionSet &selections_by_option, const std::string &name) {
|
||||
auto selection = selections_by_option.find(name);
|
||||
if(selection == selections_by_option.end()) return nullptr;
|
||||
return dynamic_cast<T *>(selection->second.get());
|
||||
}
|
||||
/*!
|
||||
'Accurate' options should correspond to the way that this device was usually used during its lifespan.
|
||||
E.g. a ColecoVision might accurately be given composite output.
|
||||
|
||||
'User-friendly' options should be more like those that a user today might most expect from an emulator.
|
||||
E.g. the ColecoVision might bump itself up to S-Video output.
|
||||
*/
|
||||
enum class OptionsType {
|
||||
Accurate,
|
||||
UserFriendly
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
@ -8,27 +8,7 @@
|
||||
|
||||
#include "StandardOptions.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
/*!
|
||||
Appends a Boolean selection of @c selection for option @c name to @c selection_set.
|
||||
*/
|
||||
void append_bool(Configurable::SelectionSet &selection_set, const std::string &name, bool selection) {
|
||||
selection_set[name] = std::make_unique<Configurable::BooleanSelection>(selection);
|
||||
}
|
||||
|
||||
/*!
|
||||
Enquires for a Boolean selection for option @c name from @c selections_by_option, storing it to @c result if found.
|
||||
*/
|
||||
bool get_bool(const Configurable::SelectionSet &selections_by_option, const std::string &name, bool &result) {
|
||||
auto selection = Configurable::selection<Configurable::BooleanSelection>(selections_by_option, name);
|
||||
if(!selection) return false;
|
||||
result = selection->value;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// MARK: - Standard option list builder
|
||||
std::vector<std::unique_ptr<Configurable::Option>> Configurable::standard_options(Configurable::StandardOptions mask) {
|
||||
std::vector<std::unique_ptr<Configurable::Option>> options;
|
||||
@ -105,4 +85,4 @@ bool Configurable::get_display(const Configurable::SelectionSet &selections_by_o
|
||||
|
||||
bool Configurable::get_quick_boot(const Configurable::SelectionSet &selections_by_option, bool &result) {
|
||||
return get_bool(selections_by_option, "quickboot", result);
|
||||
}
|
||||
}*/
|
||||
|
@ -9,87 +9,56 @@
|
||||
#ifndef StandardOptions_hpp
|
||||
#define StandardOptions_hpp
|
||||
|
||||
#include "Configurable.hpp"
|
||||
#include "../Reflection/Enum.hpp"
|
||||
|
||||
namespace Configurable {
|
||||
|
||||
enum StandardOptions {
|
||||
DisplayRGB = (1 << 0),
|
||||
DisplaySVideo = (1 << 1),
|
||||
DisplayCompositeColour = (1 << 2),
|
||||
DisplayCompositeMonochrome = (1 << 3),
|
||||
QuickLoadTape = (1 << 4),
|
||||
AutomaticTapeMotorControl = (1 << 5),
|
||||
QuickBoot = (1 << 6),
|
||||
};
|
||||
|
||||
enum class Display {
|
||||
ReflectableEnum(Display,
|
||||
RGB,
|
||||
SVideo,
|
||||
CompositeColour,
|
||||
CompositeMonochrome
|
||||
);
|
||||
|
||||
//===
|
||||
// From here downward are a bunch of templates for individual option flags.
|
||||
// Using them saves you marginally in syntax, but the primary gain is to
|
||||
// ensure unified property naming.
|
||||
//===
|
||||
|
||||
template <typename Owner> class DisplayOption {
|
||||
public:
|
||||
Configurable::Display output;
|
||||
DisplayOption(Configurable::Display output) : output(output) {}
|
||||
|
||||
protected:
|
||||
void declare_display_option() {
|
||||
static_cast<Owner *>(this)->declare(&output, "output");
|
||||
AnnounceEnumNS(Configurable, Display);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
@returns An option list comprised of the standard names for all the options indicated by @c mask.
|
||||
*/
|
||||
std::vector<std::unique_ptr<Option>> standard_options(StandardOptions mask);
|
||||
template <typename Owner> class QuickloadOption {
|
||||
public:
|
||||
bool quickload;
|
||||
QuickloadOption(bool quickload) : quickload(quickload) {}
|
||||
|
||||
/*!
|
||||
Appends to @c selection_set a selection of @c selection for QuickLoadTape.
|
||||
*/
|
||||
void append_quick_load_tape_selection(SelectionSet &selection_set, bool selection);
|
||||
protected:
|
||||
void declare_quickload_option() {
|
||||
static_cast<Owner *>(this)->declare(&quickload, "quickload");
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
Appends to @c selection_set a selection of @c selection for AutomaticTapeMotorControl.
|
||||
*/
|
||||
void append_automatic_tape_motor_control_selection(SelectionSet &selection_set, bool selection);
|
||||
template <typename Owner> class QuickbootOption {
|
||||
public:
|
||||
bool quickboot;
|
||||
QuickbootOption(bool quickboot) : quickboot(quickboot) {}
|
||||
|
||||
/*!
|
||||
Appends to @c selection_set a selection of @c selection for DisplayRGBComposite.
|
||||
*/
|
||||
void append_display_selection(SelectionSet &selection_set, Display selection);
|
||||
|
||||
/*!
|
||||
Appends to @c selection_set a selection of @c selection for QuickBoot.
|
||||
*/
|
||||
void append_quick_boot_selection(SelectionSet &selection_set, bool selection);
|
||||
|
||||
/*!
|
||||
Attempts to discern a QuickLoadTape selection from @c selections_by_option.
|
||||
|
||||
@param selections_by_option The user selections.
|
||||
@param result The location to which the selection will be stored if found.
|
||||
@returns @c true if a selection is found; @c false otherwise.
|
||||
*/
|
||||
bool get_quick_load_tape(const SelectionSet &selections_by_option, bool &result);
|
||||
|
||||
/*!
|
||||
Attempts to discern an AutomaticTapeMotorControl selection from @c selections_by_option.
|
||||
|
||||
@param selections_by_option The user selections.
|
||||
@param result The location to which the selection will be stored if found.
|
||||
@returns @c true if a selection is found; @c false otherwise.
|
||||
*/
|
||||
bool get_automatic_tape_motor_control_selection(const SelectionSet &selections_by_option, bool &result);
|
||||
|
||||
/*!
|
||||
Attempts to discern a display RGB/composite selection from @c selections_by_option.
|
||||
|
||||
@param selections_by_option The user selections.
|
||||
@param result The location to which the selection will be stored if found.
|
||||
@returns @c true if a selection is found; @c false otherwise.
|
||||
*/
|
||||
bool get_display(const SelectionSet &selections_by_option, Display &result);
|
||||
|
||||
/*!
|
||||
Attempts to QuickBoot a QuickLoadTape selection from @c selections_by_option.
|
||||
|
||||
@param selections_by_option The user selections.
|
||||
@param result The location to which the selection will be stored if found.
|
||||
@returns @c true if a selection is found; @c false otherwise.
|
||||
*/
|
||||
bool get_quick_boot(const SelectionSet &selections_by_option, bool &result);
|
||||
protected:
|
||||
void declare_quickboot_option() {
|
||||
static_cast<Owner *>(this)->declare(&quickboot, "quickboot");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
@ -40,12 +40,6 @@
|
||||
|
||||
namespace AmstradCPC {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
Configurable::StandardOptions(Configurable::DisplayRGB | Configurable::DisplayCompositeColour)
|
||||
);
|
||||
}
|
||||
|
||||
/*!
|
||||
Models the CPC's interrupt timer. Inputs are vsync, hsync, interrupt acknowledge and reset, and its output
|
||||
is simply yes or no on whether an interupt is currently requested. Internally it uses a counter with a period
|
||||
@ -354,6 +348,11 @@ class CRTCBusHandler {
|
||||
crt_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
/// Gets the type of display.
|
||||
Outputs::Display::DisplayType get_display_type() {
|
||||
return crt_.get_display_type();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the next video mode. Per the documentation, mode changes take effect only at the end of line,
|
||||
not immediately. So next means "as of the end of this line".
|
||||
@ -1045,6 +1044,11 @@ template <bool has_fdc> class ConcreteMachine:
|
||||
crtc_bus_handler_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
/// A CRTMachine function; gets the output display type.
|
||||
Outputs::Display::DisplayType get_display_type() {
|
||||
return crtc_bus_handler_.get_display_type();
|
||||
}
|
||||
|
||||
/// @returns the speaker in use.
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return ay_.get_speaker();
|
||||
@ -1114,27 +1118,15 @@ template <bool has_fdc> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return AmstradCPC::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::RGB);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::RGB);
|
||||
return selection_set;
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
}
|
||||
|
||||
// MARK: - Joysticks
|
||||
|
@ -10,17 +10,14 @@
|
||||
#define AmstradCPC_hpp
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace AmstradCPC {
|
||||
|
||||
/// @returns The options available for an Amstrad CPC.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
/*!
|
||||
Models an Amstrad CPC.
|
||||
*/
|
||||
@ -30,6 +27,18 @@ class Machine {
|
||||
|
||||
/// Creates and returns an Amstrad CPC.
|
||||
static Machine *AmstradCPC(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
/// Defines the runtime options available for an Amstrad CPC.
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) : Configurable::DisplayOption<Options>(Configurable::Display::RGB) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -37,12 +37,6 @@
|
||||
namespace Apple {
|
||||
namespace II {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::DisplayCompositeMonochrome | Configurable::DisplayCompositeColour)
|
||||
);
|
||||
}
|
||||
|
||||
#define is_iie() ((model == Analyser::Static::AppleII::Target::Model::IIe) || (model == Analyser::Static::AppleII::Target::Model::EnhancedIIe))
|
||||
|
||||
template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
@ -430,6 +424,10 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
video_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() final {
|
||||
return video_.get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
@ -866,25 +864,15 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK:: Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return Apple::II::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
return get_accurate_selections();
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
}
|
||||
|
||||
// MARK: MediaTarget
|
||||
|
@ -10,24 +10,33 @@
|
||||
#define AppleII_hpp
|
||||
|
||||
#include "../../../Configurable/Configurable.hpp"
|
||||
#include "../../../Configurable/StandardOptions.hpp"
|
||||
#include "../../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../ROMMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Apple {
|
||||
namespace II {
|
||||
|
||||
/// @returns The options available for an Apple II.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
|
||||
/// Creates and returns an AppleII.
|
||||
static Machine *AppleII(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
/// Defines the runtime options available for an Apple II.
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) : Configurable::DisplayOption<Options>(Configurable::Display::CompositeColour) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
limit_enum(&output, Configurable::Display::CompositeMonochrome, Configurable::Display::CompositeColour, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -55,6 +55,10 @@ void VideoBase::set_display_type(Outputs::Display::DisplayType display_type) {
|
||||
crt_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType VideoBase::get_display_type() {
|
||||
return crt_.get_display_type();
|
||||
}
|
||||
|
||||
/*
|
||||
Rote setters and getters.
|
||||
*/
|
||||
|
@ -46,6 +46,9 @@ class VideoBase {
|
||||
/// Sets the type of output.
|
||||
void set_display_type(Outputs::Display::DisplayType);
|
||||
|
||||
/// Gets the type of output.
|
||||
Outputs::Display::DisplayType get_display_type();
|
||||
|
||||
/*
|
||||
Descriptions for the setters below are taken verbatim from
|
||||
the Apple IIe Technical Reference. Addresses are the conventional
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../../KeyboardMachine.hpp"
|
||||
#include "../../MediaTarget.hpp"
|
||||
#include "../../MouseMachine.hpp"
|
||||
#include "../../../Configurable/Configurable.hpp"
|
||||
|
||||
#include "../../../Inputs/QuadratureMouse/QuadratureMouse.hpp"
|
||||
#include "../../../Outputs/Log.hpp"
|
||||
@ -56,12 +57,6 @@ constexpr int CLOCK_RATE = 7833600;
|
||||
namespace Apple {
|
||||
namespace Macintosh {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::QuickBoot)
|
||||
);
|
||||
}
|
||||
|
||||
template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachine:
|
||||
public Machine,
|
||||
public CRTMachine::Machine,
|
||||
@ -522,37 +517,31 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return Apple::Macintosh::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->quickboot = quickboot_;
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
bool quick_boot;
|
||||
if(Configurable::get_quick_boot(selections_by_option, quick_boot)) {
|
||||
if(quick_boot) {
|
||||
// Cf. Big Mess o' Wires' disassembly of the Mac Plus ROM, and the
|
||||
// test at $E00. TODO: adapt as(/if?) necessary for other Macs.
|
||||
ram_[0x02ae] = 0x40;
|
||||
ram_[0x02af] = 0x00;
|
||||
ram_[0x02b0] = 0x00;
|
||||
ram_[0x02b1] = 0x00;
|
||||
}
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
// TODO: should this really be a runtime option?
|
||||
// It should probably be a construction option.
|
||||
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
quickboot_ = options->quickboot;
|
||||
if(quickboot_) {
|
||||
// Cf. Big Mess o' Wires' disassembly of the Mac Plus ROM, and the
|
||||
// test at $E00. TODO: adapt as(/if?) necessary for other Macs.
|
||||
ram_[0x02ae] = 0x40;
|
||||
ram_[0x02af] = 0x00;
|
||||
ram_[0x02b0] = 0x00;
|
||||
ram_[0x02b1] = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_boot_selection(selection_set, false);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_boot_selection(selection_set, true);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
private:
|
||||
bool quickboot_ = false;
|
||||
|
||||
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) final {
|
||||
scsi_bus_is_clocked_ = scsi_bus_.preferred_clocking() != ClockingHint::Preference::None;
|
||||
}
|
||||
|
@ -10,20 +10,30 @@
|
||||
#define Macintosh_hpp
|
||||
|
||||
#include "../../../Configurable/Configurable.hpp"
|
||||
#include "../../../Configurable/StandardOptions.hpp"
|
||||
#include "../../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../ROMMachine.hpp"
|
||||
|
||||
namespace Apple {
|
||||
namespace Macintosh {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
|
||||
/// Creates and returns a Macintosh.
|
||||
static Machine *Macintosh(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::QuickbootOption<Options> {
|
||||
friend Configurable::QuickbootOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::QuickbootOption<Options>(type == Configurable::OptionsType::UserFriendly) {
|
||||
if(needs_declare()) {
|
||||
declare_quickboot_option();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
@ -42,12 +42,6 @@
|
||||
namespace Atari {
|
||||
namespace ST {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::DisplayRGB | Configurable::DisplayCompositeColour)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr int CLOCK_RATE = 8021247;
|
||||
|
||||
using Target = Analyser::Static::Target;
|
||||
@ -149,6 +143,10 @@ class ConcreteMachine:
|
||||
video_->set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() final {
|
||||
return video_->get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
@ -678,27 +676,15 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return Atari::ST::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::RGB);
|
||||
return selection_set;
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10,20 +10,32 @@
|
||||
#define AtariST_hpp
|
||||
|
||||
#include "../../../Configurable/Configurable.hpp"
|
||||
#include "../../../Configurable/StandardOptions.hpp"
|
||||
#include "../../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../ROMMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Atari {
|
||||
namespace ST {
|
||||
|
||||
/// @returns The options available for an Atari ST.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
|
||||
static Machine *AtariST(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) : Configurable::DisplayOption<Options>(
|
||||
type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -146,6 +146,10 @@ void Video::set_display_type(Outputs::Display::DisplayType display_type) {
|
||||
crt_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType Video::get_display_type() {
|
||||
return crt_.get_display_type();
|
||||
}
|
||||
|
||||
void Video::run_for(HalfCycles duration) {
|
||||
int integer_duration = int(duration.as_integral());
|
||||
assert(integer_duration >= 0);
|
||||
|
@ -54,6 +54,11 @@ class Video {
|
||||
*/
|
||||
void set_display_type(Outputs::Display::DisplayType);
|
||||
|
||||
/*!
|
||||
Gets the type of output.
|
||||
*/
|
||||
Outputs::Display::DisplayType get_display_type();
|
||||
|
||||
/*!
|
||||
Produces the next @c duration period of pixels.
|
||||
*/
|
||||
|
@ -181,10 +181,28 @@ class Machine {
|
||||
}
|
||||
|
||||
/*!
|
||||
Forwards the video signal to the target returned by get_crt().
|
||||
Maps back from Outputs::Display::VideoSignal to Configurable::Display,
|
||||
calling @c get_display_type for the input.
|
||||
*/
|
||||
Configurable::Display get_video_signal_configurable() {
|
||||
switch(get_display_type()) {
|
||||
default:
|
||||
case Outputs::Display::DisplayType::RGB: return Configurable::Display::RGB;
|
||||
case Outputs::Display::DisplayType::SVideo: return Configurable::Display::SVideo;
|
||||
case Outputs::Display::DisplayType::CompositeColour: return Configurable::Display::CompositeColour;
|
||||
case Outputs::Display::DisplayType::CompositeMonochrome: return Configurable::Display::CompositeMonochrome;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the display type.
|
||||
*/
|
||||
virtual void set_display_type(Outputs::Display::DisplayType display_type) {}
|
||||
|
||||
/*!
|
||||
Gets the display type.
|
||||
*/
|
||||
virtual Outputs::Display::DisplayType get_display_type() { return Outputs::Display::DisplayType::RGB; }
|
||||
|
||||
private:
|
||||
double clock_rate_ = 1.0;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../JoystickMachine.hpp"
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../ClockReceiver/ForceInline.hpp"
|
||||
@ -33,12 +34,6 @@ constexpr int sn76489_divider = 2;
|
||||
namespace Coleco {
|
||||
namespace Vision {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::DisplaySVideo | Configurable::DisplayCompositeColour)
|
||||
);
|
||||
}
|
||||
|
||||
class Joystick: public Inputs::ConcreteJoystick {
|
||||
public:
|
||||
Joystick() :
|
||||
@ -193,6 +188,10 @@ class ConcreteMachine:
|
||||
vdp_->set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() final {
|
||||
return vdp_->get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
@ -368,27 +367,15 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return Coleco::Vision::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::SVideo);
|
||||
return selection_set;
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -10,18 +10,29 @@
|
||||
#define ColecoVision_hpp
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
namespace Coleco {
|
||||
namespace Vision {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
static Machine *ColecoVision(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::SVideo : Configurable::Display::CompositeColour) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
limit_enum(&output, Configurable::Display::SVideo, Configurable::Display::CompositeColour, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -49,12 +49,6 @@ enum ROMSlot {
|
||||
Drive
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::DisplaySVideo | Configurable::DisplayCompositeColour | Configurable::QuickLoadTape)
|
||||
);
|
||||
}
|
||||
|
||||
enum JoystickInput {
|
||||
Up = 0x04,
|
||||
Down = 0x08,
|
||||
@ -653,6 +647,10 @@ class ConcreteMachine:
|
||||
mos6560_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() final {
|
||||
return mos6560_.get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return mos6560_.get_speaker();
|
||||
}
|
||||
@ -679,35 +677,19 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return Commodore::Vic20::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
options->quickload = allow_fast_tape_hack_;
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
bool quickload;
|
||||
if(Configurable::get_quick_load_tape(selections_by_option, quickload)) {
|
||||
allow_fast_tape_hack_ = quickload;
|
||||
set_use_fast_tape();
|
||||
}
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, false);
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, true);
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::SVideo);
|
||||
return selection_set;
|
||||
set_video_signal_configurable(options->output);
|
||||
allow_fast_tape_hack_ = options->quickload;
|
||||
set_use_fast_tape();
|
||||
}
|
||||
|
||||
void set_component_prefers_clocking(ClockingHint::Source *component, ClockingHint::Preference clocking) final {
|
||||
|
@ -10,17 +10,17 @@
|
||||
#define Vic20_hpp
|
||||
|
||||
#include "../../../Configurable/Configurable.hpp"
|
||||
#include "../../../Configurable/StandardOptions.hpp"
|
||||
#include "../../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../ROMMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Commodore {
|
||||
namespace Vic20 {
|
||||
|
||||
/// @returns The options available for a Vic-20.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options();
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
@ -28,6 +28,21 @@ class Machine {
|
||||
|
||||
/// Creates and returns a Vic-20.
|
||||
static Machine *Vic20(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options>, public Configurable::QuickloadOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
friend Configurable::QuickloadOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::SVideo : Configurable::Display::CompositeColour),
|
||||
Configurable::QuickloadOption<Options>(type == Configurable::OptionsType::UserFriendly) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
declare_quickload_option();
|
||||
limit_enum(&output, Configurable::Display::SVideo, Configurable::Display::CompositeColour, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "../MediaTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../ClockReceiver/ForceInline.hpp"
|
||||
@ -32,12 +33,6 @@
|
||||
|
||||
namespace Electron {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::DisplayRGB | Configurable::DisplayCompositeColour | Configurable::QuickLoadTape)
|
||||
);
|
||||
}
|
||||
|
||||
class ConcreteMachine:
|
||||
public Machine,
|
||||
public CRTMachine::Machine,
|
||||
@ -414,6 +409,10 @@ class ConcreteMachine:
|
||||
video_output_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() final {
|
||||
return video_output_.get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
@ -448,35 +447,19 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return Electron::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
options->quickload = allow_fast_tape_hack_;
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
bool quickload;
|
||||
if(Configurable::get_quick_load_tape(selections_by_option, quickload)) {
|
||||
allow_fast_tape_hack_ = quickload;
|
||||
set_use_fast_tape_hack();
|
||||
}
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, false);
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, true);
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::RGB);
|
||||
return selection_set;
|
||||
set_video_signal_configurable(options->output);
|
||||
allow_fast_tape_hack_ = options->quickload;
|
||||
set_use_fast_tape_hack();
|
||||
}
|
||||
|
||||
// MARK: - Activity Source
|
||||
|
@ -10,18 +10,14 @@
|
||||
#define Electron_hpp
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace Electron {
|
||||
|
||||
/// @returns The options available for an Electron.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
/*!
|
||||
@abstract Represents an Acorn Electron.
|
||||
|
||||
@ -34,6 +30,22 @@ class Machine {
|
||||
|
||||
/// Creates and returns an Electron.
|
||||
static Machine *Electron(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
/// Defines the runtime options available for an Electron.
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options>, public Configurable::QuickloadOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
friend Configurable::QuickloadOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour),
|
||||
Configurable::QuickloadOption<Options>(type == Configurable::OptionsType::UserFriendly) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
declare_quickload_option();
|
||||
limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, Configurable::Display::CompositeMonochrome, -1);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -64,6 +64,10 @@ void VideoOutput::set_display_type(Outputs::Display::DisplayType display_type) {
|
||||
crt_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType VideoOutput::get_display_type() {
|
||||
return crt_.get_display_type();
|
||||
}
|
||||
|
||||
// MARK: - Display update methods
|
||||
|
||||
void VideoOutput::start_pixel_line() {
|
||||
|
@ -45,6 +45,9 @@ class VideoOutput {
|
||||
/// Sets the type of output.
|
||||
void set_display_type(Outputs::Display::DisplayType);
|
||||
|
||||
/// Gets the type of output.
|
||||
Outputs::Display::DisplayType get_display_type();
|
||||
|
||||
/*!
|
||||
Writes @c value to the register at @c address. May mutate the results of @c get_next_interrupt,
|
||||
@c get_cycles_until_next_ram_availability and @c get_memory_access_range.
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "../JoystickMachine.hpp"
|
||||
#include "../MediaTarget.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
|
||||
#include "../../Outputs/Log.hpp"
|
||||
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
|
||||
@ -51,12 +52,6 @@
|
||||
|
||||
namespace MSX {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::DisplayRGB | Configurable::DisplaySVideo | Configurable::DisplayCompositeColour | Configurable::QuickLoadTape)
|
||||
);
|
||||
}
|
||||
|
||||
class AYPortHandler: public GI::AY38910::PortHandler {
|
||||
public:
|
||||
AYPortHandler(Storage::Tape::BinaryTapePlayer &tape_player) : tape_player_(tape_player) {
|
||||
@ -290,6 +285,10 @@ class ConcreteMachine:
|
||||
vdp_->set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() final {
|
||||
return vdp_->get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
@ -648,35 +647,19 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return MSX::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
options->quickload = allow_fast_tape_;
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
bool quickload;
|
||||
if(Configurable::get_quick_load_tape(selections_by_option, quickload)) {
|
||||
allow_fast_tape_ = quickload;
|
||||
set_use_fast_tape();
|
||||
}
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, false);
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, true);
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::RGB);
|
||||
return selection_set;
|
||||
set_video_signal_configurable(options->output);
|
||||
allow_fast_tape_ = options->quickload;
|
||||
set_use_fast_tape();
|
||||
}
|
||||
|
||||
// MARK: - Sleeper
|
||||
|
@ -10,20 +10,32 @@
|
||||
#define MSX_hpp
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace MSX {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
static Machine *MSX(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options>, public Configurable::QuickloadOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
friend Configurable::QuickloadOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour),
|
||||
Configurable::QuickloadOption<Options>(type == Configurable::OptionsType::UserFriendly) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
declare_quickload_option();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../JoystickMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
|
||||
#include "../../ClockReceiver/ForceInline.hpp"
|
||||
#include "../../ClockReceiver/JustInTime.hpp"
|
||||
@ -37,12 +38,6 @@ constexpr int sn76489_divider = 2;
|
||||
namespace Sega {
|
||||
namespace MasterSystem {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::DisplayRGB | Configurable::DisplayCompositeColour)
|
||||
);
|
||||
}
|
||||
|
||||
class Joystick: public Inputs::ConcreteJoystick {
|
||||
public:
|
||||
Joystick() :
|
||||
@ -187,6 +182,10 @@ class ConcreteMachine:
|
||||
vdp_->set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() final {
|
||||
return vdp_->get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
@ -373,27 +372,15 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return Sega::MasterSystem::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::RGB);
|
||||
return selection_set;
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -10,18 +10,30 @@
|
||||
#define MasterSystem_hpp
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Sega {
|
||||
namespace MasterSystem {
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
class Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
static Machine *MasterSystem(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -50,16 +50,6 @@ enum ROM {
|
||||
BASIC10 = 0, BASIC11, Microdisc, Colour
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(
|
||||
Configurable::DisplayRGB |
|
||||
Configurable::DisplayCompositeColour |
|
||||
Configurable::DisplayCompositeMonochrome |
|
||||
Configurable::QuickLoadTape)
|
||||
);
|
||||
}
|
||||
|
||||
/*!
|
||||
Models the Oric's keyboard: eight key rows, containing a bitfield of keys set.
|
||||
|
||||
@ -565,6 +555,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
video_output_.set_display_type(display_type);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType get_display_type() final {
|
||||
return video_output_.get_display_type();
|
||||
}
|
||||
|
||||
Outputs::Speaker::Speaker *get_speaker() final {
|
||||
return &speaker_;
|
||||
}
|
||||
@ -623,34 +617,17 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return Oric::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly);
|
||||
options->output = get_video_signal_configurable();
|
||||
options->quickload = use_fast_tape_hack_;
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
bool quickload;
|
||||
if(Configurable::get_quick_load_tape(selections_by_option, quickload)) {
|
||||
set_use_fast_tape_hack(quickload);
|
||||
}
|
||||
|
||||
Configurable::Display display;
|
||||
if(Configurable::get_display(selections_by_option, display)) {
|
||||
set_video_signal_configurable(display);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, false);
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::CompositeColour);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, true);
|
||||
Configurable::append_display_selection(selection_set, Configurable::Display::RGB);
|
||||
return selection_set;
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) final {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_video_signal_configurable(options->output);
|
||||
set_use_fast_tape_hack(options->quickload);
|
||||
}
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer) final {
|
||||
|
@ -10,13 +10,13 @@
|
||||
#define Oric_hpp
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
namespace Oric {
|
||||
#include <memory>
|
||||
|
||||
/// @returns The options available for an Oric.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
namespace Oric {
|
||||
|
||||
/*!
|
||||
Models an Oric 1/Atmos with or without a Microdisc.
|
||||
@ -27,6 +27,20 @@ class Machine {
|
||||
|
||||
/// Creates and returns an Oric.
|
||||
static Machine *Oric(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher);
|
||||
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::DisplayOption<Options>, public Configurable::QuickloadOption<Options> {
|
||||
friend Configurable::DisplayOption<Options>;
|
||||
friend Configurable::QuickloadOption<Options>;
|
||||
public:
|
||||
Options(Configurable::OptionsType type) :
|
||||
Configurable::DisplayOption<Options>(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour),
|
||||
Configurable::QuickloadOption<Options>(type == Configurable::OptionsType::UserFriendly) {
|
||||
if(needs_declare()) {
|
||||
declare_display_option();
|
||||
declare_quickload_option();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -67,6 +67,10 @@ void VideoOutput::set_display_type(Outputs::Display::DisplayType display_type) {
|
||||
}
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType VideoOutput::get_display_type() {
|
||||
return crt_.get_display_type();
|
||||
}
|
||||
|
||||
void VideoOutput::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||
crt_.set_scan_target(scan_target);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ class VideoOutput {
|
||||
|
||||
void set_scan_target(Outputs::Display::ScanTarget *scan_target);
|
||||
void set_display_type(Outputs::Display::DisplayType display_type);
|
||||
Outputs::Display::DisplayType get_display_type();
|
||||
Outputs::Display::ScanStatus get_scaled_scan_status() const;
|
||||
|
||||
void register_crt_frequency_mismatch();
|
||||
|
@ -8,6 +8,9 @@
|
||||
|
||||
#include "MachineForTarget.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// Sources for runtime options.
|
||||
#include "../AmstradCPC/AmstradCPC.hpp"
|
||||
#include "../Apple/AppleII/AppleII.hpp"
|
||||
#include "../Apple/Macintosh/Macintosh.hpp"
|
||||
@ -21,12 +24,23 @@
|
||||
#include "../Oric/Oric.hpp"
|
||||
#include "../ZX8081/ZX8081.hpp"
|
||||
|
||||
// Sources for construction options.
|
||||
#include "../../Analyser/Static/Acorn/Target.hpp"
|
||||
#include "../../Analyser/Static/AmstradCPC/Target.hpp"
|
||||
#include "../../Analyser/Static/AppleII/Target.hpp"
|
||||
#include "../../Analyser/Static/Atari2600/Target.hpp"
|
||||
#include "../../Analyser/Static/AtariST/Target.hpp"
|
||||
#include "../../Analyser/Static/Commodore/Target.hpp"
|
||||
#include "../../Analyser/Static/Macintosh/Target.hpp"
|
||||
#include "../../Analyser/Static/MSX/Target.hpp"
|
||||
#include "../../Analyser/Static/Oric/Target.hpp"
|
||||
#include "../../Analyser/Static/Sega/Target.hpp"
|
||||
#include "../../Analyser/Static/ZX8081/Target.hpp"
|
||||
|
||||
#include "../../Analyser/Dynamic/MultiMachine/MultiMachine.hpp"
|
||||
#include "TypedDynamicMachine.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
::Machine::DynamicMachine *MachineForTarget(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::Error &error) {
|
||||
Machine::DynamicMachine *Machine::MachineForTarget(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::Error &error) {
|
||||
error = Machine::Error::None;
|
||||
|
||||
Machine::DynamicMachine *machine = nullptr;
|
||||
@ -66,9 +80,7 @@ namespace {
|
||||
return machine;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
::Machine::DynamicMachine *::Machine::MachineForTargets(const Analyser::Static::TargetList &targets, const ROMMachine::ROMFetcher &rom_fetcher, Error &error) {
|
||||
Machine::DynamicMachine *Machine::MachineForTargets(const Analyser::Static::TargetList &targets, const ROMMachine::ROMFetcher &rom_fetcher, Error &error) {
|
||||
// Zero targets implies no machine.
|
||||
if(targets.empty()) {
|
||||
error = Error::NoTargets;
|
||||
@ -138,20 +150,82 @@ std::string Machine::LongNameForTargetMachine(Analyser::Machine machine) {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
std::vector<std::string> Machine::AllMachines(Type type, bool long_names) {
|
||||
std::vector<std::string> result;
|
||||
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AmstradCPC), AmstradCPC::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AppleII), Apple::II::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::AtariST), Atari::ST::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::ColecoVision), Coleco::Vision::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Electron), Electron::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Macintosh), Apple::Macintosh::get_options()));
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::MasterSystem), Sega::MasterSystem::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()));
|
||||
#define AddName(x) result.push_back(long_names ? LongNameForTargetMachine(Analyser::Machine::x) : ShortNameForTargetMachine(Analyser::Machine::x))
|
||||
|
||||
if(type == Type::Any || type == Type::RequiresMedia) {
|
||||
AddName(Atari2600);
|
||||
AddName(ColecoVision);
|
||||
AddName(MasterSystem);
|
||||
}
|
||||
|
||||
if(type == Type::Any || type == Type::DoesntRequireMedia) {
|
||||
AddName(AmstradCPC);
|
||||
AddName(AppleII);
|
||||
AddName(AtariST);
|
||||
AddName(Electron);
|
||||
AddName(Macintosh);
|
||||
AddName(MSX);
|
||||
AddName(Oric);
|
||||
AddName(Vic20);
|
||||
AddName(ZX8081);
|
||||
}
|
||||
|
||||
#undef AddName
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<std::string, std::unique_ptr<Reflection::Struct>> Machine::AllOptionsByMachineName() {
|
||||
std::map<std::string, std::unique_ptr<Reflection::Struct>> options;
|
||||
|
||||
#define Emplace(machine, class) \
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::machine), std::make_unique<class::Options>(Configurable::OptionsType::UserFriendly)));
|
||||
|
||||
Emplace(AmstradCPC, AmstradCPC::Machine);
|
||||
Emplace(AppleII, Apple::II::Machine);
|
||||
Emplace(AtariST, Atari::ST::Machine);
|
||||
Emplace(ColecoVision, Coleco::Vision::Machine);
|
||||
Emplace(Electron, Electron::Machine);
|
||||
Emplace(Macintosh, Apple::Macintosh::Machine);
|
||||
Emplace(MasterSystem, Sega::MasterSystem::Machine);
|
||||
Emplace(MSX, MSX::Machine);
|
||||
Emplace(Oric, Oric::Machine);
|
||||
Emplace(Vic20, Commodore::Vic20::Machine);
|
||||
Emplace(ZX8081, ZX8081::Machine);
|
||||
|
||||
#undef Emplace
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
std::map<std::string, std::unique_ptr<Analyser::Static::Target>> Machine::TargetsByMachineName(bool meaningful_without_media_only) {
|
||||
std::map<std::string, std::unique_ptr<Analyser::Static::Target>> options;
|
||||
|
||||
#define AddMapped(Name, TargetNamespace) \
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::Name), new Analyser::Static::TargetNamespace::Target));
|
||||
#define Add(Name) AddMapped(Name, Name)
|
||||
|
||||
Add(AmstradCPC);
|
||||
Add(AppleII);
|
||||
Add(AtariST);
|
||||
AddMapped(Electron, Acorn);
|
||||
Add(Macintosh);
|
||||
Add(MSX);
|
||||
Add(Oric);
|
||||
AddMapped(Vic20, Commodore);
|
||||
Add(ZX8081);
|
||||
|
||||
if(!meaningful_without_media_only) {
|
||||
Add(Atari2600);
|
||||
options.emplace(std::make_pair(LongNameForTargetMachine(Analyser::Machine::ColecoVision), new Analyser::Static::Target(Analyser::Machine::ColecoVision)));
|
||||
AddMapped(MasterSystem, Sega);
|
||||
}
|
||||
|
||||
#undef Add
|
||||
#undef AddTwo
|
||||
|
||||
return options;
|
||||
}
|
||||
|
@ -10,13 +10,25 @@
|
||||
#define MachineForTarget_hpp
|
||||
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../Reflection/Struct.hpp"
|
||||
|
||||
#include "../DynamicMachine.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/*!
|
||||
This namespace acts as a grab-bag of functions that allow a client to:
|
||||
|
||||
(i) discover the total list of implemented machines;
|
||||
(ii) discover the construction and runtime options available for controlling them; and
|
||||
(iii) create any implemented machine via its construction options.
|
||||
|
||||
See Reflection::Struct and Reflection::Enum for getting dynamic information from the
|
||||
Targets that this namespace deals in.
|
||||
*/
|
||||
namespace Machine {
|
||||
|
||||
enum class Error {
|
||||
@ -34,6 +46,12 @@ enum class Error {
|
||||
*/
|
||||
DynamicMachine *MachineForTargets(const Analyser::Static::TargetList &targets, const ::ROMMachine::ROMFetcher &rom_fetcher, Error &error);
|
||||
|
||||
/*!
|
||||
Allocates an instance of DynamicMaachine holding the machine described
|
||||
by @c target. It is the caller's responsibility to delete the class when finished.
|
||||
*/
|
||||
DynamicMachine *MachineForTarget(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher, Machine::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
|
||||
@ -47,11 +65,31 @@ std::string ShortNameForTargetMachine(const Analyser::Machine target);
|
||||
*/
|
||||
std::string LongNameForTargetMachine(const Analyser::Machine target);
|
||||
|
||||
enum class Type {
|
||||
RequiresMedia,
|
||||
DoesntRequireMedia,
|
||||
Any
|
||||
};
|
||||
|
||||
/*!
|
||||
Returns a map from machine name to the list of options that machine
|
||||
exposes, for all machines.
|
||||
@param type the type of machines to include.
|
||||
@param long_names If this is @c true then long names will be returned; otherwise short names will be returned.
|
||||
|
||||
@returns A list of all available machines. Names are always guaranteed to be in the same order.
|
||||
*/
|
||||
std::map<std::string, std::vector<std::unique_ptr<Configurable::Option>>> AllOptionsByMachineName();
|
||||
std::vector<std::string> AllMachines(Type type, bool long_names);
|
||||
|
||||
/*!
|
||||
Returns a map from long machine name to the list of options that machine exposes, for all machines.
|
||||
*/
|
||||
std::map<std::string, std::unique_ptr<Reflection::Struct>> AllOptionsByMachineName();
|
||||
|
||||
/*!
|
||||
Returns a map from long machine name to appropriate instances of Target for the machine.
|
||||
|
||||
NB: Usually the instances of Target can be dynamic_casted to Reflection::Struct in order to determine available properties.
|
||||
*/
|
||||
std::map<std::string, std::unique_ptr<Analyser::Static::Target>> TargetsByMachineName(bool meaningful_without_media_only);
|
||||
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "../../Storage/Tape/Parsers/ZX8081.hpp"
|
||||
|
||||
#include "../../ClockReceiver/ForceInline.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
|
||||
#include "../Utility/MemoryFuzzer.hpp"
|
||||
#include "../Utility/Typer.hpp"
|
||||
@ -51,12 +50,6 @@ enum ROMType: uint8_t {
|
||||
ZX80 = 0, ZX81
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
return Configurable::standard_options(
|
||||
static_cast<Configurable::StandardOptions>(Configurable::AutomaticTapeMotorControl | Configurable::QuickLoadTape)
|
||||
);
|
||||
}
|
||||
|
||||
template<bool is_zx81> class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public MediaTarget::Machine,
|
||||
@ -413,35 +406,18 @@ template<bool is_zx81> class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - Configuration options.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||
return ZX8081::get_options();
|
||||
std::unique_ptr<Reflection::Struct> get_options() final {
|
||||
auto options = std::make_unique<Options>(Configurable::OptionsType::UserFriendly); // OptionsType is arbitrary, but not optional.
|
||||
options->automatic_tape_motor_control = use_automatic_tape_motor_control_;
|
||||
options->quickload = allow_fast_tape_hack_;
|
||||
return options;
|
||||
}
|
||||
|
||||
void set_selections(const Configurable::SelectionSet &selections_by_option) final {
|
||||
bool quickload;
|
||||
if(Configurable::get_quick_load_tape(selections_by_option, quickload)) {
|
||||
allow_fast_tape_hack_ = quickload;
|
||||
set_use_fast_tape();
|
||||
}
|
||||
|
||||
bool autotapemotor;
|
||||
if(Configurable::get_automatic_tape_motor_control_selection(selections_by_option, autotapemotor)) {
|
||||
set_use_automatic_tape_motor_control(autotapemotor);
|
||||
}
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_accurate_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, false);
|
||||
Configurable::append_automatic_tape_motor_control_selection(selection_set, false);
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
Configurable::SelectionSet get_user_friendly_selections() final {
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::append_quick_load_tape_selection(selection_set, true);
|
||||
Configurable::append_automatic_tape_motor_control_selection(selection_set, true);
|
||||
return selection_set;
|
||||
void set_options(const std::unique_ptr<Reflection::Struct> &str) {
|
||||
const auto options = dynamic_cast<Options *>(str.get());
|
||||
set_use_automatic_tape_motor_control(options->automatic_tape_motor_control);
|
||||
allow_fast_tape_hack_ = options->quickload;
|
||||
set_use_fast_tape();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -10,14 +10,15 @@
|
||||
#define ZX8081_hpp
|
||||
|
||||
#include "../../Configurable/Configurable.hpp"
|
||||
#include "../../Configurable/StandardOptions.hpp"
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../ROMMachine.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace ZX8081 {
|
||||
|
||||
/// @returns The options available for a ZX80 or ZX81.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> get_options();
|
||||
|
||||
/// The ZX80/81 machine.
|
||||
class Machine {
|
||||
public:
|
||||
virtual ~Machine();
|
||||
@ -26,6 +27,24 @@ class Machine {
|
||||
|
||||
virtual void set_tape_is_playing(bool is_playing) = 0;
|
||||
virtual bool get_tape_is_playing() = 0;
|
||||
|
||||
/// Defines the runtime options available for a ZX80/81.
|
||||
class Options: public Reflection::StructImpl<Options>, public Configurable::QuickloadOption<Options> {
|
||||
friend Configurable::QuickloadOption<Options>;
|
||||
public:
|
||||
bool automatic_tape_motor_control;
|
||||
|
||||
Options(Configurable::OptionsType type):
|
||||
Configurable::QuickloadOption<Options>(type == Configurable::OptionsType::UserFriendly),
|
||||
automatic_tape_motor_control(type == Configurable::OptionsType::UserFriendly) {
|
||||
|
||||
// Declare fields if necessary.
|
||||
if(needs_declare()) {
|
||||
DeclareField(automatic_tape_motor_control);
|
||||
declare_quickload_option();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -101,8 +101,6 @@
|
||||
4B055AEF1FAE9BF00060FFFF /* Typer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B2B3A471F9B8FA70062DABF /* Typer.cpp */; };
|
||||
4B055AF11FAE9C160060FFFF /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; };
|
||||
4B055AF21FAE9C1C0060FFFF /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055AF01FAE9C080060FFFF /* OpenGL.framework */; };
|
||||
4B07835A1FC11D10001D12BB /* Configurable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0783591FC11D10001D12BB /* Configurable.cpp */; };
|
||||
4B07835B1FC11D42001D12BB /* Configurable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0783591FC11D10001D12BB /* Configurable.cpp */; };
|
||||
4B08A2751EE35D56008B7065 /* Z80InterruptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */; };
|
||||
4B08A2781EE39306008B7065 /* TestMachine.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B08A2771EE39306008B7065 /* TestMachine.mm */; };
|
||||
4B08A56920D72BEF0016CE5A /* Activity.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B08A56720D72BEF0016CE5A /* Activity.xib */; };
|
||||
@ -198,6 +196,8 @@
|
||||
4B4518A31F75FD1C00926311 /* HFE.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518951F75FD1B00926311 /* HFE.cpp */; };
|
||||
4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */; };
|
||||
4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518991F75FD1B00926311 /* SSD.cpp */; };
|
||||
4B47F6C5241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
||||
4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
||||
4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B49F0A723346F7A0045E6A6 /* MacintoshOptions.xib */; };
|
||||
4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */; };
|
||||
4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
||||
@ -362,7 +362,6 @@
|
||||
4B778F5C23A5F3070000D260 /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; };
|
||||
4B778F5D23A5F3230000D260 /* Commodore.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F21DCFD22A003085B1 /* Commodore.cpp */; };
|
||||
4B778F5E23A5F3230000D260 /* Oric.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8805F91DCFF807003085B1 /* Oric.cpp */; };
|
||||
4B778F5F23A5F3300000D260 /* Configurable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0783591FC11D10001D12BB /* Configurable.cpp */; };
|
||||
4B778F6023A5F3460000D260 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8944EC201967B4007DE474 /* Disk.cpp */; };
|
||||
4B778F6123A5F3560000D260 /* Disk.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8944FC201967B4007DE474 /* Disk.cpp */; };
|
||||
4B778F6223A5F35F0000D260 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894500201967B4007DE474 /* File.cpp */; };
|
||||
@ -914,7 +913,6 @@
|
||||
4B055ABE1FAE98000060FFFF /* MachineForTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MachineForTarget.cpp; sourceTree = "<group>"; };
|
||||
4B055ABF1FAE98000060FFFF /* MachineForTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MachineForTarget.hpp; sourceTree = "<group>"; };
|
||||
4B055AF01FAE9C080060FFFF /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
|
||||
4B0783591FC11D10001D12BB /* Configurable.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Configurable.cpp; sourceTree = "<group>"; };
|
||||
4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Z80InterruptTests.swift; sourceTree = "<group>"; };
|
||||
4B08A2761EE39306008B7065 /* TestMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachine.h; sourceTree = "<group>"; };
|
||||
4B08A2771EE39306008B7065 /* TestMachine.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestMachine.mm; sourceTree = "<group>"; };
|
||||
@ -1040,6 +1038,8 @@
|
||||
4B38F3471F2EC11D00D9235D /* AmstradCPC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AmstradCPC.hpp; path = AmstradCPC/AmstradCPC.hpp; sourceTree = "<group>"; };
|
||||
4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AsyncTaskQueue.cpp; path = ../../Concurrency/AsyncTaskQueue.cpp; sourceTree = "<group>"; };
|
||||
4B3940E61DA83C8300427841 /* AsyncTaskQueue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AsyncTaskQueue.hpp; path = ../../Concurrency/AsyncTaskQueue.hpp; sourceTree = "<group>"; };
|
||||
4B3AF7D02413470E00873C0B /* Enum.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Enum.hpp; sourceTree = "<group>"; };
|
||||
4B3AF7D12413472200873C0B /* Struct.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Struct.hpp; sourceTree = "<group>"; };
|
||||
4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = C1540Tests.swift; sourceTree = "<group>"; };
|
||||
4B3BA0C51D318B44005DD7A7 /* C1540Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = C1540Bridge.h; sourceTree = "<group>"; };
|
||||
4B3BA0C61D318B44005DD7A7 /* C1540Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = C1540Bridge.mm; sourceTree = "<group>"; };
|
||||
@ -1093,6 +1093,7 @@
|
||||
4B45189A1F75FD1B00926311 /* SSD.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SSD.hpp; sourceTree = "<group>"; };
|
||||
4B4518A71F76004200926311 /* TapeParser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = TapeParser.hpp; path = Parsers/TapeParser.hpp; sourceTree = "<group>"; };
|
||||
4B4518A81F76022000926311 /* DiskImageImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImageImplementation.hpp; sourceTree = "<group>"; };
|
||||
4B47F6C4241C87A100ED06F7 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = "<group>"; };
|
||||
4B49F0A823346F7A0045E6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MacintoshOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AY38910.cpp; path = AY38910/AY38910.cpp; sourceTree = "<group>"; };
|
||||
4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AY38910.hpp; path = AY38910/AY38910.hpp; sourceTree = "<group>"; };
|
||||
@ -2126,7 +2127,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B31B88F1FBFBCD800C140D5 /* Configurable.hpp */,
|
||||
4B0783591FC11D10001D12BB /* Configurable.cpp */,
|
||||
4BFE7B851FC39BF100160B38 /* StandardOptions.cpp */,
|
||||
4BFE7B861FC39BF100160B38 /* StandardOptions.hpp */,
|
||||
);
|
||||
@ -2194,6 +2194,17 @@
|
||||
name = Concurrency;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B3AF7CF2413470E00873C0B /* Reflection */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B3AF7D02413470E00873C0B /* Enum.hpp */,
|
||||
4B3AF7D12413472200873C0B /* Struct.hpp */,
|
||||
4B47F6C4241C87A100ED06F7 /* Struct.cpp */,
|
||||
);
|
||||
name = Reflection;
|
||||
path = ../../Reflection;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B3BA0C41D318B44005DD7A7 /* Bridges */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3263,6 +3274,7 @@
|
||||
4B366DFD1B5C165F0026627B /* Outputs */,
|
||||
4BB73EDD1B587CA500552FC2 /* Processors */,
|
||||
4BB73E9F1B587A5100552FC2 /* Products */,
|
||||
4B3AF7CF2413470E00873C0B /* Reflection */,
|
||||
4B055A7B1FAE84A50060FFFF /* SDL */,
|
||||
4B2409591C45DF85004DA684 /* SignalProcessing */,
|
||||
4B69FB391C4D908A00B5F0AA /* Storage */,
|
||||
@ -4408,6 +4420,7 @@
|
||||
4B0ACC3323775819008902D0 /* Atari2600.cpp in Sources */,
|
||||
4BD424E02193B5340097291A /* TextureTarget.cpp in Sources */,
|
||||
4B055AB51FAE860F0060FFFF /* TapePRG.cpp in Sources */,
|
||||
4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */,
|
||||
4B055AE01FAE9B660060FFFF /* CRT.cpp in Sources */,
|
||||
4B894527201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
4BB244D622AABAF600BE20E5 /* z8530.cpp in Sources */,
|
||||
@ -4425,7 +4438,6 @@
|
||||
4B055A7E1FAE84AA0060FFFF /* main.cpp in Sources */,
|
||||
4B894537201967B4007DE474 /* Z80.cpp in Sources */,
|
||||
4B055A9F1FAE85DA0060FFFF /* HFE.cpp in Sources */,
|
||||
4B07835B1FC11D42001D12BB /* Configurable.cpp in Sources */,
|
||||
4BD191F52191180E0042E144 /* ScanTarget.cpp in Sources */,
|
||||
4B055AEC1FAE9BA20060FFFF /* Z80Base.cpp in Sources */,
|
||||
4B0F94FF208C1A1600FE41D9 /* NIB.cpp in Sources */,
|
||||
@ -4484,7 +4496,6 @@
|
||||
4B894538201967B4007DE474 /* Tape.cpp in Sources */,
|
||||
4B54C0CB1F8D92590050900F /* Keyboard.cpp in Sources */,
|
||||
4BEA525E1DF33323007E74F2 /* Tape.cpp in Sources */,
|
||||
4B07835A1FC11D10001D12BB /* Configurable.cpp in Sources */,
|
||||
4B2BF19623E10F0100C3AD60 /* CSHighPrecisionTimer.m in Sources */,
|
||||
4B8334951F5E25B60097E338 /* C1540.cpp in Sources */,
|
||||
4B89453C201967B4007DE474 /* StaticAnalyser.cpp in Sources */,
|
||||
@ -4567,6 +4578,7 @@
|
||||
4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */,
|
||||
4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */,
|
||||
4B7F1897215486A200388727 /* StaticAnalyser.cpp in Sources */,
|
||||
4B47F6C5241C87A100ED06F7 /* Struct.cpp in Sources */,
|
||||
4BD3A30B1EE755C800B5B501 /* Video.cpp in Sources */,
|
||||
4B5FADBA1DE3151600AEC565 /* FileHolder.cpp in Sources */,
|
||||
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */,
|
||||
@ -4758,7 +4770,6 @@
|
||||
4B778F5623A5F2AF0000D260 /* CPM.cpp in Sources */,
|
||||
4B778F1C23A5ED3F0000D260 /* TimedEventLoop.cpp in Sources */,
|
||||
4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */,
|
||||
4B778F5F23A5F3300000D260 /* Configurable.cpp in Sources */,
|
||||
4B778F3823A5F11C0000D260 /* SegmentParser.cpp in Sources */,
|
||||
4B778F0723A5EC150000D260 /* CommodoreTAP.cpp in Sources */,
|
||||
4B778F4123A5F19A0000D260 /* MemoryPacker.cpp in Sources */,
|
||||
@ -4962,7 +4973,7 @@
|
||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
@ -4984,7 +4995,7 @@
|
||||
);
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_OPTIMIZATION_LEVEL = 2;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.12;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
@ -5104,7 +5115,6 @@
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
@ -5151,7 +5161,6 @@
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
|
@ -31,7 +31,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
disableMainThreadChecker = "YES"
|
||||
@ -56,13 +56,21 @@
|
||||
argument = "/Users/thomasharte/Downloads/test-dsk-for-rw-and-50-60-hz/TEST-RW-60Hz.DSK"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--new=amstradcpc"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/ColecoVision/Galaxian (1983)(Atari).col""
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Master System/R-Type (NTSC).sms""
|
||||
isEnabled = "NO">
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--output=CompositeMonochrome"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--logical-keyboard"
|
||||
@ -70,7 +78,7 @@
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Amstrad CPC/Amstrad CPC [TOSEC]/Amstrad CPC - Applications - [DSK] (TOSEC-v2011-08-31_CM)/Tasword (1984)(Tasman Software).dsk""
|
||||
isEnabled = "YES">
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Amstrad CPC/Robocop.dsk""
|
||||
@ -84,6 +92,14 @@
|
||||
argument = "--rompath=/Users/thomasharte/Projects/CLK/ROMImages"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--help"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--model=cpc6128"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
|
@ -67,7 +67,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
|
@ -586,9 +586,9 @@ struct ActivityObserver: public Activity::Observer {
|
||||
@synchronized(self) {
|
||||
_useFastLoadingHack = useFastLoadingHack;
|
||||
|
||||
Configurable::SelectionSet selection_set;
|
||||
append_quick_load_tape_selection(selection_set, useFastLoadingHack ? true : false);
|
||||
configurable_device->set_selections(selection_set);
|
||||
auto options = configurable_device->get_options();
|
||||
Reflection::set(*options, "quickload", useFastLoadingHack ? true : false);
|
||||
configurable_device->set_options(options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -599,7 +599,6 @@ struct ActivityObserver: public Activity::Observer {
|
||||
@synchronized(self) {
|
||||
_videoSignal = videoSignal;
|
||||
|
||||
Configurable::SelectionSet selection_set;
|
||||
Configurable::Display display;
|
||||
switch(videoSignal) {
|
||||
case CSMachineVideoSignalRGB: display = Configurable::Display::RGB; break;
|
||||
@ -607,8 +606,10 @@ struct ActivityObserver: public Activity::Observer {
|
||||
case CSMachineVideoSignalComposite: display = Configurable::Display::CompositeColour; break;
|
||||
case CSMachineVideoSignalMonochromeComposite: display = Configurable::Display::CompositeMonochrome; break;
|
||||
}
|
||||
append_display_selection(selection_set, display);
|
||||
configurable_device->set_selections(selection_set);
|
||||
|
||||
auto options = configurable_device->get_options();
|
||||
Reflection::set(*options, "output", int(display));
|
||||
configurable_device->set_options(options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -617,35 +618,23 @@ struct ActivityObserver: public Activity::Observer {
|
||||
if(!configurable_device) return NO;
|
||||
|
||||
// Get the options this machine provides.
|
||||
std::vector<std::unique_ptr<Configurable::Option>> options;
|
||||
@synchronized(self) {
|
||||
options = configurable_device->get_options();
|
||||
}
|
||||
auto options = configurable_device->get_options();
|
||||
|
||||
// Get the standard option for this video signal.
|
||||
Configurable::StandardOptions option;
|
||||
switch(videoSignal) {
|
||||
case CSMachineVideoSignalRGB: option = Configurable::DisplayRGB; break;
|
||||
case CSMachineVideoSignalSVideo: option = Configurable::DisplaySVideo; break;
|
||||
case CSMachineVideoSignalComposite: option = Configurable::DisplayCompositeColour; break;
|
||||
case CSMachineVideoSignalMonochromeComposite: option = Configurable::DisplayCompositeMonochrome; break;
|
||||
}
|
||||
std::unique_ptr<Configurable::Option> display_option = std::move(standard_options(option).front());
|
||||
Configurable::ListOption *display_list_option = dynamic_cast<Configurable::ListOption *>(display_option.get());
|
||||
NSAssert(display_list_option, @"Expected display option to be a list");
|
||||
// Get the standard option for this video signal.
|
||||
Configurable::Display option;
|
||||
switch(videoSignal) {
|
||||
case CSMachineVideoSignalRGB: option = Configurable::Display::RGB; break;
|
||||
case CSMachineVideoSignalSVideo: option = Configurable::Display::SVideo; break;
|
||||
case CSMachineVideoSignalComposite: option = Configurable::Display::CompositeColour; break;
|
||||
case CSMachineVideoSignalMonochromeComposite: option = Configurable::Display::CompositeMonochrome; break;
|
||||
}
|
||||
|
||||
// See whether the video signal is included in the machine options.
|
||||
for(auto &candidate: options) {
|
||||
Configurable::ListOption *list_option = dynamic_cast<Configurable::ListOption *>(candidate.get());
|
||||
// Map to a string and check against returned options for the 'output' field.
|
||||
const auto string_option = Reflection::Enum::to_string<Configurable::Display>(option);
|
||||
const auto all_values = options->values_for("output");
|
||||
|
||||
// Both should be list options
|
||||
if(!list_option) continue;
|
||||
|
||||
// Check for same name of option.
|
||||
if(candidate->short_name != display_option->short_name) continue;
|
||||
|
||||
// Check that the video signal option is included.
|
||||
return std::find(list_option->options.begin(), list_option->options.end(), display_list_option->options.front()) != list_option->options.end();
|
||||
return std::find(all_values.begin(), all_values.end(), string_option) != all_values.end();
|
||||
}
|
||||
|
||||
return NO;
|
||||
@ -658,9 +647,9 @@ struct ActivityObserver: public Activity::Observer {
|
||||
@synchronized(self) {
|
||||
_useAutomaticTapeMotorControl = useAutomaticTapeMotorControl;
|
||||
|
||||
Configurable::SelectionSet selection_set;
|
||||
append_automatic_tape_motor_control_selection(selection_set, useAutomaticTapeMotorControl ? true : false);
|
||||
configurable_device->set_selections(selection_set);
|
||||
auto options = configurable_device->get_options();
|
||||
Reflection::set(*options, "automatic_tape_motor_control", useAutomaticTapeMotorControl ? true : false);
|
||||
configurable_device->set_options(options);
|
||||
}
|
||||
}
|
||||
|
||||
@ -671,9 +660,9 @@ struct ActivityObserver: public Activity::Observer {
|
||||
@synchronized(self) {
|
||||
_useQuickBootingHack = useQuickBootingHack;
|
||||
|
||||
Configurable::SelectionSet selection_set;
|
||||
append_quick_boot_selection(selection_set, useQuickBootingHack ? true : false);
|
||||
configurable_device->set_selections(selection_set);
|
||||
auto options = configurable_device->get_options();
|
||||
Reflection::set(*options, "quickboot", useQuickBootingHack ? true : false);
|
||||
configurable_device->set_options(options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "../../../../../Analyser/Static/Acorn/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/AmstradCPC/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/AppleII/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/AtariST/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/Commodore/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/Macintosh/Target.hpp"
|
||||
#include "../../../../../Analyser/Static/MSX/Target.hpp"
|
||||
@ -46,7 +47,6 @@
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Acorn::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::Electron;
|
||||
target->has_dfs = !!dfs;
|
||||
target->has_adfs = !!adfs;
|
||||
_targets.push_back(std::move(target));
|
||||
@ -59,7 +59,6 @@
|
||||
if(self) {
|
||||
using Target = Analyser::Static::AmstradCPC::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::AmstradCPC;
|
||||
switch(model) {
|
||||
case CSMachineCPCModel464: target->model = Analyser::Static::AmstradCPC::Target::Model::CPC464; break;
|
||||
case CSMachineCPCModel664: target->model = Analyser::Static::AmstradCPC::Target::Model::CPC664; break;
|
||||
@ -75,7 +74,6 @@
|
||||
if(self) {
|
||||
using Target = Analyser::Static::MSX::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::MSX;
|
||||
target->has_disk_drive = !!hasDiskDrive;
|
||||
switch(region) {
|
||||
case CSMachineMSXRegionAmerican: target->region = Analyser::Static::MSX::Target::Region::USA; break;
|
||||
@ -92,7 +90,6 @@
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Oric::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::Oric;
|
||||
switch(model) {
|
||||
case CSMachineOricModelOric1: target->rom = Target::ROM::BASIC10; break;
|
||||
case CSMachineOricModelOricAtmos: target->rom = Target::ROM::BASIC11; break;
|
||||
@ -115,7 +112,6 @@
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Commodore::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::Vic20;
|
||||
switch(region) {
|
||||
case CSMachineVic20RegionDanish: target->region = Target::Region::Danish; break;
|
||||
case CSMachineVic20RegionSwedish: target->region = Target::Region::Swedish; break;
|
||||
@ -150,7 +146,6 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
if(self) {
|
||||
using Target = Analyser::Static::ZX8081::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::ZX8081;
|
||||
target->is_ZX81 = false;
|
||||
target->ZX80_uses_ZX81_ROM = !!useZX81ROM;
|
||||
target->memory_model = ZX8081MemoryModelFromSize(memorySize);
|
||||
@ -164,7 +159,6 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
if(self) {
|
||||
using Target = Analyser::Static::ZX8081::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::ZX8081;
|
||||
target->is_ZX81 = true;
|
||||
target->memory_model = ZX8081MemoryModelFromSize(memorySize);
|
||||
_targets.push_back(std::move(target));
|
||||
@ -177,7 +171,6 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
if(self) {
|
||||
using Target = Analyser::Static::AppleII::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::AppleII;
|
||||
switch(model) {
|
||||
default: target->model = Target::Model::II; break;
|
||||
case CSMachineAppleIIModelAppleIIPlus: target->model = Target::Model::IIplus; break;
|
||||
@ -200,7 +193,6 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Macintosh::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::Macintosh;
|
||||
|
||||
using Model = Target::Model;
|
||||
switch(model) {
|
||||
@ -219,9 +211,8 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K
|
||||
- (instancetype)initWithAtariSTModel:(CSMachineAtariSTModel)model {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
using Target = Analyser::Static::Macintosh::Target;
|
||||
using Target = Analyser::Static::AtariST::Target;
|
||||
auto target = std::make_unique<Target>();
|
||||
target->machine = Analyser::Machine::AtariST;
|
||||
_targets.push_back(std::move(target));
|
||||
}
|
||||
return self;
|
||||
|
@ -85,6 +85,8 @@ SOURCES += glob.glob('../../Processors/6502/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/68000/Implementation/*.cpp')
|
||||
SOURCES += glob.glob('../../Processors/Z80/Implementation/*.cpp')
|
||||
|
||||
SOURCES += glob.glob('../../Reflection/*.cpp')
|
||||
|
||||
SOURCES += glob.glob('../../SignalProcessing/*.cpp')
|
||||
|
||||
SOURCES += glob.glob('../../Storage/*.cpp')
|
||||
|
@ -13,10 +13,10 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <map>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
@ -34,6 +34,9 @@
|
||||
#include "../../Outputs/OpenGL/ScanTarget.hpp"
|
||||
#include "../../Outputs/OpenGL/Screenshot.hpp"
|
||||
|
||||
#include "../../Reflection/Enum.hpp"
|
||||
#include "../../Reflection/Struct.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
struct MachineRunner {
|
||||
@ -356,8 +359,22 @@ bool KeyboardKeyForSDLScancode(SDL_Scancode scancode, Inputs::Keyboard::Key &key
|
||||
}
|
||||
|
||||
struct ParsedArguments {
|
||||
std::string file_name;
|
||||
Configurable::SelectionSet selections;
|
||||
std::vector<std::string> file_names;
|
||||
std::map<std::string, std::string> selections; // The empty string will be inserted for arguments without an = suffix.
|
||||
|
||||
void apply(Reflection::Struct *reflectable) const {
|
||||
for(const auto &argument: selections) {
|
||||
// Replace any dashes with underscores in the argument name.
|
||||
std::string property;
|
||||
std::transform(argument.first.begin(), argument.first.end(), std::back_inserter(property), [](char c) { return c == '-' ? '_' : c; });
|
||||
|
||||
if(argument.second.empty()) {
|
||||
Reflection::set<bool>(*reflectable, property, true);
|
||||
} else {
|
||||
Reflection::fuzzy_set(*reflectable, property, argument.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*! Parses an argc/argv pair to discern program arguments. */
|
||||
@ -382,14 +399,14 @@ ParsedArguments parse_arguments(int argc, char *argv[]) {
|
||||
std::size_t split_index = argument.find("=");
|
||||
|
||||
if(split_index == std::string::npos) {
|
||||
arguments.selections[argument] = std::make_unique<Configurable::BooleanSelection>(true);
|
||||
arguments.selections[argument]; // To create an entry with the default empty string.
|
||||
} else {
|
||||
std::string name = argument.substr(0, split_index);
|
||||
const std::string name = argument.substr(0, split_index);
|
||||
std::string value = argument.substr(split_index+1, std::string::npos);
|
||||
arguments.selections[name] = std::make_unique<Configurable::ListSelection>(value);
|
||||
arguments.selections[name] = value;
|
||||
}
|
||||
} else {
|
||||
arguments.file_name = arg;
|
||||
arguments.file_names.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,52 +484,161 @@ int main(int argc, char *argv[]) {
|
||||
SDL_Window *window = nullptr;
|
||||
|
||||
// Attempt to parse arguments.
|
||||
ParsedArguments arguments = parse_arguments(argc, argv);
|
||||
const ParsedArguments arguments = parse_arguments(argc, argv);
|
||||
|
||||
// This may be printed either as
|
||||
const std::string usage_suffix = " [file] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}]"; /* [--logical-keyboard] */
|
||||
const std::string usage_suffix = " [file or --new={machine}] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}] [--logical-keyboard]";
|
||||
|
||||
// Print a help message if requested.
|
||||
if(arguments.selections.find("help") != arguments.selections.end() || arguments.selections.find("h") != arguments.selections.end()) {
|
||||
const auto all_machines = Machine::AllMachines(Machine::Type::DoesntRequireMedia, false);
|
||||
|
||||
std::cout << "Usage: " << final_path_component(argv[0]) << usage_suffix << std::endl;
|
||||
std::cout << "Use alt+enter to toggle full screen display. Use control+shift+V to paste text." << std::endl;
|
||||
std::cout << "Required machine type and configuration is determined from the file. Machines with further options:" << std::endl << std::endl;
|
||||
std::cout << "Required machine type **and all options** are determined from the file if specified; otherwise use:" << std::endl << std::endl;
|
||||
std::cout << "\t--new={";
|
||||
bool is_first = true;
|
||||
for(const auto &name: all_machines) {
|
||||
if(!is_first) std::cout << "|";
|
||||
std::cout << name;
|
||||
is_first = false;
|
||||
}
|
||||
std::cout << "}" << std::endl << std::endl;
|
||||
|
||||
auto all_options = Machine::AllOptionsByMachineName();
|
||||
for(const auto &machine_options: all_options) {
|
||||
std::cout << machine_options.first << ":" << std::endl;
|
||||
for(const auto &option: machine_options.second) {
|
||||
std::cout << '\t' << "--" << option->short_name;
|
||||
std::cout << "Media is required to start the: ";
|
||||
const auto other_machines = Machine::AllMachines(Machine::Type::RequiresMedia, true);
|
||||
is_first = true;
|
||||
for(const auto &name: other_machines) {
|
||||
if(!is_first) std::cout << ", ";
|
||||
std::cout << name;
|
||||
is_first = false;
|
||||
}
|
||||
std::cout << "." << std::endl << std::endl;
|
||||
|
||||
Configurable::ListOption *list_option = dynamic_cast<Configurable::ListOption *>(option.get());
|
||||
if(list_option) {
|
||||
std::cout << "Further machine options:" << std::endl << std::endl;;
|
||||
|
||||
const auto targets = Machine::TargetsByMachineName(false);
|
||||
const auto runtime_options = Machine::AllOptionsByMachineName();
|
||||
const auto machine_names = Machine::AllMachines(Machine::Type::Any, true);
|
||||
for(const auto &machine: machine_names) {
|
||||
const auto target = targets.find(machine);
|
||||
const auto options = runtime_options.find(machine);
|
||||
|
||||
const auto target_reflectable = dynamic_cast<Reflection::Struct *>(target != targets.end() ? target->second.get() : nullptr);
|
||||
const auto options_reflectable = dynamic_cast<Reflection::Struct *>(options != runtime_options.end() ? options->second.get() : nullptr);
|
||||
|
||||
// Don't print a section for this machine if it has no construction and no runtime options objects.
|
||||
if(!target_reflectable && !options_reflectable) continue;
|
||||
|
||||
const auto target_keys = target_reflectable ? target_reflectable->all_keys() : std::vector<std::string>();
|
||||
const auto options_keys = options_reflectable ? options_reflectable->all_keys() : std::vector<std::string>();
|
||||
|
||||
// Don't print a section for this machine if it doesn't actually have any options.
|
||||
if(target_keys.empty() && options_keys.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::cout << machine << ":" << std::endl;
|
||||
|
||||
// Join the two lists of properties and sort the result.
|
||||
std::vector<std::string> all_options = options_keys;
|
||||
all_options.insert(all_options.end(), target_keys.begin(), target_keys.end());
|
||||
std::sort(all_options.begin(), all_options.end());
|
||||
|
||||
for(const auto &option: all_options) {
|
||||
// Replace any underscores with hyphens, better to conform to command-line norms.
|
||||
std::string mapped_option;
|
||||
std::transform(option.begin(), option.end(), std::back_inserter(mapped_option), [](char c) { return c == '_' ? '-' : c; });
|
||||
std::cout << '\t' << "--" << mapped_option;
|
||||
|
||||
auto source = target_reflectable;
|
||||
auto type = target_reflectable ? target_reflectable->type_of(option) : nullptr;
|
||||
if(!type) {
|
||||
source = options_reflectable;
|
||||
type = options_reflectable->type_of(option);
|
||||
}
|
||||
|
||||
// Is this a registered enum? If so, list options.
|
||||
if(!Reflection::Enum::name(*type).empty()) {
|
||||
std::cout << "={";
|
||||
bool is_first = true;
|
||||
for(const auto &option: list_option->options) {
|
||||
for(const auto &value: source->values_for(option)) {
|
||||
if(!is_first) std::cout << '|';
|
||||
is_first = false;
|
||||
std::cout << option;
|
||||
|
||||
std::cout << value;
|
||||
}
|
||||
std::cout << "}";
|
||||
}
|
||||
|
||||
// The above effectively assumes that every field is either a
|
||||
// Boolean or an enum. This may need to be revisted. It also
|
||||
// assumes no name collisions, but that's kind of unavoidable.
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// Perform a sanity check on arguments.
|
||||
if(arguments.file_name.empty()) {
|
||||
std::cerr << "Usage: " << final_path_component(argv[0]) << usage_suffix << std::endl;
|
||||
std::cerr << "Use --help to learn more about available options." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
// Determine the machine for the supplied file, if any, or from --new.
|
||||
Analyser::Static::TargetList targets;
|
||||
|
||||
const auto new_argument = arguments.selections.find("new");
|
||||
std::string long_machine_name;
|
||||
if(new_argument != arguments.selections.end() && !new_argument->second.empty()) {
|
||||
// Perform for a case-insensitive search against short names.
|
||||
const auto short_names = Machine::AllMachines(Machine::Type::DoesntRequireMedia, false);
|
||||
auto short_name = short_names.begin();
|
||||
while(short_name != short_names.end()) {
|
||||
if(std::equal(
|
||||
short_name->begin(), short_name->end(),
|
||||
new_argument->second.begin(), new_argument->second.end(),
|
||||
[](char a, char b) { return tolower(b) == tolower(a); })) {
|
||||
break;
|
||||
}
|
||||
++short_name;
|
||||
}
|
||||
|
||||
// If a match was found, use the corresponding long name to look up a suitable
|
||||
// Analyser::Statuc::Target and move that to the targets list.
|
||||
if(short_name != short_names.end()) {
|
||||
long_machine_name = Machine::AllMachines(Machine::Type::DoesntRequireMedia, true)[short_name - short_names.begin()];
|
||||
auto targets_by_machine = Machine::TargetsByMachineName(false);
|
||||
std::unique_ptr<Analyser::Static::Target> tgt = std::move(targets_by_machine[long_machine_name]);
|
||||
targets.push_back(std::move(tgt));
|
||||
}
|
||||
} else if(!arguments.file_names.empty()) {
|
||||
// Take the first file name that actually implies a machine.
|
||||
auto file_name = arguments.file_names.begin();
|
||||
while(file_name != arguments.file_names.end() && targets.empty()) {
|
||||
targets = Analyser::Static::GetTargets(*file_name);
|
||||
++file_name;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the machine for the supplied file.
|
||||
const auto targets = Analyser::Static::GetTargets(arguments.file_name);
|
||||
if(targets.empty()) {
|
||||
std::cerr << "Cannot open " << arguments.file_name << "; no target machine found" << std::endl;
|
||||
if(!arguments.file_names.empty()) {
|
||||
std::cerr << "Cannot open ";
|
||||
bool is_first = true;
|
||||
for(const auto &name: arguments.file_names) {
|
||||
if(!is_first) std::cerr << ", ";
|
||||
is_first = false;
|
||||
std::cerr << name;
|
||||
}
|
||||
std::cerr << "; no target machine found" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(!new_argument->second.empty()) {
|
||||
std::cerr << "Unknown machine: " << new_argument->second << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cerr << "Usage: " << final_path_component(argv[0]) << usage_suffix << std::endl;
|
||||
std::cerr << "Use --help to learn more about available options." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -533,12 +659,13 @@ int main(int argc, char *argv[]) {
|
||||
"/usr/local/share/CLK/",
|
||||
"/usr/share/CLK/"
|
||||
};
|
||||
if(arguments.selections.find("rompath") != arguments.selections.end()) {
|
||||
const std::string user_path = arguments.selections["rompath"]->list_selection()->value;
|
||||
if(user_path.back() != '/') {
|
||||
paths.push_back(user_path + "/");
|
||||
|
||||
const auto rompath = arguments.selections.find("rompath");
|
||||
if(rompath != arguments.selections.end()) {
|
||||
if(rompath->second.back() != '/') {
|
||||
paths.push_back(rompath->second + "/");
|
||||
} else {
|
||||
paths.push_back(user_path);
|
||||
paths.push_back(rompath->second);
|
||||
}
|
||||
}
|
||||
|
||||
@ -573,6 +700,13 @@ int main(int argc, char *argv[]) {
|
||||
return results;
|
||||
};
|
||||
|
||||
// Apply all command-line options to the targets.
|
||||
for(auto &target: targets) {
|
||||
auto reflectable_target = dynamic_cast<Reflection::Struct *>(target.get());
|
||||
if(!reflectable_target) continue;
|
||||
arguments.apply(reflectable_target);
|
||||
}
|
||||
|
||||
// Create and configure a machine.
|
||||
::Machine::Error error;
|
||||
std::mutex machine_mutex;
|
||||
@ -596,9 +730,18 @@ int main(int argc, char *argv[]) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Apply all command-line options to the machines.
|
||||
auto configurable = machine->configurable_device();
|
||||
if(configurable) {
|
||||
const auto options = configurable->get_options();
|
||||
arguments.apply(options.get());
|
||||
configurable->set_options(options);
|
||||
}
|
||||
|
||||
// Apply the speed multiplier, if one was requested.
|
||||
if(arguments.selections.find("speed") != arguments.selections.end()) {
|
||||
const char *speed_string = arguments.selections["speed"]->list_selection()->value.c_str();
|
||||
const auto speed_argument = arguments.selections.find("speed");
|
||||
if(speed_argument != arguments.selections.end()) {
|
||||
const char *speed_string = speed_argument->second.c_str();
|
||||
char *end;
|
||||
double speed = strtod(speed_string, &end);
|
||||
|
||||
@ -621,6 +764,18 @@ int main(int argc, char *argv[]) {
|
||||
machine_runner.machine = machine.get();
|
||||
machine_runner.machine_mutex = &machine_mutex;
|
||||
|
||||
// Ensure all media is inserted, if this machine accepts it.
|
||||
{
|
||||
auto media_target = machine->media_target();
|
||||
if(media_target) {
|
||||
Analyser::Static::Media media;
|
||||
for(const auto &file_name: arguments.file_names) {
|
||||
media += Analyser::Static::GetMedia(file_name);
|
||||
}
|
||||
media_target->insert_media(media);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to set up video and audio.
|
||||
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
||||
std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
|
||||
@ -634,7 +789,7 @@ int main(int argc, char *argv[]) {
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
window = SDL_CreateWindow( final_path_component(arguments.file_name).c_str(),
|
||||
window = SDL_CreateWindow( long_machine_name.empty() ? final_path_component(arguments.file_names.front()).c_str() : long_machine_name.c_str(),
|
||||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
400, 300,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||
@ -686,31 +841,6 @@ int main(int argc, char *argv[]) {
|
||||
int window_width, window_height;
|
||||
SDL_GetWindowSize(window, &window_width, &window_height);
|
||||
|
||||
Configurable::Device *const configurable_device = machine->configurable_device();
|
||||
if(configurable_device) {
|
||||
// Establish user-friendly options by default.
|
||||
configurable_device->set_selections(configurable_device->get_user_friendly_selections());
|
||||
|
||||
// Consider transcoding any list selections that map to Boolean options.
|
||||
for(const auto &option: configurable_device->get_options()) {
|
||||
// Check for a corresponding selection.
|
||||
auto selection = arguments.selections.find(option->short_name);
|
||||
if(selection != arguments.selections.end()) {
|
||||
// Transcode selection if necessary.
|
||||
if(dynamic_cast<Configurable::BooleanOption *>(option.get())) {
|
||||
arguments.selections[selection->first] = std::unique_ptr<Configurable::Selection>(selection->second->boolean_selection());
|
||||
}
|
||||
|
||||
if(dynamic_cast<Configurable::ListOption *>(option.get())) {
|
||||
arguments.selections[selection->first] = std::unique_ptr<Configurable::Selection>(selection->second->list_selection());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the user's actual selections to final the defaults.
|
||||
configurable_device->set_selections(arguments.selections);
|
||||
}
|
||||
|
||||
// If this is a joystick machine, check for and open attached joysticks.
|
||||
/*!
|
||||
Provides a wrapper for SDL_Joystick pointers that can keep track
|
||||
|
@ -93,6 +93,10 @@ void CRT::set_display_type(Outputs::Display::DisplayType display_type) {
|
||||
scan_target_->set_modals(scan_target_modals_);
|
||||
}
|
||||
|
||||
Outputs::Display::DisplayType CRT::get_display_type() {
|
||||
return scan_target_modals_.display_type;
|
||||
}
|
||||
|
||||
void CRT::set_phase_linked_luminance_offset(float offset) {
|
||||
scan_target_modals_.input_data_tweaks.phase_linked_luminance_offset = offset;
|
||||
scan_target_->set_modals(scan_target_modals_);
|
||||
|
@ -298,6 +298,9 @@ class CRT {
|
||||
/*! Sets the display type that will be nominated to the scan target. */
|
||||
void set_display_type(Outputs::Display::DisplayType);
|
||||
|
||||
/*! Gets the last display type provided to set_display_type. */
|
||||
Outputs::Display::DisplayType get_display_type();
|
||||
|
||||
/*! Sets the offset to apply to phase when using the PhaseLinkedLuminance8 input data type. */
|
||||
void set_phase_linked_luminance_offset(float);
|
||||
|
||||
|
173
Reflection/Enum.hpp
Normal file
173
Reflection/Enum.hpp
Normal file
@ -0,0 +1,173 @@
|
||||
//
|
||||
// Enum.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 17/02/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Enum_hpp
|
||||
#define Enum_hpp
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Reflection {
|
||||
|
||||
#define ReflectableEnum(Name, ...) \
|
||||
enum class Name { __VA_ARGS__ }; \
|
||||
constexpr static const char *__declaration##Name = #__VA_ARGS__;
|
||||
|
||||
#define EnumDeclaration(Name) #Name, __declaration##Name
|
||||
|
||||
#define AnnounceEnum(Name) ::Reflection::Enum::declare<Name>(EnumDeclaration(Name))
|
||||
#define AnnounceEnumNS(Namespace, Name) ::Reflection::Enum::declare<Namespace::Name>(#Name, Namespace::__declaration##Name)
|
||||
|
||||
/*!
|
||||
This provides a very slight version of enum reflection; you can introspect only:
|
||||
|
||||
* enums have been registered, along with the text of their declarations;
|
||||
* provided that those enums do not declare specific values for their members.
|
||||
|
||||
The macros above help avoid duplication of the declaration, making this just mildly less
|
||||
terrible than it might have been.
|
||||
|
||||
No guarantees of speed or any other kind of efficiency are offered.
|
||||
*/
|
||||
class Enum {
|
||||
public:
|
||||
/*!
|
||||
Registers @c name and the entries within @c declaration for the enum type @c Type.
|
||||
|
||||
Assuming the caller used the macros above, a standard pattern where both things can be placed in
|
||||
the same namespace might look like:
|
||||
|
||||
ReflectableEnum(MyEnum, int, A, B, C);
|
||||
|
||||
...
|
||||
|
||||
AnnounceEnum(MyEnum)
|
||||
|
||||
If AnnounceEnum cannot be placed into the same namespace as ReflectableEnum, see the
|
||||
EnumDeclaration macro.
|
||||
*/
|
||||
template <typename Type> static void declare(const char *name, const char *declaration) {
|
||||
const char *d_ptr = declaration;
|
||||
|
||||
std::vector<std::string> result;
|
||||
while(true) {
|
||||
// Skip non-alphas, and exit if the terminator is found.
|
||||
while(*d_ptr && !isalpha(*d_ptr)) ++d_ptr;
|
||||
if(!*d_ptr) break;
|
||||
|
||||
// Note the current location and proceed for all alphas and digits.
|
||||
const auto start = d_ptr;
|
||||
while(isalpha(*d_ptr) || isdigit(*d_ptr)) ++d_ptr;
|
||||
|
||||
// Add a string view.
|
||||
result.emplace_back(std::string(start, size_t(d_ptr - start)));
|
||||
}
|
||||
|
||||
members_by_type_.emplace(std::make_pair(std::type_index(typeid(Type)), result));
|
||||
names_by_type_.emplace(std::make_pair(std::type_index(typeid(Type)), std::string(name)));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns the declared name of the enum @c Type if it has been registered; the empty string otherwise.
|
||||
*/
|
||||
template <typename Type> static const std::string &name() {
|
||||
return name(typeid(Type));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns the declared name of the enum with type_info @c type if it has been registered; the empty string otherwise.
|
||||
*/
|
||||
static const std::string &name(std::type_index type) {
|
||||
const auto entry = names_by_type_.find(type);
|
||||
if(entry == names_by_type_.end()) return empty_string_;
|
||||
return entry->second;
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns the number of members of the enum @c Type if it has been registered; 0 otherwise.
|
||||
*/
|
||||
template <typename Type> static size_t size() {
|
||||
return size(typeid(Type));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns the number of members of the enum with type_info @c type if it has been registered; @c std::string::npos otherwise.
|
||||
*/
|
||||
static size_t size(std::type_index type) {
|
||||
const auto entry = members_by_type_.find(type);
|
||||
if(entry == members_by_type_.end()) return std::string::npos;
|
||||
return entry->second.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns A @c std::string name for the enum value @c e.
|
||||
*/
|
||||
template <typename Type> static const std::string &to_string(Type e) {
|
||||
return to_string(typeid(Type), int(e));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns A @c std::string name for the enum value @c e from the enum with type_info @c type.
|
||||
*/
|
||||
static const std::string &to_string(std::type_index type, int e) {
|
||||
const auto entry = members_by_type_.find(type);
|
||||
if(entry == members_by_type_.end()) return empty_string_;
|
||||
return entry->second[size_t(e)];
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns a vector naming the members of the enum with type_info @c type if it has been registered; an empty vector otherwise.
|
||||
*/
|
||||
static const std::vector<std::string> &all_values(std::type_index type) {
|
||||
const auto entry = members_by_type_.find(type);
|
||||
if(entry == members_by_type_.end()) return empty_vector_;
|
||||
return entry->second;
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns a vector naming the members of the enum @c Type type if it has been registered; an empty vector otherwise.
|
||||
*/
|
||||
template <typename Type> static const std::vector<std::string> &all_values() {
|
||||
return all_values(typeid(Type));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns A value of @c Type for the name @c str, or @c EnumType(std::string::npos) if
|
||||
the name is not found.
|
||||
*/
|
||||
template <typename Type> static Type from_string(const std::string &str) {
|
||||
return Type(from_string(typeid(Type), str));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns A value for the name @c str in the enum with type_info @c type , or @c -1 if
|
||||
the name is not found.
|
||||
*/
|
||||
static int from_string(std::type_index type, const std::string &str) {
|
||||
const auto entry = members_by_type_.find(type);
|
||||
if(entry == members_by_type_.end()) return -1;
|
||||
const auto iterator = std::find(entry->second.begin(), entry->second.end(), str);
|
||||
if(iterator == entry->second.end()) return -1;
|
||||
return int(iterator - entry->second.begin());
|
||||
}
|
||||
|
||||
private:
|
||||
static inline std::unordered_map<std::type_index, std::vector<std::string>> members_by_type_;
|
||||
static inline std::unordered_map<std::type_index, std::string> names_by_type_;
|
||||
static inline const std::string empty_string_;
|
||||
static inline const std::vector<std::string> empty_vector_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* Enum_hpp */
|
124
Reflection/Struct.cpp
Normal file
124
Reflection/Struct.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
//
|
||||
// Struct.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 13/03/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "Struct.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// MARK: - Setters
|
||||
|
||||
template <> bool Reflection::set(Struct &target, const std::string &name, int value) {
|
||||
const auto target_type = target.type_of(name);
|
||||
if(!target_type) return false;
|
||||
|
||||
// No need to convert an int or a registered enum.
|
||||
if(*target_type == typeid(int) || !Reflection::Enum::name(*target_type).empty()) {
|
||||
target.set(name, &value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Promote to an int64_t.
|
||||
if(*target_type == typeid(int64_t)) {
|
||||
const auto int64 = int64_t(value);
|
||||
target.set(name, &int64);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <> bool Reflection::set(Struct &target, const std::string &name, const std::string &value) {
|
||||
const auto target_type = target.type_of(name);
|
||||
if(!target_type) return false;
|
||||
|
||||
if(Reflection::Enum::name(*target_type).empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int enum_value = Reflection::Enum::from_string(*target_type, value);
|
||||
if(enum_value < 0) {
|
||||
return false;
|
||||
}
|
||||
target.set(name, &enum_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <> bool Reflection::set(Struct &target, const std::string &name, const char *value) {
|
||||
const std::string string(value);
|
||||
return set<const std::string &>(target, name, string);
|
||||
}
|
||||
|
||||
template <> bool Reflection::set(Struct &target, const std::string &name, bool value) {
|
||||
const auto target_type = target.type_of(name);
|
||||
if(!target_type) return false;
|
||||
|
||||
if(*target_type == typeid(bool)) {
|
||||
target.set(name, &value);;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// MARK: - Fuzzy setter
|
||||
|
||||
bool Reflection::fuzzy_set(Struct &target, const std::string &name, const std::string &value) {
|
||||
const auto target_type = target.type_of(name);
|
||||
if(!target_type) return false;
|
||||
|
||||
// If the target is a registered enum, ttry to convert the value. Failing that,
|
||||
// try to match without case sensitivity.
|
||||
if(Reflection::Enum::size(*target_type)) {
|
||||
const int from_string = Reflection::Enum::from_string(*target_type, value);
|
||||
if(from_string >= 0) {
|
||||
target.set(name, &from_string);
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto all_values = Reflection::Enum::all_values(*target_type);
|
||||
const auto value_location = std::find_if(all_values.begin(), all_values.end(),
|
||||
[&value] (const auto &entry) {
|
||||
if(value.size() != entry.size()) return false;
|
||||
const char *v = value.c_str();
|
||||
const char *e = entry.c_str();
|
||||
while(*v) {
|
||||
if(tolower(*v) != tolower(*e)) return false;
|
||||
++v;
|
||||
++e;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if(value_location != all_values.end()) {
|
||||
const int offset = int(value_location - all_values.begin());
|
||||
target.set(name, &offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// MARK: - Getters
|
||||
|
||||
template <typename Type> bool Reflection::get(Struct &target, const std::string &name, Type &value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <> bool Reflection::get(Struct &target, const std::string &name, bool &value) {
|
||||
const auto target_type = target.type_of(name);
|
||||
if(!target_type) return false;
|
||||
|
||||
if(*target_type == typeid(bool)) {
|
||||
value = *reinterpret_cast<const bool *>(target.get(name));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
272
Reflection/Struct.hpp
Normal file
272
Reflection/Struct.hpp
Normal file
@ -0,0 +1,272 @@
|
||||
//
|
||||
// Struct.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 06/03/2020.
|
||||
// Copyright © 2020 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Struct_hpp
|
||||
#define Struct_hpp
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Enum.hpp"
|
||||
|
||||
namespace Reflection {
|
||||
|
||||
#define DeclareField(Name) declare(&Name, #Name)
|
||||
|
||||
struct Struct {
|
||||
virtual std::vector<std::string> all_keys() = 0;
|
||||
virtual const std::type_info *type_of(const std::string &name) = 0;
|
||||
virtual void set(const std::string &name, const void *value) = 0;
|
||||
virtual const void *get(const std::string &name) = 0;
|
||||
virtual std::vector<std::string> values_for(const std::string &name) = 0;
|
||||
virtual ~Struct() {}
|
||||
};
|
||||
|
||||
/*!
|
||||
Attempts to set the property @c name to @c value ; will perform limited type conversions.
|
||||
|
||||
@returns @c true if the property was successfully set; @c false otherwise.
|
||||
*/
|
||||
template <typename Type> bool set(Struct &target, const std::string &name, Type value);
|
||||
|
||||
/*!
|
||||
Setting an int:
|
||||
|
||||
* to an int copies the int;
|
||||
* to an int64_t promotes the int; and
|
||||
* to a registered enum, copies the int.
|
||||
*/
|
||||
template <> bool set(Struct &target, const std::string &name, int value);
|
||||
|
||||
/*!
|
||||
Setting a string:
|
||||
|
||||
* to an enum, if the string names a member of the enum, sets the value.
|
||||
*/
|
||||
template <> bool set(Struct &target, const std::string &name, const std::string &value);
|
||||
template <> bool set(Struct &target, const std::string &name, const char *value);
|
||||
|
||||
/*!
|
||||
Setting a bool:
|
||||
|
||||
* to a bool, copies the value.
|
||||
*/
|
||||
template <> bool set(Struct &target, const std::string &name, bool value);
|
||||
|
||||
|
||||
/*!
|
||||
Fuzzy-set attempts to set any property based on a string value. This is intended to allow input provided by the user.
|
||||
|
||||
Amongst other steps, it might:
|
||||
* if the target is a bool, map true, false, yes, no, y, n, etc;
|
||||
* if the target is an integer, parse like strtrol;
|
||||
* if the target is a float, parse like strtod; or
|
||||
* if the target is a reflective enum, attempt to match to enum members (possibly doing so in a case insensitive fashion).
|
||||
|
||||
This method reserves the right to perform more or fewer attempted mappings, using any other logic it
|
||||
decides is appropriate.
|
||||
|
||||
@returns @c true if the property was successfully set; @c false otherwise.
|
||||
*/
|
||||
bool fuzzy_set(Struct &target, const std::string &name, const std::string &value);
|
||||
|
||||
|
||||
/*!
|
||||
Attempts to get the property @c name to @c value ; will perform limited type conversions.
|
||||
|
||||
@returns @c true if the property was successfully read; @c false otherwise.
|
||||
*/
|
||||
template <typename Type> bool get(Struct &target, const std::string &name, Type &value);
|
||||
|
||||
template <> bool get(Struct &target, const std::string &name, bool &value);
|
||||
|
||||
|
||||
// TODO: move this elsewhere. It's just a sketch anyway.
|
||||
struct Serialisable {
|
||||
/// Serialises this object, appending it to @c target.
|
||||
virtual void serialise(std::vector<uint8_t> &target) = 0;
|
||||
/// Deserialises this object from @c source.
|
||||
/// @returns @c true if the deserialisation was successful; @c false otherwise.
|
||||
virtual bool deserialise(const std::vector<uint8_t> &source) = 0;
|
||||
};
|
||||
|
||||
template <typename Owner> class StructImpl: public Struct {
|
||||
public:
|
||||
/*!
|
||||
@returns the value of type @c Type that is loaded from the offset registered for the field @c name.
|
||||
It is the caller's responsibility to provide an appropriate type of data.
|
||||
*/
|
||||
const void *get(const std::string &name) final {
|
||||
const auto iterator = contents_.find(name);
|
||||
if(iterator == contents_.end()) return nullptr;
|
||||
return reinterpret_cast<uint8_t *>(this) + iterator->second.offset;
|
||||
}
|
||||
|
||||
/*!
|
||||
Stores the @c value of type @c Type to the offset registered for the field @c name.
|
||||
|
||||
It is the caller's responsibility to provide an appropriate type of data.
|
||||
*/
|
||||
void set(const std::string &name, const void *value) final {
|
||||
const auto iterator = contents_.find(name);
|
||||
if(iterator == contents_.end()) return;
|
||||
memcpy(reinterpret_cast<uint8_t *>(this) + iterator->second.offset, value, iterator->second.size);
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns @c type_info for the field @c name.
|
||||
*/
|
||||
const std::type_info *type_of(const std::string &name) final {
|
||||
const auto iterator = contents_.find(name);
|
||||
if(iterator == contents_.end()) return nullptr;
|
||||
return iterator->second.type;
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns a list of the valid enum value names for field @c name if it is a declared enum field of this struct;
|
||||
the empty list otherwise.
|
||||
*/
|
||||
std::vector<std::string> values_for(const std::string &name) final {
|
||||
std::vector<std::string> result;
|
||||
|
||||
// Return an empty vector if this field isn't declared.
|
||||
const auto type = type_of(name);
|
||||
if(!type) return result;
|
||||
|
||||
// Also return an empty vector if this field isn't a registered enum.
|
||||
const auto all_values = Enum::all_values(*type);
|
||||
if(all_values.empty()) return result;
|
||||
|
||||
// If no restriction is stored, return all values.
|
||||
const auto permitted_values = permitted_enum_values_.find(name);
|
||||
if(permitted_values == permitted_enum_values_.end()) return all_values;
|
||||
|
||||
// Compile a vector of only those values the stored set indicates.
|
||||
auto value = all_values.begin();
|
||||
auto flag = permitted_values->second.begin();
|
||||
while(value != all_values.end() && flag != permitted_values->second.end()) {
|
||||
if(*flag) {
|
||||
result.push_back(*value);
|
||||
}
|
||||
++flag;
|
||||
++value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns A vector of all declared fields for this struct.
|
||||
*/
|
||||
std::vector<std::string> all_keys() final {
|
||||
std::vector<std::string> keys;
|
||||
for(const auto &pair: contents_) {
|
||||
keys.push_back(pair.first);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
protected:
|
||||
/*
|
||||
This interface requires reflective structs to declare all fields;
|
||||
specifically they should call:
|
||||
|
||||
declare_field(&field1, "field1");
|
||||
declare_field(&field2, "field2");
|
||||
|
||||
Fields are registered in class storage. So callers can use needs_declare()
|
||||
to determine whether a class of this type has already established the
|
||||
reflective fields.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Exposes the field pointed to by @c t for reflection as @c name.
|
||||
*/
|
||||
template <typename Type> void declare(Type *t, const std::string &name) {
|
||||
contents_.emplace(
|
||||
std::make_pair(
|
||||
name,
|
||||
Field(typeid(Type), reinterpret_cast<uint8_t *>(t) - reinterpret_cast<uint8_t *>(this), sizeof(Type))
|
||||
));
|
||||
}
|
||||
|
||||
/*!
|
||||
If @c t is a previously-declared field that links to a declared enum then the variable
|
||||
arguments provide a list of the acceptable values for that field. The list should be terminated
|
||||
with a value of -1.
|
||||
*/
|
||||
template <typename Type> void limit_enum(Type *t, ...) {
|
||||
const auto name = name_of(t);
|
||||
if(name.empty()) return;
|
||||
|
||||
// The default vector size of '8' isn't especially scientific,
|
||||
// but I feel like it's a good choice.
|
||||
std::vector<bool> permitted_values(8);
|
||||
|
||||
va_list list;
|
||||
va_start(list, t);
|
||||
while(true) {
|
||||
const int next = va_arg(list, int);
|
||||
if(next < 0) break;
|
||||
|
||||
if(permitted_values.size() <= size_t(next)) {
|
||||
permitted_values.resize(permitted_values.size() << 1);
|
||||
}
|
||||
permitted_values[size_t(next)] = true;
|
||||
}
|
||||
va_end(list);
|
||||
|
||||
permitted_enum_values_.emplace(std::make_pair(name, permitted_values));
|
||||
}
|
||||
|
||||
/*!
|
||||
@returns @c true if this subclass of @c Struct has not yet declared any fields.
|
||||
*/
|
||||
bool needs_declare() {
|
||||
return !contents_.size();
|
||||
}
|
||||
|
||||
/*!
|
||||
Performs a reverse lookup from field to name.
|
||||
*/
|
||||
std::string name_of(void *field) {
|
||||
const ssize_t offset = reinterpret_cast<uint8_t *>(field) - reinterpret_cast<uint8_t *>(this);
|
||||
|
||||
auto iterator = contents_.begin();
|
||||
while(iterator != contents_.end()) {
|
||||
if(iterator->second.offset == offset) break;
|
||||
++iterator;
|
||||
}
|
||||
|
||||
if(iterator != contents_.end()) {
|
||||
return iterator->first;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct Field {
|
||||
const std::type_info *type;
|
||||
ssize_t offset;
|
||||
size_t size;
|
||||
Field(const std::type_info &type, ssize_t offset, size_t size) :
|
||||
type(&type), offset(offset), size(size) {}
|
||||
};
|
||||
static inline std::unordered_map<std::string, Field> contents_;
|
||||
static inline std::unordered_map<std::string, std::vector<bool>> permitted_enum_values_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* Struct_hpp */
|
Loading…
x
Reference in New Issue
Block a user