mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-13 00:25:26 +00:00
The overall architecture of who has responsibility for what is now very askew but: the CRT now outputs a tightly packed short buffer, with the probable OpenGL destination in mind. So it's now all fixed arithmetic internally. CRTFrame is reduced to a plain C struct with the intention that the OpenGL view will take responsibility for it and stop doing the back-and-forth sprint on getting buffer data. The Atari 2600 now outputs explicit blanks rather than level blacks for its border, so that it's easier visually to debug the CRT in its form as far as it has currently progressed: to drawing lines where the cathode ray gun would run while outputting pixel. I note that I'm still not quite getting vertical sync right yet — I'm just accepting it anywhere in teh frame — but that should be an easy fix.
This commit is contained in:
@@ -81,17 +81,7 @@ void Machine::output_state(OutputState state, uint8_t *pixel)
|
|||||||
{
|
{
|
||||||
switch(_lastOutputState)
|
switch(_lastOutputState)
|
||||||
{
|
{
|
||||||
case OutputState::Blank: {
|
case OutputState::Blank: _crt->output_blank(_lastOutputStateDuration); break;
|
||||||
_crt->allocate_write_area(1);
|
|
||||||
_outputBuffer = _crt->get_write_target_for_buffer(0);
|
|
||||||
|
|
||||||
if(_outputBuffer)
|
|
||||||
{
|
|
||||||
_outputBuffer[0] = _outputBuffer[1] = _outputBuffer[2] = 0;
|
|
||||||
_outputBuffer[3] = 0xff;
|
|
||||||
}
|
|
||||||
_crt->output_level(_lastOutputStateDuration, atari2600DataType);
|
|
||||||
} break;
|
|
||||||
case OutputState::Sync: _crt->output_sync(_lastOutputStateDuration); break;
|
case OutputState::Sync: _crt->output_sync(_lastOutputStateDuration); break;
|
||||||
case OutputState::Pixel: _crt->output_data(_lastOutputStateDuration, atari2600DataType); break;
|
case OutputState::Pixel: _crt->output_data(_lastOutputStateDuration, atari2600DataType); break;
|
||||||
}
|
}
|
||||||
|
@@ -322,6 +322,7 @@
|
|||||||
4B14145A1B58879D00E04248 /* CPU6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPU6502AllRAM.hpp; sourceTree = "<group>"; };
|
4B14145A1B58879D00E04248 /* CPU6502AllRAM.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CPU6502AllRAM.hpp; sourceTree = "<group>"; };
|
||||||
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WolfgangLorenzTests.swift; sourceTree = "<group>"; };
|
4B14145F1B58885000E04248 /* WolfgangLorenzTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WolfgangLorenzTests.swift; sourceTree = "<group>"; };
|
||||||
4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = "<group>"; };
|
4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = "<group>"; };
|
||||||
|
4B2632551B631A510082A461 /* CRTFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CRTFrame.h; path = ../../Outputs/CRTFrame.h; sourceTree = "<group>"; };
|
||||||
4B366DFA1B5C165A0026627B /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CRT.cpp; path = ../../Outputs/CRT.cpp; sourceTree = "<group>"; };
|
4B366DFA1B5C165A0026627B /* CRT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CRT.cpp; path = ../../Outputs/CRT.cpp; sourceTree = "<group>"; };
|
||||||
4B366DFB1B5C165A0026627B /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRT.hpp; path = ../../Outputs/CRT.hpp; sourceTree = "<group>"; };
|
4B366DFB1B5C165A0026627B /* CRT.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CRT.hpp; path = ../../Outputs/CRT.hpp; sourceTree = "<group>"; };
|
||||||
4B6D7F921B58822000787C9A /* Atari2600.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atari2600.cpp; sourceTree = "<group>"; };
|
4B6D7F921B58822000787C9A /* Atari2600.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atari2600.cpp; sourceTree = "<group>"; };
|
||||||
@@ -663,6 +664,7 @@
|
|||||||
children = (
|
children = (
|
||||||
4B366DFA1B5C165A0026627B /* CRT.cpp */,
|
4B366DFA1B5C165A0026627B /* CRT.cpp */,
|
||||||
4B366DFB1B5C165A0026627B /* CRT.hpp */,
|
4B366DFB1B5C165A0026627B /* CRT.hpp */,
|
||||||
|
4B2632551B631A510082A461 /* CRTFrame.h */,
|
||||||
);
|
);
|
||||||
name = Outputs;
|
name = Outputs;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@@ -19,8 +19,8 @@ const char *vertexShader =
|
|||||||
"\n"
|
"\n"
|
||||||
"void main (void)\n"
|
"void main (void)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
"colour = vec4(position, 0.0, 1.0);\n"
|
"colour = vec4(1.0, 1.0, 1.0, 1.0);\n"
|
||||||
"gl_Position = vec4(position, 0.0, 1.0);\n"
|
"gl_Position = vec4(position.x * 2.0 - 1.0, 1.0 - position.y * 2.0, 0.0, 1.0);\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
const char *fragmentShader =
|
const char *fragmentShader =
|
||||||
@@ -35,12 +35,12 @@ const char *fragmentShader =
|
|||||||
"}\n";
|
"}\n";
|
||||||
|
|
||||||
@interface CSAtari2600 (Callbacks)
|
@interface CSAtari2600 (Callbacks)
|
||||||
- (void)crtDidEndFrame:(Outputs::CRTFrame *)frame;
|
- (void)crtDidEndFrame:(CRTFrame *)frame;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
||||||
CSAtari2600 *atari;
|
CSAtari2600 *atari;
|
||||||
void crt_did_end_frame(Outputs::CRT *crt, Outputs::CRTFrame *frame) { [atari crtDidEndFrame:frame]; }
|
void crt_did_end_frame(Outputs::CRT *crt, CRTFrame *frame) { [atari crtDidEndFrame:frame]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@implementation CSAtari2600 {
|
@implementation CSAtari2600 {
|
||||||
@@ -48,7 +48,7 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
|||||||
Atari2600CRTDelegate _crtDelegate;
|
Atari2600CRTDelegate _crtDelegate;
|
||||||
|
|
||||||
dispatch_queue_t _serialDispatchQueue;
|
dispatch_queue_t _serialDispatchQueue;
|
||||||
Outputs::CRTFrame *_queuedFrame;
|
CRTFrame *_queuedFrame;
|
||||||
|
|
||||||
GLuint _vertexShader, _fragmentShader;
|
GLuint _vertexShader, _fragmentShader;
|
||||||
GLuint _shaderProgram;
|
GLuint _shaderProgram;
|
||||||
@@ -56,7 +56,7 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
|||||||
GLuint _arrayBuffer, _vertexArray;
|
GLuint _arrayBuffer, _vertexArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)crtDidEndFrame:(Outputs::CRTFrame *)frame {
|
- (void)crtDidEndFrame:(CRTFrame *)frame {
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
if(_queuedFrame) {
|
if(_queuedFrame) {
|
||||||
@@ -95,14 +95,12 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
|||||||
glGenBuffers(1, &_arrayBuffer);
|
glGenBuffers(1, &_arrayBuffer);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _arrayBuffer);
|
glBindBuffer(GL_ARRAY_BUFFER, _arrayBuffer);
|
||||||
|
|
||||||
GLfloat vertices[] = {
|
GLushort vertices[] = {
|
||||||
0.0f, 0.0f,
|
0, 0,
|
||||||
0.5f, 0.0f,
|
32767, 32767,
|
||||||
0.5f, 0.6f,
|
|
||||||
0.0f, 0.5f
|
|
||||||
};
|
};
|
||||||
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_queuedFrame)
|
if(_queuedFrame)
|
||||||
@@ -114,40 +112,12 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
|||||||
|
|
||||||
GLint position = glGetAttribLocation(_shaderProgram, "position");
|
GLint position = glGetAttribLocation(_shaderProgram, "position");
|
||||||
|
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, _queuedFrame->number_of_runs * sizeof(GLushort) * 8, _queuedFrame->runs, GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
glEnableVertexAttribArray(position);
|
glEnableVertexAttribArray(position);
|
||||||
glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, (void *)0);
|
glVertexAttribPointer(position, 2, GL_UNSIGNED_SHORT, GL_TRUE, 4 * sizeof(GLushort), (void *)0);
|
||||||
|
|
||||||
glDrawArrays(GL_LINES, 0, 4);
|
glDrawArrays(GL_LINES, 0, _queuedFrame->number_of_runs*2);
|
||||||
|
|
||||||
/* printf("\n\n===\n\n");
|
|
||||||
int c = 0;
|
|
||||||
for(int run = 0; run < _queuedFrame->number_of_runs; run++)
|
|
||||||
{
|
|
||||||
char character = ' ';
|
|
||||||
switch(_queuedFrame->runs[run].type)
|
|
||||||
{
|
|
||||||
case Outputs::CRTRun::Type::Sync: character = '<'; break;
|
|
||||||
case Outputs::CRTRun::Type::Level: character = '_'; break;
|
|
||||||
case Outputs::CRTRun::Type::Data: character = '-'; break;
|
|
||||||
case Outputs::CRTRun::Type::Blank: character = ' '; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_queuedFrame->runs[run].start_point.dst_x < 1.0 / 224.0)
|
|
||||||
{
|
|
||||||
printf("\n[%0.2f]: ", _queuedFrame->runs[run].start_point.dst_y);
|
|
||||||
c++;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("(%0.2f): ", _queuedFrame->runs[run].start_point.dst_x);
|
|
||||||
float length = fabsf(_queuedFrame->runs[run].end_point.dst_x - _queuedFrame->runs[run].start_point.dst_x);
|
|
||||||
int iLength = (int)(length * 64.0);
|
|
||||||
for(int c = 0; c < iLength; c++)
|
|
||||||
{
|
|
||||||
putc(character, stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\n\n[%d]\n\n", c);*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
178
Outputs/CRT.cpp
178
Outputs/CRT.cpp
@@ -29,10 +29,10 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
|
|||||||
_horizontal_retrace_time = (millisecondsHorizontalRetraceTime * cycles_per_line) >> 6;
|
_horizontal_retrace_time = (millisecondsHorizontalRetraceTime * cycles_per_line) >> 6;
|
||||||
_vertical_retrace_time = scanlinesVerticalRetraceTime * cycles_per_line;
|
_vertical_retrace_time = scanlinesVerticalRetraceTime * cycles_per_line;
|
||||||
|
|
||||||
_scanSpeed.x = 1.0f / (float)cycles_per_line;
|
_scanSpeed.x = UINT32_MAX / cycles_per_line;
|
||||||
_scanSpeed.y = 1.0f / (float)(height_of_display * cycles_per_line);
|
_scanSpeed.y = UINT32_MAX / (height_of_display * cycles_per_line);
|
||||||
_retraceSpeed.x = 1.0f / (float)_horizontal_retrace_time;
|
_retraceSpeed.x = UINT32_MAX / _horizontal_retrace_time;
|
||||||
_retraceSpeed.y = 1.0f / (float)_vertical_retrace_time;
|
_retraceSpeed.y = UINT32_MAX / _vertical_retrace_time;
|
||||||
|
|
||||||
// generate buffers for signal storage as requested — format is
|
// generate buffers for signal storage as requested — format is
|
||||||
// number of buffers, size of buffer 1, size of buffer 2...
|
// number of buffers, size of buffer 1, size of buffer 2...
|
||||||
@@ -42,15 +42,15 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
|
|||||||
{
|
{
|
||||||
va_list va;
|
va_list va;
|
||||||
va_start(va, number_of_buffers);
|
va_start(va, number_of_buffers);
|
||||||
_frames[frame] = new CRTFrame(bufferWidth, bufferHeight, number_of_buffers, va);
|
_frame_builders[frame] = new CRTFrameBuilder(bufferWidth, bufferHeight, number_of_buffers, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
}
|
}
|
||||||
_frames_with_delegate = 0;
|
_frames_with_delegate = 0;
|
||||||
_frame_read_pointer = 0;
|
_frame_read_pointer = 0;
|
||||||
_current_frame = _frames[0];
|
_current_frame_builder = _frame_builders[0];
|
||||||
|
|
||||||
// reset raster position
|
// reset raster position
|
||||||
_rasterPosition.x = _rasterPosition.y = 0.0f;
|
_rasterPosition.x = _rasterPosition.y = 0;
|
||||||
|
|
||||||
// reset flywheel sync
|
// reset flywheel sync
|
||||||
_expected_next_hsync = cycles_per_line;
|
_expected_next_hsync = cycles_per_line;
|
||||||
@@ -69,7 +69,7 @@ CRT::~CRT()
|
|||||||
{
|
{
|
||||||
for(int frame = 0; frame < 3; frame++)
|
for(int frame = 0; frame < 3; frame++)
|
||||||
{
|
{
|
||||||
delete _frames[frame];
|
delete _frame_builders[frame];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,14 +81,14 @@ CRT::SyncEvent CRT::next_vertical_sync_event(bool vsync_is_charging, int cycles_
|
|||||||
int proposedSyncTime = cycles_to_run_for;
|
int proposedSyncTime = cycles_to_run_for;
|
||||||
|
|
||||||
// have we overrun the maximum permitted number of horizontal syncs for this frame?
|
// have we overrun the maximum permitted number of horizontal syncs for this frame?
|
||||||
if (!_vretrace_counter)
|
// if (!_vretrace_counter)
|
||||||
{
|
// {
|
||||||
float raster_distance = _scanSpeed.y * (float)proposedSyncTime;
|
// float raster_distance = _scanSpeed.y * (float)proposedSyncTime;
|
||||||
if(_rasterPosition.y < 1.02f && _rasterPosition.y + raster_distance >= 1.02f) {
|
// if(_rasterPosition.y < 1.02f && _rasterPosition.y + raster_distance >= 1.02f) {
|
||||||
proposedSyncTime = (int)(1.02f - _rasterPosition.y) / _scanSpeed.y;
|
// proposedSyncTime = (int)(1.02f - _rasterPosition.y) / _scanSpeed.y;
|
||||||
proposedEvent = SyncEvent::StartVSync;
|
// proposedEvent = SyncEvent::StartVSync;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// will an acceptable vertical sync be triggered?
|
// will an acceptable vertical sync be triggered?
|
||||||
if (vsync_is_charging && !_vretrace_counter) {
|
if (vsync_is_charging && !_vretrace_counter) {
|
||||||
@@ -141,10 +141,16 @@ CRT::SyncEvent CRT::next_horizontal_sync_event(bool hsync_is_requested, int cycl
|
|||||||
return proposedEvent;
|
return proposedEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool vsync_charging, const CRTRun::Type type, const char *data_type)
|
void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool vsync_charging, const Type type, const char *data_type)
|
||||||
{
|
{
|
||||||
// this is safe to keep locally because it accumulates over this run of cycles only
|
const bool is_output_run = ((type == Type::Level) || (type == Type::Data));
|
||||||
int buffer_offset = 0;
|
uint16_t tex_x = 0;
|
||||||
|
uint16_t tex_y = 0;
|
||||||
|
|
||||||
|
if(is_output_run && _current_frame_builder) {
|
||||||
|
tex_x = _current_frame_builder->_write_x_position;
|
||||||
|
tex_y = _current_frame_builder->_write_y_position;
|
||||||
|
}
|
||||||
|
|
||||||
while(number_of_cycles) {
|
while(number_of_cycles) {
|
||||||
|
|
||||||
@@ -157,50 +163,40 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool
|
|||||||
// set it to false for the next run through this loop (if any)
|
// set it to false for the next run through this loop (if any)
|
||||||
int next_run_length = std::min(time_until_vertical_sync_event, time_until_horizontal_sync_event);
|
int next_run_length = std::min(time_until_vertical_sync_event, time_until_horizontal_sync_event);
|
||||||
|
|
||||||
CRTRun *nextRun = (_current_frame && next_run_length) ? _current_frame->get_next_run() : nullptr;
|
uint16_t *next_run = (is_output_run && _current_frame_builder && next_run_length) ? _current_frame_builder->get_next_run() : nullptr;
|
||||||
|
|
||||||
if(nextRun)
|
if(next_run)
|
||||||
{
|
{
|
||||||
// set the type, initial raster position and type of this run
|
// set the type, initial raster position and type of this run
|
||||||
nextRun->type = type;
|
next_run[0] = _rasterPosition.x >> 16;
|
||||||
nextRun->start_point.dst_x = _rasterPosition.x;
|
next_run[1] = _rasterPosition.y >> 16;
|
||||||
nextRun->start_point.dst_y = _rasterPosition.y;
|
next_run[2] = tex_x;
|
||||||
nextRun->data_type = data_type;
|
next_run[3] = tex_y;
|
||||||
|
|
||||||
// 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 = (_current_frame->_write_target_pointer + buffer_offset) & (_current_frame->size.width - 1);
|
|
||||||
nextRun->start_point.src_y = (_current_frame->_write_target_pointer + buffer_offset) / _current_frame->size.width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// advance the raster position as dictated by current sync status
|
// advance the raster position as dictated by current sync status
|
||||||
if (_is_in_hsync)
|
if (_is_in_hsync)
|
||||||
_rasterPosition.x = std::max(0.0f, _rasterPosition.x - (float)number_of_cycles * _retraceSpeed.x);
|
_rasterPosition.x = (uint32_t)std::max((int64_t)0, (int64_t)_rasterPosition.x - number_of_cycles * (int64_t)_retraceSpeed.x);
|
||||||
else
|
else
|
||||||
_rasterPosition.x = std::min(1.0f, _rasterPosition.x + (float)number_of_cycles * _scanSpeed.x);
|
_rasterPosition.x = (uint32_t)std::min((int64_t)UINT32_MAX, (int64_t)_rasterPosition.x + number_of_cycles * (int64_t)_scanSpeed.x);
|
||||||
|
|
||||||
if (_vretrace_counter > 0)
|
if (_vretrace_counter > 0)
|
||||||
_rasterPosition.y = std::max(0.0f, _rasterPosition.y - (float)number_of_cycles * _retraceSpeed.y);
|
_rasterPosition.y = (uint32_t)std::max((int64_t)0, (int64_t)_rasterPosition.y - number_of_cycles * (int64_t)_retraceSpeed.y);
|
||||||
else
|
else
|
||||||
_rasterPosition.y = std::min(1.0f, _rasterPosition.y + (float)number_of_cycles * _scanSpeed.y);
|
_rasterPosition.y = (uint32_t)std::min((int64_t)UINT32_MAX, (int64_t)_rasterPosition.y + number_of_cycles * (int64_t)_scanSpeed.y);
|
||||||
|
|
||||||
|
if(next_run)
|
||||||
|
{
|
||||||
// store the final raster position
|
// store the final raster position
|
||||||
nextRun->end_point.dst_x = _rasterPosition.x;
|
next_run[4] = _rasterPosition.x >> 16;
|
||||||
nextRun->end_point.dst_y = _rasterPosition.y;
|
next_run[5] = _rasterPosition.y >> 16;
|
||||||
|
|
||||||
// if this is a data run then advance the buffer pointer
|
// if this is a data run then advance the buffer pointer
|
||||||
if(type == CRTRun::Type::Data)
|
if(type == Type::Data) tex_x += next_run_length;
|
||||||
{
|
|
||||||
buffer_offset += next_run_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is a data or level run then store the end point
|
// if this is a data or level run then store the end point
|
||||||
if(type == CRTRun::Type::Data || type == CRTRun::Type::Level)
|
next_run[6] = tex_x;
|
||||||
{
|
next_run[7] = tex_y;
|
||||||
nextRun->end_point.src_x = (_current_frame->_write_target_pointer + buffer_offset) & (_current_frame->size.width - 1);
|
|
||||||
nextRun->end_point.src_y = (_current_frame->_write_target_pointer + buffer_offset) / _current_frame->size.width;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrement the number of cycles left to run for and increment the
|
// decrement the number of cycles left to run for and increment the
|
||||||
@@ -256,21 +252,21 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool
|
|||||||
// end of vertical sync: tell the delegate that we finished vertical sync,
|
// end of vertical sync: tell the delegate that we finished vertical sync,
|
||||||
// releasing all runs back into the common pool
|
// releasing all runs back into the common pool
|
||||||
case SyncEvent::EndVSync:
|
case SyncEvent::EndVSync:
|
||||||
if(_delegate && _current_frame)
|
if(_delegate && _current_frame_builder)
|
||||||
{
|
{
|
||||||
_current_frame->complete();
|
_current_frame_builder->complete();
|
||||||
_frames_with_delegate++;
|
_frames_with_delegate++;
|
||||||
_delegate->crt_did_end_frame(this, _current_frame);
|
_delegate->crt_did_end_frame(this, &_current_frame_builder->frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_frames_with_delegate < kCRTNumberOfFrames)
|
if(_frames_with_delegate < kCRTNumberOfFrames)
|
||||||
{
|
{
|
||||||
_frame_read_pointer = (_frame_read_pointer + 1)%kCRTNumberOfFrames;
|
_frame_read_pointer = (_frame_read_pointer + 1)%kCRTNumberOfFrames;
|
||||||
_current_frame = _frames[_frame_read_pointer];
|
_current_frame_builder = _frame_builders[_frame_read_pointer];
|
||||||
_current_frame->reset();
|
_current_frame_builder->reset();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_current_frame = nullptr;
|
_current_frame_builder = nullptr;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: break;
|
default: break;
|
||||||
@@ -300,104 +296,106 @@ void CRT::output_sync(int number_of_cycles)
|
|||||||
{
|
{
|
||||||
bool _hsync_requested = !_is_receiving_sync; // ensure this really is edge triggered; someone calling output_sync twice in succession shouldn't trigger it twice
|
bool _hsync_requested = !_is_receiving_sync; // ensure this really is edge triggered; someone calling output_sync twice in succession shouldn't trigger it twice
|
||||||
_is_receiving_sync = true;
|
_is_receiving_sync = true;
|
||||||
advance_cycles(number_of_cycles, _hsync_requested, true, CRTRun::Type::Sync, nullptr);
|
advance_cycles(number_of_cycles, _hsync_requested, true, Type::Sync, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRT::output_blank(int number_of_cycles)
|
void CRT::output_blank(int number_of_cycles)
|
||||||
{
|
{
|
||||||
_is_receiving_sync = false;
|
_is_receiving_sync = false;
|
||||||
advance_cycles(number_of_cycles, false, false, CRTRun::Type::Blank, nullptr);
|
advance_cycles(number_of_cycles, false, false, Type::Blank, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRT::output_level(int number_of_cycles, const char *type)
|
void CRT::output_level(int number_of_cycles, const char *type)
|
||||||
{
|
{
|
||||||
_is_receiving_sync = false;
|
_is_receiving_sync = false;
|
||||||
advance_cycles(number_of_cycles, false, false, CRTRun::Type::Level, type);
|
advance_cycles(number_of_cycles, false, false, Type::Level, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRT::output_data(int number_of_cycles, const char *type)
|
void CRT::output_data(int number_of_cycles, const char *type)
|
||||||
{
|
{
|
||||||
_is_receiving_sync = false;
|
_is_receiving_sync = false;
|
||||||
advance_cycles(number_of_cycles, false, false, CRTRun::Type::Data, type);
|
advance_cycles(number_of_cycles, false, false, Type::Data, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Buffer supply
|
#pragma mark - Buffer supply
|
||||||
|
|
||||||
void CRT::allocate_write_area(int required_length)
|
void CRT::allocate_write_area(int required_length)
|
||||||
{
|
{
|
||||||
if(_current_frame) _current_frame->allocate_write_area(required_length);
|
if(_current_frame_builder) _current_frame_builder->allocate_write_area(required_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *CRT::get_write_target_for_buffer(int buffer)
|
uint8_t *CRT::get_write_target_for_buffer(int buffer)
|
||||||
{
|
{
|
||||||
if (!_current_frame) return nullptr;
|
if (!_current_frame_builder) return nullptr;
|
||||||
return _current_frame->get_write_target_for_buffer(buffer);
|
return _current_frame_builder->get_write_target_for_buffer(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - CRTFrame
|
#pragma mark - CRTFrame
|
||||||
|
|
||||||
CRTFrame::CRTFrame(int width, int height, int number_of_buffers, va_list buffer_sizes)
|
CRTFrameBuilder::CRTFrameBuilder(int width, int height, int number_of_buffers, va_list buffer_sizes)
|
||||||
{
|
{
|
||||||
size.width = width;
|
frame.size.width = width;
|
||||||
size.height = height;
|
frame.size.height = height;
|
||||||
this->number_of_buffers = number_of_buffers;
|
frame.number_of_buffers = number_of_buffers;
|
||||||
buffers = new CRTBuffer[number_of_buffers];
|
frame.buffers = new CRTBuffer[number_of_buffers];
|
||||||
|
|
||||||
for(int buffer = 0; buffer < number_of_buffers; buffer++)
|
for(int buffer = 0; buffer < number_of_buffers; buffer++)
|
||||||
{
|
{
|
||||||
buffers[buffer].depth = va_arg(buffer_sizes, int);
|
frame.buffers[buffer].depth = va_arg(buffer_sizes, int);
|
||||||
buffers[buffer].data = new uint8_t[width * height * buffers[buffer].depth];
|
frame.buffers[buffer].data = new uint8_t[width * height * frame.buffers[buffer].depth];
|
||||||
}
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
CRTFrame::~CRTFrame()
|
CRTFrameBuilder::~CRTFrameBuilder()
|
||||||
{
|
{
|
||||||
for(int buffer = 0; buffer < number_of_buffers; buffer++)
|
for(int buffer = 0; buffer < frame.number_of_buffers; buffer++)
|
||||||
delete[] buffers[buffer].data;
|
delete[] frame.buffers[buffer].data;
|
||||||
delete buffers;
|
delete frame.buffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRTFrame::reset()
|
void CRTFrameBuilder::reset()
|
||||||
{
|
{
|
||||||
number_of_runs = 0;
|
frame.number_of_runs = 0;
|
||||||
_write_allocation_pointer = _write_target_pointer = 0;
|
_write_x_position = _write_y_position = 0;
|
||||||
|
frame.dirty_size.width = frame.dirty_size.height = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRTFrame::complete()
|
void CRTFrameBuilder::complete()
|
||||||
{
|
{
|
||||||
runs = &_all_runs[0];
|
frame.runs = &_all_runs[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
CRTRun *CRTFrame::get_next_run()
|
uint16_t *CRTFrameBuilder::get_next_run()
|
||||||
{
|
{
|
||||||
// get a run from the allocated list, allocating more if we're about to overrun
|
// get a run from the allocated list, allocating more if we're about to overrun
|
||||||
if(number_of_runs >= _all_runs.size())
|
if(frame.number_of_runs * 8 >= _all_runs.size())
|
||||||
{
|
{
|
||||||
_all_runs.resize((_all_runs.size() * 2)+1);
|
_all_runs.resize(_all_runs.size() + 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
CRTRun *nextRun = &_all_runs[number_of_runs];
|
uint16_t *next_run = &_all_runs[frame.number_of_runs * 8];
|
||||||
number_of_runs++;
|
frame.number_of_runs++;
|
||||||
|
|
||||||
return nextRun;
|
return next_run;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRTFrame::allocate_write_area(int required_length)
|
void CRTFrameBuilder::allocate_write_area(int required_length)
|
||||||
{
|
{
|
||||||
int xPos = _write_allocation_pointer & (size.width - 1);
|
if (_write_x_position + required_length > frame.size.width)
|
||||||
if (xPos + required_length > size.width)
|
|
||||||
{
|
{
|
||||||
_write_allocation_pointer &= ~(size.width - 1);
|
_write_x_position = 0;
|
||||||
_write_allocation_pointer = (_write_allocation_pointer + size.width) & ((size.height-1) * size.width);
|
_write_y_position++;
|
||||||
|
frame.dirty_size.height++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_write_target_pointer = _write_allocation_pointer;
|
_write_target_pointer = (_write_y_position * frame.size.width) + _write_x_position;
|
||||||
_write_allocation_pointer += required_length;
|
_write_x_position += required_length;
|
||||||
|
frame.dirty_size.width = std::max(frame.dirty_size.width, _write_x_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *CRTFrame::get_write_target_for_buffer(int buffer)
|
uint8_t *CRTFrameBuilder::get_write_target_for_buffer(int buffer)
|
||||||
{
|
{
|
||||||
return &buffers[buffer].data[_write_target_pointer * buffers[buffer].depth];
|
return &frame.buffers[buffer].data[_write_target_pointer * frame.buffers[buffer].depth];
|
||||||
}
|
}
|
||||||
|
@@ -14,48 +14,24 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "CRTFrame.h"
|
||||||
|
|
||||||
namespace Outputs {
|
namespace Outputs {
|
||||||
|
|
||||||
struct CRTBuffer {
|
|
||||||
uint8_t *data;
|
|
||||||
int depth;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CRTRun {
|
|
||||||
struct Point {
|
|
||||||
float dst_x, dst_y;
|
|
||||||
int src_x, src_y;
|
|
||||||
} start_point, end_point;
|
|
||||||
|
|
||||||
enum Type {
|
|
||||||
Sync, Level, Data, Blank
|
|
||||||
} type;
|
|
||||||
|
|
||||||
const char *data_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CRT;
|
class CRT;
|
||||||
struct CRTFrame {
|
struct CRTFrameBuilder {
|
||||||
struct {
|
CRTFrame frame;
|
||||||
int width, height;
|
|
||||||
} size;
|
|
||||||
|
|
||||||
int number_of_buffers;
|
CRTFrameBuilder(int width, int height, int number_of_buffers, va_list buffer_sizes);
|
||||||
CRTBuffer *buffers;
|
~CRTFrameBuilder();
|
||||||
|
|
||||||
int number_of_runs;
|
|
||||||
CRTRun *runs;
|
|
||||||
|
|
||||||
CRTFrame(int width, int height, int number_of_buffers, va_list buffer_sizes);
|
|
||||||
~CRTFrame();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<CRTRun> _all_runs;
|
std::vector<uint16_t> _all_runs;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void complete();
|
void complete();
|
||||||
|
|
||||||
CRTRun *get_next_run();
|
uint16_t *get_next_run();
|
||||||
friend CRT;
|
friend CRT;
|
||||||
|
|
||||||
void allocate_write_area(int required_length);
|
void allocate_write_area(int required_length);
|
||||||
@@ -63,7 +39,8 @@ struct CRTFrame {
|
|||||||
|
|
||||||
// a pointer to the section of content buffer currently being
|
// a pointer to the section of content buffer currently being
|
||||||
// returned and to where the next section will begin
|
// returned and to where the next section will begin
|
||||||
int _write_allocation_pointer, _write_target_pointer;
|
int _write_x_position, _write_y_position;
|
||||||
|
int _write_target_pointer;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int kCRTNumberOfFrames = 3;
|
static const int kCRTNumberOfFrames = 3;
|
||||||
@@ -98,12 +75,12 @@ class CRT {
|
|||||||
|
|
||||||
// the current scanning position
|
// the current scanning position
|
||||||
struct Vector {
|
struct Vector {
|
||||||
float x, y;
|
uint32_t x, y;
|
||||||
} _rasterPosition, _scanSpeed, _retraceSpeed;
|
} _rasterPosition, _scanSpeed, _retraceSpeed;
|
||||||
|
|
||||||
// the run delegate and the triple buffer
|
// the run delegate and the triple buffer
|
||||||
CRTFrame *_frames[kCRTNumberOfFrames];
|
CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames];
|
||||||
CRTFrame *_current_frame;
|
CRTFrameBuilder *_current_frame_builder;
|
||||||
int _frames_with_delegate;
|
int _frames_with_delegate;
|
||||||
int _frame_read_pointer;
|
int _frame_read_pointer;
|
||||||
CRTDelegate *_delegate;
|
CRTDelegate *_delegate;
|
||||||
@@ -123,7 +100,10 @@ class CRT {
|
|||||||
bool _is_in_hsync; // true for the duration of a horizontal sync — used to determine beam running direction and speed
|
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
|
// the outer entry point for dispatching output_sync, output_blank, output_level and output_data
|
||||||
void advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_charging, CRTRun::Type type, const char *data_type);
|
enum Type {
|
||||||
|
Sync, Level, Data, Blank
|
||||||
|
} type;
|
||||||
|
void advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_charging, Type type, const char *data_type);
|
||||||
|
|
||||||
// the inner entry point that determines whether and when the next sync event will occur within
|
// the inner entry point that determines whether and when the next sync event will occur within
|
||||||
// the current output window
|
// the current output window
|
||||||
|
39
Outputs/CRTFrame.h
Normal file
39
Outputs/CRTFrame.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// CRTFrame.h
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 24/07/2015.
|
||||||
|
// Copyright © 2015 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CRTFrame_h
|
||||||
|
#define CRTFrame_h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct CRTBuffer {
|
||||||
|
uint8_t *data;
|
||||||
|
int depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int width, height;
|
||||||
|
} CRTSize;
|
||||||
|
|
||||||
|
struct CRTFrame {
|
||||||
|
CRTSize size, dirty_size;
|
||||||
|
|
||||||
|
int number_of_buffers;
|
||||||
|
CRTBuffer *buffers;
|
||||||
|
|
||||||
|
int number_of_runs;
|
||||||
|
uint16_t *runs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* CRTFrame_h */
|
Reference in New Issue
Block a user