2015-07-19 17:36:27 +00:00
//
// CRT.cpp
// Clock Signal
//
// Created by Thomas Harte on 19/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
# include "CRT.hpp"
2015-07-20 01:21:34 +00:00
# include <stdarg.h>
2015-07-27 03:50:43 +00:00
# include <math.h>
2015-07-19 17:36:27 +00:00
using namespace Outputs ;
2015-08-20 01:35:26 +00:00
static const uint32_t kCRTFixedPointRange = 0xf7ffffff ;
static const uint32_t kCRTFixedPointOffset = 0x04000000 ;
2015-07-31 00:07:20 +00:00
2015-07-27 03:50:43 +00:00
# define kRetraceXMask 0x01
# define kRetraceYMask 0x02
2015-08-16 20:08:29 +00:00
void CRT : : set_new_timing ( unsigned int cycles_per_line , unsigned int height_of_display )
2015-07-19 17:36:27 +00:00
{
2015-08-16 20:08:29 +00:00
const unsigned int syncCapacityLineChargeThreshold = 3 ;
2015-08-19 13:09:00 +00:00
const unsigned int millisecondsHorizontalRetraceTime = 7 ; // source: Dictionary of Video and Television Technology, p. 234
const unsigned int scanlinesVerticalRetraceTime = 10 ; // source: ibid
2015-07-22 22:15:18 +00:00
2015-08-19 02:36:32 +00:00
// To quote:
//
// "retrace interval; The interval of time for the return of the blanked scanning beam of
// a TV picture tube or camera tube to the starting point of a line or field. It is about 7 µs
// for horizontal retrace and 500 to 750 µs for vertical retrace in NTSC and PAL TV."
2015-07-25 03:36:44 +00:00
_time_multiplier = ( 1000 + cycles_per_line - 1 ) / cycles_per_line ;
2015-07-30 21:16:49 +00:00
height_of_display + = ( height_of_display / 20 ) ; // this is the overrun area we'll use to
2015-07-25 03:36:44 +00:00
2015-07-21 20:37:39 +00:00
// store fundamental display configuration properties
2015-08-19 02:22:47 +00:00
_height_of_display = height_of_display + 5 ;
2015-07-25 03:36:44 +00:00
_cycles_per_line = cycles_per_line * _time_multiplier ;
2015-07-21 03:18:56 +00:00
2015-07-21 20:37:39 +00:00
// generate timing values implied by the given arbuments
2015-07-25 03:36:44 +00:00
_hsync_error_window = _cycles_per_line > > 5 ;
2015-07-20 01:21:34 +00:00
2015-08-03 12:42:05 +00:00
_sync_capacitor_charge_threshold = ( ( syncCapacityLineChargeThreshold * _cycles_per_line ) * 50 ) > > 7 ;
2015-07-25 03:36:44 +00:00
_horizontal_retrace_time = ( millisecondsHorizontalRetraceTime * _cycles_per_line ) > > 6 ;
2015-08-16 20:08:29 +00:00
const unsigned int vertical_retrace_time = scanlinesVerticalRetraceTime * _cycles_per_line ;
2015-08-19 02:22:47 +00:00
const float halfLineWidth = ( float ) _height_of_display * 2.0f ;
2015-07-22 22:15:18 +00:00
2015-08-02 23:30:45 +00:00
for ( int c = 0 ; c < 4 ; c + + )
{
_scanSpeed [ c ] . x = ( c & kRetraceXMask ) ? - ( kCRTFixedPointRange / _horizontal_retrace_time ) : ( kCRTFixedPointRange / _cycles_per_line ) ;
_scanSpeed [ c ] . y = ( c & kRetraceYMask ) ? - ( kCRTFixedPointRange / vertical_retrace_time ) : ( kCRTFixedPointRange / ( _height_of_display * _cycles_per_line ) ) ;
// width should be 1.0 / _height_of_display, rotated to match the direction
float angle = atan2f ( _scanSpeed [ c ] . y , _scanSpeed [ c ] . x ) ;
2015-08-16 20:08:29 +00:00
_beamWidth [ c ] . x = ( uint32_t ) ( ( sinf ( angle ) / halfLineWidth ) * kCRTFixedPointRange ) ;
_beamWidth [ c ] . y = ( uint32_t ) ( ( cosf ( angle ) / halfLineWidth ) * kCRTFixedPointRange ) ;
2015-08-02 23:30:45 +00:00
}
2015-07-31 22:04:33 +00:00
}
2015-08-16 20:08:29 +00:00
CRT : : CRT ( unsigned int cycles_per_line , unsigned int height_of_display , unsigned int number_of_buffers , . . . )
2015-07-31 22:04:33 +00:00
{
set_new_timing ( cycles_per_line , height_of_display ) ;
2015-07-27 03:50:43 +00:00
2015-07-21 20:37:39 +00:00
// generate buffers for signal storage as requested — format is
// number of buffers, size of buffer 1, size of buffer 2...
2015-08-16 20:08:29 +00:00
const uint16_t bufferWidth = 2048 ;
const uint16_t bufferHeight = 2048 ;
2015-07-31 21:47:10 +00:00
for ( int frame = 0 ; frame < sizeof ( _frame_builders ) / sizeof ( * _frame_builders ) ; frame + + )
2015-07-20 01:21:34 +00:00
{
2015-07-23 00:33:20 +00:00
va_list va ;
va_start ( va , number_of_buffers ) ;
2015-07-25 03:29:45 +00:00
_frame_builders [ frame ] = new CRTFrameBuilder ( bufferWidth , bufferHeight , number_of_buffers , va ) ;
2015-07-23 00:33:20 +00:00
va_end ( va ) ;
2015-07-20 01:21:34 +00:00
}
2015-07-23 00:33:20 +00:00
_frames_with_delegate = 0 ;
_frame_read_pointer = 0 ;
2015-07-25 03:29:45 +00:00
_current_frame_builder = _frame_builders [ 0 ] ;
2015-07-21 20:37:39 +00:00
// reset raster position
2015-07-25 03:29:45 +00:00
_rasterPosition . x = _rasterPosition . y = 0 ;
2015-07-21 20:37:39 +00:00
// reset flywheel sync
2015-07-25 03:36:44 +00:00
_expected_next_hsync = _cycles_per_line ;
2015-07-20 03:43:22 +00:00
_horizontal_counter = 0 ;
2015-07-21 20:37:39 +00:00
// reset the vertical charge capacitor
2015-07-20 03:43:22 +00:00
_sync_capacitor_charge_level = 0 ;
2015-07-21 20:37:39 +00:00
// start off not in horizontal sync, not receiving a sync signal
2015-07-21 03:18:56 +00:00
_is_receiving_sync = false ;
_is_in_hsync = false ;
2015-08-02 17:48:35 +00:00
_is_in_vsync = false ;
2015-07-20 01:21:34 +00:00
}
CRT : : ~ CRT ( )
{
2015-07-31 21:47:10 +00:00
for ( int frame = 0 ; frame < sizeof ( _frame_builders ) / sizeof ( * _frame_builders ) ; frame + + )
2015-07-20 01:21:34 +00:00
{
2015-07-25 03:29:45 +00:00
delete _frame_builders [ frame ] ;
2015-07-20 01:21:34 +00:00
}
2015-07-19 17:36:27 +00:00
}
2015-07-21 01:43:00 +00:00
# pragma mark - Sync loop
2015-07-20 03:43:22 +00:00
2015-08-16 20:08:29 +00:00
CRT : : SyncEvent CRT : : get_next_vertical_sync_event ( bool vsync_is_requested , unsigned int cycles_to_run_for , unsigned int * cycles_advanced )
2015-07-20 03:43:22 +00:00
{
2015-07-21 01:43:00 +00:00
SyncEvent proposedEvent = SyncEvent : : None ;
2015-08-16 20:08:29 +00:00
unsigned int proposedSyncTime = cycles_to_run_for ;
2015-07-21 01:43:00 +00:00
2015-08-10 15:55:16 +00:00
// will an acceptable vertical sync be triggered?
if ( vsync_is_requested & & ! _is_in_vsync ) {
2015-08-19 02:22:47 +00:00
if ( _sync_capacitor_charge_level > = _sync_capacitor_charge_threshold & & _rasterPosition . y > = 3 * ( kCRTFixedPointRange > > 2 ) ) {
2015-08-06 00:29:20 +00:00
proposedSyncTime = 0 ;
proposedEvent = SyncEvent : : StartVSync ;
_did_detect_vsync = true ;
}
2015-08-10 15:55:16 +00:00
}
2015-08-03 12:42:05 +00:00
2015-07-21 03:18:56 +00:00
// have we overrun the maximum permitted number of horizontal syncs for this frame?
2015-08-02 17:48:35 +00:00
if ( ! _is_in_vsync ) {
2015-08-19 02:22:47 +00:00
unsigned int time_until_end_of_frame = ( kCRTFixedPointRange - _rasterPosition . y ) / _scanSpeed [ 0 ] . y ;
if ( time_until_end_of_frame < proposedSyncTime ) {
proposedSyncTime = time_until_end_of_frame ;
proposedEvent = SyncEvent : : StartVSync ;
}
2015-08-02 17:48:35 +00:00
} else {
2015-08-16 20:08:29 +00:00
unsigned int time_until_start_of_frame = _rasterPosition . y / ( uint32_t ) ( - _scanSpeed [ kRetraceYMask ] . y ) ;
2015-08-02 17:48:35 +00:00
if ( time_until_start_of_frame < proposedSyncTime ) {
proposedSyncTime = time_until_start_of_frame ;
proposedEvent = SyncEvent : : EndVSync ;
}
2015-07-30 21:16:49 +00:00
}
2015-07-21 03:18:56 +00:00
2015-07-21 01:43:00 +00:00
* cycles_advanced = proposedSyncTime ;
return proposedEvent ;
2015-07-20 03:43:22 +00:00
}
2015-08-16 20:08:29 +00:00
CRT : : SyncEvent CRT : : get_next_horizontal_sync_event ( bool hsync_is_requested , unsigned int cycles_to_run_for , unsigned int * cycles_advanced )
2015-07-23 22:53:18 +00:00
{
// do we recognise this hsync, thereby adjusting future time expectations?
2015-07-23 23:24:25 +00:00
if ( hsync_is_requested ) {
if ( _horizontal_counter < _hsync_error_window | | _horizontal_counter > = _expected_next_hsync - _hsync_error_window ) {
_did_detect_hsync = true ;
2015-07-23 22:53:18 +00:00
2015-08-16 20:08:29 +00:00
unsigned int time_now = ( _horizontal_counter < _hsync_error_window ) ? _expected_next_hsync + _horizontal_counter : _horizontal_counter ;
2015-07-31 03:00:54 +00:00
_expected_next_hsync = ( _expected_next_hsync + _expected_next_hsync + _expected_next_hsync + time_now ) > > 2 ;
2015-07-23 23:24:25 +00:00
}
2015-07-23 22:53:18 +00:00
}
SyncEvent proposedEvent = SyncEvent : : None ;
2015-08-16 20:08:29 +00:00
unsigned int proposedSyncTime = cycles_to_run_for ;
2015-07-23 22:53:18 +00:00
// will we end an ongoing hsync?
if ( _horizontal_counter < _horizontal_retrace_time & & _horizontal_counter + proposedSyncTime > = _horizontal_retrace_time ) {
proposedSyncTime = _horizontal_retrace_time - _horizontal_counter ;
proposedEvent = SyncEvent : : EndHSync ;
}
// will we start an hsync?
if ( _horizontal_counter + proposedSyncTime > = _expected_next_hsync ) {
proposedSyncTime = _expected_next_hsync - _horizontal_counter ;
proposedEvent = SyncEvent : : StartHSync ;
}
* cycles_advanced = proposedSyncTime ;
return proposedEvent ;
}
2015-09-06 00:25:30 +00:00
void CRT : : advance_cycles ( unsigned int number_of_cycles , bool hsync_requested , bool vsync_requested , const bool vsync_charging , const Type type )
2015-07-20 03:43:22 +00:00
{
2015-07-25 03:36:44 +00:00
number_of_cycles * = _time_multiplier ;
2015-07-27 03:50:43 +00:00
bool is_output_run = ( ( type = = Type : : Level ) | | ( type = = Type : : Data ) ) ;
2015-07-25 03:29:45 +00:00
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 ;
}
2015-07-21 03:18:56 +00:00
2015-07-21 01:43:00 +00:00
while ( number_of_cycles ) {
2015-07-21 20:37:39 +00:00
2015-08-16 20:08:29 +00:00
unsigned int time_until_vertical_sync_event , time_until_horizontal_sync_event ;
2015-08-03 12:42:05 +00:00
SyncEvent next_vertical_sync_event = this - > get_next_vertical_sync_event ( vsync_requested , number_of_cycles , & time_until_vertical_sync_event ) ;
2015-07-31 22:15:59 +00:00
SyncEvent next_horizontal_sync_event = this - > get_next_horizontal_sync_event ( hsync_requested , time_until_vertical_sync_event , & time_until_horizontal_sync_event ) ;
2015-07-23 22:53:18 +00:00
2015-07-21 20:37:39 +00:00
// get the next sync event and its timing; hsync request is instantaneous (being edge triggered) so
// set it to false for the next run through this loop (if any)
2015-08-16 20:08:29 +00:00
unsigned int next_run_length = std : : min ( time_until_vertical_sync_event , time_until_horizontal_sync_event ) ;
2015-07-20 03:43:22 +00:00
2015-08-06 01:12:33 +00:00
hsync_requested = false ;
vsync_requested = false ;
2015-08-03 12:42:05 +00:00
2015-08-06 01:12:33 +00:00
uint8_t * next_run = ( is_output_run & & _current_frame_builder & & next_run_length ) ? _current_frame_builder - > get_next_run ( ) : nullptr ;
2015-08-13 17:29:07 +00:00
int lengthMask = ( _is_in_hsync ? kRetraceXMask : 0 ) | ( _is_in_vsync ? kRetraceYMask : 0 ) ;
2015-07-23 00:33:20 +00:00
2015-08-06 01:12:33 +00:00
# define position_x(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfPosition + 0])
# define position_y(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfPosition + 2])
# define tex_x(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfTexCoord + 0])
# define tex_y(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfTexCoord + 2])
2015-08-02 18:25:21 +00:00
# define lateral(v) next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfLateral]
2015-08-16 20:12:20 +00:00
# define InternalToUInt16(v) ((v) + 32768) >> 16
2015-07-25 03:29:45 +00:00
if ( next_run )
2015-07-23 22:53:18 +00:00
{
2015-07-23 00:33:20 +00:00
// set the type, initial raster position and type of this run
2015-08-16 20:12:20 +00:00
position_x ( 0 ) = position_x ( 4 ) = InternalToUInt16 ( kCRTFixedPointOffset + _rasterPosition . x + _beamWidth [ lengthMask ] . x ) ;
position_y ( 0 ) = position_y ( 4 ) = InternalToUInt16 ( kCRTFixedPointOffset + _rasterPosition . y + _beamWidth [ lengthMask ] . y ) ;
position_x ( 1 ) = InternalToUInt16 ( kCRTFixedPointOffset + _rasterPosition . x - _beamWidth [ lengthMask ] . x ) ;
position_y ( 1 ) = InternalToUInt16 ( kCRTFixedPointOffset + _rasterPosition . y - _beamWidth [ lengthMask ] . y ) ;
2015-08-02 18:25:21 +00:00
tex_x ( 0 ) = tex_x ( 1 ) = tex_x ( 4 ) = tex_x ;
2015-08-02 18:32:29 +00:00
// these things are constants across the line so just throw them out now
tex_y ( 0 ) = tex_y ( 4 ) = tex_y ( 1 ) = tex_y ( 2 ) = tex_y ( 3 ) = tex_y ( 5 ) = tex_y ;
lateral ( 0 ) = lateral ( 4 ) = lateral ( 5 ) = 0 ;
lateral ( 1 ) = lateral ( 2 ) = lateral ( 3 ) = 1 ;
2015-07-25 03:29:45 +00:00
}
2015-07-23 00:33:20 +00:00
2015-07-25 03:29:45 +00:00
// advance the raster position as dictated by current sync status
2015-08-02 23:30:45 +00:00
int64_t end_position [ 2 ] ;
2015-08-19 02:22:47 +00:00
end_position [ 0 ] = ( int64_t ) _rasterPosition . x + ( int64_t ) next_run_length * ( int32_t ) _scanSpeed [ lengthMask ] . x ;
end_position [ 1 ] = ( int64_t ) _rasterPosition . y + ( int64_t ) next_run_length * ( int32_t ) _scanSpeed [ lengthMask ] . y ;
2015-08-02 23:30:45 +00:00
2015-07-25 03:29:45 +00:00
if ( _is_in_hsync )
2015-08-02 23:30:45 +00:00
_rasterPosition . x = ( uint32_t ) std : : max ( ( int64_t ) 0 , end_position [ 0 ] ) ;
2015-07-25 03:29:45 +00:00
else
2015-08-02 23:30:45 +00:00
_rasterPosition . x = ( uint32_t ) std : : min ( ( int64_t ) kCRTFixedPointRange , end_position [ 0 ] ) ;
2015-07-23 00:33:20 +00:00
2015-08-02 17:48:35 +00:00
if ( _is_in_vsync )
2015-08-13 17:29:07 +00:00
_rasterPosition . y = ( uint32_t ) std : : max ( ( int64_t ) 0 , end_position [ 1 ] ) ;
2015-07-25 03:29:45 +00:00
else
2015-08-02 23:30:45 +00:00
_rasterPosition . y = ( uint32_t ) std : : min ( ( int64_t ) kCRTFixedPointRange , end_position [ 1 ] ) ;
2015-07-23 00:33:20 +00:00
2015-07-25 03:29:45 +00:00
if ( next_run )
{
2015-07-23 00:33:20 +00:00
// store the final raster position
2015-08-16 20:12:20 +00:00
position_x ( 2 ) = position_x ( 3 ) = InternalToUInt16 ( kCRTFixedPointOffset + _rasterPosition . x - _beamWidth [ lengthMask ] . x ) ;
position_y ( 2 ) = position_y ( 3 ) = InternalToUInt16 ( kCRTFixedPointOffset + _rasterPosition . y - _beamWidth [ lengthMask ] . y ) ;
position_x ( 5 ) = InternalToUInt16 ( kCRTFixedPointOffset + _rasterPosition . x + _beamWidth [ lengthMask ] . x ) ;
position_y ( 5 ) = InternalToUInt16 ( kCRTFixedPointOffset + _rasterPosition . y + _beamWidth [ lengthMask ] . y ) ;
2015-07-23 00:33:20 +00:00
// if this is a data run then advance the buffer pointer
2015-07-27 03:50:43 +00:00
if ( type = = Type : : Data ) tex_x + = next_run_length / _time_multiplier ;
2015-07-23 00:33:20 +00:00
// if this is a data or level run then store the end point
2015-08-02 18:25:21 +00:00
tex_x ( 2 ) = tex_x ( 3 ) = tex_x ( 5 ) = tex_x ;
2015-07-20 03:43:22 +00:00
}
2015-07-21 01:43:00 +00:00
2015-07-21 20:37:39 +00:00
// decrement the number of cycles left to run for and increment the
// horizontal counter appropriately
2015-07-21 01:43:00 +00:00
number_of_cycles - = next_run_length ;
_horizontal_counter + = next_run_length ;
2015-07-21 20:37:39 +00:00
// either charge or deplete the vertical retrace capacitor (making sure it stops at 0)
2015-08-06 00:29:20 +00:00
if ( vsync_charging & & ! _is_in_vsync )
2015-07-21 01:43:00 +00:00
_sync_capacitor_charge_level + = next_run_length ;
2015-07-20 03:43:22 +00:00
else
2015-08-16 20:08:29 +00:00
_sync_capacitor_charge_level = std : : max ( _sync_capacitor_charge_level - ( int ) next_run_length , 0 ) ;
2015-07-21 01:43:00 +00:00
2015-07-21 20:37:39 +00:00
// react to the incoming event...
2015-07-23 22:53:18 +00:00
if ( next_run_length = = time_until_horizontal_sync_event )
{
switch ( next_horizontal_sync_event )
{
// start of hsync: zero the scanline counter, note that we're now in
// horizontal sync, increment the lines-in-this-frame counter
case SyncEvent : : StartHSync :
_horizontal_counter = 0 ;
_is_in_hsync = true ;
break ;
// end of horizontal sync: update the flywheel's velocity, note that we're no longer
// in horizontal sync
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 ;
default : break ;
}
}
if ( next_run_length = = time_until_vertical_sync_event )
{
switch ( next_vertical_sync_event )
{
// start of vertical sync: reset the lines-in-this-frame counter,
// load the retrace counter with the amount of time it'll take to retrace
case SyncEvent : : StartVSync :
2015-08-02 17:48:35 +00:00
_is_in_vsync = true ;
2015-08-06 00:29:20 +00:00
_sync_capacitor_charge_level = 0 ;
2015-07-23 22:53:18 +00:00
break ;
// end of vertical sync: tell the delegate that we finished vertical sync,
// releasing all runs back into the common pool
case SyncEvent : : EndVSync :
2015-07-25 03:29:45 +00:00
if ( _delegate & & _current_frame_builder )
2015-07-23 22:53:18 +00:00
{
2015-07-25 03:29:45 +00:00
_current_frame_builder - > complete ( ) ;
2015-07-23 22:53:18 +00:00
_frames_with_delegate + + ;
2015-08-03 00:32:18 +00:00
_delegate - > crt_did_end_frame ( this , & _current_frame_builder - > frame , _did_detect_vsync ) ;
2015-07-23 22:53:18 +00:00
}
if ( _frames_with_delegate < kCRTNumberOfFrames )
{
_frame_read_pointer = ( _frame_read_pointer + 1 ) % kCRTNumberOfFrames ;
2015-07-25 03:29:45 +00:00
_current_frame_builder = _frame_builders [ _frame_read_pointer ] ;
_current_frame_builder - > reset ( ) ;
2015-07-23 22:53:18 +00:00
}
else
2015-07-25 03:29:45 +00:00
_current_frame_builder = nullptr ;
2015-07-31 22:04:33 +00:00
2015-08-02 17:48:35 +00:00
_is_in_vsync = false ;
2015-07-31 22:04:33 +00:00
_did_detect_vsync = false ;
2015-07-23 22:53:18 +00:00
break ;
default : break ;
}
2015-07-20 03:43:22 +00:00
}
}
}
2015-07-23 00:33:20 +00:00
void CRT : : return_frame ( )
{
_frames_with_delegate - - ;
}
2015-07-21 03:18:56 +00:00
# pragma mark - delegate
2015-07-22 22:15:18 +00:00
void CRT : : set_delegate ( CRTDelegate * delegate )
2015-07-21 03:18:56 +00:00
{
_delegate = delegate ;
}
2015-07-20 03:43:22 +00:00
# pragma mark - stream feeding methods
2015-07-21 20:37:39 +00:00
/*
These all merely channel into advance_cycles , supplying appropriate arguments
*/
2015-08-16 20:08:29 +00:00
void CRT : : output_sync ( unsigned int number_of_cycles )
2015-07-19 17:36:27 +00:00
{
2015-07-21 20:37:39 +00:00
bool _hsync_requested = ! _is_receiving_sync ; // ensure this really is edge triggered; someone calling output_sync twice in succession shouldn't trigger it twice
2015-07-21 03:18:56 +00:00
_is_receiving_sync = true ;
2015-09-06 00:25:30 +00:00
advance_cycles ( number_of_cycles , _hsync_requested , false , true , Type : : Sync ) ;
2015-07-19 17:36:27 +00:00
}
2015-08-16 20:08:29 +00:00
void CRT : : output_blank ( unsigned int number_of_cycles )
2015-07-19 17:36:27 +00:00
{
2015-08-10 15:55:16 +00:00
bool _vsync_requested = _is_receiving_sync ;
2015-07-21 03:18:56 +00:00
_is_receiving_sync = false ;
2015-09-06 00:25:30 +00:00
advance_cycles ( number_of_cycles , false , _vsync_requested , false , Type : : Blank ) ;
2015-07-19 17:36:27 +00:00
}
2015-09-06 00:25:30 +00:00
void CRT : : output_level ( unsigned int number_of_cycles )
2015-07-19 17:36:27 +00:00
{
2015-08-10 15:55:16 +00:00
bool _vsync_requested = _is_receiving_sync ;
2015-07-21 03:18:56 +00:00
_is_receiving_sync = false ;
2015-09-06 00:25:30 +00:00
advance_cycles ( number_of_cycles , false , _vsync_requested , false , Type : : Level ) ;
2015-07-21 01:43:00 +00:00
}
2015-09-06 00:25:30 +00:00
void CRT : : output_data ( unsigned int number_of_cycles )
2015-07-21 01:43:00 +00:00
{
2015-08-10 15:55:16 +00:00
bool _vsync_requested = _is_receiving_sync ;
2015-07-21 03:18:56 +00:00
_is_receiving_sync = false ;
2015-09-06 00:25:30 +00:00
advance_cycles ( number_of_cycles , false , _vsync_requested , false , Type : : Data ) ;
2015-07-19 17:36:27 +00:00
}
2015-07-20 01:21:34 +00:00
2015-07-20 03:43:22 +00:00
# pragma mark - Buffer supply
2015-07-20 01:21:34 +00:00
void CRT : : allocate_write_area ( int required_length )
{
2015-07-25 03:29:45 +00:00
if ( _current_frame_builder ) _current_frame_builder - > allocate_write_area ( required_length ) ;
2015-07-23 00:33:20 +00:00
}
uint8_t * CRT : : get_write_target_for_buffer ( int buffer )
{
2015-07-25 03:29:45 +00:00
if ( ! _current_frame_builder ) return nullptr ;
return _current_frame_builder - > get_write_target_for_buffer ( buffer ) ;
2015-07-23 00:33:20 +00:00
}
# pragma mark - CRTFrame
2015-08-16 20:08:29 +00:00
CRTFrameBuilder : : CRTFrameBuilder ( uint16_t width , uint16_t height , unsigned int number_of_buffers , va_list buffer_sizes )
2015-07-23 00:33:20 +00:00
{
2015-07-25 03:29:45 +00:00
frame . size . width = width ;
frame . size . height = height ;
frame . number_of_buffers = number_of_buffers ;
frame . buffers = new CRTBuffer [ number_of_buffers ] ;
2015-07-23 00:33:20 +00:00
for ( int buffer = 0 ; buffer < number_of_buffers ; buffer + + )
{
2015-08-16 20:08:29 +00:00
frame . buffers [ buffer ] . depth = va_arg ( buffer_sizes , unsigned int ) ;
2015-07-25 03:29:45 +00:00
frame . buffers [ buffer ] . data = new uint8_t [ width * height * frame . buffers [ buffer ] . depth ] ;
2015-07-23 00:33:20 +00:00
}
reset ( ) ;
}
2015-07-25 03:29:45 +00:00
CRTFrameBuilder : : ~ CRTFrameBuilder ( )
2015-07-23 00:33:20 +00:00
{
2015-07-25 03:29:45 +00:00
for ( int buffer = 0 ; buffer < frame . number_of_buffers ; buffer + + )
delete [ ] frame . buffers [ buffer ] . data ;
delete frame . buffers ;
2015-07-23 00:33:20 +00:00
}
2015-07-25 03:29:45 +00:00
void CRTFrameBuilder : : reset ( )
2015-07-23 00:33:20 +00:00
{
2015-07-25 03:29:45 +00:00
frame . number_of_runs = 0 ;
2015-07-27 03:50:43 +00:00
_next_write_x_position = _next_write_y_position = 0 ;
2015-08-17 04:25:32 +00:00
frame . dirty_size . width = 0 ;
frame . dirty_size . height = 1 ;
2015-07-23 00:33:20 +00:00
}
2015-07-25 03:29:45 +00:00
void CRTFrameBuilder : : complete ( )
2015-07-23 00:33:20 +00:00
{
2015-07-25 03:29:45 +00:00
frame . runs = & _all_runs [ 0 ] ;
2015-07-23 00:33:20 +00:00
}
2015-08-06 01:12:33 +00:00
uint8_t * CRTFrameBuilder : : get_next_run ( )
2015-07-23 00:33:20 +00:00
{
2015-08-02 18:25:21 +00:00
const size_t vertices_per_run = 6 ;
const size_t size_of_run = kCRTSizeOfVertex * vertices_per_run ;
2015-07-23 00:33:20 +00:00
// get a run from the allocated list, allocating more if we're about to overrun
2015-08-02 18:25:21 +00:00
if ( frame . number_of_runs * size_of_run > = _all_runs . size ( ) )
2015-07-23 00:33:20 +00:00
{
2015-08-02 18:25:21 +00:00
_all_runs . resize ( _all_runs . size ( ) + size_of_run * 200 ) ;
2015-07-23 00:33:20 +00:00
}
2015-08-06 01:12:33 +00:00
uint8_t * next_run = & _all_runs [ frame . number_of_runs * size_of_run ] ;
2015-07-25 03:29:45 +00:00
frame . number_of_runs + + ;
2015-07-23 00:33:20 +00:00
2015-07-25 03:29:45 +00:00
return next_run ;
2015-07-23 00:33:20 +00:00
}
2015-07-25 03:29:45 +00:00
void CRTFrameBuilder : : allocate_write_area ( int required_length )
2015-07-23 00:33:20 +00:00
{
2015-07-27 03:50:43 +00:00
if ( _next_write_x_position + required_length > frame . size . width )
2015-07-20 01:21:34 +00:00
{
2015-07-27 03:50:43 +00:00
_next_write_x_position = 0 ;
2015-07-27 04:28:47 +00:00
_next_write_y_position = ( _next_write_y_position + 1 ) & ( frame . size . height - 1 ) ;
2015-07-25 03:29:45 +00:00
frame . dirty_size . height + + ;
2015-07-20 01:21:34 +00:00
}
2015-07-27 03:50:43 +00:00
_write_x_position = _next_write_x_position ;
_write_y_position = _next_write_y_position ;
2015-07-30 21:16:49 +00:00
_write_target_pointer = ( _write_y_position * frame . size . width ) + _write_x_position ;
2015-07-27 03:50:43 +00:00
_next_write_x_position + = required_length ;
frame . dirty_size . width = std : : max ( frame . dirty_size . width , _next_write_x_position ) ;
2015-07-20 01:21:34 +00:00
}
2015-07-25 03:29:45 +00:00
uint8_t * CRTFrameBuilder : : get_write_target_for_buffer ( int buffer )
2015-07-20 01:21:34 +00:00
{
2015-07-25 03:29:45 +00:00
return & frame . buffers [ buffer ] . data [ _write_target_pointer * frame . buffers [ buffer ] . depth ] ;
2015-07-20 01:21:34 +00:00
}