1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-18 01:30:56 +00:00

Move responsibility for clock division; reinstate vsync interrupt.

This commit is contained in:
Thomas Harte 2024-03-22 10:01:34 -04:00
parent 4a2dcff028
commit ae6cf69449
5 changed files with 74 additions and 46 deletions

View File

@ -21,7 +21,7 @@ namespace InstructionSet::ARM {
template <Model model, typename MemoryT> template <Model model, typename MemoryT>
struct Executor { struct Executor {
template <typename... Args> template <typename... Args>
Executor(Args &&...args) : bus(std::forward<Args...>(args)...) {} Executor(Args &&...args) : bus(std::forward<Args>(args)...) {}
/// @returns @c true if @c condition implies an appropriate perform call should be made for this instruction, /// @returns @c true if @c condition implies an appropriate perform call should be made for this instruction,
/// @c false otherwise. /// @c false otherwise.

View File

@ -53,6 +53,7 @@ class ConcreteMachine:
// //
// The implementation of this is coupled to the ClockRate above, hence its // The implementation of this is coupled to the ClockRate above, hence its
// appearance here. // appearance here.
template <int video_divider>
void macro_tick() { void macro_tick() {
macro_counter_ -= 24; macro_counter_ -= 24;
@ -60,34 +61,41 @@ class ConcreteMachine:
// Hence, required ticks are: // Hence, required ticks are:
// //
// * CPU: 24; // * CPU: 24;
// * video: 12; // * video: 24 / video_divider;
// * timers: 2; // * timers: 2;
// * sound: 1. // * sound: 1.
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<0, video_divider>(); tick_cpu_video<1, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<2, video_divider>(); tick_cpu_video<3, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<4, video_divider>(); tick_cpu_video<5, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<6, video_divider>(); tick_cpu_video<7, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<8, video_divider>(); tick_cpu_video<9, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<10, video_divider>(); tick_cpu_video<11, video_divider>();
tick_timers(); tick_timers();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<12, video_divider>(); tick_cpu_video<13, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<14, video_divider>(); tick_cpu_video<15, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<16, video_divider>(); tick_cpu_video<17, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<18, video_divider>(); tick_cpu_video<19, video_divider>();
tick_cpu(); tick_cpu(); tick_video(); tick_cpu_video<20, video_divider>(); tick_cpu_video<21, video_divider>();
tick_cpu(); tick_cpu(); tick_video();
tick_timers(); tick_timers();
tick_sound(); tick_sound();
} }
int macro_counter_ = 0; int macro_counter_ = 0;
template <int offset, int video_divider>
void tick_cpu_video() {
tick_cpu();
if constexpr (!(offset % video_divider)) {
tick_video();
}
}
public: public:
ConcreteMachine( ConcreteMachine(
const Analyser::Static::Target &target, const Analyser::Static::Target &target,
const ROMMachine::ROMFetcher &rom_fetcher const ROMMachine::ROMFetcher &rom_fetcher
) : executor_(*this) { ) : executor_(*this, *this) {
set_clock_rate(ClockRate); set_clock_rate(ClockRate);
constexpr ROM::Name risc_os = ROM::Name::AcornRISCOS319; constexpr ROM::Name risc_os = ROM::Name::AcornRISCOS319;
@ -113,6 +121,10 @@ class ConcreteMachine:
} }
} }
void update_clock_rates() {
printf("");
}
private: private:
// MARK: - ScanProducer. // MARK: - ScanProducer.
void set_scan_target(Outputs::Display::ScanTarget *scan_target) override { void set_scan_target(Outputs::Display::ScanTarget *scan_target) override {
@ -131,9 +143,16 @@ class ConcreteMachine:
macro_counter_ += cycles.as<int>(); macro_counter_ += cycles.as<int>();
while(macro_counter_ > 0) { while(macro_counter_ > 0) {
macro_tick(); switch(video_divider_) {
default: macro_tick<2>(); break;
case 3: macro_tick<3>(); break;
case 4: macro_tick<4>(); break;
case 6: macro_tick<6>(); break;
}
} }
} }
int video_divider_ = 1;
void tick_cpu() { void tick_cpu() {
static uint32_t last_pc = 0; static uint32_t last_pc = 0;
@ -225,7 +244,7 @@ class ConcreteMachine:
// MARK: - ARM execution // MARK: - ARM execution
static constexpr auto arm_model = InstructionSet::ARM::Model::ARMv2; static constexpr auto arm_model = InstructionSet::ARM::Model::ARMv2;
InstructionSet::ARM::Executor<arm_model, MemoryController<ConcreteMachine>> executor_; InstructionSet::ARM::Executor<arm_model, MemoryController<ConcreteMachine, ConcreteMachine>> executor_;
}; };
} }

View File

@ -58,7 +58,7 @@ namespace InterruptRequests {
static constexpr int FIQ = 0x02; static constexpr int FIQ = 0x02;
}; };
template <typename InterruptObserverT> template <typename InterruptObserverT, typename ClockRateObserverT>
struct InputOutputController { struct InputOutputController {
int interrupt_mask() const { int interrupt_mask() const {
return return
@ -314,11 +314,11 @@ struct InputOutputController {
return true; return true;
} }
InputOutputController(InterruptObserverT &observer, const uint8_t *ram) : InputOutputController(InterruptObserverT &observer, ClockRateObserverT &clock_observer, const uint8_t *ram) :
observer_(observer), observer_(observer),
keyboard_(serial_), keyboard_(serial_),
sound_(*this, ram), sound_(*this, ram),
video_(*this, sound_, ram) video_(*this, clock_observer, sound_, ram)
{ {
irq_a_.status = IRQA::SetAlways | IRQA::PowerOnReset; irq_a_.status = IRQA::SetAlways | IRQA::PowerOnReset;
irq_b_.status = 0x00; irq_b_.status = 0x00;
@ -388,7 +388,7 @@ private:
// Audio and video. // Audio and video.
Sound<InputOutputController> sound_; Sound<InputOutputController> sound_;
Video<InputOutputController, Sound<InputOutputController>> video_; Video<InputOutputController, ClockRateObserverT, Sound<InputOutputController>> video_;
}; };
} }

View File

@ -28,11 +28,12 @@ static_assert(BitMask<15, 15>::value == 32768);
static_assert(BitMask<15, 0>::value == 0xffff); static_assert(BitMask<15, 0>::value == 0xffff);
static_assert(BitMask<15, 14>::value == 49152); static_assert(BitMask<15, 14>::value == 49152);
/// Models the MEMC, making this the Archimedes bus. Owns various other chips on the bus as a result. /// Models the MEMC, making this the Archimedes bus. Owns various other chips on the bus as a result.
template <typename InterruptObserverT> template <typename InterruptObserverT, typename ClockRateObserverT>
struct MemoryController { struct MemoryController {
MemoryController(InterruptObserverT &observer) : MemoryController(InterruptObserverT &observer, ClockRateObserverT &clock_rate_observer) :
ioc_(observer, ram_.data()) {} ioc_(observer, clock_rate_observer, ram_.data()) {}
int interrupt_mask() const { int interrupt_mask() const {
return ioc_.interrupt_mask(); return ioc_.interrupt_mask();
@ -240,7 +241,7 @@ struct MemoryController {
bool has_moved_rom_ = false; bool has_moved_rom_ = false;
std::array<uint8_t, 4*1024*1024> ram_{}; std::array<uint8_t, 4*1024*1024> ram_{};
std::array<uint8_t, 2*1024*1024> rom_; std::array<uint8_t, 2*1024*1024> rom_;
InputOutputController<InterruptObserverT> ioc_; InputOutputController<InterruptObserverT, ClockRateObserverT> ioc_;
template <typename IntT> template <typename IntT>
IntT &physical_ram(uint32_t address) { IntT &physical_ram(uint32_t address) {

View File

@ -16,10 +16,11 @@
namespace Archimedes { namespace Archimedes {
template <typename InterruptObserverT, typename SoundT> template <typename InterruptObserverT, typename ClockRateObserverT, typename SoundT>
struct Video { struct Video {
Video(InterruptObserverT &observer, SoundT &sound, const uint8_t *ram) : Video(InterruptObserverT &interrupt_observer, ClockRateObserverT &clock_rate_observer, SoundT &sound, const uint8_t *ram) :
observer_(observer), interrupt_observer_(interrupt_observer),
clock_rate_observer_(clock_rate_observer),
sound_(sound), sound_(sound),
ram_(ram), ram_(ram),
crt_(Outputs::Display::InputDataType::Red4Green4Blue4) { crt_(Outputs::Display::InputDataType::Red4Green4Blue4) {
@ -109,26 +110,27 @@ struct Video {
void tick() { void tick() {
// Pick new horizontal state, possibly rolling over into the vertical. // Pick new horizontal state, possibly rolling over into the vertical.
horizontal_state_.increment_position(clock_divider_); horizontal_state_.increment_position();
horizontal_state_.phase = horizontal_state_.phase =
horizontal_timing_.phase_after( horizontal_timing_.phase_after(
horizontal_state_.position, horizontal_state_.position,
horizontal_state_.phase, horizontal_state_.phase);
clock_divider_);
if(horizontal_state_.position == horizontal_timing_.period * clock_divider_) { if(horizontal_state_.position == horizontal_timing_.period ) {
horizontal_state_.position = 0; horizontal_state_.position = 0;
vertical_state_.increment_position(1); vertical_state_.increment_position();
vertical_state_.phase = vertical_state_.phase =
vertical_timing_.phase_after( vertical_timing_.phase_after(
vertical_state_.position, vertical_state_.position,
vertical_state_.phase, vertical_state_.phase);
1);
if(vertical_state_.position == vertical_timing_.period) { if(vertical_state_.position == vertical_timing_.period) {
vertical_state_.position = 0; vertical_state_.position = 0;
address_ = frame_start_; address_ = frame_start_;
entered_sync_ = true;
interrupt_observer_.update_interrupts();
} }
} }
@ -148,7 +150,7 @@ struct Video {
// Possibly output something. TODO: with actual pixels. // Possibly output something. TODO: with actual pixels.
if(new_phase != phase_) { if(new_phase != phase_) {
const auto duration = static_cast<int>(time_in_phase_ / clock_divider_); const auto duration = static_cast<int>(time_in_phase_);
switch(phase_) { switch(phase_) {
case Phase::Sync: crt_.output_sync(duration); break; case Phase::Sync: crt_.output_sync(duration); break;
@ -182,9 +184,14 @@ struct Video {
Outputs::CRT::CRT &crt() { return crt_; } Outputs::CRT::CRT &crt() { return crt_; }
const Outputs::CRT::CRT &crt() const { return crt_; } const Outputs::CRT::CRT &crt() const { return crt_; }
int clock_divider() const {
return clock_divider_;
}
private: private:
Log::Logger<Log::Source::ARMIOC> logger; Log::Logger<Log::Source::ARMIOC> logger;
InterruptObserverT &observer_; InterruptObserverT &interrupt_observer_;
ClockRateObserverT &clock_rate_observer_;
SoundT &sound_; SoundT &sound_;
// In the current version of this code, video DMA occurrs costlessly, // In the current version of this code, video DMA occurrs costlessly,
@ -200,9 +207,9 @@ private:
uint32_t position = 0; uint32_t position = 0;
Phase phase = Phase::Sync; Phase phase = Phase::Sync;
void increment_position(uint32_t divider) { void increment_position() {
++position; ++position;
if(position == 1024 * divider) position = 0; if(position == 1024) position = 0;
} }
}; };
State horizontal_state_, vertical_state_; State horizontal_state_, vertical_state_;
@ -229,13 +236,13 @@ private:
uint32_t cursor_start = 0; uint32_t cursor_start = 0;
uint32_t cursor_end = 0; uint32_t cursor_end = 0;
Phase phase_after(uint32_t position, Phase current_phase, uint32_t divider) { Phase phase_after(uint32_t position, Phase current_phase) {
if(position == sync_width * divider) return Phase::Blank; if(position == sync_width) return Phase::Blank;
if(position == border_start * divider) return Phase::Border; if(position == border_start) return Phase::Border;
if(position == display_start * divider) return Phase::Display; if(position == display_start) return Phase::Display;
if(position == display_end * divider) return Phase::Border; if(position == display_end) return Phase::Border;
if(position == border_end * divider) return Phase::Blank; if(position == border_end) return Phase::Blank;
if(position == period * divider) return Phase::Sync; if(position == period) return Phase::Sync;
return current_phase; return current_phase;
} }
}; };
@ -270,6 +277,7 @@ private:
Outputs::CRT::PAL::ColourCycleDenominator, Outputs::CRT::PAL::ColourCycleDenominator,
Outputs::CRT::PAL::VerticalSyncLength, Outputs::CRT::PAL::VerticalSyncLength,
Outputs::CRT::PAL::AlternatesPhase); Outputs::CRT::PAL::AlternatesPhase);
clock_rate_observer_.update_clock_rates();
} }
}; };