mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-27 00:30:26 +00:00
Merge pull request #167 from TomHarte/VerticalSync
Adjusts vertical sync detection
This commit is contained in:
commit
a2aec39633
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user