From ecc623cd6c1e959f512522a9515eb246b259bb8b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 29 Oct 2025 17:19:15 -0400 Subject: [PATCH 1/7] Improve option naming, add one for dynamic crop. --- Configurable/StandardOptions.hpp | 34 +++++++++++++++------ Machines/Acorn/Archimedes/Archimedes.cpp | 4 +-- Machines/Acorn/Archimedes/Archimedes.hpp | 10 +++--- Machines/Acorn/Electron/Electron.cpp | 4 +-- Machines/Acorn/Electron/Electron.hpp | 14 ++++----- Machines/AmstradCPC/AmstradCPC.cpp | 4 +-- Machines/AmstradCPC/AmstradCPC.hpp | 14 ++++----- Machines/Apple/AppleII/AppleII.hpp | 10 +++--- Machines/Apple/Macintosh/Macintosh.cpp | 4 +-- Machines/Apple/Macintosh/Macintosh.hpp | 8 ++--- Machines/Atari/ST/AtariST.hpp | 8 ++--- Machines/ColecoVision/ColecoVision.hpp | 10 +++--- Machines/Commodore/Plus4/Plus4.cpp | 4 +-- Machines/Commodore/Plus4/Plus4.hpp | 16 +++++----- Machines/Commodore/Vic-20/Vic20.cpp | 4 +-- Machines/Commodore/Vic-20/Vic20.hpp | 14 ++++----- Machines/Enterprise/Enterprise.hpp | 10 +++--- Machines/MSX/MSX.cpp | 4 +-- Machines/MSX/MSX.hpp | 16 +++++----- Machines/MasterSystem/MasterSystem.hpp | 10 +++--- Machines/Oric/Oric.cpp | 4 +-- Machines/Oric/Oric.hpp | 16 +++++----- Machines/PCCompatible/PCCompatible.hpp | 10 +++--- Machines/Sinclair/ZX8081/ZX8081.cpp | 4 +-- Machines/Sinclair/ZX8081/ZX8081.hpp | 10 +++--- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 4 +-- Machines/Sinclair/ZXSpectrum/ZXSpectrum.hpp | 16 +++++----- 27 files changed, 140 insertions(+), 126 deletions(-) diff --git a/Configurable/StandardOptions.hpp b/Configurable/StandardOptions.hpp index 69bea0622..d5ac31eac 100644 --- a/Configurable/StandardOptions.hpp +++ b/Configurable/StandardOptions.hpp @@ -25,10 +25,12 @@ ReflectableEnum(Display, // ensure unified property naming. //=== -template class DisplayOption { +namespace Options { + +template class Display { public: Configurable::Display output; - DisplayOption(Configurable::Display output) : output(output) {} + Display(const Configurable::Display output) noexcept : output(output) {} protected: void declare_display_option() { @@ -37,26 +39,38 @@ protected: } }; -template class QuickloadOption { +template class QuickLoad { public: - bool quickload; - QuickloadOption(bool quickload) : quickload(quickload) {} + bool quick_load; + QuickLoad(const bool quick_load) noexcept : quick_load(quick_load) {} protected: void declare_quickload_option() { - static_cast(this)->declare(&quickload, "quickload"); + static_cast(this)->declare(&quick_load, "quickload"); } }; -template class QuickbootOption { +template class QuickBoot { public: - bool quickboot; - QuickbootOption(bool quickboot) : quickboot(quickboot) {} + bool quick_boot; + QuickBoot(const bool quick_boot) noexcept : quick_boot(quick_boot) {} protected: void declare_quickboot_option() { - static_cast(this)->declare(&quickboot, "quickboot"); + static_cast(this)->declare(&quick_boot, "quickboot"); + } +}; + +template class DynamicCrop { +public: + bool dynamic_crop; + DynamicCrop(const bool dynamic_crop) noexcept : dynamic_crop(dynamic_crop) {} + +protected: + void declare_quickboot_option() { + static_cast(this)->declare(&dynamic_crop, "dynamiccrop"); } }; } +} diff --git a/Machines/Acorn/Archimedes/Archimedes.cpp b/Machines/Acorn/Archimedes/Archimedes.cpp index 5f01a6dcf..a8282c2de 100644 --- a/Machines/Acorn/Archimedes/Archimedes.cpp +++ b/Machines/Acorn/Archimedes/Archimedes.cpp @@ -509,13 +509,13 @@ private: // MARK: - Configuration options. std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); - options->quickload = accelerate_loading_; + options->quick_load = accelerate_loading_; return options; } void set_options(const std::unique_ptr &str) final { const auto options = dynamic_cast(str.get()); - accelerate_loading_ = options->quickload; + accelerate_loading_ = options->quick_load; } // MARK: - AudioProducer diff --git a/Machines/Acorn/Archimedes/Archimedes.hpp b/Machines/Acorn/Archimedes/Archimedes.hpp index f684fe37a..8f04a1b7d 100644 --- a/Machines/Acorn/Archimedes/Archimedes.hpp +++ b/Machines/Acorn/Archimedes/Archimedes.hpp @@ -24,13 +24,13 @@ struct Machine { const ROMMachine::ROMFetcher &rom_fetcher ); - class Options: public Reflection::StructImpl, public Configurable::QuickloadOption { - friend Configurable::QuickloadOption; + class Options: public Reflection::StructImpl, public Configurable::Options::QuickLoad { + friend Configurable::Options::QuickLoad; public: - Options(Configurable::OptionsType type) : - Configurable::QuickloadOption(type == Configurable::OptionsType::UserFriendly) {} + Options(const Configurable::OptionsType type) : + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { declare_quickload_option(); diff --git a/Machines/Acorn/Electron/Electron.cpp b/Machines/Acorn/Electron/Electron.cpp index 3b2ebeab9..6677b7a30 100644 --- a/Machines/Acorn/Electron/Electron.cpp +++ b/Machines/Acorn/Electron/Electron.cpp @@ -599,7 +599,7 @@ public: std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); - options->quickload = allow_fast_tape_hack_; + options->quick_load = allow_fast_tape_hack_; return options; } @@ -607,7 +607,7 @@ public: const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); - allow_fast_tape_hack_ = options->quickload; + allow_fast_tape_hack_ = options->quick_load; set_use_fast_tape_hack(); } diff --git a/Machines/Acorn/Electron/Electron.hpp b/Machines/Acorn/Electron/Electron.hpp index fbfb6cb51..cb37950fe 100644 --- a/Machines/Acorn/Electron/Electron.hpp +++ b/Machines/Acorn/Electron/Electron.hpp @@ -35,22 +35,22 @@ struct Machine { /// Defines the runtime options available for an Electron. class Options: public Reflection::StructImpl, - public Configurable::DisplayOption, - public Configurable::QuickloadOption + public Configurable::Options::Display, + public Configurable::Options::QuickLoad { - friend Configurable::DisplayOption; - friend Configurable::QuickloadOption; + friend Configurable::Options::Display; + friend Configurable::Options::QuickLoad; public: Options(const Configurable::OptionsType type) : - Configurable::DisplayOption( + Configurable::Options::Display( type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour ), - Configurable::QuickloadOption( + Configurable::Options::QuickLoad( type == Configurable::OptionsType::UserFriendly) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index d83596c59..2ed260da0 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -1191,14 +1191,14 @@ public: std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); - options->quickload = allow_fast_tape_hack_; + options->quick_load = allow_fast_tape_hack_; return options; } void set_options(const std::unique_ptr &str) { const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); - allow_fast_tape_hack_ = options->quickload; + allow_fast_tape_hack_ = options->quick_load; set_use_fast_tape_hack(); } diff --git a/Machines/AmstradCPC/AmstradCPC.hpp b/Machines/AmstradCPC/AmstradCPC.hpp index cae0e46a8..25cf6bc0b 100644 --- a/Machines/AmstradCPC/AmstradCPC.hpp +++ b/Machines/AmstradCPC/AmstradCPC.hpp @@ -32,19 +32,19 @@ struct Machine { /// Defines the runtime options available for an Amstrad CPC. class Options: public Reflection::StructImpl, - public Configurable::DisplayOption, - public Configurable::QuickloadOption + public Configurable::Options::Display, + public Configurable::Options::QuickLoad { public: Options(const Configurable::OptionsType type) : - Configurable::DisplayOption(Configurable::Display::RGB), - Configurable::QuickloadOption(type == Configurable::OptionsType::UserFriendly) {} + Configurable::Options::Display(Configurable::Display::RGB), + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly) {} private: - friend Configurable::DisplayOption; - friend Configurable::QuickloadOption; + friend Configurable::Options::Display; + friend Configurable::Options::QuickLoad; - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/Apple/AppleII/AppleII.hpp b/Machines/Apple/AppleII/AppleII.hpp index 619865778..b50469676 100644 --- a/Machines/Apple/AppleII/AppleII.hpp +++ b/Machines/Apple/AppleII/AppleII.hpp @@ -24,15 +24,15 @@ struct Machine { static std::unique_ptr AppleII(const Analyser::Static::Target *, const ROMMachine::ROMFetcher &); /// Defines the runtime options available for an Apple II. - class Options: public Reflection::StructImpl, public Configurable::DisplayOption { - friend Configurable::DisplayOption; + class Options: public Reflection::StructImpl, public Configurable::Options::Display { + friend Configurable::Options::Display; public: bool use_square_pixels = false; - Options(Configurable::OptionsType) : - Configurable::DisplayOption(Configurable::Display::CompositeColour) {} + Options(const Configurable::OptionsType) : + Configurable::Options::Display(Configurable::Display::CompositeColour) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 5b19bb7d7..260b82663 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -506,7 +506,7 @@ public: // MARK: - Configuration options. std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); - options->quickboot = quickboot_; + options->quick_boot = quickboot_; return options; } @@ -515,7 +515,7 @@ public: // It should probably be a construction option. const auto options = dynamic_cast(str.get()); - quickboot_ = options->quickboot; + quickboot_ = options->quick_boot; using Model = Analyser::Static::Macintosh::Target::Model; const bool is_plus_rom = model == Model::Mac512ke || model == Model::MacPlus; diff --git a/Machines/Apple/Macintosh/Macintosh.hpp b/Machines/Apple/Macintosh/Macintosh.hpp index 7d2d51ab2..e9e29d309 100644 --- a/Machines/Apple/Macintosh/Macintosh.hpp +++ b/Machines/Apple/Macintosh/Macintosh.hpp @@ -21,11 +21,11 @@ struct Machine { /// Creates and returns a Macintosh. static std::unique_ptr Macintosh(const Analyser::Static::Target *, const ROMMachine::ROMFetcher &); - class Options: public Reflection::StructImpl, public Configurable::QuickbootOption { - friend Configurable::QuickbootOption; + class Options: public Reflection::StructImpl, public Configurable::Options::QuickBoot { + friend Configurable::Options::QuickBoot; public: - Options(Configurable::OptionsType type) : - Configurable::QuickbootOption(type == Configurable::OptionsType::UserFriendly) {} + Options(const Configurable::OptionsType type) : + Configurable::Options::QuickBoot(type == Configurable::OptionsType::UserFriendly) {} private: Options() : Options(Configurable::OptionsType::UserFriendly) {} diff --git a/Machines/Atari/ST/AtariST.hpp b/Machines/Atari/ST/AtariST.hpp index 4a71d1926..e05dd6777 100644 --- a/Machines/Atari/ST/AtariST.hpp +++ b/Machines/Atari/ST/AtariST.hpp @@ -22,15 +22,15 @@ struct Machine { static std::unique_ptr AtariST(const Analyser::Static::Target *, const ROMMachine::ROMFetcher &); - class Options: public Reflection::StructImpl, public Configurable::DisplayOption { - friend Configurable::DisplayOption; + class Options: public Reflection::StructImpl, public Configurable::Options::Display { + friend Configurable::Options::Display; public: - Options(Configurable::OptionsType type) : Configurable::DisplayOption( + Options(const Configurable::OptionsType type) : Configurable::Options::Display( type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/ColecoVision/ColecoVision.hpp b/Machines/ColecoVision/ColecoVision.hpp index c8f0bcdbb..99727b14e 100644 --- a/Machines/ColecoVision/ColecoVision.hpp +++ b/Machines/ColecoVision/ColecoVision.hpp @@ -21,15 +21,15 @@ struct Machine { virtual ~Machine() = default; static std::unique_ptr ColecoVision(const Analyser::Static::Target *, const ROMMachine::ROMFetcher &); - class Options: public Reflection::StructImpl, public Configurable::DisplayOption { - friend Configurable::DisplayOption; + class Options: public Reflection::StructImpl, public Configurable::Options::Display { + friend Configurable::Options::Display; public: - Options(Configurable::OptionsType type) : - Configurable::DisplayOption(type == Configurable::OptionsType::UserFriendly ? + Options(const Configurable::OptionsType type) : + Configurable::Options::Display(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::SVideo : Configurable::Display::CompositeColour) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/Commodore/Plus4/Plus4.cpp b/Machines/Commodore/Plus4/Plus4.cpp index d40c1494d..c95a09f1a 100644 --- a/Machines/Commodore/Plus4/Plus4.cpp +++ b/Machines/Commodore/Plus4/Plus4.cpp @@ -1173,7 +1173,7 @@ private: std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); - options->quickload = allow_fast_tape_hack_; + options->quick_load = allow_fast_tape_hack_; return options; } @@ -1181,7 +1181,7 @@ private: const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); - allow_fast_tape_hack_ = options->quickload; + allow_fast_tape_hack_ = options->quick_load; set_use_fast_tape(); } }; diff --git a/Machines/Commodore/Plus4/Plus4.hpp b/Machines/Commodore/Plus4/Plus4.hpp index 8854eee51..d32ae0a1d 100644 --- a/Machines/Commodore/Plus4/Plus4.hpp +++ b/Machines/Commodore/Plus4/Plus4.hpp @@ -26,19 +26,19 @@ struct Machine { class Options: public Reflection::StructImpl, - public Configurable::DisplayOption, - public Configurable::QuickloadOption + public Configurable::Options::Display, + public Configurable::Options::QuickLoad { - friend Configurable::DisplayOption; - friend Configurable::QuickloadOption; + friend Configurable::Options::Display; + friend Configurable::Options::QuickLoad; public: - Options(Configurable::OptionsType type) : - Configurable::DisplayOption(type == Configurable::OptionsType::UserFriendly ? + Options(const Configurable::OptionsType type) : + Configurable::Options::Display(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::SVideo : Configurable::Display::CompositeColour), - Configurable::QuickloadOption(type == Configurable::OptionsType::UserFriendly) { + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly) { } private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 698874174..ea858f5f2 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -696,14 +696,14 @@ public: std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); - options->quickload = allow_fast_tape_hack_; + options->quick_load = allow_fast_tape_hack_; return options; } void set_options(const std::unique_ptr &str) final { const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); - allow_fast_tape_hack_ = options->quickload; + allow_fast_tape_hack_ = options->quick_load; set_use_fast_tape(); } diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index c89d216a7..9dd4f27e4 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -28,16 +28,16 @@ struct Machine { class Options: public Reflection::StructImpl, - public Configurable::DisplayOption, - public Configurable::QuickloadOption + public Configurable::Options::Display, + public Configurable::Options::QuickLoad { - friend Configurable::DisplayOption; - friend Configurable::QuickloadOption; + friend Configurable::Options::Display; + friend Configurable::Options::QuickLoad; public: - Options(Configurable::OptionsType type) : - Configurable::DisplayOption(type == Configurable::OptionsType::UserFriendly ? + Options(const Configurable::OptionsType type) : + Configurable::Options::Display(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::SVideo : Configurable::Display::CompositeColour), - Configurable::QuickloadOption(type == Configurable::OptionsType::UserFriendly) { + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly) { } private: Options() : Options(Configurable::OptionsType::UserFriendly) {} diff --git a/Machines/Enterprise/Enterprise.hpp b/Machines/Enterprise/Enterprise.hpp index 45cefc08f..7421f7174 100644 --- a/Machines/Enterprise/Enterprise.hpp +++ b/Machines/Enterprise/Enterprise.hpp @@ -28,16 +28,16 @@ struct Machine { static std::unique_ptr Enterprise(const Analyser::Static::Target *, const ROMMachine::ROMFetcher &); /// Defines the runtime options available for an Enterprise. - class Options: public Reflection::StructImpl, public Configurable::DisplayOption { - friend Configurable::DisplayOption; + class Options: public Reflection::StructImpl, public Configurable::Options::Display { + friend Configurable::Options::Display; public: - Options(Configurable::OptionsType type) : - Configurable::DisplayOption( + Options(const Configurable::OptionsType type) : + Configurable::Options::Display( type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index f94cc64d8..235ebc729 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -815,14 +815,14 @@ public: std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); - options->quickload = allow_fast_tape_; + options->quick_load = allow_fast_tape_; return options; } void set_options(const std::unique_ptr &str) final { const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); - allow_fast_tape_ = options->quickload; + allow_fast_tape_ = options->quick_load; set_use_fast_tape(); } diff --git a/Machines/MSX/MSX.hpp b/Machines/MSX/MSX.hpp index c36315601..f89552560 100644 --- a/Machines/MSX/MSX.hpp +++ b/Machines/MSX/MSX.hpp @@ -23,18 +23,18 @@ struct Machine { class Options: public Reflection::StructImpl, - public Configurable::DisplayOption, - public Configurable::QuickloadOption + public Configurable::Options::Display, + public Configurable::Options::QuickLoad { - friend Configurable::DisplayOption; - friend Configurable::QuickloadOption; + friend Configurable::Options::Display; + friend Configurable::Options::QuickLoad; public: - Options(Configurable::OptionsType type) : - Configurable::DisplayOption(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour), - Configurable::QuickloadOption(type == Configurable::OptionsType::UserFriendly) {} + Options(const Configurable::OptionsType type) : + Configurable::Options::Display(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour), + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/MasterSystem/MasterSystem.hpp b/Machines/MasterSystem/MasterSystem.hpp index d97df8df1..1103714f6 100644 --- a/Machines/MasterSystem/MasterSystem.hpp +++ b/Machines/MasterSystem/MasterSystem.hpp @@ -21,15 +21,15 @@ struct Machine { virtual ~Machine() = default; static std::unique_ptr MasterSystem(const Analyser::Static::Target *, const ROMMachine::ROMFetcher &); - class Options: public Reflection::StructImpl, public Configurable::DisplayOption { - friend Configurable::DisplayOption; + class Options: public Reflection::StructImpl, public Configurable::Options::Display { + friend Configurable::Options::Display; public: - Options(Configurable::OptionsType type) : - Configurable::DisplayOption(type == Configurable::OptionsType::UserFriendly ? + Options(const Configurable::OptionsType type) : + Configurable::Options::Display(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index fb9e7ae21..8c3e6d058 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -679,14 +679,14 @@ public: std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); - options->quickload = use_fast_tape_hack_; + options->quick_load = use_fast_tape_hack_; return options; } void set_options(const std::unique_ptr &str) final { const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); - set_use_fast_tape_hack(options->quickload); + set_use_fast_tape_hack(options->quick_load); } void set_activity_observer(Activity::Observer *observer) final { diff --git a/Machines/Oric/Oric.hpp b/Machines/Oric/Oric.hpp index e968933ea..9e5b6b839 100644 --- a/Machines/Oric/Oric.hpp +++ b/Machines/Oric/Oric.hpp @@ -26,19 +26,19 @@ struct Machine { class Options: public Reflection::StructImpl, - public Configurable::DisplayOption, - public Configurable::QuickloadOption + public Configurable::Options::Display, + public Configurable::Options::QuickLoad { - friend Configurable::DisplayOption; - friend Configurable::QuickloadOption; + friend Configurable::Options::Display; + friend Configurable::Options::QuickLoad; public: - Options(Configurable::OptionsType type) : - Configurable::DisplayOption(type == Configurable::OptionsType::UserFriendly ? + Options(const Configurable::OptionsType type) : + Configurable::Options::Display(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour), - Configurable::QuickloadOption(type == Configurable::OptionsType::UserFriendly) {} + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/PCCompatible/PCCompatible.hpp b/Machines/PCCompatible/PCCompatible.hpp index 55541864e..633b180c8 100644 --- a/Machines/PCCompatible/PCCompatible.hpp +++ b/Machines/PCCompatible/PCCompatible.hpp @@ -30,15 +30,15 @@ struct Machine { /// Defines the runtime options [sometimes] available for a PC. class Options: public Reflection::StructImpl, - public Configurable::DisplayOption + public Configurable::Options::Display { - friend Configurable::DisplayOption; + friend Configurable::Options::Display; public: - Options(Configurable::OptionsType) : - Configurable::DisplayOption(Configurable::Display::RGB) {} + Options(const Configurable::OptionsType) : + Configurable::Options::Display(Configurable::Display::RGB) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/Sinclair/ZX8081/ZX8081.cpp b/Machines/Sinclair/ZX8081/ZX8081.cpp index cd104988a..e8ca787e3 100644 --- a/Machines/Sinclair/ZX8081/ZX8081.cpp +++ b/Machines/Sinclair/ZX8081/ZX8081.cpp @@ -386,14 +386,14 @@ public: std::unique_ptr get_options() const final { auto options = std::make_unique(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_; + options->quick_load = allow_fast_tape_hack_; return options; } void set_options(const std::unique_ptr &str) { const auto options = dynamic_cast(str.get()); set_use_automatic_tape_motor_control(options->automatic_tape_motor_control); - allow_fast_tape_hack_ = options->quickload; + allow_fast_tape_hack_ = options->quick_load; set_use_fast_tape(); } diff --git a/Machines/Sinclair/ZX8081/ZX8081.hpp b/Machines/Sinclair/ZX8081/ZX8081.hpp index cc477d1c2..89863d93e 100644 --- a/Machines/Sinclair/ZX8081/ZX8081.hpp +++ b/Machines/Sinclair/ZX8081/ZX8081.hpp @@ -26,17 +26,17 @@ struct Machine { virtual bool get_tape_is_playing() = 0; /// Defines the runtime options available for a ZX80/81. - class Options: public Reflection::StructImpl, public Configurable::QuickloadOption { - friend Configurable::QuickloadOption; + class Options: public Reflection::StructImpl, public Configurable::Options::QuickLoad { + friend Configurable::Options::QuickLoad; public: bool automatic_tape_motor_control = true; - Options(Configurable::OptionsType type): - Configurable::QuickloadOption(type == Configurable::OptionsType::UserFriendly), + Options(const Configurable::OptionsType type): + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly), automatic_tape_motor_control(type == Configurable::OptionsType::UserFriendly) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index e19468f2d..c6ee0e58a 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -731,7 +731,7 @@ public: std::unique_ptr get_options() const override { auto options = std::make_unique(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_; + options->quick_load = allow_fast_tape_hack_; options->output = get_video_signal_configurable(); return options; } @@ -740,7 +740,7 @@ public: const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); set_use_automatic_tape_motor_control(options->automatic_tape_motor_control); - allow_fast_tape_hack_ = options->quickload; + allow_fast_tape_hack_ = options->quick_load; set_use_fast_tape(); } diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.hpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.hpp index 77103413a..e80ffa203 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.hpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.hpp @@ -26,21 +26,21 @@ struct Machine { class Options: public Reflection::StructImpl, - public Configurable::DisplayOption, - public Configurable::QuickloadOption + public Configurable::Options::Display, + public Configurable::Options::QuickLoad { - friend Configurable::DisplayOption; - friend Configurable::QuickloadOption; + friend Configurable::Options::Display; + friend Configurable::Options::QuickLoad; public: bool automatic_tape_motor_control = true; - Options(Configurable::OptionsType type) : - Configurable::DisplayOption(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour), - Configurable::QuickloadOption(type == Configurable::OptionsType::UserFriendly), + Options(const Configurable::OptionsType type) : + Configurable::Options::Display(type == Configurable::OptionsType::UserFriendly ? Configurable::Display::RGB : Configurable::Display::CompositeColour), + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly), automatic_tape_motor_control(type == Configurable::OptionsType::UserFriendly) {} private: - Options() : Options(Configurable::OptionsType::UserFriendly) {} + Options() : Options( Configurable::OptionsType::UserFriendly) {} friend Reflection::StructImpl; void declare_fields() { From 3427120b3f99aef838a086adf3e926426532e39a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 29 Oct 2025 17:37:58 -0400 Subject: [PATCH 2/7] Expose dynamic crop option from the CPC. --- Configurable/StandardOptions.hpp | 2 +- Machines/AmstradCPC/AmstradCPC.hpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Configurable/StandardOptions.hpp b/Configurable/StandardOptions.hpp index d5ac31eac..f0866bb95 100644 --- a/Configurable/StandardOptions.hpp +++ b/Configurable/StandardOptions.hpp @@ -67,7 +67,7 @@ public: DynamicCrop(const bool dynamic_crop) noexcept : dynamic_crop(dynamic_crop) {} protected: - void declare_quickboot_option() { + void declare_dynamic_crop_option() { static_cast(this)->declare(&dynamic_crop, "dynamiccrop"); } }; diff --git a/Machines/AmstradCPC/AmstradCPC.hpp b/Machines/AmstradCPC/AmstradCPC.hpp index 25cf6bc0b..343362c60 100644 --- a/Machines/AmstradCPC/AmstradCPC.hpp +++ b/Machines/AmstradCPC/AmstradCPC.hpp @@ -33,16 +33,19 @@ struct Machine { class Options: public Reflection::StructImpl, public Configurable::Options::Display, - public Configurable::Options::QuickLoad + public Configurable::Options::QuickLoad, + public Configurable::Options::DynamicCrop { public: Options(const Configurable::OptionsType type) : Configurable::Options::Display(Configurable::Display::RGB), - Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly) {} + Configurable::Options::QuickLoad(type == Configurable::OptionsType::UserFriendly), + Configurable::Options::DynamicCrop(type == Configurable::OptionsType::UserFriendly) {} private: friend Configurable::Options::Display; friend Configurable::Options::QuickLoad; + friend Configurable::Options::DynamicCrop; Options() : Options( Configurable::OptionsType::UserFriendly) {} @@ -50,6 +53,7 @@ struct Machine { void declare_fields() { declare_display_option(); declare_quickload_option(); + declare_dynamic_crop_option(); limit_enum(&output, Configurable::Display::RGB, Configurable::Display::CompositeColour, -1); } }; From e19bd0d517643f521bbb47dd1d591b76f4dbdee4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 29 Oct 2025 17:43:05 -0400 Subject: [PATCH 3/7] Alphabetise; mark override. --- Machines/AmstradCPC/AmstradCPC.cpp | 2 +- Machines/AmstradCPC/AmstradCPC.hpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 2ed260da0..410e50123 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -1195,7 +1195,7 @@ public: return options; } - void set_options(const std::unique_ptr &str) { + void set_options(const std::unique_ptr &str) final { const auto options = dynamic_cast(str.get()); set_video_signal_configurable(options->output); allow_fast_tape_hack_ = options->quick_load; diff --git a/Machines/AmstradCPC/AmstradCPC.hpp b/Machines/AmstradCPC/AmstradCPC.hpp index 343362c60..07b7ecbed 100644 --- a/Machines/AmstradCPC/AmstradCPC.hpp +++ b/Machines/AmstradCPC/AmstradCPC.hpp @@ -8,9 +8,9 @@ #pragma once +#include "Analyser/Static/StaticAnalyser.hpp" #include "Configurable/Configurable.hpp" #include "Configurable/StandardOptions.hpp" -#include "Analyser/Static/StaticAnalyser.hpp" #include "Machines/ROMMachine.hpp" #include @@ -58,6 +58,7 @@ struct Machine { } }; + // Provided for running the SHAKER test suite. struct SSMDelegate { virtual void perform(uint16_t) = 0; }; From d27f0e36336517480c6c7d1e3ee8a40b5e66a6c7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 29 Oct 2025 17:43:19 -0400 Subject: [PATCH 4/7] Declare that dynamic crop is an option. --- Machines/Acorn/BBCMicro/BBCMicro.cpp | 12 ++++++++++++ Machines/Acorn/BBCMicro/BBCMicro.hpp | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/Machines/Acorn/BBCMicro/BBCMicro.cpp b/Machines/Acorn/BBCMicro/BBCMicro.cpp index 174c5e5c1..83a7b2aaf 100644 --- a/Machines/Acorn/BBCMicro/BBCMicro.cpp +++ b/Machines/Acorn/BBCMicro/BBCMicro.cpp @@ -662,6 +662,7 @@ using CRTC = Motorola::CRTC::CRTC6845< template class ConcreteMachine: public Activity::Source, + public Configurable::Device, public Machine, public MachineTypes::AudioProducer, public MachineTypes::JoystickMachine, @@ -1150,6 +1151,17 @@ private: const std::vector> &get_joysticks() override { return joysticks_; } + + // MARK: - Configuration options. + std::unique_ptr get_options() const final { + auto options = std::make_unique(Configurable::OptionsType::UserFriendly); + return options; + } + + void set_options(const std::unique_ptr &str) final { + const auto options = dynamic_cast(str.get()); + (void)options; + } }; } diff --git a/Machines/Acorn/BBCMicro/BBCMicro.hpp b/Machines/Acorn/BBCMicro/BBCMicro.hpp index ac9c6ed48..dfa90a207 100644 --- a/Machines/Acorn/BBCMicro/BBCMicro.hpp +++ b/Machines/Acorn/BBCMicro/BBCMicro.hpp @@ -9,6 +9,8 @@ #pragma once #include "Analyser/Static/StaticAnalyser.hpp" +#include "Configurable/Configurable.hpp" +#include "Configurable/StandardOptions.hpp" #include "Machines/ROMMachine.hpp" #include @@ -22,6 +24,25 @@ struct Machine { const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher ); + + class Options: + public Reflection::StructImpl, + public Configurable::Options::DynamicCrop + { + public: + Options(const Configurable::OptionsType type) : + Configurable::Options::DynamicCrop(type == Configurable::OptionsType::UserFriendly) {} + + private: + friend Configurable::Options::DynamicCrop; + + Options() : Options( Configurable::OptionsType::UserFriendly) {} + + friend Reflection::StructImpl; + void declare_fields() { + declare_dynamic_crop_option(); + } + }; }; } From e75c27cb667f68ecdb999fc4672bfcdc5ba982f2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 29 Oct 2025 21:21:21 -0400 Subject: [PATCH 5/7] Add macOS UI to dynamic cropping option, apply at runtime to CPC. --- Machines/AmstradCPC/AmstradCPC.cpp | 21 +++++- .../Clock Signal.xcodeproj/project.pbxproj | 14 +++- .../CompositeDynamicCropOptions.xib | 52 +++++++++++++++ .../Base.lproj/CompositeOptions.xib | 6 +- .../Base.lproj/MachineDocument.xib | 6 +- .../Base.lproj/MacintoshOptions.xib | 4 +- .../Clock Signal/Base.lproj/OricOptions.xib | 6 +- .../Base.lproj/QuickLoadCompositeOptions.xib | 6 +- .../Base.lproj/QuickLoadOptions.xib | 4 +- .../Clock Signal/Base.lproj/ZX8081Options.xib | 4 +- .../CompositeDynamicCropOptions.xib | 66 +++++++++++++++++++ .../Documents/MachineController.swift | 23 ++++++- .../Mac/Clock Signal/Machine/CSMachine.h | 1 + .../Mac/Clock Signal/Machine/CSMachine.mm | 14 ++++ .../StaticAnalyser/CSStaticAnalyser.mm | 2 +- Outputs/CRT/CRT.cpp | 6 ++ 16 files changed, 209 insertions(+), 26 deletions(-) create mode 100644 OSBindings/Mac/Clock Signal/Base.lproj/CompositeDynamicCropOptions.xib create mode 100644 OSBindings/Mac/Clock Signal/Documents/Base.lproj/CompositeDynamicCropOptions.xib diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 410e50123..4a2b943fa 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -175,13 +175,25 @@ public: interrupt_timer_(interrupt_timer) { establish_palette_hits(); build_mode_table(); - crt_.set_dynamic_framing( - Outputs::Display::Rect(0.16842f, 0.19909f, 0.71579f, 0.67197f), - 0.0f, 0.1f); crt_.set_brightness(3.0f / 2.0f); // As only the values 0, 1 and 2 will be used in each channel, // whereas Red2Green2Blue2 defines a range of 0-3. } + void set_dynamic_framing(const bool enable) { + dynamic_framing_ = enable; + if(enable) { + crt_.set_dynamic_framing( + Outputs::Display::Rect(0.16842f, 0.19909f, 0.71579f, 0.67197f), + 0.0f, 0.1f); + } else { + crt_.set_fixed_framing(Outputs::Display::Rect(0.1072f, 0.1f, 0.842105263157895f, 0.842105263157895f)); + } + } + + bool dynamic_framing() const { + return dynamic_framing_; + } + /*! The CRTC entry function for the main part of each clock cycle; takes the current bus state and determines what output to produce based on the current palette and mode. @@ -543,6 +555,7 @@ private: int cycles_into_hsync_ = 0; Outputs::CRT::CRT crt_; + bool dynamic_framing_ = false; uint8_t *pixel_data_ = nullptr, *pixel_pointer_ = nullptr; const uint8_t *const ram_ = nullptr; @@ -1192,6 +1205,7 @@ public: auto options = std::make_unique(Configurable::OptionsType::UserFriendly); options->output = get_video_signal_configurable(); options->quick_load = allow_fast_tape_hack_; + options->dynamic_crop = crtc_bus_handler_.dynamic_framing(); return options; } @@ -1200,6 +1214,7 @@ public: set_video_signal_configurable(options->output); allow_fast_tape_hack_ = options->quick_load; set_use_fast_tape_hack(); + crtc_bus_handler_.set_dynamic_framing(options->dynamic_crop); } // MARK: - Joysticks diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 6b3a8fb07..2f96bc791 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -366,6 +366,7 @@ 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 */; }; + 4B4A75BD2EB2C55100EA398F /* CompositeDynamicCropOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B4A75BB2EB2C55100EA398F /* CompositeDynamicCropOptions.xib */; }; 4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */; }; 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; @@ -1608,6 +1609,7 @@ 4B47F3B52E7BAB94005D4DEC /* uPD7002.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uPD7002.cpp; sourceTree = ""; }; 4B47F6C4241C87A100ED06F7 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = ""; }; 4B49F0A823346F7A0045E6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MacintoshOptions.xib"; sourceTree = SOURCE_ROOT; }; + 4B4A75BC2EB2C55100EA398F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/CompositeDynamicCropOptions.xib; sourceTree = ""; }; 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AY38910.cpp; sourceTree = ""; }; 4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AY38910.hpp; sourceTree = ""; }; 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KonamiSCC.cpp; sourceTree = ""; }; @@ -3452,12 +3454,13 @@ children = ( 4B051C94266EF50200CA44E8 /* AppleIIController.swift */, 4B8FE21F1DA19D7C0090D3CE /* Atari2600OptionsController.swift */, - 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */, 4B8FE2211DA19FB20090D3CE /* MachineController.swift */, + 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */, 4B95FA9C1F11893B0008E395 /* ZX8081Controller.swift */, 4B08A56720D72BEF0016CE5A /* Activity.xib */, 4BC5FC2E20CDDDEE00410AA0 /* AppleIIOptions.xib */, 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */, + 4B4A75BB2EB2C55100EA398F /* CompositeDynamicCropOptions.xib */, 4BEEE6BB20DC72EA003723BF /* CompositeOptions.xib */, 4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */, 4B49F0A723346F7A0045E6A6 /* MacintoshOptions.xib */, @@ -5696,6 +5699,7 @@ buildActionMask = 2147483647; files = ( 4B2C45421E3C3896002A2389 /* cartridge.png in Resources */, + 4B4A75BD2EB2C55100EA398F /* CompositeDynamicCropOptions.xib in Resources */, 4BB73EA91B587A5100552FC2 /* Assets.xcassets in Resources */, 4B79E4451E3AF38600141F11 /* floppy35.png in Resources */, 4BEDA3BD25B25563000C2DBD /* README.md in Resources */, @@ -7003,6 +7007,14 @@ name = MacintoshOptions.xib; sourceTree = ""; }; + 4B4A75BB2EB2C55100EA398F /* CompositeDynamicCropOptions.xib */ = { + isa = PBXVariantGroup; + children = ( + 4B4A75BC2EB2C55100EA398F /* Base */, + ); + name = CompositeDynamicCropOptions.xib; + sourceTree = ""; + }; 4B55DD8120DF06680043F2E5 /* MachinePicker.xib */ = { isa = PBXVariantGroup; children = ( diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/CompositeDynamicCropOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/CompositeDynamicCropOptions.xib new file mode 100644 index 000000000..6e2c149cd --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Base.lproj/CompositeDynamicCropOptions.xib @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/CompositeOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/CompositeOptions.xib index 6e2c149cd..049bdf340 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/CompositeOptions.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/CompositeOptions.xib @@ -1,8 +1,8 @@ - + - + @@ -21,7 +21,7 @@ - + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib index 708b74ee0..8e15a28ec 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MachineDocument.xib @@ -1,8 +1,8 @@ - + - + @@ -21,7 +21,7 @@ - + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MacintoshOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MacintoshOptions.xib index 62e12dae4..b45b72e47 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MacintoshOptions.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MacintoshOptions.xib @@ -1,8 +1,8 @@ - + - + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/OricOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/OricOptions.xib index f2f29e25f..65f162461 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/OricOptions.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/OricOptions.xib @@ -1,8 +1,8 @@ - + - + @@ -31,7 +31,7 @@ - + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadCompositeOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadCompositeOptions.xib index 6582d3b1c..eba2d61d5 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadCompositeOptions.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadCompositeOptions.xib @@ -1,8 +1,8 @@ - + - + @@ -31,7 +31,7 @@ - + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadOptions.xib index 9fbc2efd5..f5c6f2eda 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadOptions.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/QuickLoadOptions.xib @@ -1,8 +1,8 @@ - + - + diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib index d69666c97..2704b7e71 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/ZX8081Options.xib @@ -1,8 +1,8 @@ - + - + diff --git a/OSBindings/Mac/Clock Signal/Documents/Base.lproj/CompositeDynamicCropOptions.xib b/OSBindings/Mac/Clock Signal/Documents/Base.lproj/CompositeDynamicCropOptions.xib new file mode 100644 index 000000000..15b835c09 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Documents/Base.lproj/CompositeDynamicCropOptions.xib @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineController.swift b/OSBindings/Mac/Clock Signal/Documents/MachineController.swift index aac5ac5ae..bc2018df7 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineController.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineController.swift @@ -56,10 +56,21 @@ class MachineController: NSObject { @IBAction func setDisplayType(_ sender: NSPopUpButton!) { if let selectedItem = sender.selectedItem { machine.videoSignal = signalForTag(tag: selectedItem.tag) - UserDefaults.standard.set(selectedItem.tag, forKey: self.displayTypeUserDefaultsKey) + UserDefaults.standard.set(selectedItem.tag, forKey: displayTypeUserDefaultsKey) } } + // MARK: Dynamic Cropping + var dynamicCroppingUserDefaultsKey: String { + return prefixedUserDefaultsKey("dynamicCrop") + } + @IBOutlet var dynamicCropButton: NSButton? + @IBAction func setDynamicCrop(_ sender: NSButton!) { + let useDynamicCropping = sender.state == .on + machine.useDynamicCropping = useDynamicCropping + UserDefaults.standard.set(useDynamicCropping, forKey: dynamicCroppingUserDefaultsKey) + } + // MARK: Restoring user defaults func establishStoredOptions() { let standardUserDefaults = UserDefaults.standard @@ -70,17 +81,23 @@ class MachineController: NSObject { ]) if let fastLoadingButton = self.fastLoadingButton { - let useFastLoadingHack = standardUserDefaults.bool(forKey: self.fastLoadingUserDefaultsKey) + let useFastLoadingHack = standardUserDefaults.bool(forKey: fastLoadingUserDefaultsKey) machine.useFastLoadingHack = useFastLoadingHack fastLoadingButton.state = useFastLoadingHack ? .on : .off } if let fastBootingButton = self.fastBootingButton { - let bootQuickly = standardUserDefaults.bool(forKey: self.bootQuicklyUserDefaultsKey) + let bootQuickly = standardUserDefaults.bool(forKey: bootQuicklyUserDefaultsKey) machine.useQuickBootingHack = bootQuickly fastBootingButton.state = bootQuickly ? .on : .off } + if let dynamicCropButton = self.dynamicCropButton { + let useDynamicCropping = standardUserDefaults.bool(forKey: dynamicCroppingUserDefaultsKey) + machine.useDynamicCropping = useDynamicCropping + dynamicCropButton.state = useDynamicCropping ? .on : .off + } + if let displayTypeButton = self.displayTypeButton { // Enable or disable options as per machine support. var titlesToRemove: [String] = [] diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index f78a33a83..fc6c7b86a 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -94,6 +94,7 @@ typedef NS_ENUM(NSInteger, CSMachineChangeEffect) { @property (nonatomic, assign) CSMachineVideoSignal videoSignal; @property (nonatomic, assign) BOOL useAutomaticTapeMotorControl; @property (nonatomic, assign) BOOL useQuickBootingHack; +@property (nonatomic, assign) BOOL useDynamicCropping; - (BOOL)supportsVideoSignal:(CSMachineVideoSignal)videoSignal; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 3ee5a39f1..4ff16c4f2 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -669,6 +669,20 @@ struct ActivityObserver: public Activity::Observer { } } +- (void)setUseDynamicCropping:(BOOL)useDynamicCropping { + Configurable::Device *configurable_device = _machine->configurable_device(); + if(!configurable_device) return; + + @synchronized(self) { + _useDynamicCropping = useDynamicCropping; + + auto options = configurable_device->get_options(); + Reflection::set(*options, "dynamiccrop", useDynamicCropping ? true : false); + configurable_device->set_options(options); + } + +} + - (NSString *)userDefaultsPrefix { // Assumes that the first machine in the targets list is the source of user defaults. std::string name = Machine::ShortNameForTargetMachine(_analyser.targets.front()->machine); diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index ab45edeed..e35e57280 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -403,7 +403,7 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K - (NSString *)optionsNibName { switch(_targets.front()->machine) { - case Analyser::Machine::AmstradCPC: return @"CompositeOptions"; + case Analyser::Machine::AmstradCPC: return @"CompositeDynamicCropOptions"; case Analyser::Machine::Archimedes: return @"QuickLoadOptions"; case Analyser::Machine::AppleII: return @"AppleIIOptions"; case Analyser::Machine::Atari2600: return @"Atari2600Options"; diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 943e509fa..594170bc4 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -505,6 +505,12 @@ void CRT::posit(Display::Rect rect) { Logger::info().append("First reading is (%0.5ff, %0.5ff, %0.5ff, %0.5ff)", posted_rect_.origin.x, posted_rect_.origin.y, posted_rect_.size.width, posted_rect_.size.height); + + auto frame = border_rect_; + frame.scale(0.90f, 0.90f); + Logger::info().append("90%% of whole frame was (%0.5ff, %0.5ff, %0.5ff, %0.5ff)", + frame.origin.x, frame.origin.y, + frame.size.width, frame.size.height); } return; } From cf10abff5b43cd06e17f1814c721d55a1816d301 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 29 Oct 2025 21:47:05 -0400 Subject: [PATCH 6/7] Attempt to smooth framing transitions. --- Outputs/CRT/CRT.cpp | 52 ++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 594170bc4..795b18654 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -135,8 +135,14 @@ void CRT::set_fixed_framing(const std::function &advance) { void CRT::set_fixed_framing(const Display::Rect frame) { framing_ = Framing::Static; - scan_target_modals_.visible_area = frame; - scan_target_->set_modals(scan_target_modals_); + if(!has_first_reading_) { + scan_target_modals_.visible_area = frame; + scan_target_->set_modals(scan_target_modals_); + } else { + previous_posted_rect_ = posted_rect_; + posted_rect_ = frame; + animation_step_ = 0; + } } void CRT::set_new_display_type(const int cycles_per_line, const Outputs::Display::Type displayType) { @@ -460,16 +466,33 @@ void CRT::posit(Display::Rect rect) { posted_rect_ * animation_time; }; + // Continue with any ongoing animation. + if(animation_step_ < AnimationSteps) { + set_rect(current_rect()); + ++animation_step_; + + if(animation_step_ == AnimationSteps) { + previous_posted_rect_ = posted_rect_; + + if(framing_ == Framing::CalibratingAutomaticFixed) { + framing_ = + border_rect_ != active_rect_ ? + Framing::BorderReactive : Framing::Static; + return; + } + } + } + + // Static framing: don't further evaluate. + if(framing_ == Framing::Static) { + return; + } + // Zoom out very slightly if there's space; this avoids a cramped tight crop. if(rect.size.width < 0.95 && rect.size.height < 0.95) { rect.scale(1.02f, 1.02f); } - // Static framing: don't evaluate. - if(framing_ == Framing::Static) { - return; - } - // Border reactive: take frame as gospel. if(framing_ == Framing::BorderReactive) { if(rect != posted_rect_) { @@ -479,21 +502,6 @@ void CRT::posit(Display::Rect rect) { } } - // Continue with any ongoing animation. - if(animation_step_ < AnimationSteps) { - set_rect(current_rect()); - ++animation_step_; - - if(animation_step_ == AnimationSteps) { - if(framing_ == Framing::CalibratingAutomaticFixed) { - framing_ = - border_rect_ != active_rect_ ? - Framing::BorderReactive : Framing::Static; - return; - } - } - } - if(!has_first_reading_) { rect_accumulator_.posit(rect); From 49601e0a783f7836cebd55cbb3a797308825a1b4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 30 Oct 2025 09:09:53 -0400 Subject: [PATCH 7/7] Introduce dynamic crop option to the BBC. --- Machines/Acorn/BBCMicro/BBCMicro.cpp | 23 +++++++--- .../Clock Signal.xcodeproj/project.pbxproj | 12 +++++ .../Base.lproj/DynamicCropOptions.xib | 45 +++++++++++++++++++ .../Base.lproj/DynamicCropOptions.xib | 45 +++++++++++++++++++ .../StaticAnalyser/CSStaticAnalyser.mm | 3 ++ 5 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 OSBindings/Mac/Clock Signal/Base.lproj/DynamicCropOptions.xib create mode 100644 OSBindings/Mac/Clock Signal/Documents/Base.lproj/DynamicCropOptions.xib diff --git a/Machines/Acorn/BBCMicro/BBCMicro.cpp b/Machines/Acorn/BBCMicro/BBCMicro.cpp index 83a7b2aaf..30b2b618a 100644 --- a/Machines/Acorn/BBCMicro/BBCMicro.cpp +++ b/Machines/Acorn/BBCMicro/BBCMicro.cpp @@ -379,10 +379,21 @@ public: crt_(1024, 1, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red1Green1Blue1), ram_(ram), system_via_(system_via) - { - crt_.set_dynamic_framing( - Outputs::Display::Rect(0.13333f, 0.06507f, 0.71579f, 0.86069f), - 0.0f, 0.05f); + {} + + void set_dynamic_framing(const bool enable) { + dynamic_framing_ = enable; + if(enable) { + crt_.set_dynamic_framing( + Outputs::Display::Rect(0.13333f, 0.06507f, 0.71579f, 0.86069f), + 0.0f, 0.05f); + } else { + crt_.set_fixed_framing(crt_.get_rect_for_area(30, 256, 160, 800)); + } + } + + bool dynamic_framing() const { + return dynamic_framing_; } void set_palette(const uint8_t value) { @@ -612,6 +623,7 @@ private: // int cycles_into_hsync_ = 0; Outputs::CRT::CRT crt_; + bool dynamic_framing_ = true; uint8_t *pixel_data_ = nullptr, *pixel_pointer_ = nullptr; size_t pixels_collected() const { @@ -1155,12 +1167,13 @@ private: // MARK: - Configuration options. std::unique_ptr get_options() const final { auto options = std::make_unique(Configurable::OptionsType::UserFriendly); + options->dynamic_crop = crtc_bus_handler_.dynamic_framing(); return options; } void set_options(const std::unique_ptr &str) final { const auto options = dynamic_cast(str.get()); - (void)options; + crtc_bus_handler_.set_dynamic_framing(options->dynamic_crop); } }; diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 2f96bc791..e63f14720 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -367,6 +367,7 @@ 4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; }; 4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B49F0A723346F7A0045E6A6 /* MacintoshOptions.xib */; }; 4B4A75BD2EB2C55100EA398F /* CompositeDynamicCropOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B4A75BB2EB2C55100EA398F /* CompositeDynamicCropOptions.xib */; }; + 4B4A75C02EB399D700EA398F /* DynamicCropOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B4A75BE2EB399D700EA398F /* DynamicCropOptions.xib */; }; 4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */; }; 4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; 4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; }; @@ -1610,6 +1611,7 @@ 4B47F6C4241C87A100ED06F7 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = ""; }; 4B49F0A823346F7A0045E6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MacintoshOptions.xib"; sourceTree = SOURCE_ROOT; }; 4B4A75BC2EB2C55100EA398F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/CompositeDynamicCropOptions.xib; sourceTree = ""; }; + 4B4A75BF2EB399D700EA398F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/DynamicCropOptions.xib; sourceTree = ""; }; 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AY38910.cpp; sourceTree = ""; }; 4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AY38910.hpp; sourceTree = ""; }; 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KonamiSCC.cpp; sourceTree = ""; }; @@ -3462,6 +3464,7 @@ 4B8FE2131DA19D5F0090D3CE /* Atari2600Options.xib */, 4B4A75BB2EB2C55100EA398F /* CompositeDynamicCropOptions.xib */, 4BEEE6BB20DC72EA003723BF /* CompositeOptions.xib */, + 4B4A75BE2EB399D700EA398F /* DynamicCropOptions.xib */, 4B8FE2151DA19D5F0090D3CE /* MachineDocument.xib */, 4B49F0A723346F7A0045E6A6 /* MacintoshOptions.xib */, 4B2A332B1DB86821002876E3 /* OricOptions.xib */, @@ -5718,6 +5721,7 @@ 4B1FBE202D77AAC500BAC888 /* ProcessorByModel.hpp in Resources */, 4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */, 4B051C93266D9D6900CA44E8 /* ROMImages in Resources */, + 4B4A75C02EB399D700EA398F /* DynamicCropOptions.xib in Resources */, 4B79E4461E3AF38600141F11 /* floppy525.png in Resources */, 4BEEE6BD20DC72EB003723BF /* CompositeOptions.xib in Resources */, 4B1497981EE4B97F00CE2596 /* ZX8081Options.xib in Resources */, @@ -7015,6 +7019,14 @@ name = CompositeDynamicCropOptions.xib; sourceTree = ""; }; + 4B4A75BE2EB399D700EA398F /* DynamicCropOptions.xib */ = { + isa = PBXVariantGroup; + children = ( + 4B4A75BF2EB399D700EA398F /* Base */, + ); + name = DynamicCropOptions.xib; + sourceTree = ""; + }; 4B55DD8120DF06680043F2E5 /* MachinePicker.xib */ = { isa = PBXVariantGroup; children = ( diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/DynamicCropOptions.xib b/OSBindings/Mac/Clock Signal/Base.lproj/DynamicCropOptions.xib new file mode 100644 index 000000000..f5c6f2eda --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Base.lproj/DynamicCropOptions.xib @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OSBindings/Mac/Clock Signal/Documents/Base.lproj/DynamicCropOptions.xib b/OSBindings/Mac/Clock Signal/Documents/Base.lproj/DynamicCropOptions.xib new file mode 100644 index 000000000..1ae9c4173 --- /dev/null +++ b/OSBindings/Mac/Clock Signal/Documents/Base.lproj/DynamicCropOptions.xib @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm index e35e57280..d9472b6c1 100644 --- a/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm +++ b/OSBindings/Mac/Clock Signal/Machine/StaticAnalyser/CSStaticAnalyser.mm @@ -402,12 +402,15 @@ static Analyser::Static::ZX8081::Target::MemoryModel ZX8081MemoryModelFromSize(K // MARK: - NIB mapping - (NSString *)optionsNibName { + // TODO: the below could be worked out dynamically, I think. It's a bit of a hangover from before configuration + // options were reflective. switch(_targets.front()->machine) { case Analyser::Machine::AmstradCPC: return @"CompositeDynamicCropOptions"; case Analyser::Machine::Archimedes: return @"QuickLoadOptions"; case Analyser::Machine::AppleII: return @"AppleIIOptions"; case Analyser::Machine::Atari2600: return @"Atari2600Options"; case Analyser::Machine::AtariST: return @"CompositeOptions"; + case Analyser::Machine::BBCMicro: return @"DynamicCropOptions"; case Analyser::Machine::ColecoVision: return @"CompositeOptions"; case Analyser::Machine::Electron: return @"QuickLoadCompositeOptions"; case Analyser::Machine::Enterprise: return @"CompositeOptions";