From 963cb2f6fbb7808a8e57bfd23d98d27a38cd3d4c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 22 Jul 2015 18:15:18 -0400 Subject: [PATCH] Attempted to switch to slightly more meaningful names within the CRT and implemented a delegate to investigate output. Working on it. --- Machines/Atari2600.hpp | 2 + OSBindings/Mac/Clock Signal/Atari2600.mm | 44 ++++++++++ .../Base.lproj/Atari2600Document.xib | 6 +- Outputs/CRT.cpp | 82 +++++++++---------- Outputs/CRT.hpp | 22 ++--- 5 files changed, 101 insertions(+), 55 deletions(-) diff --git a/Machines/Atari2600.hpp b/Machines/Atari2600.hpp index 75eeda41a..9b105500f 100644 --- a/Machines/Atari2600.hpp +++ b/Machines/Atari2600.hpp @@ -24,6 +24,8 @@ class Machine: public CPU6502::Processor { void set_rom(size_t length, const uint8_t *data); + Outputs::CRT *get_crt() { return _crt; } + private: uint8_t _rom[4096], _ram[128]; uint16_t _romMask; diff --git a/OSBindings/Mac/Clock Signal/Atari2600.mm b/OSBindings/Mac/Clock Signal/Atari2600.mm index 622d8e1d6..37fda8e99 100644 --- a/OSBindings/Mac/Clock Signal/Atari2600.mm +++ b/OSBindings/Mac/Clock Signal/Atari2600.mm @@ -9,8 +9,42 @@ #import "Atari2600.h" #import "Atari2600.hpp" +class Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate { + void crt_did_start_vertical_retrace_with_runs(Outputs::CRT::CRTRun *runs, int runs_to_draw) + { + printf("===\n\n"); + for(int run = 0; run < runs_to_draw; run++) + { + char character = ' '; + switch(runs[run].type) + { + case Outputs::CRT::CRTRun::Type::Sync: character = '<'; break; + case Outputs::CRT::CRTRun::Type::Level: character = '_'; break; + case Outputs::CRT::CRTRun::Type::Data: character = '-'; break; + case Outputs::CRT::CRTRun::Type::Blank: character = ' '; break; + } + +// if(runs[run].start_point.dst_x > runs[run].end_point.dst_x) +// { +// printf("\n"); +// } + + float length = fabsf(runs[run].end_point.dst_x - runs[run].start_point.dst_x); + int iLength = (int)(length * 64.0); + for(int c = 0; c < iLength; c++) + { + putc(character, stdout); + } + + if (runs[run].type == Outputs::CRT::CRTRun::Type::Sync) printf("\n"); + } + } + +}; + @implementation CSAtari2600 { Atari2600::Machine _atari2600; + Atari2600CRTDelegate _crtDelegate; } - (void)runForNumberOfCycles:(int)cycles { @@ -21,4 +55,14 @@ _atari2600.set_rom(rom.length, (const uint8_t *)rom.bytes); } +- (instancetype)init { + self = [super init]; + + if (self) { + _atari2600.get_crt()->set_delegate(&_crtDelegate); + } + + return self; +} + @end diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/Atari2600Document.xib b/OSBindings/Mac/Clock Signal/Base.lproj/Atari2600Document.xib index c74f9d9f5..d9555a642 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/Atari2600Document.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/Atari2600Document.xib @@ -1,7 +1,7 @@ - + - + @@ -12,7 +12,7 @@ - + diff --git a/Outputs/CRT.cpp b/Outputs/CRT.cpp index dfc48370d..53cf5a6dd 100644 --- a/Outputs/CRT.cpp +++ b/Outputs/CRT.cpp @@ -12,14 +12,14 @@ static const int bufferWidth = 512; static const int bufferHeight = 512; -static const int syncCapacityLineChargeThreshold = 3; -static const int millisecondsHorizontalRetraceTime = 16; -static const int scanlinesVerticalRetraceTime = 26; - using namespace Outputs; CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...) { + static const int syncCapacityLineChargeThreshold = 3; + static const int millisecondsHorizontalRetraceTime = 16; + static const int scanlinesVerticalRetraceTime = 26; + // store fundamental display configuration properties _height_of_display = height_of_display; _cycles_per_line = cycles_per_line; @@ -27,6 +27,15 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...) // generate timing values implied by the given arbuments _hsync_error_window = cycles_per_line >> 5; + _sync_capacitor_charge_threshold = syncCapacityLineChargeThreshold * cycles_per_line; + _horizontal_retrace_time = (millisecondsHorizontalRetraceTime * cycles_per_line) >> 6; + _vertical_retrace_time = scanlinesVerticalRetraceTime * cycles_per_line; + + _scanSpeed.x = 1.0f / (float)cycles_per_line; + _scanSpeed.y = 1.0f / (float)height_of_display; + _retraceSpeed.x = 1.0f / (float)_horizontal_retrace_time; + _retraceSpeed.y = 1.0f / (float)_vertical_retrace_time; + // generate buffers for signal storage as requested — format is // number of buffers, size of buffer 1, size of buffer 2... _numberOfBuffers = number_of_buffers; @@ -49,8 +58,7 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...) _run_pointer = 0; // reset raster position - _horizontalOffset = 0.0f; - _verticalOffset = 0.0f; + _rasterPosition.x = _rasterPosition.y = 0.0f; // reset flywheel sync _expected_next_hsync = cycles_per_line; @@ -62,6 +70,7 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...) // start off not in horizontal sync, not receiving a sync signal _is_receiving_sync = false; _is_in_hsync = false; + _vretrace_counter = 0; } CRT::~CRT() @@ -91,15 +100,18 @@ CRT::SyncEvent CRT::advance_to_next_sync_event(bool hsync_is_requested, bool vsy 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; + if (!_vretrace_counter) + { + float raster_distance = _scanSpeed.y * (float)proposedSyncTime; + if(_rasterPosition.y < 1.02f && _rasterPosition.y + raster_distance >= 1.02f) { + proposedSyncTime = (int)(1.02f - _rasterPosition.y) / _scanSpeed.y; + proposedEvent = SyncEvent::StartVSync; + } } // will we end an ongoing hsync? - const int endOfHSyncTime = (millisecondsHorizontalRetraceTime*_cycles_per_line) >> 6; - if (_horizontal_counter < endOfHSyncTime && _horizontal_counter+proposedSyncTime >= endOfHSyncTime) { - proposedSyncTime = endOfHSyncTime - _horizontal_counter; + if (_horizontal_counter < _horizontal_retrace_time && _horizontal_counter+proposedSyncTime >= _horizontal_retrace_time) { + proposedSyncTime = _horizontal_retrace_time - _horizontal_counter; proposedEvent = SyncEvent::EndHSync; } @@ -111,10 +123,8 @@ CRT::SyncEvent CRT::advance_to_next_sync_event(bool hsync_is_requested, bool vsy // will an acceptable vertical sync be triggered? if (vsync_is_charging && !_vretrace_counter) { - const int startOfVSyncTime = syncCapacityLineChargeThreshold*_cycles_per_line; - - if (_sync_capacitor_charge_level < startOfVSyncTime && _sync_capacitor_charge_level + proposedSyncTime >= startOfVSyncTime) { - proposedSyncTime = startOfVSyncTime - _sync_capacitor_charge_level; + if (_sync_capacitor_charge_level < _sync_capacitor_charge_threshold && _sync_capacitor_charge_level + proposedSyncTime >= _sync_capacitor_charge_threshold) { + proposedSyncTime = _sync_capacitor_charge_threshold - _sync_capacitor_charge_level; proposedEvent = SyncEvent::StartVSync; } } @@ -155,39 +165,31 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool // set the type, initial raster position and type of this run nextRun->type = type; - nextRun->start_point.dst_x = _horizontalOffset; - nextRun->start_point.dst_y = _verticalOffset; + nextRun->start_point.dst_x = _rasterPosition.x; + nextRun->start_point.dst_y = _rasterPosition.y; nextRun->data_type = data_type; // if this is a data or level run then store a starting data position 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->start_point.src_y = (_write_target_pointer + buffer_offset) / bufferWidth; } // advance the raster position as dictated by current sync status - 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); - } + _rasterPosition.x = std::max(0.0f, _rasterPosition.x - (float)number_of_cycles * _retraceSpeed.x); else - { - _horizontalOffset = std::min(1.0f, _horizontalOffset + (float)((((64 - millisecondsHorizontalRetraceTime) * _cycles_per_line) >> 6) * number_of_cycles) / (float)_cycles_per_line); - } + _rasterPosition.x = std::min(1.0f, _rasterPosition.x + (float)number_of_cycles * _scanSpeed.x); + + if (_vretrace_counter > 0) + _rasterPosition.y = std::max(0.0f, _rasterPosition.y - (float)number_of_cycles * _retraceSpeed.y); + else + _rasterPosition.y = std::min(1.0f, _rasterPosition.y + (float)number_of_cycles * _scanSpeed.y); // store the final raster position - nextRun->end_point.dst_x = _horizontalOffset; - nextRun->end_point.dst_y = _verticalOffset; + nextRun->end_point.dst_x = _rasterPosition.x; + nextRun->end_point.dst_y = _rasterPosition.y; // if this is a data run then advance the buffer pointer if(type == CRTRun::Type::Data) @@ -199,7 +201,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool 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; + nextRun->end_point.src_y = (_write_target_pointer + buffer_offset) / bufferWidth; } // decrement the number of cycles left to run for and increment the @@ -224,7 +226,6 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool case SyncEvent::StartHSync: _horizontal_counter = 0; _is_in_hsync = true; - _hsync_counter++; break; // end of horizontal sync: update the flywheel's velocity, note that we're no longer @@ -240,8 +241,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool // 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: - _vretrace_counter = scanlinesVerticalRetraceTime * _cycles_per_line; - _hsync_counter = 0; + _vretrace_counter = _vertical_retrace_time; break; // end of vertical sync: tell the delegate that we finished vertical sync, @@ -259,7 +259,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool #pragma mark - delegate -void CRT::set_crt_delegate(CRTDelegate *delegate) +void CRT::set_delegate(CRTDelegate *delegate) { _delegate = delegate; } diff --git a/Outputs/CRT.hpp b/Outputs/CRT.hpp index 799473ffa..6f7eae2d2 100644 --- a/Outputs/CRT.hpp +++ b/Outputs/CRT.hpp @@ -42,7 +42,7 @@ class CRT { public: virtual void crt_did_start_vertical_retrace_with_runs(CRTRun *runs, int runs_to_draw) = 0; }; - void set_crt_delegate(CRTDelegate *delegate); + void set_delegate(CRTDelegate *delegate); void allocate_write_area(int required_length); uint8_t *get_write_target_for_buffer(int buffer); @@ -61,7 +61,9 @@ class CRT { int _run_pointer; // the current scanning position - float _horizontalOffset, _verticalOffset; + struct Vector { + float x, y; + } _rasterPosition, _scanSpeed, _retraceSpeed; // the content buffers uint8_t **_buffers; @@ -72,20 +74,18 @@ class CRT { // returned and to where the next section will begin int _write_allocation_pointer, _write_target_pointer; - // a counter of horizontal syncs, to allow an automatic vertical - // sync to be triggered if we appear to be exiting the display - // (TODO: switch to evaluating _verticalOffset for this) - int _hsync_counter; - // outer 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 _vretrace_counter; // a down-counter for time during a vertical retrace + 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 _vretrace_counter; // a down-counter for time during a vertical retrace + int _vertical_retrace_time; // components of the flywheel sync int _horizontal_counter; // time run since the _start_ of the last horizontal sync int _expected_next_hsync; // our current expection of when the next horizontal sync will be encountered (which implies current flywheel velocity) + int _horizontal_retrace_time; bool _is_in_hsync; // true for the duration of a horizontal sync — used to determine beam running direction and speed // the outer entry point for dispatching output_sync, output_blank, output_level and output_data