DynamicClock.hh 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. #ifndef DYNAMICCLOCK_HH
  2. #define DYNAMICCLOCK_HH
  3. #include "EmuTime.hh"
  4. #include "DivModBySame.hh"
  5. #include <cassert>
  6. #include <utility>
  7. namespace openmsx {
  8. /** Represents a clock with a variable frequency.
  9. * The frequency is in Hertz, so every tick is 1/frequency second.
  10. * A clock has a current time, which can be increased by
  11. * an integer number of ticks.
  12. */
  13. class DynamicClock
  14. {
  15. public:
  16. // Note: default copy constructor and assigment operator are ok.
  17. /** Create a new clock, which starts ticking at given time.
  18. * The initial frequency is infinite;
  19. * in other words, the clock stands still.
  20. */
  21. explicit DynamicClock(EmuTime::param time) : lastTick(time) {}
  22. /** Create a new clock, which starts ticking at given time with
  23. * given frequency.
  24. */
  25. DynamicClock(EmuTime::param time, unsigned freq)
  26. : lastTick(time)
  27. {
  28. setFreq(freq);
  29. }
  30. /** Gets the time at which the last clock tick occurred.
  31. */
  32. EmuTime::param getTime() const {
  33. return lastTick;
  34. }
  35. /** Checks whether this clock's last tick is or is not before the
  36. * given time stamp.
  37. */
  38. bool before(EmuTime::param e) const {
  39. return lastTick.time < e.time;
  40. }
  41. /** Calculate the number of ticks for this clock until the given time.
  42. * It is not allowed to call this method for a time in the past.
  43. */
  44. unsigned getTicksTill(EmuTime::param e) const {
  45. assert(e.time >= lastTick.time);
  46. return divmod.div(e.time - lastTick.time);
  47. }
  48. /** Like getTicksTill(), but also returns the fractional part (in range [0, 1)).
  49. * This is equivalent to, but numerically more stable than:
  50. * EmuDuration dur = e - this->getTime();
  51. * double d = dur / this->getPeriod();
  52. * int i = int(d);
  53. * double frac = d - i;
  54. * return {i, frac};
  55. */
  56. std::pair<unsigned, float> getTicksTillAsIntFloat(EmuTime::param e) const {
  57. assert(e.time >= lastTick.time);
  58. auto dur = e.time - lastTick.time;
  59. auto [q, r] = divmod.divMod(dur);
  60. auto f = float(r) / float(getStep());
  61. assert(0.0f <= f); assert(f < 1.0f);
  62. return {q, f};
  63. }
  64. template<typename FIXED>
  65. void getTicksTill(EmuTime::param e, FIXED& result) const {
  66. assert(e.time >= lastTick.time);
  67. uint64_t tmp = (e.time - lastTick.time) << FIXED::FRACTION_BITS;
  68. result = FIXED::create(divmod.div(tmp + (getStep() / 2)));
  69. }
  70. /** Calculate the number of ticks this clock has to tick to reach
  71. * or go past the given time.
  72. * It is not allowed to call this method for a time in the past.
  73. */
  74. unsigned getTicksTillUp(EmuTime::param e) const {
  75. assert(e.time >= lastTick.time);
  76. return divmod.div(e.time - lastTick.time + (getStep() - 1));
  77. }
  78. double getTicksTillDouble(EmuTime::param e) const {
  79. assert(e.time >= lastTick.time);
  80. return double(e.time - lastTick.time) / getStep();
  81. }
  82. uint64_t getTotalTicks() const {
  83. // note: don't use divmod.div() because that one only returns a
  84. // 32 bit result. Maybe improve in the future.
  85. return lastTick.time / getStep();
  86. }
  87. /** Change the frequency at which this clock ticks.
  88. * When possible prefer setPeriod() over this method because it may
  89. * introduce less rounding errors.
  90. * @param freq New frequency in Hertz.
  91. */
  92. void setFreq(unsigned freq) {
  93. unsigned newStep = (MAIN_FREQ32 + (freq / 2)) / freq;
  94. setPeriod(EmuDuration(uint64_t(newStep)));
  95. }
  96. /** Equivalent to setFreq(freq_num / freq_denom), but possibly with
  97. * less rounding errors.
  98. * When possible prefer setPeriod() over this method because it may
  99. * introduce less rounding errors.
  100. */
  101. void setFreq(unsigned freq_num, unsigned freq_denom) {
  102. static_assert(MAIN_FREQ < (1ull << 32), "must fit in 32 bit");
  103. uint64_t p = MAIN_FREQ * freq_denom + (freq_num / 2);
  104. uint64_t newStep = p / freq_num;
  105. setPeriod(EmuDuration(newStep));
  106. }
  107. /** Returns the frequency (in Hz) at which this clock ticks.
  108. * @see setFreq()
  109. */
  110. unsigned getFreq() const {
  111. auto step = getStep();
  112. return (MAIN_FREQ + (step / 2)) / step;
  113. }
  114. /** Returns the length of one clock-cycle.
  115. */
  116. EmuDuration getPeriod() const {
  117. return EmuDuration(uint64_t(getStep()));
  118. }
  119. /** Set the duration of a clock tick. See also setFreq(). */
  120. void setPeriod(EmuDuration period) {
  121. assert(period.length() < (1ull << 32));
  122. divmod.setDivisor(uint32_t(period.length()));
  123. }
  124. /** Reset the clock to start ticking at the given time.
  125. */
  126. void reset(EmuTime::param e) {
  127. lastTick.time = e.time;
  128. }
  129. /** Advance this clock in time until the last tick which is not past
  130. * the given time.
  131. * It is not allowed to advance a clock to a time in the past.
  132. */
  133. void advance(EmuTime::param e) {
  134. assert(lastTick.time <= e.time);
  135. lastTick.time = e.time - divmod.mod(e.time - lastTick.time);
  136. }
  137. /** Advance this clock by the given number of ticks.
  138. */
  139. void operator+=(uint64_t n) {
  140. lastTick.time += n * getStep();
  141. }
  142. /** Calculate the time at which this clock will have ticked the given
  143. * number of times (counted from its last tick).
  144. */
  145. EmuTime operator+(uint64_t n) const {
  146. return EmuTime(lastTick.time + n * getStep());
  147. }
  148. /** Advance this clock by the given number of ticks.
  149. * This method is similar to operator+=, but it's optimized for
  150. * speed. OTOH the amount of ticks should not be too large,
  151. * otherwise an overflow occurs. Use operator+() when the duration
  152. * of the ticks approaches 1 second.
  153. */
  154. void fastAdd(unsigned n) {
  155. #ifdef DEBUG
  156. // we don't even want this overhead in development versions
  157. assert((uint64_t(n) * getStep()) < (1ull << 32));
  158. #endif
  159. lastTick.time += n * getStep();
  160. }
  161. EmuTime getFastAdd(unsigned n) const {
  162. return add(lastTick, n);
  163. }
  164. EmuTime add(EmuTime::param time, unsigned n) const {
  165. #ifdef DEBUG
  166. assert((uint64_t(n) * getStep()) < (1ull << 32));
  167. #endif
  168. return EmuTime(time.time + n * getStep());
  169. }
  170. template<typename Archive>
  171. void serialize(Archive& ar, unsigned version);
  172. private:
  173. /** Length of a this clock's ticks, expressed in master clock ticks.
  174. * changed uint64_t -> unsigned for performance reasons
  175. * this is _heavily_ used in the CPU code
  176. * We used to store this as a member, but DivModBySame also stores
  177. * it, so we can as well get it from there (getter is inlined).
  178. */
  179. unsigned getStep() const { return divmod.getDivisor(); }
  180. /** Time of this clock's last tick.
  181. */
  182. EmuTime lastTick;
  183. DivModBySame divmod;
  184. };
  185. SERIALIZE_CLASS_VERSION(DynamicClock, 2);
  186. } // namespace openmsx
  187. #endif