mirror of
https://github.com/TomHarte/CLK.git
synced 2025-03-08 16:29:45 +00:00
Made a quick attempt to accumulate a list of detected output runs. Which means finally having to specify normal frame height. I'm already at too many magic formulae though, so this will need proper revision when I'm next awake. Definitely my horizontal position advancement is way off.
This commit is contained in:
parent
4fa315ab4d
commit
a1a1b15d18
@ -11,6 +11,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
|
122
Outputs/CRT.cpp
122
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
|
||||
|
@ -11,44 +11,46 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <forward_list>
|
||||
#include <vector>
|
||||
|
||||
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<CRTRun> *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<CRTRun> _all_runs;
|
||||
int _run_pointer;
|
||||
|
||||
float _horizontalOffset, _verticalOffset;
|
||||
|
||||
@ -58,8 +60,6 @@ class CRT {
|
||||
|
||||
int _write_allocation_pointer, _write_target_pointer;
|
||||
|
||||
std::forward_list<CRTRun> _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);
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user