diff --git a/Machines/Atari2600.cpp b/Machines/Atari2600.cpp index 4c3452cf3..c3e730d0f 100644 --- a/Machines/Atari2600.cpp +++ b/Machines/Atari2600.cpp @@ -11,6 +11,7 @@ #include using namespace Atari2600; +static const char atari2600DataType[] = "Atari2600"; Machine::Machine() { @@ -18,7 +19,7 @@ Machine::Machine() _horizontalTimer = 0; _lastOutputStateDuration = 0; _lastOutputState = OutputState::Sync; - _crt = new Outputs::CRT(228, 1, 4); + _crt = new Outputs::CRT(228, 256, 1, 4); reset(); } @@ -84,10 +85,10 @@ void Machine::output_state(OutputState state, uint8_t *pixel) _outputBuffer = _crt->get_write_target_for_buffer(0); _outputBuffer[0] = _outputBuffer[1] = _outputBuffer[2] = 0; _outputBuffer[3] = 0xff; - _crt->output_level(_lastOutputStateDuration, "Atari2600"); + _crt->output_level(_lastOutputStateDuration, atari2600DataType); } break; case OutputState::Sync: _crt->output_sync(_lastOutputStateDuration); break; - case OutputState::Pixel: _crt->output_data(_lastOutputStateDuration, "Atari2600"); break; + case OutputState::Pixel: _crt->output_data(_lastOutputStateDuration, atari2600DataType); break; } _lastOutputStateDuration = 0; _lastOutputState = state; diff --git a/Outputs/CRT.cpp b/Outputs/CRT.cpp index 40dadbd6b..65fdcdbba 100644 --- a/Outputs/CRT.cpp +++ b/Outputs/CRT.cpp @@ -20,8 +20,11 @@ static const int scanlinesVerticalRetraceTime = 26; using namespace Outputs; -CRT::CRT(int cycles_per_line, int number_of_buffers, ...) +CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...) { + _height_of_display = height_of_display; + _cycles_per_line = cycles_per_line; + _horizontalOffset = 0.0f; _verticalOffset = 0.0f; @@ -39,13 +42,15 @@ CRT::CRT(int cycles_per_line, int number_of_buffers, ...) va_end(va); _write_allocation_pointer = 0; - _cycles_per_line = cycles_per_line; _expected_next_hsync = cycles_per_line; _hsync_error_window = cycles_per_line >> 5; _horizontal_counter = 0; _sync_capacitor_charge_level = 0; - _is_in_sync = false; + _is_receiving_sync = false; + _is_in_hsync = false; + + _run_pointer = 0; } CRT::~CRT() @@ -74,6 +79,12 @@ CRT::SyncEvent CRT::advance_to_next_sync_event(bool hsync_is_requested, bool vsy SyncEvent proposedEvent = SyncEvent::None; int proposedSyncTime = cycles_to_run_for; + // have we overrun the maximum permitted number of horizontal syncs for this frame? + if (_hsync_counter > _height_of_display + 10) { + *cycles_advanced = 0; + return SyncEvent::StartHSync; + } + // will we end an ongoing hsync? const int endOfHSyncTime = (millisecondsHorizontalRetraceTime*_cycles_per_line) >> 6; if (_horizontal_counter < endOfHSyncTime && _horizontal_counter+proposedSyncTime >= endOfHSyncTime) { @@ -97,8 +108,6 @@ CRT::SyncEvent CRT::advance_to_next_sync_event(bool hsync_is_requested, bool vsy } } - // TODO: will a late-in-the-day vertical sync be forced? - // will an ongoing vertical sync end? if (_vretrace_counter > 0) { if (_vretrace_counter < proposedSyncTime) { @@ -111,23 +120,62 @@ CRT::SyncEvent CRT::advance_to_next_sync_event(bool hsync_is_requested, bool vsy return proposedEvent; } -void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool vsync_charging, const CRTRun::Type type) +void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool vsync_charging, const CRTRun::Type type, const char *data_type) { + int buffer_offset = 0; + while(number_of_cycles) { int next_run_length; SyncEvent next_event = advance_to_next_sync_event(hsync_requested, vsync_charging, number_of_cycles, &next_run_length); - for(int c = 0; c < next_run_length; c++) + if(_run_pointer >= _all_runs.size()) { - switch(type) - { - case CRTRun::Type::Data: putc('-', stdout); break; - case CRTRun::Type::Blank: putc(' ', stdout); break; - case CRTRun::Type::Level: putc('_', stdout); break; - case CRTRun::Type::Sync: putc('<', stdout); break; - } + _all_runs.resize((_all_runs.size() * 2)+1); + } + + CRTRun *nextRun = &_all_runs[_run_pointer]; + _run_pointer++; + + nextRun->type = type; + nextRun->start_point.dst_x = _horizontalOffset; + nextRun->start_point.dst_y = _verticalOffset; + + if(type == CRTRun::Type::Data || type == CRTRun::Type::Level) + { + nextRun->start_point.src_x = (_write_target_pointer + buffer_offset) & (bufferWidth - 1); + nextRun->start_point.dst_x = (_write_target_pointer + buffer_offset) / bufferWidth; + } + nextRun->data_type = data_type; + + if (_vretrace_counter > 0) + { + _verticalOffset = std::max(0.0f, _verticalOffset - (float)number_of_cycles / (float)(scanlinesVerticalRetraceTime * _cycles_per_line)); + } + else + { + _verticalOffset = std::min(1.0f, _verticalOffset + (float)number_of_cycles / (float)(_height_of_display * _cycles_per_line)); + } + + if (_is_in_hsync) + { + _horizontalOffset = std::max(0.0f, _horizontalOffset - (float)(((millisecondsHorizontalRetraceTime * _cycles_per_line) >> 6) * number_of_cycles) / (float)_cycles_per_line); + } + else + { + _horizontalOffset = std::min(1.0f, _horizontalOffset + (float)((((64 - millisecondsHorizontalRetraceTime) * _cycles_per_line) >> 6) * number_of_cycles) / (float)_cycles_per_line); + } + + nextRun->end_point.dst_x = _horizontalOffset; + nextRun->end_point.dst_y = _verticalOffset; + if(type == CRTRun::Type::Data) + { + buffer_offset += next_run_length; + } + if(type == CRTRun::Type::Data || type == CRTRun::Type::Level) + { + nextRun->end_point.src_x = (_write_target_pointer + buffer_offset) & (bufferWidth - 1); + nextRun->end_point.dst_x = (_write_target_pointer + buffer_offset) / bufferWidth; } -// printf("[[%d]%d:%d]", type, next_event, next_run_length); hsync_requested = false; @@ -144,48 +192,62 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool default: break; case SyncEvent::StartHSync: _horizontal_counter = 0; - printf("\n"); + _is_in_hsync = true; + _hsync_counter++; break; - case SyncEvent::EndHSync: if (!_did_detect_hsync) { _expected_next_hsync = (_expected_next_hsync + (_hsync_error_window >> 1) + _cycles_per_line) >> 1; } _did_detect_hsync = false; + _is_in_hsync = false; break; case SyncEvent::StartVSync: _vretrace_counter = scanlinesVerticalRetraceTime * _cycles_per_line; - printf("\n\n===\n\n"); + _hsync_counter = 0; + break; + + case SyncEvent::EndVSync: + if(_delegate != nullptr) + _delegate->crt_did_start_vertical_retrace_with_runs(&_all_runs[0], _run_pointer); + _run_pointer = 0; break; } } } +#pragma mark - delegate + +void CRT::set_crt_delegate(CRTDelegate *delegate) +{ + _delegate = delegate; +} + #pragma mark - stream feeding methods void CRT::output_sync(int number_of_cycles) { - bool _hsync_requested = !_is_in_sync; - _is_in_sync = true; - advance_cycles(number_of_cycles, _hsync_requested, true, CRTRun::Type::Sync); + bool _hsync_requested = !_is_receiving_sync; + _is_receiving_sync = true; + advance_cycles(number_of_cycles, _hsync_requested, true, CRTRun::Type::Sync, nullptr); } -void CRT::output_level(int number_of_cycles, std::string type) +void CRT::output_blank(int number_of_cycles) { - _is_in_sync = false; - advance_cycles(number_of_cycles, false, false, CRTRun::Type::Level); + _is_receiving_sync = false; + advance_cycles(number_of_cycles, false, false, CRTRun::Type::Blank, nullptr); } -void CRT::output_data(int number_of_cycles, std::string type) +void CRT::output_level(int number_of_cycles, const char *type) { - _is_in_sync = false; - advance_cycles(number_of_cycles, false, false, CRTRun::Type::Data); + _is_receiving_sync = false; + advance_cycles(number_of_cycles, false, false, CRTRun::Type::Level, type); } -void CRT::output_blank(int number_of_cycles, std::string type) +void CRT::output_data(int number_of_cycles, const char *type) { - _is_in_sync = false; - advance_cycles(number_of_cycles, false, false, CRTRun::Type::Blank); + _is_receiving_sync = false; + advance_cycles(number_of_cycles, false, false, CRTRun::Type::Data, type); } #pragma mark - Buffer supply diff --git a/Outputs/CRT.hpp b/Outputs/CRT.hpp index d63f09919..9a431f55e 100644 --- a/Outputs/CRT.hpp +++ b/Outputs/CRT.hpp @@ -11,44 +11,46 @@ #include #include -#include +#include namespace Outputs { class CRT { public: - CRT(int cycles_per_line, int number_of_buffers, ...); + CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...); ~CRT(); void output_sync(int number_of_cycles); - void output_level(int number_of_cycles, std::string type); - void output_blank(int number_of_cycles, std::string type); - void output_data(int number_of_cycles, std::string type); + void output_blank(int number_of_cycles); + void output_level(int number_of_cycles, const char *type); + void output_data(int number_of_cycles, const char *type); struct CRTRun { struct Point { - float x, y; + float dst_x, dst_y; + int src_x, src_y; } start_point, end_point; enum Type { Sync, Level, Data, Blank } type; - std::string data_type; - uint8_t *data; + const char *data_type; }; class CRTDelegate { public: - void crt_did_start_vertical_retrace_with_runs(std::forward_list *runs, int runs_to_draw); + virtual void crt_did_start_vertical_retrace_with_runs(CRTRun *runs, int runs_to_draw) = 0; }; - void set_crt_delegate(CRTDelegate *); + void set_crt_delegate(CRTDelegate *delegate); void allocate_write_area(int required_length); uint8_t *get_write_target_for_buffer(int buffer); private: CRTDelegate *_delegate; + std::vector _all_runs; + int _run_pointer; float _horizontalOffset, _verticalOffset; @@ -58,8 +60,6 @@ class CRT { int _write_allocation_pointer, _write_target_pointer; - std::forward_list _runs; - void propose_hsync(); void charge_vsync(int number_of_cycles); void drain_vsync(int number_of_cycles); @@ -69,6 +69,9 @@ class CRT { void do_vsync(); int _cycles_per_line; + int _height_of_display; + + int _hsync_counter; enum SyncEvent { None, @@ -76,11 +79,12 @@ class CRT { StartVSync, EndVSync }; SyncEvent advance_to_next_sync_event(bool hsync_is_requested, bool vsync_is_charging, int cycles_to_run_for, int *cycles_advanced); - bool _is_in_sync, _did_detect_hsync; + bool _is_receiving_sync, _did_detect_hsync; int _sync_capacitor_charge_level, _vretrace_counter; int _horizontal_counter, _expected_next_hsync, _hsync_error_window; + bool _is_in_hsync; - void advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_charging, CRTRun::Type type); + void advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_charging, CRTRun::Type type, const char *data_type); }; }