From 37dcf611301d869bfb0e32e02328b6a5b79db1a6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 23 Apr 2021 22:29:57 -0400 Subject: [PATCH] Add timing tests, fix +3 discrepancy. --- Machines/Sinclair/ZXSpectrum/Video.hpp | 6 +- .../Clock Signal.xcodeproj/project.pbxproj | 44 ++--- .../SpectrumVideoContentionTests.mm | 156 ++++++++++++++++++ 3 files changed, 183 insertions(+), 23 deletions(-) create mode 100644 OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm diff --git a/Machines/Sinclair/ZXSpectrum/Video.hpp b/Machines/Sinclair/ZXSpectrum/Video.hpp index 2274680fd..c48392a61 100644 --- a/Machines/Sinclair/ZXSpectrum/Video.hpp +++ b/Machines/Sinclair/ZXSpectrum/Video.hpp @@ -80,7 +80,7 @@ template class Video { static constexpr Timings get_timings() { if constexpr (timing == VideoTiming::Plus3) { constexpr int delays[] = {1, 0, 7, 6, 5, 4, 3, 2}; - return Timings(228, 311, 6, 129, 14365, delays); + return Timings(228, 311, 6, 129, 14361, delays); } if constexpr (timing == VideoTiming::OneTwoEightK) { @@ -94,8 +94,8 @@ template class Video { } } - // TODO: how long is the interrupt line held for? - static constexpr int interrupt_duration = 48; + // Interrupt should be held for 32 cycles. + static constexpr int interrupt_duration = 64; public: void run_for(HalfCycles duration) { diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 01eddfd55..24c509446 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -478,6 +478,7 @@ 4B89453D201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894516201967B4007DE474 /* StaticAnalyser.cpp */; }; 4B89453E201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894517201967B4007DE474 /* StaticAnalyser.cpp */; }; 4B89453F201967B4007DE474 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B894517201967B4007DE474 /* StaticAnalyser.cpp */; }; + 4B8DD3682633B2D400B3C866 /* SpectrumVideoContentionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */; }; 4B8DF4D825465B7500F3433C /* IIgsMemoryMapTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */; }; 4B8DF4F9254E36AE00F3433C /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4F7254E36AD00F3433C /* Video.cpp */; }; 4B8DF4FA254E36AE00F3433C /* Video.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B8DF4F7254E36AD00F3433C /* Video.cpp */; }; @@ -1427,6 +1428,7 @@ 4B894540201967D6007DE474 /* Machines.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Machines.hpp; sourceTree = ""; }; 4B8A7E85212F988200F2BBC6 /* DeferredQueue.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DeferredQueue.hpp; sourceTree = ""; }; 4B8D287E1F77207100645199 /* TrackSerialiser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TrackSerialiser.hpp; sourceTree = ""; }; + 4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SpectrumVideoContentionTests.mm; sourceTree = ""; }; 4B8DF4D62546561300F3433C /* MemoryMap.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MemoryMap.hpp; sourceTree = ""; }; 4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = IIgsMemoryMapTests.mm; sourceTree = ""; }; 4B8DF4ED254B840B00F3433C /* AppleClock.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AppleClock.hpp; sourceTree = ""; }; @@ -3939,8 +3941,12 @@ 4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = { isa = PBXGroup; children = ( - 4B85322922778E4200F26553 /* Comparative68000.hpp */, - 4B90467222C6FA31000E2074 /* TestRunner68000.hpp */, + 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */, + 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */, + 4BC751B11D157E61006C31D9 /* 6522Tests.swift */, + 4B1E85801D176468001EF87D /* 6532Tests.swift */, + 4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */, + 4B8DF5132550D62900F3433C /* 65816kromTests.swift */, 4B90467522C6FD6E000E2074 /* 68000ArithmeticTests.mm */, 4B9D0C4A22C7D70900DE1AD3 /* 68000BCDTests.mm */, 4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */, @@ -3949,46 +3955,43 @@ 4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */, 4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */, 4BD388872239E198002D14B5 /* 68000Tests.mm */, + 4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */, 4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */, 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */, + 4B049CDC1DA3C82F00322067 /* BCDTest.swift */, + 4B3BA0C41D318B44005DD7A7 /* Bridges */, + 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */, + 4B85322922778E4200F26553 /* Comparative68000.hpp */, 4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */, + 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */, 4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */, + 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */, 4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */, + 4BB73EB81B587A5100552FC2 /* Info.plist */, + 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */, + 4B1414611B58888700E04248 /* KlausDormannTests.swift */, 4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */, 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */, 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */, 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */, 4BC0CB272446BC7B00A79DBB /* OPLTests.mm */, + 4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */, 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */, 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */, 4B3F76B825A1635300178AEC /* PowerPCDecoderTests.mm */, 4BE76CF822641ED300ACD6FA /* QLTests.mm */, + 4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */, + 4B1414631B588A1100E04248 /* Test Binaries */, + 4B90467222C6FA31000E2074 /* TestRunner68000.hpp */, 4B2AF8681E513FC20027EE29 /* TIATests.mm */, 4B1D08051E0F7A1100763741 /* TimeTests.mm */, + 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */, 4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */, 4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */, - 4BB73EB81B587A5100552FC2 /* Info.plist */, - 4BC9E1ED1D23449A003FCEE4 /* 6502InterruptTests.swift */, - 4B92EAC91B7C112B00246143 /* 6502TimingTests.swift */, - 4BC751B11D157E61006C31D9 /* 6522Tests.swift */, - 4B1E85801D176468001EF87D /* 6532Tests.swift */, - 4B4F478925367EDC004245B8 /* 65816AddressingTests.swift */, - 4B8DF5132550D62900F3433C /* 65816kromTests.swift */, - 4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */, - 4B049CDC1DA3C82F00322067 /* BCDTest.swift */, - 4B3BA0C21D318AEB005DD7A7 /* C1540Tests.swift */, - 4BEF6AAB1D35D1C400E73575 /* DPLLTests.swift */, - 4BBF49AE1ED2880200AB3669 /* FUSETests.swift */, - 4B4F477B253530B7004245B8 /* Jeek816Tests.swift */, - 4B1414611B58888700E04248 /* KlausDormannTests.swift */, - 4BD91D762401C2B8007BDC91 /* PatrikRakTests.swift */, - 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */, 4B08A2741EE35D56008B7065 /* Z80InterruptTests.swift */, 4BDDBA981EF3451200347E61 /* Z80MachineCycleTests.swift */, 4B01A6871F22F0DB001FD6E3 /* Z80MemptrTests.swift */, 4BFCA12A1ECBE7C400AC40C1 /* ZexallTests.swift */, - 4B3BA0C41D318B44005DD7A7 /* Bridges */, - 4B1414631B588A1100E04248 /* Test Binaries */, ); path = "Clock SignalTests"; sourceTree = ""; @@ -5643,6 +5646,7 @@ 4B4DEC07252BFA56004583AC /* 65816Base.cpp in Sources */, 4B778F2323A5EDE40000D260 /* Tape.cpp in Sources */, 4B778F4F23A5F21C0000D260 /* StaticAnalyser.cpp in Sources */, + 4B8DD3682633B2D400B3C866 /* SpectrumVideoContentionTests.mm in Sources */, 4B778EEF23A5D6680000D260 /* AsyncTaskQueue.cpp in Sources */, 4B778F1223A5EC720000D260 /* CRT.cpp in Sources */, 4B778EF423A5DB3A0000D260 /* C1540.cpp in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm b/OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm new file mode 100644 index 000000000..79c454a6c --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/SpectrumVideoContentionTests.mm @@ -0,0 +1,156 @@ +// +// SpectrumVideoContentionTests.cpp +// Clock SignalTests +// +// Created by Thomas Harte on 23/4/2021. +// Copyright © 2021 Thomas Harte. All rights reserved. +// + +#import + +#include "../../../Machines/Sinclair/ZXSpectrum/Video.hpp" + +@interface SpectrumVideoContentionTests : XCTestCase +@end + +@implementation SpectrumVideoContentionTests + +struct ContentionAnalysis { + int time_after_interrupt = 0; + int contended_lines = 0; + int contention_length = 0; + int line_length = 0; + int total_lines = 0; + HalfCycles pattern[8]; +}; + +template ContentionAnalysis analyse() { + Sinclair::ZXSpectrum::Video video; + ContentionAnalysis analysis; + + // Advance to the start of the first interrupt. + while(video.get_interrupt_line()) { + video.run_for(HalfCycles(1)); + } + while(!video.get_interrupt_line()) { + video.run_for(HalfCycles(1)); + } + + // Count half cycles until first non-zero contended time. + while(video.access_delay(HalfCycles(0)) == HalfCycles(0)) { + video.run_for(HalfCycles(2)); + analysis.time_after_interrupt += 2; + } + + // Grab the contention pattern. + for(int c = 0; c < 8; c++) { + analysis.pattern[c] = video.access_delay(HalfCycles(0)); + video.run_for(HalfCycles(2)); + } + + // Figure out how long contention goes on for. + int c = 0; + analysis.contention_length = 16; // For the 16 just skipped. + do { + analysis.contention_length += 2; + video.run_for(HalfCycles(2)); + c++; + } while(analysis.pattern[c&7] == video.access_delay(HalfCycles(0))); + + // Look for next start of contention to determine line length. + analysis.line_length = analysis.contention_length; + while(video.access_delay(HalfCycles(0)) == HalfCycles(0)) { + video.run_for(HalfCycles(2)); + analysis.line_length += 2; + } + + // Count contended lines. + analysis.contended_lines = 1; + while(video.access_delay(HalfCycles(0)) == analysis.pattern[0]) { + video.run_for(HalfCycles(analysis.line_length)); + ++analysis.contended_lines; + } + + // Count total lines. + analysis.total_lines = analysis.contended_lines; + while(video.access_delay(HalfCycles(0)) != analysis.pattern[0]) { + video.run_for(HalfCycles(analysis.line_length)); + ++analysis.total_lines; + } + + return analysis; +} + +- (void)test48k { + const auto analysis = analyse(); + + // Check time from interrupt. + XCTAssertEqual(analysis.time_after_interrupt, 14335*2); + + // Check contention pattern. + XCTAssertEqual(analysis.pattern[0], HalfCycles(6 * 2)); + XCTAssertEqual(analysis.pattern[1], HalfCycles(5 * 2)); + XCTAssertEqual(analysis.pattern[2], HalfCycles(4 * 2)); + XCTAssertEqual(analysis.pattern[3], HalfCycles(3 * 2)); + XCTAssertEqual(analysis.pattern[4], HalfCycles(2 * 2)); + XCTAssertEqual(analysis.pattern[5], HalfCycles(1 * 2)); + XCTAssertEqual(analysis.pattern[6], HalfCycles(0 * 2)); + XCTAssertEqual(analysis.pattern[7], HalfCycles(0 * 2)); + + // Check line length and count. + XCTAssertEqual(analysis.contention_length, 128*2); + XCTAssertEqual(analysis.line_length, 224*2); + XCTAssertEqual(analysis.contended_lines, 192); + XCTAssertEqual(analysis.total_lines, 312); +} + +- (void)test128k { + const auto analysis = analyse(); + + // Check time from interrupt. + XCTAssertEqual(analysis.time_after_interrupt, 14361*2); + + // Check contention pattern. + XCTAssertEqual(analysis.pattern[0], HalfCycles(6 * 2)); + XCTAssertEqual(analysis.pattern[1], HalfCycles(5 * 2)); + XCTAssertEqual(analysis.pattern[2], HalfCycles(4 * 2)); + XCTAssertEqual(analysis.pattern[3], HalfCycles(3 * 2)); + XCTAssertEqual(analysis.pattern[4], HalfCycles(2 * 2)); + XCTAssertEqual(analysis.pattern[5], HalfCycles(1 * 2)); + XCTAssertEqual(analysis.pattern[6], HalfCycles(0 * 2)); + XCTAssertEqual(analysis.pattern[7], HalfCycles(0 * 2)); + + // Check line length and count. + XCTAssertEqual(analysis.contention_length, 128*2); + XCTAssertEqual(analysis.line_length, 228*2); + XCTAssertEqual(analysis.contended_lines, 192); + XCTAssertEqual(analysis.total_lines, 311); +} + +- (void)testPlus3 { + const auto analysis = analyse(); + + // Check time from interrupt. + XCTAssertEqual(analysis.time_after_interrupt, 14361*2); + + // Check contention pattern. + XCTAssertEqual(analysis.pattern[0], HalfCycles(1 * 2)); + XCTAssertEqual(analysis.pattern[1], HalfCycles(0 * 2)); + XCTAssertEqual(analysis.pattern[2], HalfCycles(7 * 2)); + XCTAssertEqual(analysis.pattern[3], HalfCycles(6 * 2)); + XCTAssertEqual(analysis.pattern[4], HalfCycles(5 * 2)); + XCTAssertEqual(analysis.pattern[5], HalfCycles(4 * 2)); + XCTAssertEqual(analysis.pattern[6], HalfCycles(3 * 2)); + XCTAssertEqual(analysis.pattern[7], HalfCycles(2 * 2)); + + // Check line length and count. + XCTAssertEqual(analysis.contention_length, 130*2); // By the manner used for detection above, + // the first obviously missing contention spot + // will be after 130 cycles, not the textbook 129, + // because cycle 130 is a delay of 0 either way. + XCTAssertEqual(analysis.line_length, 228*2); + XCTAssertEqual(analysis.contended_lines, 192); + XCTAssertEqual(analysis.total_lines, 311); +} + +@end