diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp index b6281d291..ceb651eab 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp @@ -60,6 +60,13 @@ void MultiCRTMachine::set_scan_target(Outputs::Display::ScanTarget *scan_target) if(crt_machine) crt_machine->set_scan_target(scan_target); } +Outputs::Display::ScanStatus MultiCRTMachine::get_scan_status() const { + CRTMachine::Machine *const crt_machine = machines_.front()->crt_machine(); + if(crt_machine) crt_machine->get_scan_status(); + + return Outputs::Display::ScanStatus(); +} + Outputs::Speaker::Speaker *MultiCRTMachine::get_speaker() { return speaker_; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp index 1ecfff9fe..974e2ebe2 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp @@ -54,6 +54,7 @@ class MultiCRTMachine: public CRTMachine::Machine { // Below is the standard CRTMachine::Machine interface; see there for documentation. void set_scan_target(Outputs::Display::ScanTarget *scan_target) override; + Outputs::Display::ScanStatus get_scan_status() const override; Outputs::Speaker::Speaker *get_speaker() override; void run_for(Time::Seconds duration) override; diff --git a/ClockReceiver/JustInTime.hpp b/ClockReceiver/JustInTime.hpp index cc83a0f90..528a4a878 100644 --- a/ClockReceiver/JustInTime.hpp +++ b/ClockReceiver/JustInTime.hpp @@ -43,6 +43,13 @@ template , but preserves constness. + forceinline const T *operator->() const { + auto non_const_this = const_cast *>(this); + non_const_this->flush(); + return &object_; + } + /// Returns a pointer to the included object without flushing time. forceinline T *last_valid() { return &object_; diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 8b2406c84..fc8da5682 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -84,6 +84,7 @@ template class MOS6560 { } void set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const { return crt_.get_scan_status(); } void set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); } Outputs::Speaker::Speaker *get_speaker() { return &speaker_; } diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index b9b14fc36..de1d34015 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -117,6 +117,10 @@ void TMS9918::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } +Outputs::Display::ScanStatus TMS9918::get_scan_status() const { + return crt_.get_scan_status(); +} + void TMS9918::set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); } diff --git a/Components/9918/9918.hpp b/Components/9918/9918.hpp index ad550cd0a..15908b6bf 100644 --- a/Components/9918/9918.hpp +++ b/Components/9918/9918.hpp @@ -44,6 +44,9 @@ class TMS9918: public Base { /*! Sets the scan target this TMS will post content to. */ void set_scan_target(Outputs::Display::ScanTarget *); + /// Gets the current scan status. + Outputs::Display::ScanStatus get_scan_status() const; + /*! Sets the type of display the CRT will request. */ void set_display_type(Outputs::Display::DisplayType); diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index b1fe93c19..83aa396c5 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -335,6 +335,11 @@ class CRTCBusHandler { crt_.set_scan_target(scan_target); } + /// @returns The current scan status. + Outputs::Display::ScanStatus get_scan_status() const { + return crt_.get_scan_status(); + } + /// Sets the type of display. void set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); @@ -1006,6 +1011,11 @@ template class ConcreteMachine: crtc_bus_handler_.set_scan_target(scan_target); } + /// A CRTMachine function; returns the current scan status. + Outputs::Display::ScanStatus get_scan_status() const final { + return crtc_bus_handler_.get_scan_status(); + } + /// A CRTMachine function; sets the output display type. void set_display_type(Outputs::Display::DisplayType display_type) override final { crtc_bus_handler_.set_display_type(display_type); diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 4819d4967..6c2ae0641 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -421,6 +421,10 @@ template class ConcreteMachine: video_.set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return video_.get_scan_status(); + } + /// Sets the type of display. void set_display_type(Outputs::Display::DisplayType display_type) override { video_.set_display_type(display_type); diff --git a/Machines/Apple/AppleII/Video.cpp b/Machines/Apple/AppleII/Video.cpp index 5370aaab2..996ba5cf6 100644 --- a/Machines/Apple/AppleII/Video.cpp +++ b/Machines/Apple/AppleII/Video.cpp @@ -47,6 +47,10 @@ void VideoBase::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } +Outputs::Display::ScanStatus VideoBase::get_scan_status() const { + return crt_.get_scan_status(); +} + void VideoBase::set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); } diff --git a/Machines/Apple/AppleII/Video.hpp b/Machines/Apple/AppleII/Video.hpp index ac7e4ab70..3c1c6bb20 100644 --- a/Machines/Apple/AppleII/Video.hpp +++ b/Machines/Apple/AppleII/Video.hpp @@ -40,6 +40,9 @@ class VideoBase { /// Sets the scan target. void set_scan_target(Outputs::Display::ScanTarget *scan_target); + /// Gets the current scan status. + Outputs::Display::ScanStatus get_scan_status() const; + /// Sets the type of output. void set_display_type(Outputs::Display::DisplayType); diff --git a/Machines/Apple/Macintosh/Macintosh.cpp b/Machines/Apple/Macintosh/Macintosh.cpp index 53126dd63..c6bce2d79 100644 --- a/Machines/Apple/Macintosh/Macintosh.cpp +++ b/Machines/Apple/Macintosh/Macintosh.cpp @@ -175,6 +175,10 @@ template class ConcreteMachin video_.set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return video_.get_scan_status(); + } + Outputs::Speaker::Speaker *get_speaker() override { return &audio_.speaker; } diff --git a/Machines/Apple/Macintosh/Video.cpp b/Machines/Apple/Macintosh/Video.cpp index 640afc14a..a04c62a4d 100644 --- a/Machines/Apple/Macintosh/Video.cpp +++ b/Machines/Apple/Macintosh/Video.cpp @@ -37,6 +37,10 @@ void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } +Outputs::Display::ScanStatus Video::get_scan_status() const { + return crt_.get_scan_status(); +} + void Video::run_for(HalfCycles duration) { // Determine the current video and audio bases. These values don't appear to be latched, they apply immediately. const size_t video_base = (use_alternate_screen_buffer_ ? (0xffff2700 >> 1) : (0xffffa700 >> 1)) & ram_mask_; diff --git a/Machines/Apple/Macintosh/Video.hpp b/Machines/Apple/Macintosh/Video.hpp index e63f9c817..59f5282a2 100644 --- a/Machines/Apple/Macintosh/Video.hpp +++ b/Machines/Apple/Macintosh/Video.hpp @@ -42,6 +42,9 @@ class Video { */ void set_scan_target(Outputs::Display::ScanTarget *scan_target); + /// Gets the current scan status. + Outputs::Display::ScanStatus get_scan_status() const; + /*! Produces the next @c duration period of pixels. */ diff --git a/Machines/Atari/2600/Atari2600.cpp b/Machines/Atari/2600/Atari2600.cpp index c727482f1..9ce05d7a8 100644 --- a/Machines/Atari/2600/Atari2600.cpp +++ b/Machines/Atari/2600/Atari2600.cpp @@ -161,6 +161,10 @@ class ConcreteMachine: bus_->tia_.set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return bus_->tia_.get_scan_status(); + } + Outputs::Speaker::Speaker *get_speaker() override { return &bus_->speaker_; } diff --git a/Machines/Atari/2600/TIA.cpp b/Machines/Atari/2600/TIA.cpp index 64a4603d2..4b17bdbf9 100644 --- a/Machines/Atari/2600/TIA.cpp +++ b/Machines/Atari/2600/TIA.cpp @@ -142,6 +142,10 @@ void TIA::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } +Outputs::Display::ScanStatus TIA::get_scan_status() const { + return crt_.get_scan_status(); +} + void TIA::run_for(const Cycles cycles) { int number_of_cycles = int(cycles.as_integral()); diff --git a/Machines/Atari/2600/TIA.hpp b/Machines/Atari/2600/TIA.hpp index 57fcf636c..45b238145 100644 --- a/Machines/Atari/2600/TIA.hpp +++ b/Machines/Atari/2600/TIA.hpp @@ -75,6 +75,7 @@ class TIA { void set_crt_delegate(Outputs::CRT::Delegate *); void set_scan_target(Outputs::Display::ScanTarget *); + Outputs::Display::ScanStatus get_scan_status() const; private: Outputs::CRT::CRT crt_; diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 3ac60a22a..c6f91004b 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -139,6 +139,10 @@ class ConcreteMachine: video_->set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return video_->get_scan_status(); + } + void set_display_type(Outputs::Display::DisplayType display_type) final { video_->set_display_type(display_type); } diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index 00931efbc..6937ae72a 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -127,6 +127,10 @@ void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } +Outputs::Display::ScanStatus Video::get_scan_status() const { + return crt_.get_scan_status(); +} + void Video::set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); } diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp index 4f9f06dd5..a971b800d 100644 --- a/Machines/Atari/ST/Video.hpp +++ b/Machines/Atari/ST/Video.hpp @@ -40,6 +40,9 @@ class Video { */ void set_scan_target(Outputs::Display::ScanTarget *scan_target); + /// Gets the current scan status. + Outputs::Display::ScanStatus get_scan_status() const; + /*! Sets the type of output. */ diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index db100c99c..09577098c 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -37,6 +37,11 @@ class Machine { */ virtual void set_scan_target(Outputs::Display::ScanTarget *scan_target) = 0; + /*! + @returns The current scan status. + */ + virtual Outputs::Display::ScanStatus get_scan_status() const = 0; + /// @returns The speaker that receives this machine's output, or @c nullptr if this machine is mute. virtual Outputs::Speaker::Speaker *get_speaker() = 0; diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index 883b7a96d..65c26fbca 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -185,6 +185,10 @@ class ConcreteMachine: vdp_->set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return vdp_->get_scan_status(); + } + void set_display_type(Outputs::Display::DisplayType display_type) override { vdp_->set_display_type(display_type); } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 21bec5eca..0b2fe74bd 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -626,6 +626,10 @@ class ConcreteMachine: mos6560_.set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return mos6560_.get_scan_status(); + } + void set_display_type(Outputs::Display::DisplayType display_type) override final { mos6560_.set_display_type(display_type); } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index e9bfc5448..868cf26f0 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -383,6 +383,10 @@ class ConcreteMachine: video_output_.set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return video_output_.get_scan_status(); + } + void set_display_type(Outputs::Display::DisplayType display_type) override { video_output_.set_display_type(display_type); } diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 4dab4fde2..d87fbb721 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -56,6 +56,10 @@ void VideoOutput::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } +Outputs::Display::ScanStatus VideoOutput::get_scan_status() const { + return crt_.get_scan_status(); +} + void VideoOutput::set_display_type(Outputs::Display::DisplayType display_type) { crt_.set_display_type(display_type); } diff --git a/Machines/Electron/Video.hpp b/Machines/Electron/Video.hpp index 6daeec35a..06ab4f797 100644 --- a/Machines/Electron/Video.hpp +++ b/Machines/Electron/Video.hpp @@ -39,6 +39,9 @@ class VideoOutput { /// Sets the destination for output. void set_scan_target(Outputs::Display::ScanTarget *scan_target); + /// Gets the current scan status. + Outputs::Display::ScanStatus get_scan_status() const; + /// Sets the type of output. void set_display_type(Outputs::Display::DisplayType); diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 385a53fe9..76ee2fff3 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -282,6 +282,10 @@ class ConcreteMachine: vdp_->set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return vdp_->get_scan_status(); + } + void set_display_type(Outputs::Display::DisplayType display_type) override { vdp_->set_display_type(display_type); } diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 3c9b589e8..e6116f989 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -177,6 +177,10 @@ class ConcreteMachine: vdp_->set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return vdp_->get_scan_status(); + } + void set_display_type(Outputs::Display::DisplayType display_type) override { vdp_->set_display_type(display_type); } diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 53eee6691..742cf36fc 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -556,6 +556,10 @@ template class Co video_output_.set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return video_output_.get_scan_status(); + } + void set_display_type(Outputs::Display::DisplayType display_type) final { video_output_.set_display_type(display_type); } diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index 5ed2b3a0e..d0414a591 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -56,6 +56,10 @@ void VideoOutput::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } +Outputs::Display::ScanStatus VideoOutput::get_scan_status() const { + return crt_.get_scan_status(); +} + void VideoOutput::set_colour_rom(const std::vector &rom) { for(std::size_t c = 0; c < 8; c++) { colour_forms_[c] = 0; diff --git a/Machines/Oric/Video.hpp b/Machines/Oric/Video.hpp index 2a6e18f3a..00b75051f 100644 --- a/Machines/Oric/Video.hpp +++ b/Machines/Oric/Video.hpp @@ -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::ScanStatus get_scan_status() const; private: uint8_t *ram_; diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index 4fb4fd152..89b46f020 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -109,3 +109,7 @@ void Video::output_byte(uint8_t byte) { void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) { crt_.set_scan_target(scan_target); } + +Outputs::Display::ScanStatus Video::get_scan_status() const { + return crt_.get_scan_status(); +} diff --git a/Machines/ZX8081/Video.hpp b/Machines/ZX8081/Video.hpp index b58c6c149..5f6719e31 100644 --- a/Machines/ZX8081/Video.hpp +++ b/Machines/ZX8081/Video.hpp @@ -36,12 +36,16 @@ class Video { /// Sets the current sync output. void set_sync(bool sync); + /// Causes @c byte to be serialised into pixels and output over the next four cycles. void output_byte(uint8_t byte); /// Sets the scan target. void set_scan_target(Outputs::Display::ScanTarget *scan_target); + /// Gets the current scan status. + Outputs::Display::ScanStatus get_scan_status() const; + private: bool sync_ = false; uint8_t *line_data_ = nullptr; diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 569be4e46..87ec3e1a9 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -318,6 +318,10 @@ template class ConcreteMachine: video_.set_scan_target(scan_target); } + Outputs::Display::ScanStatus get_scan_status() const final { + return video_.get_scan_status(); + } + Outputs::Speaker::Speaker *get_speaker() override final { return is_zx81 ? &speaker_ : nullptr; } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 1465a4f62..47f9c7286 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -67,7 +67,7 @@ #include +#include "../ClockReceiver/TimeTypes.hpp" namespace Outputs { namespace Display { @@ -309,6 +310,18 @@ struct ScanTarget { virtual void announce(Event event, bool is_visible, const Scan::EndPoint &location, uint8_t composite_amplitude) {} }; +struct ScanStatus { + /// The current (prediced) length of a field. + Time::Seconds field_duration; + /// The difference applied to the field_duration estimate during the last field. + Time::Seconds field_duration_gradient; + /// The distance into the current field, from 0 (start of field) to 1 (end of field). + /// This is unlikely to be linear but should increase monotonically, being a measure + /// of the current vertical position — i.e. if current_position = 0.8 then a caller can + /// conclude that the top 80% of the visible part of the display has been painted. + float current_position; +}; + /*! Provides a null target for scans. */