1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-13 22:32:03 +00:00

Possibly adds enough for the Electron and ZX80 to start outputting dummy lines.

Let's see!
This commit is contained in:
Thomas Harte 2018-11-03 23:40:39 -04:00
parent b40211d2c0
commit f6562de325
16 changed files with 97 additions and 64 deletions

View File

@ -64,7 +64,7 @@ template <class BusHandler> class MOS6560 {
public: public:
MOS6560(BusHandler &bus_handler) : MOS6560(BusHandler &bus_handler) :
bus_handler_(bus_handler), bus_handler_(bus_handler),
crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::Display::Type::NTSC60, 2)), // crt_(new Outputs::CRT::CRT(65*4, 4, Outputs::Display::Type::NTSC60, 2)),
audio_generator_(audio_queue_), audio_generator_(audio_queue_),
speaker_(audio_generator_) speaker_(audio_generator_)
{ {

View File

@ -49,8 +49,9 @@ struct ReverseTable {
} }
Base::Base(Personality p) : Base::Base(Personality p) :
personality_(p), personality_(p)//,
crt_(new Outputs::CRT::CRT(CRTCyclesPerLine, CRTCyclesDivider, Outputs::Display::Type::NTSC60, 4)) { // crt_(new Outputs::CRT::CRT(CRTCyclesPerLine, CRTCyclesDivider, Outputs::Display::Type::NTSC60, 4))
{
switch(p) { switch(p) {
case TI::TMS::TMS9918A: case TI::TMS::TMS9918A:

View File

@ -326,7 +326,7 @@ class CRTCBusHandler {
/// Constructs an appropriate CRT for video output. /// Constructs an appropriate CRT for video output.
void setup_output(Outputs::Display::ScanTarget *scan_target) { void setup_output(Outputs::Display::ScanTarget *scan_target) {
crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::Display::Type::PAL50, 1)); // crt_.reset(new Outputs::CRT::CRT(1024, 16, Outputs::Display::Type::PAL50, 1));
// crt_->set_rgb_sampling_function( // crt_->set_rgb_sampling_function(
// "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" // "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)"
// "{" // "{"

View File

@ -11,7 +11,7 @@
using namespace AppleII::Video; using namespace AppleII::Video;
VideoBase::VideoBase(bool is_iie, std::function<void(Cycles)> &&target) : VideoBase::VideoBase(bool is_iie, std::function<void(Cycles)> &&target) :
crt_(new Outputs::CRT::CRT(910, 1, Outputs::Display::Type::NTSC60, 1)), // crt_(new Outputs::CRT::CRT(910, 1, Outputs::Display::Type::NTSC60, 1)),
is_iie_(is_iie), is_iie_(is_iie),
deferrer_(std::move(target)) { deferrer_(std::move(target)) {

View File

@ -24,7 +24,7 @@ namespace {
TIA::TIA(bool create_crt) { TIA::TIA(bool create_crt) {
if(create_crt) { if(create_crt) {
crt_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::Display::Type::NTSC60, 1)); // crt_.reset(new Outputs::CRT::CRT(cycles_per_line * 2 - 1, 1, Outputs::Display::Type::NTSC60, 1));
// crt_->set_video_signal(Outputs::Display::VideoSignal::Composite); // crt_->set_video_signal(Outputs::Display::VideoSignal::Composite);
set_output_mode(OutputMode::NTSC); set_output_mode(OutputMode::NTSC);
} }

View File

@ -374,17 +374,9 @@ class ConcreteMachine:
} }
void setup_output(Outputs::Display::ScanTarget *scan_target) override final { void setup_output(Outputs::Display::ScanTarget *scan_target) override final {
video_output_.reset(new VideoOutput(ram_)); video_output_.reset(new VideoOutput(ram_, scan_target));
} }
// void close_output() override final {
// video_output_.reset();
// }
//
// Outputs::CRT::CRT *get_crt() override final {
// return video_output_->get_crt();
// }
Outputs::Speaker::Speaker *get_speaker() override final { Outputs::Speaker::Speaker *get_speaker() override final {
return &speaker_; return &speaker_;
} }

View File

@ -38,12 +38,18 @@ namespace {
// MARK: - Lifecycle // MARK: - Lifecycle
VideoOutput::VideoOutput(uint8_t *memory) : ram_(memory) { VideoOutput::VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target) : ram_(memory) {
memset(palette_, 0xf, sizeof(palette_)); memset(palette_, 0xf, sizeof(palette_));
setup_screen_map(); setup_screen_map();
setup_base_address(); setup_base_address();
crt_.reset(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::Display::Type::PAL50, 1)); crt_.reset(new Outputs::CRT::CRT(
crt_cycles_per_line,
1024,
Outputs::Display::Type::PAL50,
Outputs::Display::ScanTarget::Modals::DataType::Red1Green1Blue1,
scan_target));
// crt_->set_rgb_sampling_function( // crt_->set_rgb_sampling_function(
// "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" // "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)"
// "{" // "{"

View File

@ -27,10 +27,12 @@ namespace Electron {
class VideoOutput { class VideoOutput {
public: public:
/*! /*!
Instantiates a VideoOutput that will read its pixels from @c memory. The pointer supplied Instantiates a VideoOutput that will read its pixels from @c memory and output video
should be to address 0 in the unexpanded Electron's memory map. to @c scan_target.
The pointer supplied should be to address 0 in the unexpanded Electron's memory map.
*/ */
VideoOutput(uint8_t *memory); VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target);
/// @returns the CRT to which output is being painted. /// @returns the CRT to which output is being painted.
Outputs::CRT::CRT *get_crt(); Outputs::CRT::CRT *get_crt();

View File

@ -23,7 +23,7 @@ namespace {
VideoOutput::VideoOutput(uint8_t *memory) : VideoOutput::VideoOutput(uint8_t *memory) :
ram_(memory), ram_(memory),
crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::Display::Type::PAL50, 2)), // crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::Display::Type::PAL50, 2)),
v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition), v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition),
counter_period_(PAL50Period) { counter_period_(PAL50Period) {
// crt_->set_rgb_sampling_function( // crt_->set_rgb_sampling_function(

View File

@ -22,8 +22,9 @@ const std::size_t StandardAllocationSize = 320;
} }
Video::Video() : Video::Video(Outputs::Display::ScanTarget *scan_target) :
crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::Display::Type::PAL50, 1)) { crt_(new Outputs::CRT::CRT(207 * 2, 414, Outputs::Display::Type::PAL50, Outputs::Display::ScanTarget::Modals::DataType::Luminance1, scan_target))
{
// Set a composite sampling function that assumes two-level input; either a byte is 0, which is black, // Set a composite sampling function that assumes two-level input; either a byte is 0, which is black,
// or it is non-zero, which is white. // or it is non-zero, which is white.

View File

@ -27,7 +27,7 @@ namespace ZX8081 {
class Video { class Video {
public: public:
/// Constructs an instance of the video feed; a CRT is also created. /// Constructs an instance of the video feed; a CRT is also created.
Video(); Video(Outputs::Display::ScanTarget *scan_target);
/// @returns The CRT this video feed is feeding. /// @returns The CRT this video feed is feeding.
Outputs::CRT::CRT *get_crt(); Outputs::CRT::CRT *get_crt();

View File

@ -311,7 +311,7 @@ template<bool is_zx81> class ConcreteMachine:
} }
void setup_output(Outputs::Display::ScanTarget *scan_target) override final { void setup_output(Outputs::Display::ScanTarget *scan_target) override final {
video_.reset(new Video); video_.reset(new Video(scan_target));
} }
Outputs::Speaker::Speaker *get_speaker() override final { Outputs::Speaker::Speaker *get_speaker() override final {

View File

@ -29,6 +29,8 @@
#import <OpenGL/OpenGL.h> #import <OpenGL/OpenGL.h>
#include <OpenGL/gl3.h> #include <OpenGL/gl3.h>
#include "ScanTarget.hpp"
@interface CSMachine() <CSFastLoading> @interface CSMachine() <CSFastLoading>
- (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; - (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
- (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker; - (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker;
@ -71,6 +73,27 @@ struct ActivityObserver: public Activity::Observer {
__unsafe_unretained CSMachine *machine; __unsafe_unretained CSMachine *machine;
}; };
class ScanTarget: public Outputs::Display::ScanTarget {
public:
void set_modals(Modals) {}
Scan *get_scan() {
return &scan_;
}
uint8_t *allocate_write_area(size_t required_length, size_t required_alignment) {
write_area_.resize(required_length);
return write_area_.data();
}
void submit(bool only_if_no_allocation_failures) {
}
private:
Scan scan_;
std::vector<uint8_t> write_area_;
};
@implementation CSMachine { @implementation CSMachine {
SpeakerDelegate _speakerDelegate; SpeakerDelegate _speakerDelegate;
ActivityObserver _activityObserver; ActivityObserver _activityObserver;
@ -83,6 +106,8 @@ struct ActivityObserver: public Activity::Observer {
CSJoystickManager *_joystickManager; CSJoystickManager *_joystickManager;
std::bitset<65536> _depressedKeys; std::bitset<65536> _depressedKeys;
NSMutableArray<NSString *> *_leds; NSMutableArray<NSString *> *_leds;
ScanTarget _scanTarget;
} }
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result { - (instancetype)initWithAnalyser:(CSStaticAnalyser *)result {
@ -231,7 +256,7 @@ struct ActivityObserver: public Activity::Observer {
} }
- (void)setupOutputWithAspectRatio:(float)aspectRatio { - (void)setupOutputWithAspectRatio:(float)aspectRatio {
// _machine->crt_machine()->setup_output(aspectRatio); _machine->crt_machine()->setup_output(&_scanTarget);
// Since OS X v10.6, Macs have had a gamma of 2.2. // Since OS X v10.6, Macs have had a gamma of 2.2.
// _machine->crt_machine()->get_crt()->set_output_gamma(2.2f); // _machine->crt_machine()->get_crt()->set_output_gamma(2.2f);

View File

@ -56,12 +56,14 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di
vertical_flywheel_.reset(new Flywheel(multiplied_cycles_per_line * height_of_display, scanlinesVerticalRetraceTime * multiplied_cycles_per_line, (multiplied_cycles_per_line * height_of_display) >> 3)); vertical_flywheel_.reset(new Flywheel(multiplied_cycles_per_line * height_of_display, scanlinesVerticalRetraceTime * multiplied_cycles_per_line, (multiplied_cycles_per_line * height_of_display) >> 3));
// figure out the divisor necessary to get the horizontal flywheel into a 16-bit range // figure out the divisor necessary to get the horizontal flywheel into a 16-bit range
const int real_clock_scan_period = (multiplied_cycles_per_line * height_of_display) / (time_multiplier_ * common_output_divisor_); // const int real_clock_scan_period = (multiplied_cycles_per_line * height_of_display) / (time_multiplier_);
vertical_flywheel_output_divider_ = static_cast<uint16_t>(ceilf(real_clock_scan_period / 65536.0f) * (time_multiplier_ * common_output_divisor_)); // vertical_flywheel_output_divider_ = static_cast<uint16_t>(ceilf(real_clock_scan_period / 65536.0f) * (time_multiplier_));
// openGL_output_builder_.set_timing(cycles_per_line, multiplied_cycles_per_line, height_of_display, horizontal_flywheel_->get_scan_period(), vertical_flywheel_->get_scan_period(), vertical_flywheel_output_divider_); // openGL_output_builder_.set_timing(cycles_per_line, multiplied_cycles_per_line, height_of_display, horizontal_flywheel_->get_scan_period(), vertical_flywheel_->get_scan_period(), vertical_flywheel_output_divider_);
// TODO: set scan_target modals. scan_target_modals_.expected_vertical_lines = height_of_display;
scan_target_modals_.composite_colour_space = colour_space;
scan_target_->set_modals(scan_target_modals_);
} }
void CRT::set_new_display_type(int cycles_per_line, Outputs::Display::Type displayType) { void CRT::set_new_display_type(int cycles_per_line, Outputs::Display::Type displayType) {
@ -91,24 +93,30 @@ void CRT::set_input_gamma(float gamma) {
// update_gamma(); // update_gamma();
} }
CRT::CRT(int common_output_divisor, int buffer_depth) :
common_output_divisor_(common_output_divisor) {}
CRT::CRT( int cycles_per_line, CRT::CRT( int cycles_per_line,
int common_output_divisor, int pixel_clock_least_common_multiple,
int height_of_display, int height_of_display,
Outputs::Display::ColourSpace colour_space, Outputs::Display::ColourSpace colour_space,
int colour_cycle_numerator, int colour_cycle_denominator, int colour_cycle_numerator, int colour_cycle_denominator,
int vertical_sync_half_lines, int vertical_sync_half_lines,
bool should_alternate, bool should_alternate,
int buffer_depth) : Outputs::Display::ScanTarget::Modals::DataType data_type,
CRT(common_output_divisor, buffer_depth) { Outputs::Display::ScanTarget *scan_target) {
scan_target_ = scan_target;
scan_target_modals_.source_data_type = data_type;
scan_target_modals_.pixel_clock_least_common_multiple = pixel_clock_least_common_multiple;
set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator, vertical_sync_half_lines, should_alternate); set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator, vertical_sync_half_lines, should_alternate);
} }
CRT::CRT(int cycles_per_line, int common_output_divisor, Outputs::Display::Type displayType, int buffer_depth) : CRT::CRT( int cycles_per_line,
CRT(common_output_divisor, buffer_depth) { int pixel_clock_least_common_multiple,
set_new_display_type(cycles_per_line, displayType); Outputs::Display::Type display_type,
Outputs::Display::ScanTarget::Modals::DataType data_type,
Outputs::Display::ScanTarget *scan_target) {
scan_target_ = scan_target;
scan_target_modals_.source_data_type = data_type;
scan_target_modals_.pixel_clock_least_common_multiple = pixel_clock_least_common_multiple;
set_new_display_type(cycles_per_line, display_type);
} }
// MARK: - Sync loop // MARK: - Sync loop
@ -193,6 +201,8 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_
} }
} }
} }
scan_target_->submit();
} }
// MARK: - stream feeding methods // MARK: - stream feeding methods

View File

@ -27,12 +27,9 @@ class Delegate {
class CRT { class CRT {
private: private:
CRT(int common_output_divisor, int buffer_depth);
// the incoming clock lengths will be multiplied by something to give at least 1000 // the incoming clock lengths will be multiplied by something to give at least 1000
// sample points per line // sample points per line
int time_multiplier_ = 1; int time_multiplier_ = 1;
const int common_output_divisor_ = 1;
// the two flywheels regulating scanning // the two flywheels regulating scanning
std::unique_ptr<Flywheel> horizontal_flywheel_, vertical_flywheel_; std::unique_ptr<Flywheel> horizontal_flywheel_, vertical_flywheel_;
@ -82,6 +79,7 @@ class CRT {
int cycles_per_line_ = 1; int cycles_per_line_ = 1;
Outputs::Display::ScanTarget *scan_target_ = nullptr; Outputs::Display::ScanTarget *scan_target_ = nullptr;
Outputs::Display::ScanTarget::Modals scan_target_modals_;
public: public:
/*! Constructs the CRT with a specified clock rate, height and colour subcarrier frequency. /*! Constructs the CRT with a specified clock rate, height and colour subcarrier frequency.
@ -91,10 +89,7 @@ class CRT {
@param cycles_per_line The clock rate at which this CRT will be driven, specified as the number @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. of cycles expected to take up one whole scanline of the display.
@param common_output_divisor The greatest a priori common divisor of all cycle counts that will be @param pixel_clock_least_common_multiple TODO.
supplied to @c output_sync, @c output_data, etc; supply 1 if no greater divisor is known. For many
machines output will run at a fixed multiple of the clock rate; knowing this divisor can improve
internal precision.
@param height_of_display The number of lines that nominally form one field of the display, rounded @param height_of_display The number of lines that nominally form one field of the display, rounded
up to the next whole integer. up to the next whole integer.
@ -107,22 +102,22 @@ class CRT {
@param vertical_sync_half_lines The expected length of vertical synchronisation (equalisation pulses aside), @param vertical_sync_half_lines The expected length of vertical synchronisation (equalisation pulses aside),
in multiples of half a line. in multiples of half a line.
@param buffer_depth The depth per pixel of source data buffers to create for this machine. Machines @param data_type TODO.
may provide per-clock-cycle data in the depth that they consider convenient, supplying a sampling
function to convert between their data format and either a composite or RGB signal, allowing that @param scan_target TODO.
work to be offloaded onto the GPU and allowing the output signal to be sampled at a rate appropriate
to the display size.
@see @c set_rgb_sampling_function , @c set_composite_sampling_function @see @c set_rgb_sampling_function , @c set_composite_sampling_function
*/ */
CRT(int cycles_per_line, CRT(int cycles_per_line,
int common_output_divisor, int pixel_clock_least_common_multiple,
int height_of_display, int height_of_display,
Outputs::Display::ColourSpace colour_space, Outputs::Display::ColourSpace colour_space,
int colour_cycle_numerator, int colour_cycle_denominator, int colour_cycle_numerator,
int colour_cycle_denominator,
int vertical_sync_half_lines, int vertical_sync_half_lines,
bool should_alternate, bool should_alternate,
int buffer_depth); Outputs::Display::ScanTarget::Modals::DataType data_type,
Outputs::Display::ScanTarget *scan_target);
/*! Constructs the CRT with the specified clock rate, with the display height and colour /*! 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 subcarrier frequency dictated by a standard display type and with the requested number of
@ -132,9 +127,10 @@ class CRT {
looked up by display type. looked up by display type.
*/ */
CRT(int cycles_per_line, CRT(int cycles_per_line,
int common_output_divisor, int pixel_clock_least_common_multiple,
Outputs::Display::Type display_type, Outputs::Display::Type display_type,
int buffer_depth); Outputs::Display::ScanTarget::Modals::DataType data_type,
Outputs::Display::ScanTarget *scan_target);
/*! Resets the CRT with new timing information. The CRT then continues as though the new timing had /*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
been provided at construction. */ been provided at construction. */
@ -149,7 +145,12 @@ class CRT {
/*! Resets the CRT with new timing information derived from a new display type. The CRT then continues /*! 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. */ as though the new timing had been provided at construction. */
void set_new_display_type(int cycles_per_line, Outputs::Display::Type display_type); 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);
/*! Output at the sync level. /*! Output at the sync level.

View File

@ -34,7 +34,7 @@ struct Rect {
float width, height; float width, height;
} size; } size;
Rect() {} Rect() : origin({0.0f, 0.0f}), size({1.0f, 1.0f}) {}
Rect(float x, float y, float width, float height) : Rect(float x, float y, float width, float height) :
origin({x, y}), size({width, height}) {} origin({x, y}), size({width, height}) {}
}; };
@ -178,7 +178,7 @@ struct ScanTarget {
/// Announces that the owner is finished with the region created by the most recent @c allocate_write_area /// Announces that the owner is finished with the region created by the most recent @c allocate_write_area
/// and indicates that its actual final size was @c actual_length. /// and indicates that its actual final size was @c actual_length.
virtual void reduce_previous_allocation_to(size_t actual_length) = 0; virtual void reduce_previous_allocation_to(size_t actual_length) {};
/// Announces that all endpoint pairs and write areas obtained since the last @c submit have now been /// Announces that all endpoint pairs and write areas obtained since the last @c submit have now been
/// populated with appropriate data. /// populated with appropriate data.
@ -187,15 +187,10 @@ struct ScanTarget {
/// as long as it feels is appropriate subject to an @c flush. /// as long as it feels is appropriate subject to an @c flush.
virtual void submit(bool only_if_no_allocation_failures = true) = 0; virtual void submit(bool only_if_no_allocation_failures = true) = 0;
/// Discards all data and endpoints supplied since the last @c submit. This is generally used when
/// failures in either get_endpoing_pair of allocate_write_area mean that proceeding would produce
/// a faulty total output.
virtual void reset() = 0;
/// Announces that any submitted data not yet output should be output now, but needn't block while /// Announces that any submitted data not yet output should be output now, but needn't block while
/// doing so. This generally communicates that processing is now otherwise 'up to date', so no /// doing so. This generally communicates that processing is now otherwise 'up to date', so no
/// further delay should be allowed. /// further delay should be allowed.
virtual void flush() = 0; // virtual void flush() = 0;
/* /*