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