1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-17 22:38:59 +00:00

Made an attempt to switch to the flywheels for both vertical and horizontal sync, and to act more appropriately for out-of-bounds sync requests. Lots of tweaking to do, I fear.

This commit is contained in:
Thomas Harte 2016-02-11 23:43:08 -05:00
parent c81891ec43
commit 1d20a29d97
6 changed files with 108 additions and 213 deletions

View File

@ -29,15 +29,15 @@ Machine::Machine() :
_audioOutputPositionError(0),
_currentOutputLine(0),
_is_odd_field(false),
_crt(Outputs::CRT(crt_cycles_per_line, Outputs::CRT::DisplayType::PAL50, 1, 1))
_crt(std::unique_ptr<Outputs::CRT>(new Outputs::CRT(crt_cycles_per_line, Outputs::CRT::DisplayType::PAL50, 1, 1)))
{
_crt.set_rgb_sampling_function(
_crt->set_rgb_sampling_function(
"vec3 rgb_sample(vec2 coordinate)"
"{"
"float texValue = texture(texID, coordinate).r;"
"return vec3(step(4.0/256.0, mod(texValue, 8.0/256.0)), step(2.0/256.0, mod(texValue, 4.0/256.0)), step(1.0/256.0, mod(texValue, 2.0/256.0)));"
"}");
_crt.set_visible_area(Outputs::Rect(0.2f, 0.0625f, 0.75f, 0.75f));
_crt->set_visible_area(Outputs::Rect(0.2f, 0.062f, 0.82f, 0.82f));
memset(_keyStates, 0, sizeof(_keyStates));
memset(_palette, 0xf, sizeof(_palette));
@ -382,8 +382,8 @@ inline void Machine::update_display()
// output 2.5 lines of sync, then half a line of level.
// if (_is_odd_field)
// {
_crt.output_blank(64 * crt_cycles_multiplier);
_crt.output_sync(320 * crt_cycles_multiplier);
_crt->output_blank(64 * crt_cycles_multiplier);
_crt->output_sync(320 * crt_cycles_multiplier);
// }
// else
// {
@ -412,7 +412,7 @@ inline void Machine::update_display()
if(line_position + remaining_period == 9)
{
// printf("!%d!", 9);
_crt.output_sync(9 * crt_cycles_multiplier);
_crt->output_sync(9 * crt_cycles_multiplier);
}
}
else
@ -425,7 +425,7 @@ inline void Machine::update_display()
if(isBlankLine)
{
int remaining_period = std::min(128 - line_position, cycles_left);
_crt.output_blank((unsigned int)remaining_period * crt_cycles_multiplier);
_crt->output_blank((unsigned int)remaining_period * crt_cycles_multiplier);
// printf(".[%d]", remaining_period);
_displayOutputPosition += remaining_period;
}
@ -435,7 +435,7 @@ inline void Machine::update_display()
if(line_position < 24)
{
int remaining_period = std::min(24 - line_position, cycles_left);
_crt.output_blank((unsigned int)remaining_period * crt_cycles_multiplier);
_crt->output_blank((unsigned int)remaining_period * crt_cycles_multiplier);
// printf("/(%d)(%d)[%d]", 24 - line_position, cycles_left, remaining_period);
_displayOutputPosition += remaining_period;
@ -448,8 +448,8 @@ inline void Machine::update_display()
case 2: case 5: _currentOutputDivider = 4; break;
}
_crt.allocate_write_area(80 * crt_cycles_multiplier / _currentOutputDivider);
_currentLine = _writePointer = (uint8_t *)_crt.get_write_target_for_buffer(0);
_crt->allocate_write_area(80 * crt_cycles_multiplier / _currentOutputDivider);
_currentLine = _writePointer = (uint8_t *)_crt->get_write_target_for_buffer(0);
if(current_line == first_graphics_line)
_startLineAddress = _startScreenAddress;
@ -470,10 +470,10 @@ inline void Machine::update_display()
}
if(newDivider != _currentOutputDivider && _currentLine)
{
_crt.output_data((unsigned int)((_writePointer - _currentLine) * _currentOutputDivider * crt_cycles_multiplier), _currentOutputDivider);
_crt->output_data((unsigned int)((_writePointer - _currentLine) * _currentOutputDivider * crt_cycles_multiplier), _currentOutputDivider);
_currentOutputDivider = newDivider;
_crt.allocate_write_area((int)((104 - (unsigned int)line_position) * crt_cycles_multiplier / _currentOutputDivider));
_currentLine = _writePointer = (uint8_t *)_crt.get_write_target_for_buffer(0);
_crt->allocate_write_area((int)((104 - (unsigned int)line_position) * crt_cycles_multiplier / _currentOutputDivider));
_currentLine = _writePointer = (uint8_t *)_crt->get_write_target_for_buffer(0);
}
@ -551,7 +551,7 @@ inline void Machine::update_display()
if(line_position >= 104)
{
int pixels_to_output = std::min(128 - line_position, cycles_left);
_crt.output_blank((unsigned int)pixels_to_output * crt_cycles_multiplier);
_crt->output_blank((unsigned int)pixels_to_output * crt_cycles_multiplier);
_displayOutputPosition += pixels_to_output;
if(line_position + pixels_to_output == 128)
@ -566,9 +566,9 @@ inline void Machine::update_display()
_startLineAddress++;
if(_writePointer)
_crt.output_data((unsigned int)((_writePointer - _currentLine) * _currentOutputDivider), _currentOutputDivider);
_crt->output_data((unsigned int)((_writePointer - _currentLine) * _currentOutputDivider), _currentOutputDivider);
else
_crt.output_data(80 * crt_cycles_multiplier, _currentOutputDivider);
_crt->output_data(80 * crt_cycles_multiplier, _currentOutputDivider);
_currentLine = nullptr;
_writePointer = nullptr;
}

View File

@ -146,7 +146,7 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
void set_key_state(Key key, bool isPressed);
Outputs::CRT *get_crt() { return &_crt; }
Outputs::CRT *get_crt() { return _crt.get(); }
Outputs::Speaker *get_speaker() { return &_speaker; }
virtual void tape_did_change_interrupt_status(Tape *tape);
@ -185,7 +185,7 @@ class Machine: public CPU6502::Processor<Machine>, Tape::Delegate {
Tape _tape;
// Outputs.
Outputs::CRT _crt;
std::unique_ptr<Outputs::CRT> _crt;
Speaker _speaker;
};

View File

@ -42,23 +42,24 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di
// for horizontal retrace and 500 to 750 µs for vertical retrace in NTSC and PAL TV."
_time_multiplier = (1000 + cycles_per_line - 1) / cycles_per_line;
height_of_display += (height_of_display / 20); // this is the overrun area we'll use to
// store fundamental display configuration properties
_height_of_display = height_of_display + 5;
_height_of_display = height_of_display;
_cycles_per_line = cycles_per_line * _time_multiplier;
// generate timing values implied by the given arbuments
_hsync_error_window = _cycles_per_line >> 5;
_sync_capacitor_charge_threshold = ((syncCapacityLineChargeThreshold * _cycles_per_line) * 50) >> 7;
_horizontal_retrace_time = (millisecondsHorizontalRetraceTime * _cycles_per_line) >> 6;
const unsigned int vertical_retrace_time = scanlinesVerticalRetraceTime * _cycles_per_line;
const float halfLineWidth = (float)_height_of_display * 2.0f;
// creat the two flywheels
unsigned int horizontal_retrace_time = scanlinesVerticalRetraceTime * _cycles_per_line;
_horizontal_flywheel = std::unique_ptr<Outputs::Flywheel>(new Outputs::Flywheel(_cycles_per_line, (millisecondsHorizontalRetraceTime * _cycles_per_line) >> 6));
_vertical_flywheel = std::unique_ptr<Outputs::Flywheel>(new Outputs::Flywheel(_cycles_per_line * height_of_display, scanlinesVerticalRetraceTime * _cycles_per_line));
for(int c = 0; c < 4; c++)
{
_scanSpeed[c].x = (c&kRetraceXMask) ? -(kCRTFixedPointRange / _horizontal_retrace_time) : (kCRTFixedPointRange / _cycles_per_line);
_scanSpeed[c].x = (c&kRetraceXMask) ? -(kCRTFixedPointRange / horizontal_retrace_time) : (kCRTFixedPointRange / _cycles_per_line);
_scanSpeed[c].y = (c&kRetraceYMask) ? -(kCRTFixedPointRange / vertical_retrace_time) : (kCRTFixedPointRange / (_height_of_display * _cycles_per_line));
// width should be 1.0 / _height_of_display, rotated to match the direction
@ -66,9 +67,6 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di
_beamWidth[c].x = (uint32_t)((sinf(angle) / halfLineWidth) * kCRTFixedPointRange);
_beamWidth[c].y = (uint32_t)((cosf(angle) / halfLineWidth) * kCRTFixedPointRange);
}
// reset flywheel sync
_expected_next_hsync = _cycles_per_line;
}
void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType displayType)
@ -104,11 +102,8 @@ void CRT::allocate_buffers(unsigned int number, va_list sizes)
CRT::CRT() :
_next_scan(0),
_frame_read_pointer(0),
_horizontal_counter(0),
_sync_capacitor_charge_level(0),
_is_receiving_sync(false),
_is_in_hsync(false),
_is_in_vsync(false),
_current_frame_mutex(new std::mutex),
_visible_area(Rect(0, 0, 1, 1)),
_rasterPosition({.x = 0, .y = 0})
@ -147,70 +142,14 @@ CRT::~CRT()
#pragma mark - Sync loop
CRT::SyncEvent CRT::get_next_vertical_sync_event(bool vsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced)
Flywheel::SyncEvent CRT::get_next_vertical_sync_event(bool vsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced)
{
SyncEvent proposedEvent = SyncEvent::None;
unsigned int proposedSyncTime = cycles_to_run_for;
// will an acceptable vertical sync be triggered?
if (vsync_is_requested && !_is_in_vsync) {
if (_sync_capacitor_charge_level >= _sync_capacitor_charge_threshold && _rasterPosition.y >= 3*(kCRTFixedPointRange >> 2)) {
proposedSyncTime = 0;
proposedEvent = SyncEvent::StartVSync;
_did_detect_vsync = true;
}
}
// have we overrun the maximum permitted number of horizontal syncs for this frame?
if (!_is_in_vsync) {
unsigned int time_until_end_of_frame = (kCRTFixedPointRange - _rasterPosition.y) / _scanSpeed[0].y;
if(time_until_end_of_frame < proposedSyncTime) {
proposedSyncTime = time_until_end_of_frame;
proposedEvent = SyncEvent::StartVSync;
}
} else {
unsigned int time_until_start_of_frame = _rasterPosition.y / (uint32_t)(-_scanSpeed[kRetraceYMask].y);
if(time_until_start_of_frame < proposedSyncTime) {
proposedSyncTime = time_until_start_of_frame;
proposedEvent = SyncEvent::EndVSync;
}
}
*cycles_advanced = proposedSyncTime;
return proposedEvent;
return _vertical_flywheel->get_next_event_in_period(vsync_is_requested, cycles_to_run_for, cycles_advanced);
}
CRT::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced)
Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced)
{
// do we recognise this hsync, thereby adjusting future time expectations?
if(hsync_is_requested) {
if (_horizontal_counter < _hsync_error_window || _horizontal_counter >= _expected_next_hsync - _hsync_error_window) {
_did_detect_hsync = true;
unsigned int time_now = (_horizontal_counter < _hsync_error_window) ? _expected_next_hsync + _horizontal_counter : _horizontal_counter;
_expected_next_hsync = (_expected_next_hsync + _expected_next_hsync + _expected_next_hsync + time_now) >> 2;
}
}
SyncEvent proposedEvent = SyncEvent::None;
unsigned int proposedSyncTime = cycles_to_run_for;
// will we end an ongoing hsync?
if (_horizontal_counter < _horizontal_retrace_time && _horizontal_counter+proposedSyncTime >= _horizontal_retrace_time) {
proposedSyncTime = _horizontal_retrace_time - _horizontal_counter;
proposedEvent = SyncEvent::EndHSync;
}
// will we start an hsync?
if (_horizontal_counter + proposedSyncTime >= _expected_next_hsync) {
proposedSyncTime = _expected_next_hsync - _horizontal_counter;
proposedEvent = SyncEvent::StartHSync;
}
*cycles_advanced = proposedSyncTime;
return proposedEvent;
return _horizontal_flywheel->get_next_event_in_period(hsync_is_requested, cycles_to_run_for, cycles_advanced);
}
void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Type type, uint16_t tex_x, uint16_t tex_y)
@ -218,12 +157,13 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
number_of_cycles *= _time_multiplier;
bool is_output_run = ((type == Type::Level) || (type == Type::Data));
vsync_requested &= (_sync_capacitor_charge_level >= _sync_capacitor_charge_threshold);
while(number_of_cycles) {
unsigned int time_until_vertical_sync_event, time_until_horizontal_sync_event;
SyncEvent next_vertical_sync_event = get_next_vertical_sync_event(vsync_requested, number_of_cycles, &time_until_vertical_sync_event);
SyncEvent next_horizontal_sync_event = get_next_horizontal_sync_event(hsync_requested, time_until_vertical_sync_event, &time_until_horizontal_sync_event);
Flywheel::SyncEvent next_vertical_sync_event = get_next_vertical_sync_event(vsync_requested, number_of_cycles, &time_until_vertical_sync_event);
Flywheel::SyncEvent next_horizontal_sync_event = get_next_horizontal_sync_event(hsync_requested, time_until_vertical_sync_event, &time_until_horizontal_sync_event);
// get the next sync event and its timing; hsync request is instantaneous (being edge triggered) so
// set it to false for the next run through this loop (if any)
@ -233,7 +173,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
vsync_requested = false;
uint8_t *next_run = (is_output_run && _current_frame_builder && next_run_length) ? _current_frame_builder->get_next_run() : nullptr;
int lengthMask = (_is_in_hsync ? kRetraceXMask : 0) | (_is_in_vsync ? kRetraceYMask : 0);
int lengthMask = (_horizontal_flywheel->is_in_retrace() ? kRetraceXMask : 0) | (_vertical_flywheel->is_in_retrace() ? kRetraceYMask : 0);
#define position_x(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfPosition + 0])
#define position_y(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfPosition + 2])
@ -245,11 +185,14 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
if(next_run)
{
unsigned int x_position = _horizontal_flywheel->get_current_output_position() * (kCRTFixedPointRange / 1024);
unsigned int y_position = (_vertical_flywheel->get_current_output_position() / 312) * (kCRTFixedPointRange / 1024);
// set the type, initial raster position and type of this run
position_x(0) = position_x(4) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.x + _beamWidth[lengthMask].x);
position_y(0) = position_y(4) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.y + _beamWidth[lengthMask].y);
position_x(1) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.x - _beamWidth[lengthMask].x);
position_y(1) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.y - _beamWidth[lengthMask].y);
position_x(0) = position_x(4) = InternalToUInt16(kCRTFixedPointOffset + x_position + _beamWidth[lengthMask].x);
position_y(0) = position_y(4) = InternalToUInt16(kCRTFixedPointOffset + y_position + _beamWidth[lengthMask].y);
position_x(1) = InternalToUInt16(kCRTFixedPointOffset + x_position - _beamWidth[lengthMask].x);
position_y(1) = InternalToUInt16(kCRTFixedPointOffset + y_position - _beamWidth[lengthMask].y);
tex_x(0) = tex_x(1) = tex_x(4) = tex_x;
@ -259,28 +202,30 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
lateral(1) = lateral(2) = lateral(3) = 1;
}
// advance the raster position as dictated by current sync status
int64_t end_position[2];
end_position[0] = (int64_t)_rasterPosition.x + (int64_t)next_run_length * (int32_t)_scanSpeed[lengthMask].x;
end_position[1] = (int64_t)_rasterPosition.y + (int64_t)next_run_length * (int32_t)_scanSpeed[lengthMask].y;
// decrement the number of cycles left to run for and increment the
// horizontal counter appropriately
number_of_cycles -= next_run_length;
if (_is_in_hsync)
_rasterPosition.x = (uint32_t)std::max((int64_t)0, end_position[0]);
// either charge or deplete the vertical retrace capacitor (making sure it stops at 0)
if (vsync_charging && !_vertical_flywheel->is_in_retrace())
_sync_capacitor_charge_level += next_run_length;
else
_rasterPosition.x = (uint32_t)std::min((int64_t)kCRTFixedPointRange, end_position[0]);
_sync_capacitor_charge_level = std::max(_sync_capacitor_charge_level - (int)next_run_length, 0);
if (_is_in_vsync)
_rasterPosition.y = (uint32_t)std::max((int64_t)0, end_position[1]);
else
_rasterPosition.y = (uint32_t)std::min((int64_t)kCRTFixedPointRange, end_position[1]);
// react to the incoming event...
_horizontal_flywheel->apply_event(next_run_length, (next_run_length == time_until_horizontal_sync_event) ? next_horizontal_sync_event : Flywheel::SyncEvent::None);
_vertical_flywheel->apply_event(next_run_length, (next_run_length == time_until_vertical_sync_event) ? next_vertical_sync_event : Flywheel::SyncEvent::None);
if(next_run)
{
unsigned int x_position = _horizontal_flywheel->get_current_output_position() * (kCRTFixedPointRange / 1024);
unsigned int y_position = (_vertical_flywheel->get_current_output_position() / 312) * (kCRTFixedPointRange / 1024);
// store the final raster position
position_x(2) = position_x(3) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.x - _beamWidth[lengthMask].x);
position_y(2) = position_y(3) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.y - _beamWidth[lengthMask].y);
position_x(5) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.x + _beamWidth[lengthMask].x);
position_y(5) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.y + _beamWidth[lengthMask].y);
position_x(2) = position_x(3) = InternalToUInt16(kCRTFixedPointOffset + x_position - _beamWidth[lengthMask].x);
position_y(2) = position_y(3) = InternalToUInt16(kCRTFixedPointOffset + y_position - _beamWidth[lengthMask].y);
position_x(5) = InternalToUInt16(kCRTFixedPointOffset + x_position + _beamWidth[lengthMask].x);
position_y(5) = InternalToUInt16(kCRTFixedPointOffset + y_position + _beamWidth[lengthMask].y);
// if this is a data run then advance the buffer pointer
if(type == Type::Data && source_divider) tex_x += next_run_length / (_time_multiplier * source_divider);
@ -289,82 +234,27 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
tex_x(2) = tex_x(3) = tex_x(5) = tex_x;
}
// decrement the number of cycles left to run for and increment the
// horizontal counter appropriately
number_of_cycles -= next_run_length;
_horizontal_counter += next_run_length;
// either charge or deplete the vertical retrace capacitor (making sure it stops at 0)
if (vsync_charging && !_is_in_vsync)
_sync_capacitor_charge_level += next_run_length;
else
_sync_capacitor_charge_level = std::max(_sync_capacitor_charge_level - (int)next_run_length, 0);
// react to the incoming event...
if(next_run_length == time_until_horizontal_sync_event)
if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event == Flywheel::SyncEvent::EndRetrace)
{
switch(next_horizontal_sync_event)
if(_current_frame_builder)
{
// start of hsync: zero the scanline counter, note that we're now in
// horizontal sync, increment the lines-in-this-frame counter
case SyncEvent::StartHSync:
_horizontal_counter = 0;
_is_in_hsync = true;
break;
// end of horizontal sync: update the flywheel's velocity, note that we're no longer
// in horizontal sync
case SyncEvent::EndHSync:
if (!_did_detect_hsync) {
_expected_next_hsync = (_expected_next_hsync + (_hsync_error_window >> 4) + _cycles_per_line) >> 1;
}
_did_detect_hsync = false;
_is_in_hsync = false;
break;
default: break;
_current_frame_builder->complete();
_current_frame_mutex->lock();
_current_frame = &_current_frame_builder->frame;
_current_frame_mutex->unlock();
// TODO: how to communicate did_detect_vsync? Bring the delegate back?
// _delegate->crt_did_end_frame(this, &_current_frame_builder->frame, _did_detect_vsync);
}
}
if(next_run_length == time_until_vertical_sync_event)
{
switch(next_vertical_sync_event)
{
// start of vertical sync: reset the lines-in-this-frame counter,
// load the retrace counter with the amount of time it'll take to retrace
case SyncEvent::StartVSync:
_is_in_vsync = true;
_sync_capacitor_charge_level = 0;
break;
// if(_frames_with_delegate < kCRTNumberOfFrames)
// {
_frame_read_pointer = (_frame_read_pointer + 1)%kCRTNumberOfFrames;
_current_frame_builder = _frame_builders[_frame_read_pointer];
_current_frame_builder->reset();
// }
// else
// _current_frame_builder = nullptr;
// end of vertical sync: tell the delegate that we finished vertical sync,
// releasing all runs back into the common pool
case SyncEvent::EndVSync:
if(_current_frame_builder)
{
_current_frame_builder->complete();
_current_frame_mutex->lock();
_current_frame = &_current_frame_builder->frame;
_current_frame_mutex->unlock();
// TODO: how to communicate did_detect_vsync? Bring the delegate back?
// _delegate->crt_did_end_frame(this, &_current_frame_builder->frame, _did_detect_vsync);
}
// if(_frames_with_delegate < kCRTNumberOfFrames)
{
_frame_read_pointer = (_frame_read_pointer + 1)%kCRTNumberOfFrames;
_current_frame_builder = _frame_builders[_frame_read_pointer];
_current_frame_builder->reset();
}
// else
// _current_frame_builder = nullptr;
_is_in_vsync = false;
_did_detect_vsync = false;
break;
default: break;
}
}
}
}
@ -378,7 +268,7 @@ void CRT::output_scan()
bool this_is_sync = (scan->type == Type::Sync);
bool hsync_requested = !_is_receiving_sync && this_is_sync;
bool vsync_requested = _is_receiving_sync;
bool vsync_requested = _is_receiving_sync && !this_is_sync;
_is_receiving_sync = this_is_sync;
advance_cycles(scan->number_of_cycles, scan->source_divider, hsync_requested, vsync_requested, this_is_sync, scan->type, scan->tex_x, scan->tex_y);

View File

@ -233,27 +233,18 @@ class CRT {
unsigned int _colour_cycle_denominator;
OutputDevice _output_device;
// properties directly derived from there
unsigned int _hsync_error_window; // the permitted window around the expected sync position in which a sync pulse will be recognised; calculated once at init
// the current scanning position
// the current scanning position (TODO: can I eliminate this in favour of just using the flywheels?)
struct Vector {
uint32_t x, y;
} _rasterPosition, _scanSpeed[4], _beamWidth[4];
// outer elements of sync separation
// the two flywheels regulating scanning
std::unique_ptr<Outputs::Flywheel> _horizontal_flywheel, _vertical_flywheel;
// 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)
bool _did_detect_hsync; // true if horizontal sync was detected during this scanline (so, this affects flywheel adjustments)
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
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
int _is_in_vsync;
// components of the flywheel sync
unsigned int _horizontal_counter; // time run since the _start_ of the last horizontal sync
unsigned int _expected_next_hsync; // our current expection of when the next horizontal sync will be encountered (which implies current flywheel velocity)
unsigned int _horizontal_retrace_time;
bool _is_in_hsync; // true for the duration of a horizontal sync — used to determine beam running direction and speed
bool _did_detect_vsync; // true if vertical sync was detected in the input stream rather than forced by emergency measure
// the outer entry point for dispatching output_sync, output_blank, output_level and output_data
enum Type {
@ -263,13 +254,8 @@ class CRT {
// the inner entry point that determines whether and when the next sync event will occur within
// the current output window
enum SyncEvent {
None,
StartHSync, EndHSync,
StartVSync, EndVSync
};
SyncEvent get_next_vertical_sync_event(bool vsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced);
SyncEvent get_next_horizontal_sync_event(bool hsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced);
Flywheel::SyncEvent get_next_vertical_sync_event(bool vsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced);
Flywheel::SyncEvent get_next_horizontal_sync_event(bool hsync_is_requested, unsigned int cycles_to_run_for, unsigned int *cycles_advanced);
// each call to output_* generates a scan. A two-slot queue for scans allows edge extensions.
struct Scan {

View File

@ -105,11 +105,11 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool
push_size_uniforms(output_width, output_height);
if(_last_drawn_frame != nullptr)
{
glUniform1f(_openGL_state->alphaUniform, 0.4f);
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_last_drawn_frame->number_of_vertices);
}
// if(_last_drawn_frame != nullptr)
// {
// glUniform1f(_openGL_state->alphaUniform, 0.4f);
// glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_last_drawn_frame->number_of_vertices);
// }
glUniform1f(_openGL_state->alphaUniform, 1.0f);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(_current_frame->number_of_vertices * _current_frame->size_per_vertex), _current_frame->vertices, GL_DYNAMIC_DRAW);

View File

@ -61,13 +61,24 @@ struct Flywheel
// do we recognise this hsync, thereby adjusting future time expectations?
if(sync_is_requested)
{
_did_detect_sync = true;
if(_counter < _sync_error_window || _counter > _expected_next_sync - _sync_error_window)
{
_did_detect_sync = true;
unsigned int time_now = (_counter < _sync_error_window) ? _expected_next_sync + _counter : _counter;
_expected_next_sync = (_expected_next_sync + _expected_next_sync + _expected_next_sync + time_now) >> 2;
}
else
{
if(_counter < _retrace_time + (_expected_next_sync >> 1))
{
_expected_next_sync = (_expected_next_sync + _standard_period + _sync_error_window) >> 1;
}
else
{
_expected_next_sync = (_expected_next_sync + _standard_period - _sync_error_window) >> 1;
}
}
}
SyncEvent proposed_event = SyncEvent::None;
@ -139,13 +150,21 @@ struct Flywheel
}
/*!
Returns the amount of time since retrace last began. Time then counts monotonically up from zero.
@returns the amount of time since retrace last began. Time then counts monotonically up from zero.
*/
inline unsigned int get_current_time()
{
return _counter;
}
/*!
@returns whether the output is currently retracing.
*/
inline bool is_in_retrace()
{
return _counter < _retrace_time;
}
private:
unsigned int _standard_period; // the normal length of time between syncs
const unsigned int _retrace_time; // a constant indicating the amount of time it takes to perform a retrace