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:
parent
1f49c3b113
commit
2de1a2dd0d
@ -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 {
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user