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  EmuTime::param getTime() const { sync(); return clock.getTime(); }
36  EmuTime getTimeFast() const { return clock.getFastAdd(limit - remaining); }
37  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  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  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
CPUClock(EmuTime::param time, Scheduler &scheduler)
Definition: CPUClock.cc:6
void setFreq(unsigned freq)
Definition: CPUClock.hh:41
uint64_t getTotalTicks() const
Definition: DynamicClock.hh:77
void advanceTime(EmuTime::param time)
Definition: CPUClock.cc:13
void waitForEvenCycle(int cc)
R800 runs at 7MHz, but I/O is done over a slower 3.5MHz bus.
Definition: CPUClock.hh:64
void add(unsigned ticks)
Definition: CPUClock.hh:27
bool limitReached() const
Definition: CPUClock.hh:112
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:67
void disableLimit()
Definition: CPUClock.hh:106
EmuTime::param getTime() const
Gets the time at which the last clock tick occurred.
Definition: DynamicClock.hh:37
unsigned advanceHalt(unsigned hltStates, EmuTime::param time)
Implementation of the HALT instruction timing.
Definition: CPUClock.hh:52
EmuTime getTimeFast() const
Definition: CPUClock.hh:36
EmuTime::param getTime() const
Definition: CPUClock.hh:35
Represents a clock with a variable frequency.
Definition: DynamicClock.hh:15
EmuTime::param getNext() const
TODO.
Definition: Scheduler.hh:54
unsigned getFreq() const
Returns the frequency (in Hz) at which this clock ticks.
void setFreq(unsigned freq)
Change the frequency at which this clock ticks.
Definition: DynamicClock.hh:86
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
EmuTime getFastAdd(unsigned n) const
void setLimit(EmuTime::param time)
Definition: CPUClock.hh:92
void enableLimit()
Definition: CPUClock.hh:102
void setTime(EmuTime::param time)
Definition: CPUClock.hh:40
void sync() const
Definition: CPUClock.hh:28
void serialize(Archive &ar, unsigned version)
Definition: CPUClock.cc:21
EmuTime add(EmuTime::param time, unsigned n) const
EmuTime getTimeFast(int cc) const
Definition: CPUClock.hh:37
void fastAdd(unsigned n)
Advance this clock by the given number of ticks.
void reset(EmuTime::param e)
Reset the clock to start ticking at the given time.
unsigned getFreq() const
Definition: CPUClock.hh:13
EmuTime calcTime(EmuTime::param time, unsigned ticks) const
Definition: CPUClock.hh:43