1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-23 20:29:42 +00:00

Merge pull request #679 from TomHarte/OffByOne

Corrects off-by-one error in the ST's vertical state machine
This commit is contained in:
Thomas Harte 2019-11-19 22:02:41 -05:00 committed by GitHub
commit 3c77d3bda0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 82 additions and 14 deletions

View File

@ -209,6 +209,7 @@ HalfCycles MFP68901::get_next_sequence_point() {
// MARK: - Timers
void MFP68901::set_timer_mode(int timer, TimerMode mode, int prescale, bool reset_timer) {
LOG("Timer " << timer << " mode set: " << int(mode) << "; prescale: " << prescale);
timers_[timer].mode = mode;
timers_[timer].prescale = prescale;
if(reset_timer) {
@ -314,8 +315,6 @@ void MFP68901::update_interrupts() {
// Update the delegate if necessary.
if(interrupt_delegate_ && interrupt_line_ != old_interrupt_line) {
if(interrupt_line_)
LOG("Generating interrupt: " << std::hex << interrupt_pending_ << " / " << std::hex << interrupt_mask_ << " : " << std::hex << interrupt_in_service_);
interrupt_delegate_->mfp68901_did_change_interrupt_status(this);
}
}

View File

@ -28,7 +28,7 @@ const struct VerticalParams {
const int height;
} vertical_params[3] = {
{63, 263, 313}, // 47 rather than 63 on early machines.
{34, 234, 262}, // TODO: is 262 correct? If it's 263, how does that interact with opening the bottom border?
{34, 234, 263}, // TODO: is 262 correct? If it's 263, how does that interact with opening the bottom border?
{1, 401, 500} // 72 Hz mode: who knows?
};
@ -87,7 +87,11 @@ struct Checker {
} checker;
#endif
const int de_delay_period = 28*2;
const int de_delay_period = 28*2; // Number of half cycles after DE that observed DE changes.
const int vsync_x_position = 54*2; // Horizontal cycle on which vertical sync changes happen.
// "VSYNC starts 104 cycles after the start of the previous line's HSYNC, so that's 4 cycles before DE would be activated. ";
// hsync is at -50, so that's +54
}
@ -147,8 +151,8 @@ void Video::run_for(HalfCycles duration) {
if(line_length_ - 10*2 > x_) next_event = std::min(next_event, line_length_ - 10*2);
// Also, a vertical sync event might intercede.
if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && x_ < 30*2 && next_event >= 30*2) {
next_event = 30*2;
if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && x_ < vsync_x_position && next_event >= vsync_x_position) {
next_event = vsync_x_position;
}
// Determine current output mode and number of cycles to output for.
@ -208,16 +212,21 @@ void Video::run_for(HalfCycles duration) {
next_vertical_ = vertical_;
next_vertical_.sync_schedule = VerticalState::SyncSchedule::None;
// Use vertical_parameters to get parameters for the current output frequency.
if(next_y_ == vertical_timings.set_enable) {
// Use vertical_parameters to get parameters for the current output frequency;
// quick note: things other than the total frame size are counted in terms
// of the line they're evaluated on — i.e. the test is this line, not the next
// one. The total height constraint is obviously whether the next one would be
// too far.
if(y_ == vertical_timings.set_enable) {
next_vertical_.enable = true;
} else if(next_y_ == vertical_timings.reset_enable) {
} else if(y_ == vertical_timings.reset_enable) {
next_vertical_.enable = false;
} else if(next_y_ == vertical_timings.height) {
next_y_ = 0;
next_vertical_.sync_schedule = VerticalState::SyncSchedule::Begin;
current_address_ = base_address_ >> 1;
} else if(next_y_ == 3) {
} else if(y_ == 0) {
next_vertical_.sync_schedule = VerticalState::SyncSchedule::Begin;
} else if(y_ == 3) {
next_vertical_.sync_schedule = VerticalState::SyncSchedule::End;
}
}
@ -235,7 +244,7 @@ void Video::run_for(HalfCycles duration) {
else if(line_length_ - 10*2 == x_) horizontal_.sync = false;
// Check vertical events.
if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && x_ == 30*2) {
if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && x_ == vsync_x_position) {
vertical_.sync = vertical_.sync_schedule == VerticalState::SyncSchedule::Begin;
vertical_.enable &= !vertical_.sync;
}
@ -320,8 +329,8 @@ HalfCycles Video::get_next_sequence_point() {
}
// If a vertical sync event is scheduled, test for that.
if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && (x_ < 30*2)) {
event_time = std::min(event_time, 30*2);
if(vertical_.sync_schedule != VerticalState::SyncSchedule::None && (x_ < vsync_x_position)) {
event_time = std::min(event_time, vsync_x_position);
}
// Test for beginning and end of horizontal sync.

View File

@ -0,0 +1,60 @@
//
// MasterSystemVDPTests.m
// Clock SignalTests
//
// Created by Thomas Harte on 09/10/2018.
// Copyright © 2018 Thomas Harte. All rights reserved.
//
#import <XCTest/XCTest.h>
#include "../../../Machines/Atari/ST/Video.hpp"
@interface AtariSTVideoTests : XCTestCase
@end
@implementation AtariSTVideoTests
- (void)setUp {
[super setUp];
}
- (void)tearDown {
[super tearDown];
}
- (void)testSequencePoints {
// Establish an instance of video.
Atari::ST::Video video;
uint16_t ram[256*1024];
video.set_ram(ram, sizeof(ram));
// Set 4bpp, 50Hz.
video.write(0x05, 0x0200);
video.write(0x30, 0x0000);
// Run for [more than] a whole frame making sure that no observeable outputs
// change at any time other than a sequence point.
HalfCycles next_event;
bool display_enable = false;
bool vsync = false;
bool hsync = false;
for(size_t c = 0; c < 10 * 1000 * 1000; ++c) {
const bool is_transition_point = next_event == HalfCycles(0);
if(is_transition_point) {
display_enable = video.display_enabled();
vsync = video.vsync();
hsync = video.hsync();
next_event = video.get_next_sequence_point();
} else {
NSAssert(display_enable == video.display_enabled(), @"Unannounced change in display enabled at cycle %zu [%d before next sequence point]", c, next_event.as<int>());
NSAssert(vsync == video.vsync(), @"Unannounced change in vsync at cycle %zu [%d before next sequence point]", c, next_event.as<int>());
NSAssert(hsync == video.hsync(), @"Unannounced change in hsync at cycle %zu [%d before next sequence point]", c, next_event.as<int>());
}
video.run_for(HalfCycles(2));
next_event -= HalfCycles(2);
}
}
@end