mirror of
https://github.com/TomHarte/CLK.git
synced 2024-06-17 06:29:28 +00:00
Kills setup_output
definitively, saving some indirection. set_scan_target
takes its place.
This commit is contained in:
parent
87df8b9e85
commit
8a699b6072
|
@ -53,7 +53,7 @@ void MultiCRTMachine::perform_serial(const std::function<void (::CRTMachine::Mac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiCRTMachine::setup_output(Outputs::Display::ScanTarget *scan_target) {
|
void MultiCRTMachine::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||||
perform_serial([=](::CRTMachine::Machine *machine) {
|
perform_serial([=](::CRTMachine::Machine *machine) {
|
||||||
// TODO.
|
// TODO.
|
||||||
// machine->setup_output(aspect_ratio);
|
// machine->setup_output(aspect_ratio);
|
||||||
|
|
|
@ -53,7 +53,7 @@ class MultiCRTMachine: public CRTMachine::Machine {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Below is the standard CRTMachine::Machine interface; see there for documentation.
|
// Below is the standard CRTMachine::Machine interface; see there for documentation.
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override;
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override;
|
||||||
Outputs::Speaker::Speaker *get_speaker() override;
|
Outputs::Speaker::Speaker *get_speaker() override;
|
||||||
void run_for(Time::Seconds duration) override;
|
void run_for(Time::Seconds duration) override;
|
||||||
|
|
||||||
|
|
|
@ -325,7 +325,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 set_scan_target(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)"
|
||||||
|
@ -983,8 +983,8 @@ template <bool has_fdc> class ConcreteMachine:
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A CRTMachine function; indicates that outputs should be created now.
|
/// A CRTMachine function; indicates that outputs should be created now.
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override final {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final {
|
||||||
crtc_bus_handler_.setup_output(scan_target);
|
crtc_bus_handler_.set_scan_target(scan_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A CRTMachine function; indicates that outputs should be destroyed now.
|
/// A CRTMachine function; indicates that outputs should be destroyed now.
|
||||||
|
|
|
@ -396,7 +396,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||||
audio_queue_.flush();
|
audio_queue_.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
video_.reset(new AppleII::Video::Video<VideoBusHandler, is_iie()>(video_bus_handler_));
|
video_.reset(new AppleII::Video::Video<VideoBusHandler, is_iie()>(video_bus_handler_));
|
||||||
video_->set_character_rom(character_rom_);
|
video_->set_character_rom(character_rom_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ class ConcreteMachine:
|
||||||
}
|
}
|
||||||
|
|
||||||
// to satisfy CRTMachine::Machine
|
// to satisfy CRTMachine::Machine
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
bus_->tia_.reset(new TIA);
|
bus_->tia_.reset(new TIA);
|
||||||
bus_->speaker_.set_input_rate(static_cast<float>(get_clock_rate() / static_cast<double>(CPUTicksPerAudioTick)));
|
bus_->speaker_.set_input_rate(static_cast<float>(get_clock_rate() / static_cast<double>(CPUTicksPerAudioTick)));
|
||||||
bus_->tia_->get_crt()->set_delegate(this);
|
bus_->tia_->get_crt()->set_delegate(this);
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Machine {
|
||||||
The @c scan_target will receive all video output; the caller guarantees
|
The @c scan_target will receive all video output; the caller guarantees
|
||||||
that it is non-null.
|
that it is non-null.
|
||||||
*/
|
*/
|
||||||
virtual void setup_output(Outputs::Display::ScanTarget *scan_target) = 0;
|
virtual void set_scan_target(Outputs::Display::ScanTarget *scan_target) = 0;
|
||||||
|
|
||||||
/// @returns The speaker that receives this machine's output, or @c nullptr if this machine is mute.
|
/// @returns The speaker that receives this machine's output, or @c nullptr if this machine is mute.
|
||||||
virtual Outputs::Speaker::Speaker *get_speaker() = 0;
|
virtual Outputs::Speaker::Speaker *get_speaker() = 0;
|
||||||
|
|
|
@ -169,7 +169,7 @@ class ConcreteMachine:
|
||||||
return joysticks_;
|
return joysticks_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A));
|
vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A));
|
||||||
// get_crt()->set_video_signal(Outputs::Display::VideoSignal::Composite);
|
// get_crt()->set_video_signal(Outputs::Display::VideoSignal::Composite);
|
||||||
}
|
}
|
||||||
|
|
|
@ -620,7 +620,7 @@ class ConcreteMachine:
|
||||||
m6502_.run_for(cycles);
|
m6502_.run_for(cycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override final {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final {
|
||||||
mos6560_.reset(new MOS::MOS6560::MOS6560<Vic6560BusHandler>(mos6560_bus_handler_));
|
mos6560_.reset(new MOS::MOS6560::MOS6560<Vic6560BusHandler>(mos6560_bus_handler_));
|
||||||
mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20.
|
mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20.
|
||||||
mos6560_->set_output_mode(output_mode_);
|
mos6560_->set_output_mode(output_mode_);
|
||||||
|
|
|
@ -51,6 +51,7 @@ class ConcreteMachine:
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
m6502_(*this),
|
m6502_(*this),
|
||||||
|
video_output_(ram_),
|
||||||
sound_generator_(audio_queue_),
|
sound_generator_(audio_queue_),
|
||||||
speaker_(sound_generator_) {
|
speaker_(sound_generator_) {
|
||||||
memset(key_states_, 0, sizeof(key_states_));
|
memset(key_states_, 0, sizeof(key_states_));
|
||||||
|
@ -160,7 +161,7 @@ class ConcreteMachine:
|
||||||
|
|
||||||
// for the entire frame, RAM is accessible only on odd cycles; in modes below 4
|
// for the entire frame, RAM is accessible only on odd cycles; in modes below 4
|
||||||
// it's also accessible only outside of the pixel regions
|
// it's also accessible only outside of the pixel regions
|
||||||
cycles += video_output_->get_cycles_until_next_ram_availability(cycles_since_display_update_.as_int() + 1);
|
cycles += video_output_.get_cycles_until_next_ram_availability(cycles_since_display_update_.as_int() + 1);
|
||||||
} else {
|
} else {
|
||||||
switch(address & 0xff0f) {
|
switch(address & 0xff0f) {
|
||||||
case 0xfe00:
|
case 0xfe00:
|
||||||
|
@ -198,8 +199,8 @@ class ConcreteMachine:
|
||||||
case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f:
|
case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f:
|
||||||
if(!isReadOperation(operation)) {
|
if(!isReadOperation(operation)) {
|
||||||
update_display();
|
update_display();
|
||||||
video_output_->set_register(address, *value);
|
video_output_.set_register(address, *value);
|
||||||
video_access_range_ = video_output_->get_memory_access_range();
|
video_access_range_ = video_output_.get_memory_access_range();
|
||||||
queue_next_display_interrupt();
|
queue_next_display_interrupt();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -373,8 +374,8 @@ class ConcreteMachine:
|
||||||
audio_queue_.perform();
|
audio_queue_.perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override final {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final {
|
||||||
video_output_.reset(new VideoOutput(ram_, scan_target));
|
video_output_.set_scan_target(scan_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
Outputs::Speaker::Speaker *get_speaker() override final {
|
Outputs::Speaker::Speaker *get_speaker() override final {
|
||||||
|
@ -500,12 +501,12 @@ class ConcreteMachine:
|
||||||
// MARK: - Work deferral updates.
|
// MARK: - Work deferral updates.
|
||||||
inline void update_display() {
|
inline void update_display() {
|
||||||
if(cycles_since_display_update_ > 0) {
|
if(cycles_since_display_update_ > 0) {
|
||||||
video_output_->run_for(cycles_since_display_update_.flush());
|
video_output_.run_for(cycles_since_display_update_.flush());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void queue_next_display_interrupt() {
|
inline void queue_next_display_interrupt() {
|
||||||
VideoOutput::Interrupt next_interrupt = video_output_->get_next_interrupt();
|
VideoOutput::Interrupt next_interrupt = video_output_.get_next_interrupt();
|
||||||
cycles_until_display_interrupt_ = next_interrupt.cycles;
|
cycles_until_display_interrupt_ = next_interrupt.cycles;
|
||||||
next_display_interrupt_ = next_interrupt.interrupt;
|
next_display_interrupt_ = next_interrupt.interrupt;
|
||||||
}
|
}
|
||||||
|
@ -575,7 +576,7 @@ class ConcreteMachine:
|
||||||
int shift_restart_counter_ = 0;
|
int shift_restart_counter_ = 0;
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
std::unique_ptr<VideoOutput> video_output_;
|
VideoOutput video_output_;
|
||||||
|
|
||||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||||
SoundGenerator sound_generator_;
|
SoundGenerator sound_generator_;
|
||||||
|
|
|
@ -38,18 +38,16 @@ namespace {
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
VideoOutput::VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target) : ram_(memory) {
|
VideoOutput::VideoOutput(uint8_t *memory) :
|
||||||
|
ram_(memory),
|
||||||
|
crt_(crt_cycles_per_line,
|
||||||
|
1,
|
||||||
|
Outputs::Display::Type::PAL50,
|
||||||
|
Outputs::Display::InputDataType::Red1Green1Blue1) {
|
||||||
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,
|
|
||||||
1,
|
|
||||||
Outputs::Display::Type::PAL50,
|
|
||||||
Outputs::Display::InputDataType::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)"
|
||||||
// "{"
|
// "{"
|
||||||
|
@ -57,13 +55,11 @@ VideoOutput::VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_tar
|
||||||
// "return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));"
|
// "return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));"
|
||||||
// "}");
|
// "}");
|
||||||
// TODO: as implied below, I've introduced a clock's latency into the graphics pipeline somehow. Investigate.
|
// TODO: as implied below, I've introduced a clock's latency into the graphics pipeline somehow. Investigate.
|
||||||
crt_->set_visible_area(crt_->get_rect_for_area(first_graphics_line - 1, 256, (first_graphics_cycle+1) * crt_cycles_multiplier, 80 * crt_cycles_multiplier, 4.0f / 3.0f));
|
crt_.set_visible_area(crt_.get_rect_for_area(first_graphics_line - 1, 256, (first_graphics_cycle+1) * crt_cycles_multiplier, 80 * crt_cycles_multiplier, 4.0f / 3.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - CRT getter
|
void VideoOutput::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||||
|
crt_.set_scan_target(scan_target);
|
||||||
Outputs::CRT::CRT *VideoOutput::get_crt() {
|
|
||||||
return crt_.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Display update methods
|
// MARK: - Display update methods
|
||||||
|
@ -95,7 +91,7 @@ void VideoOutput::start_pixel_line() {
|
||||||
void VideoOutput::end_pixel_line() {
|
void VideoOutput::end_pixel_line() {
|
||||||
if(current_output_target_) {
|
if(current_output_target_) {
|
||||||
const int data_length = int(current_output_target_ - initial_output_target_);
|
const int data_length = int(current_output_target_ - initial_output_target_);
|
||||||
crt_->output_data(data_length * current_output_divider_, size_t(data_length));
|
crt_.output_data(data_length * current_output_divider_, size_t(data_length));
|
||||||
}
|
}
|
||||||
current_character_row_++;
|
current_character_row_++;
|
||||||
}
|
}
|
||||||
|
@ -104,7 +100,7 @@ void VideoOutput::output_pixels(int number_of_cycles) {
|
||||||
if(!number_of_cycles) return;
|
if(!number_of_cycles) return;
|
||||||
|
|
||||||
if(is_blank_line_) {
|
if(is_blank_line_) {
|
||||||
crt_->output_blank(number_of_cycles * crt_cycles_multiplier);
|
crt_.output_blank(number_of_cycles * crt_cycles_multiplier);
|
||||||
} else {
|
} else {
|
||||||
int divider = 1;
|
int divider = 1;
|
||||||
switch(screen_mode_) {
|
switch(screen_mode_) {
|
||||||
|
@ -116,10 +112,10 @@ void VideoOutput::output_pixels(int number_of_cycles) {
|
||||||
if(!initial_output_target_ || divider != current_output_divider_) {
|
if(!initial_output_target_ || divider != current_output_divider_) {
|
||||||
if(current_output_target_) {
|
if(current_output_target_) {
|
||||||
const int data_length = int(current_output_target_ - initial_output_target_);
|
const int data_length = int(current_output_target_ - initial_output_target_);
|
||||||
crt_->output_data(data_length * current_output_divider_, size_t(data_length));
|
crt_.output_data(data_length * current_output_divider_, size_t(data_length));
|
||||||
}
|
}
|
||||||
current_output_divider_ = divider;
|
current_output_divider_ = divider;
|
||||||
initial_output_target_ = current_output_target_ = crt_->begin_data(size_t(640 / current_output_divider_), size_t(8 / divider));
|
initial_output_target_ = current_output_target_ = crt_.begin_data(size_t(640 / current_output_divider_), size_t(8 / divider));
|
||||||
}
|
}
|
||||||
|
|
||||||
#define get_pixel() \
|
#define get_pixel() \
|
||||||
|
@ -242,9 +238,9 @@ void VideoOutput::run_for(const Cycles cycles) {
|
||||||
cycles_into_draw_action_ += time_left_in_action;
|
cycles_into_draw_action_ += time_left_in_action;
|
||||||
if(cycles_into_draw_action_ == draw_action_length) {
|
if(cycles_into_draw_action_ == draw_action_length) {
|
||||||
switch(screen_map_[screen_map_pointer_].type) {
|
switch(screen_map_[screen_map_pointer_].type) {
|
||||||
case DrawAction::Sync: crt_->output_sync(draw_action_length * crt_cycles_multiplier); break;
|
case DrawAction::Sync: crt_.output_sync(draw_action_length * crt_cycles_multiplier); break;
|
||||||
case DrawAction::ColourBurst: crt_->output_default_colour_burst(draw_action_length * crt_cycles_multiplier); break;
|
case DrawAction::ColourBurst: crt_.output_default_colour_burst(draw_action_length * crt_cycles_multiplier); break;
|
||||||
case DrawAction::Blank: crt_->output_blank(draw_action_length * crt_cycles_multiplier); break;
|
case DrawAction::Blank: crt_.output_blank(draw_action_length * crt_cycles_multiplier); break;
|
||||||
case DrawAction::Pixels: end_pixel_line(); break;
|
case DrawAction::Pixels: end_pixel_line(); break;
|
||||||
}
|
}
|
||||||
screen_map_pointer_ = (screen_map_pointer_ + 1) % screen_map_.size();
|
screen_map_pointer_ = (screen_map_pointer_ + 1) % screen_map_.size();
|
||||||
|
|
|
@ -27,19 +27,18 @@ namespace Electron {
|
||||||
class VideoOutput {
|
class VideoOutput {
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
Instantiates a VideoOutput that will read its pixels from @c memory and output video
|
Instantiates a VideoOutput that will read its pixels from @c memory.
|
||||||
to @c scan_target.
|
|
||||||
|
|
||||||
The pointer supplied should be to address 0 in the unexpanded Electron's memory map.
|
The pointer supplied should be to address 0 in the unexpanded Electron's memory map.
|
||||||
*/
|
*/
|
||||||
VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target);
|
VideoOutput(uint8_t *memory);
|
||||||
|
|
||||||
/// @returns the CRT to which output is being painted.
|
|
||||||
Outputs::CRT::CRT *get_crt();
|
|
||||||
|
|
||||||
/// Produces the next @c cycles of video output.
|
/// Produces the next @c cycles of video output.
|
||||||
void run_for(const Cycles cycles);
|
void run_for(const Cycles cycles);
|
||||||
|
|
||||||
|
/// Sets the destination for output.
|
||||||
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Writes @c value to the register at @c address. May mutate the results of @c get_next_interrupt,
|
Writes @c value to the register at @c address. May mutate the results of @c get_next_interrupt,
|
||||||
@c get_cycles_until_next_ram_availability and @c get_memory_access_range.
|
@c get_cycles_until_next_ram_availability and @c get_memory_access_range.
|
||||||
|
@ -114,8 +113,7 @@ class VideoOutput {
|
||||||
uint8_t *current_output_target_ = nullptr;
|
uint8_t *current_output_target_ = nullptr;
|
||||||
uint8_t *initial_output_target_ = nullptr;
|
uint8_t *initial_output_target_ = nullptr;
|
||||||
int current_output_divider_ = 1;
|
int current_output_divider_ = 1;
|
||||||
|
Outputs::CRT::CRT crt_;
|
||||||
std::unique_ptr<Outputs::CRT::CRT> crt_;
|
|
||||||
|
|
||||||
struct DrawAction {
|
struct DrawAction {
|
||||||
enum Type {
|
enum Type {
|
||||||
|
|
|
@ -218,7 +218,7 @@ class ConcreteMachine:
|
||||||
audio_queue_.flush();
|
audio_queue_.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A));
|
vdp_.reset(new TI::TMS::TMS9918(TI::TMS::TMS9918A));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,7 +161,7 @@ class ConcreteMachine:
|
||||||
audio_queue_.flush();
|
audio_queue_.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
|
||||||
TI::TMS::Personality personality = TI::TMS::TMS9918A;
|
TI::TMS::Personality personality = TI::TMS::TMS9918A;
|
||||||
switch(model_) {
|
switch(model_) {
|
||||||
case Target::Model::SG1000: personality = TI::TMS::TMS9918A; break;
|
case Target::Model::SG1000: personality = TI::TMS::TMS9918A; break;
|
||||||
|
|
|
@ -456,7 +456,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||||
}
|
}
|
||||||
|
|
||||||
// to satisfy CRTMachine::Machine
|
// to satisfy CRTMachine::Machine
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override final {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final {
|
||||||
speaker_.set_input_rate(1000000.0f);
|
speaker_.set_input_rate(1000000.0f);
|
||||||
|
|
||||||
video_output_.reset(new VideoOutput(ram_));
|
video_output_.reset(new VideoOutput(ram_));
|
||||||
|
|
|
@ -22,21 +22,20 @@ const std::size_t StandardAllocationSize = 320;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Video::Video(Outputs::Display::ScanTarget *scan_target) :
|
Video::Video() :
|
||||||
crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Luminance1, scan_target))
|
crt_(207 * 2, 1, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Luminance1) {
|
||||||
{
|
|
||||||
|
|
||||||
// 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.
|
||||||
// crt_->set_composite_sampling_function(
|
// crt_.set_composite_sampling_function(
|
||||||
// "float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)"
|
// "float composite_sample(usampler2D sampler, vec2 coordinate, float phase, float amplitude)"
|
||||||
// "{"
|
// "{"
|
||||||
// "return texture(sampler, coordinate).r;"
|
// "return texture(sampler, coordinate).r;"
|
||||||
// "}");
|
// "}");
|
||||||
|
|
||||||
// Show only the centre 80% of the TV frame.
|
// Show only the centre 80% of the TV frame.
|
||||||
// crt_->set_video_signal(Outputs::Display::VideoSignal::Composite);
|
// crt_.set_video_signal(Outputs::Display::VideoSignal::Composite);
|
||||||
crt_->set_visible_area(Outputs::Display::Rect(0.1f, 0.1f, 0.8f, 0.8f));
|
crt_.set_visible_area(Outputs::Display::Rect(0.1f, 0.1f, 0.8f, 0.8f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Video::run_for(const HalfCycles half_cycles) {
|
void Video::run_for(const HalfCycles half_cycles) {
|
||||||
|
@ -51,7 +50,7 @@ void Video::flush() {
|
||||||
void Video::flush(bool next_sync) {
|
void Video::flush(bool next_sync) {
|
||||||
if(sync_) {
|
if(sync_) {
|
||||||
// If in sync, that takes priority. Output the proper amount of sync.
|
// If in sync, that takes priority. Output the proper amount of sync.
|
||||||
crt_->output_sync(time_since_update_.as_int());
|
crt_.output_sync(time_since_update_.as_int());
|
||||||
} else {
|
} else {
|
||||||
// If not presently in sync, then...
|
// If not presently in sync, then...
|
||||||
|
|
||||||
|
@ -61,16 +60,16 @@ void Video::flush(bool next_sync) {
|
||||||
int data_length = static_cast<int>(line_data_pointer_ - line_data_);
|
int data_length = static_cast<int>(line_data_pointer_ - line_data_);
|
||||||
if(data_length < time_since_update_.as_int() || next_sync) {
|
if(data_length < time_since_update_.as_int() || next_sync) {
|
||||||
auto output_length = std::min(data_length, time_since_update_.as_int());
|
auto output_length = std::min(data_length, time_since_update_.as_int());
|
||||||
crt_->output_data(output_length);
|
crt_.output_data(output_length);
|
||||||
line_data_pointer_ = line_data_ = nullptr;
|
line_data_pointer_ = line_data_ = nullptr;
|
||||||
time_since_update_ -= HalfCycles(output_length);
|
time_since_update_ -= HalfCycles(output_length);
|
||||||
} else return;
|
} else return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any pending pixels being dealt with, pad with the white level.
|
// Any pending pixels being dealt with, pad with the white level.
|
||||||
uint8_t *colour_pointer = static_cast<uint8_t *>(crt_->begin_data(1));
|
uint8_t *colour_pointer = static_cast<uint8_t *>(crt_.begin_data(1));
|
||||||
if(colour_pointer) *colour_pointer = 0xff;
|
if(colour_pointer) *colour_pointer = 0xff;
|
||||||
crt_->output_level(time_since_update_.as_int());
|
crt_.output_level(time_since_update_.as_int());
|
||||||
}
|
}
|
||||||
|
|
||||||
time_since_update_ = 0;
|
time_since_update_ = 0;
|
||||||
|
@ -92,16 +91,16 @@ void Video::output_byte(uint8_t byte) {
|
||||||
|
|
||||||
// Grab a buffer if one isn't already available.
|
// Grab a buffer if one isn't already available.
|
||||||
if(!line_data_) {
|
if(!line_data_) {
|
||||||
line_data_pointer_ = line_data_ = crt_->begin_data(StandardAllocationSize);
|
line_data_pointer_ = line_data_ = crt_.begin_data(StandardAllocationSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a buffer was obtained, serialise the new pixels.
|
// If a buffer was obtained, serialise the new pixels.
|
||||||
if(line_data_) {
|
if(line_data_) {
|
||||||
// If the buffer is full, output it now and obtain a new one
|
// If the buffer is full, output it now and obtain a new one
|
||||||
if(line_data_pointer_ - line_data_ == StandardAllocationSize) {
|
if(line_data_pointer_ - line_data_ == StandardAllocationSize) {
|
||||||
crt_->output_data(StandardAllocationSize, StandardAllocationSize);
|
crt_.output_data(StandardAllocationSize, StandardAllocationSize);
|
||||||
time_since_update_ -= StandardAllocationSize;
|
time_since_update_ -= StandardAllocationSize;
|
||||||
line_data_pointer_ = line_data_ = crt_->begin_data(StandardAllocationSize);
|
line_data_pointer_ = line_data_ = crt_.begin_data(StandardAllocationSize);
|
||||||
if(!line_data_) return;
|
if(!line_data_) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,6 +114,6 @@ void Video::output_byte(uint8_t byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Outputs::CRT::CRT *Video::get_crt() {
|
void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||||
return crt_.get();
|
crt_.set_scan_target(scan_target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,8 @@ 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.
|
||||||
Video(Outputs::Display::ScanTarget *scan_target);
|
Video();
|
||||||
/// @returns The CRT this video feed is feeding.
|
|
||||||
Outputs::CRT::CRT *get_crt();
|
|
||||||
|
|
||||||
/// Advances time by @c half-cycles.
|
/// Advances time by @c half-cycles.
|
||||||
void run_for(const HalfCycles);
|
void run_for(const HalfCycles);
|
||||||
|
@ -41,12 +39,15 @@ class Video {
|
||||||
/// Causes @c byte to be serialised into pixels and output over the next four cycles.
|
/// Causes @c byte to be serialised into pixels and output over the next four cycles.
|
||||||
void output_byte(uint8_t byte);
|
void output_byte(uint8_t byte);
|
||||||
|
|
||||||
|
/// Sets the scan target.
|
||||||
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool sync_ = false;
|
bool sync_ = false;
|
||||||
uint8_t *line_data_ = nullptr;
|
uint8_t *line_data_ = nullptr;
|
||||||
uint8_t *line_data_pointer_ = nullptr;
|
uint8_t *line_data_pointer_ = nullptr;
|
||||||
HalfCycles time_since_update_ = 0;
|
HalfCycles time_since_update_ = 0;
|
||||||
std::unique_ptr<Outputs::CRT::CRT> crt_;
|
Outputs::CRT::CRT crt_;
|
||||||
|
|
||||||
void flush(bool next_sync);
|
void flush(bool next_sync);
|
||||||
};
|
};
|
||||||
|
|
|
@ -135,23 +135,23 @@ template<bool is_zx81> class ConcreteMachine:
|
||||||
time_since_ay_update_ += cycle.length;
|
time_since_ay_update_ += cycle.length;
|
||||||
|
|
||||||
if(previous_counter < vsync_start_ && horizontal_counter_ >= vsync_start_) {
|
if(previous_counter < vsync_start_ && horizontal_counter_ >= vsync_start_) {
|
||||||
video_->run_for(vsync_start_ - previous_counter);
|
video_.run_for(vsync_start_ - previous_counter);
|
||||||
set_hsync(true);
|
set_hsync(true);
|
||||||
line_counter_ = (line_counter_ + 1) & 7;
|
line_counter_ = (line_counter_ + 1) & 7;
|
||||||
if(nmi_is_enabled_) {
|
if(nmi_is_enabled_) {
|
||||||
z80_.set_non_maskable_interrupt_line(true);
|
z80_.set_non_maskable_interrupt_line(true);
|
||||||
}
|
}
|
||||||
video_->run_for(horizontal_counter_ - vsync_start_);
|
video_.run_for(horizontal_counter_ - vsync_start_);
|
||||||
} else if(previous_counter < vsync_end_ && horizontal_counter_ >= vsync_end_) {
|
} else if(previous_counter < vsync_end_ && horizontal_counter_ >= vsync_end_) {
|
||||||
video_->run_for(vsync_end_ - previous_counter);
|
video_.run_for(vsync_end_ - previous_counter);
|
||||||
set_hsync(false);
|
set_hsync(false);
|
||||||
if(nmi_is_enabled_) {
|
if(nmi_is_enabled_) {
|
||||||
z80_.set_non_maskable_interrupt_line(false);
|
z80_.set_non_maskable_interrupt_line(false);
|
||||||
z80_.set_wait_line(false);
|
z80_.set_wait_line(false);
|
||||||
}
|
}
|
||||||
video_->run_for(horizontal_counter_ - vsync_end_);
|
video_.run_for(horizontal_counter_ - vsync_end_);
|
||||||
} else {
|
} else {
|
||||||
video_->run_for(cycle.length);
|
video_.run_for(cycle.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(is_zx81) horizontal_counter_ %= HalfCycles(Cycles(207));
|
if(is_zx81) horizontal_counter_ %= HalfCycles(Cycles(207));
|
||||||
|
@ -240,7 +240,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||||
latched_video_byte_ = ram_[address & ram_mask_] ^ mask;
|
latched_video_byte_ = ram_[address & ram_mask_] ^ mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
video_->output_byte(latched_video_byte_);
|
video_.output_byte(latched_video_byte_);
|
||||||
has_latched_video_byte_ = false;
|
has_latched_video_byte_ = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -303,15 +303,15 @@ template<bool is_zx81> class ConcreteMachine:
|
||||||
}
|
}
|
||||||
|
|
||||||
forceinline void flush() {
|
forceinline void flush() {
|
||||||
video_->flush();
|
video_.flush();
|
||||||
if(is_zx81) {
|
if(is_zx81) {
|
||||||
update_audio();
|
update_audio();
|
||||||
audio_queue_.perform();
|
audio_queue_.perform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_output(Outputs::Display::ScanTarget *scan_target) override final {
|
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final {
|
||||||
video_.reset(new Video(scan_target));
|
video_.set_scan_target(scan_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
Outputs::Speaker::Speaker *get_speaker() override final {
|
Outputs::Speaker::Speaker *get_speaker() override final {
|
||||||
|
@ -407,8 +407,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CPU::Z80::Processor<ConcreteMachine, false, is_zx81> z80_;
|
CPU::Z80::Processor<ConcreteMachine, false, is_zx81> z80_;
|
||||||
|
Video video_;
|
||||||
std::unique_ptr<Video> video_;
|
|
||||||
|
|
||||||
uint16_t tape_trap_address_, tape_return_address_;
|
uint16_t tape_trap_address_, tape_return_address_;
|
||||||
uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_;
|
uint16_t automatic_tape_motor_start_address_, automatic_tape_motor_end_address_;
|
||||||
|
@ -456,7 +455,7 @@ template<bool is_zx81> class ConcreteMachine:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void update_sync() {
|
inline void update_sync() {
|
||||||
video_->set_sync(vsync_ || hsync_);
|
video_.set_sync(vsync_ || hsync_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Audio
|
// MARK: - Audio
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
4B0333AF2094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
||||||
4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
4B0333B02094081A0050B93D /* AppleDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B0333AD2094081A0050B93D /* AppleDSK.cpp */; };
|
||||||
4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; };
|
4B049CDD1DA3C82F00322067 /* BCDTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B049CDC1DA3C82F00322067 /* BCDTest.swift */; };
|
||||||
|
4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
|
||||||
|
4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B05401D219D1618001BF69C /* ScanTarget.cpp */; };
|
||||||
4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; };
|
4B055A7A1FAE78A00060FFFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B055A771FAE78210060FFFF /* SDL2.framework */; };
|
||||||
4B055A7E1FAE84AA0060FFFF /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B055A7C1FAE84A50060FFFF /* main.cpp */; };
|
4B055A7E1FAE84AA0060FFFF /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B055A7C1FAE84A50060FFFF /* main.cpp */; };
|
||||||
4B055A8D1FAE85920060FFFF /* AsyncTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */; };
|
4B055A8D1FAE85920060FFFF /* AsyncTaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3940E51DA83C8300427841 /* AsyncTaskQueue.cpp */; };
|
||||||
|
@ -697,6 +699,7 @@
|
||||||
4B046DC31CFE651500E9E45E /* CRTMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTMachine.hpp; sourceTree = "<group>"; };
|
4B046DC31CFE651500E9E45E /* CRTMachine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTMachine.hpp; sourceTree = "<group>"; };
|
||||||
4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
|
4B047075201ABC180047AB0D /* Cartridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Cartridge.hpp; sourceTree = "<group>"; };
|
||||||
4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; };
|
4B049CDC1DA3C82F00322067 /* BCDTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BCDTest.swift; sourceTree = "<group>"; };
|
||||||
|
4B05401D219D1618001BF69C /* ScanTarget.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = ScanTarget.cpp; path = ../../Outputs/ScanTarget.cpp; sourceTree = "<group>"; };
|
||||||
4B055A6A1FAE763F0060FFFF /* Clock Signal Kiosk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Clock Signal Kiosk"; sourceTree = BUILT_PRODUCTS_DIR; };
|
4B055A6A1FAE763F0060FFFF /* Clock Signal Kiosk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Clock Signal Kiosk"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; };
|
4B055A771FAE78210060FFFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../Library/Frameworks/SDL2.framework; sourceTree = SOURCE_ROOT; };
|
||||||
4B055A7C1FAE84A50060FFFF /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
|
4B055A7C1FAE84A50060FFFF /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
|
||||||
|
@ -1782,10 +1785,11 @@
|
||||||
4B366DFD1B5C165F0026627B /* Outputs */ = {
|
4B366DFD1B5C165F0026627B /* Outputs */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4BD191D5219113B80042E144 /* OpenGL */,
|
4B05401D219D1618001BF69C /* ScanTarget.cpp */,
|
||||||
4BD601A920D89F2A00CBCE57 /* Log.hpp */,
|
4BD601A920D89F2A00CBCE57 /* Log.hpp */,
|
||||||
4BF52672218E752E00313227 /* ScanTarget.hpp */,
|
4BF52672218E752E00313227 /* ScanTarget.hpp */,
|
||||||
4B0CCC411C62D0B3001CAC5F /* CRT */,
|
4B0CCC411C62D0B3001CAC5F /* CRT */,
|
||||||
|
4BD191D5219113B80042E144 /* OpenGL */,
|
||||||
4BD060A41FE49D3C006E14BE /* Speaker */,
|
4BD060A41FE49D3C006E14BE /* Speaker */,
|
||||||
);
|
);
|
||||||
name = Outputs;
|
name = Outputs;
|
||||||
|
@ -3693,6 +3697,7 @@
|
||||||
4B055A9D1FAE85DA0060FFFF /* D64.cpp in Sources */,
|
4B055A9D1FAE85DA0060FFFF /* D64.cpp in Sources */,
|
||||||
4B055ABB1FAE86170060FFFF /* Oric.cpp in Sources */,
|
4B055ABB1FAE86170060FFFF /* Oric.cpp in Sources */,
|
||||||
4B12C0EE1FCFAD1A005BFD93 /* Keyboard.cpp in Sources */,
|
4B12C0EE1FCFAD1A005BFD93 /* Keyboard.cpp in Sources */,
|
||||||
|
4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */,
|
||||||
4B055AE81FAE9B7B0060FFFF /* FIRFilter.cpp in Sources */,
|
4B055AE81FAE9B7B0060FFFF /* FIRFilter.cpp in Sources */,
|
||||||
4B055A901FAE85A90060FFFF /* TimedEventLoop.cpp in Sources */,
|
4B055A901FAE85A90060FFFF /* TimedEventLoop.cpp in Sources */,
|
||||||
4B055AC71FAE9AEE0060FFFF /* TIA.cpp in Sources */,
|
4B055AC71FAE9AEE0060FFFF /* TIA.cpp in Sources */,
|
||||||
|
@ -3847,6 +3852,7 @@
|
||||||
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
|
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
|
||||||
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
|
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
|
||||||
4BC39568208EE6CF0044766B /* DiskIICard.cpp in Sources */,
|
4BC39568208EE6CF0044766B /* DiskIICard.cpp in Sources */,
|
||||||
|
4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */,
|
||||||
4B4518861F75E91A00926311 /* MFMDiskController.cpp in Sources */,
|
4B4518861F75E91A00926311 /* MFMDiskController.cpp in Sources */,
|
||||||
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */,
|
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */,
|
||||||
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,
|
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,
|
||||||
|
|
|
@ -236,7 +236,7 @@ struct ActivityObserver: public Activity::Observer {
|
||||||
|
|
||||||
- (void)setupOutputWithAspectRatio:(float)aspectRatio {
|
- (void)setupOutputWithAspectRatio:(float)aspectRatio {
|
||||||
_scanTarget.reset(new Outputs::Display::OpenGL::ScanTarget);
|
_scanTarget.reset(new Outputs::Display::OpenGL::ScanTarget);
|
||||||
_machine->crt_machine()->setup_output(_scanTarget.get());
|
_machine->crt_machine()->set_scan_target(_scanTarget.get());
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
|
@ -67,6 +67,12 @@ void CRT::set_new_timing(int cycles_per_line, int height_of_display, Outputs::Di
|
||||||
scan_target_->set_modals(scan_target_modals_);
|
scan_target_->set_modals(scan_target_modals_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CRT::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
|
||||||
|
scan_target_ = scan_target;
|
||||||
|
if(!scan_target_) scan_target_ = &Outputs::Display::NullScanTarget::singleton;
|
||||||
|
scan_target_->set_modals(scan_target_modals_);
|
||||||
|
}
|
||||||
|
|
||||||
void CRT::set_new_data_type(Outputs::Display::InputDataType data_type) {
|
void CRT::set_new_data_type(Outputs::Display::InputDataType data_type) {
|
||||||
scan_target_modals_.input_data_type = data_type;
|
scan_target_modals_.input_data_type = data_type;
|
||||||
scan_target_->set_modals(scan_target_modals_);
|
scan_target_->set_modals(scan_target_modals_);
|
||||||
|
@ -111,9 +117,7 @@ CRT::CRT( int cycles_per_line,
|
||||||
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,
|
||||||
Outputs::Display::InputDataType data_type,
|
Outputs::Display::InputDataType data_type) {
|
||||||
Outputs::Display::ScanTarget *scan_target) {
|
|
||||||
scan_target_ = scan_target;
|
|
||||||
scan_target_modals_.input_data_type = data_type;
|
scan_target_modals_.input_data_type = data_type;
|
||||||
scan_target_modals_.cycles_per_line = cycles_per_line;
|
scan_target_modals_.cycles_per_line = cycles_per_line;
|
||||||
scan_target_modals_.clocks_per_pixel_greatest_common_divisor = clocks_per_pixel_greatest_common_divisor;
|
scan_target_modals_.clocks_per_pixel_greatest_common_divisor = clocks_per_pixel_greatest_common_divisor;
|
||||||
|
@ -123,9 +127,7 @@ CRT::CRT( int cycles_per_line,
|
||||||
CRT::CRT( int cycles_per_line,
|
CRT::CRT( int cycles_per_line,
|
||||||
int clocks_per_pixel_greatest_common_divisor,
|
int clocks_per_pixel_greatest_common_divisor,
|
||||||
Outputs::Display::Type display_type,
|
Outputs::Display::Type display_type,
|
||||||
Outputs::Display::InputDataType data_type,
|
Outputs::Display::InputDataType data_type) {
|
||||||
Outputs::Display::ScanTarget *scan_target) {
|
|
||||||
scan_target_ = scan_target;
|
|
||||||
scan_target_modals_.input_data_type = data_type;
|
scan_target_modals_.input_data_type = data_type;
|
||||||
scan_target_modals_.cycles_per_line = cycles_per_line;
|
scan_target_modals_.cycles_per_line = cycles_per_line;
|
||||||
scan_target_modals_.clocks_per_pixel_greatest_common_divisor = clocks_per_pixel_greatest_common_divisor;
|
scan_target_modals_.clocks_per_pixel_greatest_common_divisor = clocks_per_pixel_greatest_common_divisor;
|
||||||
|
|
|
@ -81,7 +81,7 @@ class CRT {
|
||||||
|
|
||||||
int cycles_per_line_ = 1;
|
int cycles_per_line_ = 1;
|
||||||
|
|
||||||
Outputs::Display::ScanTarget *scan_target_ = nullptr;
|
Outputs::Display::ScanTarget *scan_target_ = &Outputs::Display::NullScanTarget::singleton;
|
||||||
Outputs::Display::ScanTarget::Modals scan_target_modals_;
|
Outputs::Display::ScanTarget::Modals scan_target_modals_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -118,8 +118,7 @@ class CRT {
|
||||||
int colour_cycle_denominator,
|
int colour_cycle_denominator,
|
||||||
int vertical_sync_half_lines,
|
int vertical_sync_half_lines,
|
||||||
bool should_alternate,
|
bool should_alternate,
|
||||||
Outputs::Display::InputDataType data_type,
|
Outputs::Display::InputDataType data_type);
|
||||||
Outputs::Display::ScanTarget *scan_target);
|
|
||||||
|
|
||||||
/*! Exactly identical to calling the designated constructor with colour subcarrier information
|
/*! Exactly identical to calling the designated constructor with colour subcarrier information
|
||||||
looked up by display type.
|
looked up by display type.
|
||||||
|
@ -127,8 +126,7 @@ class CRT {
|
||||||
CRT(int cycles_per_line,
|
CRT(int cycles_per_line,
|
||||||
int minimum_cycles_per_pixel,
|
int minimum_cycles_per_pixel,
|
||||||
Outputs::Display::Type display_type,
|
Outputs::Display::Type display_type,
|
||||||
Outputs::Display::InputDataType data_type,
|
Outputs::Display::InputDataType 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. */
|
||||||
|
@ -263,6 +261,9 @@ class CRT {
|
||||||
inline void set_delegate(Delegate *delegate) {
|
inline void set_delegate(Delegate *delegate) {
|
||||||
delegate_ = delegate;
|
delegate_ = delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Sets the scan target for CRT output. */
|
||||||
|
void set_scan_target(Outputs::Display::ScanTarget *);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
13
Outputs/ScanTarget.cpp
Normal file
13
Outputs/ScanTarget.cpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
//
|
||||||
|
// ScanTarget.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 14/11/2018.
|
||||||
|
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ScanTarget.hpp"
|
||||||
|
|
||||||
|
using namespace Outputs::Display;
|
||||||
|
|
||||||
|
NullScanTarget NullScanTarget::singleton;
|
|
@ -249,6 +249,18 @@ struct ScanTarget {
|
||||||
virtual void announce(Event event, uint16_t x, uint16_t y) {}
|
virtual void announce(Event event, uint16_t x, uint16_t y) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Provides a null target for scans.
|
||||||
|
*/
|
||||||
|
struct NullScanTarget: public ScanTarget {
|
||||||
|
void set_modals(Modals) {}
|
||||||
|
Scan *begin_scan() { return nullptr; }
|
||||||
|
uint8_t *begin_data(size_t required_length, size_t required_alignment = 1) { return nullptr; }
|
||||||
|
void submit() {}
|
||||||
|
|
||||||
|
static NullScanTarget singleton;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user