From 595791cee0e2db4a99dd684d30392653af8496d7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 18 Jun 2016 08:51:18 -0400 Subject: [PATCH 1/7] Made the base 6522 class more abstract: you must now opt-in if you want the IRQ line to be sent to a delegate. --- Components/6522/6522.hpp | 34 ++++++++++++++++++++++------------ Machines/Vic-20/Vic20.hpp | 6 +++--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index 15458d465..816f7ce8d 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -14,11 +14,6 @@ namespace MOS { -class MOS6522Delegate { - public: - virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0; -}; - template class MOS6522 { private: enum InterruptFlag: uint8_t { @@ -181,11 +176,6 @@ template class MOS6522 { return !!interrupt_status; } - void set_delegate(MOS6522Delegate *delegate) - { - _delegate = delegate; - } - MOS6522() : _timer_is_running{false, false}, _last_posted_interrupt_status(false) @@ -197,7 +187,6 @@ template class MOS6522 { void set_port_output(int port, uint8_t value) {} // Delegate and communications - MOS6522Delegate *_delegate; bool _last_posted_interrupt_status; inline void reevaluate_interrupts() { @@ -205,7 +194,7 @@ template class MOS6522 { if(new_interrupt_status != _last_posted_interrupt_status) { _last_posted_interrupt_status = new_interrupt_status; - if(_delegate) _delegate->mos6522_did_change_interrupt_status(this); + static_cast(this)->set_interrupt_status(new_interrupt_status); } } @@ -228,6 +217,27 @@ template class MOS6522 { bool _timer_is_running[2]; }; +class MOS6522IRQDelegate { + public: + class Delegate { + public: + virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0; + }; + + void set_delegate(Delegate *delegate) + { + _delegate = delegate; + } + + void set_interrupt_status(bool new_status) + { + if(_delegate) _delegate->mos6522_did_change_interrupt_status(this); + } + + private: + Delegate *_delegate; +}; + } #endif /* _522_hpp */ diff --git a/Machines/Vic-20/Vic20.hpp b/Machines/Vic-20/Vic20.hpp index 0910b8f40..ea4d26e3b 100644 --- a/Machines/Vic-20/Vic20.hpp +++ b/Machines/Vic-20/Vic20.hpp @@ -44,10 +44,10 @@ enum Key: uint16_t { Key9 = key(0, 0x10), KeyPlus = key(0, 0x20), KeyGBP = key(0, 0x40), KeyDelete = key(0, 0x80), }; -class UserPortVIA: public MOS::MOS6522 { +class UserPortVIA: public MOS::MOS6522, public MOS::MOS6522IRQDelegate { }; -class KeyboardVIA: public MOS::MOS6522 { +class KeyboardVIA: public MOS::MOS6522, public MOS::MOS6522IRQDelegate { public: void set_key_state(Key key, bool isPressed) { if(isPressed) @@ -88,7 +88,7 @@ class KeyboardVIA: public MOS::MOS6522 { uint8_t _activation_mask; }; -class Machine: public CPU6502::Processor, public CRTMachine::Machine, public MOS::MOS6522Delegate { +class Machine: public CPU6502::Processor, public CRTMachine::Machine, public MOS::MOS6522IRQDelegate::Delegate { public: Machine(); ~Machine(); From 06fb2ff1c704752d20263d965a250416366acafc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 18 Jun 2016 09:28:46 -0400 Subject: [PATCH 2/7] Started endeavouring to sketch out the boilerplate for writing a 6522 test harness. Added a default implementation of `synchronise` to the 6522 too, since not everybody is going to want one. --- .../Clock Signal.xcodeproj/project.pbxproj | 16 +++++- .../Mac/Clock SignalTests/6522Tests.swift | 13 +++++ .../Clock SignalTests-Bridging-Header.h | 1 + .../Mac/Clock SignalTests/MOS6522Bridge.h | 20 +++++++ .../Mac/Clock SignalTests/MOS6522Bridge.mm | 55 +++++++++++++++++++ Processors/6502/CPU6502.hpp | 7 +++ 6 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 OSBindings/Mac/Clock SignalTests/6522Tests.swift create mode 100644 OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h create mode 100644 OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 057575c69..1571aec89 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -313,6 +313,8 @@ 4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BBF99121C8FBA6F0075DAFB /* TextureTarget.cpp */; }; 4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; }; 4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; }; + 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; }; + 4BC751B61D157EB3006C31D9 /* MOS6522Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B51D157EB3006C31D9 /* MOS6522Bridge.mm */; }; 4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; }; 4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; }; 4BC9DF451D044FCA00F44158 /* ROMImages in Resources */ = {isa = PBXBuildFile; fileRef = 4BC9DF441D044FCA00F44158 /* ROMImages */; }; @@ -685,6 +687,9 @@ 4BC3B74E1CD194CC00F86E85 /* Shader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OutputShader.cpp; sourceTree = ""; }; 4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = ""; }; + 4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = ""; }; + 4BC751B41D157EB3006C31D9 /* MOS6522Bridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOS6522Bridge.h; sourceTree = ""; }; + 4BC751B51D157EB3006C31D9 /* MOS6522Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MOS6522Bridge.mm; sourceTree = ""; }; 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = ""; }; 4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = ""; }; 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; @@ -1214,15 +1219,18 @@ 4BB73EB51B587A5100552FC2 /* Clock SignalTests */ = { isa = PBXGroup; children = ( - 4BB73EB81B587A5100552FC2 /* Info.plist */, 4BB297DF1B587D8200A49093 /* Clock SignalTests-Bridging-Header.h */, - 4B1414631B588A1100E04248 /* Test Binaries */, + 4BC751B41D157EB3006C31D9 /* MOS6522Bridge.h */, 4BB297E21B587D8300A49093 /* TestMachine.h */, + 4BC751B51D157EB3006C31D9 /* MOS6522Bridge.mm */, 4BB297E31B587D8300A49093 /* TestMachine.mm */, + 4BB73EB81B587A5100552FC2 /* Info.plist */, + 4BC751B11D157E61006C31D9 /* 6522Tests.swift */, 4BB73EB61B587A5100552FC2 /* AllSuiteATests.swift */, 4B1414611B58888700E04248 /* KlausDormannTests.swift */, - 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */, 4B92EAC91B7C112B00246143 /* TimingTests.swift */, + 4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */, + 4B1414631B588A1100E04248 /* Test Binaries */, ); path = "Clock SignalTests"; sourceTree = ""; @@ -1774,10 +1782,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4BC751B61D157EB3006C31D9 /* MOS6522Bridge.mm in Sources */, 4B14145E1B5887AA00E04248 /* CPU6502AllRAM.cpp in Sources */, 4B14145D1B5887A600E04248 /* CPU6502.cpp in Sources */, 4B92EACA1B7C112B00246143 /* TimingTests.swift in Sources */, 4BB73EB71B587A5100552FC2 /* AllSuiteATests.swift in Sources */, + 4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */, 4B1414621B58888700E04248 /* KlausDormannTests.swift in Sources */, 4BB298F01B587D8400A49093 /* TestMachine.mm in Sources */, 4B1414601B58885000E04248 /* WolfgangLorenzTests.swift in Sources */, diff --git a/OSBindings/Mac/Clock SignalTests/6522Tests.swift b/OSBindings/Mac/Clock SignalTests/6522Tests.swift new file mode 100644 index 000000000..833d17621 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/6522Tests.swift @@ -0,0 +1,13 @@ +// +// 6522Tests.swift +// Clock Signal +// +// Created by Thomas Harte on 18/06/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +import XCTest +import Foundation + +class MOS6522Tests: XCTestCase { +} diff --git a/OSBindings/Mac/Clock SignalTests/Clock SignalTests-Bridging-Header.h b/OSBindings/Mac/Clock SignalTests/Clock SignalTests-Bridging-Header.h index a628ea2a9..0bcb4a76b 100644 --- a/OSBindings/Mac/Clock SignalTests/Clock SignalTests-Bridging-Header.h +++ b/OSBindings/Mac/Clock SignalTests/Clock SignalTests-Bridging-Header.h @@ -3,3 +3,4 @@ // #import "TestMachine.h" +#import "MOS6522Bridge.h" diff --git a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h new file mode 100644 index 000000000..d2bed3fe8 --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h @@ -0,0 +1,20 @@ +// +// MOS6522Bridge.h +// Clock Signal +// +// Created by Thomas Harte on 18/06/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#import + +@interface MOS6522Bridge : NSObject + +@property (nonatomic, readonly) BOOL irqLine; + +- (void)setValue:(uint8_t)value forRegister:(NSUInteger)registerNumber; +- (uint8_t)valueForRegister:(NSUInteger)registerNumber; + +- (void)runForHalfCycles:(NSUInteger)numberOfHalfCycles; + +@end diff --git a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm new file mode 100644 index 000000000..0f1a725ed --- /dev/null +++ b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm @@ -0,0 +1,55 @@ +// +// MOS6522Bridge.m +// Clock Signal +// +// Created by Thomas Harte on 18/06/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#import "MOS6522Bridge.h" +#include "6522.hpp" + +@class MOS6522Bridge; + +class VanillaVIA: public MOS::MOS6522 { + public: + MOS6522Bridge *bridge; + bool irq_line; + + void set_interrupt_status(bool new_status) + { + irq_line = new_status; + } +}; + +@implementation MOS6522Bridge +{ + VanillaVIA _via; +} + +- (instancetype)init +{ + self = [super init]; + if(self) + { + _via.bridge = self; + } + return self; +} + +- (void)setValue:(uint8_t)value forRegister:(NSUInteger)registerNumber +{ + _via.set_register((int)registerNumber, value); +} + +- (uint8_t)valueForRegister:(NSUInteger)registerNumber +{ + return _via.get_register((int)registerNumber); +} + +- (void)runForHalfCycles:(NSUInteger)numberOfHalfCycles +{ + _via.run_for_cycles(numberOfHalfCycles); +} + +@end diff --git a/Processors/6502/CPU6502.hpp b/Processors/6502/CPU6502.hpp index e4e884a06..e335df436 100644 --- a/Processors/6502/CPU6502.hpp +++ b/Processors/6502/CPU6502.hpp @@ -1057,6 +1057,13 @@ template class Processor { static_cast(this)->synchronise(); } + /*! + Called to announce the end of a run_for_cycles period, allowing deferred work to take place. + + Users of the 6502 template may override this. + */ + void synchronise() {} + /*! Gets the value of a register. From 394902f4099c6a01a423904f3b815972592651ca Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 18 Jun 2016 13:57:10 -0400 Subject: [PATCH 3/7] Switched to clocking the 6522 by the half-cycle. Very trivial test now passes. --- Components/6522/6522.hpp | 55 ++++++++++++------- Machines/Vic-20/Vic20.cpp | 4 +- .../Mac/Clock SignalTests/6522Tests.swift | 17 ++++++ .../Mac/Clock SignalTests/MOS6522Bridge.mm | 2 +- 4 files changed, 54 insertions(+), 24 deletions(-) diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index 816f7ce8d..b0edbd770 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -144,30 +144,38 @@ template class MOS6522 { { } - void run_for_cycles(unsigned int number_of_cycles) + void run_for_half_cycles(unsigned int number_of_cycles) { - _registers.timer[0] -= number_of_cycles; - _registers.timer[1] -= number_of_cycles; - - if(!_registers.timer[1] && _timer_is_running[1]) + while(number_of_cycles--) { - _timer_is_running[1] = false; - _registers.interrupt_flags |= InterruptFlag::Timer2; - reevaluate_interrupts(); - } + if(_is_phase2) + { + _registers.timer[0] --; + _registers.timer[1] --; - if(!_registers.timer[0] && _timer_is_running[0]) - { - _registers.interrupt_flags |= InterruptFlag::Timer1; - reevaluate_interrupts(); + if(!_registers.timer[1] && _timer_is_running[1]) + { + _timer_is_running[1] = false; + _registers.interrupt_flags |= InterruptFlag::Timer2; + reevaluate_interrupts(); + } - // TODO: reload shouldn't occur for a further 1.5 cycles - if(_registers.auxiliary_control&0x40) - _registers.timer[0] = _registers.timer_latch[0]; - else - _timer_is_running[0] = false; + if(!_registers.timer[0] && _timer_is_running[0]) + { + _registers.interrupt_flags |= InterruptFlag::Timer1; + reevaluate_interrupts(); + + // TODO: reload shouldn't occur for a further 1.5 cycles + if(_registers.auxiliary_control&0x40) + _registers.timer[0] = _registers.timer_latch[0]; + else + _timer_is_running[0] = false; + } + // TODO: lots of other status effects + } + + _is_phase2 ^= true; } - // TODO: lots of other status effects } bool get_interrupt_line() @@ -178,13 +186,18 @@ template class MOS6522 { MOS6522() : _timer_is_running{false, false}, - _last_posted_interrupt_status(false) + _last_posted_interrupt_status(false), + _is_phase2(false) {} private: - // Intended to be overwritten + // Intended to be overridden uint8_t get_port_input(int port) { return 0xff; } void set_port_output(int port, uint8_t value) {} +// void set_interrupt_status(bool status) {} + + // Phase toggle + bool _is_phase2; // Delegate and communications bool _last_posted_interrupt_status; diff --git a/Machines/Vic-20/Vic20.cpp b/Machines/Vic-20/Vic20.cpp index 5a60dcc1d..71888551f 100644 --- a/Machines/Vic-20/Vic20.cpp +++ b/Machines/Vic-20/Vic20.cpp @@ -79,8 +79,8 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin } } - _userPortVIA.run_for_cycles(1); - _keyboardVIA.run_for_cycles(1); + _userPortVIA.run_for_half_cycles(2); + _keyboardVIA.run_for_half_cycles(2); return 1; } diff --git a/OSBindings/Mac/Clock SignalTests/6522Tests.swift b/OSBindings/Mac/Clock SignalTests/6522Tests.swift index 833d17621..567337c4a 100644 --- a/OSBindings/Mac/Clock SignalTests/6522Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/6522Tests.swift @@ -10,4 +10,21 @@ import XCTest import Foundation class MOS6522Tests: XCTestCase { + + private func with6522(action: (MOS6522Bridge) -> ()) { + let bridge = MOS6522Bridge() + action(bridge) + } + + func testTimerCount() { + with6522 { + $0.setValue(10, forRegister: 4) + $0.setValue(0, forRegister: 5) + + $0.runForHalfCycles(10) + + XCTAssert($0.valueForRegister(4) == 5, "Low order byte of timer should be 5; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 5; was \($0.valueForRegister(5))") + } + } } diff --git a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm index 0f1a725ed..329dbb06a 100644 --- a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm +++ b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm @@ -49,7 +49,7 @@ class VanillaVIA: public MOS::MOS6522 { - (void)runForHalfCycles:(NSUInteger)numberOfHalfCycles { - _via.run_for_cycles(numberOfHalfCycles); + _via.run_for_half_cycles(numberOfHalfCycles); } @end From 5d26cd85a3312b427c931dd1126a6fbd40337220 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 18 Jun 2016 14:30:23 -0400 Subject: [PATCH 4/7] Wrote test case for what appears to be correct timer behaviour if those were acting in isolation. Ensured implementation matches test case. --- Components/6522/6522.hpp | 31 +++++++++----- .../Mac/Clock SignalTests/6522Tests.swift | 40 ++++++++++++++++++- .../Mac/Clock SignalTests/MOS6522Bridge.mm | 7 +++- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index b0edbd770..3250b09b3 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -150,28 +150,39 @@ template class MOS6522 { { if(_is_phase2) { - _registers.timer[0] --; - _registers.timer[1] --; + _registers.last_timer[0] = _registers.timer[0]; + _registers.last_timer[1] = _registers.timer[1]; - if(!_registers.timer[1] && _timer_is_running[1]) + if(_registers.timer_needs_reload) + { + _registers.timer_needs_reload = false; + _registers.timer[0] = _registers.timer_latch[0]; + } + else + _registers.timer[0] --; + + _registers.timer[1] --; + } + else + { + // IRQ is raised on the half cycle after overflow + if((_registers.timer[1] == 0xffff) && !_registers.last_timer[1] && _timer_is_running[1]) { _timer_is_running[1] = false; _registers.interrupt_flags |= InterruptFlag::Timer2; reevaluate_interrupts(); } - if(!_registers.timer[0] && _timer_is_running[0]) + if((_registers.timer[0] == 0xffff) && !_registers.last_timer[0] && _timer_is_running[0]) { _registers.interrupt_flags |= InterruptFlag::Timer1; reevaluate_interrupts(); - // TODO: reload shouldn't occur for a further 1.5 cycles if(_registers.auxiliary_control&0x40) - _registers.timer[0] = _registers.timer_latch[0]; + _registers.timer_needs_reload = true; else _timer_is_running[0] = false; } - // TODO: lots of other status effects } _is_phase2 ^= true; @@ -214,16 +225,18 @@ template class MOS6522 { // The registers struct Registers { uint8_t output[2], input[2], data_direction[2]; - uint16_t timer[2], timer_latch[2]; + uint16_t timer[2], timer_latch[2], last_timer[2]; uint8_t shift; uint8_t auxiliary_control, peripheral_control; uint8_t interrupt_flags, interrupt_enable; + bool timer_needs_reload; // "A low reset (RES) input clears all R6522 internal registers to logic 0" Registers() : output{0, 0}, input{0, 0}, data_direction{0, 0}, auxiliary_control(0), peripheral_control(0), - interrupt_flags(0), interrupt_enable(0) {} + interrupt_flags(0), interrupt_enable(0), + last_timer{0, 0}, timer_needs_reload(false) {} } _registers; // Internal state other than the registers diff --git a/OSBindings/Mac/Clock SignalTests/6522Tests.swift b/OSBindings/Mac/Clock SignalTests/6522Tests.swift index 567337c4a..5e4a78054 100644 --- a/OSBindings/Mac/Clock SignalTests/6522Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/6522Tests.swift @@ -18,13 +18,51 @@ class MOS6522Tests: XCTestCase { func testTimerCount() { with6522 { + // set timer 1 to a value of $000a $0.setValue(10, forRegister: 4) $0.setValue(0, forRegister: 5) + // run for 5 cycles $0.runForHalfCycles(10) + // check that the timer has gone down by 5 XCTAssert($0.valueForRegister(4) == 5, "Low order byte of timer should be 5; was \($0.valueForRegister(4))") - XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 5; was \($0.valueForRegister(5))") + XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 0; was \($0.valueForRegister(5))") + } + } + + func testTimerReload() { + with6522 { + // set timer 1 to a value of $0010, enable repeating mode + $0.setValue(16, forRegister: 4) + $0.setValue(0, forRegister: 5) + $0.setValue(0x40, forRegister: 11) + $0.setValue(0x40 | 0x80, forRegister: 14) + + // run for 16 cycles + $0.runForHalfCycles(32) + + // check that the timer has gone down to 0 but not yet triggered an interrupt + XCTAssert($0.valueForRegister(4) == 0, "Low order byte of timer should be 0; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 0; was \($0.valueForRegister(5))") + XCTAssert(!$0.irqLine, "IRQ should not yet be active") + + // check that two half-cycles later the timer is $ffff but IRQ still hasn't triggered + $0.runForHalfCycles(2) + XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte of timer should be 0xff; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0xff, "High order byte of timer should be 0xff; was \($0.valueForRegister(5))") + XCTAssert(!$0.irqLine, "IRQ should not yet be active") + + // check that one half-cycle later the timer is still $ffff and IRQ has triggered + $0.runForHalfCycles(1) + XCTAssert($0.irqLine, "IRQ should be active") + XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte of timer should be 0xff; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0xff, "High order byte of timer should be 0xff; was \($0.valueForRegister(5))") + + // check that one half-cycles later the timer has reloaded + $0.runForHalfCycles(1) + XCTAssert($0.valueForRegister(4) == 0x10, "Low order byte of timer should be 0x10; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0x00, "High order byte of timer should be 0x00; was \($0.valueForRegister(5))") } } } diff --git a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm index 329dbb06a..e47ccc844 100644 --- a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm +++ b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm @@ -49,7 +49,12 @@ class VanillaVIA: public MOS::MOS6522 { - (void)runForHalfCycles:(NSUInteger)numberOfHalfCycles { - _via.run_for_half_cycles(numberOfHalfCycles); + _via.run_for_half_cycles((int)numberOfHalfCycles); +} + +- (BOOL)irqLine +{ + return _via.irq_line; } @end From 2282b59768dc33b03bfe08c454acc420bcb3c5f2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 18 Jun 2016 16:10:46 -0400 Subject: [PATCH 5/7] Added a quick latching test, and shortened test messages, albeit that they're still displeasingly boilerplate. --- .../Mac/Clock SignalTests/6522Tests.swift | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/6522Tests.swift b/OSBindings/Mac/Clock SignalTests/6522Tests.swift index 5e4a78054..66617a990 100644 --- a/OSBindings/Mac/Clock SignalTests/6522Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/6522Tests.swift @@ -26,8 +26,30 @@ class MOS6522Tests: XCTestCase { $0.runForHalfCycles(10) // check that the timer has gone down by 5 - XCTAssert($0.valueForRegister(4) == 5, "Low order byte of timer should be 5; was \($0.valueForRegister(4))") - XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 0; was \($0.valueForRegister(5))") + XCTAssert($0.valueForRegister(4) == 5, "Low order byte should be 5; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0, "High order byte should be 0; was \($0.valueForRegister(5))") + } + } + + func testTimerLatches() { + with6522 { + // set timer 2 to $1020 + $0.setValue(0x10, forRegister: 8) + $0.setValue(0x20, forRegister: 9) + + // change the low-byte latch + $0.setValue(0x40, forRegister: 8) + + // chek that the new latched value hasn't been copied + XCTAssert($0.valueForRegister(8) == 0x10, "Low order byte should be 0x10; was \($0.valueForRegister(8))") + XCTAssert($0.valueForRegister(9) == 0x20, "High order byte should be 0x20; was \($0.valueForRegister(9))") + + // write the low-byte latch + $0.setValue(0x50, forRegister: 9) + + // chek that the latched value has been copied + XCTAssert($0.valueForRegister(8) == 0x40, "Low order byte should be 0x50; was \($0.valueForRegister(8))") + XCTAssert($0.valueForRegister(9) == 0x50, "High order byte should be 0x40; was \($0.valueForRegister(9))") } } @@ -43,26 +65,29 @@ class MOS6522Tests: XCTestCase { $0.runForHalfCycles(32) // check that the timer has gone down to 0 but not yet triggered an interrupt - XCTAssert($0.valueForRegister(4) == 0, "Low order byte of timer should be 0; was \($0.valueForRegister(4))") - XCTAssert($0.valueForRegister(5) == 0, "High order byte of timer should be 0; was \($0.valueForRegister(5))") + XCTAssert($0.valueForRegister(4) == 0, "Low order byte should be 0; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0, "High order byte should be 0; was \($0.valueForRegister(5))") XCTAssert(!$0.irqLine, "IRQ should not yet be active") // check that two half-cycles later the timer is $ffff but IRQ still hasn't triggered $0.runForHalfCycles(2) - XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte of timer should be 0xff; was \($0.valueForRegister(4))") - XCTAssert($0.valueForRegister(5) == 0xff, "High order byte of timer should be 0xff; was \($0.valueForRegister(5))") + XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte should be 0xff; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0xff, "High order byte should be 0xff; was \($0.valueForRegister(5))") XCTAssert(!$0.irqLine, "IRQ should not yet be active") - // check that one half-cycle later the timer is still $ffff and IRQ has triggered + // check that one half-cycle later the timer is still $ffff and IRQ has triggered... $0.runForHalfCycles(1) XCTAssert($0.irqLine, "IRQ should be active") - XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte of timer should be 0xff; was \($0.valueForRegister(4))") - XCTAssert($0.valueForRegister(5) == 0xff, "High order byte of timer should be 0xff; was \($0.valueForRegister(5))") + XCTAssert($0.valueForRegister(4) == 0xff, "Low order byte should be 0xff; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0xff, "High order byte should be 0xff; was \($0.valueForRegister(5))") + + // ... but that reading the timer cleared the interrupt + XCTAssert(!$0.irqLine, "IRQ should be active") // check that one half-cycles later the timer has reloaded $0.runForHalfCycles(1) - XCTAssert($0.valueForRegister(4) == 0x10, "Low order byte of timer should be 0x10; was \($0.valueForRegister(4))") - XCTAssert($0.valueForRegister(5) == 0x00, "High order byte of timer should be 0x00; was \($0.valueForRegister(5))") + XCTAssert($0.valueForRegister(4) == 0x10, "Low order byte should be 0x10; was \($0.valueForRegister(4))") + XCTAssert($0.valueForRegister(5) == 0x00, "High order byte should be 0x00; was \($0.valueForRegister(5))") } } } From eea850cd12aed6a86221c59c33567a47c8418a47 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 18 Jun 2016 16:40:01 -0400 Subject: [PATCH 6/7] Added a deliberately failing data direction test. --- .../Mac/Clock SignalTests/6522Tests.swift | 20 ++++++++++++++ .../Mac/Clock SignalTests/MOS6522Bridge.h | 2 ++ .../Mac/Clock SignalTests/MOS6522Bridge.mm | 27 +++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/OSBindings/Mac/Clock SignalTests/6522Tests.swift b/OSBindings/Mac/Clock SignalTests/6522Tests.swift index 66617a990..41100aacc 100644 --- a/OSBindings/Mac/Clock SignalTests/6522Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/6522Tests.swift @@ -16,6 +16,8 @@ class MOS6522Tests: XCTestCase { action(bridge) } + // MARK: Timer tests + func testTimerCount() { with6522 { // set timer 1 to a value of $000a @@ -90,4 +92,22 @@ class MOS6522Tests: XCTestCase { XCTAssert($0.valueForRegister(5) == 0x00, "High order byte should be 0x00; was \($0.valueForRegister(5))") } } + + + // MARK: Data direction tests + func testDataDirection() { + with6522 { + // set low four bits of register B as output, the top four as input + $0.setValue(0x0f, forRegister: 2) + + // ask to output 0x8c + $0.setValue(0x8c, forRegister: 0) + + // set current input as 0xda + $0.portBInput = 0xda + + // test that the result of reading register B is therefore 0x8a + XCTAssert($0.valueForRegister(0) == 0x8a, "Data direction register should mix input and output; got \($0.valueForRegister(0))") + } + } } diff --git a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h index d2bed3fe8..b5110ae86 100644 --- a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h +++ b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.h @@ -11,6 +11,8 @@ @interface MOS6522Bridge : NSObject @property (nonatomic, readonly) BOOL irqLine; +@property (nonatomic) uint8_t portBInput; +@property (nonatomic) uint8_t portAInput; - (void)setValue:(uint8_t)value forRegister:(NSUInteger)registerNumber; - (uint8_t)valueForRegister:(NSUInteger)registerNumber; diff --git a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm index e47ccc844..8dd444a05 100644 --- a/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm +++ b/OSBindings/Mac/Clock SignalTests/MOS6522Bridge.mm @@ -15,11 +15,18 @@ class VanillaVIA: public MOS::MOS6522 { public: MOS6522Bridge *bridge; bool irq_line; + uint8_t port_a_value; + uint8_t port_b_value; void set_interrupt_status(bool new_status) { irq_line = new_status; } + + uint8_t get_port_input(int port) + { + return port ? port_b_value : port_a_value; + } }; @implementation MOS6522Bridge @@ -57,4 +64,24 @@ class VanillaVIA: public MOS::MOS6522 { return _via.irq_line; } +- (void)setPortAInput:(uint8_t)portAInput +{ + _via.port_a_value = portAInput; +} + +- (uint8_t)portAInput +{ + return _via.port_a_value; +} + +- (void)setPortBInput:(uint8_t)portBInput +{ + _via.port_b_value = portBInput; +} + +- (uint8_t)portBInput +{ + return _via.port_b_value; +} + @end From f4915c5ad6d8bac2e21a1d7d2d52f68531725126 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 18 Jun 2016 17:17:03 -0400 Subject: [PATCH 7/7] Fixed test and added basic implementation of data direction. --- Components/6522/6522.hpp | 32 +++++++++++-------- Machines/Vic-20/Vic20.hpp | 4 +-- .../Mac/Clock SignalTests/6522Tests.swift | 2 +- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Components/6522/6522.hpp b/Components/6522/6522.hpp index 3250b09b3..4cd616fde 100644 --- a/Components/6522/6522.hpp +++ b/Components/6522/6522.hpp @@ -35,17 +35,17 @@ template class MOS6522 { { case 0x0: _registers.output[1] = value; - static_cast(this)->set_port_output(1, value); // TODO: handshake - break; - case 0x1: - _registers.output[0] = value; - static_cast(this)->set_port_output(0, value); // TODO: handshake + static_cast(this)->set_port_output(1, value, _registers.data_direction[1]); // TODO: handshake break; case 0xf: - // No handshake, so write directly + case 0x1: _registers.output[0] = value; - static_cast(this)->set_port_output(0, value); + static_cast(this)->set_port_output(0, value, _registers.data_direction[0]); // TODO: handshake break; +// // No handshake, so write directly +// _registers.output[0] = value; +// static_cast(this)->set_port_output(0, value); +// break; case 0x2: _registers.data_direction[1] = value; @@ -104,10 +104,9 @@ template class MOS6522 { // printf("6522 %p: %d\n", this, address); switch(address) { -// case 0x0: return (_registers.auxiliary_control & 0x40) ? _registers.input[1] : static_cast(this)->get_port_input(1); - case 0x0: return _registers.output[1];//static_cast(this)->get_port_input(1); + case 0x0: return get_port_input(1, _registers.data_direction[1], _registers.output[1]); case 0xf: // TODO: handshake, latching - case 0x1: return static_cast(this)->get_port_input(0); + case 0x1: return get_port_input(0, _registers.data_direction[0], _registers.output[0]); case 0x2: return _registers.data_direction[1]; case 0x3: return _registers.data_direction[0]; @@ -202,11 +201,18 @@ template class MOS6522 { {} private: - // Intended to be overridden - uint8_t get_port_input(int port) { return 0xff; } - void set_port_output(int port, uint8_t value) {} + // Expected to be overridden + uint8_t get_port_input(int port) { return 0xff; } + void set_port_output(int port, uint8_t value, uint8_t direction_mask) {} // void set_interrupt_status(bool status) {} + // Input/output multiplexer + uint8_t get_port_input(int port, uint8_t output_mask, uint8_t output) + { + uint8_t input = static_cast(this)->get_port_input(port); + return (input & ~output_mask) | (output & output_mask); + } + // Phase toggle bool _is_phase2; diff --git a/Machines/Vic-20/Vic20.hpp b/Machines/Vic-20/Vic20.hpp index ea4d26e3b..4fb639a55 100644 --- a/Machines/Vic-20/Vic20.hpp +++ b/Machines/Vic-20/Vic20.hpp @@ -75,9 +75,9 @@ class KeyboardVIA: public MOS::MOS6522, public MOS::MOS6522IRQDeleg return 0xff; } - void set_port_output(int port, uint8_t value) { + void set_port_output(int port, uint8_t value, uint8_t mask) { if(port) - _activation_mask = value; + _activation_mask = (value & mask) | (~mask); } KeyboardVIA() { diff --git a/OSBindings/Mac/Clock SignalTests/6522Tests.swift b/OSBindings/Mac/Clock SignalTests/6522Tests.swift index 41100aacc..cb5a7ded8 100644 --- a/OSBindings/Mac/Clock SignalTests/6522Tests.swift +++ b/OSBindings/Mac/Clock SignalTests/6522Tests.swift @@ -98,7 +98,7 @@ class MOS6522Tests: XCTestCase { func testDataDirection() { with6522 { // set low four bits of register B as output, the top four as input - $0.setValue(0x0f, forRegister: 2) + $0.setValue(0xf0, forRegister: 2) // ask to output 0x8c $0.setValue(0x8c, forRegister: 0)