OXYGENHUB
◆ main openmsx / src / cpu / CPUClock.hh
3.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef CPUCLOCK_HH
2
#define CPUCLOCK_HH
3
4
#include "DynamicClock.hh"
5
#include "Scheduler.hh"
6
#include "narrow.hh"
7
#include <cassert>
8
9
namespace openmsx {
10
11
class CPUClock
12
{
13
public:
14
	unsigned getFreq() const { return clock.getFreq(); }
15
16
protected:
17
	CPUClock(EmuTime time, Scheduler& scheduler);
18
19
// benchmarking showed a slowdown of ~3% on AMD64
20
// when using the following code:
21
#if 0
22
	// 64-bit addition is cheap
23
	void add(unsigned ticks) { clock += ticks; }
24
	void sync() const { }
25
#else
26
	// 64-bit addition is expensive
27
	// (if executed several million times per second)
28
	void add(unsigned ticks) { remaining -= narrow_cast<int>(ticks); }
29
	void sync() const {
30
		clock.fastAdd(limit - remaining);
31
		limit = remaining;
32
	}
33
#endif
34
35
	// These are similar to the corresponding methods in DynamicClock.
36
	[[nodiscard]] EmuTime getTime() const { sync(); return clock.getTime(); }
37
	[[nodiscard]] EmuTime getTimeFast() const { return clock.getFastAdd(limit - remaining); }
38
	[[nodiscard]] EmuTime getTimeFast(int cc) const {
39
		return clock.getFastAdd(limit - remaining + cc);
40
	}
41
	void setTime(EmuTime time) { sync(); clock.reset(time); }
42
	void setFreq(unsigned freq) { sync(); disableLimit(); clock.setFreq(freq); }
43
	void advanceTime(EmuTime time);
44
	[[nodiscard]] EmuTime calcTime(EmuTime time, unsigned ticks) const {
45
		return clock.add(time, ticks);
46
	}
47
48
	/** Implementation of the HALT instruction timing.
49
	  * Advances the clock with an integer multiple of 'hltStates' cycles
50
	  * so that it equal or bigger than 'time'. Returns the number of times
51
	  * 'hltStates' needed to be added.
52
	  */
53
	unsigned advanceHalt(unsigned hltStates, EmuTime time) {
54
		sync();
55
		unsigned ticks = clock.getTicksTillUp(time);
56
		unsigned halts = (ticks + hltStates - 1) / hltStates; // round up
57
		clock += halts * hltStates;
58
		return halts;
59
	}
60
61
	/** R800 runs at 7MHz, but I/O is done over a slower 3.5MHz bus. So
62
	  * sometimes right before I/O it's needed to wait for one cycle so
63
	  * that we're at the start of a clock cycle of the slower bus.
64
	  */
65
	void waitForEvenCycle(int cc)
66
	{
67
		sync();
68
		auto totalTicks = clock.getTotalTicks() + cc;
69
		if (totalTicks & 1) {
70
			add(1);
71
		}
72
	}
73
74
	// The following 3 methods are used in the innermost CPU loop. It
75
	// allows to implement this loop with just one test. This loop must
76
	// be exited on the following three conditions:
77
	//   1) a Synchronization Point is reached
78
	//   2) a 'slow' instruction must be executed (ei, di, ..)
79
	//   3) another thread has requested to exit the loop
80
	// The limitReached() method indicates whether the loop should be
81
	// exited.
82
	// The earliest SP must always be kept up-to-date with the setLimit()
83
	// method, so after every action that might change SP. The Scheduler
84
	// class is responsible for this.
85
	// In 'slow' mode the limit mechanism can be disabled with the
86
	// disableLimit() method. In disabled mode, the limitReached() method
87
	// always returns true.
88
	// When another thread requests to exit the loop, it's not needed to
89
	// already exit at the next instruction. If we exit soon that's good
90
	// enough. This is implemented by simply regularly exiting the loop
91
	// (outside the inner loop, the real exit condition should be tested).
92
93
	void setLimit(EmuTime time) {
94
		if (limitEnabled) {
95
			sync();
96
			assert(remaining == limit);
97
			limit = narrow_cast<int>(clock.getTicksTillUp(time) - 1);
98
			remaining = limit;
99
		} else {
100
			assert(limit < 0);
101
		}
102
	}
103
	void enableLimit() {
104
		limitEnabled = true;
105
		setLimit(scheduler.getNext());
106
	}
107
	void disableLimit() {
108
		limitEnabled = false;
109
		int extra = limit - remaining;
110
		limit = -1;
111
		remaining = limit - extra;
112
	}
113
	[[nodiscard]] bool limitReached() const {
114
		return remaining < 0;
115
	}
116
117
	template<typename Archive>
118
	void serialize(Archive& ar, unsigned version);
119
120
private:
121
	mutable DynamicClock clock;
122
	Scheduler& scheduler;
123
	int remaining = -1;
124
	mutable int limit = -1;
125
	bool limitEnabled = false;
126
};
127
128
} // namespace openmsx
129
130
#endif