<commit_before>//
//  CRT.cpp
//  Clock Signal
//
//  Created by <NAME> on 19/07/2015.
//  Copyright © 2015 <NAME>. All rights reserved.
//

#include "CRT.hpp"
#include <stdarg.h>
#include <math.h>

using namespace Outputs;

static const uint32_t kCRTFixedPointRange	= 0xf8ffffff;
static const uint32_t kCRTFixedPointOffset	= 0x00800000;

#define kRetraceXMask	0x01
#define kRetraceYMask	0x02

CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
{
	const int syncCapacityLineChargeThreshold = 5;
	const int millisecondsHorizontalRetraceTime = 7;	// source: Dictionary of Video and Television Technology, p. 234
	const int scanlinesVerticalRetraceTime = 10;		// source: ibid

	_time_multiplier = (1000 + cycles_per_line - 1) / cycles_per_line;
	height_of_display += (height_of_display / 20);  // this is the overrun area we'll use to

	// store fundamental display configuration properties
	_height_of_display = height_of_display;// + (height_of_display / 10);
	_cycles_per_line = cycles_per_line * _time_multiplier;

	// generate timing values implied by the given arbuments
	_hsync_error_window = _cycles_per_line >> 5;

	_sync_capacitor_charge_threshold = (syncCapacityLineChargeThreshold * _cycles_per_line) >> 1;
	_horizontal_retrace_time = (millisecondsHorizontalRetraceTime * _cycles_per_line) >> 6;
	_vertical_retrace_time = scanlinesVerticalRetraceTime * _cycles_per_line;

	_scanSpeed.x = kCRTFixedPointRange / _cycles_per_line;
	_scanSpeed.y = kCRTFixedPointRange / (_height_of_display * _cycles_per_line);
	_retraceSpeed.x = kCRTFixedPointRange / _horizontal_retrace_time;
	_retraceSpeed.y = kCRTFixedPointRange / _vertical_retrace_time;

	// precompute the lengths of all four combinations of scan direction, for fast triangle
	// strip generation later
	float scanSpeedXfl = 1.0f / (float)_cycles_per_line;
	float scanSpeedYfl = 1.0f / (float)(_height_of_display * _cycles_per_line);
	float retraceSpeedXfl = 1.0f / (float)_horizontal_retrace_time;
	float retraceSpeedYfl = 1.0f / (float)(_vertical_retrace_time);
	float lengths[4];

	lengths[0]								= sqrtf(scanSpeedXfl*scanSpeedXfl		+ scanSpeedYfl*scanSpeedYfl);
	lengths[kRetraceXMask]					= sqrtf(retraceSpeedXfl*retraceSpeedXfl + scanSpeedYfl*scanSpeedYfl);
	lengths[kRetraceXMask | kRetraceYMask]	= sqrtf(retraceSpeedXfl*retraceSpeedXfl + retraceSpeedYfl*retraceSpeedYfl);
	lengths[kRetraceYMask]					= sqrtf(scanSpeedXfl*scanSpeedXfl		+ retraceSpeedYfl*retraceSpeedYfl);

	// 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;
	_widths[0][0] = (sinf(angle) / halfLineWidth) * kCRTFixedPointRange;
	_widths[0][1] = (cosf(angle) / halfLineWidth) * kCRTFixedPointRange;

	// generate buffers for signal storage as requested — format is
	// 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++)
	{
		va_list va;
		va_start(va, number_of_buffers);
		_frame_builders[frame] = new CRTFrameBuilder(bufferWidth, bufferHeight, number_of_buffers, va);
		va_end(va);
	}
	_frames_with_delegate = 0;
	_frame_read_pointer = 0;
	_current_frame_builder = _frame_builders[0];

	// reset raster position
	_rasterPosition.x = _rasterPosition.y = 0;

	// reset flywheel sync
	_expected_next_hsync = _cycles_per_line;
	_horizontal_counter = 0;

	// reset the vertical charge capacitor
	_sync_capacitor_charge_level = 0;

	// start off not in horizontal sync, not receiving a sync signal
	_is_receiving_sync = false;
	_is_in_hsync = false;
	_vretrace_counter = 0;
}

CRT::~CRT()
{
	for(int frame = 0; frame < 3; frame++)
	{
		delete _frame_builders[frame];
	}
}

#pragma mark - Sync loop

CRT::SyncEvent CRT::next_vertical_sync_event(bool vsync_is_charging, int cycles_to_run_for, int *cycles_advanced)
{
	SyncEvent proposedEvent = SyncEvent::None;
	int proposedSyncTime = cycles_to_run_for;

	// have we overrun the maximum permitted number of horizontal syncs for this frame?
	if (!_vretrace_counter && _rasterPosition.y == kCRTFixedPointRange) {
		proposedSyncTime = 0;
		proposedEvent = SyncEvent::StartVSync;
	}

	// will an acceptable vertical sync be triggered?
	if (vsync_is_charging && !_vretrace_counter) {
		 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) {
				proposedSyncTime = _sync_capacitor_charge_threshold - _sync_capacitor_charge_level;
				proposedEvent = SyncEvent::StartVSync;
			}
		 }
	}

	// will an ongoing vertical sync end?
	if (_vretrace_counter > 0) {
		if (_vretrace_counter < proposedSyncTime) {
			proposedSyncTime = _vretrace_counter;
			proposedEvent = SyncEvent::EndVSync;
		}
	}

	*cycles_advanced = proposedSyncTime;
	return proposedEvent;
}

CRT::SyncEvent CRT::next_horizontal_sync_event(bool hsync_is_requested, int cycles_to_run_for, int *cycles_advanced)
{
	// do we recognise this hsync, thereby adjusting future time expectations?
	if(hsync_is_requested) {
		if (_horizontal_counter < _hsync_error_window || _horizontal_counter >= _expected_next_hsync - _hsync_error_window) {
			_did_detect_hsync = true;

			int time_now = (_horizontal_counter < _hsync_error_window) ? _expected_next_hsync + _horizontal_counter : _horizontal_counter;
			_expected_next_hsync = (_expected_next_hsync + time_now) >> 1;<commit_msg>Slowed flywheel adjustments a little, the better to highlight phase errors for the time being.<commit_after>//
//  CRT.cpp
//  Clock Signal
//
//  Created by <NAME> on 19/07/2015.
//  Copyright © 2015 <NAME>. All rights reserved.
//

#include "CRT.hpp"
#include <stdarg.h>
#include <math.h>

using namespace Outputs;

static const uint32_t kCRTFixedPointRange	= 0xf8ffffff;
static const uint32_t kCRTFixedPointOffset	= 0x00800000;

#define kRetraceXMask	0x01
#define kRetraceYMask	0x02

CRT::CRT(int cycles_per_line, int height_of_display, int number_of_buffers, ...)
{
	const int syncCapacityLineChargeThreshold = 5;
	const int millisecondsHorizontalRetraceTime = 7;	// source: Dictionary of Video and Television Technology, p. 234
	const int scanlinesVerticalRetraceTime = 10;		// source: ibid

	_time_multiplier = (1000 + cycles_per_line - 1) / cycles_per_line;
	height_of_display += (height_of_display / 20);  // this is the overrun area we'll use to

	// store fundamental display configuration properties
	_height_of_display = height_of_display;// + (height_of_display / 10);
	_cycles_per_line = cycles_per_line * _time_multiplier;

	// generate timing values implied by the given arbuments
	_hsync_error_window = _cycles_per_line >> 5;

	_sync_capacitor_charge_threshold = (syncCapacityLineChargeThreshold * _cycles_per_line) >> 1;
	_horizontal_retrace_time = (millisecondsHorizontalRetraceTime * _cycles_per_line) >> 6;
	_vertical_retrace_time = scanlinesVerticalRetraceTime * _cycles_per_line;

	_scanSpeed.x = kCRTFixedPointRange / _cycles_per_line;
	_scanSpeed.y = kCRTFixedPointRange / (_height_of_display * _cycles_per_line);
	_retraceSpeed.x = kCRTFixedPointRange / _horizontal_retrace_time;
	_retraceSpeed.y = kCRTFixedPointRange / _vertical_retrace_time;

	// precompute the lengths of all four combinations of scan direction, for fast triangle
	// strip generation later
	float scanSpeedXfl = 1.0f / (float)_cycles_per_line;
	float scanSpeedYfl = 1.0f / (float)(_height_of_display * _cycles_per_line);
	float retraceSpeedXfl = 1.0f / (float)_horizontal_retrace_time;
	float retraceSpeedYfl = 1.0f / (float)(_vertical_retrace_time);
	float lengths[4];

	lengths[0]								= sqrtf(scanSpeedXfl*scanSpeedXfl		+ scanSpeedYfl*scanSpeedYfl);
	lengths[kRetraceXMask]					= sqrtf(retraceSpeedXfl*retraceSpeedXfl + scanSpeedYfl*scanSpeedYfl);
	lengths[kRetraceXMask | kRetraceYMask]	= sqrtf(retraceSpeedXfl*retraceSpeedXfl + retraceSpeedYfl*retraceSpeedYfl);
	lengths[kRetraceYMask]					= sqrtf(scanSpeedXfl*scanSpeedXfl		+ retraceSpeedYfl*retraceSpeedYfl);

	// 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;
	_widths[0][0] = (sinf(angle) / halfLineWidth) * kCRTFixedPointRange;
	_widths[0][1] = (cosf(angle) / halfLineWidth) * kCRTFixedPointRange;

	// generate buffers for signal storage as requested — format is
	// 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++)
	{
		va_list va;
		va_start(va, number_of_buffers);
		_frame_builders[frame] = new CRTFrameBuilder(bufferWidth, bufferHeight, number_of_buffers, va);
		va_end(va);
	}
	_frames_with_delegate = 0;
	_frame_read_pointer = 0;
	_current_frame_builder = _frame_builders[0];

	// reset raster position
	_rasterPosition.x = _rasterPosition.y = 0;

	// reset flywheel sync
	_expected_next_hsync = _cycles_per_line;
	_horizontal_counter = 0;

	// reset the vertical charge capacitor
	_sync_capacitor_charge_level = 0;

	// start off not in horizontal sync, not receiving a sync signal
	_is_receiving_sync = false;
	_is_in_hsync = false;
	_vretrace_counter = 0;
}

CRT::~CRT()
{
	for(int frame = 0; frame < 3; frame++)
	{
		delete _frame_builders[frame];
	}
}

#pragma mark - Sync loop

CRT::SyncEvent CRT::next_vertical_sync_event(bool vsync_is_charging, int cycles_to_run_for, int *cycles_advanced)
{
	SyncEvent proposedEvent = SyncEvent::None;
	int proposedSyncTime = cycles_to_run_for;

	// have we overrun the maximum permitted number of horizontal syncs for this frame?
	if (!_vretrace_counter && _rasterPosition.y == kCRTFixedPointRange) {
		proposedSyncTime = 0;
		proposedEvent = SyncEvent::StartVSync;
	}

	// will an acceptable vertical sync be triggered?
	if (vsync_is_charging && !_vretrace_counter) {
		 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) {
				proposedSyncTime = _sync_capacitor_charge_threshold - _sync_capacitor_charge_level;
				proposedEvent = SyncEvent::StartVSync;
			}
		 }
	}

	// will an ongoing vertical sync end?
	if (_vretrace_counter > 0) {
		if (_vretrace_counter < proposedSyncTime) {
			proposedSyncTime = _vretrace_counter;
			proposedEvent = SyncEvent::EndVSync;
		}
	}

	*cycles_advanced = proposedSyncTime;
	return proposedEvent;
}

CRT::SyncEvent CRT::next_horizontal_sync_event(bool hsync_is_requested, int cycles_to_run_for, int *cycles_advanced)
{
	// do we recognise this hsync, thereby adjusting future time expectations?
	if(hsync_is_requested) {
		if (_horizontal_counter < _hsync_error_window || _horizontal_counter >= _expected_next_hsync - _hsync_error_window) {
			_did_detect_hsync = true;

			int time_now = (_horizontal_counter < _hsync_error_window) ? _expected_next_hsync + _horizontal_counter : _horizontal_counter;
			_expected_next_hsync = (_expected_next_hsync + _expected_next_hsync + _expected_next_hsync + time_now) >> 2;
{'fraction_non_alphanumeric': 0.08820709491850431, 'fraction_numerical': 0.01690926523141288, 'mean_word_length': 5.417225950782997, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6387196', 'n_tokens_mistral': 4317, 'n_tokens_neox': 3940, 'n_words': 877}