1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +00:00

Removes the CRT requirement for an integral relationship between cycles and samples.

This commit is contained in:
Thomas Harte 2018-04-16 20:00:56 -04:00
parent 6a79ce9eb1
commit 1c605d58e3
10 changed files with 46 additions and 27 deletions

View File

@ -287,7 +287,7 @@ template <class BusHandler> class MOS6560 {
case State::Sync: crt_->output_sync(cycles_in_state_ * 4); break; 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::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::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_; output_state_ = this_state_;
cycles_in_state_ = 0; cycles_in_state_ = 0;

View File

@ -536,7 +536,8 @@ void TMS9918::run_for(const HalfCycles cycles) {
} }
if(output_column_ == first_right_border_column_) { if(output_column_ == first_right_border_column_) {
crt_->output_data(static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_) * 4, 4); const unsigned int data_length = static_cast<unsigned int>(first_right_border_column_ - first_pixel_column_);
crt_->output_data(data_length * 4, data_length);
pixel_target_ = nullptr; pixel_target_ = nullptr;
} }
} }

View File

@ -203,7 +203,7 @@ class CRTCBusHandler {
} else { } else {
if(was_enabled_) { if(was_enabled_) {
if(cycles_) { if(cycles_) {
crt_->output_data(cycles_ * 16, pixel_divider_); crt_->output_data(cycles_ * 16, cycles_ * 16 / pixel_divider_);
pixel_pointer_ = pixel_data_ = nullptr; pixel_pointer_ = pixel_data_ = nullptr;
} }
} else { } else {
@ -267,7 +267,7 @@ class CRTCBusHandler {
// widths so it's not necessarily possible to predict the correct number in advance // widths so it's not necessarily possible to predict the correct number in advance
// and using the upper bound could lead to inefficient behaviour // and using the upper bound could lead to inefficient behaviour
if(pixel_pointer_ == pixel_data_ + 320) { 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; pixel_pointer_ = pixel_data_ = nullptr;
cycles_ = 0; cycles_ = 0;
} }

View File

@ -38,9 +38,14 @@ template <class BusHandler> class Video {
// "uint texValue = texture(sampler, coordinate).r;" // "uint texValue = texture(sampler, coordinate).r;"
// "texValue <<= int(icoordinate.x * 8) & 7;" // "texValue <<= int(icoordinate.x * 8) & 7;"
// "return float(texValue & 128u);" // "return float(texValue & 128u);"
"uint texValue = texture(sampler, coordinate).r;" "uint texValue = texture(sampler, coordinate).r;"
"texValue <<= uint(icoordinate.x * 7.0) % 7u;" "texValue <<= uint(icoordinate.x * 7.0) % 7u;"
"return float(texValue & 64u);" "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! // TODO: the above has precision issues. Fix!
@ -91,7 +96,7 @@ template <class BusHandler> class Video {
const int pixel_end = std::min(40, ending_column); const int pixel_end = std::min(40, ending_column);
const int character_row = row_ >> 3; const int character_row = row_ >> 3;
const int pixel_row = row_ & 7; const int pixel_row = row_ & 7;
const uint16_t line_address = static_cast<uint16_t>(0x400 + (character_row >> 3) * 40 + ((character_row&7) << 7)); const uint16_t line_address = static_cast<uint16_t>(0x400 + (video_page_ * 0x400) + (character_row >> 3) * 40 + ((character_row&7) << 7));
for(int c = column_; c < pixel_end; ++c) { for(int c = column_; c < pixel_end; ++c) {
const uint16_t address = static_cast<uint16_t>(line_address + c); const uint16_t address = static_cast<uint16_t>(line_address + c);
@ -102,8 +107,10 @@ template <class BusHandler> class Video {
pixel_pointer_[c] = character_rom_[character_address] ^ ((character & 0x80) ? 0x00 : 0xff); pixel_pointer_[c] = character_rom_[character_address] ^ ((character & 0x80) ? 0x00 : 0xff);
} }
// TODO: graphics; 0x2000+ for high resolution
if(ending_column >= 40) { if(ending_column >= 40) {
crt_->output_data(280, 7); crt_->output_data(280, 40);
} }
} else { } else {
if(ending_column >= 40) { if(ending_column >= 40) {
@ -163,6 +170,7 @@ template <class BusHandler> class Video {
} }
void set_video_page(int page) { void set_video_page(int page) {
video_page_ = page;
printf("Video page: %d\n", page); printf("Video page: %d\n", page);
} }

View File

@ -437,7 +437,10 @@ void TIA::output_for_cycles(int number_of_cycles) {
if(output_mode_ & blank_flag) { if(output_mode_ & blank_flag) {
if(pixel_target_) { if(pixel_target_) {
output_pixels(pixels_start_location_, output_cursor); output_pixels(pixels_start_location_, output_cursor);
if(crt_) crt_->output_data(static_cast<unsigned int>(output_cursor - pixels_start_location_) * 2, 2); if(crt_) {
const unsigned int data_length = static_cast<unsigned int>(output_cursor - pixels_start_location_);
crt_->output_data(data_length * 2, data_length);
}
pixel_target_ = nullptr; pixel_target_ = nullptr;
pixels_start_location_ = 0; pixels_start_location_ = 0;
} }
@ -459,7 +462,8 @@ void TIA::output_for_cycles(int number_of_cycles) {
} }
if(horizontal_counter_ == cycles_per_line && crt_) { if(horizontal_counter_ == cycles_per_line && crt_) {
crt_->output_data(static_cast<unsigned int>(output_cursor - pixels_start_location_) * 2, 2); const unsigned int data_length = static_cast<unsigned int>(output_cursor - pixels_start_location_);
crt_->output_data(data_length * 2, data_length);
pixel_target_ = nullptr; pixel_target_ = nullptr;
pixels_start_location_ = 0; pixels_start_location_ = 0;
} }

View File

@ -97,7 +97,10 @@ void VideoOutput::start_pixel_line() {
} }
void VideoOutput::end_pixel_line() { void VideoOutput::end_pixel_line() {
if(current_output_target_) crt_->output_data(static_cast<unsigned int>((current_output_target_ - initial_output_target_) * current_output_divider_), current_output_divider_); if(current_output_target_) {
const unsigned int data_length = static_cast<unsigned int>(current_output_target_ - initial_output_target_);
crt_->output_data(data_length * current_output_divider_, data_length);
}
current_character_row_++; 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(!initial_output_target_ || divider != current_output_divider_) {
if(current_output_target_) crt_->output_data(static_cast<unsigned int>((current_output_target_ - initial_output_target_) * current_output_divider_), current_output_divider_); if(current_output_target_) {
const unsigned int data_length = static_cast<unsigned int>(current_output_target_ - initial_output_target_);
crt_->output_data(data_length * current_output_divider_, data_length);
}
current_output_divider_ = divider; current_output_divider_ = divider;
initial_output_target_ = current_output_target_ = crt_->allocate_write_area(640 / current_output_divider_, 4); initial_output_target_ = current_output_target_ = crt_->allocate_write_area(640 / current_output_divider_, 4);
} }

View File

@ -191,7 +191,7 @@ void VideoOutput::run_for(const Cycles cycles) {
} }
if(h_counter == 40) { if(h_counter == 40) {
crt_->output_data(40 * 6, 1); crt_->output_data(40 * 6);
} }
} else { } else {
// this is a blank line (or the equivalent part of a pixel line) // this is a blank line (or the equivalent part of a pixel line)

View File

@ -62,7 +62,7 @@ void Video::flush(bool next_sync) {
unsigned int data_length = static_cast<unsigned int>(line_data_pointer_ - line_data_) * HalfCyclesPerByte; unsigned int data_length = static_cast<unsigned int>(line_data_pointer_ - line_data_) * HalfCyclesPerByte;
if(data_length < cycles_since_update_ || next_sync) { if(data_length < cycles_since_update_ || next_sync) {
unsigned int output_length = std::min(data_length, cycles_since_update_); 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; line_data_pointer_ = line_data_ = nullptr;
cycles_since_update_ -= output_length; cycles_since_update_ -= output_length;
} else return; } else return;
@ -100,7 +100,7 @@ void Video::output_byte(uint8_t byte) {
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, HalfCyclesPerByte); crt_->output_data(StandardAllocationSize * HalfCyclesPerByte, StandardAllocationSize);
cycles_since_update_ -= StandardAllocationSize * HalfCyclesPerByte; cycles_since_update_ -= StandardAllocationSize * HalfCyclesPerByte;
line_data_pointer_ = line_data_ = crt_->allocate_write_area(StandardAllocationSize); line_data_pointer_ = line_data_ = crt_->allocate_write_area(StandardAllocationSize);
if(!line_data_) return; if(!line_data_) return;

View File

@ -125,11 +125,8 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested,
#define output_position_y() (*reinterpret_cast<uint16_t *>(&next_output_run[OutputVertexOffsetOfVertical + 0])) #define output_position_y() (*reinterpret_cast<uint16_t *>(&next_output_run[OutputVertexOffsetOfVertical + 0]))
#define output_tex_y() (*reinterpret_cast<uint16_t *>(&next_output_run[OutputVertexOffsetOfVertical + 2])) #define output_tex_y() (*reinterpret_cast<uint16_t *>(&next_output_run[OutputVertexOffsetOfVertical + 2]))
#define source_input_position_x1() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfInputStart + 0]))
#define source_input_position_y() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfInputStart + 2])) #define source_input_position_y() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfInputStart + 2]))
#define source_input_position_x2() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfEnds + 0]))
#define source_output_position_x1() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfOutputStart + 0])) #define source_output_position_x1() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfOutputStart + 0]))
#define source_output_position_y() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfOutputStart + 2]))
#define source_output_position_x2() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfEnds + 2])) #define source_output_position_x2() (*reinterpret_cast<uint16_t *>(&next_run[SourceVertexOffsetOfEnds + 2]))
#define source_phase() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 0] #define source_phase() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 0]
#define source_amplitude() next_run[SourceVertexOffsetOfPhaseTimeAndAmplitude + 1] #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_tex_y() = output_y;
output_x2() = static_cast<uint16_t>(horizontal_flywheel_->get_current_output_position()); output_x2() = static_cast<uint16_t>(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( openGL_output_builder_.array_builder.flush(
[=] (uint8_t *input_buffer, std::size_t input_size, uint8_t *output_buffer, std::size_t output_size) { [=] (uint8_t *input_buffer, std::size_t input_size, uint8_t *output_buffer, std::size_t output_size) {
openGL_output_builder_.texture_builder.flush( 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_position_y
#undef output_tex_y #undef output_tex_y
#undef source_input_position_x1
#undef source_input_position_y #undef source_input_position_y
#undef source_input_position_x2
#undef source_output_position_x1 #undef source_output_position_x1
#undef source_output_position_y
#undef source_output_position_x2 #undef source_output_position_x2
#undef source_phase #undef source_phase
#undef source_amplitude #undef source_amplitude
#undef source_phase_time
// MARK: - stream feeding methods // MARK: - stream feeding methods
@ -384,8 +380,8 @@ void CRT::set_immediate_default_phase(float phase) {
phase_numerator_ = static_cast<unsigned int>(phase * static_cast<float>(phase_denominator_)); phase_numerator_ = static_cast<unsigned int>(phase * static_cast<float>(phase_denominator_));
} }
void CRT::output_data(unsigned int number_of_cycles, unsigned int 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_cycles / source_divider); openGL_output_builder_.texture_builder.reduce_previous_allocation_to(number_of_samples);
Scan scan; Scan scan;
scan.type = Scan::Type::Data; scan.type = Scan::Type::Data;
scan.number_of_cycles = number_of_cycles; scan.number_of_cycles = number_of_cycles;

View File

@ -180,17 +180,21 @@ class CRT {
void output_level(unsigned int number_of_cycles); 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 /*! 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 that is at least @c number_of_samples long, and that the first @c number_of_samples should be spread
over that amount of time. over @c number_of_cycles.
@param number_of_cycles The amount of data to output. @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, @param number_of_samples The number of samples of input data to output.
if it is 2 then one source pixel covers two cycles; if it is n then one source pixel covers n cycles.
@see @c allocate_write_area , @c get_write_target_for_buffer @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. /*! Outputs a colour burst.