From 7e319374b634f99868d970fbdae1ec970cc4ea66 Mon Sep 17 00:00:00 2001 From: Thomas Harte <thomas.harte@gmail.com> Date: Thu, 11 May 2023 23:49:12 -0400 Subject: [PATCH] Consolidate StandardTiming into LineLayout. --- Components/9918/9918.hpp | 2 + Components/9918/Implementation/9918.cpp | 44 +++++++++---------- .../9918/Implementation/ClockConverter.hpp | 30 ++++++------- 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Components/9918/9918.hpp b/Components/9918/9918.hpp index d49a33fad..0789533a3 100644 --- a/Components/9918/9918.hpp +++ b/Components/9918/9918.hpp @@ -18,6 +18,8 @@ namespace TI::TMS { enum Personality { TMS9918A, // includes the 9928 and 9929; set TV standard and output device as desired. + + // Yamaha extensions. V9938, V9958, diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index bb59af32b..25595f79a 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -35,7 +35,7 @@ Base<personality>::Base() : // "For a line interrupt, /INT is pulled low 608 mclks into the appropriate scanline relative to pixel 0. // This is 3 mclks before the rising edge of /HSYNC which starts the next scanline." - mode_timing_.line_interrupt_position = (LineLayout<personality>::EndOfLeftBorder + 304) % Timing<personality>::CyclesPerLine; + mode_timing_.line_interrupt_position = (LineLayout<personality>::EndOfLeftBorder + 304) % LineLayout<personality>::CyclesPerLine; // For a frame interrupt, /INT is pulled low 607 mclks into scanline 192 (of scanlines 0 through 261) relative to pixel 0. // This is 4 mclks before the rising edge of /HSYNC which starts the next scanline. @@ -197,7 +197,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) { if(fetch_cycles_pool) { // Determine how much writing to do; at the absolute most go to the end of this line. const int fetch_cycles = std::min( - Timing<personality>::CyclesPerLine - this->fetch_pointer_.column, + LineLayout<personality>::CyclesPerLine - this->fetch_pointer_.column, fetch_cycles_pool ); const int end_column = this->fetch_pointer_.column + fetch_cycles; @@ -319,7 +319,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) { fetch_cycles_pool -= fetch_cycles; // Check for end of line. - if(this->fetch_pointer_.column == Timing<personality>::CyclesPerLine) { + if(this->fetch_pointer_.column == LineLayout<personality>::CyclesPerLine) { this->fetch_pointer_.column = 0; this->fetch_pointer_.row = (this->fetch_pointer_.row + 1) % this->mode_timing_.total_lines; @@ -341,13 +341,13 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) { this->minimum_access_column_ = std::max( 0, - this->minimum_access_column_ - Timing<personality>::CyclesPerLine + this->minimum_access_column_ - LineLayout<personality>::CyclesPerLine ); if constexpr (is_yamaha_vdp(personality)) { Storage<personality>::minimum_command_column_ = std::max( 0, - Storage<personality>::minimum_command_column_ - Timing<personality>::CyclesPerLine + Storage<personality>::minimum_command_column_ - LineLayout<personality>::CyclesPerLine ); } @@ -457,7 +457,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) { if(output_cycles_pool) { // Determine how much time has passed in the remainder of this line, and proceed. const int target_output_cycles = std::min( - Timing<personality>::CyclesPerLine - this->output_pointer_.column, + LineLayout<personality>::CyclesPerLine - this->output_pointer_.column, output_cycles_pool ); int output_cycles_performed = 0; @@ -522,8 +522,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) { }; const auto right_blank = [&]() { - if(end_column == Timing<personality>::CyclesPerLine) { - output_blank(Timing<personality>::CyclesPerLine - LineLayout<personality>::EndOfRightBorder); + if(end_column == LineLayout<personality>::CyclesPerLine) { + output_blank(LineLayout<personality>::CyclesPerLine - LineLayout<personality>::EndOfRightBorder); } }; @@ -534,8 +534,8 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) { ) { // Vertical sync. // TODO: the Yamaha and Mega Drive both support interlaced video. - if(end_column == Timing<personality>::CyclesPerLine) { - output_sync(Timing<personality>::CyclesPerLine); + if(end_column == LineLayout<personality>::CyclesPerLine) { + output_sync(LineLayout<personality>::CyclesPerLine); } } else { left_blank(); @@ -610,14 +610,14 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) { // Advance time. // ------------- this->output_pointer_.column = end_column; - if(end_column == Timing<personality>::CyclesPerLine) { + if(end_column == LineLayout<personality>::CyclesPerLine) { // Advance line buffer. this->advance(this->draw_line_buffer_); } } output_cycles_pool -= target_output_cycles; - if(this->output_pointer_.column == Timing<personality>::CyclesPerLine) { + if(this->output_pointer_.column == LineLayout<personality>::CyclesPerLine) { this->output_pointer_.column = 0; this->output_pointer_.row = (this->output_pointer_.row + 1) % this->mode_timing_.total_lines; } @@ -685,7 +685,7 @@ void Base<personality>::write_vram(uint8_t value) { // Enqueue the write to occur at the next available slot. read_ahead_buffer_ = value; queued_access_ = MemoryAccess::Write; - minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay; + minimum_access_column_ = fetch_pointer_.column + LineLayout<personality>::VRAMAccessDelay; } template <Personality personality> @@ -1034,7 +1034,7 @@ void Base<personality>::write_register(uint8_t value) { // A read request is enqueued upon setting the address; conversely a write // won't be enqueued unless and until some actual data is supplied. queued_access_ = MemoryAccess::Read; - minimum_access_column_ = fetch_pointer_.column + Timing<personality>::VRAMAccessDelay; + minimum_access_column_ = fetch_pointer_.column + LineLayout<personality>::VRAMAccessDelay; } if constexpr (is_sega_vdp(personality)) { @@ -1242,11 +1242,11 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const { if(get_interrupt_line()) return HalfCycles::max(); // Calculate the amount of time until the next end-of-frame interrupt. - const int frame_length = Timing<personality>::CyclesPerLine * this->mode_timing_.total_lines; + const int frame_length = LineLayout<personality>::CyclesPerLine * this->mode_timing_.total_lines; int time_until_frame_interrupt = ( - ((this->mode_timing_.end_of_frame_interrupt_position.row * Timing<personality>::CyclesPerLine) + this->mode_timing_.end_of_frame_interrupt_position.column + frame_length) - - ((this->fetch_pointer_.row * Timing<personality>::CyclesPerLine) + this->fetch_pointer_.column) + ((this->mode_timing_.end_of_frame_interrupt_position.row * LineLayout<personality>::CyclesPerLine) + this->mode_timing_.end_of_frame_interrupt_position.column + frame_length) - + ((this->fetch_pointer_.row * LineLayout<personality>::CyclesPerLine) + this->fetch_pointer_.column) ) % frame_length; if(!time_until_frame_interrupt) time_until_frame_interrupt = frame_length; @@ -1260,7 +1260,7 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const { int cycles_to_next_interrupt_threshold = this->mode_timing_.line_interrupt_position - this->fetch_pointer_.column; int line_of_next_interrupt_threshold = this->fetch_pointer_.row; if(cycles_to_next_interrupt_threshold <= 0) { - cycles_to_next_interrupt_threshold += Timing<personality>::CyclesPerLine; + cycles_to_next_interrupt_threshold += LineLayout<personality>::CyclesPerLine; ++line_of_next_interrupt_threshold; } @@ -1291,7 +1291,7 @@ HalfCycles TMS9918<personality>::get_next_sequence_point() const { // Figure out the number of internal cycles until the next line interrupt, which is the amount // of time to the next tick over and then next_line_interrupt_row - row_ lines further. const int lines_until_interrupt = (next_line_interrupt_row - line_of_next_interrupt_threshold + this->mode_timing_.total_lines) % this->mode_timing_.total_lines; - const int local_cycles_until_line_interrupt = cycles_to_next_interrupt_threshold + lines_until_interrupt * Timing<personality>::CyclesPerLine; + const int local_cycles_until_line_interrupt = cycles_to_next_interrupt_threshold + lines_until_interrupt * LineLayout<personality>::CyclesPerLine; if(!this->generate_interrupts_) return this->clock_converter_.half_cycles_before_internal_cycles(local_cycles_until_line_interrupt); // Return whichever interrupt is closer. @@ -1305,7 +1305,7 @@ HalfCycles TMS9918<personality>::get_time_until_line(int line) { int cycles_to_next_interrupt_threshold = this->mode_timing_.line_interrupt_position - this->fetch_pointer_.column; int line_of_next_interrupt_threshold = this->fetch_pointer_.row; if(cycles_to_next_interrupt_threshold <= 0) { - cycles_to_next_interrupt_threshold += Timing<personality>::CyclesPerLine; + cycles_to_next_interrupt_threshold += LineLayout<personality>::CyclesPerLine; ++line_of_next_interrupt_threshold; } @@ -1313,7 +1313,7 @@ HalfCycles TMS9918<personality>::get_time_until_line(int line) { line += this->mode_timing_.total_lines; } - return this->clock_converter_.half_cycles_before_internal_cycles(cycles_to_next_interrupt_threshold + (line - line_of_next_interrupt_threshold)*Timing<personality>::CyclesPerLine); + return this->clock_converter_.half_cycles_before_internal_cycles(cycles_to_next_interrupt_threshold + (line - line_of_next_interrupt_threshold)*LineLayout<personality>::CyclesPerLine); } template <Personality personality> @@ -1329,7 +1329,7 @@ template <Personality personality>uint8_t TMS9918<personality>::get_latched_hori // which counts the 256 pixels as items 0–255, starts // counting at -48, and returns only the top 8 bits of the number. int public_counter = this->latched_column_ - LineLayout<personality>::EndOfLeftBorder; - if(public_counter < -46) public_counter += Timing<personality>::CyclesPerLine; + if(public_counter < -46) public_counter += LineLayout<personality>::CyclesPerLine; return uint8_t(public_counter >> 1); } diff --git a/Components/9918/Implementation/ClockConverter.hpp b/Components/9918/Implementation/ClockConverter.hpp index 9e115d935..9c61bbb96 100644 --- a/Components/9918/Implementation/ClockConverter.hpp +++ b/Components/9918/Implementation/ClockConverter.hpp @@ -51,20 +51,6 @@ template <Personality personality, Clock clock> constexpr int from_internal(int return length * clock_rate<personality, clock>() / clock_rate<personality, Clock::Internal>(); } -/// Provides default timing measurements that duplicate the layout of a TMS9928's line, -/// scaled to the clock rate specified. -template <Personality personality> struct StandardTiming { - /// The total number of internal cycles per line of output. - constexpr static int CyclesPerLine = clock_rate<personality, Clock::Internal>(); - - /// 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; -}; - -/// Provides concrete, specific timing for the nominated personality. -template <Personality personality> struct Timing: public StandardTiming<personality> {}; - /*! Provides a [potentially-]stateful conversion between the external and internal clocks. Unlike the other clock conversions, this one may be non-integral, requiring that @@ -174,9 +160,15 @@ template <Personality personality> struct LineLayout<personality, std::enable_if constexpr static int EndOfLeftBorder = 63; constexpr static int EndOfPixels = 319; constexpr static int EndOfRightBorder = 334; - + + constexpr static int CyclesPerLine = 342; + constexpr static int TextModeEndOfLeftBorder = 69; constexpr static int TextModeEndOfPixels = 309; + + /// 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; }; template <Personality personality> struct LineLayout<personality, std::enable_if_t<is_yamaha_vdp(personality)>> { @@ -187,9 +179,15 @@ template <Personality personality> struct LineLayout<personality, std::enable_if constexpr static int EndOfLeftBorder = 258; constexpr static int EndOfPixels = 1282; constexpr static int EndOfRightBorder = 1341; - + + constexpr static int CyclesPerLine = 1368; + constexpr static int TextModeEndOfLeftBorder = 294; constexpr static int TextModeEndOfPixels = 1254; + + /// 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 = 16; }; }