diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 9e2e44185..98ada585c 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -18,7 +18,6 @@ using namespace Outputs::CRT; void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int vertical_sync_half_lines, bool should_alternate) { openGL_output_builder_.set_colour_format(colour_space, colour_cycle_numerator, colour_cycle_denominator); - const unsigned int syncCapacityLineChargeThreshold = 2; const unsigned int millisecondsHorizontalRetraceTime = 7; // source: Dictionary of Video and Television Technology, p. 234 const unsigned int scanlinesVerticalRetraceTime = 10; // source: ibid @@ -37,8 +36,7 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di cycles_per_line_ = cycles_per_line; unsigned int multiplied_cycles_per_line = cycles_per_line * time_multiplier_; - // generate timing values implied by the given arguments - sync_capacitor_charge_threshold_ = ((unsigned int)(syncCapacityLineChargeThreshold * cycles_per_line) * 3) / 4; + sync_capacitor_charge_threshold_ = (vertical_sync_half_lines * cycles_per_line) >> 1; // create the two flywheels horizontal_flywheel_.reset(new Flywheel(multiplied_cycles_per_line, (millisecondsHorizontalRetraceTime * multiplied_cycles_per_line) >> 6, multiplied_cycles_per_line >> 6)); @@ -74,7 +72,6 @@ void CRT::set_composite_function_type(CompositeSourceType type, float offset_of_ CRT::CRT(unsigned int common_output_divisor, unsigned int buffer_depth) : sync_capacitor_charge_level_(0), is_receiving_sync_(false), - sync_period_(0), common_output_divisor_(common_output_divisor), is_writing_composite_run_(false), delegate_(nullptr), @@ -264,31 +261,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo #pragma mark - stream feeding methods void CRT::output_scan(const Scan *const scan) { - const bool this_is_sync = (scan->type == Scan::Type::Sync); - const bool is_leading_edge = (!is_receiving_sync_ && this_is_sync); - is_receiving_sync_ = this_is_sync; - - // Accumulate: (i) a total of the amount of time in sync; and (ii) the amount of time since sync. - if(this_is_sync) { cycles_of_sync_ += scan->number_of_cycles; cycles_since_sync_ = 0; } - else cycles_since_sync_ += scan->number_of_cycles; - - bool vsync_requested = false; - // If it has been at least half a line since sync ended, then it is safe to decide whether what ended - // was vertical sync. - if(cycles_since_sync_ > (cycles_per_line_ >> 1)) { - // If it was vertical sync, set that flag. If it wasn't, clear the summed amount of sync to avoid - // a mistaken vertical sync due to an aggregate of horizontals. - vsync_requested = (cycles_of_sync_ > sync_capacitor_charge_threshold_); - if(vsync_requested || cycles_of_sync_ < (cycles_per_line_ >> 2)) - cycles_of_sync_ = 0; - } - - // This introduces a blackout period close to the expected vertical sync point in which horizontal syncs are not - // recognised, effectively causing the horizontal flywheel to freewheel during that period. This attempts to seek - // the problem that vertical sync otherwise often starts halfway through a scanline, which confuses the horizontal - // flywheel. I'm currently unclear whether this is an accurate solution to this problem. - const bool hsync_requested = is_leading_edge && !vertical_flywheel_->is_near_expected_sync(); - // simplified colour burst logic: if it's within the back porch we'll take it if(scan->type == Scan::Type::ColourBurst) { if(!colour_burst_amplitude_ && horizontal_flywheel_->get_current_time() < (horizontal_flywheel_->get_standard_period() * 12) >> 6) { @@ -300,11 +272,54 @@ void CRT::output_scan(const Scan *const scan) { colour_burst_phase_ = (colour_burst_phase_ & ~63) + colour_burst_phase_adjustment_; } } + // TODO: inspect raw data for potential colour burst if required; the DPLL and some zero crossing logic + // will probably be sufficient but some test data would be helpful - // TODO: inspect raw data for potential colour burst if required + // sync logic: mark whether this is currently sync and check for a leading edge + const bool this_is_sync = (scan->type == Scan::Type::Sync); + const bool is_leading_edge = (!is_receiving_sync_ && this_is_sync); + is_receiving_sync_ = this_is_sync; - sync_period_ = is_receiving_sync_ ? (sync_period_ + scan->number_of_cycles) : 0; - advance_cycles(scan->number_of_cycles, hsync_requested, vsync_requested, scan->type); + // horizontal sync is recognised on any leading edge that is not 'near' the expected vertical sync; + // the second limb is to avoid slightly horizontal sync shifting from the common pattern of + // equalisation pulses as the inverse of ordinary horizontal sync + bool hsync_requested = is_leading_edge && !vertical_flywheel_->is_near_expected_sync(); + + if(this_is_sync) { + // if this is sync then either begin or continue a sync accumulation phase + is_accumulating_sync_ = true; + } else { + // if this is not sync then check how long it has been since sync. If it's more than + // half a line then end sync accumulation and zero out the accumulating count + cycles_since_sync_ += scan->number_of_cycles; + if(cycles_since_sync_ > (cycles_per_line_ >> 1)) { + cycles_of_sync_ = 0; + is_accumulating_sync_ = false; + } + } + + unsigned int number_of_cycles = scan->number_of_cycles; + bool vsync_requested = false; + + // if sync is being accumulated then accumulate it; if it crosses the vertical sync threshold then + // divide this line at the crossing point and indicate vertical sync there + if(is_accumulating_sync_) { + cycles_of_sync_ += scan->number_of_cycles; + + if(this_is_sync && cycles_of_sync_ >= sync_capacitor_charge_threshold_) { + unsigned int overshoot = std::max(cycles_of_sync_ - sync_capacitor_charge_threshold_, number_of_cycles); + number_of_cycles -= overshoot; + advance_cycles(number_of_cycles, hsync_requested, false, scan->type); + + cycles_of_sync_ = 0; + is_accumulating_sync_ = false; + number_of_cycles = overshoot; + hsync_requested = false; + vsync_requested = true; + } + } + + advance_cycles(number_of_cycles, hsync_requested, vsync_requested, scan->type); } /* diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 160b1547a..9fe8e3bb3 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -44,7 +44,6 @@ class CRT { bool is_receiving_sync_; // true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync) int sync_capacitor_charge_level_; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync unsigned int sync_capacitor_charge_threshold_; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync - unsigned int sync_period_; struct Scan { enum Type { @@ -94,6 +93,7 @@ class CRT { } // sync counter, for determining vertical sync + bool is_accumulating_sync_; unsigned int cycles_of_sync_; unsigned int cycles_since_sync_; unsigned int cycles_per_line_;