1 /* 2 * clockticks.hpp 3 * ClockTicks is a class that tracks signed ticks & fractional ticks for 4 * high granularity game clocks that are backwards-compatible with 5 * Build integer timing 6 * 7 * Copyright � 2019, Alex Dawson. All rights reserved. 8 */ 9 10 #ifndef CLOCKTICKS_HPP_ 11 #define CLOCKTICKS_HPP_ 12 13 #include "timer.h" 14 15 //POGO: BUILD/EDuke32 uses right shifts on signed variables expecting arithmetic shifts. 16 // This was already non-portable, and we carry that assumption forth here 17 // (so we might as well check it). 18 EDUKE32_STATIC_ASSERT(-1 >> 1 == -1); 19 20 class ClockTicks 21 { 22 public: ClockTicks()23 ClockTicks() : ClockTicks(0, 0) {}; ClockTicks(int32_t const ticks)24 ClockTicks(int32_t const ticks) : ClockTicks(ticks, 0) {}; ClockTicks(int32_t const ticks,uint32_t const fraction)25 ClockTicks(int32_t const ticks, uint32_t const fraction) { set(ticks, fraction); }; 26 ClockTicks(const ClockTicks&) = default; 27 getFraction() const28 int64_t getFraction() const 29 { 30 return (ticksS32 & FRACTION_MASK) >> 16 | ((ticksS32 < 0) ? VALUE_MASK : 0); 31 } setFraction(uint16_t const fraction)32 int64_t setFraction(uint16_t const fraction) 33 { 34 return ticksS32 = (ticksS32 & WHOLE_MASK) | ((ticksS32 < 0) ? ((int64_t) 0 - fraction) & FRACTION_16_MASK : fraction) << 16; 35 } set(int32_t const ticks,uint16_t const fraction)36 int64_t set(int32_t const ticks, uint16_t const fraction) 37 { 38 ticksS32 = ((uint64_t) ticks) << 32 | ((ticks < 0) ? ((int64_t) 0 - fraction) & FRACTION_16_MASK : fraction) << 16; 39 update(); 40 return ticksS32; 41 } 42 toScale16() const43 int64_t toScale16() const 44 { 45 return ticksS32 >> 16; 46 } setFromScale16(int64_t const ticksScale16)47 ClockTicks& setFromScale16(int64_t const ticksScale16) 48 { 49 ticksS32 = ticksScale16 << 16; 50 update(); 51 return *this; 52 } fromScale16(int64_t const ticksScale16)53 static ClockTicks fromScale16(int64_t const ticksScale16) 54 { 55 ClockTicks ct; 56 ct.setFromScale16(ticksScale16); 57 return ct; 58 } 59 60 // returns 0 if equal, < 0 if a < b, > 0 if a > b compareHighPrecision(ClockTicks const a,ClockTicks const b)61 static int64_t compareHighPrecision(ClockTicks const a, ClockTicks const b) 62 { 63 ClockTicks delta = a - b; 64 return delta.toScale16(); 65 } 66 operator =(const ClockTicks & rhs)67 ClockTicks& operator=(const ClockTicks& rhs) 68 { 69 ticksS32 = rhs.ticksS32; 70 update(); 71 return *this; 72 }; 73 operator +=(const ClockTicks & rhs)74 ClockTicks& operator+=(const ClockTicks& rhs) 75 { 76 ticksS32 += rhs.ticksS32; 77 update(); 78 return *this; 79 }; operator -=(const ClockTicks & rhs)80 ClockTicks& operator-=(const ClockTicks& rhs) 81 { 82 ticksS32 -= rhs.ticksS32; 83 update(); 84 return *this; 85 }; operator *=(const ClockTicks & rhs)86 ClockTicks& operator*=(const ClockTicks& rhs) 87 { 88 ticksS32 = (ticksS32>>16)*(rhs.ticksS32>>16) >> 16; 89 update(); 90 return *this; 91 }; operator /=(const ClockTicks & rhs)92 ClockTicks& operator/=(const ClockTicks& rhs) 93 { 94 ticksS32 = ticksS32/rhs.ticksS32 << 32; 95 update(); 96 return *this; 97 }; operator %=(const ClockTicks & rhs)98 ClockTicks& operator%=(const ClockTicks& rhs) 99 { 100 ticksS32 %= rhs.ticksS32; 101 update(); 102 return *this; 103 }; operator <<=(int32_t const rhs)104 ClockTicks& operator<<=(int32_t const rhs) 105 { 106 ticksS32 = ticksS32 << rhs; 107 update(); 108 return *this; 109 }; operator >>=(int32_t const rhs)110 ClockTicks& operator>>=(int32_t const rhs) 111 { 112 ticksS32 = ticksS32 >> rhs; 113 update(); 114 return *this; 115 }; 116 operator +(ClockTicks lhs,const ClockTicks & rhs)117 friend ClockTicks operator+(ClockTicks lhs, const ClockTicks& rhs) { return lhs += rhs; } operator -(ClockTicks lhs,const ClockTicks & rhs)118 friend ClockTicks operator-(ClockTicks lhs, const ClockTicks& rhs) { return lhs -= rhs; } operator -(ClockTicks val)119 friend ClockTicks operator-(ClockTicks val) { return ClockTicks(0) -= val; } operator *(ClockTicks lhs,const ClockTicks & rhs)120 friend ClockTicks operator*(ClockTicks lhs, const ClockTicks& rhs) { return lhs *= rhs; } operator /(ClockTicks lhs,const ClockTicks & rhs)121 friend ClockTicks operator/(ClockTicks lhs, const ClockTicks& rhs) { return lhs /= rhs; } operator %(ClockTicks lhs,const ClockTicks & rhs)122 friend ClockTicks operator%(ClockTicks lhs, const ClockTicks& rhs) { return lhs %= rhs; } operator <<(ClockTicks lhs,int32_t rhs)123 friend ClockTicks operator<<(ClockTicks lhs, int32_t rhs) { return lhs >>= rhs; } operator >>(ClockTicks lhs,int32_t rhs)124 friend ClockTicks operator>>(ClockTicks lhs, int32_t rhs) { return lhs <<= rhs; } 125 operator ==(const ClockTicks & lhs,const ClockTicks & rhs)126 friend inline bool operator==(const ClockTicks& lhs, const ClockTicks& rhs) { return lhs.wholeTicks == rhs.wholeTicks; } operator !=(const ClockTicks & lhs,const ClockTicks & rhs)127 friend inline bool operator!=(const ClockTicks& lhs, const ClockTicks& rhs) { return !(lhs == rhs); } operator <(const ClockTicks & lhs,const ClockTicks & rhs)128 friend inline bool operator<(const ClockTicks& lhs, const ClockTicks& rhs) { return lhs.wholeTicks < rhs.wholeTicks; } operator >(const ClockTicks & lhs,const ClockTicks & rhs)129 friend inline bool operator>(const ClockTicks& lhs, const ClockTicks& rhs) { return rhs < lhs; } operator <=(const ClockTicks & lhs,const ClockTicks & rhs)130 friend inline bool operator<=(const ClockTicks& lhs, const ClockTicks& rhs) { return !(lhs > rhs); } operator >=(const ClockTicks & lhs,const ClockTicks & rhs)131 friend inline bool operator>=(const ClockTicks& lhs, const ClockTicks& rhs) { return !(lhs < rhs); } 132 operator uint32_t() const133 explicit operator uint32_t() const { return wholeTicks; }; operator int32_t() const134 explicit operator int32_t() const { return wholeTicks; }; 135 136 private: 137 //POGO: wholeTicks must be first in member-order to ensure the address of 138 // ClockTicks can be treated as a pointer to int32_t. 139 int32_t wholeTicks; 140 //POGO: Organize our bits as if we're scaled to have an additional 32-bits 141 // of fractional precision so that we can handle overflows in a 142 // way that is accurate to the original BUILD int32_t expectation. 143 // Multiplication overflows best with 16-bits of fractional precision, 144 // so only promise that much publicly. 145 int64_t ticksS32; 146 147 static constexpr uint64_t const VALUE_MASK = 0xFFFFFFFFFFFF0000ull; 148 static constexpr uint64_t const WHOLE_MASK = 0xFFFFFFFF00000000ull; 149 static constexpr uint64_t const FRACTION_MASK = 0x00000000FFFF0000ull; 150 static constexpr uint64_t const FRACTION_16_MASK = 0x000000000000FFFFull; 151 update()152 inline void update() 153 { 154 wholeTicks = ticksS32 >> 32; 155 ticksS32 &= VALUE_MASK; 156 } 157 }; 158 159 #endif /* CLOCKTICKS_HPP_ */ 160