Kills `setup_output` definitively, saving some indirection. `set_scan_target` takes its place.

This commit is contained in:
Thomas Harte 2018-11-14 21:52:57 -05:00
parent 87df8b9e85
commit 8a699b6072
23 changed files with 122 additions and 94 deletions

View File

@ -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) {
// TODO.
// machine->setup_output(aspect_ratio);

View File

@ -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;

View File

@ -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 <bool has_fdc> 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.

View File

@ -396,7 +396,7 @@ template <Analyser::Static::AppleII::Target::Model model> 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<VideoBusHandler, is_iie()>(video_bus_handler_));
video_->set_character_rom(character_rom_);
}

View File

@ -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<float>(get_clock_rate() / static_cast<double>(CPUTicksPerAudioTick)));
bus_->tia_->get_crt()->set_delegate(this);

View File

@ -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;

View File

@ -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);
}

View File

@ -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<Vic6560BusHandler>(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_);

View File

@ -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<VideoOutput> video_output_;
VideoOutput video_output_;
Concurrency::DeferringAsyncTaskQueue audio_queue_;
SoundGenerator sound_generator_;

View File

@ -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();

View File

@ -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<Outputs::CRT::CRT> crt_;
Outputs::CRT::CRT crt_;
struct DrawAction {
enum Type {

View File

@ -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));
}

View File

@ -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;

View File

@ -456,7 +456,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> 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_));

View File

@ -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<int>(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<uint8_t *>(crt_->begin_data(1));
uint8_t *colour_pointer = static_cast<uint8_t *>(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);
}

View File

@ -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<Outputs::CRT::CRT> crt_;
Outputs::CRT::CRT crt_;
void flush(bool next_sync);
};

View File

@ -135,23 +135,23 @@ template<bool is_zx81> 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<bool is_zx81> 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<bool is_zx81> 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<bool is_zx81> class ConcreteMachine:
private:
CPU::Z80::Processor<ConcreteMachine, false, is_zx81> z80_;
std::unique_ptr<Video> video_;
Video video_;
uint16_t tape_trap_address_, tape_return_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() {
video_->set_sync(vsync_ || hsync_);
video_.set_sync(vsync_ || hsync_);
}
// MARK: - Audio

View File

@ -12,6 +12,8 @@
4B0333AF2094081A0050B93D /* 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 */; };
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 */; };
4B055A7E1FAE84AA0060FFFF /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B055A7C1FAE84A50060FFFF /* main.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>"; };
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>"; };
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; };
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>"; };
@ -1782,10 +1785,11 @@
4B366DFD1B5C165F0026627B /* Outputs */ = {
isa = PBXGroup;
children = (
4BD191D5219113B80042E144 /* OpenGL */,
4B05401D219D1618001BF69C /* ScanTarget.cpp */,
4BD601A920D89F2A00CBCE57 /* Log.hpp */,
4BF52672218E752E00313227 /* ScanTarget.hpp */,
4B0CCC411C62D0B3001CAC5F /* CRT */,
4BD191D5219113B80042E144 /* OpenGL */,
4BD060A41FE49D3C006E14BE /* Speaker */,
);
name = Outputs;
@ -3693,6 +3697,7 @@
4B055A9D1FAE85DA0060FFFF /* D64.cpp in Sources */,
4B055ABB1FAE86170060FFFF /* Oric.cpp in Sources */,
4B12C0EE1FCFAD1A005BFD93 /* Keyboard.cpp in Sources */,
4B05401F219D1618001BF69C /* ScanTarget.cpp in Sources */,
4B055AE81FAE9B7B0060FFFF /* FIRFilter.cpp in Sources */,
4B055A901FAE85A90060FFFF /* TimedEventLoop.cpp in Sources */,
4B055AC71FAE9AEE0060FFFF /* TIA.cpp in Sources */,
@ -3847,6 +3852,7 @@
4B1497921EE4B5A800CE2596 /* ZX8081.cpp in Sources */,
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
4BC39568208EE6CF0044766B /* DiskIICard.cpp in Sources */,
4B05401E219D1618001BF69C /* ScanTarget.cpp in Sources */,
4B4518861F75E91A00926311 /* MFMDiskController.cpp in Sources */,
4B54C0BF1F8D8F450050900F /* Keyboard.cpp in Sources */,
4B3FE75E1F3CF68B00448EE4 /* CPM.cpp in Sources */,

View File

@ -236,7 +236,7 @@ struct ActivityObserver: public Activity::Observer {
- (void)setupOutputWithAspectRatio:(float)aspectRatio {
_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.
// _machine->crt_machine()->get_crt()->set_output_gamma(2.2f);

View File

@ -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_);
}
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) {
scan_target_modals_.input_data_type = data_type;
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 vertical_sync_half_lines,
bool should_alternate,
Outputs::Display::InputDataType data_type,
Outputs::Display::ScanTarget *scan_target) {
scan_target_ = scan_target;
Outputs::Display::InputDataType data_type) {
scan_target_modals_.input_data_type = data_type;
scan_target_modals_.cycles_per_line = cycles_per_line;
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,
int clocks_per_pixel_greatest_common_divisor,
Outputs::Display::Type display_type,
Outputs::Display::InputDataType data_type,
Outputs::Display::ScanTarget *scan_target) {
scan_target_ = scan_target;
Outputs::Display::InputDataType data_type) {
scan_target_modals_.input_data_type = data_type;
scan_target_modals_.cycles_per_line = cycles_per_line;
scan_target_modals_.clocks_per_pixel_greatest_common_divisor = clocks_per_pixel_greatest_common_divisor;

View File

@ -81,7 +81,7 @@ class CRT {
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_;
public:
@ -118,8 +118,7 @@ class CRT {
int colour_cycle_denominator,
int vertical_sync_half_lines,
bool should_alternate,
Outputs::Display::InputDataType data_type,
Outputs::Display::ScanTarget *scan_target);
Outputs::Display::InputDataType data_type);
/*! Exactly identical to calling the designated constructor with colour subcarrier information
looked up by display type.
@ -127,8 +126,7 @@ class CRT {
CRT(int cycles_per_line,
int minimum_cycles_per_pixel,
Outputs::Display::Type display_type,
Outputs::Display::InputDataType data_type,
Outputs::Display::ScanTarget *scan_target);
Outputs::Display::InputDataType data_type);
/*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
been provided at construction. */
@ -263,6 +261,9 @@ class CRT {
inline void set_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
View 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;

View File

@ -249,6 +249,18 @@ struct ScanTarget {
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;
};
}
}