1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-03 08:05:40 +00:00
CLK/Machines/ZX8081/Video.cpp

121 lines
3.5 KiB
C++

//
// Video.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/06/2017.
// Copyright 2017 Thomas Harte. All rights reserved.
//
#include "Video.hpp"
#include <algorithm>
using namespace ZX8081;
namespace {
/*!
The number of bytes of PCM data to allocate at once; if/when more are required,
the class will simply allocate another batch.
*/
const std::size_t StandardAllocationSize = 320;
}
Video::Video(Outputs::Display::ScanTarget *scan_target) :
crt_(new Outputs::CRT::CRT(207 * 2, 414, Outputs::Display::Type::PAL50, Outputs::Display::ScanTarget::Modals::DataType::Luminance1, scan_target))
{
// 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(
// "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));
}
void Video::run_for(const HalfCycles half_cycles) {
// Just keep a running total of the amount of time that remains owed to the CRT.
time_since_update_ += half_cycles;
}
void Video::flush() {
flush(sync_);
}
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());
} else {
// If not presently in sync, then...
if(line_data_) {
// If there is output data queued, output it either if it's being interrupted by
// sync, or if we're past its end anyway. Otherwise let it be.
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);
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_->allocate_write_area(1));
if(colour_pointer) *colour_pointer = 0xff;
crt_->output_level(time_since_update_.as_int());
}
time_since_update_ = 0;
}
void Video::set_sync(bool sync) {
// Do nothing if sync hasn't changed.
if(sync_ == sync) return;
// Complete whatever was being drawn, and update sync.
flush(sync);
sync_ = sync;
}
void Video::output_byte(uint8_t byte) {
// Complete whatever was going on.
if(sync_) return;
flush();
// Grab a buffer if one isn't already available.
if(!line_data_) {
line_data_pointer_ = line_data_ = crt_->allocate_write_area(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);
time_since_update_ -= StandardAllocationSize;
line_data_pointer_ = line_data_ = crt_->allocate_write_area(StandardAllocationSize);
if(!line_data_) return;
}
// Convert to one-byte-per-pixel where any non-zero value will act as white.
uint8_t mask = 0x80;
for(int c = 0; c < 8; c++) {
line_data_pointer_[c] = byte & mask;
mask >>= 1;
}
line_data_pointer_ += 8;
}
}
Outputs::CRT::CRT *Video::get_crt() {
return crt_.get();
}