2015-07-19 17:36:27 +00:00
//
// CRT.hpp
// Clock Signal
//
// Created by Thomas Harte on 19/07/2015.
2018-05-13 19:19:52 +00:00
// Copyright 2015 Thomas Harte. All rights reserved.
2015-07-19 17:36:27 +00:00
//
2016-03-09 01:59:16 +00:00
# ifndef CRT_hpp
# define CRT_hpp
2015-07-19 17:36:27 +00:00
2017-11-11 20:28:40 +00:00
# include <cstdint>
2018-11-04 01:54:25 +00:00
# include <memory>
2015-07-19 17:36:27 +00:00
2018-11-04 01:54:25 +00:00
# include "../ScanTarget.hpp"
2016-03-09 01:59:16 +00:00
# include "Internals/Flywheel.hpp"
2018-10-31 01:50:35 +00:00
2015-07-25 03:29:45 +00:00
namespace Outputs {
2016-03-09 01:49:07 +00:00
namespace CRT {
2015-07-23 00:33:20 +00:00
2016-04-24 22:36:22 +00:00
class CRT ;
class Delegate {
public :
2018-11-03 23:58:44 +00:00
virtual void crt_did_end_batch_of_frames ( CRT * crt , int number_of_frames , int number_of_unexpected_vertical_syncs ) = 0 ;
2016-04-24 22:36:22 +00:00
} ;
2016-03-09 03:40:23 +00:00
class CRT {
private :
// the incoming clock lengths will be multiplied by something to give at least 1000
// sample points per line
2018-11-03 23:58:44 +00:00
int time_multiplier_ = 1 ;
2016-02-08 03:18:55 +00:00
2016-03-09 03:40:23 +00:00
// the two flywheels regulating scanning
2016-11-17 03:00:11 +00:00
std : : unique_ptr < Flywheel > horizontal_flywheel_ , vertical_flywheel_ ;
2018-11-05 02:06:25 +00:00
int vertical_flywheel_output_divider_ = 1 ;
2015-07-23 00:33:20 +00:00
2016-03-09 03:40:23 +00:00
struct Scan {
enum Type {
Sync , Level , Data , Blank , ColourBurst
} type ;
2018-11-03 23:58:44 +00:00
int number_of_cycles , number_of_samples ;
2016-03-09 03:40:23 +00:00
union {
struct {
uint8_t phase , amplitude ;
} ;
} ;
} ;
2016-03-09 03:54:05 +00:00
void output_scan ( const Scan * scan ) ;
2016-02-07 22:32:38 +00:00
2018-11-03 23:58:44 +00:00
int16_t colour_burst_angle_ = 0 ;
uint8_t colour_burst_amplitude_ = 30 ;
int colour_burst_phase_adjustment_ = 0 ;
2017-10-18 02:34:10 +00:00
bool is_writing_composite_run_ = false ;
2016-03-09 01:49:07 +00:00
2018-11-03 23:58:44 +00:00
int phase_denominator_ = 1 , phase_numerator_ = 1 , colour_cycle_numerator_ = 1 ;
2017-10-18 02:34:10 +00:00
bool is_alernate_line_ = false , phase_alternates_ = false ;
2016-12-10 18:42:34 +00:00
2016-03-09 03:40:23 +00:00
// the outer entry point for dispatching output_sync, output_blank, output_level and output_data
2018-11-03 23:58:44 +00:00
void advance_cycles ( int number_of_cycles , bool hsync_requested , bool vsync_requested , const Scan : : Type type , int number_of_samples ) ;
2016-03-09 03:40:23 +00:00
// the inner entry point that determines whether and when the next sync event will occur within
// the current output window
2018-11-03 23:58:44 +00:00
Flywheel : : SyncEvent get_next_vertical_sync_event ( bool vsync_is_requested , int cycles_to_run_for , int * cycles_advanced ) ;
Flywheel : : SyncEvent get_next_horizontal_sync_event ( bool hsync_is_requested , int cycles_to_run_for , int * cycles_advanced ) ;
2016-05-10 23:04:03 +00:00
2017-02-20 02:46:07 +00:00
// the delegate
2017-10-18 02:34:10 +00:00
Delegate * delegate_ = nullptr ;
2018-11-03 23:58:44 +00:00
int frames_since_last_delegate_call_ = 0 ;
2017-02-20 02:46:07 +00:00
2017-03-07 00:15:33 +00:00
// sync counter, for determining vertical sync
2017-10-18 02:34:10 +00:00
bool is_receiving_sync_ = false ; // true if the CRT is currently receiving sync (i.e. this is for edge triggering of horizontal sync)
bool is_accumulating_sync_ = false ; // true if a sync level has triggered the suspicion that a vertical sync might be in progress
bool is_refusing_sync_ = false ; // true once a vertical sync has been detected, until a prolonged period of non-sync has ended suspicion of an ongoing vertical sync
2018-11-03 23:58:44 +00:00
int sync_capacitor_charge_threshold_ = 0 ; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync
int cycles_of_sync_ = 0 ; // the number of cycles since the potential vertical sync began
int cycles_since_sync_ = 0 ; // the number of cycles since last in sync, for defeating the possibility of this being a vertical sync
2017-07-29 22:25:04 +00:00
2018-11-03 23:58:44 +00:00
int cycles_per_line_ = 1 ;
2017-03-07 00:15:33 +00:00
2018-11-04 01:54:25 +00:00
Outputs : : Display : : ScanTarget * scan_target_ = nullptr ;
2018-11-04 03:40:39 +00:00
Outputs : : Display : : ScanTarget : : Modals scan_target_modals_ ;
2017-08-10 19:17:08 +00:00
2016-03-09 03:40:23 +00:00
public :
2016-02-03 03:41:33 +00:00
/*! Constructs the CRT with a specified clock rate, height and colour subcarrier frequency.
The requested number of buffers , each with the requested number of bytes per pixel ,
is created for the machine to write raw pixel data to .
2015-07-23 00:33:20 +00:00
2016-02-03 03:41:33 +00:00
@ param cycles_per_line The clock rate at which this CRT will be driven , specified as the number
of cycles expected to take up one whole scanline of the display .
2016-12-10 18:42:34 +00:00
2018-11-05 02:06:25 +00:00
@ param minimum_cycles_per_pixel TODO .
2015-07-23 00:33:20 +00:00
2016-10-13 01:42:36 +00:00
@ param height_of_display The number of lines that nominally form one field of the display , rounded
2016-02-03 03:41:33 +00:00
up to the next whole integer .
2015-07-23 00:33:20 +00:00
2016-02-03 03:41:33 +00:00
@ param colour_cycle_numerator Specifies the numerator for the per - line frequency of the colour subcarrier .
2015-07-23 00:33:20 +00:00
2016-02-03 03:41:33 +00:00
@ param colour_cycle_denominator Specifies the denominator for the per - line frequency of the colour subcarrier .
The colour subcarrier is taken to have colour_cycle_numerator / colour_cycle_denominator cycles per line .
2015-07-23 00:33:20 +00:00
2017-07-29 18:53:53 +00:00
@ param vertical_sync_half_lines The expected length of vertical synchronisation ( equalisation pulses aside ) ,
in multiples of half a line .
2018-11-04 03:40:39 +00:00
@ param data_type TODO .
@ param scan_target TODO .
2015-07-23 00:33:20 +00:00
2016-02-03 03:41:33 +00:00
@ see @ c set_rgb_sampling_function , @ c set_composite_sampling_function
*/
2018-11-03 23:58:44 +00:00
CRT ( int cycles_per_line ,
2018-11-05 02:06:25 +00:00
int minimum_cycles_per_pixel ,
2018-11-03 23:58:44 +00:00
int height_of_display ,
2018-11-04 01:54:25 +00:00
Outputs : : Display : : ColourSpace colour_space ,
2018-11-04 03:40:39 +00:00
int colour_cycle_numerator ,
int colour_cycle_denominator ,
2018-11-03 23:58:44 +00:00
int vertical_sync_half_lines ,
2017-07-29 18:53:53 +00:00
bool should_alternate ,
2018-11-04 03:40:39 +00:00
Outputs : : Display : : ScanTarget : : Modals : : DataType data_type ,
Outputs : : Display : : ScanTarget * scan_target ) ;
2016-02-03 03:41:33 +00:00
/*! Constructs the CRT with the specified clock rate, with the display height and colour
subcarrier frequency dictated by a standard display type and with the requested number of
buffers , each with the requested number of bytes per pixel .
Exactly identical to calling the designated constructor with colour subcarrier information
looked up by display type .
*/
2018-11-03 23:58:44 +00:00
CRT ( int cycles_per_line ,
2018-11-05 02:06:25 +00:00
int minimum_cycles_per_pixel ,
2018-11-04 01:54:25 +00:00
Outputs : : Display : : Type display_type ,
2018-11-04 03:40:39 +00:00
Outputs : : Display : : ScanTarget : : Modals : : DataType data_type ,
Outputs : : Display : : ScanTarget * scan_target ) ;
2015-07-20 01:21:34 +00:00
2016-02-03 03:41:33 +00:00
/*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
been provided at construction . */
2018-11-04 01:54:25 +00:00
void set_new_timing (
int cycles_per_line ,
int height_of_display ,
Outputs : : Display : : ColourSpace colour_space ,
int colour_cycle_numerator ,
int colour_cycle_denominator ,
int vertical_sync_half_lines ,
bool should_alternate ) ;
2016-02-03 03:41:33 +00:00
/*! Resets the CRT with new timing information derived from a new display type. The CRT then continues
as though the new timing had been provided at construction . */
2018-11-04 03:40:39 +00:00
void set_new_display_type (
int cycles_per_line ,
Outputs : : Display : : Type display_type ) ;
// TODO.
void set_new_data_type ( Outputs : : Display : : ScanTarget : : Modals : : DataType data_type ) ;
2015-07-31 22:04:33 +00:00
2016-02-03 03:41:33 +00:00
/*! Output at the sync level.
2016-01-22 02:17:47 +00:00
@ param number_of_cycles The amount of time to putput sync for .
*/
2018-11-03 23:58:44 +00:00
void output_sync ( int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
2016-02-03 03:41:33 +00:00
/*! Output at the blanking level.
2016-01-22 02:17:47 +00:00
@ param number_of_cycles The amount of time to putput the blanking level for .
*/
2018-11-03 23:58:44 +00:00
void output_blank ( int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
2016-02-03 03:41:33 +00:00
/*! Outputs the first written to the most-recently created run of data repeatedly for a prolonged period.
2016-01-22 02:17:47 +00:00
@ param number_of_cycles The number of cycles to repeat the output for .
*/
2018-11-03 23:58:44 +00:00
void output_level ( int number_of_cycles ) ;
2016-01-22 02:17:47 +00:00
2016-02-03 03:41:33 +00:00
/*! Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer
2018-04-17 00:00:56 +00:00
that is at least @ c number_of_samples long , and that the first @ c number_of_samples should be spread
over @ c number_of_cycles .
2016-01-22 02:17:47 +00:00
@ param number_of_cycles The amount of data to output .
2018-04-17 00:00:56 +00:00
@ param number_of_samples The number of samples of input data to output .
2016-02-03 03:41:33 +00:00
@ see @ c allocate_write_area , @ c get_write_target_for_buffer
2016-01-22 02:17:47 +00:00
*/
2018-11-03 23:58:44 +00:00
void output_data ( int number_of_cycles , size_t number_of_samples ) ;
2018-04-17 00:00:56 +00:00
/*! A shorthand form for output_data that assumes the number of cycles to output for is the same as the number of samples. */
2018-11-03 23:58:44 +00:00
void output_data ( int number_of_cycles ) {
output_data ( number_of_cycles , size_t ( number_of_cycles ) ) ;
2018-04-17 00:00:56 +00:00
}
2016-01-22 02:17:47 +00:00
2016-02-03 03:41:33 +00:00
/*! Outputs a colour burst.
2016-01-22 02:17:47 +00:00
@ 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 .
2016-03-06 01:47:11 +00:00
@ param amplitude The amplitude of the colour burst in 1 / 256 ths of the amplitude of the
2016-01-22 02:17:47 +00:00
positive portion of the wave .
*/
2018-11-03 23:58:44 +00:00
void output_colour_burst ( int number_of_cycles , uint8_t phase , uint8_t amplitude = 102 ) ;
2015-07-19 22:32:42 +00:00
2016-12-10 18:42:34 +00:00
/*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude.
@ param number_of_cycles The length of the colour burst ;
*/
2018-11-03 23:58:44 +00:00
void output_default_colour_burst ( int number_of_cycles ) ;
2016-12-10 18:42:34 +00:00
2018-03-03 18:53:00 +00:00
/*! Sets the current phase of the colour subcarrier used by output_default_colour_burst.
2018-05-13 19:34:31 +00:00
@ param phase The normalised instantaneous phase . 0.0f is the start of a colour cycle , 1.0f is the
2018-03-03 18:53:00 +00:00
end of a colour cycle , 0.25f is a quarter of the way through a colour cycle , etc .
*/
void set_immediate_default_phase ( float phase ) ;
2016-05-04 11:39:45 +00:00
/*! Attempts to allocate the given number of output samples for writing.
2016-02-03 03:41:33 +00:00
The beginning of the most recently allocated area is used as the start
of data written by a call to @ c output_data ; it is acceptable to write and to
output less data than the amount requested but that may be less efficient .
2016-05-04 11:39:45 +00:00
Allocation should fail only if emulation is running significantly below real speed .
2016-02-03 03:41:33 +00:00
@ param required_length The number of samples to allocate .
2016-05-04 11:39:45 +00:00
@ returns A pointer to the allocated area if room is available ; @ c nullptr otherwise .
2016-02-03 03:41:33 +00:00
*/
2017-11-11 20:28:40 +00:00
inline uint8_t * allocate_write_area ( std : : size_t required_length , std : : size_t required_alignment = 1 ) {
2018-11-03 23:58:44 +00:00
return scan_target_ - > allocate_write_area ( required_length , required_alignment ) ;
2017-11-13 00:29:22 +00:00
}
2017-08-10 19:17:08 +00:00
/*! Sets the gamma exponent for the simulated screen. */
void set_input_gamma ( float gamma ) ;
2017-05-17 00:31:39 +00:00
enum CompositeSourceType {
/// The composite function provides continuous output.
Continuous ,
/// The composite function provides discrete output with four unique values per colour cycle.
DiscreteFourSamplesPerCycle
} ;
2018-05-13 19:34:31 +00:00
/*! Provides information about the type of output the composite sampling function provides, discrete or continuous.
2017-05-17 00:31:39 +00:00
This is necessary because the CRT implementation samples discretely and therefore can use fewer intermediate
samples if it can exactly duplicate the sampling rate and placement of the composite sampling function .
A continuous function is assumed by default .
@ param type The type of output provided by the function supplied to ` set_composite_sampling_function ` .
@ param offset_of_first_sample The relative position within a full cycle of the colour subcarrier at which the
first sample falls . E . g . 0.125 means " at 1/8th of the way through the complete cycle " .
*/
void set_composite_function_type ( CompositeSourceType type , float offset_of_first_sample = 0.0f ) ;
2018-11-04 01:54:25 +00:00
inline void set_visible_area ( Outputs : : Display : : Rect visible_area ) {
2016-02-20 22:32:51 +00:00
}
2016-04-12 01:47:23 +00:00
2018-11-04 01:54:25 +00:00
Outputs : : Display : : Rect get_rect_for_area (
int first_line_after_sync ,
int number_of_lines ,
int first_cycle_after_sync ,
int number_of_cycles ,
float aspect_ratio ) ;
2016-04-24 22:36:22 +00:00
2017-03-26 18:34:47 +00:00
inline void set_delegate ( Delegate * delegate ) {
2016-11-17 03:00:11 +00:00
delegate_ = delegate ;
2016-04-24 22:36:22 +00:00
}
2015-07-19 17:36:27 +00:00
} ;
}
2016-03-09 01:49:07 +00:00
}
2015-07-19 17:36:27 +00:00
# endif /* CRT_cpp */