summaryrefslogtreecommitdiff
path: root/src/nes/util/cycle_count.hh
blob: f8039c22fdc5781cd47173b5234ff74ecb40143e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#pragma once

#include <chrono>

namespace nes
{
	/// A cycle count that can be used for multiple components.
	///
	/// Each component can have a different clock frequency and this structure offers conversions between different
	/// cycle counts.
	class cycle_count
	{
		friend constexpr auto operator+(cycle_count, cycle_count) -> cycle_count;
		friend constexpr auto operator*(std::uint64_t, cycle_count) -> cycle_count;
		friend constexpr auto operator*(cycle_count, std::uint64_t) -> cycle_count;
		friend constexpr auto operator/(cycle_count, cycle_count) -> std::uint64_t;

		std::uint64_t units_{ 0 }; // The most fine-granular cycle count (here: PPU cycles).

		static constexpr auto ppu_cycle() -> cycle_count { return cycle_count{ 1 }; }
		static constexpr auto cpu_cycle() -> cycle_count { return cycle_count{ 3 }; }
		static constexpr auto second() -> cycle_count { return 1789773 * cpu_cycle(); }

	public:
		constexpr explicit cycle_count() = default;

		constexpr auto operator+=(cycle_count const other) -> cycle_count& { return *this = *this + other; }
		constexpr auto operator*=(std::uint64_t const other) -> cycle_count& { return *this = *this * other; }

		static constexpr auto from_units(std::uint64_t const units) -> cycle_count
		{
			return cycle_count{ units };
		}

		static constexpr auto from_duration(std::chrono::microseconds const duration) -> cycle_count
		{
			return from_units(duration.count() * second().get_units() / (1000 * 1000));
		}

		static constexpr auto from_cpu(std::uint64_t const cpu_cycles) -> cycle_count
		{
			return cpu_cycles * cpu_cycle();
		}

		static constexpr auto from_ppu(std::uint64_t const ppu_cycles) -> cycle_count
		{
			return ppu_cycles * ppu_cycle();
		}

		/// Value in implementation-defined units.
		constexpr auto get_units() const -> std::uint64_t { return units_; }

		constexpr auto to_duration() const -> std::chrono::microseconds
		{
			return std::chrono::microseconds{ get_units() * 1000 * 1000 / second().get_units() };
		}

		constexpr auto to_cpu() const -> std::uint64_t
		{
			return *this / cpu_cycle();
		}

		constexpr auto to_ppu() const -> std::uint64_t
		{
			return *this / ppu_cycle();
		}

	private:
		constexpr explicit cycle_count(std::uint64_t const units)
			: units_{ units }
		{
		}
	};

	constexpr auto operator==(cycle_count const a, cycle_count const b) -> bool { return a.get_units() == b.get_units(); }
	constexpr auto operator!=(cycle_count const a, cycle_count const b) -> bool { return a.get_units() != b.get_units(); }
	constexpr auto operator<(cycle_count const a, cycle_count const b) -> bool { return a.get_units() < b.get_units(); }
	constexpr auto operator>(cycle_count const a, cycle_count const b) -> bool { return a.get_units() > b.get_units(); }
	constexpr auto operator<=(cycle_count const a, cycle_count const b) -> bool { return a.get_units() <= b.get_units(); }
	constexpr auto operator>=(cycle_count const a, cycle_count const b) -> bool { return a.get_units() >= b.get_units(); }
	constexpr auto operator+(cycle_count const a, cycle_count const b) -> cycle_count { return cycle_count{ a.get_units() + b.get_units() }; }
	constexpr auto operator*(std::uint64_t const a, cycle_count const b) -> cycle_count { return cycle_count{ a * b.get_units() }; }
	constexpr auto operator*(cycle_count const a, std::uint64_t const b) -> cycle_count { return cycle_count{ a.get_units() * b }; }
	constexpr auto operator/(cycle_count const a, cycle_count const b) -> std::uint64_t { return a.get_units() / b.get_units(); }
} // namespace nes