game_toolkit_dosbus/
clock.rs1use core::ops::{Add, AddAssign, Sub, SubAssign};
2use core::time::Duration;
3
4#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
12#[repr(transparent)]
13pub struct Tick(pub u64);
14
15impl Tick {
16 pub const ZERO: Self = Self(0);
18 pub const MAX: Self = Self(u64::MAX);
20 pub const HZ_NUMERATOR: u64 = 315_000_000;
22 pub const HZ_DENOMINATOR: u64 = 22;
24
25 pub const fn from_micros(micros: u64) -> Self {
30 Self::from_duration(Duration::from_micros(micros))
31 }
32
33 pub const fn from_nanos(nanos: u64) -> Self {
35 Self::from_duration(Duration::from_nanos(nanos))
36 }
37
38 pub const fn from_duration(duration: Duration) -> Self {
43 match Self::checked_from_duration(duration) {
44 Some(tick) => tick,
45 None => panic!("duration exceeds Tick::MAX"),
46 }
47 }
48
49 pub const fn checked_from_duration(duration: Duration) -> Option<Self> {
51 let ticks = duration.as_nanos() * Self::HZ_NUMERATOR as u128
53 / (Self::HZ_DENOMINATOR as u128 * 1_000_000_000);
54 if ticks > u64::MAX as u128 {
55 None
56 } else {
57 Some(Self(ticks as u64))
58 }
59 }
60
61 pub fn from_secs_f64(seconds: f64) -> Self {
69 assert!(seconds.is_finite() && seconds >= 0.0, "invalid seconds");
70 let ticks = seconds * Self::HZ_NUMERATOR as f64 / Self::HZ_DENOMINATOR as f64 + 0.5;
72 assert!(ticks < u64::MAX as f64, "seconds exceed Tick::MAX");
74 Self(ticks as u64)
75 }
76
77 pub const fn as_duration(self) -> Duration {
79 let nanos = self.0 as u128 * Self::HZ_DENOMINATOR as u128 * 1_000_000_000
80 / Self::HZ_NUMERATOR as u128;
81 Duration::new(
82 (nanos / 1_000_000_000) as u64,
83 (nanos % 1_000_000_000) as u32,
84 )
85 }
86
87 pub fn as_secs_f64(self) -> f64 {
89 self.0 as f64 * Self::HZ_DENOMINATOR as f64 / Self::HZ_NUMERATOR as f64
90 }
91
92 pub fn as_nanos_f64(self) -> f64 {
94 self.0 as f64 * (Self::HZ_DENOMINATOR * 1_000_000_000) as f64 / Self::HZ_NUMERATOR as f64
95 }
96
97 pub const fn checked_add(self, rhs: Self) -> Option<Self> {
99 match self.0.checked_add(rhs.0) {
100 Some(value) => Some(Self(value)),
101 None => None,
102 }
103 }
104
105 pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
107 match self.0.checked_sub(rhs.0) {
108 Some(value) => Some(Self(value)),
109 None => None,
110 }
111 }
112}
113
114impl Add for Tick {
115 type Output = Self;
116
117 fn add(self, rhs: Self) -> Self {
118 self.checked_add(rhs).expect("tick addition overflow")
119 }
120}
121
122impl AddAssign for Tick {
123 fn add_assign(&mut self, rhs: Self) {
124 *self = *self + rhs;
125 }
126}
127
128impl Sub for Tick {
129 type Output = Self;
130
131 fn sub(self, rhs: Self) -> Self {
132 self.checked_sub(rhs).expect("tick subtraction below zero")
133 }
134}
135
136impl SubAssign for Tick {
137 fn sub_assign(&mut self, rhs: Self) {
138 *self = *self - rhs;
139 }
140}
141
142#[derive(Clone, Debug, Eq, PartialEq)]
150pub struct TickAccumulator {
151 source_hz: u64,
152 remainder: u128,
153}
154
155impl TickAccumulator {
156 pub const fn new(source_hz: u64) -> Self {
161 assert!(source_hz > 0, "source frequency must be nonzero");
162 Self {
163 source_hz,
164 remainder: 0,
165 }
166 }
167
168 pub const fn source_hz(&self) -> u64 {
170 self.source_hz
171 }
172
173 pub fn advance(&mut self, cycles: u64) -> Tick {
179 self.checked_advance(cycles)
180 .expect("cycle charge exceeds Tick::MAX")
181 }
182
183 pub fn checked_advance(&mut self, cycles: u64) -> Option<Tick> {
185 let denominator = self.source_hz as u128 * Tick::HZ_DENOMINATOR as u128;
186 let numerator = cycles as u128 * Tick::HZ_NUMERATOR as u128 + self.remainder;
187 let ticks = u64::try_from(numerator / denominator).ok()?;
188 self.remainder = numerator % denominator;
189 Some(Tick(ticks))
190 }
191}