From 9baa8617427ecabfbf52647922cf3b7983d0d9d7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 21 Apr 2021 19:18:07 -0400 Subject: [PATCH 1/2] Simplifies timing calculation expression. --- Machines/Sinclair/ZXSpectrum/Video.hpp | 42 ++++++--------------- Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp | 4 +- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/Machines/Sinclair/ZXSpectrum/Video.hpp b/Machines/Sinclair/ZXSpectrum/Video.hpp index 246445f4d..148307bec 100644 --- a/Machines/Sinclair/ZXSpectrum/Video.hpp +++ b/Machines/Sinclair/ZXSpectrum/Video.hpp @@ -63,9 +63,9 @@ template class Video { // Number of cycles after first pixel fetch at which interrupt is first signalled. int interrupt_time; - // Contention to apply, in half-cycles, as a function of number of half cycles since + // Contention to apply, in whole cycles, as a function of number of whole cycles since // contention began. - int delays[16]; + int delays[8]; }; static constexpr Timings get_timings() { @@ -98,17 +98,10 @@ template class Video { .contention_duration = 129 * 2, // i.e. interrupt is first signalled 14368 cycles before the first video fetch. - .interrupt_time = (1 + 228*311 - 14365 - 5) * 2, + .interrupt_time = (228*311 - 14365 - 5) * 2, .delays = { // Should start at 14365 - 2, 1, - 0, 0, - 14, 13, - 12, 11, - 10, 9, - 8, 7, - 6, 5, - 4, 3, + 1, 0, 7, 6, 5, 4, 3, 2, } }; @@ -123,17 +116,10 @@ template class Video { .contention_leadin = 4 * 2, .contention_duration = 128 * 2, - .interrupt_time = (1 + 228*311 - 14361 - 4) * 2, + .interrupt_time = (228*311 - 14361 - 4) * 2, .delays = { // Should start at 14361. - 12, 11, - 10, 9, - 8, 7, - 6, 5, - 4, 3, - 2, 1, - 0, 0, - 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0 } }; @@ -148,17 +134,10 @@ template class Video { .contention_leadin = 4 * 2, .contention_duration = 128 * 2, - .interrupt_time = (1 + 224*312 - 14335 - 4) * 2, + .interrupt_time = (224*312 - 14335 - 4) * 2, .delays = { // Should start at 14335. - 12, 11, - 10, 9, - 8, 7, - 6, 5, - 4, 3, - 2, 1, - 0, 0, - 0, 0, + 6, 5, 4, 3, 2, 1, 0, 0 } }; @@ -377,9 +356,10 @@ template class Video { @returns How many cycles the [ULA/gate array] would delay the CPU for if it were to recognise that contention needs to be applied in @c offset half-cycles from now. */ - int access_delay(HalfCycles offset) const { + HalfCycles access_delay(HalfCycles offset) const { constexpr auto timings = get_timings(); const int delay_time = (time_into_frame_ + offset.as() + timings.contention_leadin) % (timings.cycles_per_line * timings.lines_per_frame); + assert(!(delay_time&1)); // Check for a time within the no-contention window. if(delay_time >= (191*timings.cycles_per_line + timings.contention_duration)) { @@ -391,7 +371,7 @@ template class Video { return 0; } - return timings.delays[time_into_line & 15]; + return timings.delays[(time_into_line >> 1) & 7] * 2; } /*! diff --git a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp index c7ce231ca..06812cdce 100644 --- a/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp +++ b/Machines/Sinclair/ZXSpectrum/ZXSpectrum.cpp @@ -218,7 +218,7 @@ template class ConcreteMachine: cycle.operation >= PartialMachineCycle::ReadOpcodeStart && cycle.operation <= PartialMachineCycle::WriteStart) { - const HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush() + HalfCycles(1)); + const auto delay = video_.last_valid()->access_delay(video_.time_since_flush()); advance(cycle.length + delay); return delay; } @@ -269,7 +269,7 @@ template class ConcreteMachine: // These all start by loading the address bus, then set MREQ // half a cycle later. if(is_contended_[address >> 14]) { - const HalfCycles delay = video_.last_valid()->access_delay(video_.time_since_flush()); + const auto delay = video_.last_valid()->access_delay(video_.time_since_flush()); advance(cycle.length + delay); return delay; From d0c789ff9a5eeedbed4208802ecb5d094389b7c0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 21 Apr 2021 19:37:36 -0400 Subject: [PATCH 2/2] Locks declarative form of contention closer to regular expressions. --- Machines/Sinclair/ZXSpectrum/Video.hpp | 83 +++++--------------------- 1 file changed, 16 insertions(+), 67 deletions(-) diff --git a/Machines/Sinclair/ZXSpectrum/Video.hpp b/Machines/Sinclair/ZXSpectrum/Video.hpp index 148307bec..a4132dae9 100644 --- a/Machines/Sinclair/ZXSpectrum/Video.hpp +++ b/Machines/Sinclair/ZXSpectrum/Video.hpp @@ -66,82 +66,31 @@ template class Video { // Contention to apply, in whole cycles, as a function of number of whole cycles since // contention began. int delays[8]; + + constexpr Timings(int cycles_per_line, int lines_per_frame, int contention_leadin, int contention_duration, int interrupt_offset, const int *delays) noexcept : + cycles_per_line(cycles_per_line * 2), + lines_per_frame(lines_per_frame), + contention_leadin(contention_leadin * 2), + contention_duration(contention_duration * 2), + interrupt_time((cycles_per_line * lines_per_frame - interrupt_offset - contention_leadin) * 2), + delays{ delays[0] * 2, delays[1] * 2, delays[2] * 2, delays[3] * 2, delays[4] * 2, delays[5] * 2, delays[6] * 2, delays[7] * 2} + {} }; static constexpr Timings get_timings() { if constexpr (timing == VideoTiming::Plus3) { - // Amstrad gate array timings, classic statement: - // - // Contention begins 14361 cycles "after interrupt" and follows the pattern [1, 0, 7, 6 5 4, 3, 2]. - // The first four bytes of video are fetched at 14365–14368 cycles, in the order [pixels, attribute, pixels, attribute]. - // - // For my purposes: - // - // Video fetching always begins at 0. Since there are 311*228 = 70908 cycles per frame, and the interrupt - // should "occur" (I assume: begin) 14365 before that, it should actually begin at 70908 - 14365 = 56543. - // - // Contention begins four cycles before the first video fetch, so it begins at 70904. I don't currently - // know whether the four cycles is true across all models, so it's given here as convention_leadin. - // - // ... except that empirically that all seems to be two cycles off. So maybe I misunderstand what the - // contention patterns are supposed to indicate relative to MREQ? It's frustrating that all documentation - // I can find is vaguely in terms of contention patterns, and what they mean isn't well-defined in terms - // of regular Z80 signalling. - constexpr Timings result = { - .cycles_per_line = 228 * 2, - .lines_per_frame = 311, - - // i.e. video fetching begins five cycles after the start of the - // contended memory pattern below; that should put a clear two - // cycles between a Z80 access and the first video fetch. - .contention_leadin = 5 * 2, - .contention_duration = 129 * 2, - - // i.e. interrupt is first signalled 14368 cycles before the first video fetch. - .interrupt_time = (228*311 - 14365 - 5) * 2, - - .delays = { // Should start at 14365 - 1, 0, 7, 6, 5, 4, 3, 2, - } - }; - - return result; + constexpr int delays[] = {1, 0, 7, 6, 5, 4, 3, 2}; + return Timings(228, 311, 6, 129, 14365, delays); } if constexpr (timing == VideoTiming::OneTwoEightK) { - constexpr Timings result = { - .cycles_per_line = 228 * 2, - .lines_per_frame = 311, - - .contention_leadin = 4 * 2, - .contention_duration = 128 * 2, - - .interrupt_time = (228*311 - 14361 - 4) * 2, - - .delays = { // Should start at 14361. - 6, 5, 4, 3, 2, 1, 0, 0 - } - }; - - return result; + constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0}; + return Timings(228, 311, 4, 128, 14361, delays); } if constexpr (timing == VideoTiming::FortyEightK) { - constexpr Timings result = { - .cycles_per_line = 224 * 2, - .lines_per_frame = 312, - - .contention_leadin = 4 * 2, - .contention_duration = 128 * 2, - - .interrupt_time = (224*312 - 14335 - 4) * 2, - - .delays = { // Should start at 14335. - 6, 5, 4, 3, 2, 1, 0, 0 - } - }; - - return result; + constexpr int delays[] = {6, 5, 4, 3, 2, 1, 0, 0}; + return Timings(224, 312, 4, 128, 14335, delays); } } @@ -371,7 +320,7 @@ template class Video { return 0; } - return timings.delays[(time_into_line >> 1) & 7] * 2; + return HalfCycles(timings.delays[(time_into_line >> 1) & 7]); } /*!