openMSX
CPUClock.hh
Go to the documentation of this file.
1#ifndef CPUCLOCK_HH
2#define CPUCLOCK_HH
3
4#include "DynamicClock.hh"
5#include "Scheduler.hh"
6#include "narrow.hh"
7#include <cassert>
8
9namespace openmsx {
10
12{
13public:
14 unsigned getFreq() const { return clock.getFreq(); }
15
16protected:
17 CPUClock(EmuTime::param time, Scheduler& scheduler);
18
19// benchmarking showed a slowdown of ~3% on AMD64
20// when using the following code:
21#if 0
22 // 64-bit addition is cheap
23 inline void add(unsigned ticks) { clock += ticks; }
24 inline void sync() const { }
25#else
26 // 64-bit addition is expensive
27 // (if executed several million times per second)
28 inline void add(unsigned ticks) { remaining -= narrow_cast<int>(ticks); }
29 inline void sync() const {
30 clock.fastAdd(limit - remaining);
31 limit = remaining;
32 }
33#endif
34
35 // These are similar to the corresponding methods in DynamicClock.
36 [[nodiscard]] EmuTime::param getTime() const { sync(); return clock.getTime(); }
37 [[nodiscard]] EmuTime getTimeFast() const { return clock.getFastAdd(limit - remaining); }
38 [[nodiscard]] EmuTime getTimeFast(int cc) const {
39 return clock.getFastAdd(limit - remaining + cc);
40 }
41 void setTime(EmuTime::param time) { sync(); clock.reset(time); }
42 void setFreq(unsigned freq) { clock.setFreq(freq); }
43 void advanceTime(EmuTime::param time);
44 [[nodiscard]] EmuTime calcTime(EmuTime::param time, unsigned ticks) const {
45 return clock.add(time, ticks);
46 }
47
53 unsigned advanceHalt(unsigned hltStates, EmuTime::param time) {
54 sync();
55 unsigned ticks = clock.getTicksTillUp(time);
56 unsigned halts = (ticks + hltStates - 1) / hltStates; // round up
57 clock += halts * hltStates;
58 return halts;
59 }
60
65 void waitForEvenCycle(int cc)
66 {
67 sync();
68 auto totalTicks = clock.getTotalTicks() + cc;
69 if (totalTicks & 1) {
70 add(1);
71 }
72 }
73
74 // The following 3 methods are used in the innermost CPU loop. It
75 // allows to implement this loop with just one test. This loop must
76 // be exited on the following three conditions:
77 // 1) a Synchronization Point is reached
78 // 2) a 'slow' instruction must be executed (ei, di, ..)
79 // 3) another thread has requested to exit the loop
80 // The limitReached() method indicates whether the loop should be
81 // exited.
82 // The earliest SP must always be kept up-to-date with the setLimit()
83 // method, so after every action that might change SP. The Scheduler
84 // class is responsible for this.
85 // In 'slow' mode the limit mechanism can be disabled with the
86 // disableLimit() method. In disabled mode, the limitReached() method
87 // always returns true.
88 // When another thread requests to exit the loop, it's not needed to
89 // already exit at the next instruction. If we exit soon that's good
90 // enough. This is implemented by simply regularly exiting the loop
91 // (outside the inner loop, the real exit condition should be tested).
92
93 void setLimit(EmuTime::param time) {
94 if (limitEnabled) {
95 sync();
96 assert(remaining == limit);
97 limit = narrow_cast<int>(clock.getTicksTillUp(time) - 1);
98 remaining = limit;
99 } else {
100 assert(limit < 0);
101 }
102 }
103 void enableLimit() {
104 limitEnabled = true;
105 setLimit(scheduler.getNext());
106 }
108 limitEnabled = false;
109 int extra = limit - remaining;
110 limit = -1;
111 remaining = limit - extra;
112 }
113 [[nodiscard]] inline bool limitReached() const {
114 return remaining < 0;
115 }
116
117 template<typename Archive>
118 void serialize(Archive& ar, unsigned version);
119
120private:
121 mutable DynamicClock clock;
122 Scheduler& scheduler;
123 int remaining = -1;
124 mutable int limit = -1;
125 bool limitEnabled = false;
126};
127
128} // namespace openmsx
129
130#endif
unsigned getFreq() const
Definition: CPUClock.hh:14
void setFreq(unsigned freq)
Definition: CPUClock.hh:42
unsigned advanceHalt(unsigned hltStates, EmuTime::param time)
Implementation of the HALT instruction timing.
Definition: CPUClock.hh:53
void add(unsigned ticks)
Definition: CPUClock.hh:28
EmuTime::param getTime() const
Definition: CPUClock.hh:36
void enableLimit()
Definition: CPUClock.hh:103
CPUClock(EmuTime::param time, Scheduler &scheduler)
Definition: CPUClock.cc:6
void serialize(Archive &ar, unsigned version)
Definition: CPUClock.cc:20
void advanceTime(EmuTime::param time)
Definition: CPUClock.cc:12
void disableLimit()
Definition: CPUClock.hh:107
bool limitReached() const
Definition: CPUClock.hh:113
EmuTime getTimeFast() const
Definition: CPUClock.hh:37
void setTime(EmuTime::param time)
Definition: CPUClock.hh:41
EmuTime calcTime(EmuTime::param time, unsigned ticks) const
Definition: CPUClock.hh:44
void sync() const
Definition: CPUClock.hh:29
void setLimit(EmuTime::param time)
Definition: CPUClock.hh:93
EmuTime getTimeFast(int cc) const
Definition: CPUClock.hh:38
void waitForEvenCycle(int cc)
R800 runs at 7MHz, but I/O is done over a slower 3.5MHz bus.
Definition: CPUClock.hh:65
Represents a clock with a variable frequency.
Definition: DynamicClock.hh:17
EmuTime getFastAdd(unsigned n) const
uint64_t getTotalTicks() const
Definition: DynamicClock.hh:98
unsigned getFreq() const
Returns the frequency (in Hz) at which this clock ticks.
unsigned getTicksTillUp(EmuTime::param e) const
Calculate the number of ticks this clock has to tick to reach or go past the given time.
Definition: DynamicClock.hh:88
EmuTime add(EmuTime::param time, unsigned n) const
void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
void fastAdd(unsigned n)
Advance this clock by the given number of ticks.
void setFreq(unsigned freq)
Change the frequency at which this clock ticks.
EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
Definition: DynamicClock.hh:38
EmuTime::param getNext() const
TODO.
Definition: Scheduler.hh:53
This file implemented 3 utility functions:
Definition: Autofire.cc:9