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 
8 namespace openmsx {
9 
10 class CPUClock
11 {
12 public:
13  unsigned getFreq() const { return clock.getFreq(); }
14 
15 protected:
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  }
106  void disableLimit() {
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 
119 private:
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:17
EmuTime getFastAdd(unsigned n) const
uint64_t getTotalTicks() const
Definition: DynamicClock.hh:94
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:84
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:54
This file implemented 3 utility functions:
Definition: Autofire.cc:5