1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-04 18:29:40 +00:00

Factored out a few more constants, started trying to ensure there's enough slack and the mechanisms in place for the CathodeRayView to hold onto two frames if it desires, for potential phosphor simulation, switched once again to additive blending — much more like a real CRT — and added a sine function across the width of spans per my understanding of how an electron gun actually fires.

Why do all this when overall timing is still so far off? It helps me more easily see how overall timing is so far off.
This commit is contained in:
Thomas Harte 2015-07-31 17:47:10 -04:00
parent c1d1fb65cb
commit a5d66e9dd6
6 changed files with 28 additions and 20 deletions

View File

@ -12,11 +12,12 @@
using namespace Atari2600;
static const char atari2600DataType[] = "Atari2600";
static const int horizontalTimerReload = 227;
Machine::Machine()
{
_timestamp = 0;
_horizontalTimer = 227;
_horizontalTimer = horizontalTimerReload;
_lastOutputStateDuration = 0;
_lastOutputState = OutputState::Sync;
_crt = new Outputs::CRT(228, 312, 1, 4);
@ -187,7 +188,7 @@ void Machine::output_pixels(int count)
// an attempt to avoid both the % operator and a conditional
_horizontalTimer--;
const int32_t sign_extension = _horizontalTimer >> 31;
_horizontalTimer = (_horizontalTimer&~sign_extension) | (sign_extension&227);
_horizontalTimer = (_horizontalTimer&~sign_extension) | (sign_extension&horizontalTimerReload);
}
}
@ -196,7 +197,7 @@ int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t add
uint8_t returnValue = 0xff;
output_pixels(3);
if(_horizontalTimer == 227)
if(_horizontalTimer == horizontalTimerReload)
set_ready_line(false);
if(operation != CPU6502::BusOperation::Ready) {
@ -236,7 +237,7 @@ int Machine::perform_bus_operation(CPU6502::BusOperation operation, uint16_t add
case 0x02: {
set_ready_line(true);
} break;
case 0x03: _horizontalTimer = 227; break;
case 0x03: _horizontalTimer = horizontalTimerReload; break;
case 0x04: _playerAndMissileSize[0] = *value; break;
case 0x05: _playerAndMissileSize[1] = *value; break;

View File

@ -28,8 +28,7 @@ struct Atari2600CRTDelegate: public Outputs::CRT::CRTDelegate {
- (void)crtDidEndFrame:(CRTFrame *)frame {
dispatch_async(dispatch_get_main_queue(), ^{
BOOL hasReturn = !!self.view.crtFrame;
self.view.crtFrame = frame;
BOOL hasReturn = [self.view pushFrame:frame];
if(hasReturn)
dispatch_async(_serialDispatchQueue, ^{

View File

@ -19,8 +19,9 @@
@interface CSCathodeRayView : NSOpenGLView
@property (nonatomic, weak) id <CSCathodeRayViewDelegate> delegate;
@property (nonatomic, assign, nullable) CRTFrame *crtFrame;
- (void)invalidate;
- (BOOL)pushFrame:(CRTFrame * __nonnull)crtFrame;
@end

View File

@ -22,6 +22,8 @@
GLuint _textureName;
CRTSize _textureSize;
CRTFrame *_crtFrame;
}
- (void)prepareOpenGL
@ -50,7 +52,7 @@
CVDisplayLinkStart(displayLink);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext)
@ -116,7 +118,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
self.wantsBestResolutionOpenGLSurface = YES;
}
- (void)setCrtFrame:(CRTFrame *)crtFrame
- (BOOL)pushFrame:(CRTFrame * __nonnull)crtFrame
{
_crtFrame = crtFrame;
[self setNeedsDisplay:YES];
@ -136,6 +139,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _crtFrame->size.width, _crtFrame->dirty_size.height, GL_RGBA, GL_UNSIGNED_BYTE, _crtFrame->buffers[0].data);
}
return YES;
}
#pragma mark - Frame output
@ -153,7 +158,7 @@ const char *vertexShader =
"\n"
"void main (void)\n"
"{\n"
"srcCoordinatesVarying = vec2(srcCoordinates.x / 512.0, (srcCoordinates.y + 0.5) / 512.0);\n"
"srcCoordinatesVarying = vec2(srcCoordinates.x / 512.0, srcCoordinates.y / 512.0);\n"
"gl_Position = vec4(position.x * 2.0 - 1.0, 1.0 - position.y * 2.0 + position.x / 131.0, 0.0, 1.0);\n"
"}\n";
@ -167,7 +172,7 @@ const char *fragmentShader =
"\n"
"void main(void)\n"
"{\n"
"fragColour = texture(texID, srcCoordinatesVarying);\n" // vec4(1.0, 1.0, 1.0, 0.5)
"fragColour = texture(texID, srcCoordinatesVarying) * sin(mod(srcCoordinatesVarying.y * 512, 1.0) * 2.09435310266667 + 0.52359877566668);\n" // vec4(1.0, 1.0, 1.0, 0.5)
"}\n";
#if defined(DEBUG)

View File

@ -12,8 +12,8 @@
using namespace Outputs;
static const uint32_t kCRTFixedPointRange = 0xf8ffffff;
static const uint32_t kCRTFixedPointOffset = 0x00800000;
static const uint32_t kCRTFixedPointRange = 0xefffffff;
static const uint32_t kCRTFixedPointOffset = 0x08000000;
#define kRetraceXMask 0x01
#define kRetraceYMask 0x02
@ -58,7 +58,7 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
// width should be 1.0 / _height_of_display, rotated to match the direction
float angle = atan2f(scanSpeedYfl, scanSpeedXfl);
float halfLineWidth = (float)_height_of_display * 1.9f;
float halfLineWidth = (float)_height_of_display * 1.6f;
_widths[0][0] = (sinf(angle) / halfLineWidth) * kCRTFixedPointRange;
_widths[0][1] = (cosf(angle) / halfLineWidth) * kCRTFixedPointRange;
@ -66,7 +66,7 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
// number of buffers, size of buffer 1, size of buffer 2...
const int bufferWidth = 512;
const int bufferHeight = 512;
for(int frame = 0; frame < 3; frame++)
for(int frame = 0; frame < sizeof(_frame_builders) / sizeof(*_frame_builders); frame++)
{
va_list va;
va_start(va, number_of_buffers);
@ -95,7 +95,7 @@ CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
CRT::~CRT()
{
for(int frame = 0; frame < 3; frame++)
for(int frame = 0; frame < sizeof(_frame_builders) / sizeof(*_frame_builders); frame++)
{
delete _frame_builders[frame];
}
@ -119,7 +119,7 @@ CRT::SyncEvent CRT::next_vertical_sync_event(bool vsync_is_charging, int cycles_
if (_sync_capacitor_charge_level < _sync_capacitor_charge_threshold && _sync_capacitor_charge_level + proposedSyncTime >= _sync_capacitor_charge_threshold) {
uint32_t proposed_sync_y = _rasterPosition.y + (_sync_capacitor_charge_threshold - _sync_capacitor_charge_level) * _scanSpeed.y;
if(proposed_sync_y > (kCRTFixedPointRange * 7) >> 3) {
if(proposed_sync_y > (kCRTFixedPointRange * 15) >> 4) {
proposedSyncTime = _sync_capacitor_charge_threshold - _sync_capacitor_charge_level;
proposedEvent = SyncEvent::StartVSync;
}
@ -208,7 +208,8 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool
next_run[5] = (kCRTFixedPointOffset + _rasterPosition.y - width[1]) >> 16;
next_run[2] = next_run[6] = next_run[22] = tex_x;
next_run[3] = next_run[7] = next_run[23] = tex_y;
next_run[3] = next_run[23] = tex_y;
next_run[7] = tex_y + 1;
}
// advance the raster position as dictated by current sync status
@ -236,7 +237,8 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, const bool
// if this is a data or level run then store the end point
next_run[10] = next_run[14] = next_run[18] = tex_x;
next_run[11] = next_run[15] = next_run[19] = tex_y;
next_run[11] = next_run[15] = tex_y + 1;
next_run[19] = tex_y;
}
// decrement the number of cycles left to run for and increment the

View File

@ -44,7 +44,7 @@ struct CRTFrameBuilder {
int _write_target_pointer;
};
static const int kCRTNumberOfFrames = 3;
static const int kCRTNumberOfFrames = 4;
class CRT {
public: