OXYGENHUB
◆ main openmsx / src / video / VDPAccessSlots.hh
5.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef VDPACCESSSLOTS_HH
2
#define VDPACCESSSLOTS_HH
3
4
#include "VDP.hh"
5
6
#include "narrow.hh"
7
8
#include <cassert>
9
#include <cstdint>
10
#include <span>
11
#include <utility>
12
13
namespace openmsx::VDPAccessSlots {
14
15
inline constexpr int TICKS = VDP::TICKS_PER_LINE;
16
17
/** Minimum distance until the next VRAM access. */
18
enum class Delta : int {
19
	D0        =  0 * TICKS, // These 2 are internal helpers
20
	D1        =  1 * TICKS,
21
	CPU_16    =  2 * TICKS, // These 3 are for CPU access delays (V99x8)
22
	CPU_28    =  3 * TICKS, //                                   (TMS99x8)
23
	// Like CPU_16, but without skipping the slots the CPU cannot be served
24
	// in: the slot it would have been granted, which the VDP spends on a
25
	// dummy read when the two differ.
26
	CPU_16_ANY =  4 * TICKS,
27
	CMD_24    =  5 * TICKS, // The remaining ones are command engine delays
28
	CMD_32    =  6 * TICKS, //   counted in 'memory cycles' rather than 'VDP cycles'
29
	CMD_36    =  7 * TICKS, //   see the comment about 'pad' in
30
	CMD_46    =  8 * TICKS, //   VDPAccessSlots.cc
31
	CMD_60    =  9 * TICKS,
32
	CMD_72    = 10 * TICKS,
33
	CMD_84    = 11 * TICKS,
34
	CMD_88    = 12 * TICKS,
35
	CMD_36_68 = 13 * TICKS, // 36+68 = 104
36
	CMD_46_58 = 13 * TICKS, // 46+58 = 104 (notice: duplicate!)
37
	CMD_84_36 = 14 * TICKS, // 84+36 = 120
38
	CMD_60_68 = 15 * TICKS, // 60+68 = 128
39
	CMD_72_58 = 16 * TICKS, // 72+58 = 130
40
	// The delay between the write to R#46 that starts a command and the
41
	// command's first VRAM access. Measured from the rising edge of /CSW as
42
	// 45, 46, 58, 70, 82 and 94 cycles; the values below are those plus the
43
	// 18 cycles between openMSX's port-write timestamp and that edge. The
44
	// startup is a wait like any other, so it is counted in memory cycles
45
	// and it gets the sprite addend.
46
	CMD_START_63  = 17 * TICKS, // POINT
47
	CMD_START_64  = 18 * TICKS, // LMMM
48
	CMD_START_76  = 19 * TICKS, // LMCM
49
	CMD_START_88  = 12 * TICKS, // LMMV, LMMC, PSET, SRCH  (= CMD_88!)
50
	CMD_START_100 = 20 * TICKS, // HMMM, YMMM
51
	CMD_START_112 = 21 * TICKS, // HMMV, LINE, HMMC
52
};
53
static constexpr int NUM_DELTAS = 22;
54
/** The CPU access delays in the 'Delta' enum: CPU_16, CPU_28 and CPU_16_ANY. */
55
static constexpr int FIRST_CPU_DELTA = 2;
56
static constexpr int LAST_CPU_DELTA = 5; // exclusive
57
/** The one of those that does use the slots the CPU cannot be served in. */
58
static constexpr int CPU_ANY_DELTA = 4;
59
/** The command engine delays in the 'Delta' enum: the steps and the startup
60
  * delays. These get the sprite addend; they and the CPU delays above are all
61
  * subject to the memory-cycle counting. */
62
static constexpr int FIRST_CMD_DELTA = 5;
63
static constexpr int LAST_CMD_DELTA = NUM_DELTAS; // exclusive
64
65
/** VDP-VRAM access slot calculator, meant to be used in the inner loops of the
66
  * VDPCmdEngine commands. Code optimized for the case that:
67
  *  - timing remains constant (sprites/display enable/disable)
68
  *  - there are more calls to next() and limitReached() than to getTime()
69
  */
70
class Calculator
71
{
72
public:
73
	/** This shouldn't be called directly, instead use getCalculator(). */
74
	Calculator(EmuTime frame, EmuTime time,
75
	           EmuTime limit_, std::span<const uint8_t, NUM_DELTAS * TICKS> tab_)
76
		: ref(frame), tab(tab_)
77
	{
78
		assert(frame <= time);
79
		assert(frame <= limit_);
80
		// not required that time <= limit
81
82
		ticks = narrow<int>(ref.getTicksTill(time));
83
		limit = narrow<int>(ref.getTicksTill(limit_));
84
		int lines = ticks / TICKS;
85
		ticks -= lines * TICKS;
86
		limit -= lines * TICKS; // might be negative
87
		ref   += lines * TICKS;
88
		assert(0 <= ticks); assert(ticks < TICKS);
89
	}
90
91
	/** Has 'time' advanced to or past 'limit'? */
92
	[[nodiscard]] bool limitReached() const {
93
		return ticks >= limit;
94
	}
95
96
	/** Get the current time. Initially this will return the 'time'
97
	  * constructor parameter. Each call to next() will increase this
98
	  * value. */
99
	[[nodiscard]] EmuTime getTime() const {
100
		return ref.getFastAdd(ticks);
101
	}
102
103
	/** Advance time to the earliest access slot that is at least 'delta'
104
	  * ticks later than the current time. */
105
	void next(Delta delta) {
106
		ticks += tab[std::to_underlying(delta) + ticks];
107
		if (ticks >= TICKS) [[unlikely]] {
108
			ticks -= TICKS;
109
			limit -= TICKS;
110
			ref   += TICKS;
111
		}
112
	}
113
114
private:
115
	int ticks;
116
	int limit;
117
	VDP::VDPClock ref;
118
	std::span<const uint8_t, NUM_DELTAS * TICKS> tab;
119
};
120
121
/** Return the time of the next available access slot that is at least 'delta'
122
  * cycles later than 'time'. The start of the current 'frame' is needed for
123
  * reference. */
124
[[nodiscard]] EmuTime getAccessSlot(EmuTime frame, EmuTime time, Delta delta,
125
                      const VDP& vdp);
126
127
/** When many calls to getAccessSlot() are needed, it's more efficient to
128
  * instead use this function. */
129
[[nodiscard]] Calculator getCalculator(
130
	EmuTime frame, EmuTime time, EmuTime limit,
131
	const VDP& vdp);
132
133
/** The largest interval paddingCycles() accepts. */
134
inline constexpr int MAX_PADDING_SPAN = 8;
135
136
/** How many cycles of line padding complete in the interval (t, t + n], where
137
  * 't' is at position 'tick' in its line: the number of cycles by which that
138
  * interval is longer in VDP cycles than in the VDP's memory cycles. Only for
139
  * short intervals; see the comment about 'pad' in VDPAccessSlots.cc. */
140
[[nodiscard]] int paddingCycles(int tick, int n, const VDP& vdp);
141
142
/** Is the CPU slot at line position 'slotTick' a 'late' one? Those hand out
143
  * their grant 2 cycles later than the rest, and release the CPU's request
144
  * buffer one cycle before their access instead of one cycle after. */
145
[[nodiscard]] bool isLateCpuSlot(int slotTick, const VDP& vdp);
146
147
} // namespace openmsx::VDPAccessSlots
148
149
#endif