1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00
CLK/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm

168 lines
5.4 KiB
Plaintext

//
// MasterSystemVDPTests.m
// Clock SignalTests
//
// Created by Thomas Harte on 09/10/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include "9918.hpp"
@interface MasterSystemVDPTests : XCTestCase
@end
@implementation MasterSystemVDPTests
- (void)setUp {
[super setUp];
}
using VDP = TI::TMS::TMS9918<TI::TMS::Personality::SMSVDP>;
- (void)testLineInterrupt {
VDP vdp;
// Disable end-of-frame interrupts, enable line interrupts.
vdp.write(1, 0x00);
vdp.write(1, 0x81);
vdp.write(1, 0x10);
vdp.write(1, 0x80);
// Set a line interrupt to occur in five lines.
vdp.write(1, 5);
vdp.write(1, 0x8a);
// Get time until interrupt.
auto time_until_interrupt = vdp.next_sequence_point().as_integral() - 1;
// Check that an interrupt is now scheduled.
NSAssert(time_until_interrupt != HalfCycles::max().as_integral() - 1, @"No interrupt scheduled");
NSAssert(time_until_interrupt > 0, @"Interrupt is scheduled in the past");
// Check interrupt flag isn't set prior to the reported time.
vdp.run_for(HalfCycles(time_until_interrupt));
NSAssert(!vdp.get_interrupt_line(), @"Interrupt line went active early [1]");
// Check interrupt flag is set at the reported time.
vdp.run_for(HalfCycles(1));
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised [1]");
// Read the status register to clear interrupt status.
vdp.read(1);
NSAssert(!vdp.get_interrupt_line(), @"Interrupt wasn't reset by status read");
// Check interrupt flag isn't set prior to the reported time.
time_until_interrupt = vdp.next_sequence_point().as_integral() - 1;
vdp.run_for(HalfCycles(time_until_interrupt));
NSAssert(!vdp.get_interrupt_line(), @"Interrupt line went active early [2]");
// Check interrupt flag is set at the reported time.
vdp.run_for(HalfCycles(1));
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised [2]");
}
- (void)testFirstLineInterrupt {
VDP vdp;
// Disable end-of-frame interrupts, enable line interrupts, set an interrupt to occur every line.
vdp.write(1, 0x00);
vdp.write(1, 0x81);
vdp.write(1, 0x10);
vdp.write(1, 0x80);
vdp.write(1, 0);
vdp.write(1, 0x8a);
// Advance to outside of the counted area.
while(vdp.get_current_line() < 200) vdp.run_for(Cycles(228));
// Clear the pending interrupt and ask about the next one (i.e. the first one).
vdp.read(1);
auto time_until_interrupt = vdp.next_sequence_point().as_integral() - 1;
// Check that an interrupt is now scheduled.
NSAssert(time_until_interrupt != HalfCycles::max().as_integral() - 1, @"No interrupt scheduled");
NSAssert(time_until_interrupt > 0, @"Interrupt is scheduled in the past");
// Check interrupt flag isn't set prior to the reported time.
vdp.run_for(HalfCycles(time_until_interrupt));
NSAssert(!vdp.get_interrupt_line(), @"Interrupt line went active early");
// Check interrupt flag is set at the reported time.
vdp.run_for(HalfCycles(1));
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised");
}
- (void)testInterruptPrediction {
VDP vdp;
for(int c = 0; c < 256; ++c) {
for(int with_eof = (c < 192) ? 0 : 1; with_eof < 2; ++with_eof) {
// Enable or disable end-of-frame interrupts as required.
vdp.write(1, with_eof ? 0x20 : 0x00);
vdp.write(1, 0x81);
// Enable line interrupts.
vdp.write(1, 0x10);
vdp.write(1, 0x80);
// Set the line interrupt timing as desired.
vdp.write(1, c);
vdp.write(1, 0x8a);
// Now run through an entire frame...
int half_cycles = 262*228*2;
auto last_time_until_interrupt = vdp.next_sequence_point().as_integral();
while(half_cycles--) {
// Validate that an interrupt happened if one was expected, and clear anything that's present.
NSAssert(vdp.get_interrupt_line() == (last_time_until_interrupt == HalfCycles::max().as_integral()), @"Unexpected interrupt state change; expected %d but got %d; position %d %d @ %d", (last_time_until_interrupt == 0), vdp.get_interrupt_line(), c, with_eof, half_cycles);
if(vdp.get_interrupt_line()) {
vdp.read(1);
last_time_until_interrupt = 0;
}
vdp.run_for(HalfCycles(1));
// Get the time until interrupt.
auto time_until_interrupt = vdp.next_sequence_point().as_integral();
NSAssert(time_until_interrupt != HalfCycles::max().as_integral() || vdp.get_interrupt_line(), @"No interrupt scheduled; position %d %d @ %d", c, with_eof, half_cycles);
NSAssert(time_until_interrupt >= 0, @"Interrupt is scheduled in the past; position %d %d @ %d", c, with_eof, half_cycles);
if(last_time_until_interrupt > 1) {
NSAssert(
time_until_interrupt == (last_time_until_interrupt - 1),
@"Discontinuity found in interrupt prediction; from %@ to %@; position %d %d @ %d",
@(last_time_until_interrupt), @(time_until_interrupt), c, with_eof, half_cycles);
}
last_time_until_interrupt = time_until_interrupt;
}
}
}
}
- (void)testTimeUntilLine {
VDP vdp;
auto time_until_line = vdp.get_time_until_line(-1).as_integral();
for(int c = 0; c < 262*228*5; ++c) {
vdp.run_for(HalfCycles(1));
const auto time_remaining_until_line = vdp.get_time_until_line(-1).as_integral();
--time_until_line;
if(time_until_line) {
NSAssert(
time_remaining_until_line == time_until_line,
@"Discontinuity found in distance-to-line prediction; expected %@ but got %@",
@(time_until_line), @(time_remaining_until_line));
}
time_until_line = time_remaining_until_line;
}
}
@end