| 1 |
#ifndef DYNAMICCLOCK_HH |
| 2 |
#define DYNAMICCLOCK_HH |
| 3 |
|
| 4 |
#include "EmuTime.hh" |
| 5 |
|
| 6 |
#include "DivModBySame.hh" |
| 7 |
#include "narrow.hh" |
| 8 |
|
| 9 |
#include <cassert> |
| 10 |
|
| 11 |
namespace openmsx { |
| 12 |
|
| 13 |
|
| 14 |
|
| 15 |
|
| 16 |
|
| 17 |
|
| 18 |
class DynamicClock |
| 19 |
{ |
| 20 |
public: |
| 21 |
|
| 22 |
|
| 23 |
|
| 24 |
|
| 25 |
|
| 26 |
|
| 27 |
explicit DynamicClock(EmuTime time) : lastTick(time) {} |
| 28 |
|
| 29 |
|
| 30 |
|
| 31 |
|
| 32 |
DynamicClock(EmuTime time, unsigned freq) |
| 33 |
: lastTick(time) |
| 34 |
{ |
| 35 |
setFreq(freq); |
| 36 |
} |
| 37 |
|
| 38 |
|
| 39 |
|
| 40 |
[[nodiscard]] EmuTime getTime() const { |
| 41 |
return lastTick; |
| 42 |
} |
| 43 |
|
| 44 |
|
| 45 |
|
| 46 |
|
| 47 |
[[nodiscard]] bool before(EmuTime e) const { |
| 48 |
return lastTick.time < e.time; |
| 49 |
} |
| 50 |
|
| 51 |
|
| 52 |
|
| 53 |
|
| 54 |
[[nodiscard]] unsigned getTicksTill(EmuTime e) const { |
| 55 |
assert(e.time >= lastTick.time); |
| 56 |
return divMod.div(e.time - lastTick.time); |
| 57 |
} |
| 58 |
|
| 59 |
|
| 60 |
|
| 61 |
|
| 62 |
|
| 63 |
|
| 64 |
|
| 65 |
|
| 66 |
struct IntegralFractional { |
| 67 |
unsigned integral; |
| 68 |
float fractional; |
| 69 |
}; |
| 70 |
[[nodiscard]] IntegralFractional getTicksTillAsIntFloat(EmuTime e) const { |
| 71 |
assert(e.time >= lastTick.time); |
| 72 |
auto dur = e.time - lastTick.time; |
| 73 |
auto [q, r] = divMod.divMod(dur); |
| 74 |
auto f = float(r) / float(getStep()); |
| 75 |
assert(0.0f <= f); assert(f < 1.0f); |
| 76 |
return {.integral = q, .fractional = f}; |
| 77 |
} |
| 78 |
|
| 79 |
template<typename FIXED> |
| 80 |
void getTicksTill(EmuTime e, FIXED& result) const { |
| 81 |
assert(e.time >= lastTick.time); |
| 82 |
uint64_t tmp = (e.time - lastTick.time) << FIXED::FRACTION_BITS; |
| 83 |
result = FIXED::create(divMod.div(tmp + (getStep() / 2))); |
| 84 |
} |
| 85 |
|
| 86 |
|
| 87 |
|
| 88 |
|
| 89 |
|
| 90 |
[[nodiscard]] unsigned getTicksTillUp(EmuTime e) const { |
| 91 |
assert(e.time >= lastTick.time); |
| 92 |
return divMod.div(e.time - lastTick.time + (getStep() - 1)); |
| 93 |
} |
| 94 |
|
| 95 |
[[nodiscard]] double getTicksTillDouble(EmuTime e) const { |
| 96 |
assert(e.time >= lastTick.time); |
| 97 |
return double(e.time - lastTick.time) / getStep(); |
| 98 |
} |
| 99 |
|
| 100 |
[[nodiscard]] uint64_t getTotalTicks() const { |
| 101 |
|
| 102 |
|
| 103 |
return lastTick.time / getStep(); |
| 104 |
} |
| 105 |
|
| 106 |
|
| 107 |
|
| 108 |
|
| 109 |
|
| 110 |
|
| 111 |
void setFreq(unsigned freq) { |
| 112 |
unsigned newStep = (MAIN_FREQ32 + (freq / 2)) / freq; |
| 113 |
setPeriod(EmuDuration(uint64_t(newStep))); |
| 114 |
} |
| 115 |
|
| 116 |
|
| 117 |
|
| 118 |
|
| 119 |
|
| 120 |
void setFreq(unsigned freq_num, unsigned freq_denom) { |
| 121 |
static_assert(MAIN_FREQ < (1ULL << 32), "must fit in 32 bit"); |
| 122 |
uint64_t p = MAIN_FREQ * freq_denom + (freq_num / 2); |
| 123 |
uint64_t newStep = p / freq_num; |
| 124 |
setPeriod(EmuDuration(newStep)); |
| 125 |
} |
| 126 |
|
| 127 |
|
| 128 |
|
| 129 |
|
| 130 |
[[nodiscard]] unsigned getFreq() const { |
| 131 |
auto step = getStep(); |
| 132 |
return narrow<unsigned>((MAIN_FREQ + (step / 2)) / step); |
| 133 |
} |
| 134 |
|
| 135 |
|
| 136 |
|
| 137 |
[[nodiscard]] EmuDuration getPeriod() const { |
| 138 |
return EmuDuration(uint64_t(getStep())); |
| 139 |
} |
| 140 |
|
| 141 |
|
| 142 |
void setPeriod(EmuDuration period) { |
| 143 |
assert(period.toUint64() < (1ULL << 32)); |
| 144 |
divMod.setDivisor(uint32_t(period.toUint64())); |
| 145 |
} |
| 146 |
|
| 147 |
|
| 148 |
|
| 149 |
void reset(EmuTime e) { |
| 150 |
lastTick.time = e.time; |
| 151 |
} |
| 152 |
|
| 153 |
|
| 154 |
|
| 155 |
|
| 156 |
|
| 157 |
void advance(EmuTime e) { |
| 158 |
assert(lastTick.time <= e.time); |
| 159 |
lastTick.time = e.time - divMod.mod(e.time - lastTick.time); |
| 160 |
} |
| 161 |
|
| 162 |
|
| 163 |
|
| 164 |
void operator+=(uint64_t n) { |
| 165 |
lastTick.time += n * getStep(); |
| 166 |
} |
| 167 |
|
| 168 |
|
| 169 |
|
| 170 |
|
| 171 |
[[nodiscard]] friend EmuTime operator+(const DynamicClock& c, uint64_t n) { |
| 172 |
return EmuTime::fromUint64(c.lastTick.toUint64() + n * c.getStep()); |
| 173 |
} |
| 174 |
|
| 175 |
|
| 176 |
|
| 177 |
|
| 178 |
|
| 179 |
|
| 180 |
|
| 181 |
void fastAdd(unsigned n) { |
| 182 |
#ifdef DEBUG |
| 183 |
|
| 184 |
assert((uint64_t(n) * getStep()) < (1ULL << 32)); |
| 185 |
#endif |
| 186 |
lastTick.time += n * getStep(); |
| 187 |
} |
| 188 |
[[nodiscard]] EmuTime getFastAdd(unsigned n) const { |
| 189 |
return add(lastTick, n); |
| 190 |
} |
| 191 |
[[nodiscard]] EmuTime add(EmuTime time, unsigned n) const { |
| 192 |
#ifdef DEBUG |
| 193 |
assert((uint64_t(n) * getStep()) < (1ULL << 32)); |
| 194 |
#endif |
| 195 |
return EmuTime(time.time + n * getStep()); |
| 196 |
} |
| 197 |
|
| 198 |
template<typename Archive> |
| 199 |
void serialize(Archive& ar, unsigned version); |
| 200 |
|
| 201 |
private: |
| 202 |
|
| 203 |
|
| 204 |
|
| 205 |
|
| 206 |
|
| 207 |
|
| 208 |
[[nodiscard]] unsigned getStep() const { return divMod.getDivisor(); } |
| 209 |
|
| 210 |
private: |
| 211 |
|
| 212 |
|
| 213 |
EmuTime lastTick; |
| 214 |
|
| 215 |
DivModBySame divMod; |
| 216 |
}; |
| 217 |
SERIALIZE_CLASS_VERSION(DynamicClock, 2); |
| 218 |
|
| 219 |
} |
| 220 |
|
| 221 |
#endif |