diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index 2e417fa5e..7343aebf9 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -560,10 +560,14 @@ uint8_t TMS9918::get_register(int address) { } // Figure out the number of internal cycles until the next line interrupt. - const int local_cycles_until_line_interrupt = ((mode_timing_.line_interrupt_position - column_ + 342) % 342) + (next_line_interrupt_row - row_) * 342; + int local_cycles_until_line_interrupt = ((mode_timing_.line_interrupt_position - column_ + 342) % 342) + (next_line_interrupt_row - row_) * 342; + local_cycles_until_line_interrupt <<= 2; + local_cycles_until_line_interrupt -= cycles_error_; - // Map that to input cycles by multiplying by 2/3 (and incrementing on any carry). - auto time_until_line_interrupt = HalfCycles( (local_cycles_until_line_interrupt * 6 + 7) / 4); + // Map that to input cycles by multiplying by 2/3 (and incrementing on any carry, TODO: allowing for current error). + auto time_until_line_interrupt = HalfCycles(local_cycles_until_line_interrupt / 3); + + if(!generate_interrupts_) return time_until_line_interrupt; // Return whichever interrupt is closer. return std::min(time_until_frame_interrupt, time_until_line_interrupt); diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 6f56f10e9..95080054f 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -315,6 +315,7 @@ 4B9BE401203A0C0600FFAE60 /* MultiSpeaker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B9BE3FE203A0C0600FFAE60 /* MultiSpeaker.cpp */; }; 4BA0F68E1EEA0E8400E9489E /* ZX8081.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BA0F68C1EEA0E8400E9489E /* ZX8081.cpp */; }; 4BA61EB01D91515900B3C876 /* NSData+StdVector.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */; }; + 4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */; }; 4BAD13441FF709C700FD114A /* MSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0E61051FF34737002A9DBD /* MSX.cpp */; }; 4BAE49582032881E004BE78E /* CSZX8081.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B14978E1EE4B4D200CE2596 /* CSZX8081.mm */; }; 4BAE495920328897004BE78E /* ZX8081OptionsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B95FA9C1F11893B0008E395 /* ZX8081OptionsPanel.swift */; }; @@ -1041,6 +1042,7 @@ 4BA141C12073100800A31EC9 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = ""; }; 4BA61EAE1D91515900B3C876 /* NSData+StdVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+StdVector.h"; sourceTree = ""; }; 4BA61EAF1D91515900B3C876 /* NSData+StdVector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSData+StdVector.mm"; sourceTree = ""; }; + 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MasterSystemVDPTests.mm; sourceTree = ""; }; 4BA9C3CF1D8164A9002DDB61 /* MediaTarget.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MediaTarget.hpp; sourceTree = ""; }; 4BAA167B21582B1D008A3276 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = ""; }; 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Disk.hpp; sourceTree = ""; }; @@ -2818,10 +2820,11 @@ 4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = { isa = PBXGroup; children = ( - 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */, 4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */, 4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */, 4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */, + 4BA91E1C216D85BA00F79557 /* MasterSystemVDPTests.mm */, + 4B98A0601FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm */, 4B121F9A1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm */, 4BD4A8CF1E077FD20020D856 /* PCMTrackTests.mm */, 4B2AF8681E513FC20027EE29 /* TIATests.mm */, @@ -4000,6 +4003,7 @@ 4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */, 4B01A6881F22F0DB001FD6E3 /* Z80MemptrTests.swift in Sources */, 4B121F9B1E06293F00BFDA12 /* PCMSegmentEventSourceTests.mm in Sources */, + 4BA91E1D216D85BA00F79557 /* MasterSystemVDPTests.mm in Sources */, 4B98A0611FFADCDE00ADF63B /* MSXStaticAnalyserTests.mm in Sources */, 4BEF6AAC1D35D1C400E73575 /* DPLLTests.swift in Sources */, 4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm b/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm new file mode 100644 index 000000000..0047ac20b --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm @@ -0,0 +1,69 @@ +// +// MasterSystemVDPTests.m +// Clock SignalTests +// +// Created by Thomas Harte on 09/10/2018. +// Copyright © 2018 Thomas Harte. All rights reserved. +// + +#import +#import + +#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. + int time_until_interrupt = vdp.get_time_until_interrupt().as_int() - 1; + + // 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"); +} + +@end