mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +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:
parent
5ab47e600a
commit
ecb2898bd5
@ -81,17 +81,7 @@ void Machine::output_state(OutputState state, uint8_t *pixel)
|
||||
{
|
||||
switch(_lastOutputState)
|
||||
{
|
||||
case OutputState::Blank: {
|
||||
_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::Blank: _crt->output_blank(_lastOutputStateDuration); break;
|
||||
case OutputState::Sync: _crt->output_sync(_lastOutputStateDuration); 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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -663,6 +664,7 @@
|
||||
children = (
|
||||
4B366DFA1B5C165A0026627B /* CRT.cpp */,
|
||||
4B366DFB1B5C165A0026627B /* CRT.hpp */,
|
||||
4B2632551B631A510082A461 /* CRTFrame.h */,
|
||||
);
|
||||
name = Outputs;
|
||||
sourceTree = "<group>";
|
||||
|
@ -19,8 +19,8 @@ const char *vertexShader =
|
||||
"\n"
|
||||
"void main (void)\n"
|
||||
"{\n"
|
||||
"colour = vec4(position, 0.0, 1.0);\n"
|
||||
"gl_Position = vec4(position, 0.0, 1.0);\n"
|
||||
"colour = vec4(1.0, 1.0, 1.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";
|
||||
|
||||
const char *fragmentShader =
|
||||
@ -35,12 +35,12 @@ const char *fragmentShader =
|
||||
"}\n";
|
||||
|
||||
@interface CSAtari2600 (Callbacks)
|
||||
- (void)crtDidEndFrame:(Outputs::CRTFrame *)frame;
|
||||
- (void)crtDidEndFrame:(CRTFrame *)frame;
|
||||
@end
|
||||
|
||||
struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
||||
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 {
|
||||
@ -48,7 +48,7 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
||||
Atari2600CRTDelegate _crtDelegate;
|
||||
|
||||
dispatch_queue_t _serialDispatchQueue;
|
||||
Outputs::CRTFrame *_queuedFrame;
|
||||
CRTFrame *_queuedFrame;
|
||||
|
||||
GLuint _vertexShader, _fragmentShader;
|
||||
GLuint _shaderProgram;
|
||||
@ -56,7 +56,7 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
||||
GLuint _arrayBuffer, _vertexArray;
|
||||
}
|
||||
|
||||
- (void)crtDidEndFrame:(Outputs::CRTFrame *)frame {
|
||||
- (void)crtDidEndFrame:(CRTFrame *)frame {
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if(_queuedFrame) {
|
||||
@ -95,14 +95,12 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
||||
glGenBuffers(1, &_arrayBuffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _arrayBuffer);
|
||||
|
||||
GLfloat vertices[] = {
|
||||
0.0f, 0.0f,
|
||||
0.5f, 0.0f,
|
||||
0.5f, 0.6f,
|
||||
0.0f, 0.5f
|
||||
GLushort vertices[] = {
|
||||
0, 0,
|
||||
32767, 32767,
|
||||
};
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
|
||||
}
|
||||
|
||||
if(_queuedFrame)
|
||||
@ -114,40 +112,12 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
|
||||
|
||||
GLint position = glGetAttribLocation(_shaderProgram, "position");
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, _queuedFrame->number_of_runs * sizeof(GLushort) * 8, _queuedFrame->runs, GL_DYNAMIC_DRAW);
|
||||
|
||||
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);
|
||||
|
||||
/* 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);*/
|
||||
glDrawArrays(GL_LINES, 0, _queuedFrame->number_of_runs*2);
|
||||
}
|
||||
}
|
||||
|
||||
|
190
Outputs/CRT.cpp
190
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;
|
||||
_vertical_retrace_time = scanlinesVerticalRetraceTime * cycles_per_line;
|
||||
|
||||
_scanSpeed.x = 1.0f / (float)cycles_per_line;
|
||||
_scanSpeed.y = 1.0f / (float)(height_of_display * cycles_per_line);
|
||||
_retraceSpeed.x = 1.0f / (float)_horizontal_retrace_time;
|
||||
_retraceSpeed.y = 1.0f / (float)_vertical_retrace_time;
|
||||
_scanSpeed.x = UINT32_MAX / cycles_per_line;
|
||||
_scanSpeed.y = UINT32_MAX / (height_of_display * cycles_per_line);
|
||||
_retraceSpeed.x = UINT32_MAX / _horizontal_retrace_time;
|
||||
_retraceSpeed.y = UINT32_MAX / _vertical_retrace_time;
|
||||
|
||||
// generate buffers for signal storage as requested — format is
|
||||
// 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_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);
|
||||
}
|
||||
_frames_with_delegate = 0;
|
||||
_frame_read_pointer = 0;
|
||||
_current_frame = _frames[0];
|
||||
_current_frame_builder = _frame_builders[0];
|
||||
|
||||
// reset raster position
|
||||
_rasterPosition.x = _rasterPosition.y = 0.0f;
|
||||
_rasterPosition.x = _rasterPosition.y = 0;
|
||||
|
||||
// reset flywheel sync
|
||||
_expected_next_hsync = cycles_per_line;
|
||||
@ -69,7 +69,7 @@ CRT::~CRT()
|
||||
{
|
||||
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;
|
||||
|
||||
// have we overrun the maximum permitted number of horizontal syncs for this frame?
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 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 an acceptable vertical sync be triggered?
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
int buffer_offset = 0;
|
||||
const bool is_output_run = ((type == Type::Level) || (type == Type::Data));
|
||||
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) {
|
||||
|
||||
@ -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)
|
||||
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
|
||||
nextRun->type = type;
|
||||
nextRun->start_point.dst_x = _rasterPosition.x;
|
||||
nextRun->start_point.dst_y = _rasterPosition.y;
|
||||
nextRun->data_type = data_type;
|
||||
next_run[0] = _rasterPosition.x >> 16;
|
||||
next_run[1] = _rasterPosition.y >> 16;
|
||||
next_run[2] = tex_x;
|
||||
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
|
||||
if (_is_in_hsync)
|
||||
_rasterPosition.x = (uint32_t)std::max((int64_t)0, (int64_t)_rasterPosition.x - number_of_cycles * (int64_t)_retraceSpeed.x);
|
||||
else
|
||||
_rasterPosition.x = (uint32_t)std::min((int64_t)UINT32_MAX, (int64_t)_rasterPosition.x + number_of_cycles * (int64_t)_scanSpeed.x);
|
||||
|
||||
// advance the raster position as dictated by current sync status
|
||||
if (_is_in_hsync)
|
||||
_rasterPosition.x = std::max(0.0f, _rasterPosition.x - (float)number_of_cycles * _retraceSpeed.x);
|
||||
else
|
||||
_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);
|
||||
if (_vretrace_counter > 0)
|
||||
_rasterPosition.y = (uint32_t)std::max((int64_t)0, (int64_t)_rasterPosition.y - number_of_cycles * (int64_t)_retraceSpeed.y);
|
||||
else
|
||||
_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
|
||||
nextRun->end_point.dst_x = _rasterPosition.x;
|
||||
nextRun->end_point.dst_y = _rasterPosition.y;
|
||||
next_run[4] = _rasterPosition.x >> 16;
|
||||
next_run[5] = _rasterPosition.y >> 16;
|
||||
|
||||
// if this is a data run then advance the buffer pointer
|
||||
if(type == CRTRun::Type::Data)
|
||||
{
|
||||
buffer_offset += next_run_length;
|
||||
}
|
||||
if(type == Type::Data) tex_x += next_run_length;
|
||||
|
||||
// if this is a data or level run then store the end point
|
||||
if(type == CRTRun::Type::Data || type == CRTRun::Type::Level)
|
||||
{
|
||||
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;
|
||||
}
|
||||
next_run[6] = tex_x;
|
||||
next_run[7] = tex_y;
|
||||
}
|
||||
|
||||
// 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,
|
||||
// releasing all runs back into the common pool
|
||||
case SyncEvent::EndVSync:
|
||||
if(_delegate && _current_frame)
|
||||
if(_delegate && _current_frame_builder)
|
||||
{
|
||||
_current_frame->complete();
|
||||
_current_frame_builder->complete();
|
||||
_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)
|
||||
{
|
||||
_frame_read_pointer = (_frame_read_pointer + 1)%kCRTNumberOfFrames;
|
||||
_current_frame = _frames[_frame_read_pointer];
|
||||
_current_frame->reset();
|
||||
_current_frame_builder = _frame_builders[_frame_read_pointer];
|
||||
_current_frame_builder->reset();
|
||||
}
|
||||
else
|
||||
_current_frame = nullptr;
|
||||
_current_frame_builder = nullptr;
|
||||
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
|
||||
_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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_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)
|
||||
{
|
||||
_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
|
||||
|
||||
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)
|
||||
{
|
||||
if (!_current_frame) return nullptr;
|
||||
return _current_frame->get_write_target_for_buffer(buffer);
|
||||
if (!_current_frame_builder) return nullptr;
|
||||
return _current_frame_builder->get_write_target_for_buffer(buffer);
|
||||
}
|
||||
|
||||
#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;
|
||||
size.height = height;
|
||||
this->number_of_buffers = number_of_buffers;
|
||||
buffers = new CRTBuffer[number_of_buffers];
|
||||
frame.size.width = width;
|
||||
frame.size.height = height;
|
||||
frame.number_of_buffers = number_of_buffers;
|
||||
frame.buffers = new CRTBuffer[number_of_buffers];
|
||||
|
||||
for(int buffer = 0; buffer < number_of_buffers; buffer++)
|
||||
{
|
||||
buffers[buffer].depth = va_arg(buffer_sizes, int);
|
||||
buffers[buffer].data = new uint8_t[width * height * buffers[buffer].depth];
|
||||
frame.buffers[buffer].depth = va_arg(buffer_sizes, int);
|
||||
frame.buffers[buffer].data = new uint8_t[width * height * frame.buffers[buffer].depth];
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
CRTFrame::~CRTFrame()
|
||||
CRTFrameBuilder::~CRTFrameBuilder()
|
||||
{
|
||||
for(int buffer = 0; buffer < number_of_buffers; buffer++)
|
||||
delete[] buffers[buffer].data;
|
||||
delete buffers;
|
||||
for(int buffer = 0; buffer < frame.number_of_buffers; buffer++)
|
||||
delete[] frame.buffers[buffer].data;
|
||||
delete frame.buffers;
|
||||
}
|
||||
|
||||
void CRTFrame::reset()
|
||||
void CRTFrameBuilder::reset()
|
||||
{
|
||||
number_of_runs = 0;
|
||||
_write_allocation_pointer = _write_target_pointer = 0;
|
||||
frame.number_of_runs = 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
|
||||
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];
|
||||
number_of_runs++;
|
||||
uint16_t *next_run = &_all_runs[frame.number_of_runs * 8];
|
||||
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 (xPos + required_length > size.width)
|
||||
if (_write_x_position + required_length > frame.size.width)
|
||||
{
|
||||
_write_allocation_pointer &= ~(size.width - 1);
|
||||
_write_allocation_pointer = (_write_allocation_pointer + size.width) & ((size.height-1) * size.width);
|
||||
_write_x_position = 0;
|
||||
_write_y_position++;
|
||||
frame.dirty_size.height++;
|
||||
}
|
||||
|
||||
_write_target_pointer = _write_allocation_pointer;
|
||||
_write_allocation_pointer += required_length;
|
||||
_write_target_pointer = (_write_y_position * frame.size.width) + _write_x_position;
|
||||
_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 <vector>
|
||||
|
||||
#include "CRTFrame.h"
|
||||
|
||||
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;
|
||||
struct CRTFrame {
|
||||
struct {
|
||||
int width, height;
|
||||
} size;
|
||||
struct CRTFrameBuilder {
|
||||
CRTFrame frame;
|
||||
|
||||
int number_of_buffers;
|
||||
CRTBuffer *buffers;
|
||||
|
||||
int number_of_runs;
|
||||
CRTRun *runs;
|
||||
|
||||
CRTFrame(int width, int height, int number_of_buffers, va_list buffer_sizes);
|
||||
~CRTFrame();
|
||||
CRTFrameBuilder(int width, int height, int number_of_buffers, va_list buffer_sizes);
|
||||
~CRTFrameBuilder();
|
||||
|
||||
private:
|
||||
std::vector<CRTRun> _all_runs;
|
||||
std::vector<uint16_t> _all_runs;
|
||||
|
||||
void reset();
|
||||
void complete();
|
||||
|
||||
CRTRun *get_next_run();
|
||||
uint16_t *get_next_run();
|
||||
friend CRT;
|
||||
|
||||
void allocate_write_area(int required_length);
|
||||
@ -63,7 +39,8 @@ struct CRTFrame {
|
||||
|
||||
// a pointer to the section of content buffer currently being
|
||||
// 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;
|
||||
@ -98,12 +75,12 @@ class CRT {
|
||||
|
||||
// the current scanning position
|
||||
struct Vector {
|
||||
float x, y;
|
||||
uint32_t x, y;
|
||||
} _rasterPosition, _scanSpeed, _retraceSpeed;
|
||||
|
||||
// the run delegate and the triple buffer
|
||||
CRTFrame *_frames[kCRTNumberOfFrames];
|
||||
CRTFrame *_current_frame;
|
||||
CRTFrameBuilder *_frame_builders[kCRTNumberOfFrames];
|
||||
CRTFrameBuilder *_current_frame_builder;
|
||||
int _frames_with_delegate;
|
||||
int _frame_read_pointer;
|
||||
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
|
||||
|
||||
// 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 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 */
|
Loading…
Reference in New Issue
Block a user