mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-17 07:29:04 +00:00
159 lines
5.0 KiB
Plaintext
159 lines
5.0 KiB
Plaintext
//
|
|
// SpectrumVideoContentionTests.cpp
|
|
// Clock SignalTests
|
|
//
|
|
// Created by Thomas Harte on 23/4/2021.
|
|
// Copyright © 2021 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
#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];
|
|
};
|
|
|
|
using Timing = Sinclair::ZXSpectrum::Video::Timing;
|
|
|
|
template <Timing video_timing> ContentionAnalysis analyse() {
|
|
Sinclair::ZXSpectrum::Video::Video<video_timing> 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<Timing::FortyEightK>();
|
|
|
|
// 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<Timing::OneTwoEightK>();
|
|
|
|
// 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<Timing::Plus3>();
|
|
|
|
// 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
|