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 serialize(Archive &ar, unsigned version)
Definition CPUClock.cc:20
void advanceTime(EmuTime::param time)
Definition CPUClock.cc:12
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.
EmuTime getFastAdd(unsigned n) const
uint64_t getTotalTicks() const
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.
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.
EmuTime::param getNext() const
TODO.
Definition Scheduler.hh:53
This file implemented 3 utility functions:
Definition Autofire.cc:9