2015-07-19 17:36:27 +00:00
//
// CRT.hpp
// Clock Signal
//
// Created by Thomas Harte on 19/07/2015.
// Copyright © 2015 Thomas Harte. All rights reserved.
//
# ifndef CRT_cpp
# define CRT_cpp
# include <stdint.h>
2015-07-23 00:33:20 +00:00
# include <stdarg.h>
2015-07-19 17:36:27 +00:00
# include <string>
2015-07-21 03:18:56 +00:00
# include <vector>
2015-07-19 17:36:27 +00:00
2015-07-25 03:29:45 +00:00
# include "CRTFrame.h"
2015-07-23 00:33:20 +00:00
2015-07-25 03:29:45 +00:00
namespace Outputs {
2015-07-23 00:33:20 +00:00
class CRT ;
2015-07-25 03:29:45 +00:00
struct CRTFrameBuilder {
CRTFrame frame ;
2015-07-23 00:33:20 +00:00
2015-08-16 20:08:29 +00:00
CRTFrameBuilder ( uint16_t width , uint16_t height , unsigned int number_of_buffers , va_list buffer_sizes ) ;
2015-07-25 03:29:45 +00:00
~ CRTFrameBuilder ( ) ;
2015-07-23 00:33:20 +00:00
private :
2015-08-06 01:12:33 +00:00
std : : vector < uint8_t > _all_runs ;
2015-07-23 00:33:20 +00:00
void reset ( ) ;
void complete ( ) ;
2015-08-06 01:12:33 +00:00
uint8_t * get_next_run ( ) ;
2015-07-23 00:33:20 +00:00
friend CRT ;
void allocate_write_area ( int required_length ) ;
uint8_t * get_write_target_for_buffer ( int buffer ) ;
// a pointer to the section of content buffer currently being
// returned and to where the next section will begin
2015-08-16 20:08:29 +00:00
uint16_t _next_write_x_position , _next_write_y_position ;
uint16_t _write_x_position , _write_y_position ;
2015-08-17 04:25:32 +00:00
size_t _write_target_pointer ;
2015-07-23 00:33:20 +00:00
} ;
2015-07-31 21:47:10 +00:00
static const int kCRTNumberOfFrames = 4 ;
2015-07-23 00:33:20 +00:00
2015-07-19 17:36:27 +00:00
class CRT {
public :
2015-08-16 20:08:29 +00:00
CRT ( unsigned int cycles_per_line , unsigned int height_of_display , unsigned int number_of_buffers , . . . ) ;
2015-07-20 01:21:34 +00:00
~ CRT ( ) ;
2015-08-16 20:08:29 +00:00
void set_new_timing ( unsigned int cycles_per_line , unsigned int height_of_display ) ;
2015-07-31 22:04:33 +00:00
2016-01-22 02:17:47 +00:00
/*! Output at the sync level.
@ param number_of_cycles The amount of time to putput sync for .
*/
2015-08-16 20:08:29 +00:00
void output_sync ( unsigned int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
/*! Output at the blanking level.
@ param number_of_cycles The amount of time to putput the blanking level for .
*/
2015-08-16 20:08:29 +00:00
void output_blank ( unsigned int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
/*! Outputs the first written to the most-recently created run of data repeatedly for a prolonged period.
@ param number_of_cycles The number of cycles to repeat the output for .
*/
2015-09-06 00:25:30 +00:00
void output_level ( unsigned int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
/*! Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer
that is at least @ c number_of_cycles long , and that the first @ c number_of_cycles / source_divider should be spread
over that amount of time .
@ param number_of_cycles The amount of data to output .
@ param source_divider A divider for source data ; if the divider is 1 then one source pixel is output every cycle ,
if it is 2 then one source pixel covers two cycles ; if it is n then one source pixel covers n cycles .
*/
void output_data ( unsigned int number_of_cycles , unsigned int source_divider ) ;
/*! Outputs a colour burst.
@ param number_of_cycles The length of the colour burst .
@ param phase The initial phase of the colour burst in a measuring system with 256 units
per circle , e . g . 0 = 0 degrees , 128 = 180 degrees , 256 = 360 degree .
@ param magnitude The magnitude of the colour burst in 1 / 256 ths of the magnitude of the
positive portion of the wave .
*/
void output_colour_burst ( unsigned int number_of_cycles , uint8_t phase , uint8_t magnitude ) ;
2015-07-19 22:32:42 +00:00
2016-01-12 21:54:09 +00:00
class Delegate {
2015-07-19 22:32:42 +00:00
public :
2015-07-31 22:04:33 +00:00
virtual void crt_did_end_frame ( CRT * crt , CRTFrame * frame , bool did_detect_vsync ) = 0 ;
2015-07-19 22:32:42 +00:00
} ;
2016-01-12 21:54:09 +00:00
void set_delegate ( Delegate * delegate ) ;
2015-07-23 00:33:20 +00:00
void return_frame ( ) ;
2015-07-19 22:32:42 +00:00
2015-07-20 01:21:34 +00:00
void allocate_write_area ( int required_length ) ;
uint8_t * get_write_target_for_buffer ( int buffer ) ;
2015-07-19 22:32:42 +00:00
private :
2015-07-25 03:36:44 +00:00
// the incoming clock lengths will be multiplied by something to give at least 1000
// sample points per line
2015-08-16 20:08:29 +00:00
unsigned int _time_multiplier ;
2015-07-25 03:36:44 +00:00
2015-07-21 20:37:39 +00:00
// fundamental creator-specified properties
2015-08-16 20:08:29 +00:00
unsigned int _cycles_per_line ;
unsigned int _height_of_display ;
2015-07-21 20:37:39 +00:00
// properties directly derived from there
2015-08-16 20:08:29 +00:00
unsigned int _hsync_error_window ; // the permitted window around the expected sync position in which a sync pulse will be recognised; calculated once at init
2015-07-21 20:37:39 +00:00
// the current scanning position
2015-07-22 22:15:18 +00:00
struct Vector {
2015-07-25 03:29:45 +00:00
uint32_t x , y ;
2015-08-02 23:30:45 +00:00
} _rasterPosition , _scanSpeed [ 4 ] , _beamWidth [ 4 ] ;
2015-07-27 03:50:43 +00:00
2015-07-23 00:33:20 +00:00
// the run delegate and the triple buffer
2015-07-25 03:29:45 +00:00
CRTFrameBuilder * _frame_builders [ kCRTNumberOfFrames ] ;
CRTFrameBuilder * _current_frame_builder ;
2015-07-23 00:33:20 +00:00
int _frames_with_delegate ;
int _frame_read_pointer ;
2016-01-12 21:54:09 +00:00
Delegate * _delegate ;
2015-07-20 03:43:22 +00:00
2015-07-21 20:37:39 +00:00
// outer elements of sync separation
2015-07-22 22:15:18 +00:00
bool _is_receiving_sync ; // true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync)
bool _did_detect_hsync ; // true if horizontal sync was detected during this scanline (so, this affects flywheel adjustments)
int _sync_capacitor_charge_level ; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync
int _sync_capacitor_charge_threshold ; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync
2015-08-02 17:48:35 +00:00
int _is_in_vsync ;
2015-07-21 03:18:56 +00:00
2015-07-21 20:37:39 +00:00
// components of the flywheel sync
2015-08-16 20:08:29 +00:00
unsigned int _horizontal_counter ; // time run since the _start_ of the last horizontal sync
unsigned int _expected_next_hsync ; // our current expection of when the next horizontal sync will be encountered (which implies current flywheel velocity)
unsigned int _horizontal_retrace_time ;
2015-07-21 20:37:39 +00:00
bool _is_in_hsync ; // true for the duration of a horizontal sync — used to determine beam running direction and speed
2015-07-31 22:04:33 +00:00
bool _did_detect_vsync ; // true if vertical sync was detected in the input stream rather than forced by emergency measure
2015-07-21 20:37:39 +00:00
// the outer entry point for dispatching output_sync, output_blank, output_level and output_data
2015-07-25 03:29:45 +00:00
enum Type {
Sync , Level , Data , Blank
} type ;
2016-01-22 02:17:47 +00:00
void advance_cycles ( unsigned int number_of_cycles , unsigned int source_divider , bool hsync_requested , bool vsync_requested , bool vsync_charging , Type type ) ;
2015-07-21 01:43:00 +00:00
2015-07-21 20:37:39 +00:00
// the inner entry point that determines whether and when the next sync event will occur within
// the current output window
2015-07-21 01:43:00 +00:00
enum SyncEvent {
None ,
StartHSync , EndHSync ,
StartVSync , EndVSync
} ;
2015-08-16 20:08:29 +00:00
SyncEvent get_next_vertical_sync_event ( bool vsync_is_requested , unsigned int cycles_to_run_for , unsigned int * cycles_advanced ) ;
SyncEvent get_next_horizontal_sync_event ( bool hsync_is_requested , unsigned int cycles_to_run_for , unsigned int * cycles_advanced ) ;
2015-07-19 17:36:27 +00:00
} ;
}
# endif /* CRT_cpp */