1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-01 22:41:32 +00:00

Starts a switch to reflectable-style runtime options.

The Amstrad CPC and ZX80/81 have made the jump so far, subject to caveats. The macOS build is unlikely currently to work properly.
This commit is contained in:
Thomas Harte 2020-03-16 23:25:05 -04:00
parent 1d40aa687e
commit 394ee61c78
24 changed files with 163 additions and 141 deletions

View File

@ -22,7 +22,7 @@ MultiConfigurable::MultiConfigurable(const std::vector<std::unique_ptr<::Machine
void MultiConfigurable::set_options(const std::unique_ptr<Reflection::Struct> &options) {
}
std::unique_ptr<Reflection::Struct> MultiConfigurable::get_options(OptionsType type) {
std::unique_ptr<Reflection::Struct> MultiConfigurable::get_options() {
// TODO: this'll need to mash options together, maybe? Or just take the front?
return nullptr;
}

View File

@ -30,7 +30,7 @@ class MultiConfigurable: public Configurable::Device {
// Below is the standard Configurable::Device interface; see there for documentation.
void set_options(const std::unique_ptr<Reflection::Struct> &options) final;
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final;
std::unique_ptr<Reflection::Struct> get_options() final;
// 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;

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

@ -17,27 +17,28 @@ namespace Configurable {
/*!
A Configurable::Device provides a reflective struct listing the available runtime options for this machine.
It can provide it populated with 'accurate' options, 'user-friendly' options or just whatever the user
currently has selected.
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 {
/// 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;
};
/*!
'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.
*/
struct Device {
/// Sets the current options.
virtual void set_options(const std::unique_ptr<Reflection::Struct> &options) = 0;
enum class OptionsType {
Current,
Accurate,
UserFriendly
};
/// @returns Options of type @c type.
virtual std::unique_ptr<Reflection::Struct> get_options(OptionsType type) = 0;
enum class OptionsType {
Accurate,
UserFriendly
};
}

View File

@ -40,16 +40,6 @@
namespace AmstradCPC {
//std::vector<std::unique_ptr<Configurable::Option>> get_options() {
// return Configurable::standard_options(
// Configurable::StandardOptions(Configurable::DisplayRGB | Configurable::DisplayCompositeColour)
// );
//}
std::unique_ptr<Reflection::Struct> get_options() {
return nullptr;
}
/*!
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
@ -358,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".
@ -1049,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();
@ -1118,34 +1118,16 @@ template <bool has_fdc> class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
return nullptr;
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_options(const std::unique_ptr<Reflection::Struct> &options) {
void set_options(const std::unique_ptr<Reflection::Struct> &str) {
const auto options = dynamic_cast<Options *>(str.get());
set_video_signal_configurable(options->output);
}
// std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
// return AmstradCPC::get_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;
// }
// MARK: - Joysticks
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() final {

View File

@ -9,7 +9,8 @@
#ifndef AmstradCPC_hpp
#define AmstradCPC_hpp
#include "../../Reflection/Struct.h"
#include "../../Configurable/Configurable.hpp"
#include "../../Configurable/StandardOptions.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
@ -17,9 +18,6 @@
namespace AmstradCPC {
/// @returns The options available for an Amstrad CPC.
std::unique_ptr<Reflection::Struct> get_options();
/*!
Models an Amstrad CPC.
*/
@ -29,6 +27,22 @@ 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 a ZX80/81.
class Options: public Reflection::StructImpl<Options> {
public:
Configurable::Display output = Configurable::Display::RGB;
Options(Configurable::OptionsType type) {
// Declare fields if necessary.
if(needs_declare()) {
DeclareField(output);
AnnounceEnumNS(Configurable, Display);
// TODO: some way to set limited enum support for a struct?
// In this case, to support only RGB and CompositeColour.
}
}
};
};
}

View File

@ -869,7 +869,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
}
// MARK:: Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

View File

@ -527,7 +527,7 @@ template <Analyser::Static::Macintosh::Target::Model model> class ConcreteMachin
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

View File

@ -682,7 +682,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

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

@ -373,7 +373,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

View File

@ -682,7 +682,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

View File

@ -454,7 +454,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

View File

@ -653,7 +653,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

View File

@ -378,7 +378,7 @@ class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

View File

@ -627,7 +627,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
std::unique_ptr<Reflection::Struct> get_options() final {
return nullptr;
}

View File

@ -176,7 +176,6 @@ std::vector<std::string> Machine::AllMachines(bool meaningful_without_media_only
std::map<std::string, std::unique_ptr<Reflection::Struct>> Machine::AllOptionsByMachineName() {
std::map<std::string, std::unique_ptr<Reflection::Struct>> options;
// 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()));
@ -186,7 +185,14 @@ std::map<std::string, std::unique_ptr<Reflection::Struct>> Machine::AllOptionsBy
// 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 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(ZX8081, ZX8081::Machine);
#undef Emplace
return options;
}

View File

@ -50,22 +50,6 @@ enum ROMType: uint8_t {
ZX80 = 0, ZX81
};
struct Options: public Reflection::StructImpl<Options> {
bool automatic_tape_motor_control = true;
bool quickload = true;
Options() {
if(needs_declare()) {
DeclareField(automatic_tape_motor_control);
DeclareField(quickload);
}
}
};
std::unique_ptr<Reflection::Struct> get_options() {
return std::make_unique<Options>();
}
template<bool is_zx81> class ConcreteMachine:
public CRTMachine::Machine,
public MediaTarget::Machine,
@ -422,31 +406,18 @@ template<bool is_zx81> class ConcreteMachine:
}
// MARK: - Configuration options.
std::unique_ptr<Reflection::Struct> get_options(OptionsType type) final {
auto options = std::make_unique<Options>();
switch(type) {
case OptionsType::Current:
options->automatic_tape_motor_control = use_automatic_tape_motor_control_;
options->quickload = allow_fast_tape_hack_;
break;
case OptionsType::Accurate:
case OptionsType::UserFriendly:
options->automatic_tape_motor_control =
options->quickload = type == OptionsType::UserFriendly;
break;
}
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_options(const std::unique_ptr<Reflection::Struct> &options) {
if(Reflection::get<bool>(*options, "quickload", allow_fast_tape_hack_)) {
set_use_fast_tape();
}
bool autotapemotor;
if(Reflection::get<bool>(*options, "automatic_tape_motor_control", autotapemotor)) {
set_use_automatic_tape_motor_control(autotapemotor);
}
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

@ -9,7 +9,7 @@
#ifndef ZX8081_hpp
#define ZX8081_hpp
#include "../../Reflection/Struct.h"
#include "../../Configurable/Configurable.hpp"
#include "../../Analyser/Static/StaticAnalyser.hpp"
#include "../ROMMachine.hpp"
@ -17,9 +17,7 @@
namespace ZX8081 {
/// @returns The options available for a ZX80 or ZX81.
std::unique_ptr<Reflection::Struct> get_options();
/// The ZX80/81 machine.
class Machine {
public:
virtual ~Machine();
@ -28,6 +26,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:
bool automatic_tape_motor_control;
bool quickload;
Options(Configurable::OptionsType type):
automatic_tape_motor_control(type == Configurable::OptionsType::UserFriendly),
quickload(automatic_tape_motor_control) {
// Declare fields if necessary.
if(needs_declare()) {
DeclareField(automatic_tape_motor_control);
DeclareField(quickload);
}
}
};
};
}

View File

@ -90,7 +90,7 @@
</CommandLineArgument>
<CommandLineArgument
argument = "--help"
isEnabled = "NO">
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</LaunchAction>

View File

@ -497,39 +497,42 @@ int main(int argc, char *argv[]) {
std::cout << "Further machine options:" << std::endl;
std::cout << "(* means: a selection will be made automatically based on the file selected, if any)" << std::endl << std::endl;
// const 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;
//
// Configurable::ListOption *list_option = dynamic_cast<Configurable::ListOption *>(option.get());
// if(list_option) {
// std::cout << "={";
// bool is_first = true;
// for(const auto &option: list_option->options) {
// if(!is_first) std::cout << '|';
// is_first = false;
// std::cout << option;
// }
// std::cout << "}";
// }
// std::cout << std::endl;
// }
// std::cout << std::endl;
// }
const auto targets = Machine::TargetsByMachineName(false);
for(const auto &target: targets) {
const auto reflectable = dynamic_cast<Reflection::Struct *>(target.second.get());
if(!reflectable) continue;
const auto all_keys = reflectable->all_keys();
if(all_keys.empty()) continue;
const auto runtime_options = Machine::AllOptionsByMachineName();
const auto machine_names = Machine::AllMachines(false, true);
for(const auto &machine: machine_names) {
const auto target = targets.find(machine);
const auto options = runtime_options.find(machine);
std::cout << target.first << ":" << std::endl;
for(const auto &option: reflectable->all_keys()) {
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.
std::vector<std::string> all_options = options_keys;
all_options.insert(all_options.end(), target_keys.begin(), target_keys.end());
for(const auto &option: all_options) {
std::cout << '\t' << "--" << option;
const auto type = reflectable->type_of(option);
bool is_construction_option = true;
auto type = target_reflectable->type_of(option);
if(!type) {
is_construction_option = false;
type = options_reflectable->type_of(option);
}
// Is this a registered enum? If so, list options.
if(!Reflection::Enum::name(*type).empty()) {
@ -544,7 +547,9 @@ int main(int argc, char *argv[]) {
}
// TODO: if not a registered enum... then assume it was a Boolean?
std::cout << "\t*" << std::endl;
if(is_construction_option) std::cout << "\t*";
std::cout << std::endl;
}
std::cout << std::endl;

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

View File

@ -26,6 +26,7 @@ namespace Reflection {
#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: