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