mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 01:32:55 +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) {
|
||||
// TODO.
|
||||
// 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.
|
||||
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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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_);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_);
|
||||
|
@ -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_;
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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_));
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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 */,
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
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) {}
|
||||
};
|
||||
|
||||
/*!
|
||||
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