diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 4cb48f246..48f1f105d 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -287,7 +287,7 @@ template class MOS6560 { case State::Sync: crt_->output_sync(cycles_in_state_ * 4); break; case State::ColourBurst: crt_->output_colour_burst(cycles_in_state_ * 4, (is_odd_frame_ || is_odd_line_) ? 128 : 0); break; case State::Border: output_border(cycles_in_state_ * 4); break; - case State::Pixels: crt_->output_data(cycles_in_state_ * 4, 1); break; + case State::Pixels: crt_->output_data(cycles_in_state_ * 4); break; } output_state_ = this_state_; cycles_in_state_ = 0; diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index f6ba07db1..f72b785ff 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -536,7 +536,8 @@ void TMS9918::run_for(const HalfCycles cycles) { } if(output_column_ == first_right_border_column_) { - crt_->output_data(static_cast(first_right_border_column_ - first_pixel_column_) * 4, 4); + const unsigned int data_length = static_cast(first_right_border_column_ - first_pixel_column_); + crt_->output_data(data_length * 4, data_length); pixel_target_ = nullptr; } } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index cfd9c2e8f..e5278fec6 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -203,7 +203,7 @@ class CRTCBusHandler { } else { if(was_enabled_) { if(cycles_) { - crt_->output_data(cycles_ * 16, pixel_divider_); + crt_->output_data(cycles_ * 16, cycles_ * 16 / pixel_divider_); pixel_pointer_ = pixel_data_ = nullptr; } } else { @@ -267,7 +267,7 @@ class CRTCBusHandler { // widths so it's not necessarily possible to predict the correct number in advance // and using the upper bound could lead to inefficient behaviour if(pixel_pointer_ == pixel_data_ + 320) { - crt_->output_data(cycles_ * 16, pixel_divider_); + crt_->output_data(cycles_ * 16, cycles_ * 16 / pixel_divider_); pixel_pointer_ = pixel_data_ = nullptr; cycles_ = 0; } diff --git a/Machines/AppleII/Video.hpp b/Machines/AppleII/Video.hpp index b2ec40a1f..2394e333c 100644 --- a/Machines/AppleII/Video.hpp +++ b/Machines/AppleII/Video.hpp @@ -38,9 +38,14 @@ template class Video { // "uint texValue = texture(sampler, coordinate).r;" // "texValue <<= int(icoordinate.x * 8) & 7;" // "return float(texValue & 128u);" + "uint texValue = texture(sampler, coordinate).r;" "texValue <<= uint(icoordinate.x * 7.0) % 7u;" "return float(texValue & 64u);" + +// "uint texValue = texture(sampler, coordinate).r;" +// "texValue <<= uint(mod(icoordinate.x, 1.0) * 7.0);" +// "return float(texValue & 64u);" "}"); // TODO: the above has precision issues. Fix! @@ -91,7 +96,7 @@ template class Video { const int pixel_end = std::min(40, ending_column); const int character_row = row_ >> 3; const int pixel_row = row_ & 7; - const uint16_t line_address = static_cast(0x400 + (character_row >> 3) * 40 + ((character_row&7) << 7)); + const uint16_t line_address = static_cast(0x400 + (video_page_ * 0x400) + (character_row >> 3) * 40 + ((character_row&7) << 7)); for(int c = column_; c < pixel_end; ++c) { const uint16_t address = static_cast(line_address + c); @@ -102,8 +107,10 @@ template class Video { pixel_pointer_[c] = character_rom_[character_address] ^ ((character & 0x80) ? 0x00 : 0xff); } + // TODO: graphics; 0x2000+ for high resolution + if(ending_column >= 40) { - crt_->output_data(280, 7); + crt_->output_data(280, 40); } } else { if(ending_column >= 40) { @@ -163,6 +170,7 @@ template class Video { } void set_video_page(int page) { + video_page_ = page; printf("Video page: %d\n", page); } diff --git a/Machines/Atari2600/TIA.cpp b/Machines/Atari2600/TIA.cpp index c26580a98..af92c71e9 100644 --- a/Machines/Atari2600/TIA.cpp +++ b/Machines/Atari2600/TIA.cpp @@ -437,7 +437,10 @@ void TIA::output_for_cycles(int number_of_cycles) { if(output_mode_ & blank_flag) { if(pixel_target_) { output_pixels(pixels_start_location_, output_cursor); - if(crt_) crt_->output_data(static_cast(output_cursor - pixels_start_location_) * 2, 2); + if(crt_) { + const unsigned int data_length = static_cast(output_cursor - pixels_start_location_); + crt_->output_data(data_length * 2, data_length); + } pixel_target_ = nullptr; pixels_start_location_ = 0; } @@ -459,7 +462,8 @@ void TIA::output_for_cycles(int number_of_cycles) { } if(horizontal_counter_ == cycles_per_line && crt_) { - crt_->output_data(static_cast(output_cursor - pixels_start_location_) * 2, 2); + const unsigned int data_length = static_cast(output_cursor - pixels_start_location_); + crt_->output_data(data_length * 2, data_length); pixel_target_ = nullptr; pixels_start_location_ = 0; } diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index fb3e3eefe..a7c218546 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -97,7 +97,10 @@ void VideoOutput::start_pixel_line() { } void VideoOutput::end_pixel_line() { - if(current_output_target_) crt_->output_data(static_cast((current_output_target_ - initial_output_target_) * current_output_divider_), current_output_divider_); + if(current_output_target_) { + const unsigned int data_length = static_cast(current_output_target_ - initial_output_target_); + crt_->output_data(data_length * current_output_divider_, data_length); + } current_character_row_++; } @@ -115,7 +118,10 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles) { } if(!initial_output_target_ || divider != current_output_divider_) { - if(current_output_target_) crt_->output_data(static_cast((current_output_target_ - initial_output_target_) * current_output_divider_), current_output_divider_); + if(current_output_target_) { + const unsigned int data_length = static_cast(current_output_target_ - initial_output_target_); + crt_->output_data(data_length * current_output_divider_, data_length); + } current_output_divider_ = divider; initial_output_target_ = current_output_target_ = crt_->allocate_write_area(640 / current_output_divider_, 4); } diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index efb8fbfa2..6fbe91382 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -191,7 +191,7 @@ void VideoOutput::run_for(const Cycles cycles) { } if(h_counter == 40) { - crt_->output_data(40 * 6, 1); + crt_->output_data(40 * 6); } } else { // this is a blank line (or the equivalent part of a pixel line) diff --git a/Machines/ZX8081/Video.cpp b/Machines/ZX8081/Video.cpp index 6abbb2a56..6617e6ac8 100644 --- a/Machines/ZX8081/Video.cpp +++ b/Machines/ZX8081/Video.cpp @@ -62,7 +62,7 @@ void Video::flush(bool next_sync) { unsigned int data_length = static_cast(line_data_pointer_ - line_data_) * HalfCyclesPerByte; if(data_length < cycles_since_update_ || next_sync) { unsigned int output_length = std::min(data_length, cycles_since_update_); - crt_->output_data(output_length, HalfCyclesPerByte); + crt_->output_data(output_length, output_length / HalfCyclesPerByte); line_data_pointer_ = line_data_ = nullptr; cycles_since_update_ -= output_length; } else return; @@ -100,7 +100,7 @@ void Video::output_byte(uint8_t byte) { 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, HalfCyclesPerByte); + crt_->output_data(StandardAllocationSize * HalfCyclesPerByte, StandardAllocationSize); cycles_since_update_ -= StandardAllocationSize * HalfCyclesPerByte; line_data_pointer_ = line_data_ = crt_->allocate_write_area(StandardAllocationSize); if(!line_data_) return; diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 82f582f6b..579820d17 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -125,11 +125,8 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested, #define output_position_y() (*reinterpret_cast(&next_output_run[OutputVertexOffsetOfVertical + 0])) #define output_tex_y() (*reinterpret_cast(&next_output_run[OutputVertexOffsetOfVertical + 2])) -#define source_input_position_x1() (*reinterpret_cast(&next_run[SourceVertexOffsetOfInputStart + 0])) #define source_input_position_y() (*reinterpret_cast(&next_run[SourceVertexOffsetOfInputStart + 2])) -#define source_input_position_x2() (*reinterpret_cast(&next_run[SourceVertexOffsetOfEnds + 0])) #define source_output_position_x1() (*reinterpret_cast(&next_run[SourceVertexOffsetOfOutputStart + 0])) -#define source_output_position_y() (*reinterpret_cast(&next_run[SourceVertexOffsetOfOutputStart + 2])) #define source_output_position_x2() (*reinterpret_cast(&next_run[SourceVertexOffsetOfEnds + 2])) #define source_phase() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 0] #define source_amplitude() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 1] @@ -217,6 +214,9 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo output_tex_y() = output_y; output_x2() = static_cast(horizontal_flywheel_->get_current_output_position()); } + + // TODO: below I've assumed a one-to-one correspondance with output runs and input data; that's + // obviously not completely sustainable. It's a latent bug. openGL_output_builder_.array_builder.flush( [=] (uint8_t *input_buffer, std::size_t input_size, uint8_t *output_buffer, std::size_t output_size) { openGL_output_builder_.texture_builder.flush( @@ -264,15 +264,11 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo #undef output_position_y #undef output_tex_y -#undef source_input_position_x1 #undef source_input_position_y -#undef source_input_position_x2 #undef source_output_position_x1 -#undef source_output_position_y #undef source_output_position_x2 #undef source_phase #undef source_amplitude -#undef source_phase_time // MARK: - stream feeding methods @@ -384,8 +380,8 @@ void CRT::set_immediate_default_phase(float phase) { phase_numerator_ = static_cast(phase * static_cast(phase_denominator_)); } -void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) { - openGL_output_builder_.texture_builder.reduce_previous_allocation_to(number_of_cycles / source_divider); +void CRT::output_data(unsigned int number_of_cycles, unsigned int number_of_samples) { + openGL_output_builder_.texture_builder.reduce_previous_allocation_to(number_of_samples); Scan scan; scan.type = Scan::Type::Data; scan.number_of_cycles = number_of_cycles; diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index be156e071..a03f29c71 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -180,17 +180,21 @@ class CRT { void output_level(unsigned int number_of_cycles); /*! Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer - that is at least @c number_of_cycles long, and that the first @c number_of_cycles/source_divider should be spread - over that amount of time. + that is at least @c number_of_samples long, and that the first @c number_of_samples should be spread + over @c number_of_cycles. @param number_of_cycles The amount of data to output. - @param source_divider A divider for source data; if the divider is 1 then one source pixel is output every cycle, - if it is 2 then one source pixel covers two cycles; if it is n then one source pixel covers n cycles. + @param number_of_samples The number of samples of input data to output. @see @c allocate_write_area , @c get_write_target_for_buffer */ - void output_data(unsigned int number_of_cycles, unsigned int source_divider); + void output_data(unsigned int number_of_cycles, unsigned int number_of_samples); + + /*! A shorthand form for output_data that assumes the number of cycles to output for is the same as the number of samples. */ + void output_data(unsigned int number_of_cycles) { + output_data(number_of_cycles, number_of_cycles); + } /*! Outputs a colour burst.