mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +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
|
||||
// appearance here.
|
||||
template <int video_divider>
|
||||
template <int video_divider, bool original_speed>
|
||||
void macro_tick() {
|
||||
macro_counter_ -= 24;
|
||||
|
||||
@ -349,39 +349,37 @@ class ConcreteMachine:
|
||||
// * timers: 2;
|
||||
// * sound: 1.
|
||||
|
||||
tick_cpu_video<0, video_divider>(); tick_cpu_video<1, video_divider>();
|
||||
tick_cpu_video<2, video_divider>(); tick_floppy();
|
||||
tick_cpu_video<3, video_divider>(); tick_cpu_video<4, video_divider>();
|
||||
tick_cpu_video<5, video_divider>(); tick_floppy();
|
||||
tick_cpu_video<6, video_divider>(); tick_cpu_video<7, video_divider>();
|
||||
tick_cpu_video<8, video_divider>(); tick_floppy();
|
||||
tick_cpu_video<9, video_divider>(); tick_cpu_video<10, video_divider>();
|
||||
tick_cpu_video<11, video_divider>(); tick_floppy();
|
||||
tick_cpu_video<0, video_divider, original_speed>(); tick_cpu_video<1, video_divider, original_speed>();
|
||||
tick_cpu_video<2, video_divider, original_speed>(); tick_floppy();
|
||||
tick_cpu_video<3, video_divider, original_speed>(); tick_cpu_video<4, video_divider, original_speed>();
|
||||
tick_cpu_video<5, video_divider, original_speed>(); tick_floppy();
|
||||
tick_cpu_video<6, video_divider, original_speed>(); tick_cpu_video<7, video_divider, original_speed>();
|
||||
tick_cpu_video<8, video_divider, original_speed>(); tick_floppy();
|
||||
tick_cpu_video<9, video_divider, original_speed>(); tick_cpu_video<10, video_divider, original_speed>();
|
||||
tick_cpu_video<11, video_divider, original_speed>(); tick_floppy();
|
||||
tick_timers();
|
||||
|
||||
tick_cpu_video<12, video_divider>(); tick_cpu_video<13, video_divider>();
|
||||
tick_cpu_video<14, video_divider>(); tick_floppy();
|
||||
tick_cpu_video<15, video_divider>(); tick_cpu_video<16, video_divider>();
|
||||
tick_cpu_video<17, video_divider>(); tick_floppy();
|
||||
tick_cpu_video<18, video_divider>(); tick_cpu_video<19, video_divider>();
|
||||
tick_cpu_video<20, video_divider>(); tick_floppy();
|
||||
tick_cpu_video<21, video_divider>(); tick_cpu_video<22, video_divider>();
|
||||
tick_cpu_video<23, video_divider>(); tick_floppy();
|
||||
tick_cpu_video<12, video_divider, original_speed>(); tick_cpu_video<13, video_divider, original_speed>();
|
||||
tick_cpu_video<14, video_divider, original_speed>(); tick_floppy();
|
||||
tick_cpu_video<15, video_divider, original_speed>(); tick_cpu_video<16, video_divider, original_speed>();
|
||||
tick_cpu_video<17, video_divider, original_speed>(); tick_floppy();
|
||||
tick_cpu_video<18, video_divider, original_speed>(); tick_cpu_video<19, video_divider, original_speed>();
|
||||
tick_cpu_video<20, video_divider, original_speed>(); tick_floppy();
|
||||
tick_cpu_video<21, video_divider, original_speed>(); tick_cpu_video<22, video_divider, original_speed>();
|
||||
tick_cpu_video<23, video_divider, original_speed>(); tick_floppy();
|
||||
tick_timers();
|
||||
tick_sound();
|
||||
}
|
||||
int macro_counter_ = 0;
|
||||
|
||||
template <int offset, int video_divider>
|
||||
template <int offset, int video_divider, bool original_speed>
|
||||
void tick_cpu_video() {
|
||||
if constexpr (!(offset % video_divider)) {
|
||||
tick_video();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Debug mode: run CPU a lot slower. Actually at close to original advertised MIPS speed.
|
||||
if constexpr (offset & 7) return;
|
||||
#endif
|
||||
if constexpr (original_speed && (offset & 7)) return;
|
||||
if constexpr (offset & 1) return;
|
||||
tick_cpu();
|
||||
}
|
||||
@ -473,20 +471,36 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
// MARK: - TimedMachine.
|
||||
int video_divider_ = 1;
|
||||
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>();
|
||||
|
||||
while(macro_counter_ > 0) {
|
||||
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;
|
||||
default: macro_tick<2, original_speed>(); break;
|
||||
case 3: macro_tick<3, original_speed>(); break;
|
||||
case 4: macro_tick<4, original_speed>(); break;
|
||||
case 6: macro_tick<6, original_speed>(); break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
int video_divider_ = 1;
|
||||
|
||||
void tick_cpu() {
|
||||
const uint32_t instruction = advance_pipeline(executor_.pc() + 8);
|
||||
|
@ -142,6 +142,16 @@ struct Video {
|
||||
if(phase == Phase::Display) {
|
||||
address_ = frame_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) {
|
||||
entered_flyback_ = true;
|
||||
@ -211,7 +221,10 @@ struct Video {
|
||||
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_end(uint32_t address) { buffer_end_ = address; }
|
||||
void set_cursor_start(uint32_t address) { cursor_start_ = address; }
|
||||
@ -223,6 +236,10 @@ struct Video {
|
||||
return static_cast<int>(clock_divider_);
|
||||
}
|
||||
|
||||
int frame_rate_overages() const {
|
||||
return overages_;
|
||||
}
|
||||
|
||||
private:
|
||||
Log::Logger<Log::Source::ARMIOC> logger;
|
||||
InterruptObserverT &interrupt_observer_;
|
||||
@ -361,6 +378,10 @@ private:
|
||||
uint32_t frame_start_ = 0;
|
||||
uint32_t cursor_start_ = 0;
|
||||
|
||||
int frame_start_sets_ = 0;
|
||||
int frame_starts_ = 0;
|
||||
int overages_ = 0;
|
||||
|
||||
// Ephemeral address state.
|
||||
uint32_t address_ = 0;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user