mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-18 01:07:58 +00:00
110 lines
3.0 KiB
C++
110 lines
3.0 KiB
C++
//
|
|
// Video.cpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 06/06/2017.
|
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#include "Video.hpp"
|
|
|
|
using namespace ZX8081;
|
|
|
|
Video::Video() :
|
|
crt_(new Outputs::CRT::CRT(207 * 2, 1, Outputs::CRT::DisplayType::PAL50, 1)),
|
|
line_data_(nullptr),
|
|
line_data_pointer_(nullptr),
|
|
cycles_since_update_(0),
|
|
sync_(false) {
|
|
|
|
// Set a composite sampling function that assumes 8bpp input grayscale.
|
|
// TODO: lessen this to 1bpp.
|
|
crt_->set_composite_sampling_function(
|
|
"float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)"
|
|
"{"
|
|
"return float(texture(texID, coordinate).r) / 255.0;"
|
|
"}");
|
|
|
|
// Show only the centre 80% of the TV frame.
|
|
crt_->set_visible_area(Outputs::CRT::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.
|
|
cycles_since_update_ += (unsigned int)half_cycles.as_int();
|
|
}
|
|
|
|
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(cycles_since_update_);
|
|
} 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.
|
|
unsigned int data_length = (unsigned int)(line_data_pointer_ - line_data_);
|
|
if(data_length < cycles_since_update_ || next_sync) {
|
|
unsigned int output_length = std::min(data_length, cycles_since_update_);
|
|
crt_->output_data(output_length, 1);
|
|
line_data_pointer_ = line_data_ = nullptr;
|
|
cycles_since_update_ -= output_length;
|
|
} else return;
|
|
}
|
|
|
|
// Any pending pixels being dealt with, pad with the white level.
|
|
uint8_t *colour_pointer = (uint8_t *)crt_->allocate_write_area(1);
|
|
if(colour_pointer) *colour_pointer = 0xff;
|
|
crt_->output_level(cycles_since_update_);
|
|
}
|
|
|
|
cycles_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(320);
|
|
}
|
|
|
|
// 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_ == 320) {
|
|
crt_->output_data(320, 1);
|
|
cycles_since_update_ -= 320;
|
|
line_data_pointer_ = line_data_ = crt_->allocate_write_area(320);
|
|
if(!line_data_) return;
|
|
}
|
|
|
|
uint8_t mask = 0x80;
|
|
for(int c = 0; c < 8; c++) {
|
|
line_data_pointer_[c] = (byte & mask) ? 0xff : 0x00;
|
|
mask >>= 1;
|
|
}
|
|
line_data_pointer_ += 8;
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<Outputs::CRT::CRT> Video::get_crt() {
|
|
return crt_;
|
|
}
|