diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp index aed3e1721..73e086532 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.cpp @@ -53,7 +53,7 @@ void MultiCRTMachine::perform_serial(const std::functionsetup_output(aspect_ratio); diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp index 17b13992c..6cd886669 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiCRTMachine.hpp @@ -53,7 +53,7 @@ class MultiCRTMachine: public CRTMachine::Machine { } // 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; void run_for(Time::Seconds duration) override; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index b93359bb0..d41e52de8 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -325,7 +325,7 @@ class CRTCBusHandler { } /// 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_->set_rgb_sampling_function( // "vec3 rgb_sample(usampler2D sampler, vec2 coordinate)" @@ -983,8 +983,8 @@ template class ConcreteMachine: } /// A CRTMachine function; indicates that outputs should be created now. - void setup_output(Outputs::Display::ScanTarget *scan_target) override final { - crtc_bus_handler_.setup_output(scan_target); + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final { + crtc_bus_handler_.set_scan_target(scan_target); } /// A CRTMachine function; indicates that outputs should be destroyed now. diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 56401a336..6ec7bf158 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -396,7 +396,7 @@ template class ConcreteMachine: 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(video_bus_handler_)); video_->set_character_rom(character_rom_); } diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index f0f7baf46..874840dfd 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -157,7 +157,7 @@ class ConcreteMachine: } // 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_->speaker_.set_input_rate(static_cast(get_clock_rate() / static_cast(CPUTicksPerAudioTick))); bus_->tia_->get_crt()->set_delegate(this); diff --git a/Machines/CRTMachine.hpp b/Machines/CRTMachine.hpp index 42197f975..c1ad324c6 100644 --- a/Machines/CRTMachine.hpp +++ b/Machines/CRTMachine.hpp @@ -35,7 +35,7 @@ class Machine { The @c scan_target will receive all video output; the caller guarantees 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. virtual Outputs::Speaker::Speaker *get_speaker() = 0; diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp index b2840a76e..2b83e225d 100644 --- a/Machines/ColecoVision/ColecoVision.cpp +++ b/Machines/ColecoVision/ColecoVision.cpp @@ -169,7 +169,7 @@ class ConcreteMachine: 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)); // get_crt()->set_video_signal(Outputs::Display::VideoSignal::Composite); } diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index ec415437b..dd4a73732 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -620,7 +620,7 @@ class ConcreteMachine: 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(mos6560_bus_handler_)); mos6560_->set_high_frequency_cutoff(1600); // There is a 1.6Khz low-pass filter in the Vic-20. mos6560_->set_output_mode(output_mode_); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 010db7391..1301b5569 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -51,6 +51,7 @@ class ConcreteMachine: public: ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : m6502_(*this), + video_output_(ram_), sound_generator_(audio_queue_), speaker_(sound_generator_) { 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 // 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 { switch(address & 0xff0f) { case 0xfe00: @@ -198,8 +199,8 @@ class ConcreteMachine: case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f: if(!isReadOperation(operation)) { update_display(); - video_output_->set_register(address, *value); - video_access_range_ = video_output_->get_memory_access_range(); + video_output_.set_register(address, *value); + video_access_range_ = video_output_.get_memory_access_range(); queue_next_display_interrupt(); } break; @@ -373,8 +374,8 @@ class ConcreteMachine: audio_queue_.perform(); } - void setup_output(Outputs::Display::ScanTarget *scan_target) override final { - video_output_.reset(new VideoOutput(ram_, scan_target)); + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final { + video_output_.set_scan_target(scan_target); } Outputs::Speaker::Speaker *get_speaker() override final { @@ -500,12 +501,12 @@ class ConcreteMachine: // MARK: - Work deferral updates. inline void update_display() { 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() { - 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; next_display_interrupt_ = next_interrupt.interrupt; } @@ -575,7 +576,7 @@ class ConcreteMachine: int shift_restart_counter_ = 0; // Outputs - std::unique_ptr video_output_; + VideoOutput video_output_; Concurrency::DeferringAsyncTaskQueue audio_queue_; SoundGenerator sound_generator_; diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 54fed963e..9b07e1b89 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -38,18 +38,16 @@ namespace { // 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_)); setup_screen_map(); 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( // "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));" // "}"); // 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 - -Outputs::CRT::CRT *VideoOutput::get_crt() { - return crt_.get(); +void VideoOutput::set_scan_target(Outputs::Display::ScanTarget *scan_target) { + crt_.set_scan_target(scan_target); } // MARK: - Display update methods @@ -95,7 +91,7 @@ void VideoOutput::start_pixel_line() { void VideoOutput::end_pixel_line() { if(current_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_++; } @@ -104,7 +100,7 @@ void VideoOutput::output_pixels(int number_of_cycles) { if(!number_of_cycles) return; if(is_blank_line_) { - crt_->output_blank(number_of_cycles * crt_cycles_multiplier); + crt_.output_blank(number_of_cycles * crt_cycles_multiplier); } else { int divider = 1; switch(screen_mode_) { @@ -116,10 +112,10 @@ void VideoOutput::output_pixels(int number_of_cycles) { if(!initial_output_target_ || divider != current_output_divider_) { if(current_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; - 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() \ @@ -242,9 +238,9 @@ void VideoOutput::run_for(const Cycles cycles) { cycles_into_draw_action_ += time_left_in_action; if(cycles_into_draw_action_ == draw_action_length) { switch(screen_map_[screen_map_pointer_].type) { - 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::Blank: crt_->output_blank(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::Blank: crt_.output_blank(draw_action_length * crt_cycles_multiplier); break; case DrawAction::Pixels: end_pixel_line(); break; } screen_map_pointer_ = (screen_map_pointer_ + 1) % screen_map_.size(); diff --git a/Machines/Electron/Video.hpp b/Machines/Electron/Video.hpp index 716c6d808..b839a7f55 100644 --- a/Machines/Electron/Video.hpp +++ b/Machines/Electron/Video.hpp @@ -27,19 +27,18 @@ namespace Electron { class VideoOutput { public: /*! - Instantiates a VideoOutput that will read its pixels from @c memory and output video - to @c scan_target. + Instantiates a VideoOutput that will read its pixels from @c memory. The pointer supplied should be to address 0 in the unexpanded Electron's memory map. */ - VideoOutput(uint8_t *memory, Outputs::Display::ScanTarget *scan_target); - - /// @returns the CRT to which output is being painted. - Outputs::CRT::CRT *get_crt(); + VideoOutput(uint8_t *memory); /// Produces the next @c cycles of video output. 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, @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 *initial_output_target_ = nullptr; int current_output_divider_ = 1; - - std::unique_ptr crt_; + Outputs::CRT::CRT crt_; struct DrawAction { enum Type { diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index dcc06db5e..940d92019 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -218,7 +218,7 @@ class ConcreteMachine: 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)); } diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 8d2c587f1..1da0b8003 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -161,7 +161,7 @@ class ConcreteMachine: 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; switch(model_) { case Target::Model::SG1000: personality = TI::TMS::TMS9918A; break; diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index aca16adc9..0a8af14bc 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -456,7 +456,7 @@ template class Co } // 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); video_output_.reset(new VideoOutput(ram_)); diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index 1afde2ebb..7e9416cd3 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -22,21 +22,20 @@ const std::size_t StandardAllocationSize = 320; } -Video::Video(Outputs::Display::ScanTarget *scan_target) : - crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Luminance1, scan_target)) - { +Video::Video() : + 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, // 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)" // "{" // "return texture(sampler, coordinate).r;" // "}"); // Show only the centre 80% of the TV frame. -// 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_video_signal(Outputs::Display::VideoSignal::Composite); + crt_.set_visible_area(Outputs::Display::Rect(0.1f, 0.1f, 0.8f, 0.8f)); } void Video::run_for(const HalfCycles half_cycles) { @@ -51,7 +50,7 @@ void Video::flush() { void Video::flush(bool next_sync) { if(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 { // If not presently in sync, then... @@ -61,16 +60,16 @@ void Video::flush(bool next_sync) { int data_length = static_cast(line_data_pointer_ - line_data_); if(data_length < time_since_update_.as_int() || next_sync) { 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; time_since_update_ -= HalfCycles(output_length); } else return; } // Any pending pixels being dealt with, pad with the white level. - uint8_t *colour_pointer = static_cast(crt_->begin_data(1)); + uint8_t *colour_pointer = static_cast(crt_.begin_data(1)); 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; @@ -92,16 +91,16 @@ void Video::output_byte(uint8_t byte) { // Grab a buffer if one isn't already available. 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(line_data_) { // If the buffer is full, output it now and obtain a new one if(line_data_pointer_ - line_data_ == StandardAllocationSize) { - crt_->output_data(StandardAllocationSize, StandardAllocationSize); + crt_.output_data(StandardAllocationSize, 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; } @@ -115,6 +114,6 @@ void Video::output_byte(uint8_t byte) { } } -Outputs::CRT::CRT *Video::get_crt() { - return crt_.get(); +void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) { + crt_.set_scan_target(scan_target); } diff --git a/Machines/ZX8081/Video.hpp b/Machines/ZX8081/Video.hpp index bc5813582..b58c6c149 100644 --- a/Machines/ZX8081/Video.hpp +++ b/Machines/ZX8081/Video.hpp @@ -26,10 +26,8 @@ namespace ZX8081 { */ class Video { public: - /// Constructs an instance of the video feed; a CRT is also created. - Video(Outputs::Display::ScanTarget *scan_target); - /// @returns The CRT this video feed is feeding. - Outputs::CRT::CRT *get_crt(); + /// Constructs an instance of the video feed. + Video(); /// Advances time by @c half-cycles. 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. void output_byte(uint8_t byte); + /// Sets the scan target. + void set_scan_target(Outputs::Display::ScanTarget *scan_target); + private: bool sync_ = false; uint8_t *line_data_ = nullptr; uint8_t *line_data_pointer_ = nullptr; HalfCycles time_since_update_ = 0; - std::unique_ptr crt_; + Outputs::CRT::CRT crt_; void flush(bool next_sync); }; diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index cfaa28bc0..ab9818006 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -135,23 +135,23 @@ template class ConcreteMachine: time_since_ay_update_ += cycle.length; 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); line_counter_ = (line_counter_ + 1) & 7; if(nmi_is_enabled_) { 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_) { - video_->run_for(vsync_end_ - previous_counter); + video_.run_for(vsync_end_ - previous_counter); set_hsync(false); if(nmi_is_enabled_) { z80_.set_non_maskable_interrupt_line(false); z80_.set_wait_line(false); } - video_->run_for(horizontal_counter_ - vsync_end_); + video_.run_for(horizontal_counter_ - vsync_end_); } else { - video_->run_for(cycle.length); + video_.run_for(cycle.length); } if(is_zx81) horizontal_counter_ %= HalfCycles(Cycles(207)); @@ -240,7 +240,7 @@ template class ConcreteMachine: 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; } break; @@ -303,15 +303,15 @@ template class ConcreteMachine: } forceinline void flush() { - video_->flush(); + video_.flush(); if(is_zx81) { update_audio(); audio_queue_.perform(); } } - void setup_output(Outputs::Display::ScanTarget *scan_target) override final { - video_.reset(new Video(scan_target)); + void set_scan_target(Outputs::Display::ScanTarget *scan_target) override final { + video_.set_scan_target(scan_target); } Outputs::Speaker::Speaker *get_speaker() override final { @@ -407,8 +407,7 @@ template class ConcreteMachine: private: CPU::Z80::Processor z80_; - - std::unique_ptr