spectrusty_core/clock.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*/
8//! T-state timestamp types and counters.
9use core::cmp::{Ordering, Ord, PartialEq, PartialOrd};
10use core::convert::{TryInto, TryFrom};
11use core::fmt::Debug;
12use core::hash::{Hash, Hasher};
13use core::marker::PhantomData;
14use core::num::{NonZeroU8, NonZeroU16};
15use core::ops::{Deref, DerefMut};
16
17use z80emu::{Clock, host::cycles::*};
18#[cfg(feature = "snapshot")]
19use serde::{Serialize, Deserialize};
20
21use crate::video::VideoFrame;
22
23mod packed;
24mod ops;
25pub use packed::*;
26pub use ops::*;
27
28/// A linear T-state timestamp type.
29pub type FTs = i32;
30/// A type used for a horizontal T-state timestamp or a video scanline index for [VideoTs].
31pub type Ts = i16;
32
33/// A timestamp type that consists of two video counters: vertical and horizontal.
34///
35/// `VideoTs { vc: 0, hc: 0 }` marks the start of the video frame.
36#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
37#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
38pub struct VideoTs {
39 /// A vertical counter - a video scan-line index.
40 pub vc: Ts,
41 /// A horizontal counter - measured in T-states.
42 pub hc: Ts,
43}
44
45/// A [VideoTs] timestamp wrapper with a constraint to the `V:` [VideoFrame],
46/// implementing methods and traits for timestamp calculations.
47#[cfg_attr(feature = "snapshot", derive(Serialize, Deserialize))]
48#[cfg_attr(feature = "snapshot", serde(try_from="FTs", into="FTs"))]
49#[cfg_attr(feature = "snapshot", serde(bound = "V: VideoFrame"))]
50#[derive(Copy, Debug)]
51pub struct VFrameTs<V> {
52 /// The current value of the timestamp.
53 pub ts: VideoTs,
54 _vframe: PhantomData<V>,
55}
56
57/// A trait used by [VFrameTsCounter] for checking if an `address` is a contended one.
58pub trait MemoryContention: Copy + Debug {
59 fn is_contended_address(self, address: u16) -> bool;
60}
61
62/// A generic [`VFrameTs<V>`][VFrameTs] based T-states counter.
63///
64/// Implements [Clock] for counting cycles when code is being executed by [z80emu::Cpu].
65///
66/// Inserts additional T-states according to the contention model specified by generic
67/// parameters: `V:` [VideoFrame] and `C:` [MemoryContention].
68///
69/// [Clock]: /z80emu/%2A/z80emu/host/trait.Clock.html
70/// [z80emu::Cpu]: /z80emu/%2A/z80emu/trait.Cpu.html
71#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
72pub struct VFrameTsCounter<V, C> {
73 /// The current value of the counter.
74 pub vts: VFrameTs<V>,
75 /// An instance implementing a [MemoryContention] trait.
76 pub contention: C,
77}
78
79/// If a vertical counter of [VideoTs] exceeds this value, it signals the control unit
80/// to emulate hanging CPU indefinitely.
81pub const HALT_VC_THRESHOLD: i16 = i16::max_value() >> 1;
82
83const WAIT_STATES_THRESHOLD: u16 = i16::max_value() as u16 - 256;
84
85impl VideoTs {
86 #[inline]
87 pub const fn new(vc: Ts, hc: Ts) -> Self {
88 VideoTs { vc, hc }
89 }
90}
91
92impl <V: VideoFrame> VFrameTs<V> {
93 /// The end-of-frame timestamp, equal to the total number of T-states per frame.
94 pub const EOF: VFrameTs<V> = VFrameTs { ts: VideoTs {
95 vc: V::VSL_COUNT,
96 hc: 0
97 },
98 _vframe: PhantomData };
99 /// Constructs a new `VFrameTs` from the given vertical and horizontal counter values.
100 ///
101 /// __Note__: The returned `VFrameTs` is not normalized.
102 #[inline]
103 pub fn new(vc: Ts, hc: Ts) -> Self {
104 VFrameTs { ts: VideoTs::new(vc, hc), _vframe: PhantomData }
105 }
106 /// Returns `true` if a video timestamp is normalized. Otherwise returns `false`.
107 #[inline]
108 pub fn is_normalized(self) -> bool {
109 V::HTS_RANGE.contains(&self.ts.hc)
110 }
111 /// Normalizes self with a horizontal counter within the allowed range and a scan line
112 /// counter adjusted accordingly.
113 ///
114 /// # Panics
115 /// Panics when an attempt to normalize leads to an overflow of the capacity of [VideoTs].
116 #[inline]
117 pub fn normalized(self) -> Self {
118 let VideoTs { mut vc, mut hc } = self.ts;
119 if hc < V::HTS_RANGE.start || hc >= V::HTS_RANGE.end {
120 let fhc: FTs = hc as FTs - if hc < 0 {
121 V::HTS_RANGE.end
122 }
123 else {
124 V::HTS_RANGE.start
125 } as FTs;
126 vc = vc.checked_add((fhc / V::HTS_COUNT as FTs) as Ts)
127 .expect("video timestamp overflow");
128 hc = fhc.rem_euclid(V::HTS_COUNT as FTs) as Ts + V::HTS_RANGE.start;
129 }
130 VFrameTs::new(vc, hc)
131 }
132 /// Returns a video timestamp with a horizontal counter within the allowed range and a scan line
133 /// counter adjusted accordingly. Saturates at [VFrameTs::min_value] or [VFrameTs::max_value].
134 #[inline]
135 pub fn saturating_normalized(self) -> Self {
136 let VideoTs { mut vc, mut hc } = self.ts;
137 if hc < V::HTS_RANGE.start || hc >= V::HTS_RANGE.end {
138 let fhc: FTs = hc as FTs - if hc < 0 {
139 V::HTS_RANGE.end
140 }
141 else {
142 V::HTS_RANGE.start
143 } as FTs;
144 let dvc = (fhc / V::HTS_COUNT as FTs) as Ts;
145 if let Some(vc1) = vc.checked_add(dvc) {
146 vc = vc1;
147 hc = fhc.rem_euclid(V::HTS_COUNT as FTs) as Ts + V::HTS_RANGE.start;
148 }
149 else {
150 return if dvc < 0 { Self::min_value() } else { Self::max_value() };
151 }
152 }
153 VFrameTs::new(vc, hc)
154 }
155 /// Returns the largest value that can be represented by a normalized timestamp.
156 #[inline(always)]
157 pub fn max_value() -> Self {
158 VFrameTs { ts: VideoTs { vc: Ts::max_value(), hc: V::HTS_RANGE.end - 1 },
159 _vframe: PhantomData }
160 }
161 /// Returns the smallest value that can be represented by a normalized timestamp.
162 #[inline(always)]
163 pub fn min_value() -> Self {
164 VFrameTs { ts: VideoTs { vc: Ts::min_value(), hc: V::HTS_RANGE.start },
165 _vframe: PhantomData }
166 }
167 /// Returns `true` if the counter value is past or near the end of a frame. Otherwise returns `false`.
168 ///
169 /// Specifically, the condition is met if the vertical counter is equal to or greater than [VideoFrame::VSL_COUNT].
170 #[inline(always)]
171 pub fn is_eof(self) -> bool {
172 self.vc >= V::VSL_COUNT
173 }
174 /// Ensures the vertical counter is in the range: `(-VSL_COUNT, VSL_COUNT)` by calculating
175 /// a remainder of the division of the vertical counter by [VideoFrame::VSL_COUNT].
176 #[inline(always)]
177 pub fn wrap_frame(&mut self) {
178 self.ts.vc %= V::VSL_COUNT
179 }
180 /// Returns a video timestamp after subtracting the total number of frame video scanlines
181 /// from the scan line counter.
182 #[inline]
183 pub fn saturating_sub_frame(self) -> Self {
184 let VideoTs { vc, hc } = self.ts;
185 let vc = vc.saturating_sub(V::VSL_COUNT);
186 VFrameTs::new(vc, hc)
187 }
188 /// Returns a normalized timestamp from the given number of T-states.
189 ///
190 /// # Panics
191 /// Panics when the given `ts` overflows the capacity of the timestamp.
192 #[inline]
193 pub fn from_tstates(ts: FTs) -> Self {
194 Self::try_from_tstates(ts).expect("video timestamp overflow")
195 }
196 /// On success returns a normalized timestamp from the given number of T-states.
197 ///
198 /// Returns `None` when the given `ts` overflows the capacity of the timestamp.
199 #[inline]
200 pub fn try_from_tstates(ts: FTs) -> Option<Self> {
201 let mut vc = match (ts / V::HTS_COUNT as FTs).try_into() {
202 Ok(vc) => vc,
203 Err(..) => return None
204 };
205 let mut hc: Ts = (ts % V::HTS_COUNT as FTs) as Ts;
206 if hc >= V::HTS_RANGE.end {
207 hc -= V::HTS_COUNT;
208 vc += 1;
209 }
210 else if hc < V::HTS_RANGE.start {
211 hc += V::HTS_COUNT;
212 vc -= 1;
213 }
214 Some(VFrameTs::new(vc, hc))
215 }
216 /// Converts the timestamp to FTs.
217 #[inline]
218 pub fn into_tstates(self) -> FTs {
219 let VideoTs { vc, hc } = self.ts;
220 V::vc_hc_to_tstates(vc, hc)
221 }
222 /// Returns a tuple with an adjusted frame counter and with the frame-normalized timestamp as
223 /// the number of T-states measured from the start of the frame.
224 ///
225 /// The frame starts when the horizontal and vertical counter are both 0.
226 ///
227 /// The returned timestamp value is in the range [0, [VideoFrame::FRAME_TSTATES_COUNT]).
228 #[inline]
229 pub fn into_frame_tstates(self, frames: u64) -> (u64, FTs) {
230 let ts = TimestampOps::into_tstates(self);
231 let frmdlt = ts / V::FRAME_TSTATES_COUNT;
232 let ufrmdlt = if ts < 0 { frmdlt - 1 } else { frmdlt } as u64;
233 let frames = frames.wrapping_add(ufrmdlt);
234 let ts = ts.rem_euclid(V::FRAME_TSTATES_COUNT);
235 (frames, ts)
236 }
237
238 #[inline]
239 fn set_hc_after_small_increment(&mut self, mut hc: Ts) {
240 if hc >= V::HTS_RANGE.end {
241 hc -= V::HTS_COUNT as Ts;
242 self.ts.vc += 1;
243 }
244 self.ts.hc = hc;
245 }
246}
247
248impl<V, C> VFrameTsCounter<V, C>
249 where V: VideoFrame,
250 C: MemoryContention
251{
252 /// Constructs a new and normalized `VFrameTsCounter` from the given vertical and horizontal counter values.
253 ///
254 /// # Panics
255 /// Panics when the given values lead to an overflow of the capacity of [VideoTs].
256 #[inline]
257 pub fn new(vc: Ts, hc: Ts, contention: C) -> Self {
258 let vts = VFrameTs::new(vc, hc).normalized();
259 VFrameTsCounter { vts, contention }
260 }
261 /// Builds a normalized [VFrameTsCounter] from the given count of T-states.
262 ///
263 /// # Panics
264 ///
265 /// Panics when the given `ts` overflows the capacity of [VideoTs].
266 #[inline]
267 pub fn from_tstates(ts: FTs, contention: C) -> Self {
268 let vts = TimestampOps::from_tstates(ts);
269 VFrameTsCounter { vts, contention }
270 }
271 /// Builds a normalized [VFrameTsCounter] from the given count of T-states.
272 ///
273 /// # Panics
274 ///
275 /// Panics when the given `ts` overflows the capacity of [VideoTs].
276 #[inline]
277 pub fn from_video_ts(vts: VideoTs, contention: C) -> Self {
278 let vts = VFrameTs::from(vts).normalized();
279 VFrameTsCounter { vts, contention }
280 }
281 /// Builds a normalized [VFrameTsCounter] from the given count of T-states.
282 ///
283 /// # Panics
284 ///
285 /// Panics when the given `ts` overflows the capacity of [VideoTs].
286 #[inline]
287 pub fn from_vframe_ts(vfts: VFrameTs<V>, contention: C) -> Self {
288 let vts = vfts.normalized();
289 VFrameTsCounter { vts, contention }
290 }
291
292 #[inline]
293 pub fn is_contended_address(self, address: u16) -> bool {
294 self.contention.is_contended_address(address)
295 }
296}
297
298/// This macro is used to implement the ULA I/O contention scheme, for [z80emu::Clock::add_io] method of
299/// [VFrameTsCounter].
300/// It's being exported for the purpose of performing FUSE tests.
301///
302/// * $mc should be a type implementing [MemoryContention] trait.
303/// * $port is a port address.
304/// * $hc is an identifier of a mutable variable containing the `hc` property of a `VideoTs` timestamp.
305/// * $contention should be a path to the [VideoFrame::contention] function.
306///
307/// The macro returns a horizontal timestamp pointing after the whole I/O cycle is over.
308/// The `hc` variable is modified to contain a horizontal timestamp indicating when the data R/W operation
309/// takes place.
310#[macro_export]
311macro_rules! ula_io_contention {
312 ($mc:expr, $port:expr, $hc:ident, $contention:path) => {
313 {
314 use $crate::z80emu::host::cycles::*;
315 if $mc.is_contended_address($port) {
316 $hc = $contention($hc) + IO_IORQ_LOW_TS as Ts;
317 if $port & 1 == 0 { // C:1, C:3
318 $contention($hc) + (IO_CYCLE_TS - IO_IORQ_LOW_TS) as Ts
319 }
320 else { // C:1, C:1, C:1, C:1
321 let mut hc1 = $hc;
322 for _ in 0..(IO_CYCLE_TS - IO_IORQ_LOW_TS) {
323 hc1 = $contention(hc1) + 1;
324 }
325 hc1
326 }
327 }
328 else {
329 $hc += IO_IORQ_LOW_TS as Ts;
330 if $port & 1 == 0 { // N:1 C:3
331 $contention($hc) + (IO_CYCLE_TS - IO_IORQ_LOW_TS) as Ts
332 }
333 else { // N:4
334 $hc + (IO_CYCLE_TS - IO_IORQ_LOW_TS) as Ts
335 }
336 }
337 }
338 };
339}
340/*
341impl<V: VideoFrame> Clock for VFrameTs<V> {
342 type Limit = Ts;
343 type Timestamp = VideoTs;
344
345 #[inline(always)]
346 fn is_past_limit(&self, limit: Self::Limit) -> bool {
347 self.vc >= limit
348 }
349
350 fn add_irq(&mut self, _pc: u16) -> Self::Timestamp {
351 self.set_hc_after_small_increment(self.hc + IRQ_ACK_CYCLE_TS as Ts);
352 self.as_timestamp()
353 }
354
355 fn add_no_mreq(&mut self, _address: u16, add_ts: NonZeroU8) {
356 let hc = self.hc + add_ts.get() as Ts;
357 self.set_hc_after_small_increment(hc);
358 }
359
360 fn add_m1(&mut self, _address: u16) -> Self::Timestamp {
361 self.set_hc_after_small_increment(self.hc + M1_CYCLE_TS as Ts);
362 self.as_timestamp()
363 }
364
365 fn add_mreq(&mut self, _address: u16) -> Self::Timestamp {
366 self.set_hc_after_small_increment(self.hc + MEMRW_CYCLE_TS as Ts);
367 self.as_timestamp()
368 }
369
370 fn add_io(&mut self, _port: u16) -> Self::Timestamp {
371 let hc = self.hc + IO_IORQ_LOW_TS as Ts;
372 let hc1 = hc + (IO_CYCLE_TS - IO_IORQ_LOW_TS) as Ts;
373
374 let mut tsc = *self;
375 tsc.set_hc_after_small_increment(hc);
376 self.set_hc_after_small_increment(hc1);
377 tsc.as_timestamp()
378 }
379
380 fn add_wait_states(&mut self, _bus: u16, wait_states: NonZeroU16) {
381 let ws = wait_states.get();
382 if ws > WAIT_STATES_THRESHOLD {
383 // emulate hanging the Spectrum
384 self.vc += HALT_VC_THRESHOLD;
385 }
386 else if ws < V::HTS_COUNT as u16 {
387 self.set_hc_after_small_increment(self.hc + ws as i16);
388 }
389 else {
390 *self += ws as u32;
391 }
392 }
393
394 #[inline(always)]
395 fn as_timestamp(&self) -> Self::Timestamp {
396 self.ts
397 }
398}
399*/
400impl<V: VideoFrame, C: MemoryContention> Clock for VFrameTsCounter<V, C> {
401 type Limit = Ts;
402 type Timestamp = VideoTs;
403
404 #[inline(always)]
405 fn is_past_limit(&self, limit: Self::Limit) -> bool {
406 self.vc >= limit
407 }
408
409 fn add_irq(&mut self, _pc: u16) -> Self::Timestamp {
410 self.vts.set_hc_after_small_increment(self.hc + IRQ_ACK_CYCLE_TS as Ts);
411 self.as_timestamp()
412 }
413
414 #[inline(always)]
415 fn add_no_mreq(&mut self, address: u16, add_ts: NonZeroU8) {
416 let mut hc = self.hc;
417 if V::is_contended_line_no_mreq(self.vc) && self.contention.is_contended_address(address) {
418 for _ in 0..add_ts.get() {
419 hc = V::contention(hc) + 1;
420 }
421 }
422 else {
423 hc += add_ts.get() as Ts;
424 }
425 self.vts.set_hc_after_small_increment(hc);
426 }
427
428 #[inline(always)]
429 fn add_m1(&mut self, address: u16) -> Self::Timestamp {
430 // match address {
431 // // 0x8043 => println!("0x{:04x}: {} {:?}", address, self.as_tstates(), self.tsc),
432 // 0x806F..=0x8078 => println!("0x{:04x}: {} {:?}", address, self.as_tstates(), self.tsc),
433 // // 0xC008..=0xC011 => println!("0x{:04x}: {} {:?}", address, self.as_tstates(), self.tsc),
434 // _ => {}
435 // }
436 let hc = if V::is_contended_line_mreq(self.vc) && self.contention.is_contended_address(address) {
437 V::contention(self.hc)
438 }
439 else {
440 self.hc
441 };
442 self.vts.set_hc_after_small_increment(hc + M1_CYCLE_TS as Ts);
443 self.as_timestamp()
444 }
445
446 #[inline(always)]
447 fn add_mreq(&mut self, address: u16) -> Self::Timestamp {
448 let hc = if V::is_contended_line_mreq(self.vc) && self.contention.is_contended_address(address) {
449 V::contention(self.hc)
450 }
451 else {
452 self.hc
453 };
454 self.vts.set_hc_after_small_increment(hc + MEMRW_CYCLE_TS as Ts);
455 self.as_timestamp()
456 }
457
458 // fn add_io(&mut self, port: u16) -> Self::Timestamp {
459 // let VideoTs{ vc, hc } = self.tsc;
460 // let hc = if V::is_contended_line_no_mreq(vc) {
461 // if self.contention.is_contended_address(port) {
462 // let hc = V::contention(hc) + 1;
463 // if port & 1 == 0 { // C:1, C:3
464 // V::contention(hc) + (IO_CYCLE_TS - 1) as Ts
465 // }
466 // else { // C:1, C:1, C:1, C:1
467 // let mut hc1 = hc;
468 // for _ in 1..IO_CYCLE_TS {
469 // hc1 = V::contention(hc1) + 1;
470 // }
471 // hc1
472 // }
473 // }
474 // else {
475 // if port & 1 == 0 { // N:1 C:3
476 // V::contention(hc + 1) + (IO_CYCLE_TS - 1) as Ts
477 // }
478 // else { // N:4
479 // hc + IO_CYCLE_TS as Ts
480 // }
481 // }
482 // }
483 // else { // N:4
484 // hc + IO_CYCLE_TS as Ts
485 // };
486 // self.vts.set_hc_after_small_increment(hc);
487 // Self::new(vc, hc - 1).as_timestamp() // data read at last cycle
488 // }
489
490 fn add_io(&mut self, port: u16) -> Self::Timestamp {
491 let VideoTs{ vc, mut hc } = self.as_timestamp();
492 // if port == 0x7ffd {
493 // println!("0x{:04x}: {} {:?}", port, self.as_tstates(), self.tsc);
494 // }
495 let hc1 = if V::is_contended_line_no_mreq(vc) {
496 ula_io_contention!(self.contention, port, hc, V::contention)
497 // if is_contended_address(self.contention_mask, port) {
498 // hc = V::contention(hc) + IO_IORQ_LOW_TS as Ts;
499 // if port & 1 == 0 { // C:1, C:3
500 // V::contention(hc) + (IO_CYCLE_TS - IO_IORQ_LOW_TS) as Ts
501 // }
502 // else { // C:1, C:1, C:1, C:1
503 // let mut hc1 = hc;
504 // for _ in 0..(IO_CYCLE_TS - IO_IORQ_LOW_TS) {
505 // hc1 = V::contention(hc1) + 1;
506 // }
507 // hc1
508 // }
509 // }
510 // else {
511 // hc += IO_IORQ_LOW_TS as Ts;
512 // if port & 1 == 0 { // N:1 C:3
513 // V::contention(hc) + (IO_CYCLE_TS - IO_IORQ_LOW_TS) as Ts
514 // }
515 // else { // N:4
516 // hc + (IO_CYCLE_TS - IO_IORQ_LOW_TS) as Ts
517 // }
518 // }
519 }
520 else {
521 hc += IO_IORQ_LOW_TS as Ts;
522 hc + (IO_CYCLE_TS - IO_IORQ_LOW_TS) as Ts
523 };
524 let mut vtsc = *self;
525 vtsc.vts.set_hc_after_small_increment(hc);
526 self.vts.set_hc_after_small_increment(hc1);
527 vtsc.as_timestamp()
528 }
529
530 fn add_wait_states(&mut self, _bus: u16, wait_states: NonZeroU16) {
531 let ws = wait_states.get();
532 if ws > WAIT_STATES_THRESHOLD {
533 // emulate hanging the Spectrum
534 self.vc += HALT_VC_THRESHOLD;
535 }
536 else if ws < V::HTS_COUNT as u16 {
537 self.vts.set_hc_after_small_increment(self.hc + ws as i16);
538 }
539 else {
540 *self += ws as u32;
541 }
542 }
543
544 #[inline(always)]
545 fn as_timestamp(&self) -> Self::Timestamp {
546 ***self
547 }
548}
549
550impl<V> Default for VFrameTs<V> {
551 fn default() -> Self {
552 VFrameTs::from(VideoTs::default())
553 }
554}
555
556impl<V> Clone for VFrameTs<V> {
557 fn clone(&self) -> Self {
558 VFrameTs::from(self.ts)
559 }
560}
561
562impl<V> Hash for VFrameTs<V> {
563 fn hash<H: Hasher>(&self, state: &mut H) {
564 self.ts.hash(state);
565 }
566}
567
568impl<V> Eq for VFrameTs<V> {}
569
570impl<V> PartialEq for VFrameTs<V> {
571 #[inline(always)]
572 fn eq(&self, other: &Self) -> bool {
573 self.ts == other.ts
574 }
575}
576
577impl<V> Ord for VFrameTs<V> {
578 #[inline(always)]
579 fn cmp(&self, other: &Self) -> Ordering {
580 self.ts.cmp(other)
581 }
582}
583
584impl<V> PartialOrd for VFrameTs<V> {
585 #[inline(always)]
586 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
587 Some(self.cmp(other))
588 }
589}
590
591impl<V: VideoFrame> From<VFrameTs<V>> for FTs {
592 #[inline(always)]
593 fn from(vfts: VFrameTs<V>) -> FTs {
594 VFrameTs::into_tstates(vfts)
595 }
596}
597
598impl<V: VideoFrame> TryFrom<FTs> for VFrameTs<V> {
599 type Error = &'static str;
600
601 fn try_from(ts: FTs) -> Result<Self, Self::Error> {
602 VFrameTs::try_from_tstates(ts).ok_or(
603 "out of range video timestamp conversion attempted")
604 }
605}
606
607impl<V> From<VFrameTs<V>> for VideoTs {
608 #[inline(always)]
609 fn from(vfts: VFrameTs<V>) -> VideoTs {
610 vfts.ts
611 }
612}
613
614impl<V> From<VideoTs> for VFrameTs<V> {
615 /// Returns a [VFrameTs] from the given [VideoTs].
616 /// A returned `VFrameTs` is not being normalized.
617 ///
618 /// # Panics
619 ///
620 /// Panics when the given `ts` overflows the capacity of [VideoTs].
621 #[inline(always)]
622 fn from(ts: VideoTs) -> Self {
623 VFrameTs { ts, _vframe: PhantomData }
624 }
625}
626
627impl<V, C> From<VFrameTsCounter<V, C>> for VideoTs {
628 #[inline(always)]
629 fn from(vftsc: VFrameTsCounter<V, C>) -> VideoTs {
630 vftsc.vts.ts
631 }
632}
633
634impl<V, C> From<VFrameTsCounter<V, C>> for VFrameTs<V> {
635 #[inline(always)]
636 fn from(vftsc: VFrameTsCounter<V, C>) -> VFrameTs<V> {
637 vftsc.vts
638 }
639}
640
641impl<V> Deref for VFrameTs<V> {
642 type Target = VideoTs;
643
644 #[inline(always)]
645 fn deref(&self) -> &Self::Target {
646 &self.ts
647 }
648}
649
650impl<V> DerefMut for VFrameTs<V> {
651 #[inline(always)]
652 fn deref_mut(&mut self) -> &mut Self::Target {
653 &mut self.ts
654 }
655}
656
657impl<V, C> Deref for VFrameTsCounter<V, C> {
658 type Target = VFrameTs<V>;
659
660 #[inline(always)]
661 fn deref(&self) -> &Self::Target {
662 &self.vts
663 }
664}
665
666impl<V, C> DerefMut for VFrameTsCounter<V, C> {
667 #[inline(always)]
668 fn deref_mut(&mut self) -> &mut Self::Target {
669 &mut self.vts
670 }
671}