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

182 lines
6.1 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>
#import <OpenGL/OpenGL.h>
#include "9918.hpp"
@interface MasterSystemVDPTests : XCTestCase
@end
@implementation MasterSystemVDPTests {
NSOpenGLContext *_openGLContext;
}
- (void)setUp {
[super setUp];
// Create a valid OpenGL context, so that a VDP can be constructed.
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
0
};
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
[_openGLContext makeCurrentContext];
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
_openGLContext = nil;
[super tearDown];
}
- (void)testLineInterrupt {
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
// Disable end-of-frame interrupts, enable line interrupts.
vdp.set_register(1, 0x00);
vdp.set_register(1, 0x81);
vdp.set_register(1, 0x10);
vdp.set_register(1, 0x80);
// Set a line interrupt to occur in five lines.
vdp.set_register(1, 5);
vdp.set_register(1, 0x8a);
// Get time until interrupt.
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral() - 1;
// Check that an interrupt is now scheduled.
NSAssert(time_until_interrupt != -2, @"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.get_register(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.get_time_until_interrupt().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 {
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
// Disable end-of-frame interrupts, enable line interrupts, set an interrupt to occur every line.
vdp.set_register(1, 0x00);
vdp.set_register(1, 0x81);
vdp.set_register(1, 0x10);
vdp.set_register(1, 0x80);
vdp.set_register(1, 0);
vdp.set_register(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.get_register(1);
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral() - 1;
// Check that an interrupt is now scheduled.
NSAssert(time_until_interrupt != -2, @"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 {
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
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.set_register(1, with_eof ? 0x20 : 0x00);
vdp.set_register(1, 0x81);
// Enable line interrupts.
vdp.set_register(1, 0x10);
vdp.set_register(1, 0x80);
// Set the line interrupt timing as desired.
vdp.set_register(1, c);
vdp.set_register(1, 0x8a);
// Now run through an entire frame...
int half_cycles = 262*228*2;
auto last_time_until_interrupt = vdp.get_time_until_interrupt().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 == 0), @"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);
vdp.get_register(1);
vdp.run_for(HalfCycles(1));
// Get the time until interrupt.
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral();
NSAssert(time_until_interrupt != -1, @"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) {
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 {
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
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