From 768b3709b8c57ae5e6c899fcf39c89ccd2a8fbe8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 10 Dec 2019 20:25:27 -0500 Subject: [PATCH 1/7] Corrects audio clock rate. --- Machines/Atari/ST/AtariST.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 322d35c7e..a9e8697c9 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -67,7 +67,7 @@ class ConcreteMachine: speaker_(ay_), ikbd_(keyboard_acia_->transmit, keyboard_acia_->receive) { set_clock_rate(CLOCK_RATE); - speaker_.set_input_rate(CLOCK_RATE / 4); + speaker_.set_input_rate(float(CLOCK_RATE) / 4.0f); ram_.resize(512 * 1024); // i.e. 512kb video_->set_ram(reinterpret_cast(ram_.data()), ram_.size()); From 0a405d1c06eeef47fe955f57f70c3f47e0bcd80b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 10 Dec 2019 21:24:15 -0500 Subject: [PATCH 2/7] Introduces a latency between DE and load. --- Machines/Atari/ST/Video.cpp | 37 ++++++++++++++++++++++++++++--------- Machines/Atari/ST/Video.hpp | 3 +++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index 6fa3a9aa0..1eee97234 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -145,6 +145,7 @@ void Video::run_for(HalfCycles duration) { if(horizontal_timings.set_blank > x_) next_event = std::min(next_event, horizontal_timings.set_blank); if(horizontal_timings.reset_enable > x_) next_event = std::min(next_event, horizontal_timings.reset_enable); if(horizontal_timings.set_enable > x_) next_event = std::min(next_event, horizontal_timings.set_enable); + if(next_load_toggle_ > x_) next_event = std::min(next_event, next_load_toggle_); // Check for events that are relative to existing latched state. if(line_length_ - 50*2 > x_) next_event = std::min(next_event, line_length_ - 50*2); @@ -163,14 +164,16 @@ void Video::run_for(HalfCycles duration) { shifter_.output_sync(run_length); } else if(horizontal_.blank || vertical_.blank) { shifter_.output_blank(run_length); - } else if(!vertical_.enable || !horizontal_.enable) { + } else if(!load_) { shifter_.output_border(run_length, output_bpp_); } else { + const int since_load = x_ - load_base_; + // There will be pixels this line, subject to the shifter pipeline. // Divide into 8-[half-]cycle windows; at the start of each window fetch a word, // and during the rest of the window, shift out. - int start_column = x_ >> 3; - const int end_column = (x_ + run_length) >> 3; + int start_column = since_load >> 3; + const int end_column = (since_load + run_length) >> 3; // Rules obeyed below: // @@ -182,9 +185,9 @@ void Video::run_for(HalfCycles duration) { shifter_.output_pixels(run_length, output_bpp_); } else { // Continue the current column if partway across. - if(x_&7) { + if(since_load&7) { // If at least one column boundary is crossed, complete this column. - shifter_.output_pixels(8 - (x_ & 7), output_bpp_); + shifter_.output_pixels(8 - (since_load & 7), output_bpp_); ++start_column; // This starts a new column, so latch a new word. latch_word(); } @@ -197,8 +200,8 @@ void Video::run_for(HalfCycles duration) { } // Output the start of the next column, if necessary. - if((x_ + run_length) & 7) { - shifter_.output_pixels((x_ + run_length) & 7, output_bpp_); + if((since_load + run_length) & 7) { + shifter_.output_pixels((since_load + run_length) & 7, output_bpp_); } } } @@ -248,8 +251,20 @@ void Video::run_for(HalfCycles duration) { else if(horizontal_timings.set_blank == x_) horizontal_.blank = true; else if(horizontal_timings.reset_enable == x_) horizontal_.enable = false; else if(horizontal_timings.set_enable == x_) horizontal_.enable = true; - else if(line_length_ - 50*2 == x_) horizontal_.sync = true; + else if(line_length_ - 50*2 == x_) { horizontal_.sync = true; horizontal_.enable = false; } else if(line_length_ - 10*2 == x_) horizontal_.sync = false; + else if(next_load_toggle_ == x_) { + next_load_toggle_ = -1; + load_ ^= true; + load_base_ = x_; + } + + // Check for an upcoming load toggle — that is, a time at which load_ will be either + // disabled or enabled. These are scheduled for four cycles after the event that should + // change load has occurred. + if((horizontal_.enable && vertical_.enable) != load_ && next_load_toggle_ == -1) { + next_load_toggle_ = x_ + 8; // 4 cycles = 8 half-cycles + } // Check vertical events. if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && x_ == vsync_x_position) { @@ -406,6 +421,8 @@ void Video::write(int address, uint16_t value) { } void Video::update_output_mode() { +// const auto old_frequency = field_frequency_; + // If this is black and white mode, that's that. switch((video_mode_ >> 8) & 3) { case 0: output_bpp_ = OutputBpp::Four; break; @@ -416,10 +433,12 @@ void Video::update_output_mode() { case 2: output_bpp_ = OutputBpp::One; field_frequency_ = FieldFrequency::SeventyTwo; +// if(field_frequency_ != old_frequency) { +// printf("%d, %d -> %d\n", x_, y_, field_frequency_); +// } return; } -// const auto old_frequency = field_frequency_; field_frequency_ = (sync_mode_ & 0x200) ? FieldFrequency::Fifty : FieldFrequency::Sixty; // if(field_frequency_ != old_frequency) { // printf("%d, %d -> %d\n", x_, y_, field_frequency_); diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp index fc8a4b476..8866d3700 100644 --- a/Machines/Atari/ST/Video.hpp +++ b/Machines/Atari/ST/Video.hpp @@ -116,6 +116,9 @@ class Video { uint16_t line_buffer_[256]; int x_ = 0, y_ = 0, next_y_ = 0; + int next_load_toggle_ = -1; + bool load_ = false; + int load_base_ = 0; uint16_t video_mode_ = 0; uint16_t sync_mode_ = 0; From de4403e02124a27ff2a20460b6b4e8349d6cf5ef Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 10 Dec 2019 22:17:57 -0500 Subject: [PATCH 3/7] Corrects blank timing. --- Machines/Atari/ST/Video.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index 1eee97234..dbf9cbf12 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -58,9 +58,9 @@ const struct HorizontalParams { const int length; } horizontal_params[3] = { - {56*2, 376*2, 450*2, 28*2, 512*2}, - {52*2, 372*2, 450*2, 24*2, 508*2}, - {4*2, 164*2, 184*2, 2*2, 224*2} + {56*2, 376*2, 448*2, 28*2, 512*2}, + {52*2, 372*2, 448*2, 24*2, 508*2}, + {4*2, 164*2, 999*2, 999*2, 224*2} // 72Hz mode doesn't set or reset blank. }; const HorizontalParams &horizontal_parameters(Video::FieldFrequency frequency) { @@ -73,10 +73,16 @@ struct Checker { for(int c = 0; c < 3; ++c) { // Expected horizontal order of events: reset blank, enable display, disable display, enable blank (at least 50 before end of line), end of line const auto horizontal = horizontal_parameters(Video::FieldFrequency(c)); - assert(horizontal.reset_blank < horizontal.set_enable); - assert(horizontal.set_enable < horizontal.reset_enable); - assert(horizontal.reset_enable < horizontal.set_blank); - assert(horizontal.set_blank+50 < horizontal.length); + + if(c < 2) { + assert(horizontal.reset_blank < horizontal.set_enable); + assert(horizontal.set_enable < horizontal.reset_enable); + assert(horizontal.reset_enable < horizontal.set_blank); + assert(horizontal.set_blank+50 < horizontal.length); + } else { + assert(horizontal.set_enable < horizontal.reset_enable); + assert(horizontal.set_enable+50 Date: Wed, 11 Dec 2019 23:22:20 -0500 Subject: [PATCH 4/7] Tweaks timings yet further, adds a FIFO reset. The accuracy of this may require further research. --- Machines/Atari/ST/Video.cpp | 47 +++++++++++++++++++++++-------------- Machines/Atari/ST/Video.hpp | 1 + 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index dbf9cbf12..cb8f1a901 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -38,6 +38,7 @@ const VerticalParams &vertical_parameters(Video::FieldFrequency frequency) { } +#define CYCLE(x) ((x) * 2) /*! Defines the horizontal counts at which mode-specific events will occur: horizontal enable being set and being reset, blank being set and reset, and the @@ -58,9 +59,9 @@ const struct HorizontalParams { const int length; } horizontal_params[3] = { - {56*2, 376*2, 448*2, 28*2, 512*2}, - {52*2, 372*2, 448*2, 24*2, 508*2}, - {4*2, 164*2, 999*2, 999*2, 224*2} // 72Hz mode doesn't set or reset blank. + {CYCLE(56), CYCLE(376), CYCLE(450), CYCLE(28), CYCLE(512)}, + {CYCLE(52), CYCLE(372), CYCLE(450), CYCLE(24), CYCLE(508)}, + {CYCLE(4), CYCLE(164), CYCLE(999), CYCLE(999), CYCLE(224)} // 72Hz mode doesn't set or reset blank. }; const HorizontalParams &horizontal_parameters(Video::FieldFrequency frequency) { @@ -93,8 +94,10 @@ struct Checker { } checker; #endif -const int de_delay_period = 28*2; // Number of half cycles after DE that observed DE changes. -const int vsync_x_position = 54*2; // Horizontal cycle on which vertical sync changes happen. +const int de_delay_period = CYCLE(28); // Number of half cycles after DE that observed DE changes. +const int vsync_x_position = CYCLE(54); // Horizontal cycle on which vertical sync changes happen. +const int hsync_start = CYCLE(48); // Cycles before end of line when hsync starts. +const int hsync_end = CYCLE(8); // Cycles before end of line when hsync ends. // "VSYNC starts 104 cycles after the start of the previous line's HSYNC, so that's 4 cycles before DE would be activated. "; // hsync is at -50, so that's +54 @@ -154,8 +157,8 @@ void Video::run_for(HalfCycles duration) { if(next_load_toggle_ > x_) next_event = std::min(next_event, next_load_toggle_); // Check for events that are relative to existing latched state. - if(line_length_ - 50*2 > x_) next_event = std::min(next_event, line_length_ - 50*2); - if(line_length_ - 10*2 > x_) next_event = std::min(next_event, line_length_ - 10*2); + if(line_length_ - hsync_start > x_) next_event = std::min(next_event, line_length_ - hsync_start); + if(line_length_ - hsync_end > x_) next_event = std::min(next_event, line_length_ - hsync_end); // Also, a vertical sync event might intercede. if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && x_ < vsync_x_position && next_event >= vsync_x_position) { @@ -213,10 +216,10 @@ void Video::run_for(HalfCycles duration) { } // Check for whether line length should have been latched during this run. - if(x_ <= 54*2 && (x_ + run_length) > 54*2) line_length_ = horizontal_timings.length; + if(x_ <= CYCLE(54) && (x_ + run_length) > CYCLE(54)) line_length_ = horizontal_timings.length; // Make a decision about vertical state on cycle 502. - if(x_ <= 502*2 && (x_ + run_length) > 502*2) { + if(x_ <= CYCLE(502) && (x_ + run_length) > CYCLE(502)) { next_y_ = y_ + 1; next_vertical_ = vertical_; next_vertical_.sync_schedule = VerticalState::SyncSchedule::None; @@ -233,6 +236,7 @@ void Video::run_for(HalfCycles duration) { } else if(next_y_ == vertical_timings.height) { next_y_ = 0; current_address_ = base_address_ >> 1; + reset_fifo(); // TODO: remove this, I think, once otherwise stable. // Consider a shout out to the range observer. if(previous_base_address_ != base_address_) { @@ -257,8 +261,8 @@ void Video::run_for(HalfCycles duration) { else if(horizontal_timings.set_blank == x_) horizontal_.blank = true; else if(horizontal_timings.reset_enable == x_) horizontal_.enable = false; else if(horizontal_timings.set_enable == x_) horizontal_.enable = true; - else if(line_length_ - 50*2 == x_) { horizontal_.sync = true; horizontal_.enable = false; } - else if(line_length_ - 10*2 == x_) horizontal_.sync = false; + else if(line_length_ - hsync_start == x_) { horizontal_.sync = true; horizontal_.enable = false; } + else if(line_length_ - hsync_end == x_) horizontal_.sync = false; else if(next_load_toggle_ == x_) { next_load_toggle_ = -1; load_ ^= true; @@ -308,6 +312,10 @@ void Video::latch_word() { } } +void Video::reset_fifo() { + data_latch_position_ = 0; +} + bool Video::hsync() { return horizontal_.sync; } @@ -362,8 +370,8 @@ HalfCycles Video::get_next_sequence_point() { } // Test for beginning and end of horizontal sync. - if(x_ < line_length_ - 50*2) event_time = std::min(line_length_ - 50*2, event_time); - else if(x_ < line_length_ - 10*2) event_time = std::min(line_length_ - 10*2, event_time); + if(x_ < line_length_ - hsync_start) event_time = std::min(line_length_ - hsync_start, event_time); + else if(x_ < line_length_ - hsync_end) event_time = std::min(line_length_ - hsync_end, event_time); // It wasn't any of those, so as a temporary expedient, just supply end of line. return HalfCycles(event_time - x_); @@ -426,7 +434,8 @@ void Video::write(int address, uint16_t value) { } void Video::update_output_mode() { -// const auto old_frequency = field_frequency_; + // All I know about this is: "the 71-Hz-switch does something like a shifter-reset." + const auto old_frequency = field_frequency_; // If this is black and white mode, that's that. switch((video_mode_ >> 8) & 3) { @@ -438,16 +447,18 @@ void Video::update_output_mode() { case 2: output_bpp_ = OutputBpp::One; field_frequency_ = FieldFrequency::SeventyTwo; -// if(field_frequency_ != old_frequency) { + if(field_frequency_ != old_frequency) { + reset_fifo(); // printf("%d, %d -> %d\n", x_, y_, field_frequency_); -// } + } return; } field_frequency_ = (sync_mode_ & 0x200) ? FieldFrequency::Fifty : FieldFrequency::Sixty; -// if(field_frequency_ != old_frequency) { + if(field_frequency_ != old_frequency && old_frequency == FieldFrequency::SeventyTwo) { + reset_fifo(); // printf("%d, %d -> %d\n", x_, y_, field_frequency_); -// } + } } // MARK: - The shifter diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp index 8866d3700..d8e322a05 100644 --- a/Machines/Atari/ST/Video.hpp +++ b/Machines/Atari/ST/Video.hpp @@ -153,6 +153,7 @@ class Video { int data_latch_position_ = 0; uint16_t data_latch_[4] = {0, 0, 0, 0}; void latch_word(); + void reset_fifo(); class Shifter { public: From f6f2b4b90fde360df99f0143c0c3e2a5d1910259 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 12 Dec 2019 22:50:35 -0500 Subject: [PATCH 5/7] Removes double DE edge test. --- Machines/Atari/ST/Video.cpp | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index cb8f1a901..d19e6e357 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -269,12 +269,6 @@ void Video::run_for(HalfCycles duration) { load_base_ = x_; } - // Check for an upcoming load toggle; that is, a time at which load_ will be either - // disabled or enabled. These occur with a brief latency. - if((horizontal_.enable && vertical_.enable) != load_ && next_load_toggle_ == -1) { - next_load_toggle_ = x_ + 8; // 4 cycles = 8 half-cycles - } - // Check vertical events. if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && x_ == vsync_x_position) { vertical_.sync = vertical_.sync_schedule == VerticalState::SyncSchedule::Begin; @@ -292,7 +286,11 @@ void Video::run_for(HalfCycles duration) { // Chuck any deferred output changes into the queue. const bool next_display_enable = vertical_.enable && horizontal_.enable; if(display_enable != next_display_enable) { + // Schedule change in outwardly-visible DE line. add_event(de_delay_period - integer_duration, next_display_enable ? Event::Type::SetDisplayEnable : Event::Type::ResetDisplayEnable); + + // Schedule change in inwardly-visible effect. + next_load_toggle_ = x_ + 8; // 4 cycles = 8 half-cycles } } } @@ -434,30 +432,25 @@ void Video::write(int address, uint16_t value) { } void Video::update_output_mode() { - // All I know about this is: "the 71-Hz-switch does something like a shifter-reset." - const auto old_frequency = field_frequency_; + const auto old_bpp_ = output_bpp_; // If this is black and white mode, that's that. switch((video_mode_ >> 8) & 3) { case 0: output_bpp_ = OutputBpp::Four; break; case 1: output_bpp_ = OutputBpp::Two; break; - - // 1bpp mode ignores the otherwise-programmed frequency. default: - case 2: - output_bpp_ = OutputBpp::One; - field_frequency_ = FieldFrequency::SeventyTwo; - if(field_frequency_ != old_frequency) { - reset_fifo(); -// printf("%d, %d -> %d\n", x_, y_, field_frequency_); - } - return; + case 2: output_bpp_ = OutputBpp::One; break; } - field_frequency_ = (sync_mode_ & 0x200) ? FieldFrequency::Fifty : FieldFrequency::Sixty; - if(field_frequency_ != old_frequency && old_frequency == FieldFrequency::SeventyTwo) { + // 1bpp mode ignores the otherwise-programmed frequency. + if(output_bpp_ == OutputBpp::One) { + field_frequency_ = FieldFrequency::SeventyTwo; + } else { + field_frequency_ = (sync_mode_ & 0x200) ? FieldFrequency::Fifty : FieldFrequency::Sixty; + } + if(output_bpp_ != old_bpp_) { + // "the 71-Hz-switch does something like a shifter-reset." (and some people use a high-low resolutions switch instead) reset_fifo(); -// printf("%d, %d -> %d\n", x_, y_, field_frequency_); } } From 4bcf21732465d37075db7ce7d8a3327379026c5a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 12 Dec 2019 23:20:28 -0500 Subject: [PATCH 6/7] Ensures delayed loading isn't interrupted by blank, hsync. --- Machines/Atari/ST/Video.cpp | 46 ++++++++++++++++++++++++++----------- Machines/Atari/ST/Video.hpp | 6 +++-- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index d19e6e357..b118bac05 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -169,6 +169,28 @@ void Video::run_for(HalfCycles duration) { const int run_length = std::min(integer_duration, next_event - x_); const bool display_enable = vertical_.enable && horizontal_.enable; + // Ensure proper fetching irrespective of the output. + if(load_) { + const int since_load = x_ - load_base_; + + // There will be pixels this line, subject to the shifter pipeline. + // Divide into 8-[half-]cycle windows; at the start of each window fetch a word, + // and during the rest of the window, shift out. + int start_column = since_load >> 3; + const int end_column = (since_load + run_length) >> 3; + + while(start_column != end_column) { + data_latch_[data_latch_position_] = ram_[current_address_ & 262143]; + data_latch_position_ = (data_latch_position_ + 1) & 127; + ++current_address_; + ++start_column; + } + } + + // TODO: if I'm asserting that sync and blank override the shifter (but, presumably, + // the shifter keeps shifting), then output_sync and output_blank need to have an effect + // inside the shifter on the temporary register values. + if(horizontal_.sync || vertical_.sync) { shifter_.output_sync(run_length); } else if(horizontal_.blank || vertical_.blank) { @@ -198,14 +220,14 @@ void Video::run_for(HalfCycles duration) { // If at least one column boundary is crossed, complete this column. shifter_.output_pixels(8 - (since_load & 7), output_bpp_); ++start_column; // This starts a new column, so latch a new word. - latch_word(); + push_latched_data(); } // Run for all columns that have their starts in this time period. int complete_columns = end_column - start_column; while(complete_columns--) { shifter_.output_pixels(8, output_bpp_); - latch_word(); + push_latched_data(); } // Output the start of the next column, if necessary. @@ -295,23 +317,21 @@ void Video::run_for(HalfCycles duration) { } } -void Video::latch_word() { - data_latch_[data_latch_position_] = ram_[current_address_ & 262143]; - ++current_address_; - ++data_latch_position_; - if(data_latch_position_ == 4) { - data_latch_position_ = 0; +void Video::push_latched_data() { + data_latch_read_position_ = (data_latch_read_position_ + 1) & 127; + + if(!(data_latch_read_position_ & 3)) { shifter_.load( - (uint64_t(data_latch_[0]) << 48) | - (uint64_t(data_latch_[1]) << 32) | - (uint64_t(data_latch_[2]) << 16) | - uint64_t(data_latch_[3]) + (uint64_t(data_latch_[(data_latch_read_position_ - 4) & 127]) << 48) | + (uint64_t(data_latch_[(data_latch_read_position_ - 3) & 127]) << 32) | + (uint64_t(data_latch_[(data_latch_read_position_ - 2) & 127]) << 16) | + uint64_t(data_latch_[(data_latch_read_position_ - 1) & 127]) ); } } void Video::reset_fifo() { - data_latch_position_ = 0; + data_latch_read_position_ = data_latch_position_ = 0; } bool Video::hsync() { diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp index d8e322a05..fd7af3027 100644 --- a/Machines/Atari/ST/Video.hpp +++ b/Machines/Atari/ST/Video.hpp @@ -151,8 +151,10 @@ class Video { int line_length_ = 1024; int data_latch_position_ = 0; - uint16_t data_latch_[4] = {0, 0, 0, 0}; - void latch_word(); + int data_latch_read_position_ = 0; + uint16_t data_latch_[128]; + void push_latched_data(); + void reset_fifo(); class Shifter { From c00ae7ce6a9b6050d36cf846756c3592535397fc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 13 Dec 2019 19:57:54 -0500 Subject: [PATCH 7/7] Adds a one-cycle delay on frequency changes. --- Machines/Atari/ST/Video.cpp | 12 ++++++++++-- Machines/Atari/ST/Video.hpp | 4 ++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index b118bac05..22e41770b 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -105,6 +105,7 @@ const int hsync_end = CYCLE(8); // Cycles before end of line when hsync ends. } Video::Video() : + deferrer_([=] (HalfCycles duration) { advance(duration); }), crt_(1024, 1, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Red4Green4Blue4), shifter_(crt_, palette_) { @@ -122,6 +123,10 @@ void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) { } void Video::run_for(HalfCycles duration) { + deferrer_.run_for(duration); +} + +void Video::advance(HalfCycles duration) { const auto horizontal_timings = horizontal_parameters(field_frequency_); const auto vertical_timings = vertical_parameters(field_frequency_); int integer_duration = int(duration.as_integral()); @@ -430,8 +435,11 @@ void Video::write(int address, uint16_t value) { // Sync mode and pixel mode. case 0x05: - sync_mode_ = value; - update_output_mode(); + // Writes to sync mode have a one-cycle delay in effect. + deferrer_.defer(HalfCycles(2), [=] { + sync_mode_ = value; + update_output_mode(); + }); break; case 0x30: video_mode_ = value; diff --git a/Machines/Atari/ST/Video.hpp b/Machines/Atari/ST/Video.hpp index fd7af3027..971d31ac1 100644 --- a/Machines/Atari/ST/Video.hpp +++ b/Machines/Atari/ST/Video.hpp @@ -11,6 +11,7 @@ #include "../../../Outputs/CRT/CRT.hpp" #include "../../../ClockReceiver/ClockReceiver.hpp" +#include "../../../ClockReceiver/DeferredQueue.hpp" #include @@ -103,6 +104,9 @@ class Video { Range get_memory_access_range(); private: + void advance(HalfCycles duration); + DeferredQueue deferrer_; + Outputs::CRT::CRT crt_; RangeObserver *range_observer_ = nullptr;