mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 01:32:55 +00:00
With a unit test in aid, corrects some lingering TimedInterruptSource
issues.
This commit is contained in:
parent
614953a222
commit
283092cfbc
@ -215,7 +215,7 @@ void TimedInterruptSource::write(uint16_t address, uint8_t value) {
|
||||
|
||||
const InterruptRate rate = InterruptRate((value >> 5) & 3);
|
||||
if(rate != rate_) {
|
||||
rate_ = InterruptRate((value >> 5) & 3);
|
||||
rate_ = rate;
|
||||
|
||||
if(rate_ >= InterruptRate::ToneGenerator0) {
|
||||
programmable_level_ = channels_[int(rate_) - int(InterruptRate::ToneGenerator0)].level;
|
||||
@ -234,12 +234,27 @@ void TimedInterruptSource::update_channel(int c, bool is_linked, int decrement)
|
||||
if(decrement <= channels_[c].value) {
|
||||
channels_[c].value -= decrement;
|
||||
} else {
|
||||
const int num_flips = (decrement - channels_[c].value) / (channels_[c].reload + 1);
|
||||
// The decrement is greater than the current value, therefore
|
||||
// there'll be at least one flip.
|
||||
//
|
||||
// After decreasing the decrement by the current value + 1,
|
||||
// it'll be clear how many decrements are left after reload.
|
||||
//
|
||||
// Dividing that by the number of decrements necessary for a
|
||||
// flip will provide the total number of flips.
|
||||
const int decrements_after_flip = decrement - (channels_[c].value + 1);
|
||||
const int num_flips = 1 + decrements_after_flip / (channels_[c].reload + 1);
|
||||
|
||||
// If this is a linked channel, set the interrupt mask if a transition
|
||||
// from high to low is amongst the included flips.
|
||||
if(is_linked && num_flips + channels_[c].level >= 2) {
|
||||
interrupts_ |= uint8_t(Interrupt::VariableFrequency);
|
||||
}
|
||||
channels_[c].level ^= (num_flips & 1);
|
||||
channels_[c].value = (decrement - channels_[c].value) % (channels_[c].reload + 1);
|
||||
|
||||
// Apply the modulo number of decrements to the reload value to
|
||||
// figure out where things stand now.
|
||||
channels_[c].value = channels_[c].reload - decrements_after_flip % (channels_[c].reload + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -275,12 +290,12 @@ Cycles TimedInterruptSource::get_next_sequence_point() const {
|
||||
switch(rate_) {
|
||||
case InterruptRate::OnekHz:
|
||||
case InterruptRate::FiftyHz:
|
||||
result = std::min(result, programmable_offset_);
|
||||
result = std::min(result, programmable_offset_ + (!programmable_level_)*programmble_reload(rate_));
|
||||
break;
|
||||
case InterruptRate::ToneGenerator0:
|
||||
case InterruptRate::ToneGenerator1: {
|
||||
const auto& channel = channels_[int(rate_) - int(InterruptRate::ToneGenerator0)];
|
||||
const int cycles_until_interrupt = (channel.value + 1) + (channel.level ? 0 : channel.reload + 1);
|
||||
const auto &channel = channels_[int(rate_) - int(InterruptRate::ToneGenerator0)];
|
||||
const int cycles_until_interrupt = channel.value + 1 + (channel.level ? 0 : channel.reload + 1);
|
||||
result = std::min(result, cycles_until_interrupt);
|
||||
} break;
|
||||
}
|
||||
|
@ -242,6 +242,7 @@
|
||||
4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */; };
|
||||
4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518991F75FD1B00926311 /* SSD.cpp */; };
|
||||
4B47770B268FBE4D005C2340 /* FAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B477709268FBE4D005C2340 /* FAT.cpp */; };
|
||||
4B47770D26900685005C2340 /* EnterpriseDaveTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */; };
|
||||
4B47F6C5241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
||||
4B47F6C6241C87A100ED06F7 /* Struct.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B47F6C4241C87A100ED06F7 /* Struct.cpp */; };
|
||||
4B49F0A923346F7A0045E6A6 /* MacintoshOptions.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4B49F0A723346F7A0045E6A6 /* MacintoshOptions.xib */; };
|
||||
@ -1279,6 +1280,7 @@
|
||||
4B4518A81F76022000926311 /* DiskImageImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImageImplementation.hpp; sourceTree = "<group>"; };
|
||||
4B477709268FBE4D005C2340 /* FAT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FAT.cpp; path = Parsers/FAT.cpp; sourceTree = "<group>"; };
|
||||
4B47770A268FBE4D005C2340 /* FAT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = FAT.hpp; path = Parsers/FAT.hpp; sourceTree = "<group>"; };
|
||||
4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EnterpriseDaveTests.mm; sourceTree = "<group>"; };
|
||||
4B47F6C4241C87A100ED06F7 /* Struct.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Struct.cpp; sourceTree = "<group>"; };
|
||||
4B49F0A823346F7A0045E6A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = "Clock Signal/Base.lproj/MacintoshOptions.xib"; sourceTree = SOURCE_ROOT; };
|
||||
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AY38910.cpp; sourceTree = "<group>"; };
|
||||
@ -4066,6 +4068,7 @@
|
||||
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */,
|
||||
4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */,
|
||||
4BFF1D3C2235C3C100838EA1 /* EmuTOSTests.mm */,
|
||||
4B47770C26900685005C2340 /* EnterpriseDaveTests.mm */,
|
||||
4B051CB2267D3FF800CA44E8 /* EnterpriseNickTests.mm */,
|
||||
4B8DF4D725465B7500F3433C /* IIgsMemoryMapTests.mm */,
|
||||
4BEE1EBF22B5E236000A26A6 /* MacGCRTests.mm */,
|
||||
@ -5774,6 +5777,7 @@
|
||||
4BB2A9AF1E13367E001A5C23 /* CRCTests.mm in Sources */,
|
||||
4B778F5623A5F2AF0000D260 /* CPM.cpp in Sources */,
|
||||
4B778F1C23A5ED3F0000D260 /* TimedEventLoop.cpp in Sources */,
|
||||
4B47770D26900685005C2340 /* EnterpriseDaveTests.mm in Sources */,
|
||||
4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */,
|
||||
4B778F3823A5F11C0000D260 /* SegmentParser.cpp in Sources */,
|
||||
4B778F0723A5EC150000D260 /* CommodoreTAP.cpp in Sources */,
|
||||
|
89
OSBindings/Mac/Clock SignalTests/EnterpriseDaveTests.mm
Normal file
89
OSBindings/Mac/Clock SignalTests/EnterpriseDaveTests.mm
Normal file
@ -0,0 +1,89 @@
|
||||
//
|
||||
// EnterpriseDaveTests.m
|
||||
// Clock SignalTests
|
||||
//
|
||||
// Created by Thomas Harte on 02/07/2021.
|
||||
// Copyright © 2021 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include "../../../Machines/Enterprise/Dave.hpp"
|
||||
#include <memory>
|
||||
|
||||
@interface EnterpriseDaveTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation EnterpriseDaveTests {
|
||||
std::unique_ptr<Enterprise::Dave::TimedInterruptSource> _interruptSource;
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
_interruptSource = std::make_unique<Enterprise::Dave::TimedInterruptSource>();
|
||||
}
|
||||
|
||||
- (void)testExpectedToggles:(int)expectedToggles expectedInterrupts:(int)expectedInterrupts {
|
||||
// Check that the programmable timer flag toggles at a rate
|
||||
// of 2kHz, causing 1000 interrupts, and that sequence points
|
||||
// are properly predicted.
|
||||
int toggles = 0;
|
||||
int interrupts = 0;
|
||||
uint8_t dividerState = _interruptSource->get_divider_state();
|
||||
int nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>();
|
||||
for(int c = 0; c < 250000; c++) {
|
||||
// Advance one cycle. Clock is 250,000 Hz.
|
||||
_interruptSource->run_for(Cycles(1));
|
||||
|
||||
const uint8_t newDividerState = _interruptSource->get_divider_state();
|
||||
if((dividerState^newDividerState)&0x1) {
|
||||
++toggles;
|
||||
}
|
||||
dividerState = newDividerState;
|
||||
|
||||
--nextSequencePoint;
|
||||
|
||||
// Check for the relevant interrupt.
|
||||
const uint8_t newInterrupts = _interruptSource->get_new_interrupts();
|
||||
if(newInterrupts & 0x02) {
|
||||
++interrupts;
|
||||
XCTAssertEqual(nextSequencePoint, 0);
|
||||
nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>();
|
||||
}
|
||||
|
||||
// Failing that, confirm that the other interrupt happend.
|
||||
if(!nextSequencePoint) {
|
||||
XCTAssertTrue(newInterrupts & 0x08);
|
||||
nextSequencePoint = _interruptSource->get_next_sequence_point().as<int>();
|
||||
}
|
||||
|
||||
XCTAssertEqual(nextSequencePoint, _interruptSource->get_next_sequence_point().as<int>(), @"At cycle %d", c);
|
||||
}
|
||||
|
||||
XCTAssertEqual(toggles, expectedToggles);
|
||||
XCTAssertEqual(interrupts, expectedInterrupts);
|
||||
}
|
||||
|
||||
- (void)test1kHzTimer {
|
||||
// Set 1kHz timer.
|
||||
_interruptSource->write(7, 0 << 5);
|
||||
[self testExpectedToggles:2000 expectedInterrupts:1000];
|
||||
}
|
||||
|
||||
- (void)test50HzTimer {
|
||||
// Set 50Hz timer.
|
||||
_interruptSource->write(7, 1 << 5);
|
||||
[self testExpectedToggles:100 expectedInterrupts:50];
|
||||
}
|
||||
|
||||
- (void)testTone0Timer {
|
||||
// Set tone generator 0 as the interrupt source, with a divider of 137;
|
||||
// apply sync momentarily.
|
||||
_interruptSource->write(7, 2 << 5);
|
||||
_interruptSource->write(0, 137);
|
||||
_interruptSource->write(2, 0);
|
||||
|
||||
[self testExpectedToggles:250000/138 expectedInterrupts:250000/(138*2)];
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue
Block a user