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