1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-16 22:28:57 +00:00
CLK/Machines/ZX8081/Video.cpp
Thomas Harte d97a073d1b Adds the necessary routine for all machines to be able to respond to get_scan_status.
They all just as the CRT, as all are currently based on the CRT. Which doesn't currently know the total clock rate it would need to in order properly to scale the answer to the question. Further thought coming.
2020-01-20 21:45:10 -05:00

116 lines
3.2 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() :
crt_(207 * 2, 1, Outputs::Display::Type::PAL50, Outputs::Display::InputDataType::Luminance1) {
// Show only the centre 80% of the TV frame.
crt_.set_display_type(Outputs::Display::DisplayType::CompositeMonochrome);
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(int(time_since_update_.as_integral()));
} 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 < int(time_since_update_.as_integral()) || next_sync) {
auto output_length = std::min(data_length, int(time_since_update_.as_integral()));
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_.begin_data(1));
if(colour_pointer) *colour_pointer = 0xff;
crt_.output_level(int(time_since_update_.as_integral()));
}
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_.begin_data(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_.begin_data(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;
}
}
void Video::set_scan_target(Outputs::Display::ScanTarget *scan_target) {
crt_.set_scan_target(scan_target);
}
Outputs::Display::ScanStatus Video::get_scan_status() const {
return crt_.get_scan_status();
}