2018-10-10 01:49:21 +00:00
|
|
|
//
|
|
|
|
// 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 {
|
2019-01-25 03:59:03 +00:00
|
|
|
[super setUp];
|
2018-10-10 01:49:21 +00:00
|
|
|
|
|
|
|
// Create a valid OpenGL context, so that a VDP can be constructed.
|
2019-12-22 05:00:23 +00:00
|
|
|
NSOpenGLPixelFormatAttribute attributes[] = {
|
2018-10-10 01:49:21 +00:00
|
|
|
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
|
|
|
|
_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
|
|
|
|
[_openGLContext makeCurrentContext];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)tearDown {
|
2019-01-25 03:59:03 +00:00
|
|
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
|
|
_openGLContext = nil;
|
2018-10-10 01:49:21 +00:00
|
|
|
|
2019-01-25 03:59:03 +00:00
|
|
|
[super tearDown];
|
2018-10-10 01:49:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)testLineInterrupt {
|
|
|
|
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
|
|
|
|
|
|
|
|
// Disable end-of-frame interrupts, enable line interrupts.
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, 0x00);
|
|
|
|
vdp.write(1, 0x81);
|
2018-10-10 01:49:21 +00:00
|
|
|
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, 0x10);
|
|
|
|
vdp.write(1, 0x80);
|
2018-10-10 01:49:21 +00:00
|
|
|
|
|
|
|
// Set a line interrupt to occur in five lines.
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, 5);
|
|
|
|
vdp.write(1, 0x8a);
|
2018-10-10 01:49:21 +00:00
|
|
|
|
|
|
|
// Get time until interrupt.
|
2019-10-30 02:36:29 +00:00
|
|
|
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral() - 1;
|
2018-10-10 01:49:21 +00:00
|
|
|
|
2018-10-20 22:25:55 +00:00
|
|
|
// 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");
|
|
|
|
|
2018-10-10 01:49:21 +00:00
|
|
|
// Check interrupt flag isn't set prior to the reported time.
|
|
|
|
vdp.run_for(HalfCycles(time_until_interrupt));
|
2018-10-10 02:14:35 +00:00
|
|
|
NSAssert(!vdp.get_interrupt_line(), @"Interrupt line went active early [1]");
|
2018-10-10 01:49:21 +00:00
|
|
|
|
|
|
|
// Check interrupt flag is set at the reported time.
|
|
|
|
vdp.run_for(HalfCycles(1));
|
2018-10-10 02:14:35 +00:00
|
|
|
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised [1]");
|
|
|
|
|
|
|
|
// Read the status register to clear interrupt status.
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.read(1);
|
2018-10-10 02:14:35 +00:00
|
|
|
NSAssert(!vdp.get_interrupt_line(), @"Interrupt wasn't reset by status read");
|
|
|
|
|
|
|
|
// Check interrupt flag isn't set prior to the reported time.
|
2019-10-30 02:36:29 +00:00
|
|
|
time_until_interrupt = vdp.get_time_until_interrupt().as_integral() - 1;
|
2018-10-10 02:14:35 +00:00
|
|
|
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]");
|
2018-10-10 01:49:21 +00:00
|
|
|
}
|
|
|
|
|
2018-10-20 22:25:55 +00:00
|
|
|
- (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.
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, 0x00);
|
|
|
|
vdp.write(1, 0x81);
|
2018-10-20 22:25:55 +00:00
|
|
|
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, 0x10);
|
|
|
|
vdp.write(1, 0x80);
|
2018-10-20 22:25:55 +00:00
|
|
|
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, 0);
|
|
|
|
vdp.write(1, 0x8a);
|
2018-10-20 22:25:55 +00:00
|
|
|
|
|
|
|
// 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).
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.read(1);
|
2019-10-30 02:36:29 +00:00
|
|
|
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral() - 1;
|
2018-10-20 22:25:55 +00:00
|
|
|
|
|
|
|
// 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");
|
|
|
|
}
|
|
|
|
|
2018-10-25 01:59:30 +00:00
|
|
|
- (void)testInterruptPrediction {
|
2018-10-21 17:59:14 +00:00
|
|
|
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
|
|
|
|
|
|
|
|
for(int c = 0; c < 256; ++c) {
|
2018-10-21 22:42:49 +00:00
|
|
|
for(int with_eof = (c < 192) ? 0 : 1; with_eof < 2; ++with_eof) {
|
2018-10-21 17:59:14 +00:00
|
|
|
// Enable or disable end-of-frame interrupts as required.
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, with_eof ? 0x20 : 0x00);
|
|
|
|
vdp.write(1, 0x81);
|
2018-10-21 17:59:14 +00:00
|
|
|
|
|
|
|
// Enable line interrupts.
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, 0x10);
|
|
|
|
vdp.write(1, 0x80);
|
2018-10-21 17:59:14 +00:00
|
|
|
|
|
|
|
// Set the line interrupt timing as desired.
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.write(1, c);
|
|
|
|
vdp.write(1, 0x8a);
|
2018-10-21 17:59:14 +00:00
|
|
|
|
|
|
|
// Now run through an entire frame...
|
2018-10-25 01:59:30 +00:00
|
|
|
int half_cycles = 262*228*2;
|
2019-10-30 02:36:29 +00:00
|
|
|
auto last_time_until_interrupt = vdp.get_time_until_interrupt().as_integral();
|
2018-10-21 17:59:14 +00:00
|
|
|
while(half_cycles--) {
|
|
|
|
// Validate that an interrupt happened if one was expected, and clear anything that's present.
|
2018-10-21 22:42:49 +00:00
|
|
|
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);
|
2020-01-05 18:40:02 +00:00
|
|
|
vdp.read(1);
|
2018-10-21 17:59:14 +00:00
|
|
|
|
|
|
|
vdp.run_for(HalfCycles(1));
|
|
|
|
|
|
|
|
// Get the time until interrupt.
|
2019-10-30 02:36:29 +00:00
|
|
|
auto time_until_interrupt = vdp.get_time_until_interrupt().as_integral();
|
2018-10-21 18:35:44 +00:00
|
|
|
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);
|
2018-10-21 17:59:14 +00:00
|
|
|
|
|
|
|
if(last_time_until_interrupt) {
|
2019-12-08 16:56:05 +00:00
|
|
|
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);
|
2018-10-21 17:59:14 +00:00
|
|
|
}
|
|
|
|
last_time_until_interrupt = time_until_interrupt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-25 01:59:30 +00:00
|
|
|
- (void)testTimeUntilLine {
|
|
|
|
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
|
|
|
|
|
2019-10-30 02:36:29 +00:00
|
|
|
auto time_until_line = vdp.get_time_until_line(-1).as_integral();
|
2018-10-25 01:59:30 +00:00
|
|
|
for(int c = 0; c < 262*228*5; ++c) {
|
|
|
|
vdp.run_for(HalfCycles(1));
|
|
|
|
|
2019-10-30 02:36:29 +00:00
|
|
|
const auto time_remaining_until_line = vdp.get_time_until_line(-1).as_integral();
|
2018-10-25 01:59:30 +00:00
|
|
|
--time_until_line;
|
|
|
|
if(time_until_line) {
|
2019-12-08 16:56:05 +00:00
|
|
|
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));
|
2018-10-25 01:59:30 +00:00
|
|
|
}
|
|
|
|
time_until_line = time_remaining_until_line;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-10 01:49:21 +00:00
|
|
|
@end
|