1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-23 20:29:42 +00:00

Install and properly clock a CRT.

This commit is contained in:
Thomas Harte 2024-03-21 20:41:24 -04:00
parent 1f49c3b113
commit 2de1a2dd0d
6 changed files with 69 additions and 26 deletions

View File

@ -116,10 +116,10 @@ class ConcreteMachine:
private:
// MARK: - ScanProducer.
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
(void)scan_target;
executor_.bus.video().crt().set_scan_target(scan_target);
}
Outputs::Display::ScanStatus get_scaled_scan_status() const override {
return Outputs::Display::ScanStatus();
return executor_.bus.video().crt().get_scaled_scan_status();
}
std::array<uint32_t, 10> pc_history;
@ -208,8 +208,8 @@ class ConcreteMachine:
}
void tick_timers() { executor_.bus.tick_timers(); }
void tick_sound() { executor_.bus.tick_sound(); }
void tick_video() { executor_.bus.tick_video(); }
void tick_sound() { executor_.bus.sound().tick(); }
void tick_video() { executor_.bus.video().tick(); }
// MARK: - MediaTarget
bool insert_media(const Analyser::Static::Media &) override {

View File

@ -328,13 +328,10 @@ struct InputOutputController {
update_interrupts();
}
Sound<InputOutputController> &sound() {
return sound_;
}
Video<InputOutputController, Sound<InputOutputController>> &video() {
return video_;
}
auto &sound() { return sound_; }
const auto &sound() const { return sound_; }
auto &video() { return video_; }
const auto &video() const { return video_; }
void update_interrupts() {
if(sound_.interrupt()) {

View File

@ -197,15 +197,11 @@ struct MemoryController {
return false;
}
void tick_timers() { ioc_.tick_timers(); }
void tick_sound() {
// TODO: does disabling sound DMA pause output, or leave it ticking and merely
// stop allowing it to use the bus?
ioc_.sound().tick();
}
void tick_video() {
ioc_.video().tick();
}
void tick_timers() { ioc_.tick_timers(); }
auto &sound() { return ioc_.sound(); }
const auto &sound() const { return ioc_.sound(); }
auto &video() { return ioc_.video(); }
const auto &video() const { return ioc_.video(); }
private:
Log::Logger<Log::Source::ARMIOC> logger;

View File

@ -9,6 +9,7 @@
#pragma once
#include "../../../Outputs/Log.hpp"
#include "../../../Outputs/CRT/CRT.hpp"
#include <cstdint>
@ -17,7 +18,12 @@ namespace Archimedes {
template <typename InterruptObserverT, typename SoundT>
struct Video {
Video(InterruptObserverT &observer, SoundT &sound, const uint8_t *ram) :
observer_(observer), sound_(sound), ram_(ram) {}
observer_(observer),
sound_(sound),
ram_(ram),
crt_(Outputs::Display::InputDataType::Red4Green4Blue4) {
set_clock_divider(2);
}
void write(uint32_t value) {
const auto target = (value >> 24) & 0xfc;
@ -112,10 +118,10 @@ struct Video {
// Set pixel rate. This is the value that a 24Mhz clock should be divided
// by to get half the pixel rate.
switch(value & 0b11) {
case 0b00: clock_divider_ = 6; break; // i.e. pixel clock = 8Mhz.
case 0b01: clock_divider_ = 4; break; // 12Mhz.
case 0b10: clock_divider_ = 3; break; // 16Mhz.
case 0b11: clock_divider_ = 2; break; // 24Mhz.
case 0b00: set_clock_divider(6); break; // i.e. pixel clock = 8Mhz.
case 0b01: set_clock_divider(4); break; // 12Mhz.
case 0b10: set_clock_divider(3); break; // 16Mhz.
case 0b11: set_clock_divider(2); break; // 24Mhz.
}
break;
@ -161,6 +167,9 @@ struct Video {
void set_buffer_end(uint32_t address) { buffer_end_ = address; }
void set_cursor_start(uint32_t address) { cursor_start_ = address; }
Outputs::CRT::CRT &crt() { return crt_; }
const Outputs::CRT::CRT &crt() const { return crt_; }
private:
Log::Logger<Log::Source::ARMIOC> logger;
InterruptObserverT &observer_;
@ -169,6 +178,7 @@ private:
// In the current version of this code, video DMA occurrs costlessly,
// being deferred to the component itself.
const uint8_t *ram_ = nullptr;
Outputs::CRT::CRT crt_;
// TODO: real video output.
uint32_t position_ = 0;
@ -202,6 +212,22 @@ private:
// the pixel clock because that's the fidelity at which the programmer
// places horizontal events — display start, end, sync period, etc.
uint32_t clock_divider_ = 0;
void set_clock_divider(uint32_t divider) {
if(divider == clock_divider_) {
return;
}
clock_divider_ = divider;
crt_.set_new_timing(
24'000'000 / (divider * 312 * 50), /* Cycle per line. */
312, /* Height of display. */
Outputs::Display::ColourSpace::YIQ, /* Composite colour space. */
Outputs::CRT::PAL::ColourCycleNumerator,
Outputs::CRT::PAL::ColourCycleDenominator,
Outputs::CRT::PAL::VerticalSyncLength,
true);
}
};
}

View File

@ -117,7 +117,14 @@ void CRT::set_new_display_type(int cycles_per_line, Outputs::Display::Type displ
case Outputs::Display::Type::PAL50:
case Outputs::Display::Type::PAL60:
scan_target_modals_.intended_gamma = 2.8f;
set_new_timing(cycles_per_line, (displayType == Outputs::Display::Type::PAL50) ? 312 : 262, Outputs::Display::ColourSpace::YUV, 709379, 2500, 5, true);
set_new_timing(
cycles_per_line,
(displayType == Outputs::Display::Type::PAL50) ? 312 : 262,
Outputs::Display::ColourSpace::YUV,
PAL::ColourCycleNumerator,
PAL::ColourCycleDenominator,
PAL::VerticalSyncLength,
true);
// i.e. 283.7516 colour cycles per line; 2.5 lines = vertical sync.
break;
@ -176,6 +183,9 @@ CRT::CRT(int cycles_per_line,
set_new_timing(cycles_per_line, height_of_display, Outputs::Display::ColourSpace::YIQ, 1, 1, vertical_sync_half_lines, false);
}
// Use some from-thin-air arbitrary constants for default timing, otherwise passing
// construction off to one of the other constructors.
CRT::CRT(Outputs::Display::InputDataType data_type) : CRT(100, 1, 100, 1, data_type) {}
// MARK: - Sync loop

View File

@ -18,6 +18,15 @@
namespace Outputs::CRT {
namespace PAL {
// PAL: 283.7516 colour cycles per line; 2.5 lines = vertical sync.
static constexpr int ColourCycleNumerator = 709379;
static constexpr int ColourCycleDenominator = 2500;
static constexpr int VerticalSyncLength = 5;
}
class CRT;
class Delegate {
@ -136,6 +145,11 @@ class CRT {
Outputs::Display::Type display_type,
Outputs::Display::InputDataType data_type);
/*! Constructs a CRT with no guaranteed expectations as to input signal other than data type;
this allows for callers that intend to rely on @c set_new_timing.
*/
CRT(Outputs::Display::InputDataType data_type);
/*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
been provided at construction. */
void set_new_timing(