1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-11 04:28:58 +00:00

Merge pull request #167 from TomHarte/VerticalSync

Adjusts vertical sync detection
This commit is contained in:
Thomas Harte 2017-07-29 21:44:47 -04:00 committed by GitHub
commit a2aec39633
2 changed files with 87 additions and 49 deletions

View File

@ -15,10 +15,9 @@
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, bool should_alternate) {
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));
@ -54,11 +52,11 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di
void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType displayType) {
switch(displayType) {
case DisplayType::PAL50:
set_new_timing(cycles_per_line, 312, ColourSpace::YUV, 709379, 2500, true); // i.e. 283.7516
set_new_timing(cycles_per_line, 312, ColourSpace::YUV, 709379, 2500, 5, true); // i.e. 283.7516; 2.5 lines = vertical sync
break;
case DisplayType::NTSC60:
set_new_timing(cycles_per_line, 262, ColourSpace::YIQ, 455, 2, false); // i.e. 227.5
set_new_timing(cycles_per_line, 262, ColourSpace::YIQ, 455, 2, 6, false); // i.e. 227.5, 3 lines = vertical sync
break;
}
}
@ -72,9 +70,7 @@ 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),
@ -82,9 +78,16 @@ CRT::CRT(unsigned int common_output_divisor, unsigned int buffer_depth) :
openGL_output_builder_(buffer_depth),
is_alernate_line_(false) {}
CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, bool should_alternate, unsigned int buffer_depth) :
CRT::CRT( unsigned int cycles_per_line,
unsigned int common_output_divisor,
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,
unsigned int buffer_depth) :
CRT(common_output_divisor, buffer_depth) {
set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator, should_alternate);
set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator, vertical_sync_half_lines, should_alternate);
}
CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int buffer_depth) :
@ -257,31 +260,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) {
@ -293,11 +271,59 @@ 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;
cycles_since_sync_ = 0;
} 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_ >> 2)) {
cycles_of_sync_ = 0;
is_accumulating_sync_ = false;
is_refusing_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_ && !is_refusing_sync_) {
cycles_of_sync_ += scan->number_of_cycles;
if(this_is_sync && cycles_of_sync_ >= sync_capacitor_charge_threshold_) {
is_refusing_sync_ = true;
unsigned int overshoot = std::min(cycles_of_sync_ - sync_capacitor_charge_threshold_, number_of_cycles);
if(overshoot) {
number_of_cycles -= overshoot;
advance_cycles(number_of_cycles, hsync_requested, false, scan->type);
hsync_requested = false;
number_of_cycles = overshoot;
}
cycles_of_sync_ = 0;
is_accumulating_sync_ = false;
vsync_requested = true;
}
}
advance_cycles(number_of_cycles, hsync_requested, vsync_requested, scan->type);
}
/*

View File

@ -40,12 +40,6 @@ class CRT {
std::unique_ptr<Flywheel> horizontal_flywheel_, vertical_flywheel_;
uint16_t vertical_flywheel_output_divider_;
// elements of sync separation
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 {
Sync, Level, Data, Blank, ColourBurst
@ -94,8 +88,13 @@ class CRT {
}
// sync counter, for determining vertical sync
unsigned int cycles_of_sync_;
unsigned int cycles_since_sync_;
bool is_receiving_sync_; // true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync)
bool is_accumulating_sync_; // true if a sync level has triggered the suspicion that a vertical sync might be in progress
bool is_refusing_sync_; // true once a vertical sync has been detected, until a prolonged period of non-sync has ended suspicion of an ongoing 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 cycles_of_sync_; // the number of cycles since the potential vertical sync began
unsigned int cycles_since_sync_; // the number of cycles since last in sync, for defeating the possibility of this being a vertical sync
unsigned int cycles_per_line_;
public:
@ -119,6 +118,9 @@ class CRT {
@param colour_cycle_denominator Specifies the denominator for the per-line frequency of the colour subcarrier.
The colour subcarrier is taken to have colour_cycle_numerator/colour_cycle_denominator cycles per line.
@param vertical_sync_half_lines The expected length of vertical synchronisation (equalisation pulses aside),
in multiples of half a line.
@param buffer_depth The depth per pixel of source data buffers to create for this machine. Machines
may provide per-clock-cycle data in the depth that they consider convenient, supplying a sampling
function to convert between their data format and either a composite or RGB signal, allowing that
@ -127,7 +129,14 @@ class CRT {
@see @c set_rgb_sampling_function , @c set_composite_sampling_function
*/
CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, bool should_alternate, unsigned int buffer_depth);
CRT(unsigned int cycles_per_line,
unsigned int common_output_divisor,
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,
unsigned int buffer_depth);
/*! Constructs the CRT with the specified clock rate, with the display height and colour
subcarrier frequency dictated by a standard display type and with the requested number of
@ -136,11 +145,14 @@ class CRT {
Exactly identical to calling the designated constructor with colour subcarrier information
looked up by display type.
*/
CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int buffer_depth);
CRT(unsigned int cycles_per_line,
unsigned int common_output_divisor,
DisplayType displayType,
unsigned int buffer_depth);
/*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
been provided at construction. */
void 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, bool should_alternate);
void 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);
/*! Resets the CRT with new timing information derived from a new display type. The CRT then continues
as though the new timing had been provided at construction. */