mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Merge pull request #1370 from TomHarte/Zarch
Add automatic runtime frame-rate limiter.
This commit is contained in:
commit
cb70967971
@ -336,7 +336,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>
|
template <int video_divider, bool original_speed>
|
||||||
void macro_tick() {
|
void macro_tick() {
|
||||||
macro_counter_ -= 24;
|
macro_counter_ -= 24;
|
||||||
|
|
||||||
@ -349,39 +349,37 @@ class ConcreteMachine:
|
|||||||
// * timers: 2;
|
// * timers: 2;
|
||||||
// * sound: 1.
|
// * sound: 1.
|
||||||
|
|
||||||
tick_cpu_video<0, video_divider>(); tick_cpu_video<1, video_divider>();
|
tick_cpu_video<0, video_divider, original_speed>(); tick_cpu_video<1, video_divider, original_speed>();
|
||||||
tick_cpu_video<2, video_divider>(); tick_floppy();
|
tick_cpu_video<2, video_divider, original_speed>(); tick_floppy();
|
||||||
tick_cpu_video<3, video_divider>(); tick_cpu_video<4, video_divider>();
|
tick_cpu_video<3, video_divider, original_speed>(); tick_cpu_video<4, video_divider, original_speed>();
|
||||||
tick_cpu_video<5, video_divider>(); tick_floppy();
|
tick_cpu_video<5, video_divider, original_speed>(); tick_floppy();
|
||||||
tick_cpu_video<6, video_divider>(); tick_cpu_video<7, video_divider>();
|
tick_cpu_video<6, video_divider, original_speed>(); tick_cpu_video<7, video_divider, original_speed>();
|
||||||
tick_cpu_video<8, video_divider>(); tick_floppy();
|
tick_cpu_video<8, video_divider, original_speed>(); tick_floppy();
|
||||||
tick_cpu_video<9, video_divider>(); tick_cpu_video<10, video_divider>();
|
tick_cpu_video<9, video_divider, original_speed>(); tick_cpu_video<10, video_divider, original_speed>();
|
||||||
tick_cpu_video<11, video_divider>(); tick_floppy();
|
tick_cpu_video<11, video_divider, original_speed>(); tick_floppy();
|
||||||
tick_timers();
|
tick_timers();
|
||||||
|
|
||||||
tick_cpu_video<12, video_divider>(); tick_cpu_video<13, video_divider>();
|
tick_cpu_video<12, video_divider, original_speed>(); tick_cpu_video<13, video_divider, original_speed>();
|
||||||
tick_cpu_video<14, video_divider>(); tick_floppy();
|
tick_cpu_video<14, video_divider, original_speed>(); tick_floppy();
|
||||||
tick_cpu_video<15, video_divider>(); tick_cpu_video<16, video_divider>();
|
tick_cpu_video<15, video_divider, original_speed>(); tick_cpu_video<16, video_divider, original_speed>();
|
||||||
tick_cpu_video<17, video_divider>(); tick_floppy();
|
tick_cpu_video<17, video_divider, original_speed>(); tick_floppy();
|
||||||
tick_cpu_video<18, video_divider>(); tick_cpu_video<19, video_divider>();
|
tick_cpu_video<18, video_divider, original_speed>(); tick_cpu_video<19, video_divider, original_speed>();
|
||||||
tick_cpu_video<20, video_divider>(); tick_floppy();
|
tick_cpu_video<20, video_divider, original_speed>(); tick_floppy();
|
||||||
tick_cpu_video<21, video_divider>(); tick_cpu_video<22, video_divider>();
|
tick_cpu_video<21, video_divider, original_speed>(); tick_cpu_video<22, video_divider, original_speed>();
|
||||||
tick_cpu_video<23, video_divider>(); tick_floppy();
|
tick_cpu_video<23, video_divider, original_speed>(); tick_floppy();
|
||||||
tick_timers();
|
tick_timers();
|
||||||
tick_sound();
|
tick_sound();
|
||||||
}
|
}
|
||||||
int macro_counter_ = 0;
|
int macro_counter_ = 0;
|
||||||
|
|
||||||
template <int offset, int video_divider>
|
template <int offset, int video_divider, bool original_speed>
|
||||||
void tick_cpu_video() {
|
void tick_cpu_video() {
|
||||||
if constexpr (!(offset % video_divider)) {
|
if constexpr (!(offset % video_divider)) {
|
||||||
tick_video();
|
tick_video();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
// Debug mode: run CPU a lot slower. Actually at close to original advertised MIPS speed.
|
// Debug mode: run CPU a lot slower. Actually at close to original advertised MIPS speed.
|
||||||
if constexpr (offset & 7) return;
|
if constexpr (original_speed && (offset & 7)) return;
|
||||||
#endif
|
|
||||||
if constexpr (offset & 1) return;
|
if constexpr (offset & 1) return;
|
||||||
tick_cpu();
|
tick_cpu();
|
||||||
}
|
}
|
||||||
@ -473,20 +471,36 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - TimedMachine.
|
// MARK: - TimedMachine.
|
||||||
|
int video_divider_ = 1;
|
||||||
void run_for(Cycles cycles) override {
|
void run_for(Cycles cycles) override {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// Debug mode: always run 'slowly' because that's less of a burden, and
|
||||||
|
// because it allows me to peer at problems with greater leisure.
|
||||||
|
const bool use_original_speed = true;
|
||||||
|
#else
|
||||||
|
// As a first, blunt implementation: try to model something close
|
||||||
|
// to original speed if there have been 10 frame rate overages in total.
|
||||||
|
const bool use_original_speed = executor_.bus.video().frame_rate_overages() > 10;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(use_original_speed) run_for<true>(cycles);
|
||||||
|
else run_for<false>(cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool original_speed>
|
||||||
|
void run_for(Cycles cycles) {
|
||||||
macro_counter_ += cycles.as<int>();
|
macro_counter_ += cycles.as<int>();
|
||||||
|
|
||||||
while(macro_counter_ > 0) {
|
while(macro_counter_ > 0) {
|
||||||
switch(video_divider_) {
|
switch(video_divider_) {
|
||||||
default: macro_tick<2>(); break;
|
default: macro_tick<2, original_speed>(); break;
|
||||||
case 3: macro_tick<3>(); break;
|
case 3: macro_tick<3, original_speed>(); break;
|
||||||
case 4: macro_tick<4>(); break;
|
case 4: macro_tick<4, original_speed>(); break;
|
||||||
case 6: macro_tick<6>(); break;
|
case 6: macro_tick<6, original_speed>(); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int video_divider_ = 1;
|
|
||||||
|
|
||||||
void tick_cpu() {
|
void tick_cpu() {
|
||||||
const uint32_t instruction = advance_pipeline(executor_.pc() + 8);
|
const uint32_t instruction = advance_pipeline(executor_.pc() + 8);
|
||||||
|
@ -142,6 +142,16 @@ struct Video {
|
|||||||
if(phase == Phase::Display) {
|
if(phase == Phase::Display) {
|
||||||
address_ = frame_start_;
|
address_ = frame_start_;
|
||||||
cursor_address_ = cursor_start_;
|
cursor_address_ = cursor_start_;
|
||||||
|
|
||||||
|
// Accumulate a count of how many times the processor has tried
|
||||||
|
// to update the visible buffer more than once in a frame; this
|
||||||
|
// will usually indicate that the software being run isn't properly
|
||||||
|
// synchronised to actual machine speed.
|
||||||
|
++frame_starts_;
|
||||||
|
if(frame_start_sets_ > 10) {
|
||||||
|
overages_ += frame_start_sets_ > frame_starts_;
|
||||||
|
frame_start_sets_ = frame_starts_ = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(old_phase == Phase::Display) {
|
if(old_phase == Phase::Display) {
|
||||||
entered_flyback_ = true;
|
entered_flyback_ = true;
|
||||||
@ -211,7 +221,10 @@ struct Video {
|
|||||||
return vertical_state_.phase() != Phase::Display;
|
return vertical_state_.phase() != Phase::Display;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_frame_start(uint32_t address) { frame_start_ = address; }
|
void set_frame_start(uint32_t address) {
|
||||||
|
frame_start_ = address;
|
||||||
|
++frame_start_sets_;
|
||||||
|
}
|
||||||
void set_buffer_start(uint32_t address) { buffer_start_ = address; }
|
void set_buffer_start(uint32_t address) { buffer_start_ = address; }
|
||||||
void set_buffer_end(uint32_t address) { buffer_end_ = address; }
|
void set_buffer_end(uint32_t address) { buffer_end_ = address; }
|
||||||
void set_cursor_start(uint32_t address) { cursor_start_ = address; }
|
void set_cursor_start(uint32_t address) { cursor_start_ = address; }
|
||||||
@ -223,6 +236,10 @@ struct Video {
|
|||||||
return static_cast<int>(clock_divider_);
|
return static_cast<int>(clock_divider_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int frame_rate_overages() const {
|
||||||
|
return overages_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Log::Logger<Log::Source::ARMIOC> logger;
|
Log::Logger<Log::Source::ARMIOC> logger;
|
||||||
InterruptObserverT &interrupt_observer_;
|
InterruptObserverT &interrupt_observer_;
|
||||||
@ -361,6 +378,10 @@ private:
|
|||||||
uint32_t frame_start_ = 0;
|
uint32_t frame_start_ = 0;
|
||||||
uint32_t cursor_start_ = 0;
|
uint32_t cursor_start_ = 0;
|
||||||
|
|
||||||
|
int frame_start_sets_ = 0;
|
||||||
|
int frame_starts_ = 0;
|
||||||
|
int overages_ = 0;
|
||||||
|
|
||||||
// Ephemeral address state.
|
// Ephemeral address state.
|
||||||
uint32_t address_ = 0;
|
uint32_t address_ = 0;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user