spectrusty_core/clock/
ops.rs

1/*
2    Copyright (C) 2020-2022  Rafal Michalski
3
4    This file is part of SPECTRUSTY, a Rust library for building emulators.
5
6    For the full copyright notice, see the lib.rs file.
7*/
8use core::cmp::{Ord, PartialEq, PartialOrd};
9use core::convert::TryInto;
10use core::fmt::Debug;
11use core::ops::{Add, Sub, AddAssign, SubAssign};
12
13use crate::video::VideoFrame;
14use super::{VFrameTsCounter, VFrameTs, VideoTs, FTs, Ts};
15
16/// A trait providing calculation methods for timestamps.
17///
18/// Allows [BusDevice][crate::bus::BusDevice] implementations to depend on a generic timestamp type.
19pub trait TimestampOps: Copy
20                        + PartialEq
21                        + Eq
22                        + PartialOrd
23                        + Ord
24                        + Debug
25                        + Add<FTs, Output=Self>
26                        + Sub<FTs, Output=Self>
27                        + AddAssign<FTs>
28                        + SubAssign<FTs>
29{
30    /// Returns a normalized timestamp from the given number of T-states.
31    ///
32    /// # Panics
33    /// Panics when the given `ts` overflows the capacity of the timestamp.
34    fn from_tstates(ts: FTs) -> Self;
35    /// Converts the timestamp to FTs.
36    ///
37    /// # Panics
38    /// Panics when `self` overflows the capacity of the result type.
39    fn into_tstates(self) -> FTs;
40    /// Returns the largest value that can be represented by a normalized timestamp.
41    fn max_value() -> Self;
42    /// Returns the smallest value that can be represented by a normalized timestamp.
43    fn min_value() -> Self;
44    /// Returns the difference between `ts_from` and `self` in the number of T-states.
45    ///
46    /// # Panics
47    /// May panic if the result would exceed the capacity of the result type.
48    fn diff_from(self, ts_from: Self) -> FTs;
49    /// Returns a normalized timestamp after adding `other` to it.
50    ///
51    /// Saturates at [TimestampOps::min_value] or [TimestampOps::max_value].
52    fn saturating_add(self, other: Self) -> Self;
53    /// Returns a normalized timestamp after subtracting `other` from it.
54    ///
55    /// Saturates at [TimestampOps::min_value] or [TimestampOps::max_value].
56    fn saturating_sub(self, other: Self) -> Self;
57}
58
59impl TimestampOps for FTs {
60    #[inline(always)]
61    fn from_tstates(ts: FTs) -> Self {
62        ts
63    }
64    #[inline(always)]
65    fn into_tstates(self) -> FTs {
66        self
67    }
68    #[inline(always)]
69    fn max_value() -> Self {
70        FTs::max_value()
71    }
72    #[inline(always)]
73    fn min_value() -> Self {
74        FTs::min_value()
75    }
76    #[inline(always)]
77    fn diff_from(self, ts_from: Self) -> FTs {
78        self - ts_from
79    }
80    #[inline(always)]
81    fn saturating_add(self, other: Self) -> Self {
82        self.saturating_add(other)
83    }
84    #[inline(always)]
85    fn saturating_sub(self, other: Self) -> Self {
86        self.saturating_sub(other)
87    }
88}
89
90impl <V: VideoFrame> TimestampOps for VFrameTs<V> {
91    #[inline]
92    fn from_tstates(ts: FTs) -> Self {
93        VFrameTs::from_tstates(ts)
94    }
95    #[inline]
96    fn into_tstates(self) -> FTs {
97        VFrameTs::into_tstates(self)
98    }
99    #[inline(always)]
100    fn max_value() -> Self {
101        VFrameTs::max_value()
102    }
103    #[inline(always)]
104    fn min_value() -> Self {
105        VFrameTs::min_value()
106    }
107    #[inline]
108    fn diff_from(self, vts_from: Self) -> FTs {
109        (self.vc as FTs - vts_from.vc as FTs) * V::HTS_COUNT as FTs +
110        (self.hc as FTs - vts_from.hc as FTs)
111    }
112    /// # Panics
113    /// May panic if `self` or `other` hasn't been normalized.
114    fn saturating_add(self, other: Self) -> Self {
115        let VideoTs { vc, hc } = self.ts;
116        let vc = vc.saturating_add(other.vc);
117        let hc = hc + other.hc;
118        VFrameTs::new(vc, hc).saturating_normalized()
119    }
120    /// # Panics
121    /// May panic if `self` or `other` hasn't been normalized.
122    fn saturating_sub(self, other: Self) -> Self {
123        let VideoTs { vc, hc } = self.ts;
124        let vc = vc.saturating_sub(other.vc);
125        let hc = hc - other.hc;
126        VFrameTs::new(vc, hc).saturating_normalized()
127    }
128}
129
130impl<V: VideoFrame> Add<FTs> for VFrameTs<V> {
131    type Output = Self;
132    /// Returns a normalized video timestamp after adding `delta` T-states.
133    ///
134    /// # Panics
135    /// Panics when normalized timestamp after addition leads to an overflow of the capacity of [VideoTs].
136    #[inline]
137    #[allow(clippy::suspicious_arithmetic_impl)]
138    fn add(self, delta: FTs) -> Self {
139        let VideoTs { vc, hc } = self.ts;
140        let dvc: Ts = (delta / V::HTS_COUNT as FTs).try_into().expect("absolute delta FTs is too large");
141        let dhc = (delta % V::HTS_COUNT as FTs) as Ts;
142        VFrameTs::new(vc + dvc, hc + dhc).normalized()
143    }
144}
145
146impl<V: VideoFrame> AddAssign<FTs> for VFrameTs<V> {
147    #[inline(always)]
148    fn add_assign(&mut self, delta: FTs) {
149        *self = *self + delta
150    }
151}
152
153impl<V: VideoFrame> Sub<FTs> for VFrameTs<V> {
154    type Output = Self;
155    /// Returns a normalized video timestamp after subtracting `delta` T-states.
156    ///
157    /// # Panics
158    /// Panics when normalized timestamp after addition leads to an overflow of the capacity of [VideoTs].
159    #[inline]
160    #[allow(clippy::suspicious_arithmetic_impl)]
161    fn sub(self, delta: FTs) -> Self {
162        let VideoTs { vc, hc } = self.ts;
163        let dvc: Ts = (delta / V::HTS_COUNT as FTs).try_into().expect("delta too large");
164        let dhc = (delta % V::HTS_COUNT as FTs) as Ts;
165        VFrameTs::new(vc - dvc, hc - dhc).normalized()
166    }
167}
168
169impl<V: VideoFrame> SubAssign<FTs> for VFrameTs<V> {
170    #[inline(always)]
171    fn sub_assign(&mut self, delta: FTs) {
172        *self = *self - delta
173    }
174}
175
176impl<V: VideoFrame> Add<u32> for VFrameTs<V> {
177    type Output = Self;
178    /// Returns a normalized video timestamp after adding a `delta` T-state count.
179    ///
180    /// # Panics
181    /// Panics when normalized timestamp after addition leads to an overflow of the capacity of [VideoTs].
182    #[inline]
183    #[allow(clippy::suspicious_arithmetic_impl)]
184    fn add(self, delta: u32) -> Self {
185        let VideoTs { vc, hc } = self.ts;
186        let dvc = (delta / V::HTS_COUNT as u32).try_into().expect("delta too large");
187        let dhc = (delta % V::HTS_COUNT as u32) as Ts;
188        let vc = vc.checked_add(dvc).expect("delta too large");
189        VFrameTs::new(vc, hc + dhc).normalized()
190    }
191}
192
193impl<V: VideoFrame> AddAssign<u32> for VFrameTs<V> {
194    #[inline(always)]
195    fn add_assign(&mut self, delta: u32) {
196        *self = *self + delta
197    }
198}
199
200impl<V: VideoFrame> Sub<u32> for VFrameTs<V> {
201    type Output = Self;
202    /// Returns a normalized video timestamp after adding a `delta` T-state count.
203    ///
204    /// # Panics
205    /// Panics when normalized timestamp after addition leads to an overflow of the capacity of [VideoTs].
206    #[inline]
207    #[allow(clippy::suspicious_arithmetic_impl)]
208    fn sub(self, delta: u32) -> Self {
209        let VideoTs { vc, hc } = self.ts;
210        let dvc = (delta / V::HTS_COUNT as u32).try_into().expect("delta too large");
211        let dhc = (delta % V::HTS_COUNT as u32) as Ts;
212        let vc = vc.checked_sub(dvc).expect("delta too large");
213        VFrameTs::new(vc, hc - dhc).normalized()
214    }
215}
216
217impl<V: VideoFrame> SubAssign<u32> for VFrameTs<V> {
218    #[inline(always)]
219    fn sub_assign(&mut self, delta: u32) {
220        *self = *self - delta
221    }
222}
223
224impl<V: VideoFrame, C> AddAssign<u32> for VFrameTsCounter<V, C> {
225    fn add_assign(&mut self, delta: u32) {
226        self.vts += delta;
227    }
228}
229
230impl<V: VideoFrame, C> SubAssign<u32> for VFrameTsCounter<V, C> {
231    fn sub_assign(&mut self, delta: u32) {
232        self.vts -= delta;
233    }
234}