1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-09 15:39:08 +00:00

Merge pull request #772 from TomHarte/ReflectiveEnum

Endeavours to bring introspection to machine selection options.
This commit is contained in:
Thomas Harte 2020-03-19 23:30:19 -04:00 committed by GitHub
commit 129bc485bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 1639 additions and 795 deletions

View File

@ -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_);
}

View File

@ -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_;

View File

@ -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;

View File

@ -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);
}
}
};
}

View File

@ -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;

View File

@ -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);
}
}
};
}

View File

@ -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())

View File

@ -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);
}
}
};
}

View File

@ -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;

View File

@ -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) {}
};
}

View File

@ -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));

View File

@ -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) {}
};
}

View File

@ -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())

View File

@ -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);
}
}
};
}

View File

@ -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";

View File

@ -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));
}

View File

@ -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);
}
}
};
}

View File

@ -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));

View File

@ -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);
}
}
};
}

View File

@ -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;

View File

@ -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);
}
}
};
}

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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);
}
}
};
}

View File

@ -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) {

View File

@ -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;

View File

@ -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.

View File

@ -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);
}

View File

@ -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
};
}

View File

@ -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);
}
}*/

View File

@ -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");
}
};
}

View File

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

View File

@ -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);
}
}
};
};
}

View File

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

View File

@ -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);
}
}
};
};
}

View File

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

View File

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

View File

@ -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;
}

View File

@ -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();
}
}
};
};

View File

@ -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);
}
};

View File

@ -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);
}
}
};
};
}

View File

@ -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);

View File

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

View File

@ -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;

View File

@ -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:

View File

@ -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);
}
}
};
};
}

View File

@ -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 {

View File

@ -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);
}
}
};
};
}

View File

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

View File

@ -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);
}
}
};
};
}

View File

@ -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() {

View File

@ -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.

View File

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

View File

@ -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();
}
}
};
};
}

View File

@ -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:

View File

@ -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();
}
}
};
};
}

View File

@ -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 {

View File

@ -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();
}
}
};
};
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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:

View File

@ -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();
}
}
};
};
}

View File

@ -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;

View File

@ -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 = "&quot;/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/ColecoVision/Galaxian (1983)(Atari).col&quot;"
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "&quot;/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Master System/R-Type (NTSC).sms&quot;"
isEnabled = "NO">
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "--output=CompositeMonochrome"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "--logical-keyboard"
@ -70,7 +78,7 @@
</CommandLineArgument>
<CommandLineArgument
argument = "&quot;/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&quot;"
isEnabled = "YES">
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "&quot;/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Amstrad CPC/Robocop.dsk&quot;"
@ -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

View File

@ -67,7 +67,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableASanStackUseAfterReturn = "YES"

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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')

View File

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

View File

@ -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_);

View File

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