From fd148299921e760482dfaa66ca2ec959f211ca82 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 9 Jan 2023 22:34:56 -0500 Subject: [PATCH] Avoid hand-writing all the various conversions. --- Components/9918/Implementation/9918.cpp | 34 ++--- .../9918/Implementation/ClockConverter.hpp | 134 ++++++------------ Components/9918/Implementation/Fetch.hpp | 6 +- 3 files changed, 60 insertions(+), 114 deletions(-) diff --git a/Components/9918/Implementation/9918.cpp b/Components/9918/Implementation/9918.cpp index af47dffd0..ff05d409e 100644 --- a/Components/9918/Implementation/9918.cpp +++ b/Components/9918/Implementation/9918.cpp @@ -200,21 +200,21 @@ void TMS9918::run_for(const HalfCycles cycles) { // ------------------------ // Perform memory accesses. // ------------------------ -#define fetch(function, converter) \ - const int first_window = this->clock_converter_.converter(this->write_pointer_.column); \ - const int final_window = this->clock_converter_.converter(end_column); \ +#define fetch(function, clock) \ + const int first_window = from_internal(this->write_pointer_.column);\ + const int final_window = from_internal(end_column); \ if(first_window == final_window) break; \ - if(final_window != TMSAccessWindowsPerLine) { \ + if(final_window != clock_rate()) { \ function(first_window, final_window); \ } else { \ function(first_window, final_window); \ } switch(line_buffer.line_mode) { - case LineMode::Text: { fetch(this->template fetch_tms_text, to_tms_access_clock); } break; - case LineMode::Character: { fetch(this->template fetch_tms_character, to_tms_access_clock); } break; - case LineMode::SMS: { fetch(this->template fetch_sms, to_tms_access_clock); } break; - case LineMode::Refresh: { fetch(this->template fetch_tms_refresh, to_tms_access_clock); } break; + case LineMode::Text: { fetch(this->template fetch_tms_text, Clock::TMSMemoryWindow); } break; + case LineMode::Character: { fetch(this->template fetch_tms_character, Clock::TMSMemoryWindow); } break; + case LineMode::SMS: { fetch(this->template fetch_sms, Clock::TMSMemoryWindow); } break; + case LineMode::Refresh: { fetch(this->template fetch_tms_refresh, Clock::TMSMemoryWindow); } break; } #undef fetch @@ -343,7 +343,7 @@ void TMS9918::run_for(const HalfCycles cycles) { // Output video stream. // -------------------- -#define crt_convert(action, time) this->crt_.action(this->clock_converter_.to_crt_clock(time)) +#define crt_convert(action, time) this->crt_.action(from_internal(time)) #define output_sync(x) crt_convert(output_sync, x) #define output_blank(x) crt_convert(output_blank, x) #define output_default_colour_burst(x) crt_convert(output_default_colour_burst, x) @@ -407,9 +407,9 @@ void TMS9918::run_for(const HalfCycles cycles) { // Left border. border(Timing::StartOfLeftBorder, line_buffer.first_pixel_output_column); -#define draw(function, converter) { \ - const int relative_start = this->clock_converter_.converter(start - line_buffer.first_pixel_output_column); \ - const int relative_end = this->clock_converter_.converter(end - line_buffer.first_pixel_output_column); \ +#define draw(function, clock) { \ + const int relative_start = from_internal(start - line_buffer.first_pixel_output_column); \ + const int relative_end = from_internal(end - line_buffer.first_pixel_output_column); \ if(relative_start == relative_end) break; \ this->function; } @@ -427,9 +427,9 @@ void TMS9918::run_for(const HalfCycles cycles) { if(this->pixel_target_) { switch(line_buffer.line_mode) { - case LineMode::SMS: draw(draw_sms(relative_start, relative_end, cram_value), to_tms_pixel_clock); break; - case LineMode::Character: draw(draw_tms_character(relative_start, relative_end), to_tms_pixel_clock); break; - case LineMode::Text: draw(draw_tms_text(relative_start, relative_end), to_tms_pixel_clock); break; + case LineMode::SMS: draw(draw_sms(relative_start, relative_end, cram_value), Clock::TMSPixel); break; + case LineMode::Character: draw(draw_tms_character(relative_start, relative_end), Clock::TMSPixel); break; + case LineMode::Text: draw(draw_tms_text(relative_start, relative_end), Clock::TMSPixel); break; case LineMode::Refresh: break; /* Dealt with elsewhere. */ } @@ -437,7 +437,7 @@ void TMS9918::run_for(const HalfCycles cycles) { if(end == line_buffer.next_border_column) { const int length = line_buffer.next_border_column - line_buffer.first_pixel_output_column; - this->crt_.output_data(this->clock_converter_.to_crt_clock(length), line_buffer.pixel_count); + this->crt_.output_data(from_internal(length), line_buffer.pixel_count); this->pixel_origin_ = this->pixel_target_ = nullptr; this->asked_for_write_area_ = false; } @@ -480,7 +480,7 @@ void TMS9918::run_for(const HalfCycles cycles) { template void Base::output_border(int cycles, [[maybe_unused]] uint32_t cram_dot) { - cycles = this->clock_converter_.to_crt_clock(cycles); + cycles = from_internal(cycles); const uint32_t border_colour = is_sega_vdp(personality) ? master_system_.colour_ram[16 + background_colour_] : diff --git a/Components/9918/Implementation/ClockConverter.hpp b/Components/9918/Implementation/ClockConverter.hpp index ce7f50657..dab21f5a4 100644 --- a/Components/9918/Implementation/ClockConverter.hpp +++ b/Components/9918/Implementation/ClockConverter.hpp @@ -15,14 +15,48 @@ namespace TI { namespace TMS { -// Timing constants. -template struct Timing {}; +enum class Clock { + Internal, + TMSPixel, + TMSMemoryWindow, + CRT +}; + +template constexpr int clock_rate() { + static_assert( + is_classic_vdp(personality) || + is_yamaha_vdp(personality) || + (personality == Personality::MDVDP) + ); + + switch(clk) { + case Clock::TMSPixel: return 342; + case Clock::TMSMemoryWindow: return 171; + case Clock::CRT: return 1368; + case Clock::Internal: + if constexpr (is_classic_vdp(personality)) { + return 342; + } else if constexpr (is_yamaha_vdp(personality)) { + return 1368; + } else if constexpr (personality == Personality::MDVDP) { + return 3420; + } + } +} + +template constexpr int to_internal(int length) { + return length * clock_rate() / clock_rate(); +} + +template constexpr int from_internal(int length) { + return length * clock_rate() / clock_rate(); +} /// Provides default timing measurements that duplicate the layout of a TMS9928's line, /// scaled to the clock rate specified. -template struct StandardTiming { +template struct StandardTiming { /// The total number of internal cycles per line of output. - constexpr static int CyclesPerLine = _CyclesPerLine; + constexpr static int CyclesPerLine = clock_rate(); /// The number of internal cycles that must elapse between a request to read or write and /// it becoming a candidate for action. @@ -58,22 +92,8 @@ template struct StandardTiming { constexpr static int StartOfLeftBorder = 73 * CyclesPerLine / 342; }; -template -struct Timing>: public StandardTiming<342> { -}; - -template -struct Timing>: public StandardTiming<1368> { -}; - -template <> -struct Timing: public StandardTiming<3420> { - // Implementation note: descending from StandardTiming works as long as the numbers computed - // end up being a multiple of 2.5. In practice they're all multiples of 10, so that's guaranteed. - // Coupled logic is as per to_crt_clock below. -}; - -constexpr int TMSAccessWindowsPerLine = 171; +/// Provides concrete, specific timing for the nominated personality. +template struct Timing: public StandardTiming {}; /*! This implementation of the TMS, etc mediates between three clocks: @@ -164,80 +184,6 @@ template class ClockConverter { } } - /*! - Converts a position in internal cycles to its corresponding position - on the TMS memory-access clock, i.e. scales to 171 clocks per line. - */ - static constexpr int to_tms_access_clock(int source) { - switch(personality) { - default: - return source >> 1; - - case Personality::V9938: - case Personality::V9958: - return source >> 3; - - case Personality::MDVDP: - return source / 20; - } - } - - /*! - Converts a position in TMS access cycles back to one at the native - clock rate. - */ - static constexpr int from_tms_access_clock(int source) { - switch(personality) { - default: - return source << 1; - - case Personality::V9938: - case Personality::V9958: - return source << 3; - - case Personality::MDVDP: - return source * 20; - } - } - - /*! - Converts a position in internal cycles to its corresponding position - on the TMS pixel clock, i.e. scales to 342 clocks per line. - */ - static constexpr int to_tms_pixel_clock(int source) { - switch(personality) { - default: - return source; - - case Personality::V9938: - case Personality::V9958: - return source >> 2; - - case Personality::MDVDP: - return source / 10; - } - } - - /*! - Convers a position in internal cycles to its corresponding position - on the CRT's output clock, which [TODO] is clocked so that - 1368 cycles is 228 NTSC colour cycles. - */ - static constexpr int to_crt_clock(int source) { - switch(personality) { - default: - return source << 2; - - case Personality::V9938: - case Personality::V9958: - return source; - - case Personality::MDVDP: - return (source * 2) / 5; - } - - } - private: // Holds current residue in conversion from the external to // internal clock. diff --git a/Components/9918/Implementation/Fetch.hpp b/Components/9918/Implementation/Fetch.hpp index c44ab0822..a2367de93 100644 --- a/Components/9918/Implementation/Fetch.hpp +++ b/Components/9918/Implementation/Fetch.hpp @@ -47,7 +47,7 @@ case n #define external_slot(n) \ - slot(n): do_external_slot(clock_converter_.from_tms_access_clock(n)); + slot(n): do_external_slot(to_internal(n)); #define external_slots_2(n) \ external_slot(n); \ @@ -280,7 +280,7 @@ template void Base::fetch_tms_character(int start, in slot(31): sprite_selection_buffer.reset_sprite_collection(); - do_external_slot(clock_converter_.from_tms_access_clock(31)); + do_external_slot(to_internal(31)); external_slots_2(32); external_slot(34); @@ -431,7 +431,7 @@ template void Base::fetch_sms(int start, int end) { slot(29): sprite_selection_buffer.reset_sprite_collection(); - do_external_slot(clock_converter_.from_tms_access_clock(29)); + do_external_slot(to_internal(29)); external_slot(30); sprite_y_read(31, 0);