1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-12 15:31:09 +00:00

Makes ScanTarget a little more communicative and orthogonal.

This commit is contained in:
Thomas Harte 2018-11-10 19:52:57 -05:00
parent 491817d85c
commit 6d277fecd5
13 changed files with 76 additions and 51 deletions

View File

@ -294,7 +294,7 @@ template <class BusHandler> class MOS6560 {
pixel_pointer = nullptr;
if(output_state_ == State::Pixels) {
pixel_pointer = reinterpret_cast<uint16_t *>(crt_->allocate_write_area(260));
pixel_pointer = reinterpret_cast<uint16_t *>(crt_->begin_data(260));
}
}
cycles_in_state_++;
@ -512,7 +512,7 @@ template <class BusHandler> class MOS6560 {
uint16_t *pixel_pointer;
void output_border(int number_of_cycles) {
uint16_t *colour_pointer = reinterpret_cast<uint16_t *>(crt_->allocate_write_area(1));
uint16_t *colour_pointer = reinterpret_cast<uint16_t *>(crt_->begin_data(1));
if(colour_pointer) *colour_pointer = registers_.borderColour;
crt_->output_level(number_of_cycles);
}

View File

@ -411,7 +411,7 @@ void TMS9918::run_for(const HalfCycles cycles) {
if(!asked_for_write_area_) {
asked_for_write_area_ = true;
pixel_origin_ = pixel_target_ = reinterpret_cast<uint32_t *>(
crt_->allocate_write_area(size_t(line_buffer.next_border_column - line_buffer.first_pixel_output_column))
crt_->begin_data(size_t(line_buffer.next_border_column - line_buffer.first_pixel_output_column))
);
}
@ -471,14 +471,14 @@ void Base::output_border(int cycles, uint32_t cram_dot) {
palette[background_colour_];
if(cram_dot) {
uint32_t *const pixel_target = reinterpret_cast<uint32_t *>(crt_->allocate_write_area(1));
uint32_t *const pixel_target = reinterpret_cast<uint32_t *>(crt_->begin_data(1));
*pixel_target = border_colour | cram_dot;
crt_->output_level(4);
cycles -= 4;
}
if(cycles) {
uint32_t *const pixel_target = reinterpret_cast<uint32_t *>(crt_->allocate_write_area(1));
uint32_t *const pixel_target = reinterpret_cast<uint32_t *>(crt_->begin_data(1));
*pixel_target = border_colour;
crt_->output_level(cycles);
}

View File

@ -239,7 +239,7 @@ class CRTCBusHandler {
// collect some more pixels if output is ongoing
if(previous_output_mode_ == OutputMode::Pixels) {
if(!pixel_data_) {
pixel_pointer_ = pixel_data_ = crt_->allocate_write_area(320, 8);
pixel_pointer_ = pixel_data_ = crt_->begin_data(320, 8);
}
if(pixel_pointer_) {
// the CPC shuffles output lines as:
@ -378,7 +378,7 @@ class CRTCBusHandler {
private:
void output_border(int length) {
uint8_t *colour_pointer = static_cast<uint8_t *>(crt_->allocate_write_area(1));
uint8_t *colour_pointer = static_cast<uint8_t *>(crt_->begin_data(1));
if(colour_pointer) *colour_pointer = border_;
crt_->output_level(length * 16);
}

View File

@ -402,7 +402,7 @@ template <class BusHandler, bool is_iie> class Video: public VideoBase {
// remain where they would naturally be but auxiliary
// graphics appear to the left of that.
if(!column_) {
pixel_pointer_ = crt_->allocate_write_area(568);
pixel_pointer_ = crt_->begin_data(568);
graphics_carry_ = 0;
was_double_ = true;
}

View File

@ -449,7 +449,7 @@ void TIA::output_for_cycles(int number_of_cycles) {
} else {
if(!pixels_start_location_ && crt_) {
pixels_start_location_ = output_cursor;
pixel_target_ = crt_->allocate_write_area(160);
pixel_target_ = crt_->begin_data(160);
}
// convert that into pixels

View File

@ -119,7 +119,7 @@ void VideoOutput::output_pixels(int number_of_cycles) {
crt_->output_data(data_length * current_output_divider_, size_t(data_length));
}
current_output_divider_ = divider;
initial_output_target_ = current_output_target_ = crt_->allocate_write_area(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() \

View File

@ -96,7 +96,7 @@ void VideoOutput::run_for(const Cycles cycles) {
paper_ = 0x0;
use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false;
set_character_set_base_address();
pixel_target_ = reinterpret_cast<uint16_t *>(crt_->allocate_write_area(240));
pixel_target_ = reinterpret_cast<uint16_t *>(crt_->begin_data(240));
if(!counter_) {
frame_counter_++;

View File

@ -68,7 +68,7 @@ void Video::flush(bool next_sync) {
}
// Any pending pixels being dealt with, pad with the white level.
uint8_t *colour_pointer = static_cast<uint8_t *>(crt_->allocate_write_area(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());
}
@ -92,7 +92,7 @@ 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_->allocate_write_area(StandardAllocationSize);
line_data_pointer_ = line_data_ = crt_->begin_data(StandardAllocationSize);
}
// If a buffer was obtained, serialise the new pixels.
@ -101,7 +101,7 @@ void Video::output_byte(uint8_t byte) {
if(line_data_pointer_ - line_data_ == StandardAllocationSize) {
crt_->output_data(StandardAllocationSize, StandardAllocationSize);
time_since_update_ -= StandardAllocationSize;
line_data_pointer_ = line_data_ = crt_->allocate_write_area(StandardAllocationSize);
line_data_pointer_ = line_data_ = crt_->begin_data(StandardAllocationSize);
if(!line_data_) return;
}

View File

@ -162,7 +162,7 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_
// Determine whether to output any data for this portion of the output; if so then grab somewhere to put it.
const bool is_output_segment = ((is_output_run && next_run_length) && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace());
Outputs::Display::ScanTarget::Scan *const next_scan = is_output_segment ? scan_target_->get_scan() : nullptr;
Outputs::Display::ScanTarget::Scan *const next_scan = is_output_segment ? scan_target_->begin_scan() : nullptr;
did_output |= is_output_segment;
// If outputting, store the start location and scan constants.
@ -182,24 +182,41 @@ void CRT::advance_cycles(int number_of_cycles, bool hsync_requested, bool vsync_
horizontal_flywheel_->apply_event(next_run_length, (next_run_length == time_until_horizontal_sync_event) ? next_horizontal_sync_event : Flywheel::SyncEvent::None);
vertical_flywheel_->apply_event(next_run_length, (next_run_length == time_until_vertical_sync_event) ? next_vertical_sync_event : Flywheel::SyncEvent::None);
// Store an endpoint if necessary.
// End the scan if necessary.
if(next_scan) {
next_scan->end_points[1].x = uint16_t(horizontal_flywheel_->get_current_output_position());
next_scan->end_points[1].y = uint16_t(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_);
next_scan->end_points[1].composite_angle = int16_t((phase_numerator_ << 6) / phase_denominator_) * (is_alernate_line_ ? -1 : 1);
next_scan->end_points[1].data_offset = uint16_t((total_cycles - number_of_cycles) * number_of_samples / total_cycles);
scan_target_->end_scan();
}
// If this is horizontal retrace then announce as such, and prepare for the next line.
if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) {
scan_target_->announce(Outputs::Display::ScanTarget::Event::HorizontalRetrace);
is_alernate_line_ ^= phase_alternates_;
colour_burst_amplitude_ = 0;
// Announce horizontal retrace events.
if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) {
const auto event =
(next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace)
? Outputs::Display::ScanTarget::Event::BeginHorizontalRetrace : Outputs::Display::ScanTarget::Event::EndHorizontalRetrace;
scan_target_->announce(
event,
uint16_t(horizontal_flywheel_->get_current_output_position()),
uint16_t(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_));
// Prepare for the next line.
if(next_horizontal_sync_event == Flywheel::SyncEvent::EndRetrace) {
is_alernate_line_ ^= phase_alternates_;
colour_burst_amplitude_ = 0;
}
}
// Also announce if this is vertical retrace.
if(next_run_length == time_until_vertical_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) {
scan_target_->announce(Outputs::Display::ScanTarget::Event::VerticalRetrace);
// Also announce vertical retrace events.
if(next_run_length == time_until_vertical_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) {
const auto event =
(next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace)
? Outputs::Display::ScanTarget::Event::BeginVerticalRetrace : Outputs::Display::ScanTarget::Event::EndVerticalRetrace;
scan_target_->announce(
event,
uint16_t(horizontal_flywheel_->get_current_output_position()),
uint16_t(vertical_flywheel_->get_current_output_position() / vertical_flywheel_output_divider_));
}
// if this is vertical retrace then adcance a field
@ -308,7 +325,7 @@ void CRT::output_blank(int number_of_cycles) {
}
void CRT::output_level(int number_of_cycles) {
scan_target_->reduce_previous_allocation_to(1);
scan_target_->end_data(1);
Scan scan;
scan.type = Scan::Type::Level;
scan.number_of_cycles = number_of_cycles;
@ -336,7 +353,7 @@ void CRT::set_immediate_default_phase(float phase) {
}
void CRT::output_data(int number_of_cycles, size_t number_of_samples) {
scan_target_->reduce_previous_allocation_to(number_of_samples);
scan_target_->end_data(number_of_samples);
Scan scan;
scan.type = Scan::Type::Data;
scan.number_of_cycles = number_of_cycles;

View File

@ -169,15 +169,14 @@ class CRT {
*/
void output_level(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_samples long, and that the first @c number_of_samples should be spread
over @c number_of_cycles.
/*! Declares that the caller has created a run of data via @c begin_data 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 number_of_samples The number of samples of input data to output.
@see @c allocate_write_area , @c get_write_target_for_buffer
@see @c begin_data
*/
void output_data(int number_of_cycles, size_t number_of_samples);
@ -222,8 +221,8 @@ class CRT {
@param required_length The number of samples to allocate.
@returns A pointer to the allocated area if room is available; @c nullptr otherwise.
*/
inline uint8_t *allocate_write_area(std::size_t required_length, std::size_t required_alignment = 1) {
return scan_target_->allocate_write_area(required_length, required_alignment);
inline uint8_t *begin_data(std::size_t required_length, std::size_t required_alignment = 1) {
return scan_target_->begin_data(required_length, required_alignment);
}
/*! Sets the gamma exponent for the simulated screen. */

View File

@ -56,12 +56,14 @@ ScanTarget::ScanTarget() {
// write straight into it.
glGenTextures(1, &write_area_texture_name_);
glGenVertexArrays(1, &vertex_array_);
}
ScanTarget::~ScanTarget() {
// Release scan space.
glDeleteBuffers(1, &scan_buffer_name_);
glDeleteTextures(1, &write_area_texture_name_);
glDeleteVertexArrays(1, &vertex_array_);
}
void ScanTarget::set_modals(Modals modals) {
@ -81,7 +83,7 @@ void ScanTarget::set_modals(Modals modals) {
}
}
Outputs::Display::ScanTarget::Scan *ScanTarget::get_scan() {
Outputs::Display::ScanTarget::Scan *ScanTarget::begin_scan() {
if(allocation_has_failed_) return nullptr;
const auto result = &scan_buffer_[write_pointers_.scan_buffer];
@ -104,7 +106,7 @@ Outputs::Display::ScanTarget::Scan *ScanTarget::get_scan() {
return static_cast<Outputs::Display::ScanTarget::Scan *>(result);
}
uint8_t *ScanTarget::allocate_write_area(size_t required_length, size_t required_alignment) {
uint8_t *ScanTarget::begin_data(size_t required_length, size_t required_alignment) {
if(allocation_has_failed_) return nullptr;
// Determine where the proposed write area would start and end.
@ -143,7 +145,7 @@ uint8_t *ScanTarget::allocate_write_area(size_t required_length, size_t required
// write_pointers_.write_area points to the first pixel the client is expected to draw to.
}
void ScanTarget::reduce_previous_allocation_to(size_t actual_length) {
void ScanTarget::end_data(size_t actual_length) {
if(allocation_has_failed_) return;
// The write area was allocated in the knowledge that there's sufficient

View File

@ -29,9 +29,9 @@ class ScanTarget: public Outputs::Display::ScanTarget {
// Outputs::Display::ScanTarget overrides.
void set_modals(Modals) override;
Scan *get_scan() override;
uint8_t *allocate_write_area(size_t required_length, size_t required_alignment) override;
void reduce_previous_allocation_to(size_t actual_length) override;
Scan *begin_scan() override;
uint8_t *begin_data(size_t required_length, size_t required_alignment) override;
void end_data(size_t actual_length) override;
void submit() override;
private:
@ -65,6 +65,7 @@ class ScanTarget: public Outputs::Display::ScanTarget {
// Maintains a buffer of the most recent 3072 scans.
std::array<Scan, 3072> scan_buffer_;
GLuint scan_buffer_name_ = 0;
GLuint vertex_array_;
// Uses a texture to vend write areas.
std::vector<uint8_t> write_area_texture_;

View File

@ -196,29 +196,32 @@ struct ScanTarget {
/// Requests a new scan to populate.
///
/// @return A valid pointer, or @c nullptr if insufficient further storage is available.
virtual Scan *get_scan() = 0;
virtual Scan *begin_scan() = 0;
/// Finds the first available space of at least @c required_length pixels in size which is suitably aligned
/// for writing of @c required_alignment number of pixels at a time.
/// Requests a new scan to populate.
virtual void end_scan() {}
/// Finds the first available storage of at least @c required_length pixels in size which is
/// suitably aligned for writing of @c required_alignment number of samples at a time.
///
/// Calls will be paired off with calls to @c reduce_previous_allocation_to.
/// Calls will be paired off with calls to @c end_data.
///
/// @returns a pointer to the allocated space if any was available; @c nullptr otherwise.
virtual uint8_t *allocate_write_area(size_t required_length, size_t required_alignment = 1) = 0;
virtual uint8_t *begin_data(size_t required_length, size_t required_alignment = 1) = 0;
/// Announces that the owner is finished with the region created by the most recent @c allocate_write_area
/// Announces that the owner is finished with the region created by the most recent @c begin_data
/// and indicates that its actual final size was @c actual_length.
///
/// It is required that every call to allocate_write_area be paired with a call to reduce_previous_allocation_to.
virtual void reduce_previous_allocation_to(size_t actual_length) {}
/// It is required that every call to begin_data be paired with a call to end_data.
virtual void end_data(size_t actual_length) {}
/// Marks the end of an atomic set of data. Drawing is best effort, so the scan target should either:
///
/// (i) output everything received since the previous submit; or
/// (ii) output nothing.
///
/// If there were any allocation failures — i.e. any null responses to allocate_write_area or
/// get_scan — then (ii) is a required response. But a scan target may also need to opt for (ii)
/// If there were any allocation failures — i.e. any nullptr responses to begin_data or
/// begin_scan — then (ii) is a required response. But a scan target may also need to opt for (ii)
/// for any other reason.
///
/// The ScanTarget isn't bound to take any drawing action immediately; it may sit on submitted data for
@ -232,12 +235,15 @@ struct ScanTarget {
*/
enum class Event {
HorizontalRetrace,
VerticalRetrace
BeginHorizontalRetrace,
EndHorizontalRetrace,
BeginVerticalRetrace,
EndVerticalRetrace,
};
/// Provides a hint that the named event has occurred.
virtual void announce(Event event) {}
virtual void announce(Event event, uint16_t x, uint16_t y) {}
};
}