From 96896f838c30c88d3a554157f61fca783e54bdef Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 23 Apr 2023 12:17:55 -0400 Subject: [PATCH 1/8] Adjust layout inner loop. --- Components/9918/Implementation/9918.cpp | 58 ++++++++++--------- .../9918/Implementation/ClockConverter.hpp | 19 ------ 2 files changed, 30 insertions(+), 47 deletions(-) diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index d87c584a4..7e7cfdb31 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -31,6 +31,8 @@ Base::Base() : // into whether there's a more natural form. It feels unlikely given the diversity of chips modelled. if constexpr (is_sega_vdp(personality)) { + // TODO: all these relate to the old line timing; resource and review. + mode_timing_.line_interrupt_position = 64; mode_timing_.end_of_frame_interrupt_position.column = 63; @@ -39,13 +41,13 @@ Base::Base() : if constexpr (is_yamaha_vdp(personality)) { // TODO: start of sync, or end of sync? - mode_timing_.line_interrupt_position = Timing::StartOfSync; + mode_timing_.line_interrupt_position = 0;//Timing::StartOfSync; } // Establish that output is delayed after reading by `output_lag` cycles, // i.e. the fetch pointer is currently _ahead_ of the output pointer. // - // Start at a random position. + // TODO: Start at a random position. output_pointer_.row = output_pointer_.column = 0; // output_pointer_.row = rand() % 262; // output_pointer_.column = rand() % (Timing::CyclesPerLine - output_lag); @@ -508,43 +510,40 @@ void TMS9918::run_for(const HalfCycles cycles) { output_sync(Timing::CyclesPerLine); } } else { - // Right border. - border(0, Timing::EndOfRightBorder); - // Blanking region: output the entire sequence when the cursor // crosses the start-of-border point. if( - this->output_pointer_.column < Timing::StartOfLeftBorder && - end_column >= Timing::StartOfLeftBorder + this->output_pointer_.column < LineLayout::EndOfLeftErase && + end_column >= LineLayout::EndOfLeftErase ) { - output_blank(Timing::StartOfSync - Timing::EndOfRightBorder); - output_sync(Timing::EndOfSync - Timing::StartOfSync); - output_blank(Timing::StartOfColourBurst - Timing::EndOfSync); - output_default_colour_burst(Timing::EndOfColourBurst - Timing::StartOfColourBurst); - output_blank(Timing::StartOfLeftBorder - Timing::EndOfColourBurst); + output_sync(LineLayout::EndOfSync); + output_blank(LineLayout::StartOfColourBurst - LineLayout::EndOfSync); + output_default_colour_burst(LineLayout::EndOfColourBurst - LineLayout::StartOfColourBurst); + output_blank(LineLayout::EndOfLeftErase - LineLayout::EndOfColourBurst); } - // Border colour for the rest of the line. - border(Timing::StartOfLeftBorder, Timing::CyclesPerLine); + // Border colour until beginning of right erase. + border(LineLayout::EndOfLeftErase, LineLayout::EndOfRightBorder); + + // Right erase. + if(this->output_pointer_.column == Timing::CyclesPerLine) { + output_blank(Timing::CyclesPerLine - LineLayout::EndOfRightBorder); + } } } else { - // Right border. - border(0, Timing::EndOfRightBorder); - // Blanking region. if( - this->output_pointer_.column < Timing::StartOfLeftBorder && - end_column >= Timing::StartOfLeftBorder + this->output_pointer_.column < LineLayout::EndOfLeftErase && + end_column >= LineLayout::EndOfLeftErase ) { - output_blank(Timing::StartOfSync - Timing::EndOfRightBorder); - output_sync(Timing::EndOfSync - Timing::StartOfSync); - output_blank(Timing::StartOfColourBurst - Timing::EndOfSync); - output_default_colour_burst(Timing::EndOfColourBurst - Timing::StartOfColourBurst); - output_blank(Timing::StartOfLeftBorder - Timing::EndOfColourBurst); + output_sync(LineLayout::EndOfSync); + output_blank(LineLayout::StartOfColourBurst - LineLayout::EndOfSync); + output_default_colour_burst(LineLayout::EndOfColourBurst - LineLayout::StartOfColourBurst); + output_blank(LineLayout::EndOfLeftErase - LineLayout::EndOfColourBurst); } // Left border. - border(Timing::StartOfLeftBorder, this->draw_line_buffer_->first_pixel_output_column); + border(LineLayout::EndOfLeftErase, this->draw_line_buffer_->first_pixel_output_column); #define draw(function, clock) { \ const int relative_start = from_internal(start - this->draw_line_buffer_->first_pixel_output_column); \ @@ -588,9 +587,12 @@ void TMS9918::run_for(const HalfCycles cycles) { #undef draw - // Additional right border, if called for. - if(this->draw_line_buffer_->next_border_column != Timing::CyclesPerLine) { - border(this->draw_line_buffer_->next_border_column, Timing::CyclesPerLine); + // Right border. + border(this->draw_line_buffer_->next_border_column, LineLayout::EndOfRightBorder); + + // Right erase. + if(this->output_pointer_.column == Timing::CyclesPerLine) { + output_blank(Timing::CyclesPerLine - LineLayout::EndOfRightBorder); } } diff --git a/Components/9918/Implementation/ClockConverter.hpp b/Components/9918/Implementation/ClockConverter.hpp index 89427c439..0eece395d 100644 --- a/Components/9918/Implementation/ClockConverter.hpp +++ b/Components/9918/Implementation/ClockConverter.hpp @@ -70,25 +70,6 @@ template struct StandardTiming { /// The final internal cycle at which pixels will be output text mode. constexpr static int LastTextCycle = 334 * CyclesPerLine / 342; - - // For the below, the fixed portion of line layout is: - // - // [0, EndOfRightBorder): right border colour - // [EndOfRightBorder, StartOfSync): blank - // [StartOfSync, EndOfSync): sync - // [EndOfSync, StartOfColourBurst): blank - // [StartOfColourBurst, EndOfColourBurst): the colour burst - // [EndOfColourBurst, StartOfLeftBorder): blank - // - // The region from StartOfLeftBorder until the end is then filled with - // some combination of pixels and more border, depending on the vertical - // position of this line and the current screen mode. - constexpr static int EndOfRightBorder = 15 * CyclesPerLine / 342; - constexpr static int StartOfSync = 23 * CyclesPerLine / 342; - constexpr static int EndOfSync = 49 * CyclesPerLine / 342; - constexpr static int StartOfColourBurst = 51 * CyclesPerLine / 342; - constexpr static int EndOfColourBurst = 65 * CyclesPerLine / 342; - constexpr static int StartOfLeftBorder = 73 * CyclesPerLine / 342; }; /// Provides concrete, specific timing for the nominated personality. From e5b0e666cc76dd78ab5d3c34481515e5eb89788b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 23 Apr 2023 21:16:04 -0400 Subject: [PATCH 2/8] Realign fetching. --- Components/9918/Implementation/Fetch.hpp | 3 +-- Components/9918/Implementation/Storage.hpp | 14 ++------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Components/9918/Implementation/Fetch.hpp b/Components/9918/Implementation/Fetch.hpp index d26df8e27..d4644c328 100644 --- a/Components/9918/Implementation/Fetch.hpp +++ b/Components/9918/Implementation/Fetch.hpp @@ -71,8 +71,7 @@ template void Base::dispatch(Seq #define index(n) \ if(use_end && end == n) return; \ [[fallthrough]]; \ - case n: fetcher.template fetch<(n + 171 - 16) % 171>(n); - // `template fetch` call includes an in-place internal -> sync-aligned conversion for now, during transition. + case n: fetcher.template fetch(n); switch(start) { default: assert(false); diff --git a/Components/9918/Implementation/Storage.hpp b/Components/9918/Implementation/Storage.hpp index 4d6d0da4e..85f272c4a 100644 --- a/Components/9918/Implementation/Storage.hpp +++ b/Components/9918/Implementation/Storage.hpp @@ -216,20 +216,10 @@ template struct Storage static constexpr size_t events_size() { size_t size = 0; for(int c = 0; c < 1368; c++) { - const auto event_type = GeneratorT::event(internal_to_grauw(c)); + const auto event_type = GeneratorT::event(c); size += event_type.has_value(); } return size + 1; @@ -240,7 +230,7 @@ template struct Storage result{}; size_t index = 0; for(int c = 0; c < 1368; c++) { - const auto event = GeneratorT::event(internal_to_grauw(c)); + const auto event = GeneratorT::event(c); if(!event) { continue; } From f5c8eba8437ca4f3321014a8125892700173d02a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 23 Apr 2023 22:02:41 -0400 Subject: [PATCH 3/8] Reduce duplication. --- Components/9918/Implementation/9918.cpp | 37 ++++++++----------- .../xcschemes/Clock Signal.xcscheme | 2 +- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 7e7cfdb31..35e1a714b 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -499,6 +499,20 @@ void TMS9918::run_for(const HalfCycles cycles) { #define border(left, right) intersect(left, right, this->output_border(end - start, cram_value)) + const auto left_blank = [&]() { + // Blanking region: output the entire sequence when the cursor + // crosses the start-of-border point. + if( + this->output_pointer_.column < LineLayout::EndOfLeftErase && + end_column >= LineLayout::EndOfLeftErase + ) { + output_sync(LineLayout::EndOfSync); + output_blank(LineLayout::StartOfColourBurst - LineLayout::EndOfSync); + output_default_colour_burst(LineLayout::EndOfColourBurst - LineLayout::StartOfColourBurst); + output_blank(LineLayout::EndOfLeftErase - LineLayout::EndOfColourBurst); + } + }; + if(this->draw_line_buffer_->vertical_state != VerticalState::Pixels) { if( this->output_pointer_.row >= this->mode_timing_.first_vsync_line && @@ -510,17 +524,7 @@ void TMS9918::run_for(const HalfCycles cycles) { output_sync(Timing::CyclesPerLine); } } else { - // Blanking region: output the entire sequence when the cursor - // crosses the start-of-border point. - if( - this->output_pointer_.column < LineLayout::EndOfLeftErase && - end_column >= LineLayout::EndOfLeftErase - ) { - output_sync(LineLayout::EndOfSync); - output_blank(LineLayout::StartOfColourBurst - LineLayout::EndOfSync); - output_default_colour_burst(LineLayout::EndOfColourBurst - LineLayout::StartOfColourBurst); - output_blank(LineLayout::EndOfLeftErase - LineLayout::EndOfColourBurst); - } + left_blank(); // Border colour until beginning of right erase. border(LineLayout::EndOfLeftErase, LineLayout::EndOfRightBorder); @@ -531,16 +535,7 @@ void TMS9918::run_for(const HalfCycles cycles) { } } } else { - // Blanking region. - if( - this->output_pointer_.column < LineLayout::EndOfLeftErase && - end_column >= LineLayout::EndOfLeftErase - ) { - output_sync(LineLayout::EndOfSync); - output_blank(LineLayout::StartOfColourBurst - LineLayout::EndOfSync); - output_default_colour_burst(LineLayout::EndOfColourBurst - LineLayout::StartOfColourBurst); - output_blank(LineLayout::EndOfLeftErase - LineLayout::EndOfColourBurst); - } + left_blank(); // Left border. border(LineLayout::EndOfLeftErase, this->draw_line_buffer_->first_pixel_output_column); 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 474fa352e..3534e9926 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -1,7 +1,7 @@ + version = "1.3"> From 5daec050dd73afe4ccbd261da265ea94391d2366 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 23 Apr 2023 22:18:36 -0400 Subject: [PATCH 4/8] Adopt proper pixel-content placement. --- Components/9918/Implementation/9918.cpp | 14 +++++++------- .../9918/Implementation/ClockConverter.hpp | 16 ++++++---------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 35e1a714b..576e1ae3c 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -369,8 +369,8 @@ void TMS9918::run_for(const HalfCycles cycles) { } // Based on the output mode, pick a line mode. - this->fetch_line_buffer_->first_pixel_output_column = Timing::FirstPixelCycle; - this->fetch_line_buffer_->next_border_column = Timing::CyclesPerLine; + this->fetch_line_buffer_->first_pixel_output_column = LineLayout::EndOfLeftBorder; + this->fetch_line_buffer_->next_border_column = LineLayout::EndOfPixels; this->fetch_line_buffer_->pixel_count = 256; this->fetch_line_buffer_->screen_mode = this->screen_mode_; this->mode_timing_.maximum_visible_sprites = 4; @@ -381,14 +381,14 @@ void TMS9918::run_for(const HalfCycles cycles) { } else { this->fetch_line_buffer_->fetch_mode = FetchMode::Text; } - this->fetch_line_buffer_->first_pixel_output_column = Timing::FirstTextCycle; - this->fetch_line_buffer_->next_border_column = Timing::LastTextCycle; + this->fetch_line_buffer_->first_pixel_output_column = LineLayout::TextModeEndOfLeftBorder; + this->fetch_line_buffer_->next_border_column = LineLayout::TextModeEndOfPixels; this->fetch_line_buffer_->pixel_count = 240; break; case ScreenMode::YamahaText80: this->fetch_line_buffer_->fetch_mode = FetchMode::Yamaha; - this->fetch_line_buffer_->first_pixel_output_column = Timing::FirstTextCycle; - this->fetch_line_buffer_->next_border_column = Timing::LastTextCycle; + this->fetch_line_buffer_->first_pixel_output_column = LineLayout::TextModeEndOfLeftBorder; + this->fetch_line_buffer_->next_border_column = LineLayout::TextModeEndOfPixels; this->fetch_line_buffer_->pixel_count = 480; break; @@ -1200,7 +1200,7 @@ VerticalState Base::vertical_state() const { template bool Base::is_horizontal_blank() const { - return fetch_pointer_.column < StandardTiming::FirstPixelCycle; + return fetch_pointer_.column < LineLayout::EndOfLeftErase || fetch_pointer_.column >= LineLayout::EndOfRightBorder; } template diff --git a/Components/9918/Implementation/ClockConverter.hpp b/Components/9918/Implementation/ClockConverter.hpp index 0eece395d..4fb8830ab 100644 --- a/Components/9918/Implementation/ClockConverter.hpp +++ b/Components/9918/Implementation/ClockConverter.hpp @@ -60,16 +60,6 @@ template struct StandardTiming { /// The number of internal cycles that must elapse between a request to read or write and /// it becoming a candidate for action. constexpr static int VRAMAccessDelay = 6; - - /// The first internal cycle at which pixels will be output in any mode other than text. - /// Pixels implicitly run from here to the end of the line. - constexpr static int FirstPixelCycle = 86 * CyclesPerLine / 342; - - /// The first internal cycle at which pixels will be output text mode. - constexpr static int FirstTextCycle = 94 * CyclesPerLine / 342; - - /// The final internal cycle at which pixels will be output text mode. - constexpr static int LastTextCycle = 334 * CyclesPerLine / 342; }; /// Provides concrete, specific timing for the nominated personality. @@ -183,6 +173,9 @@ template struct LineLayout struct LineLayout> { @@ -193,6 +186,9 @@ template struct LineLayout Date: Sun, 23 Apr 2023 22:21:22 -0400 Subject: [PATCH 5/8] Subsume right erase. --- Components/9918/Implementation/9918.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 576e1ae3c..153efe2cb 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -513,6 +513,12 @@ void TMS9918::run_for(const HalfCycles cycles) { } }; + const auto right_blank = [&]() { + if(this->output_pointer_.column == Timing::CyclesPerLine) { + output_blank(Timing::CyclesPerLine - LineLayout::EndOfRightBorder); + } + }; + if(this->draw_line_buffer_->vertical_state != VerticalState::Pixels) { if( this->output_pointer_.row >= this->mode_timing_.first_vsync_line && @@ -525,14 +531,8 @@ void TMS9918::run_for(const HalfCycles cycles) { } } else { left_blank(); - - // Border colour until beginning of right erase. border(LineLayout::EndOfLeftErase, LineLayout::EndOfRightBorder); - - // Right erase. - if(this->output_pointer_.column == Timing::CyclesPerLine) { - output_blank(Timing::CyclesPerLine - LineLayout::EndOfRightBorder); - } + right_blank(); } } else { left_blank(); @@ -585,10 +585,7 @@ void TMS9918::run_for(const HalfCycles cycles) { // Right border. border(this->draw_line_buffer_->next_border_column, LineLayout::EndOfRightBorder); - // Right erase. - if(this->output_pointer_.column == Timing::CyclesPerLine) { - output_blank(Timing::CyclesPerLine - LineLayout::EndOfRightBorder); - } + right_blank(); } #undef border From dbddcd109cc9cdfbc2fffe621eb18fd1b9b4497e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 23 Apr 2023 22:38:42 -0400 Subject: [PATCH 6/8] Add mention of text mode. --- Components/9918/Implementation/ClockConverter.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Components/9918/Implementation/ClockConverter.hpp b/Components/9918/Implementation/ClockConverter.hpp index 4fb8830ab..9e115d935 100644 --- a/Components/9918/Implementation/ClockConverter.hpp +++ b/Components/9918/Implementation/ClockConverter.hpp @@ -162,8 +162,9 @@ template struct LineLayout; // [EndOfRightBorder, ] blank // // ... with minor caveats: -// * horizontal adjust on the Yamaha VDPs is applied to EndOfLeftBorder and EndOfPixels; and -// * the Sega VDPs may programatically extend the left border. +// * horizontal adjust on the Yamaha VDPs is applied to EndOfLeftBorder and EndOfPixels; +// * the Sega VDPs may programatically extend the left border; and +// * text mode on all VDPs adjusts border width. template struct LineLayout> { constexpr static int EndOfSync = 26; From 1b4df01a28d8385c66ba014ab8f7725690fe1d3e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 24 Apr 2023 22:23:09 -0400 Subject: [PATCH 7/8] Fix missing right blank. --- Components/9918/Implementation/9918.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 153efe2cb..6b74bf471 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -514,7 +514,7 @@ void TMS9918::run_for(const HalfCycles cycles) { }; const auto right_blank = [&]() { - if(this->output_pointer_.column == Timing::CyclesPerLine) { + if(end_column == Timing::CyclesPerLine) { output_blank(Timing::CyclesPerLine - LineLayout::EndOfRightBorder); } }; From e49e98d30936309fc8f550ae102b2c32aedc993c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 24 Apr 2023 22:43:11 -0400 Subject: [PATCH 8/8] Support horizontal offsets. --- Components/9918/Implementation/9918.cpp | 10 +++++++--- Components/9918/Implementation/Storage.hpp | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index 6b74bf471..52a71a86c 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -419,6 +419,11 @@ void TMS9918::run_for(const HalfCycles cycles) { break; } + if constexpr (is_yamaha_vdp(personality)) { + this->fetch_line_buffer_->first_pixel_output_column += Storage::adjustment_[0]; + this->fetch_line_buffer_->next_border_column += Storage::adjustment_[0]; + } + this->fetch_line_buffer_->vertical_state = this->screen_mode_ == ScreenMode::Blank ? VerticalState::Blank : @@ -874,9 +879,8 @@ void Base::commit_register(int reg, uint8_t value) { break; case 18: - if(value) { - LOG("TODO: Yamaha position adjustment; " << PADHEX(2) << +value); - } + Storage::adjustment_[0] = (8 - ((value & 15) ^ 8)) * 4; + Storage::adjustment_[1] = 8 - ((value >> 4) ^ 8); // b0-b3: horizontal adjustment // b4-b7: vertical adjustment break; diff --git a/Components/9918/Implementation/Storage.hpp b/Components/9918/Implementation/Storage.hpp index 85f272c4a..53382e29e 100644 --- a/Components/9918/Implementation/Storage.hpp +++ b/Components/9918/Implementation/Storage.hpp @@ -38,6 +38,8 @@ template struct Storage palette_{}; std::array background_palette_{}; bool solid_background_ = true;